Skip to main content

uv_settings/
settings.rs

1#[cfg(feature = "schemars")]
2use std::borrow::Cow;
3use std::{fmt::Debug, num::NonZeroUsize, path::Path, path::PathBuf};
4
5use serde::{Deserialize, Serialize};
6
7use uv_cache_info::CacheKey;
8use uv_configuration::{
9    BuildIsolation, IndexStrategy, KeyringProviderType, PackageNameSpecifier, ProxyUrl, Reinstall,
10    RequiredVersion, TargetTriple, TrustedHost, TrustedPublishing, Upgrade,
11};
12use uv_distribution_types::{
13    ConfigSettings, ExtraBuildVariables, Index, IndexUrl, IndexUrlError, Origin,
14    PackageConfigSettings, PipExtraIndex, PipFindLinks, PipIndex, StaticMetadata,
15};
16use uv_install_wheel::LinkMode;
17use uv_macros::{CombineOptions, OptionsMetadata};
18use uv_normalize::{ExtraName, PackageName, PipGroupName};
19use uv_pep508::Requirement;
20use uv_preview::{MaybePreviewFeature, Preview};
21use uv_pypi_types::{SupportedEnvironments, VerbatimParsedUrl};
22use uv_python::{PythonDownloads, PythonPreference, PythonVersion};
23use uv_redacted::DisplaySafeUrl;
24use uv_resolver::{
25    AnnotationStyle, ExcludeNewerOverride, ExcludeNewerPackage, ExcludeNewerSpan,
26    ExcludeNewerValue, ForkStrategy, PrereleaseMode, ResolutionMode,
27    serialize_exclude_newer_package_with_spans,
28};
29use uv_torch::TorchMode;
30use uv_workspace::pyproject::ExtraBuildDependencies;
31use uv_workspace::pyproject_mut::AddBoundsKind;
32
33/// A `pyproject.toml` with an (optional) `[tool.uv]` section.
34#[allow(dead_code)]
35#[derive(Debug, Clone, Default, Deserialize)]
36pub(crate) struct PyProjectToml {
37    pub(crate) tool: Option<Tools>,
38}
39
40/// A `[tool]` section.
41#[allow(dead_code)]
42#[derive(Debug, Clone, Default, Deserialize)]
43pub(crate) struct Tools {
44    pub(crate) uv: Option<Options>,
45}
46
47/// A `pyproject.toml` with an (optional) `[tool.uv.required-version]`.
48#[derive(Debug, Clone, Default, Deserialize)]
49pub(crate) struct PyProjectRequiredVersionToml {
50    pub(crate) tool: Option<RequiredVersionTools>,
51}
52
53/// A `[tool]` section containing only the fields required for `required-version` discovery.
54#[derive(Debug, Clone, Default, Deserialize)]
55pub(crate) struct RequiredVersionTools {
56    pub(crate) uv: Option<RequiredVersionOptions>,
57}
58
59/// The minimal `[tool.uv]` subset required to enforce `required-version` before full parsing.
60#[derive(Debug, Clone, Default, Deserialize)]
61#[serde(rename_all = "kebab-case")]
62pub(crate) struct RequiredVersionOptions {
63    pub(crate) required_version: Option<RequiredVersion>,
64}
65
66/// A `uv.toml` containing only the fields required for `required-version` discovery.
67#[derive(Debug, Clone, Default, Deserialize)]
68#[serde(rename_all = "kebab-case")]
69pub(crate) struct UvRequiredVersionToml {
70    pub(crate) required_version: Option<RequiredVersion>,
71}
72
73/// A `[tool.uv]` section.
74#[allow(dead_code)]
75#[derive(Debug, Clone, Default, Deserialize, CombineOptions, OptionsMetadata)]
76#[serde(try_from = "OptionsWire", rename_all = "kebab-case")]
77#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
78#[cfg_attr(feature = "schemars", schemars(!try_from))]
79pub struct Options {
80    #[serde(flatten)]
81    pub globals: GlobalOptions,
82
83    #[serde(flatten)]
84    pub top_level: ResolverInstallerSchema,
85
86    #[serde(flatten)]
87    pub install_mirrors: PythonInstallMirrors,
88
89    #[serde(flatten)]
90    pub publish: PublishOptions,
91
92    #[serde(flatten)]
93    pub add: AddOptions,
94
95    #[option_group]
96    pub audit: Option<AuditOptions>,
97
98    #[option_group]
99    pub pip: Option<PipOptions>,
100
101    /// The keys to consider when caching builds for the project.
102    ///
103    /// Cache keys enable you to specify the files or directories that should trigger a rebuild when
104    /// modified. By default, uv will rebuild a project whenever the `pyproject.toml`, `setup.py`,
105    /// or `setup.cfg` files in the project directory are modified, or if a `src` directory is
106    /// added or removed, i.e.:
107    ///
108    /// ```toml
109    /// cache-keys = [{ file = "pyproject.toml" }, { file = "setup.py" }, { file = "setup.cfg" }, { dir = "src" }]
110    /// ```
111    ///
112    /// As an example: if a project uses dynamic metadata to read its dependencies from a
113    /// `requirements.txt` file, you can specify `cache-keys = [{ file = "requirements.txt" }, { file = "pyproject.toml" }]`
114    /// to ensure that the project is rebuilt whenever the `requirements.txt` file is modified (in
115    /// addition to watching the `pyproject.toml`).
116    ///
117    /// Globs are supported, following the syntax of the [`glob`](https://docs.rs/glob/0.3.1/glob/struct.Pattern.html)
118    /// crate. For example, to invalidate the cache whenever a `.toml` file in the project directory
119    /// or any of its subdirectories is modified, you can specify `cache-keys = [{ file = "**/*.toml" }]`.
120    /// Note that the use of globs can be expensive, as uv may need to walk the filesystem to
121    /// determine whether any files have changed.
122    ///
123    /// Cache keys can also include version control information. For example, if a project uses
124    /// `setuptools_scm` to read its version from a Git commit, you can specify `cache-keys = [{ git = { commit = true }, { file = "pyproject.toml" }]`
125    /// to include the current Git commit hash in the cache key (in addition to the
126    /// `pyproject.toml`). Git tags are also supported via `cache-keys = [{ git = { commit = true, tags = true } }]`.
127    ///
128    /// Cache keys can also include environment variables. For example, if a project relies on
129    /// `MACOSX_DEPLOYMENT_TARGET` or other environment variables to determine its behavior, you can
130    /// specify `cache-keys = [{ env = "MACOSX_DEPLOYMENT_TARGET" }]` to invalidate the cache
131    /// whenever the environment variable changes.
132    ///
133    /// Cache keys only affect the project defined by the `pyproject.toml` in which they're
134    /// specified (as opposed to, e.g., affecting all members in a workspace), and all paths and
135    /// globs are interpreted as relative to the project directory.
136    #[option(
137        default = r#"[{ file = "pyproject.toml" }, { file = "setup.py" }, { file = "setup.cfg" }]"#,
138        value_type = "list[dict]",
139        example = r#"
140            cache-keys = [{ file = "pyproject.toml" }, { file = "requirements.txt" }, { git = { commit = true } }]
141        "#
142    )]
143    pub cache_keys: Option<Vec<CacheKey>>,
144
145    // NOTE(charlie): These fields are shared with `ToolUv` in
146    // `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct.
147    // They're respected in both `pyproject.toml` and `uv.toml` files.
148    #[cfg_attr(feature = "schemars", schemars(skip))]
149    pub override_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
150
151    #[cfg_attr(feature = "schemars", schemars(skip))]
152    pub exclude_dependencies: Option<Vec<uv_normalize::PackageName>>,
153
154    #[cfg_attr(feature = "schemars", schemars(skip))]
155    pub constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
156
157    #[cfg_attr(feature = "schemars", schemars(skip))]
158    pub build_constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
159
160    #[cfg_attr(feature = "schemars", schemars(skip))]
161    pub environments: Option<SupportedEnvironments>,
162
163    #[cfg_attr(feature = "schemars", schemars(skip))]
164    pub required_environments: Option<SupportedEnvironments>,
165
166    // NOTE(charlie): These fields should be kept in-sync with `ToolUv` in
167    // `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct.
168    // They're only respected in `pyproject.toml` files, and should be rejected in `uv.toml` files.
169    #[cfg_attr(feature = "schemars", schemars(skip))]
170    pub(crate) conflicts: Option<serde::de::IgnoredAny>,
171
172    #[cfg_attr(feature = "schemars", schemars(skip))]
173    pub(crate) workspace: Option<serde::de::IgnoredAny>,
174
175    #[cfg_attr(feature = "schemars", schemars(skip))]
176    pub(crate) sources: Option<serde::de::IgnoredAny>,
177
178    #[cfg_attr(feature = "schemars", schemars(skip))]
179    pub(crate) dev_dependencies: Option<serde::de::IgnoredAny>,
180
181    #[cfg_attr(feature = "schemars", schemars(skip))]
182    pub(crate) default_groups: Option<serde::de::IgnoredAny>,
183
184    #[cfg_attr(feature = "schemars", schemars(skip))]
185    pub(crate) dependency_groups: Option<serde::de::IgnoredAny>,
186
187    #[cfg_attr(feature = "schemars", schemars(skip))]
188    pub(crate) managed: Option<serde::de::IgnoredAny>,
189
190    #[cfg_attr(feature = "schemars", schemars(skip))]
191    pub(crate) r#package: Option<serde::de::IgnoredAny>,
192
193    #[cfg_attr(feature = "schemars", schemars(skip))]
194    pub(crate) build_backend: Option<serde::de::IgnoredAny>,
195}
196
197impl Options {
198    /// Construct an [`Options`] with the given global and top-level settings.
199    pub fn simple(globals: GlobalOptions, top_level: ResolverInstallerSchema) -> Self {
200        Self {
201            globals,
202            top_level,
203            ..Default::default()
204        }
205    }
206
207    /// Set the [`Origin`] on all indexes without an existing origin.
208    #[must_use]
209    pub(crate) fn with_origin(mut self, origin: Origin) -> Self {
210        if let Some(indexes) = &mut self.top_level.index {
211            for index in indexes {
212                index.origin.get_or_insert(origin);
213            }
214        }
215        if let Some(index_url) = &mut self.top_level.index_url {
216            index_url.try_set_origin(origin);
217        }
218        if let Some(extra_index_urls) = &mut self.top_level.extra_index_url {
219            for index_url in extra_index_urls {
220                index_url.try_set_origin(origin);
221            }
222        }
223        if let Some(pip) = &mut self.pip {
224            if let Some(indexes) = &mut pip.index {
225                for index in indexes {
226                    index.origin.get_or_insert(origin);
227                }
228            }
229            if let Some(index_url) = &mut pip.index_url {
230                index_url.try_set_origin(origin);
231            }
232            if let Some(extra_index_urls) = &mut pip.extra_index_url {
233                for index_url in extra_index_urls {
234                    index_url.try_set_origin(origin);
235                }
236            }
237        }
238        self
239    }
240
241    /// Resolve the [`Options`] relative to the given root directory.
242    pub(crate) fn relative_to(self, root_dir: &Path) -> Result<Self, IndexUrlError> {
243        Ok(Self {
244            top_level: self.top_level.relative_to(root_dir)?,
245            pip: self.pip.map(|pip| pip.relative_to(root_dir)).transpose()?,
246            ..self
247        })
248    }
249}
250
251/// Global settings, relevant to all invocations.
252#[derive(Debug, Clone, Default, Deserialize, CombineOptions, OptionsMetadata)]
253#[serde(try_from = "GlobalOptionsWire", rename_all = "kebab-case")]
254#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
255#[cfg_attr(feature = "schemars", schemars(!try_from))]
256pub struct GlobalOptions {
257    /// Enforce a requirement on the version of uv.
258    ///
259    /// If the version of uv does not meet the requirement at runtime, uv will exit
260    /// with an error.
261    ///
262    /// Accepts a [PEP 440](https://peps.python.org/pep-0440/) specifier, like `==0.5.0` or `>=0.5.0`.
263    #[option(
264        default = "null",
265        value_type = "str",
266        example = r#"
267            required-version = ">=0.5.0"
268        "#
269    )]
270    pub required_version: Option<RequiredVersion>,
271    /// Whether to load TLS certificates from the platform's native certificate store.
272    ///
273    /// By default, uv uses bundled Mozilla root certificates. When enabled, this loads
274    /// certificates from the platform's native certificate store instead.
275    #[option(
276        default = "false",
277        value_type = "bool",
278        uv_toml_only = true,
279        example = r#"
280            system-certs = true
281        "#
282    )]
283    pub system_certs: Option<bool>,
284    /// Whether to load TLS certificates from the platform's native certificate store.
285    ///
286    /// By default, uv uses bundled Mozilla root certificates. When enabled, this loads
287    /// certificates from the platform's native certificate store instead.
288    ///
289    /// (Deprecated: use `system-certs` instead.)
290    #[deprecated(note = "use `system-certs` instead")]
291    #[option(
292        default = "false",
293        value_type = "bool",
294        uv_toml_only = true,
295        example = r#"
296            native-tls = true
297        "#
298    )]
299    pub native_tls: Option<bool>,
300    /// Disable network access, relying only on locally cached data and locally available files.
301    #[option(
302        default = "false",
303        value_type = "bool",
304        example = r#"
305            offline = true
306        "#
307    )]
308    pub offline: Option<bool>,
309    /// Avoid reading from or writing to the cache, instead using a temporary directory for the
310    /// duration of the operation.
311    #[option(
312        default = "false",
313        value_type = "bool",
314        example = r#"
315            no-cache = true
316        "#
317    )]
318    pub no_cache: Option<bool>,
319    /// Path to the cache directory.
320    ///
321    /// Defaults to `$XDG_CACHE_HOME/uv` or `$HOME/.cache/uv` on Linux and macOS, and
322    /// `%LOCALAPPDATA%\uv\cache` on Windows.
323    #[option(
324        default = "None",
325        value_type = "str",
326        uv_toml_only = true,
327        example = r#"
328            cache-dir = "./.uv_cache"
329        "#
330    )]
331    pub cache_dir: Option<PathBuf>,
332
333    /// The user's preview configuration.
334    #[serde(flatten)]
335    pub preview: Option<PreviewOption>,
336
337    /// Whether to prefer using Python installations that are already present on the system, or
338    /// those that are downloaded and installed by uv.
339    #[option(
340        default = "\"managed\"",
341        value_type = "str",
342        example = r#"
343            python-preference = "managed"
344        "#,
345        possible_values = true
346    )]
347    pub python_preference: Option<PythonPreference>,
348    /// Whether to allow Python downloads.
349    #[option(
350        default = "\"automatic\"",
351        value_type = "str",
352        example = r#"
353            python-downloads = "manual"
354        "#,
355        possible_values = true
356    )]
357    pub python_downloads: Option<PythonDownloads>,
358    /// The maximum number of in-flight concurrent downloads that uv will perform at any given
359    /// time.
360    #[option(
361        default = "50",
362        value_type = "int",
363        example = r#"
364            concurrent-downloads = 4
365        "#
366    )]
367    pub concurrent_downloads: Option<NonZeroUsize>,
368    /// The maximum number of source distributions that uv will build concurrently at any given
369    /// time.
370    ///
371    /// Defaults to the number of available CPU cores.
372    #[option(
373        default = "None",
374        value_type = "int",
375        example = r#"
376            concurrent-builds = 4
377        "#
378    )]
379    pub concurrent_builds: Option<NonZeroUsize>,
380    /// The number of threads used when installing and unzipping packages.
381    ///
382    /// Defaults to the number of available CPU cores.
383    #[option(
384        default = "None",
385        value_type = "int",
386        example = r#"
387            concurrent-installs = 4
388        "#
389    )]
390    pub concurrent_installs: Option<NonZeroUsize>,
391    /// The URL of the HTTP proxy to use.
392    #[option(
393        default = "None",
394        value_type = "str",
395        uv_toml_only = true,
396        example = r#"
397            http-proxy = "http://proxy.example.com"
398        "#
399    )]
400    pub http_proxy: Option<ProxyUrl>,
401    /// The URL of the HTTPS proxy to use.
402    #[option(
403        default = "None",
404        value_type = "str",
405        uv_toml_only = true,
406        example = r#"
407            https-proxy = "https://proxy.example.com"
408        "#
409    )]
410    pub https_proxy: Option<ProxyUrl>,
411    /// A list of hosts to exclude from proxying.
412    #[option(
413        default = "None",
414        value_type = "list[str]",
415        uv_toml_only = true,
416        example = r#"
417            no-proxy = ["localhost", "127.0.0.1"]
418        "#
419    )]
420    pub no_proxy: Option<Vec<String>>,
421    /// Allow insecure connections to host.
422    ///
423    /// Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g.,
424    /// `localhost:8080`), or a URL (e.g., `https://localhost`).
425    ///
426    /// WARNING: Hosts included in this list will not be verified against the system's certificate
427    /// store. Only use `--allow-insecure-host` in a secure network with verified sources, as it
428    /// bypasses SSL verification and could expose you to MITM attacks.
429    #[option(
430        default = "[]",
431        value_type = "list[str]",
432        example = r#"
433            allow-insecure-host = ["localhost:8080"]
434        "#
435    )]
436    pub allow_insecure_host: Option<Vec<TrustedHost>>,
437}
438
439/// Like [`GlobalOptions`], but with any `#[serde(flatten)]` fields inlined.
440/// This improves line/column information in error messages.
441#[derive(Debug, Clone, Default, Deserialize)]
442#[serde(rename_all = "kebab-case")]
443struct GlobalOptionsWire {
444    required_version: Option<RequiredVersion>,
445    system_certs: Option<bool>,
446    native_tls: Option<bool>,
447    offline: Option<bool>,
448    no_cache: Option<bool>,
449    cache_dir: Option<PathBuf>,
450
451    preview: Option<bool>,
452    preview_features: Option<PreviewFeaturesOption>,
453
454    python_preference: Option<PythonPreference>,
455    python_downloads: Option<PythonDownloads>,
456    concurrent_downloads: Option<NonZeroUsize>,
457    concurrent_builds: Option<NonZeroUsize>,
458    concurrent_installs: Option<NonZeroUsize>,
459    http_proxy: Option<ProxyUrl>,
460    https_proxy: Option<ProxyUrl>,
461    no_proxy: Option<Vec<String>>,
462    allow_insecure_host: Option<Vec<TrustedHost>>,
463}
464
465impl TryFrom<GlobalOptionsWire> for GlobalOptions {
466    type Error = &'static str;
467
468    #[allow(deprecated)]
469    fn try_from(value: GlobalOptionsWire) -> Result<Self, Self::Error> {
470        let GlobalOptionsWire {
471            required_version,
472            system_certs,
473            native_tls,
474            offline,
475            no_cache,
476            cache_dir,
477            preview,
478            preview_features,
479            python_preference,
480            python_downloads,
481            concurrent_downloads,
482            concurrent_builds,
483            concurrent_installs,
484            http_proxy,
485            https_proxy,
486            no_proxy,
487            allow_insecure_host,
488        } = value;
489
490        Ok(Self {
491            required_version,
492            system_certs,
493            native_tls,
494            offline,
495            no_cache,
496            cache_dir,
497            preview: PreviewOption::try_from(preview, preview_features)?,
498            python_preference,
499            python_downloads,
500            concurrent_downloads,
501            concurrent_builds,
502            concurrent_installs,
503            http_proxy,
504            https_proxy,
505            no_proxy,
506            allow_insecure_host,
507        })
508    }
509}
510
511/// Settings relevant to all installer operations.
512#[derive(Debug, Clone, Default, CombineOptions)]
513pub struct InstallerOptions {
514    index: Option<Vec<Index>>,
515    index_url: Option<PipIndex>,
516    extra_index_url: Option<Vec<PipExtraIndex>>,
517    no_index: Option<bool>,
518    find_links: Option<Vec<PipFindLinks>>,
519    index_strategy: Option<IndexStrategy>,
520    keyring_provider: Option<KeyringProviderType>,
521    config_settings: Option<ConfigSettings>,
522    exclude_newer: Option<ExcludeNewerOverride>,
523    link_mode: Option<LinkMode>,
524    compile_bytecode: Option<bool>,
525    reinstall: Option<Reinstall>,
526    build_isolation: Option<BuildIsolation>,
527    no_build: Option<bool>,
528    no_build_package: Option<Vec<PackageName>>,
529    no_binary: Option<bool>,
530    no_binary_package: Option<Vec<PackageName>>,
531    no_sources: Option<bool>,
532    no_sources_package: Option<Vec<PackageName>>,
533}
534
535/// Settings relevant to all resolver operations.
536#[derive(Debug, Clone, Default, CombineOptions)]
537pub struct ResolverOptions {
538    pub index: Option<Vec<Index>>,
539    pub index_url: Option<PipIndex>,
540    pub extra_index_url: Option<Vec<PipExtraIndex>>,
541    pub no_index: Option<bool>,
542    pub find_links: Option<Vec<PipFindLinks>>,
543    pub index_strategy: Option<IndexStrategy>,
544    pub keyring_provider: Option<KeyringProviderType>,
545    pub resolution: Option<ResolutionMode>,
546    pub prerelease: Option<PrereleaseMode>,
547    pub fork_strategy: Option<ForkStrategy>,
548    pub dependency_metadata: Option<Vec<StaticMetadata>>,
549    pub config_settings: Option<ConfigSettings>,
550    pub config_settings_package: Option<PackageConfigSettings>,
551    pub exclude_newer: Option<ExcludeNewerOverride>,
552    pub exclude_newer_package: Option<ExcludeNewerPackage>,
553    pub link_mode: Option<LinkMode>,
554    pub torch_backend: Option<TorchMode>,
555    pub upgrade: Option<Upgrade>,
556    pub build_isolation: Option<BuildIsolation>,
557    pub no_build: Option<bool>,
558    pub no_build_package: Option<Vec<PackageName>>,
559    pub no_binary: Option<bool>,
560    pub no_binary_package: Option<Vec<PackageName>>,
561    pub extra_build_dependencies: Option<ExtraBuildDependencies>,
562    pub extra_build_variables: Option<ExtraBuildVariables>,
563    pub no_sources: Option<bool>,
564    pub no_sources_package: Option<Vec<PackageName>>,
565}
566
567/// Shared settings, relevant to all operations that must resolve and install dependencies. The
568/// union of [`InstallerOptions`] and [`ResolverOptions`].
569#[derive(Debug, Clone, Default, CombineOptions)]
570pub struct ResolverInstallerOptions {
571    pub index: Option<Vec<Index>>,
572    pub index_url: Option<PipIndex>,
573    pub extra_index_url: Option<Vec<PipExtraIndex>>,
574    pub no_index: Option<bool>,
575    pub find_links: Option<Vec<PipFindLinks>>,
576    pub index_strategy: Option<IndexStrategy>,
577    pub keyring_provider: Option<KeyringProviderType>,
578    pub resolution: Option<ResolutionMode>,
579    pub prerelease: Option<PrereleaseMode>,
580    pub fork_strategy: Option<ForkStrategy>,
581    pub dependency_metadata: Option<Vec<StaticMetadata>>,
582    pub config_settings: Option<ConfigSettings>,
583    pub config_settings_package: Option<PackageConfigSettings>,
584    pub build_isolation: Option<BuildIsolation>,
585    pub extra_build_dependencies: Option<ExtraBuildDependencies>,
586    pub extra_build_variables: Option<ExtraBuildVariables>,
587    pub exclude_newer: Option<ExcludeNewerOverride>,
588    pub exclude_newer_package: Option<ExcludeNewerPackage>,
589    pub link_mode: Option<LinkMode>,
590    pub torch_backend: Option<TorchMode>,
591    pub compile_bytecode: Option<bool>,
592    pub no_sources: Option<bool>,
593    pub no_sources_package: Option<Vec<PackageName>>,
594    pub upgrade: Option<Upgrade>,
595    pub reinstall: Option<Reinstall>,
596    pub no_build: Option<bool>,
597    pub no_build_package: Option<Vec<PackageName>>,
598    pub no_binary: Option<bool>,
599    pub no_binary_package: Option<Vec<PackageName>>,
600}
601
602impl From<ResolverInstallerSchema> for ResolverInstallerOptions {
603    fn from(value: ResolverInstallerSchema) -> Self {
604        let ResolverInstallerSchema {
605            index,
606            index_url,
607            extra_index_url,
608            no_index,
609            find_links,
610            index_strategy,
611            keyring_provider,
612            resolution,
613            prerelease,
614            fork_strategy,
615            dependency_metadata,
616            config_settings,
617            config_settings_package,
618            no_build_isolation,
619            no_build_isolation_package,
620            extra_build_dependencies,
621            extra_build_variables,
622            exclude_newer,
623            exclude_newer_package,
624            link_mode,
625            torch_backend,
626            compile_bytecode,
627            no_sources,
628            no_sources_package,
629            upgrade,
630            upgrade_package,
631            reinstall,
632            reinstall_package,
633            no_build,
634            no_build_package,
635            no_binary,
636            no_binary_package,
637        } = value;
638        Self {
639            index,
640            index_url,
641            extra_index_url,
642            no_index,
643            find_links,
644            index_strategy,
645            keyring_provider,
646            resolution,
647            prerelease,
648            fork_strategy,
649            dependency_metadata,
650            config_settings,
651            config_settings_package,
652            build_isolation: BuildIsolation::from_args(
653                no_build_isolation,
654                no_build_isolation_package.into_iter().flatten().collect(),
655            ),
656            extra_build_dependencies,
657            extra_build_variables,
658            exclude_newer,
659            exclude_newer_package,
660            link_mode,
661            torch_backend,
662            compile_bytecode,
663            no_sources,
664            no_sources_package,
665            upgrade: Upgrade::from_args(
666                upgrade,
667                upgrade_package
668                    .into_iter()
669                    .flatten()
670                    .map(Into::into)
671                    .collect(),
672                Vec::new(),
673            ),
674            reinstall: Reinstall::from_args(reinstall, reinstall_package.unwrap_or_default()),
675            no_build,
676            no_build_package,
677            no_binary,
678            no_binary_package,
679        }
680    }
681}
682
683impl ResolverInstallerSchema {
684    /// Resolve the [`ResolverInstallerSchema`] relative to the given root directory.
685    fn relative_to(self, root_dir: &Path) -> Result<Self, IndexUrlError> {
686        Ok(Self {
687            index: self
688                .index
689                .map(|index| {
690                    index
691                        .into_iter()
692                        .map(|index| index.relative_to(root_dir))
693                        .collect::<Result<Vec<_>, _>>()
694                })
695                .transpose()?,
696            index_url: self
697                .index_url
698                .map(|index_url| index_url.relative_to(root_dir))
699                .transpose()?,
700            extra_index_url: self
701                .extra_index_url
702                .map(|extra_index_url| {
703                    extra_index_url
704                        .into_iter()
705                        .map(|extra_index_url| extra_index_url.relative_to(root_dir))
706                        .collect::<Result<Vec<_>, _>>()
707                })
708                .transpose()?,
709            find_links: self
710                .find_links
711                .map(|find_links| {
712                    find_links
713                        .into_iter()
714                        .map(|find_link| find_link.relative_to(root_dir))
715                        .collect::<Result<Vec<_>, _>>()
716                })
717                .transpose()?,
718            ..self
719        })
720    }
721}
722
723/// The JSON schema for the `[tool.uv]` section of a `pyproject.toml` file.
724#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, CombineOptions, OptionsMetadata)]
725#[serde(rename_all = "kebab-case")]
726#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
727pub struct ResolverInstallerSchema {
728    /// The package indexes to use when resolving dependencies.
729    ///
730    /// Accepts either a repository compliant with [PEP 503](https://peps.python.org/pep-0503/)
731    /// (the simple repository API), or a local directory laid out in the same format.
732    ///
733    /// Indexes are considered in the order in which they're defined, such that the first-defined
734    /// index has the highest priority. Further, the indexes provided by this setting are given
735    /// higher priority than any indexes specified via [`index_url`](#index-url) or
736    /// [`extra_index_url`](#extra-index-url). uv will only consider the first index that contains
737    /// a given package, unless an alternative [index strategy](#index-strategy) is specified.
738    ///
739    /// If an index is marked as `explicit = true`, it will be used exclusively for those
740    /// dependencies that select it explicitly via `[tool.uv.sources]`, as in:
741    ///
742    /// ```toml
743    /// [[tool.uv.index]]
744    /// name = "pytorch"
745    /// url = "https://download.pytorch.org/whl/cu130"
746    /// explicit = true
747    ///
748    /// [tool.uv.sources]
749    /// torch = { index = "pytorch" }
750    /// ```
751    ///
752    /// If an index is marked as `default = true`, it will be moved to the end of the prioritized list, such that it is
753    /// given the lowest priority when resolving packages. Additionally, marking an index as default will disable the
754    /// PyPI default index.
755    #[option(
756        default = "\"[]\"",
757        value_type = "dict",
758        example = r#"
759            [[tool.uv.index]]
760            name = "pytorch"
761            url = "https://download.pytorch.org/whl/cu130"
762        "#
763    )]
764    pub index: Option<Vec<Index>>,
765    /// The URL of the Python package index (by default: <https://pypi.org/simple>).
766    ///
767    /// Accepts either a repository compliant with [PEP 503](https://peps.python.org/pep-0503/)
768    /// (the simple repository API), or a local directory laid out in the same format.
769    ///
770    /// The index provided by this setting is given lower priority than any indexes specified via
771    /// [`extra_index_url`](#extra-index-url) or [`index`](#index).
772    ///
773    /// (Deprecated: use `index` instead.)
774    #[option(
775        default = "\"https://pypi.org/simple\"",
776        value_type = "str",
777        example = r#"
778            index-url = "https://test.pypi.org/simple"
779        "#
780    )]
781    pub index_url: Option<PipIndex>,
782    /// Extra URLs of package indexes to use, in addition to `--index-url`.
783    ///
784    /// Accepts either a repository compliant with [PEP 503](https://peps.python.org/pep-0503/)
785    /// (the simple repository API), or a local directory laid out in the same format.
786    ///
787    /// All indexes provided via this flag take priority over the index specified by
788    /// [`index_url`](#index-url) or [`index`](#index) with `default = true`. When multiple indexes
789    /// are provided, earlier values take priority.
790    ///
791    /// To control uv's resolution strategy when multiple indexes are present, see
792    /// [`index_strategy`](#index-strategy).
793    ///
794    /// (Deprecated: use `index` instead.)
795    #[option(
796        default = "[]",
797        value_type = "list[str]",
798        example = r#"
799            extra-index-url = ["https://download.pytorch.org/whl/cpu"]
800        "#
801    )]
802    pub extra_index_url: Option<Vec<PipExtraIndex>>,
803    /// Ignore all registry indexes (e.g., PyPI), instead relying on direct URL dependencies and
804    /// those provided via `--find-links`.
805    #[option(
806        default = "false",
807        value_type = "bool",
808        example = r#"
809            no-index = true
810        "#
811    )]
812    pub no_index: Option<bool>,
813    /// Locations to search for candidate distributions, in addition to those found in the registry
814    /// indexes.
815    ///
816    /// If a path, the target must be a directory that contains packages as wheel files (`.whl`) or
817    /// source distributions (e.g., `.tar.gz` or `.zip`) at the top level.
818    ///
819    /// If a URL, the page must contain a flat list of links to package files adhering to the
820    /// formats described above.
821    #[option(
822        default = "[]",
823        value_type = "list[str]",
824        example = r#"
825            find-links = ["https://download.pytorch.org/whl/torch_stable.html"]
826        "#
827    )]
828    pub find_links: Option<Vec<PipFindLinks>>,
829    /// The strategy to use when resolving against multiple index URLs.
830    ///
831    /// By default, uv will stop at the first index on which a given package is available, and
832    /// limit resolutions to those present on that first index (`first-index`). This prevents
833    /// "dependency confusion" attacks, whereby an attacker can upload a malicious package under the
834    /// same name to an alternate index.
835    #[option(
836        default = "\"first-index\"",
837        value_type = "str",
838        example = r#"
839            index-strategy = "unsafe-best-match"
840        "#,
841        possible_values = true
842    )]
843    pub index_strategy: Option<IndexStrategy>,
844    /// Attempt to use `keyring` for authentication for index URLs.
845    ///
846    /// At present, only `--keyring-provider subprocess` is supported, which configures uv to
847    /// use the `keyring` CLI to handle authentication.
848    #[option(
849        default = "\"disabled\"",
850        value_type = "str",
851        example = r#"
852            keyring-provider = "subprocess"
853        "#
854    )]
855    pub keyring_provider: Option<KeyringProviderType>,
856    /// The strategy to use when selecting between the different compatible versions for a given
857    /// package requirement.
858    ///
859    /// By default, uv will use the latest compatible version of each package (`highest`).
860    #[option(
861        default = "\"highest\"",
862        value_type = "str",
863        example = r#"
864            resolution = "lowest-direct"
865        "#,
866        possible_values = true
867    )]
868    pub resolution: Option<ResolutionMode>,
869    /// The strategy to use when considering pre-release versions.
870    ///
871    /// By default, uv will accept pre-releases for packages that _only_ publish pre-releases,
872    /// along with first-party requirements that contain an explicit pre-release marker in the
873    /// declared specifiers (`if-necessary-or-explicit`).
874    #[option(
875        default = "\"if-necessary-or-explicit\"",
876        value_type = "str",
877        example = r#"
878            prerelease = "allow"
879        "#,
880        possible_values = true
881    )]
882    pub prerelease: Option<PrereleaseMode>,
883    /// The strategy to use when selecting multiple versions of a given package across Python
884    /// versions and platforms.
885    ///
886    /// By default, uv will optimize for selecting the latest version of each package for each
887    /// supported Python version (`requires-python`), while minimizing the number of selected
888    /// versions across platforms.
889    ///
890    /// Under `fewest`, uv will minimize the number of selected versions for each package,
891    /// preferring older versions that are compatible with a wider range of supported Python
892    /// versions or platforms.
893    #[option(
894        default = "\"requires-python\"",
895        value_type = "str",
896        example = r#"
897            fork-strategy = "fewest"
898        "#,
899        possible_values = true
900    )]
901    pub fork_strategy: Option<ForkStrategy>,
902    /// Pre-defined static metadata for dependencies of the project (direct or transitive). When
903    /// provided, enables the resolver to use the specified metadata instead of querying the
904    /// registry or building the relevant package from source.
905    ///
906    /// Metadata should be provided in adherence with the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/)
907    /// standard, though only the following fields are respected:
908    ///
909    /// - `name`: The name of the package.
910    /// - (Optional) `version`: The version of the package. If omitted, the metadata will be applied
911    ///   to all versions of the package.
912    /// - (Optional) `requires-dist`: The dependencies of the package (e.g., `werkzeug>=0.14`).
913    /// - (Optional) `requires-python`: The Python version required by the package (e.g., `>=3.10`).
914    /// - (Optional) `provides-extra`: The extras provided by the package.
915    #[option(
916        default = r#"[]"#,
917        value_type = "list[dict]",
918        example = r#"
919            dependency-metadata = [
920                { name = "flask", version = "1.0.0", requires-dist = ["werkzeug"], requires-python = ">=3.6" },
921            ]
922        "#
923    )]
924    pub dependency_metadata: Option<Vec<StaticMetadata>>,
925    /// Settings to pass to the [PEP 517](https://peps.python.org/pep-0517/) build backend,
926    /// specified as `KEY=VALUE` pairs.
927    #[option(
928        default = "{}",
929        value_type = "dict",
930        example = r#"
931            config-settings = { editable_mode = "compat" }
932        "#
933    )]
934    pub config_settings: Option<ConfigSettings>,
935    /// Settings to pass to the [PEP 517](https://peps.python.org/pep-0517/) build backend for specific packages,
936    /// specified as `KEY=VALUE` pairs.
937    ///
938    /// Accepts a map from package names to string key-value pairs.
939    #[option(
940        default = "{}",
941        value_type = "dict",
942        example = r#"
943            config-settings-package = { numpy = { editable_mode = "compat" } }
944        "#
945    )]
946    pub config_settings_package: Option<PackageConfigSettings>,
947    /// Disable isolation when building source distributions.
948    ///
949    /// Assumes that build dependencies specified by [PEP 518](https://peps.python.org/pep-0518/)
950    /// are already installed.
951    #[option(
952        default = "false",
953        value_type = "bool",
954        example = r#"
955            no-build-isolation = true
956        "#
957    )]
958    pub no_build_isolation: Option<bool>,
959    /// Disable isolation when building source distributions for a specific package.
960    ///
961    /// Assumes that the packages' build dependencies specified by [PEP 518](https://peps.python.org/pep-0518/)
962    /// are already installed.
963    #[option(
964        default = "[]",
965        value_type = "list[str]",
966        example = r#"
967        no-build-isolation-package = ["package1", "package2"]
968    "#
969    )]
970    pub no_build_isolation_package: Option<Vec<PackageName>>,
971    /// Additional build dependencies for packages.
972    ///
973    /// This allows extending the PEP 517 build environment for the project's dependencies with
974    /// additional packages. This is useful for packages that assume the presence of packages like
975    /// `pip`, and do not declare them as build dependencies.
976    #[option(
977        default = "[]",
978        value_type = "dict",
979        example = r#"
980            extra-build-dependencies = { pytest = ["setuptools"] }
981        "#
982    )]
983    pub extra_build_dependencies: Option<ExtraBuildDependencies>,
984    /// Extra environment variables to set when building certain packages.
985    ///
986    /// Environment variables will be added to the environment when building the
987    /// specified packages.
988    #[option(
989        default = r#"{}"#,
990        value_type = r#"dict[str, dict[str, str]]"#,
991        example = r#"
992            extra-build-variables = { flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" } }
993        "#
994    )]
995    pub extra_build_variables: Option<ExtraBuildVariables>,
996    /// Limit candidate packages to those that were uploaded prior to the given date.
997    ///
998    /// The date is compared against the upload time of each individual distribution artifact
999    /// (i.e., when each file was uploaded to the package index), not the release date of the
1000    /// package version.
1001    ///
1002    /// Accepts RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`), a "friendly" duration (e.g.,
1003    /// `24 hours`, `1 week`, `30 days`), or an ISO 8601 duration (e.g., `PT24H`, `P7D`, `P30D`).
1004    ///
1005    /// Durations do not respect semantics of the local time zone and are always resolved to a fixed
1006    /// number of seconds assuming that a day is 24 hours (e.g., DST transitions are ignored).
1007    /// Calendar units such as months and years are not allowed.
1008    ///
1009    /// Set to `false` to disable `exclude-newer`.
1010    #[option(
1011        default = "None",
1012        value_type = "str | false",
1013        example = r#"
1014            exclude-newer = "2006-12-02T02:07:43Z"
1015        "#
1016    )]
1017    pub exclude_newer: Option<ExcludeNewerOverride>,
1018    /// Limit candidate packages for specific packages to those that were uploaded prior to the
1019    /// given date.
1020    ///
1021    /// Accepts a dictionary format of `PACKAGE = "DATE"` pairs, where `DATE` is an RFC 3339
1022    /// timestamp (e.g., `2006-12-02T02:07:43Z`), a "friendly" duration (e.g., `24 hours`, `1 week`,
1023    /// `30 days`), or a ISO 8601 duration (e.g., `PT24H`, `P7D`, `P30D`).
1024    ///
1025    /// Durations do not respect semantics of the local time zone and are always resolved to a fixed
1026    /// number of seconds assuming that a day is 24 hours (e.g., DST transitions are ignored).
1027    /// Calendar units such as months and years are not allowed.
1028    ///
1029    /// Set a package to `false` to exempt it from the global [`exclude-newer`](#exclude-newer)
1030    /// constraint entirely.
1031    #[option(
1032        default = "None",
1033        value_type = "dict",
1034        example = r#"
1035            exclude-newer-package = { tqdm = "2022-04-04T00:00:00Z", markupsafe = false }
1036        "#
1037    )]
1038    pub exclude_newer_package: Option<ExcludeNewerPackage>,
1039    /// The method to use when installing packages from the global cache.
1040    ///
1041    /// Defaults to `clone` (also known as Copy-on-Write) on macOS and Linux, and `hardlink` on
1042    /// Windows.
1043    ///
1044    /// WARNING: The use of symlink link mode is discouraged, as they create tight coupling between
1045    /// the cache and the target environment. For example, clearing the cache (`uv cache clean`)
1046    /// will break all installed packages by way of removing the underlying source files. Use
1047    /// symlinks with caution.
1048    #[option(
1049        default = "\"clone\" (macOS, Linux) or \"hardlink\" (Windows)",
1050        value_type = "str",
1051        example = r#"
1052            link-mode = "copy"
1053        "#,
1054        possible_values = true
1055    )]
1056    pub link_mode: Option<LinkMode>,
1057    /// Compile Python files to bytecode after installation.
1058    ///
1059    /// By default, uv does not compile Python (`.py`) files to bytecode (`__pycache__/*.pyc`);
1060    /// instead, compilation is performed lazily the first time a module is imported. For use-cases
1061    /// in which start time is critical, such as CLI applications and Docker containers, this option
1062    /// can be enabled to trade longer installation times for faster start times.
1063    ///
1064    /// When enabled, uv will process the entire site-packages directory (including packages that
1065    /// are not being modified by the current operation) for consistency. Like pip, it will also
1066    /// ignore errors.
1067    #[option(
1068        default = "false",
1069        value_type = "bool",
1070        example = r#"
1071            compile-bytecode = true
1072        "#
1073    )]
1074    pub compile_bytecode: Option<bool>,
1075    /// Ignore the `tool.uv.sources` table when resolving dependencies. Used to lock against the
1076    /// standards-compliant, publishable package metadata, as opposed to using any local or Git
1077    /// sources.
1078    #[option(
1079        default = "false",
1080        value_type = "bool",
1081        example = r#"
1082            no-sources = true
1083        "#
1084    )]
1085    pub no_sources: Option<bool>,
1086    /// Ignore `tool.uv.sources` for the specified packages.
1087    #[option(
1088        default = "[]",
1089        value_type = "list[str]",
1090        example = r#"
1091            no-sources-package = ["ruff"]
1092        "#
1093    )]
1094    pub no_sources_package: Option<Vec<PackageName>>,
1095    /// Allow package upgrades, ignoring pinned versions in any existing output file.
1096    #[option(
1097        default = "false",
1098        value_type = "bool",
1099        example = r#"
1100            upgrade = true
1101        "#
1102    )]
1103    pub upgrade: Option<bool>,
1104    /// Allow upgrades for a specific package, ignoring pinned versions in any existing output
1105    /// file.
1106    ///
1107    /// Accepts both standalone package names (`ruff`) and version specifiers (`ruff<0.5.0`).
1108    #[option(
1109        default = "[]",
1110        value_type = "list[str]",
1111        example = r#"
1112            upgrade-package = ["ruff"]
1113        "#
1114    )]
1115    pub upgrade_package: Option<Vec<Requirement<VerbatimParsedUrl>>>,
1116    /// Reinstall all packages, regardless of whether they're already installed. Implies `refresh`.
1117    #[option(
1118        default = "false",
1119        value_type = "bool",
1120        example = r#"
1121            reinstall = true
1122        "#
1123    )]
1124    pub reinstall: Option<bool>,
1125    /// Reinstall a specific package, regardless of whether it's already installed. Implies
1126    /// `refresh-package`.
1127    #[option(
1128        default = "[]",
1129        value_type = "list[str]",
1130        example = r#"
1131            reinstall-package = ["ruff"]
1132        "#
1133    )]
1134    pub reinstall_package: Option<Vec<PackageName>>,
1135    /// Don't build source distributions.
1136    ///
1137    /// When enabled, resolving will not run arbitrary Python code. The cached wheels of
1138    /// already-built source distributions will be reused, but operations that require building
1139    /// distributions will exit with an error.
1140    #[option(
1141        default = "false",
1142        value_type = "bool",
1143        example = r#"
1144            no-build = true
1145        "#
1146    )]
1147    pub no_build: Option<bool>,
1148    /// Don't build source distributions for a specific package.
1149    #[option(
1150        default = "[]",
1151        value_type = "list[str]",
1152        example = r#"
1153            no-build-package = ["ruff"]
1154        "#
1155    )]
1156    pub no_build_package: Option<Vec<PackageName>>,
1157    /// Don't install pre-built wheels.
1158    ///
1159    /// The given packages will be built and installed from source. The resolver will still use
1160    /// pre-built wheels to extract package metadata, if available.
1161    #[option(
1162        default = "false",
1163        value_type = "bool",
1164        example = r#"
1165            no-binary = true
1166        "#
1167    )]
1168    pub no_binary: Option<bool>,
1169    /// Don't install pre-built wheels for a specific package.
1170    #[option(
1171        default = "[]",
1172        value_type = "list[str]",
1173        example = r#"
1174            no-binary-package = ["ruff"]
1175        "#
1176    )]
1177    pub no_binary_package: Option<Vec<PackageName>>,
1178    /// The backend to use when fetching packages in the PyTorch ecosystem.
1179    ///
1180    /// When set, uv will ignore the configured index URLs for packages in the PyTorch ecosystem,
1181    /// and will instead use the defined backend.
1182    ///
1183    /// For example, when set to `cpu`, uv will use the CPU-only PyTorch index; when set to `cu126`,
1184    /// uv will use the PyTorch index for CUDA 12.6.
1185    ///
1186    /// The `auto` mode will attempt to detect the appropriate PyTorch index based on the currently
1187    /// installed CUDA drivers.
1188    ///
1189    /// This setting is only respected by `uv pip` commands.
1190    ///
1191    /// This option is in preview and may change in any future release.
1192    #[option(
1193        default = "null",
1194        value_type = "str",
1195        example = r#"
1196            torch-backend = "auto"
1197        "#
1198    )]
1199    pub torch_backend: Option<TorchMode>,
1200}
1201
1202/// Shared settings, relevant to all operations that might create managed python installations.
1203#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, CombineOptions, OptionsMetadata)]
1204#[serde(rename_all = "kebab-case")]
1205#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1206pub struct PythonInstallMirrors {
1207    /// Mirror URL for downloading managed Python installations.
1208    ///
1209    /// By default, managed Python installations are downloaded from [`python-build-standalone`](https://github.com/astral-sh/python-build-standalone).
1210    /// This variable can be set to a mirror URL to use a different source for Python installations.
1211    /// The provided URL will replace `https://github.com/astral-sh/python-build-standalone/releases/download` in, e.g., `https://github.com/astral-sh/python-build-standalone/releases/download/20240713/cpython-3.12.4%2B20240713-aarch64-apple-darwin-install_only.tar.gz`.
1212    ///
1213    /// Distributions can be read from a local directory by using the `file://` URL scheme.
1214    #[option(
1215        default = "None",
1216        value_type = "str",
1217        uv_toml_only = true,
1218        example = r#"
1219            python-install-mirror = "https://github.com/astral-sh/python-build-standalone/releases/download"
1220        "#
1221    )]
1222    pub python_install_mirror: Option<String>,
1223    /// Mirror URL to use for downloading managed PyPy installations.
1224    ///
1225    /// By default, managed PyPy installations are downloaded from [downloads.python.org](https://downloads.python.org/).
1226    /// This variable can be set to a mirror URL to use a different source for PyPy installations.
1227    /// The provided URL will replace `https://downloads.python.org/pypy` in, e.g., `https://downloads.python.org/pypy/pypy3.8-v7.3.7-osx64.tar.bz2`.
1228    ///
1229    /// Distributions can be read from a
1230    /// local directory by using the `file://` URL scheme.
1231    #[option(
1232        default = "None",
1233        value_type = "str",
1234        uv_toml_only = true,
1235        example = r#"
1236            pypy-install-mirror = "https://downloads.python.org/pypy"
1237        "#
1238    )]
1239    pub pypy_install_mirror: Option<String>,
1240
1241    /// URL pointing to JSON of custom Python installations.
1242    #[option(
1243        default = "None",
1244        value_type = "str",
1245        uv_toml_only = true,
1246        example = r#"
1247            python-downloads-json-url = "/etc/uv/python-downloads.json"
1248        "#
1249    )]
1250    pub python_downloads_json_url: Option<String>,
1251}
1252
1253impl PythonInstallMirrors {
1254    #[must_use]
1255    pub fn combine(self, other: Self) -> Self {
1256        Self {
1257            python_install_mirror: self.python_install_mirror.or(other.python_install_mirror),
1258            pypy_install_mirror: self.pypy_install_mirror.or(other.pypy_install_mirror),
1259            python_downloads_json_url: self
1260                .python_downloads_json_url
1261                .or(other.python_downloads_json_url),
1262        }
1263    }
1264}
1265
1266/// Settings that are specific to the `uv pip` command-line interface.
1267///
1268/// These values will be ignored when running commands outside the `uv pip` namespace (e.g.,
1269/// `uv lock`, `uvx`).
1270#[derive(Debug, Clone, Default, Deserialize, CombineOptions, OptionsMetadata)]
1271#[serde(deny_unknown_fields, rename_all = "kebab-case")]
1272#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1273pub struct PipOptions {
1274    /// The Python interpreter into which packages should be installed.
1275    ///
1276    /// By default, uv installs into the virtual environment in the current working directory or
1277    /// any parent directory. The `--python` option allows you to specify a different interpreter,
1278    /// which is intended for use in continuous integration (CI) environments or other automated
1279    /// workflows.
1280    ///
1281    /// Supported formats:
1282    /// - `3.10` looks for an installed Python 3.10 in the registry on Windows (see
1283    ///   `py --list-paths`), or `python3.10` on Linux and macOS.
1284    /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`.
1285    /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path.
1286    #[option(
1287        default = "None",
1288        value_type = "str",
1289        example = r#"
1290            python = "3.10"
1291        "#
1292    )]
1293    pub python: Option<String>,
1294    /// Install packages into the system Python environment.
1295    ///
1296    /// By default, uv installs into the virtual environment in the current working directory or
1297    /// any parent directory. The `--system` option instructs uv to instead use the first Python
1298    /// found in the system `PATH`.
1299    ///
1300    /// WARNING: `--system` is intended for use in continuous integration (CI) environments and
1301    /// should be used with caution, as it can modify the system Python installation.
1302    #[option(
1303        default = "false",
1304        value_type = "bool",
1305        example = r#"
1306            system = true
1307        "#
1308    )]
1309    pub system: Option<bool>,
1310    /// Allow uv to modify an `EXTERNALLY-MANAGED` Python installation.
1311    ///
1312    /// WARNING: `--break-system-packages` is intended for use in continuous integration (CI)
1313    /// environments, when installing into Python installations that are managed by an external
1314    /// package manager, like `apt`. It should be used with caution, as such Python installations
1315    /// explicitly recommend against modifications by other package managers (like uv or pip).
1316    #[option(
1317        default = "false",
1318        value_type = "bool",
1319        example = r#"
1320            break-system-packages = true
1321        "#
1322    )]
1323    pub break_system_packages: Option<bool>,
1324    /// Install packages into the specified directory, rather than into the virtual or system Python
1325    /// environment. The packages will be installed at the top-level of the directory.
1326    #[option(
1327        default = "None",
1328        value_type = "str",
1329        example = r#"
1330            target = "./target"
1331        "#
1332    )]
1333    pub target: Option<PathBuf>,
1334    /// Install packages into `lib`, `bin`, and other top-level folders under the specified
1335    /// directory, as if a virtual environment were present at that location.
1336    ///
1337    /// In general, prefer the use of `--python` to install into an alternate environment, as
1338    /// scripts and other artifacts installed via `--prefix` will reference the installing
1339    /// interpreter, rather than any interpreter added to the `--prefix` directory, rendering them
1340    /// non-portable.
1341    #[option(
1342        default = "None",
1343        value_type = "str",
1344        example = r#"
1345            prefix = "./prefix"
1346        "#
1347    )]
1348    pub prefix: Option<PathBuf>,
1349    #[serde(skip)]
1350    #[cfg_attr(feature = "schemars", schemars(skip))]
1351    pub index: Option<Vec<Index>>,
1352    /// The URL of the Python package index (by default: <https://pypi.org/simple>).
1353    ///
1354    /// Accepts either a repository compliant with [PEP 503](https://peps.python.org/pep-0503/)
1355    /// (the simple repository API), or a local directory laid out in the same format.
1356    ///
1357    /// The index provided by this setting is given lower priority than any indexes specified via
1358    /// [`extra_index_url`](#extra-index-url).
1359    #[option(
1360        default = "\"https://pypi.org/simple\"",
1361        value_type = "str",
1362        example = r#"
1363            index-url = "https://test.pypi.org/simple"
1364        "#
1365    )]
1366    pub index_url: Option<PipIndex>,
1367    /// Extra URLs of package indexes to use, in addition to `--index-url`.
1368    ///
1369    /// Accepts either a repository compliant with [PEP 503](https://peps.python.org/pep-0503/)
1370    /// (the simple repository API), or a local directory laid out in the same format.
1371    ///
1372    /// All indexes provided via this flag take priority over the index specified by
1373    /// [`index_url`](#index-url). When multiple indexes are provided, earlier values take priority.
1374    ///
1375    /// To control uv's resolution strategy when multiple indexes are present, see
1376    /// [`index_strategy`](#index-strategy).
1377    #[option(
1378        default = "[]",
1379        value_type = "list[str]",
1380        example = r#"
1381            extra-index-url = ["https://download.pytorch.org/whl/cpu"]
1382        "#
1383    )]
1384    pub extra_index_url: Option<Vec<PipExtraIndex>>,
1385    /// Ignore all registry indexes (e.g., PyPI), instead relying on direct URL dependencies and
1386    /// those provided via `--find-links`.
1387    #[option(
1388        default = "false",
1389        value_type = "bool",
1390        example = r#"
1391            no-index = true
1392        "#
1393    )]
1394    pub no_index: Option<bool>,
1395    /// Locations to search for candidate distributions, in addition to those found in the registry
1396    /// indexes.
1397    ///
1398    /// If a path, the target must be a directory that contains packages as wheel files (`.whl`) or
1399    /// source distributions (e.g., `.tar.gz` or `.zip`) at the top level.
1400    ///
1401    /// If a URL, the page must contain a flat list of links to package files adhering to the
1402    /// formats described above.
1403    #[option(
1404        default = "[]",
1405        value_type = "list[str]",
1406        example = r#"
1407            find-links = ["https://download.pytorch.org/whl/torch_stable.html"]
1408        "#
1409    )]
1410    pub find_links: Option<Vec<PipFindLinks>>,
1411    /// The strategy to use when resolving against multiple index URLs.
1412    ///
1413    /// By default, uv will stop at the first index on which a given package is available, and
1414    /// limit resolutions to those present on that first index (`first-index`). This prevents
1415    /// "dependency confusion" attacks, whereby an attacker can upload a malicious package under the
1416    /// same name to an alternate index.
1417    #[option(
1418        default = "\"first-index\"",
1419        value_type = "str",
1420        example = r#"
1421            index-strategy = "unsafe-best-match"
1422        "#,
1423        possible_values = true
1424    )]
1425    pub index_strategy: Option<IndexStrategy>,
1426    /// Attempt to use `keyring` for authentication for index URLs.
1427    ///
1428    /// At present, only `--keyring-provider subprocess` is supported, which configures uv to
1429    /// use the `keyring` CLI to handle authentication.
1430    #[option(
1431        default = "disabled",
1432        value_type = "str",
1433        example = r#"
1434            keyring-provider = "subprocess"
1435        "#
1436    )]
1437    pub keyring_provider: Option<KeyringProviderType>,
1438    /// Don't build source distributions.
1439    ///
1440    /// When enabled, resolving will not run arbitrary Python code. The cached wheels of
1441    /// already-built source distributions will be reused, but operations that require building
1442    /// distributions will exit with an error.
1443    ///
1444    /// Alias for `--only-binary :all:`.
1445    #[option(
1446        default = "false",
1447        value_type = "bool",
1448        example = r#"
1449            no-build = true
1450        "#
1451    )]
1452    pub no_build: Option<bool>,
1453    /// Don't install pre-built wheels.
1454    ///
1455    /// The given packages will be built and installed from source. The resolver will still use
1456    /// pre-built wheels to extract package metadata, if available.
1457    ///
1458    /// Multiple packages may be provided. Disable binaries for all packages with `:all:`.
1459    /// Clear previously specified packages with `:none:`.
1460    #[option(
1461        default = "[]",
1462        value_type = "list[str]",
1463        example = r#"
1464            no-binary = ["ruff"]
1465        "#
1466    )]
1467    pub no_binary: Option<Vec<PackageNameSpecifier>>,
1468    /// Only use pre-built wheels; don't build source distributions.
1469    ///
1470    /// When enabled, resolving will not run code from the given packages. The cached wheels of already-built
1471    /// source distributions will be reused, but operations that require building distributions will
1472    /// exit with an error.
1473    ///
1474    /// Multiple packages may be provided. Disable binaries for all packages with `:all:`.
1475    /// Clear previously specified packages with `:none:`.
1476    #[option(
1477        default = "[]",
1478        value_type = "list[str]",
1479        example = r#"
1480            only-binary = ["ruff"]
1481        "#
1482    )]
1483    pub only_binary: Option<Vec<PackageNameSpecifier>>,
1484    /// Disable isolation when building source distributions.
1485    ///
1486    /// Assumes that build dependencies specified by [PEP 518](https://peps.python.org/pep-0518/)
1487    /// are already installed.
1488    #[option(
1489        default = "false",
1490        value_type = "bool",
1491        example = r#"
1492            no-build-isolation = true
1493        "#
1494    )]
1495    pub no_build_isolation: Option<bool>,
1496    /// Disable isolation when building source distributions for a specific package.
1497    ///
1498    /// Assumes that the packages' build dependencies specified by [PEP 518](https://peps.python.org/pep-0518/)
1499    /// are already installed.
1500    #[option(
1501        default = "[]",
1502        value_type = "list[str]",
1503        example = r#"
1504            no-build-isolation-package = ["package1", "package2"]
1505        "#
1506    )]
1507    pub no_build_isolation_package: Option<Vec<PackageName>>,
1508    /// Additional build dependencies for packages.
1509    ///
1510    /// This allows extending the PEP 517 build environment for the project's dependencies with
1511    /// additional packages. This is useful for packages that assume the presence of packages like
1512    /// `pip`, and do not declare them as build dependencies.
1513    #[option(
1514        default = "[]",
1515        value_type = "dict",
1516        example = r#"
1517            extra-build-dependencies = { pytest = ["setuptools"] }
1518        "#
1519    )]
1520    pub extra_build_dependencies: Option<ExtraBuildDependencies>,
1521    /// Extra environment variables to set when building certain packages.
1522    ///
1523    /// Environment variables will be added to the environment when building the
1524    /// specified packages.
1525    #[option(
1526        default = r#"{}"#,
1527        value_type = r#"dict[str, dict[str, str]]"#,
1528        example = r#"
1529            extra-build-variables = { flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" } }
1530        "#
1531    )]
1532    pub extra_build_variables: Option<ExtraBuildVariables>,
1533    /// Validate the Python environment, to detect packages with missing dependencies and other
1534    /// issues.
1535    #[option(
1536        default = "false",
1537        value_type = "bool",
1538        example = r#"
1539            strict = true
1540        "#
1541    )]
1542    pub strict: Option<bool>,
1543    /// Include optional dependencies from the specified extra; may be provided more than once.
1544    ///
1545    /// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
1546    #[option(
1547        default = "[]",
1548        value_type = "list[str]",
1549        example = r#"
1550            extra = ["dev", "docs"]
1551        "#
1552    )]
1553    pub extra: Option<Vec<ExtraName>>,
1554    /// Include all optional dependencies.
1555    ///
1556    /// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
1557    #[option(
1558        default = "false",
1559        value_type = "bool",
1560        example = r#"
1561            all-extras = true
1562        "#
1563    )]
1564    pub all_extras: Option<bool>,
1565    /// Exclude the specified optional dependencies if `all-extras` is supplied.
1566    #[option(
1567        default = "[]",
1568        value_type = "list[str]",
1569        example = r#"
1570            all-extras = true
1571            no-extra = ["dev", "docs"]
1572        "#
1573    )]
1574    pub no_extra: Option<Vec<ExtraName>>,
1575    /// Ignore package dependencies, instead only add those packages explicitly listed
1576    /// on the command line to the resulting requirements file.
1577    #[option(
1578        default = "false",
1579        value_type = "bool",
1580        example = r#"
1581            no-deps = true
1582        "#
1583    )]
1584    pub no_deps: Option<bool>,
1585    /// Include the following dependency groups.
1586    #[option(
1587        default = "None",
1588        value_type = "list[str]",
1589        example = r#"
1590            group = ["dev", "docs"]
1591        "#
1592    )]
1593    pub group: Option<Vec<PipGroupName>>,
1594    /// Allow `uv pip sync` with empty requirements, which will clear the environment of all
1595    /// packages.
1596    #[option(
1597        default = "false",
1598        value_type = "bool",
1599        example = r#"
1600            allow-empty-requirements = true
1601        "#
1602    )]
1603    pub allow_empty_requirements: Option<bool>,
1604    /// The strategy to use when selecting between the different compatible versions for a given
1605    /// package requirement.
1606    ///
1607    /// By default, uv will use the latest compatible version of each package (`highest`).
1608    #[option(
1609        default = "\"highest\"",
1610        value_type = "str",
1611        example = r#"
1612            resolution = "lowest-direct"
1613        "#,
1614        possible_values = true
1615    )]
1616    pub resolution: Option<ResolutionMode>,
1617    /// The strategy to use when considering pre-release versions.
1618    ///
1619    /// By default, uv will accept pre-releases for packages that _only_ publish pre-releases,
1620    /// along with first-party requirements that contain an explicit pre-release marker in the
1621    /// declared specifiers (`if-necessary-or-explicit`).
1622    #[option(
1623        default = "\"if-necessary-or-explicit\"",
1624        value_type = "str",
1625        example = r#"
1626            prerelease = "allow"
1627        "#,
1628        possible_values = true
1629    )]
1630    pub prerelease: Option<PrereleaseMode>,
1631    /// The strategy to use when selecting multiple versions of a given package across Python
1632    /// versions and platforms.
1633    ///
1634    /// By default, uv will optimize for selecting the latest version of each package for each
1635    /// supported Python version (`requires-python`), while minimizing the number of selected
1636    /// versions across platforms.
1637    ///
1638    /// Under `fewest`, uv will minimize the number of selected versions for each package,
1639    /// preferring older versions that are compatible with a wider range of supported Python
1640    /// versions or platforms.
1641    #[option(
1642        default = "\"requires-python\"",
1643        value_type = "str",
1644        example = r#"
1645            fork-strategy = "fewest"
1646        "#,
1647        possible_values = true
1648    )]
1649    pub fork_strategy: Option<ForkStrategy>,
1650    /// Pre-defined static metadata for dependencies of the project (direct or transitive). When
1651    /// provided, enables the resolver to use the specified metadata instead of querying the
1652    /// registry or building the relevant package from source.
1653    ///
1654    /// Metadata should be provided in adherence with the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/)
1655    /// standard, though only the following fields are respected:
1656    ///
1657    /// - `name`: The name of the package.
1658    /// - (Optional) `version`: The version of the package. If omitted, the metadata will be applied
1659    ///   to all versions of the package.
1660    /// - (Optional) `requires-dist`: The dependencies of the package (e.g., `werkzeug>=0.14`).
1661    /// - (Optional) `requires-python`: The Python version required by the package (e.g., `>=3.10`).
1662    /// - (Optional) `provides-extra`: The extras provided by the package.
1663    #[option(
1664        default = r#"[]"#,
1665        value_type = "list[dict]",
1666        example = r#"
1667            dependency-metadata = [
1668                { name = "flask", version = "1.0.0", requires-dist = ["werkzeug"], requires-python = ">=3.6" },
1669            ]
1670        "#
1671    )]
1672    pub dependency_metadata: Option<Vec<StaticMetadata>>,
1673    /// Write the requirements generated by `uv pip compile` to the given `requirements.txt` file.
1674    ///
1675    /// If the file already exists, the existing versions will be preferred when resolving
1676    /// dependencies, unless `--upgrade` is also specified.
1677    #[option(
1678        default = "None",
1679        value_type = "str",
1680        example = r#"
1681            output-file = "requirements.txt"
1682        "#
1683    )]
1684    pub output_file: Option<PathBuf>,
1685    /// Include extras in the output file.
1686    ///
1687    /// By default, uv strips extras, as any packages pulled in by the extras are already included
1688    /// as dependencies in the output file directly. Further, output files generated with
1689    /// `--no-strip-extras` cannot be used as constraints files in `install` and `sync` invocations.
1690    #[option(
1691        default = "false",
1692        value_type = "bool",
1693        example = r#"
1694            no-strip-extras = true
1695        "#
1696    )]
1697    pub no_strip_extras: Option<bool>,
1698    /// Include environment markers in the output file generated by `uv pip compile`.
1699    ///
1700    /// By default, uv strips environment markers, as the resolution generated by `compile` is
1701    /// only guaranteed to be correct for the target environment.
1702    #[option(
1703        default = "false",
1704        value_type = "bool",
1705        example = r#"
1706            no-strip-markers = true
1707        "#
1708    )]
1709    pub no_strip_markers: Option<bool>,
1710    /// Exclude comment annotations indicating the source of each package from the output file
1711    /// generated by `uv pip compile`.
1712    #[option(
1713        default = "false",
1714        value_type = "bool",
1715        example = r#"
1716            no-annotate = true
1717        "#
1718    )]
1719    pub no_annotate: Option<bool>,
1720    /// Exclude the comment header at the top of output file generated by `uv pip compile`.
1721    #[option(
1722        default = r#"false"#,
1723        value_type = "bool",
1724        example = r#"
1725            no-header = true
1726        "#
1727    )]
1728    pub no_header: Option<bool>,
1729    /// The header comment to include at the top of the output file generated by `uv pip compile`.
1730    ///
1731    /// Used to reflect custom build scripts and commands that wrap `uv pip compile`.
1732    #[option(
1733        default = "None",
1734        value_type = "str",
1735        example = r#"
1736            custom-compile-command = "./custom-uv-compile.sh"
1737        "#
1738    )]
1739    pub custom_compile_command: Option<String>,
1740    /// Include distribution hashes in the output file.
1741    #[option(
1742        default = "false",
1743        value_type = "bool",
1744        example = r#"
1745            generate-hashes = true
1746        "#
1747    )]
1748    pub generate_hashes: Option<bool>,
1749    /// Settings to pass to the [PEP 517](https://peps.python.org/pep-0517/) build backend,
1750    /// specified as `KEY=VALUE` pairs.
1751    #[option(
1752        default = "{}",
1753        value_type = "dict",
1754        example = r#"
1755            config-settings = { editable_mode = "compat" }
1756        "#
1757    )]
1758    pub config_settings: Option<ConfigSettings>,
1759    /// Settings to pass to the [PEP 517](https://peps.python.org/pep-0517/) build backend for specific packages,
1760    /// specified as `KEY=VALUE` pairs.
1761    #[option(
1762        default = "{}",
1763        value_type = "dict",
1764        example = r#"
1765            config-settings-package = { numpy = { editable_mode = "compat" } }
1766        "#
1767    )]
1768    pub config_settings_package: Option<PackageConfigSettings>,
1769    /// The minimum Python version that should be supported by the resolved requirements (e.g.,
1770    /// `3.8` or `3.8.17`).
1771    ///
1772    /// If a patch version is omitted, the minimum patch version is assumed. For example, `3.8` is
1773    /// mapped to `3.8.0`.
1774    #[option(
1775        default = "None",
1776        value_type = "str",
1777        example = r#"
1778            python-version = "3.8"
1779        "#
1780    )]
1781    pub python_version: Option<PythonVersion>,
1782    /// The platform for which requirements should be resolved.
1783    ///
1784    /// Represented as a "target triple", a string that describes the target platform in terms of
1785    /// its CPU, vendor, and operating system name, like `x86_64-unknown-linux-gnu` or
1786    /// `aarch64-apple-darwin`.
1787    #[option(
1788        default = "None",
1789        value_type = "str",
1790        example = r#"
1791            python-platform = "x86_64-unknown-linux-gnu"
1792        "#
1793    )]
1794    pub python_platform: Option<TargetTriple>,
1795    /// Perform a universal resolution, attempting to generate a single `requirements.txt` output
1796    /// file that is compatible with all operating systems, architectures, and Python
1797    /// implementations.
1798    ///
1799    /// In universal mode, the current Python version (or user-provided `--python-version`) will be
1800    /// treated as a lower bound. For example, `--universal --python-version 3.7` would produce a
1801    /// universal resolution for Python 3.7 and later.
1802    #[option(
1803        default = "false",
1804        value_type = "bool",
1805        example = r#"
1806            universal = true
1807        "#
1808    )]
1809    pub universal: Option<bool>,
1810    /// Limit candidate packages to those that were uploaded prior to a given point in time.
1811    ///
1812    /// The date is compared against the upload time of each individual distribution artifact
1813    /// (i.e., when each file was uploaded to the package index), not the release date of the
1814    /// package version.
1815    ///
1816    /// Accepts RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`), a "friendly" duration (e.g.,
1817    /// `24 hours`, `1 week`, `30 days`), or an ISO 8601 duration (e.g., `PT24H`, `P7D`, `P30D`).
1818    ///
1819    /// Durations do not respect semantics of the local time zone and are always resolved to a fixed
1820    /// number of seconds assuming that a day is 24 hours (e.g., DST transitions are ignored).
1821    /// Calendar units such as months and years are not allowed.
1822    ///
1823    /// Set to `false` to disable `exclude-newer`.
1824    #[option(
1825        default = "None",
1826        value_type = "str | false",
1827        example = r#"
1828            exclude-newer = "2006-12-02T02:07:43Z"
1829        "#
1830    )]
1831    pub exclude_newer: Option<ExcludeNewerOverride>,
1832    /// Limit candidate packages for specific packages to those that were uploaded prior to the given date.
1833    ///
1834    /// Accepts a dictionary format of `PACKAGE = "DATE"` pairs, where `DATE` is an RFC 3339
1835    /// timestamp (e.g., `2006-12-02T02:07:43Z`), a "friendly" duration (e.g., `24 hours`, `1 week`,
1836    /// `30 days`), or a ISO 8601 duration (e.g., `PT24H`, `P7D`, `P30D`).
1837    ///
1838    /// Durations do not respect semantics of the local time zone and are always resolved to a fixed
1839    /// number of seconds assuming that a day is 24 hours (e.g., DST transitions are ignored).
1840    /// Calendar units such as months and years are not allowed.
1841    ///
1842    /// Set a package to `false` to exempt it from the global [`exclude-newer`](#exclude-newer)
1843    /// constraint entirely.
1844    #[option(
1845        default = "None",
1846        value_type = "dict",
1847        example = r#"
1848            exclude-newer-package = { tqdm = "2022-04-04T00:00:00Z", markupsafe = false }
1849        "#
1850    )]
1851    pub exclude_newer_package: Option<ExcludeNewerPackage>,
1852    /// Specify a package to omit from the output resolution. Its dependencies will still be
1853    /// included in the resolution. Equivalent to pip-compile's `--unsafe-package` option.
1854    #[option(
1855        default = "[]",
1856        value_type = "list[str]",
1857        example = r#"
1858            no-emit-package = ["ruff"]
1859        "#
1860    )]
1861    pub no_emit_package: Option<Vec<PackageName>>,
1862    /// Include `--index-url` and `--extra-index-url` entries in the output file generated by `uv pip compile`.
1863    #[option(
1864        default = "false",
1865        value_type = "bool",
1866        example = r#"
1867            emit-index-url = true
1868        "#
1869    )]
1870    pub emit_index_url: Option<bool>,
1871    /// Include `--find-links` entries in the output file generated by `uv pip compile`.
1872    #[option(
1873        default = "false",
1874        value_type = "bool",
1875        example = r#"
1876            emit-find-links = true
1877        "#
1878    )]
1879    pub emit_find_links: Option<bool>,
1880    /// Include `--no-binary` and `--only-binary` entries in the output file generated by `uv pip compile`.
1881    #[option(
1882        default = "false",
1883        value_type = "bool",
1884        example = r#"
1885            emit-build-options = true
1886        "#
1887    )]
1888    pub emit_build_options: Option<bool>,
1889    /// Whether to emit a marker string indicating the conditions under which the set of pinned
1890    /// dependencies is valid.
1891    ///
1892    /// The pinned dependencies may be valid even when the marker expression is
1893    /// false, but when the expression is true, the requirements are known to
1894    /// be correct.
1895    #[option(
1896        default = "false",
1897        value_type = "bool",
1898        example = r#"
1899            emit-marker-expression = true
1900        "#
1901    )]
1902    pub emit_marker_expression: Option<bool>,
1903    /// Include comment annotations indicating the index used to resolve each package (e.g.,
1904    /// `# from https://pypi.org/simple`).
1905    #[option(
1906        default = "false",
1907        value_type = "bool",
1908        example = r#"
1909            emit-index-annotation = true
1910        "#
1911    )]
1912    pub emit_index_annotation: Option<bool>,
1913    /// The style of the annotation comments included in the output file, used to indicate the
1914    /// source of each package.
1915    #[option(
1916        default = "\"split\"",
1917        value_type = "str",
1918        example = r#"
1919            annotation-style = "line"
1920        "#,
1921        possible_values = true
1922    )]
1923    pub annotation_style: Option<AnnotationStyle>,
1924    /// The method to use when installing packages from the global cache.
1925    ///
1926    /// Defaults to `clone` (also known as Copy-on-Write) on macOS and Linux, and `hardlink` on
1927    /// Windows.
1928    ///
1929    /// WARNING: The use of symlink link mode is discouraged, as they create tight coupling between
1930    /// the cache and the target environment. For example, clearing the cache (`uv cache clean`)
1931    /// will break all installed packages by way of removing the underlying source files. Use
1932    /// symlinks with caution.
1933    #[option(
1934        default = "\"clone\" (macOS, Linux) or \"hardlink\" (Windows)",
1935        value_type = "str",
1936        example = r#"
1937            link-mode = "copy"
1938        "#,
1939        possible_values = true
1940    )]
1941    pub link_mode: Option<LinkMode>,
1942    /// Compile Python files to bytecode after installation.
1943    ///
1944    /// By default, uv does not compile Python (`.py`) files to bytecode (`__pycache__/*.pyc`);
1945    /// instead, compilation is performed lazily the first time a module is imported. For use-cases
1946    /// in which start time is critical, such as CLI applications and Docker containers, this option
1947    /// can be enabled to trade longer installation times for faster start times.
1948    ///
1949    /// When enabled, uv will process the entire site-packages directory (including packages that
1950    /// are not being modified by the current operation) for consistency. Like pip, it will also
1951    /// ignore errors.
1952    #[option(
1953        default = "false",
1954        value_type = "bool",
1955        example = r#"
1956            compile-bytecode = true
1957        "#
1958    )]
1959    pub compile_bytecode: Option<bool>,
1960    /// Require a matching hash for each requirement.
1961    ///
1962    /// Hash-checking mode is all or nothing. If enabled, _all_ requirements must be provided
1963    /// with a corresponding hash or set of hashes. Additionally, if enabled, _all_ requirements
1964    /// must either be pinned to exact versions (e.g., `==1.0.0`), or be specified via direct URL.
1965    ///
1966    /// Hash-checking mode introduces a number of additional constraints:
1967    ///
1968    /// - Git dependencies are not supported.
1969    /// - Editable installations are not supported.
1970    /// - Local dependencies are not supported, unless they point to a specific wheel (`.whl`) or
1971    ///   source archive (`.zip`, `.tar.gz`), as opposed to a directory.
1972    #[option(
1973        default = "false",
1974        value_type = "bool",
1975        example = r#"
1976            require-hashes = true
1977        "#
1978    )]
1979    pub require_hashes: Option<bool>,
1980    /// Validate any hashes provided in the requirements file.
1981    ///
1982    /// Unlike `--require-hashes`, `--verify-hashes` does not require that all requirements have
1983    /// hashes; instead, it will limit itself to verifying the hashes of those requirements that do
1984    /// include them.
1985    #[option(
1986        default = "true",
1987        value_type = "bool",
1988        example = r#"
1989            verify-hashes = true
1990        "#
1991    )]
1992    pub verify_hashes: Option<bool>,
1993    /// Ignore the `tool.uv.sources` table when resolving dependencies. Used to lock against the
1994    /// standards-compliant, publishable package metadata, as opposed to using any local or Git
1995    /// sources.
1996    #[option(
1997        default = "false",
1998        value_type = "bool",
1999        example = r#"
2000            no-sources = true
2001        "#
2002    )]
2003    pub no_sources: Option<bool>,
2004    /// Ignore `tool.uv.sources` for the specified packages.
2005    #[option(
2006        default = "[]",
2007        value_type = "list[str]",
2008        example = r#"
2009            no-sources-package = ["ruff"]
2010        "#
2011    )]
2012    pub no_sources_package: Option<Vec<PackageName>>,
2013    /// Allow package upgrades, ignoring pinned versions in any existing output file.
2014    #[option(
2015        default = "false",
2016        value_type = "bool",
2017        example = r#"
2018            upgrade = true
2019        "#
2020    )]
2021    pub upgrade: Option<bool>,
2022    /// Allow upgrades for a specific package, ignoring pinned versions in any existing output
2023    /// file.
2024    ///
2025    /// Accepts both standalone package names (`ruff`) and version specifiers (`ruff<0.5.0`).
2026    #[option(
2027        default = "[]",
2028        value_type = "list[str]",
2029        example = r#"
2030            upgrade-package = ["ruff"]
2031        "#
2032    )]
2033    pub upgrade_package: Option<Vec<Requirement<VerbatimParsedUrl>>>,
2034    /// Reinstall all packages, regardless of whether they're already installed. Implies `refresh`.
2035    #[option(
2036        default = "false",
2037        value_type = "bool",
2038        example = r#"
2039            reinstall = true
2040        "#
2041    )]
2042    pub reinstall: Option<bool>,
2043    /// Reinstall a specific package, regardless of whether it's already installed. Implies
2044    /// `refresh-package`.
2045    #[option(
2046        default = "[]",
2047        value_type = "list[str]",
2048        example = r#"
2049            reinstall-package = ["ruff"]
2050        "#
2051    )]
2052    pub reinstall_package: Option<Vec<PackageName>>,
2053    /// The backend to use when fetching packages in the PyTorch ecosystem.
2054    ///
2055    /// When set, uv will ignore the configured index URLs for packages in the PyTorch ecosystem,
2056    /// and will instead use the defined backend.
2057    ///
2058    /// For example, when set to `cpu`, uv will use the CPU-only PyTorch index; when set to `cu126`,
2059    /// uv will use the PyTorch index for CUDA 12.6.
2060    ///
2061    /// The `auto` mode will attempt to detect the appropriate PyTorch index based on the currently
2062    /// installed CUDA drivers.
2063    ///
2064    /// This setting is only respected by `uv pip` commands.
2065    ///
2066    /// This option is in preview and may change in any future release.
2067    #[option(
2068        default = "null",
2069        value_type = "str",
2070        example = r#"
2071            torch-backend = "auto"
2072        "#
2073    )]
2074    pub torch_backend: Option<TorchMode>,
2075}
2076
2077impl PipOptions {
2078    /// Resolve the [`PipOptions`] relative to the given root directory.
2079    fn relative_to(self, root_dir: &Path) -> Result<Self, IndexUrlError> {
2080        Ok(Self {
2081            index: self
2082                .index
2083                .map(|index| {
2084                    index
2085                        .into_iter()
2086                        .map(|index| index.relative_to(root_dir))
2087                        .collect::<Result<Vec<_>, _>>()
2088                })
2089                .transpose()?,
2090            index_url: self
2091                .index_url
2092                .map(|index_url| index_url.relative_to(root_dir))
2093                .transpose()?,
2094            extra_index_url: self
2095                .extra_index_url
2096                .map(|extra_index_url| {
2097                    extra_index_url
2098                        .into_iter()
2099                        .map(|extra_index_url| extra_index_url.relative_to(root_dir))
2100                        .collect::<Result<Vec<_>, _>>()
2101                })
2102                .transpose()?,
2103            find_links: self
2104                .find_links
2105                .map(|find_links| {
2106                    find_links
2107                        .into_iter()
2108                        .map(|find_link| find_link.relative_to(root_dir))
2109                        .collect::<Result<Vec<_>, _>>()
2110                })
2111                .transpose()?,
2112            ..self
2113        })
2114    }
2115}
2116
2117impl From<ResolverInstallerSchema> for ResolverOptions {
2118    fn from(value: ResolverInstallerSchema) -> Self {
2119        Self {
2120            index: value.index,
2121            index_url: value.index_url,
2122            extra_index_url: value.extra_index_url,
2123            no_index: value.no_index,
2124            find_links: value.find_links,
2125            index_strategy: value.index_strategy,
2126            keyring_provider: value.keyring_provider,
2127            resolution: value.resolution,
2128            prerelease: value.prerelease,
2129            fork_strategy: value.fork_strategy,
2130            dependency_metadata: value.dependency_metadata,
2131            config_settings: value.config_settings,
2132            config_settings_package: value.config_settings_package,
2133            exclude_newer: value.exclude_newer,
2134            exclude_newer_package: value.exclude_newer_package,
2135            link_mode: value.link_mode,
2136            upgrade: Upgrade::from_args(
2137                value.upgrade,
2138                value
2139                    .upgrade_package
2140                    .into_iter()
2141                    .flatten()
2142                    .map(Into::into)
2143                    .collect(),
2144                Vec::new(),
2145            ),
2146            no_build: value.no_build,
2147            no_build_package: value.no_build_package,
2148            no_binary: value.no_binary,
2149            no_binary_package: value.no_binary_package,
2150            build_isolation: BuildIsolation::from_args(
2151                value.no_build_isolation,
2152                value.no_build_isolation_package.unwrap_or_default(),
2153            ),
2154            extra_build_dependencies: value.extra_build_dependencies,
2155            extra_build_variables: value.extra_build_variables,
2156            no_sources: value.no_sources,
2157            no_sources_package: value.no_sources_package,
2158            torch_backend: value.torch_backend,
2159        }
2160    }
2161}
2162
2163impl From<ResolverInstallerSchema> for InstallerOptions {
2164    fn from(value: ResolverInstallerSchema) -> Self {
2165        Self {
2166            index: value.index,
2167            index_url: value.index_url,
2168            extra_index_url: value.extra_index_url,
2169            no_index: value.no_index,
2170            find_links: value.find_links,
2171            index_strategy: value.index_strategy,
2172            keyring_provider: value.keyring_provider,
2173            config_settings: value.config_settings,
2174            exclude_newer: value.exclude_newer,
2175            link_mode: value.link_mode,
2176            compile_bytecode: value.compile_bytecode,
2177            reinstall: Reinstall::from_args(
2178                value.reinstall,
2179                value.reinstall_package.unwrap_or_default(),
2180            ),
2181            build_isolation: BuildIsolation::from_args(
2182                value.no_build_isolation,
2183                value.no_build_isolation_package.unwrap_or_default(),
2184            ),
2185            no_build: value.no_build,
2186            no_build_package: value.no_build_package,
2187            no_binary: value.no_binary,
2188            no_binary_package: value.no_binary_package,
2189            no_sources: value.no_sources,
2190            no_sources_package: value.no_sources_package,
2191        }
2192    }
2193}
2194
2195/// The options persisted alongside an installed tool.
2196///
2197/// A mirror of [`ResolverInstallerSchema`], without upgrades and reinstalls, which shouldn't be
2198/// persisted in a tool receipt.
2199#[derive(
2200    Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, CombineOptions, OptionsMetadata,
2201)]
2202#[serde(deny_unknown_fields, rename_all = "kebab-case")]
2203#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2204pub struct ToolOptions {
2205    index: Option<Vec<Index>>,
2206    index_url: Option<PipIndex>,
2207    extra_index_url: Option<Vec<PipExtraIndex>>,
2208    no_index: Option<bool>,
2209    find_links: Option<Vec<PipFindLinks>>,
2210    index_strategy: Option<IndexStrategy>,
2211    keyring_provider: Option<KeyringProviderType>,
2212    resolution: Option<ResolutionMode>,
2213    prerelease: Option<PrereleaseMode>,
2214    fork_strategy: Option<ForkStrategy>,
2215    dependency_metadata: Option<Vec<StaticMetadata>>,
2216    config_settings: Option<ConfigSettings>,
2217    config_settings_package: Option<PackageConfigSettings>,
2218    build_isolation: Option<BuildIsolation>,
2219    extra_build_dependencies: Option<ExtraBuildDependencies>,
2220    extra_build_variables: Option<ExtraBuildVariables>,
2221    exclude_newer: Option<ExcludeNewerOverride>,
2222    exclude_newer_package: Option<ExcludeNewerPackage>,
2223    link_mode: Option<LinkMode>,
2224    compile_bytecode: Option<bool>,
2225    no_sources: Option<bool>,
2226    no_sources_package: Option<Vec<PackageName>>,
2227    no_build: Option<bool>,
2228    no_build_package: Option<Vec<PackageName>>,
2229    no_binary: Option<bool>,
2230    no_binary_package: Option<Vec<PackageName>>,
2231    torch_backend: Option<TorchMode>,
2232}
2233
2234/// The on-disk representation of [`ToolOptions`] in a tool receipt.
2235#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2236#[serde(deny_unknown_fields, rename_all = "kebab-case")]
2237pub struct ToolOptionsWire {
2238    index: Option<Vec<Index>>,
2239    index_url: Option<PipIndex>,
2240    extra_index_url: Option<Vec<PipExtraIndex>>,
2241    no_index: Option<bool>,
2242    find_links: Option<Vec<PipFindLinks>>,
2243    index_strategy: Option<IndexStrategy>,
2244    keyring_provider: Option<KeyringProviderType>,
2245    resolution: Option<ResolutionMode>,
2246    prerelease: Option<PrereleaseMode>,
2247    fork_strategy: Option<ForkStrategy>,
2248    dependency_metadata: Option<Vec<StaticMetadata>>,
2249    config_settings: Option<ConfigSettings>,
2250    config_settings_package: Option<PackageConfigSettings>,
2251    build_isolation: Option<BuildIsolation>,
2252    extra_build_dependencies: Option<ExtraBuildDependencies>,
2253    extra_build_variables: Option<ExtraBuildVariables>,
2254    exclude_newer: Option<ExcludeNewerOverride>,
2255    exclude_newer_span: Option<ExcludeNewerSpan>,
2256    #[serde(serialize_with = "serialize_exclude_newer_package_with_spans")]
2257    exclude_newer_package: Option<ExcludeNewerPackage>,
2258    link_mode: Option<LinkMode>,
2259    compile_bytecode: Option<bool>,
2260    no_sources: Option<bool>,
2261    no_sources_package: Option<Vec<PackageName>>,
2262    no_build: Option<bool>,
2263    no_build_package: Option<Vec<PackageName>>,
2264    no_binary: Option<bool>,
2265    no_binary_package: Option<Vec<PackageName>>,
2266    torch_backend: Option<TorchMode>,
2267}
2268
2269impl From<ResolverInstallerOptions> for ToolOptions {
2270    fn from(value: ResolverInstallerOptions) -> Self {
2271        Self {
2272            index: value.index.map(|indexes| {
2273                indexes
2274                    .into_iter()
2275                    .map(Index::with_promoted_auth_policy)
2276                    .collect()
2277            }),
2278            index_url: value.index_url,
2279            extra_index_url: value.extra_index_url,
2280            no_index: value.no_index,
2281            find_links: value.find_links,
2282            index_strategy: value.index_strategy,
2283            keyring_provider: value.keyring_provider,
2284            resolution: value.resolution,
2285            prerelease: value.prerelease,
2286            fork_strategy: value.fork_strategy,
2287            dependency_metadata: value.dependency_metadata,
2288            config_settings: value.config_settings,
2289            config_settings_package: value.config_settings_package,
2290            build_isolation: value.build_isolation,
2291            extra_build_dependencies: value.extra_build_dependencies,
2292            extra_build_variables: value.extra_build_variables,
2293            exclude_newer: value.exclude_newer,
2294            exclude_newer_package: value.exclude_newer_package,
2295            link_mode: value.link_mode,
2296            compile_bytecode: value.compile_bytecode,
2297            no_sources: value.no_sources,
2298            no_sources_package: value.no_sources_package,
2299            no_build: value.no_build,
2300            no_build_package: value.no_build_package,
2301            no_binary: value.no_binary,
2302            no_binary_package: value.no_binary_package,
2303            torch_backend: value.torch_backend,
2304        }
2305    }
2306}
2307
2308impl From<ToolOptionsWire> for ToolOptions {
2309    fn from(value: ToolOptionsWire) -> Self {
2310        let exclude_newer = value
2311            .exclude_newer
2312            .map(|exclude_newer| match exclude_newer {
2313                ExcludeNewerOverride::Disabled => ExcludeNewerOverride::Disabled,
2314                ExcludeNewerOverride::Enabled(exclude_newer) => {
2315                    let exclude_newer = *exclude_newer;
2316                    if let Some(span) = value.exclude_newer_span
2317                        && exclude_newer.span().is_none()
2318                    {
2319                        ExcludeNewerValue::relative(span).into()
2320                    } else {
2321                        exclude_newer.into()
2322                    }
2323                }
2324            });
2325
2326        Self {
2327            index: value.index,
2328            index_url: value.index_url,
2329            extra_index_url: value.extra_index_url,
2330            no_index: value.no_index,
2331            find_links: value.find_links,
2332            index_strategy: value.index_strategy,
2333            keyring_provider: value.keyring_provider,
2334            resolution: value.resolution,
2335            prerelease: value.prerelease,
2336            fork_strategy: value.fork_strategy,
2337            dependency_metadata: value.dependency_metadata,
2338            config_settings: value.config_settings,
2339            config_settings_package: value.config_settings_package,
2340            build_isolation: value.build_isolation,
2341            extra_build_dependencies: value.extra_build_dependencies,
2342            extra_build_variables: value.extra_build_variables,
2343            exclude_newer,
2344            exclude_newer_package: value.exclude_newer_package,
2345            link_mode: value.link_mode,
2346            compile_bytecode: value.compile_bytecode,
2347            no_sources: value.no_sources,
2348            no_sources_package: value.no_sources_package,
2349            no_build: value.no_build,
2350            no_build_package: value.no_build_package,
2351            no_binary: value.no_binary,
2352            no_binary_package: value.no_binary_package,
2353            torch_backend: value.torch_backend,
2354        }
2355    }
2356}
2357
2358impl From<ToolOptions> for ToolOptionsWire {
2359    fn from(value: ToolOptions) -> Self {
2360        let (exclude_newer, exclude_newer_span) = match &value.exclude_newer {
2361            Some(ExcludeNewerOverride::Disabled) => (Some(ExcludeNewerOverride::Disabled), None),
2362            Some(ExcludeNewerOverride::Enabled(value)) => match value.as_ref() {
2363                ExcludeNewerValue::Absolute(_) => {
2364                    (Some(ExcludeNewerOverride::Enabled(value.clone())), None)
2365                }
2366                ExcludeNewerValue::Relative(span) => (
2367                    Some(ExcludeNewerValue::absolute(value.timestamp()).into()),
2368                    Some(*span),
2369                ),
2370            },
2371            None => (None, None),
2372        };
2373
2374        Self {
2375            index: value.index,
2376            index_url: value.index_url,
2377            extra_index_url: value.extra_index_url,
2378            no_index: value.no_index,
2379            find_links: value.find_links,
2380            index_strategy: value.index_strategy,
2381            keyring_provider: value.keyring_provider,
2382            resolution: value.resolution,
2383            prerelease: value.prerelease,
2384            fork_strategy: value.fork_strategy,
2385            dependency_metadata: value.dependency_metadata,
2386            config_settings: value.config_settings,
2387            config_settings_package: value.config_settings_package,
2388            build_isolation: value.build_isolation,
2389            extra_build_dependencies: value.extra_build_dependencies,
2390            extra_build_variables: value.extra_build_variables,
2391            exclude_newer,
2392            exclude_newer_span,
2393            exclude_newer_package: value.exclude_newer_package,
2394            link_mode: value.link_mode,
2395            compile_bytecode: value.compile_bytecode,
2396            no_sources: value.no_sources,
2397            no_sources_package: value.no_sources_package,
2398            no_build: value.no_build,
2399            no_build_package: value.no_build_package,
2400            no_binary: value.no_binary,
2401            no_binary_package: value.no_binary_package,
2402            torch_backend: value.torch_backend,
2403        }
2404    }
2405}
2406
2407impl From<ToolOptions> for ResolverInstallerOptions {
2408    fn from(value: ToolOptions) -> Self {
2409        Self {
2410            index: value.index,
2411            index_url: value.index_url,
2412            extra_index_url: value.extra_index_url,
2413            no_index: value.no_index,
2414            find_links: value.find_links,
2415            index_strategy: value.index_strategy,
2416            keyring_provider: value.keyring_provider,
2417            resolution: value.resolution,
2418            prerelease: value.prerelease,
2419            fork_strategy: value.fork_strategy,
2420            dependency_metadata: value.dependency_metadata,
2421            config_settings: value.config_settings,
2422            config_settings_package: value.config_settings_package,
2423            build_isolation: value.build_isolation,
2424            extra_build_dependencies: value.extra_build_dependencies,
2425            extra_build_variables: value.extra_build_variables,
2426            exclude_newer: value.exclude_newer,
2427            exclude_newer_package: value.exclude_newer_package,
2428            link_mode: value.link_mode,
2429            compile_bytecode: value.compile_bytecode,
2430            no_sources: value.no_sources,
2431            no_sources_package: value.no_sources_package,
2432            upgrade: None,
2433            reinstall: None,
2434            no_build: value.no_build,
2435            no_build_package: value.no_build_package,
2436            no_binary: value.no_binary,
2437            no_binary_package: value.no_binary_package,
2438            torch_backend: value.torch_backend,
2439        }
2440    }
2441}
2442
2443/// Like [`Options]`, but with any `#[serde(flatten)]` fields inlined. This leads to far, far
2444/// better error messages when deserializing.
2445#[derive(Debug, Clone, Default, Deserialize)]
2446#[serde(rename_all = "kebab-case", deny_unknown_fields)]
2447struct OptionsWire {
2448    // #[serde(flatten)]
2449    // globals: GlobalOptions
2450    required_version: Option<RequiredVersion>,
2451    system_certs: Option<bool>,
2452    native_tls: Option<bool>,
2453    offline: Option<bool>,
2454    no_cache: Option<bool>,
2455    cache_dir: Option<PathBuf>,
2456    preview: Option<bool>,
2457    preview_features: Option<PreviewFeaturesOption>,
2458    python_preference: Option<PythonPreference>,
2459    python_downloads: Option<PythonDownloads>,
2460    concurrent_downloads: Option<NonZeroUsize>,
2461    concurrent_builds: Option<NonZeroUsize>,
2462    concurrent_installs: Option<NonZeroUsize>,
2463
2464    // #[serde(flatten)]
2465    // top_level: ResolverInstallerOptions
2466    index: Option<Vec<Index>>,
2467    index_url: Option<PipIndex>,
2468    extra_index_url: Option<Vec<PipExtraIndex>>,
2469    no_index: Option<bool>,
2470    find_links: Option<Vec<PipFindLinks>>,
2471    index_strategy: Option<IndexStrategy>,
2472    keyring_provider: Option<KeyringProviderType>,
2473    http_proxy: Option<ProxyUrl>,
2474    https_proxy: Option<ProxyUrl>,
2475    no_proxy: Option<Vec<String>>,
2476    allow_insecure_host: Option<Vec<TrustedHost>>,
2477    resolution: Option<ResolutionMode>,
2478    prerelease: Option<PrereleaseMode>,
2479    fork_strategy: Option<ForkStrategy>,
2480    dependency_metadata: Option<Vec<StaticMetadata>>,
2481    config_settings: Option<ConfigSettings>,
2482    config_settings_package: Option<PackageConfigSettings>,
2483    no_build_isolation: Option<bool>,
2484    no_build_isolation_package: Option<Vec<PackageName>>,
2485    extra_build_dependencies: Option<ExtraBuildDependencies>,
2486    extra_build_variables: Option<ExtraBuildVariables>,
2487    exclude_newer: Option<ExcludeNewerOverride>,
2488    exclude_newer_package: Option<ExcludeNewerPackage>,
2489    link_mode: Option<LinkMode>,
2490    compile_bytecode: Option<bool>,
2491    no_sources: Option<bool>,
2492    no_sources_package: Option<Vec<PackageName>>,
2493    upgrade: Option<bool>,
2494    upgrade_package: Option<Vec<Requirement<VerbatimParsedUrl>>>,
2495    reinstall: Option<bool>,
2496    reinstall_package: Option<Vec<PackageName>>,
2497    no_build: Option<bool>,
2498    no_build_package: Option<Vec<PackageName>>,
2499    no_binary: Option<bool>,
2500    no_binary_package: Option<Vec<PackageName>>,
2501    torch_backend: Option<TorchMode>,
2502
2503    // #[serde(flatten)]
2504    // install_mirror: PythonInstallMirrors,
2505    python_install_mirror: Option<String>,
2506    pypy_install_mirror: Option<String>,
2507    python_downloads_json_url: Option<String>,
2508
2509    // #[serde(flatten)]
2510    // publish: PublishOptions
2511    publish_url: Option<DisplaySafeUrl>,
2512    trusted_publishing: Option<TrustedPublishing>,
2513    check_url: Option<IndexUrl>,
2514
2515    // #[serde(flatten)]
2516    // add: AddOptions
2517    add_bounds: Option<AddBoundsKind>,
2518
2519    audit: Option<AuditOptions>,
2520    pip: Option<PipOptions>,
2521    cache_keys: Option<Vec<CacheKey>>,
2522
2523    // NOTE(charlie): These fields are shared with `ToolUv` in
2524    // `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct.
2525    // They're respected in both `pyproject.toml` and `uv.toml` files.
2526    override_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
2527    exclude_dependencies: Option<Vec<PackageName>>,
2528    constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
2529    build_constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
2530    environments: Option<SupportedEnvironments>,
2531    required_environments: Option<SupportedEnvironments>,
2532
2533    // NOTE(charlie): These fields should be kept in-sync with `ToolUv` in
2534    // `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct.
2535    // They're only respected in `pyproject.toml` files, and should be rejected in `uv.toml` files.
2536    conflicts: Option<serde::de::IgnoredAny>,
2537    workspace: Option<serde::de::IgnoredAny>,
2538    sources: Option<serde::de::IgnoredAny>,
2539    managed: Option<serde::de::IgnoredAny>,
2540    r#package: Option<serde::de::IgnoredAny>,
2541    default_groups: Option<serde::de::IgnoredAny>,
2542    dependency_groups: Option<serde::de::IgnoredAny>,
2543    dev_dependencies: Option<serde::de::IgnoredAny>,
2544
2545    // Build backend
2546    build_backend: Option<serde::de::IgnoredAny>,
2547}
2548
2549impl TryFrom<OptionsWire> for Options {
2550    type Error = &'static str;
2551
2552    #[allow(deprecated)]
2553    fn try_from(value: OptionsWire) -> Result<Self, Self::Error> {
2554        let OptionsWire {
2555            required_version,
2556            system_certs,
2557            native_tls,
2558            offline,
2559            no_cache,
2560            cache_dir,
2561            preview,
2562            preview_features,
2563            python_preference,
2564            python_downloads,
2565            python_install_mirror,
2566            pypy_install_mirror,
2567            python_downloads_json_url,
2568            concurrent_downloads,
2569            concurrent_builds,
2570            concurrent_installs,
2571            index,
2572            index_url,
2573            extra_index_url,
2574            no_index,
2575            find_links,
2576            index_strategy,
2577            keyring_provider,
2578            http_proxy,
2579            https_proxy,
2580            no_proxy,
2581            allow_insecure_host,
2582            resolution,
2583            prerelease,
2584            fork_strategy,
2585            dependency_metadata,
2586            config_settings,
2587            config_settings_package,
2588            no_build_isolation,
2589            no_build_isolation_package,
2590            exclude_newer,
2591            exclude_newer_package,
2592            link_mode,
2593            compile_bytecode,
2594            no_sources,
2595            no_sources_package,
2596            upgrade,
2597            upgrade_package,
2598            reinstall,
2599            reinstall_package,
2600            no_build,
2601            no_build_package,
2602            no_binary,
2603            no_binary_package,
2604            torch_backend,
2605            audit,
2606            pip,
2607            cache_keys,
2608            override_dependencies,
2609            exclude_dependencies,
2610            constraint_dependencies,
2611            build_constraint_dependencies,
2612            environments,
2613            required_environments,
2614            conflicts,
2615            publish_url,
2616            trusted_publishing,
2617            check_url,
2618            workspace,
2619            sources,
2620            default_groups,
2621            dependency_groups,
2622            extra_build_dependencies,
2623            extra_build_variables,
2624            dev_dependencies,
2625            managed,
2626            package,
2627            add_bounds: bounds,
2628            // Used by the build backend
2629            build_backend,
2630        } = value;
2631
2632        Ok(Self {
2633            globals: GlobalOptions {
2634                required_version,
2635                system_certs,
2636                native_tls,
2637                offline,
2638                no_cache,
2639                cache_dir,
2640                preview: PreviewOption::try_from(preview, preview_features)?,
2641                python_preference,
2642                python_downloads,
2643                concurrent_downloads,
2644                concurrent_builds,
2645                concurrent_installs,
2646                http_proxy,
2647                https_proxy,
2648                no_proxy,
2649                // Used twice for backwards compatibility
2650                allow_insecure_host: allow_insecure_host.clone(),
2651            },
2652            top_level: ResolverInstallerSchema {
2653                index,
2654                index_url,
2655                extra_index_url,
2656                no_index,
2657                find_links,
2658                index_strategy,
2659                keyring_provider,
2660                resolution,
2661                prerelease,
2662                fork_strategy,
2663                dependency_metadata,
2664                config_settings,
2665                config_settings_package,
2666                no_build_isolation,
2667                no_build_isolation_package,
2668                extra_build_dependencies,
2669                extra_build_variables,
2670                exclude_newer,
2671                exclude_newer_package,
2672                link_mode,
2673                compile_bytecode,
2674                no_sources,
2675                no_sources_package,
2676                upgrade,
2677                upgrade_package,
2678                reinstall,
2679                reinstall_package,
2680                no_build,
2681                no_build_package,
2682                no_binary,
2683                no_binary_package,
2684                torch_backend,
2685            },
2686            pip,
2687            cache_keys,
2688            build_backend,
2689            override_dependencies,
2690            exclude_dependencies,
2691            constraint_dependencies,
2692            build_constraint_dependencies,
2693            environments,
2694            required_environments,
2695            install_mirrors: PythonInstallMirrors {
2696                python_install_mirror,
2697                pypy_install_mirror,
2698                python_downloads_json_url,
2699            },
2700            conflicts,
2701            publish: PublishOptions {
2702                publish_url,
2703                trusted_publishing,
2704                check_url,
2705            },
2706            add: AddOptions { add_bounds: bounds },
2707            audit,
2708            workspace,
2709            sources,
2710            dev_dependencies,
2711            default_groups,
2712            dependency_groups,
2713            managed,
2714            package,
2715        })
2716    }
2717}
2718
2719#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, CombineOptions, OptionsMetadata)]
2720#[serde(rename_all = "kebab-case")]
2721#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2722pub struct PublishOptions {
2723    /// The URL for publishing packages to the Python package index (by default:
2724    /// <https://upload.pypi.org/legacy/>).
2725    #[option(
2726        default = "\"https://upload.pypi.org/legacy/\"",
2727        value_type = "str",
2728        example = r#"
2729            publish-url = "https://test.pypi.org/legacy/"
2730        "#
2731    )]
2732    pub publish_url: Option<DisplaySafeUrl>,
2733
2734    /// Configure trusted publishing.
2735    ///
2736    /// By default, uv checks for trusted publishing when running in a supported environment, but
2737    /// ignores it if it isn't configured.
2738    ///
2739    /// uv's supported environments for trusted publishing include GitHub Actions and GitLab CI/CD.
2740    #[option(
2741        default = "automatic",
2742        value_type = "str",
2743        example = r#"
2744            trusted-publishing = "always"
2745        "#
2746    )]
2747    pub trusted_publishing: Option<TrustedPublishing>,
2748
2749    /// Check an index URL for existing files to skip duplicate uploads.
2750    ///
2751    /// This option allows retrying publishing that failed after only some, but not all files have
2752    /// been uploaded, and handles error due to parallel uploads of the same file.
2753    ///
2754    /// Before uploading, the index is checked. If the exact same file already exists in the index,
2755    /// the file will not be uploaded. If an error occurred during the upload, the index is checked
2756    /// again, to handle cases where the identical file was uploaded twice in parallel.
2757    ///
2758    /// The exact behavior will vary based on the index. When uploading to PyPI, uploading the same
2759    /// file succeeds even without `--check-url`, while most other indexes error.
2760    ///
2761    /// The index must provide one of the supported hashes (SHA-256, SHA-384, or SHA-512).
2762    #[option(
2763        default = "None",
2764        value_type = "str",
2765        example = r#"
2766            check-url = "https://test.pypi.org/simple"
2767        "#
2768    )]
2769    pub check_url: Option<IndexUrl>,
2770}
2771
2772#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, CombineOptions, OptionsMetadata)]
2773#[serde(rename_all = "kebab-case")]
2774#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2775pub struct AddOptions {
2776    /// The default version specifier when adding a dependency.
2777    ///
2778    /// When adding a dependency to the project, if no constraint or URL is provided, a constraint
2779    /// is added based on the latest compatible version of the package. By default, a lower bound
2780    /// constraint is used, e.g., `>=1.2.3`.
2781    ///
2782    /// When `--frozen` is provided, no resolution is performed, and dependencies are always added
2783    /// without constraints.
2784    ///
2785    /// This option is in preview and may change in any future release.
2786    #[option(
2787        default = "\"lower\"",
2788        value_type = "str",
2789        example = r#"
2790            add-bounds = "major"
2791        "#,
2792        possible_values = true
2793    )]
2794    pub add_bounds: Option<AddBoundsKind>,
2795}
2796
2797#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, CombineOptions, OptionsMetadata)]
2798#[serde(rename_all = "kebab-case")]
2799#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2800pub struct AuditOptions {
2801    /// A list of vulnerability IDs to ignore during auditing.
2802    ///
2803    /// Vulnerabilities matching any of the provided IDs (including aliases) will be excluded from
2804    /// the audit results.
2805    #[option(
2806        default = "[]",
2807        value_type = "list[str]",
2808        example = r#"
2809            ignore = ["PYSEC-2022-43017", "GHSA-5239-wwwm-4pmq"]
2810        "#
2811    )]
2812    pub ignore: Option<Vec<String>>,
2813
2814    /// A list of vulnerability IDs to ignore during auditing, but only while no fix is available.
2815    ///
2816    /// Vulnerabilities matching any of the provided IDs (including aliases) will be excluded from
2817    /// the audit results as long as they have no known fix versions. Once a fix version becomes
2818    /// available, the vulnerability will be reported again.
2819    #[option(
2820        default = "[]",
2821        value_type = "list[str]",
2822        example = r#"
2823            ignore-until-fixed = ["PYSEC-2022-43017"]
2824        "#
2825    )]
2826    pub ignore_until_fixed: Option<Vec<String>>,
2827}
2828
2829#[derive(Debug, Clone)]
2830pub struct MalwareCheckSettings {
2831    /// Whether the malware check is enabled.
2832    pub enabled: bool,
2833    /// The OSV-shaped service URL to use for malware checks.
2834    pub malware_check_url: Option<DisplaySafeUrl>,
2835}
2836
2837impl From<&crate::EnvironmentOptions> for MalwareCheckSettings {
2838    fn from(options: &crate::EnvironmentOptions) -> Self {
2839        Self {
2840            enabled: options.malware_check.value == Some(true),
2841            malware_check_url: options.malware_check_url.clone(),
2842        }
2843    }
2844}
2845
2846/// Represents the `preview-features` configuration option.
2847#[derive(Debug, Clone)]
2848#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2849#[cfg_attr(feature = "schemars", schemars(untagged))]
2850pub enum PreviewFeaturesOption {
2851    Toggle(bool),
2852    Features(Vec<MaybePreviewFeature>),
2853}
2854
2855// A derived `#[serde(untagged)]` implementation collapses detailed type and element errors into
2856// "data did not match any variant", so use a type-directed visitor to preserve useful diagnostics.
2857impl<'de> Deserialize<'de> for PreviewFeaturesOption {
2858    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2859    where
2860        D: serde::Deserializer<'de>,
2861    {
2862        serde_untagged::UntaggedEnumVisitor::new()
2863            .expecting("a boolean or a list of preview feature names")
2864            .bool(|value| Ok(Self::Toggle(value)))
2865            .seq(|sequence| sequence.deserialize().map(Self::Features))
2866            .deserialize(deserializer)
2867    }
2868}
2869
2870#[expect(
2871    dead_code,
2872    reason = "Fields are only used by the OptionsMetadata and JsonSchema derives"
2873)]
2874#[derive(OptionsMetadata)]
2875#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2876#[cfg_attr(feature = "schemars", schemars(rename_all = "kebab-case"))]
2877struct PreviewOptionsDefinition {
2878    // This legacy setting remains supported and included in the JSON schema, but is omitted from
2879    // option metadata so the generated settings reference documents only `preview-features`.
2880    /// Whether to enable all experimental, preview features.
2881    ///
2882    /// Use `preview-features` instead.
2883    #[deprecated(note = "use `preview-features` instead")]
2884    preview: Option<bool>,
2885    /// Whether to enable specific or all experimental preview features.
2886    ///
2887    /// Unknown feature names are ignored with a warning.
2888    #[option(
2889        default = "false",
2890        value_type = "bool | list[str]",
2891        example = r#"
2892            preview-features = true
2893            # or
2894            preview-features = ["python-upgrade"]
2895        "#
2896    )]
2897    preview_features: Option<PreviewFeaturesOption>,
2898}
2899
2900/// Represents the user's preview configuration from either `preview` or `preview-features`.
2901#[derive(Debug, Clone)]
2902pub enum PreviewOption {
2903    /// Whether to enable all experimental, preview features.
2904    Preview(bool),
2905    /// Whether to enable specific or all experimental preview features.
2906    PreviewFeatures(PreviewFeaturesOption),
2907}
2908
2909impl uv_options_metadata::OptionsMetadata for PreviewOption {
2910    fn record(visit: &mut dyn uv_options_metadata::Visit) {
2911        <PreviewOptionsDefinition as uv_options_metadata::OptionsMetadata>::record(visit);
2912    }
2913}
2914
2915#[cfg(feature = "schemars")]
2916impl schemars::JsonSchema for PreviewOption {
2917    fn schema_name() -> Cow<'static, str> {
2918        Cow::Borrowed("PreviewOption")
2919    }
2920
2921    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
2922        let mut schema = <PreviewOptionsDefinition as schemars::JsonSchema>::json_schema(generator);
2923        schema.insert(
2924            "not".to_string(),
2925            schemars::json_schema!({
2926                "required": ["preview", "preview-features"],
2927            })
2928            .into(),
2929        );
2930        schema
2931    }
2932}
2933
2934impl PreviewOption {
2935    fn try_from(
2936        preview: Option<bool>,
2937        preview_features: Option<PreviewFeaturesOption>,
2938    ) -> Result<Option<Self>, &'static str> {
2939        match (preview, preview_features) {
2940            (Some(_), Some(_)) => Err("cannot specify both `preview` and `preview-features`"),
2941            (Some(b), None) => Ok(Some(Self::Preview(b))),
2942            (None, Some(features)) => Ok(Some(Self::PreviewFeatures(features))),
2943            (None, None) => Ok(None),
2944        }
2945    }
2946
2947    /// Resolve the preview configuration, warning and ignoring unknown feature names.
2948    pub fn resolve(&self) -> Preview {
2949        use PreviewFeaturesOption::{Features, Toggle};
2950
2951        match self {
2952            Self::Preview(false) | Self::PreviewFeatures(Toggle(false)) => Preview::default(),
2953            Self::Preview(true) | Self::PreviewFeatures(Toggle(true)) => Preview::all(),
2954            Self::PreviewFeatures(Features(features)) => Preview::from_feature_names(features),
2955        }
2956    }
2957}