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, ExcludeDependency, IndexStrategy, KeyringProviderType, PackageNameSpecifier,
10    ProxyUrl, Reinstall, 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, OverrideDependency};
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<OverrideDependency>>,
150
151    #[cfg_attr(feature = "schemars", schemars(skip))]
152    pub exclude_dependencies: Option<Vec<ExcludeDependency>>,
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, uv will reuse cached wheels from previously built source distributions, but
1138    /// operations that require building a source distribution will exit with an error. uv may
1139    /// still build editable requirements, and their build backends may run arbitrary Python code.
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, uv will reuse cached wheels from previously built source distributions, but
1441    /// operations that require building a source distribution will exit with an error. uv may
1442    /// still build editable requirements, and their build backends may run arbitrary Python code.
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, uv will reuse cached wheels from previously built source distributions, but
1471    /// operations that require building a source distribution for the given packages will exit
1472    /// with an error. uv may still build editable requirements, and their build backends may run
1473    /// arbitrary Python code.
1474    ///
1475    /// Multiple packages may be provided. Disable binaries for all packages with `:all:`.
1476    /// Clear previously specified packages with `:none:`.
1477    #[option(
1478        default = "[]",
1479        value_type = "list[str]",
1480        example = r#"
1481            only-binary = ["ruff"]
1482        "#
1483    )]
1484    pub only_binary: Option<Vec<PackageNameSpecifier>>,
1485    /// Disable isolation when building source distributions.
1486    ///
1487    /// Assumes that build dependencies specified by [PEP 518](https://peps.python.org/pep-0518/)
1488    /// are already installed.
1489    #[option(
1490        default = "false",
1491        value_type = "bool",
1492        example = r#"
1493            no-build-isolation = true
1494        "#
1495    )]
1496    pub no_build_isolation: Option<bool>,
1497    /// Disable isolation when building source distributions for a specific package.
1498    ///
1499    /// Assumes that the packages' build dependencies specified by [PEP 518](https://peps.python.org/pep-0518/)
1500    /// are already installed.
1501    #[option(
1502        default = "[]",
1503        value_type = "list[str]",
1504        example = r#"
1505            no-build-isolation-package = ["package1", "package2"]
1506        "#
1507    )]
1508    pub no_build_isolation_package: Option<Vec<PackageName>>,
1509    /// Additional build dependencies for packages.
1510    ///
1511    /// This allows extending the PEP 517 build environment for the project's dependencies with
1512    /// additional packages. This is useful for packages that assume the presence of packages like
1513    /// `pip`, and do not declare them as build dependencies.
1514    #[option(
1515        default = "[]",
1516        value_type = "dict",
1517        example = r#"
1518            extra-build-dependencies = { pytest = ["setuptools"] }
1519        "#
1520    )]
1521    pub extra_build_dependencies: Option<ExtraBuildDependencies>,
1522    /// Extra environment variables to set when building certain packages.
1523    ///
1524    /// Environment variables will be added to the environment when building the
1525    /// specified packages.
1526    #[option(
1527        default = r#"{}"#,
1528        value_type = r#"dict[str, dict[str, str]]"#,
1529        example = r#"
1530            extra-build-variables = { flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" } }
1531        "#
1532    )]
1533    pub extra_build_variables: Option<ExtraBuildVariables>,
1534    /// Validate the Python environment, to detect packages with missing dependencies and other
1535    /// issues.
1536    #[option(
1537        default = "false",
1538        value_type = "bool",
1539        example = r#"
1540            strict = true
1541        "#
1542    )]
1543    pub strict: Option<bool>,
1544    /// Include optional dependencies from the specified extra; may be provided more than once.
1545    ///
1546    /// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
1547    #[option(
1548        default = "[]",
1549        value_type = "list[str]",
1550        example = r#"
1551            extra = ["dev", "docs"]
1552        "#
1553    )]
1554    pub extra: Option<Vec<ExtraName>>,
1555    /// Include all optional dependencies.
1556    ///
1557    /// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
1558    #[option(
1559        default = "false",
1560        value_type = "bool",
1561        example = r#"
1562            all-extras = true
1563        "#
1564    )]
1565    pub all_extras: Option<bool>,
1566    /// Exclude the specified optional dependencies if `all-extras` is supplied.
1567    #[option(
1568        default = "[]",
1569        value_type = "list[str]",
1570        example = r#"
1571            all-extras = true
1572            no-extra = ["dev", "docs"]
1573        "#
1574    )]
1575    pub no_extra: Option<Vec<ExtraName>>,
1576    /// Ignore package dependencies, instead only add those packages explicitly listed
1577    /// on the command line to the resulting requirements file.
1578    #[option(
1579        default = "false",
1580        value_type = "bool",
1581        example = r#"
1582            no-deps = true
1583        "#
1584    )]
1585    pub no_deps: Option<bool>,
1586    /// Include the following dependency groups.
1587    #[option(
1588        default = "None",
1589        value_type = "list[str]",
1590        example = r#"
1591            group = ["dev", "docs"]
1592        "#
1593    )]
1594    pub group: Option<Vec<PipGroupName>>,
1595    /// Allow `uv pip sync` with empty requirements, which will clear the environment of all
1596    /// packages.
1597    #[option(
1598        default = "false",
1599        value_type = "bool",
1600        example = r#"
1601            allow-empty-requirements = true
1602        "#
1603    )]
1604    pub allow_empty_requirements: Option<bool>,
1605    /// The strategy to use when selecting between the different compatible versions for a given
1606    /// package requirement.
1607    ///
1608    /// By default, uv will use the latest compatible version of each package (`highest`).
1609    #[option(
1610        default = "\"highest\"",
1611        value_type = "str",
1612        example = r#"
1613            resolution = "lowest-direct"
1614        "#,
1615        possible_values = true
1616    )]
1617    pub resolution: Option<ResolutionMode>,
1618    /// The strategy to use when considering pre-release versions.
1619    ///
1620    /// By default, uv will accept pre-releases for packages that _only_ publish pre-releases,
1621    /// along with first-party requirements that contain an explicit pre-release marker in the
1622    /// declared specifiers (`if-necessary-or-explicit`).
1623    #[option(
1624        default = "\"if-necessary-or-explicit\"",
1625        value_type = "str",
1626        example = r#"
1627            prerelease = "allow"
1628        "#,
1629        possible_values = true
1630    )]
1631    pub prerelease: Option<PrereleaseMode>,
1632    /// The strategy to use when selecting multiple versions of a given package across Python
1633    /// versions and platforms.
1634    ///
1635    /// By default, uv will optimize for selecting the latest version of each package for each
1636    /// supported Python version (`requires-python`), while minimizing the number of selected
1637    /// versions across platforms.
1638    ///
1639    /// Under `fewest`, uv will minimize the number of selected versions for each package,
1640    /// preferring older versions that are compatible with a wider range of supported Python
1641    /// versions or platforms.
1642    #[option(
1643        default = "\"requires-python\"",
1644        value_type = "str",
1645        example = r#"
1646            fork-strategy = "fewest"
1647        "#,
1648        possible_values = true
1649    )]
1650    pub fork_strategy: Option<ForkStrategy>,
1651    /// Pre-defined static metadata for dependencies of the project (direct or transitive). When
1652    /// provided, enables the resolver to use the specified metadata instead of querying the
1653    /// registry or building the relevant package from source.
1654    ///
1655    /// Metadata should be provided in adherence with the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/)
1656    /// standard, though only the following fields are respected:
1657    ///
1658    /// - `name`: The name of the package.
1659    /// - (Optional) `version`: The version of the package. If omitted, the metadata will be applied
1660    ///   to all versions of the package.
1661    /// - (Optional) `requires-dist`: The dependencies of the package (e.g., `werkzeug>=0.14`).
1662    /// - (Optional) `requires-python`: The Python version required by the package (e.g., `>=3.10`).
1663    /// - (Optional) `provides-extra`: The extras provided by the package.
1664    #[option(
1665        default = r#"[]"#,
1666        value_type = "list[dict]",
1667        example = r#"
1668            dependency-metadata = [
1669                { name = "flask", version = "1.0.0", requires-dist = ["werkzeug"], requires-python = ">=3.6" },
1670            ]
1671        "#
1672    )]
1673    pub dependency_metadata: Option<Vec<StaticMetadata>>,
1674    /// Write the requirements generated by `uv pip compile` to the given `requirements.txt` file.
1675    ///
1676    /// If the file already exists, the existing versions will be preferred when resolving
1677    /// dependencies, unless `--upgrade` is also specified.
1678    #[option(
1679        default = "None",
1680        value_type = "str",
1681        example = r#"
1682            output-file = "requirements.txt"
1683        "#
1684    )]
1685    pub output_file: Option<PathBuf>,
1686    /// Include extras in the output file.
1687    ///
1688    /// By default, uv strips extras, as any packages pulled in by the extras are already included
1689    /// as dependencies in the output file directly. Further, output files generated with
1690    /// `--no-strip-extras` cannot be used as constraints files in `install` and `sync` invocations.
1691    #[option(
1692        default = "false",
1693        value_type = "bool",
1694        example = r#"
1695            no-strip-extras = true
1696        "#
1697    )]
1698    pub no_strip_extras: Option<bool>,
1699    /// Include environment markers in the output file generated by `uv pip compile`.
1700    ///
1701    /// By default, uv strips environment markers, as the resolution generated by `compile` is
1702    /// only guaranteed to be correct for the target environment.
1703    #[option(
1704        default = "false",
1705        value_type = "bool",
1706        example = r#"
1707            no-strip-markers = true
1708        "#
1709    )]
1710    pub no_strip_markers: Option<bool>,
1711    /// Exclude comment annotations indicating the source of each package from the output file
1712    /// generated by `uv pip compile`.
1713    #[option(
1714        default = "false",
1715        value_type = "bool",
1716        example = r#"
1717            no-annotate = true
1718        "#
1719    )]
1720    pub no_annotate: Option<bool>,
1721    /// Exclude the comment header at the top of output file generated by `uv pip compile`.
1722    #[option(
1723        default = r#"false"#,
1724        value_type = "bool",
1725        example = r#"
1726            no-header = true
1727        "#
1728    )]
1729    pub no_header: Option<bool>,
1730    /// The header comment to include at the top of the output file generated by `uv pip compile`.
1731    ///
1732    /// Used to reflect custom build scripts and commands that wrap `uv pip compile`.
1733    #[option(
1734        default = "None",
1735        value_type = "str",
1736        example = r#"
1737            custom-compile-command = "./custom-uv-compile.sh"
1738        "#
1739    )]
1740    pub custom_compile_command: Option<String>,
1741    /// Include distribution hashes in the output file.
1742    #[option(
1743        default = "false",
1744        value_type = "bool",
1745        example = r#"
1746            generate-hashes = true
1747        "#
1748    )]
1749    pub generate_hashes: Option<bool>,
1750    /// Settings to pass to the [PEP 517](https://peps.python.org/pep-0517/) build backend,
1751    /// specified as `KEY=VALUE` pairs.
1752    #[option(
1753        default = "{}",
1754        value_type = "dict",
1755        example = r#"
1756            config-settings = { editable_mode = "compat" }
1757        "#
1758    )]
1759    pub config_settings: Option<ConfigSettings>,
1760    /// Settings to pass to the [PEP 517](https://peps.python.org/pep-0517/) build backend for specific packages,
1761    /// specified as `KEY=VALUE` pairs.
1762    #[option(
1763        default = "{}",
1764        value_type = "dict",
1765        example = r#"
1766            config-settings-package = { numpy = { editable_mode = "compat" } }
1767        "#
1768    )]
1769    pub config_settings_package: Option<PackageConfigSettings>,
1770    /// The minimum Python version that should be supported by the resolved requirements (e.g.,
1771    /// `3.8` or `3.8.17`).
1772    ///
1773    /// If a patch version is omitted, the minimum patch version is assumed. For example, `3.8` is
1774    /// mapped to `3.8.0`.
1775    #[option(
1776        default = "None",
1777        value_type = "str",
1778        example = r#"
1779            python-version = "3.8"
1780        "#
1781    )]
1782    pub python_version: Option<PythonVersion>,
1783    /// The platform for which requirements should be resolved.
1784    ///
1785    /// Represented as a "target triple", a string that describes the target platform in terms of
1786    /// its CPU, vendor, and operating system name, like `x86_64-unknown-linux-gnu` or
1787    /// `aarch64-apple-darwin`.
1788    #[option(
1789        default = "None",
1790        value_type = "str",
1791        example = r#"
1792            python-platform = "x86_64-unknown-linux-gnu"
1793        "#
1794    )]
1795    pub python_platform: Option<TargetTriple>,
1796    /// Perform a universal resolution, attempting to generate a single `requirements.txt` output
1797    /// file that is compatible with all operating systems, architectures, and Python
1798    /// implementations.
1799    ///
1800    /// In universal mode, the current Python version (or user-provided `--python-version`) will be
1801    /// treated as a lower bound. For example, `--universal --python-version 3.7` would produce a
1802    /// universal resolution for Python 3.7 and later.
1803    #[option(
1804        default = "false",
1805        value_type = "bool",
1806        example = r#"
1807            universal = true
1808        "#
1809    )]
1810    pub universal: Option<bool>,
1811    /// Limit candidate packages to those that were uploaded prior to a given point in time.
1812    ///
1813    /// The date is compared against the upload time of each individual distribution artifact
1814    /// (i.e., when each file was uploaded to the package index), not the release date of the
1815    /// package version.
1816    ///
1817    /// Accepts RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`), a "friendly" duration (e.g.,
1818    /// `24 hours`, `1 week`, `30 days`), or an ISO 8601 duration (e.g., `PT24H`, `P7D`, `P30D`).
1819    ///
1820    /// Durations do not respect semantics of the local time zone and are always resolved to a fixed
1821    /// number of seconds assuming that a day is 24 hours (e.g., DST transitions are ignored).
1822    /// Calendar units such as months and years are not allowed.
1823    ///
1824    /// Set to `false` to disable `exclude-newer`.
1825    #[option(
1826        default = "None",
1827        value_type = "str | false",
1828        example = r#"
1829            exclude-newer = "2006-12-02T02:07:43Z"
1830        "#
1831    )]
1832    pub exclude_newer: Option<ExcludeNewerOverride>,
1833    /// Limit candidate packages for specific packages to those that were uploaded prior to the given date.
1834    ///
1835    /// Accepts a dictionary format of `PACKAGE = "DATE"` pairs, where `DATE` is an RFC 3339
1836    /// timestamp (e.g., `2006-12-02T02:07:43Z`), a "friendly" duration (e.g., `24 hours`, `1 week`,
1837    /// `30 days`), or a ISO 8601 duration (e.g., `PT24H`, `P7D`, `P30D`).
1838    ///
1839    /// Durations do not respect semantics of the local time zone and are always resolved to a fixed
1840    /// number of seconds assuming that a day is 24 hours (e.g., DST transitions are ignored).
1841    /// Calendar units such as months and years are not allowed.
1842    ///
1843    /// Set a package to `false` to exempt it from the global [`exclude-newer`](#exclude-newer)
1844    /// constraint entirely.
1845    #[option(
1846        default = "None",
1847        value_type = "dict",
1848        example = r#"
1849            exclude-newer-package = { tqdm = "2022-04-04T00:00:00Z", markupsafe = false }
1850        "#
1851    )]
1852    pub exclude_newer_package: Option<ExcludeNewerPackage>,
1853    /// Specify a package to omit from the output resolution. Its dependencies will still be
1854    /// included in the resolution. Equivalent to pip-compile's `--unsafe-package` option.
1855    #[option(
1856        default = "[]",
1857        value_type = "list[str]",
1858        example = r#"
1859            no-emit-package = ["ruff"]
1860        "#
1861    )]
1862    pub no_emit_package: Option<Vec<PackageName>>,
1863    /// Include `--index-url` and `--extra-index-url` entries in the output file generated by `uv pip compile`.
1864    #[option(
1865        default = "false",
1866        value_type = "bool",
1867        example = r#"
1868            emit-index-url = true
1869        "#
1870    )]
1871    pub emit_index_url: Option<bool>,
1872    /// Include `--find-links` entries in the output file generated by `uv pip compile`.
1873    #[option(
1874        default = "false",
1875        value_type = "bool",
1876        example = r#"
1877            emit-find-links = true
1878        "#
1879    )]
1880    pub emit_find_links: Option<bool>,
1881    /// Include `--no-binary` and `--only-binary` entries in the output file generated by `uv pip compile`.
1882    #[option(
1883        default = "false",
1884        value_type = "bool",
1885        example = r#"
1886            emit-build-options = true
1887        "#
1888    )]
1889    pub emit_build_options: Option<bool>,
1890    /// Whether to emit a marker string indicating the conditions under which the set of pinned
1891    /// dependencies is valid.
1892    ///
1893    /// The pinned dependencies may be valid even when the marker expression is
1894    /// false, but when the expression is true, the requirements are known to
1895    /// be correct.
1896    #[option(
1897        default = "false",
1898        value_type = "bool",
1899        example = r#"
1900            emit-marker-expression = true
1901        "#
1902    )]
1903    pub emit_marker_expression: Option<bool>,
1904    /// Include comment annotations indicating the index used to resolve each package (e.g.,
1905    /// `# from https://pypi.org/simple`).
1906    #[option(
1907        default = "false",
1908        value_type = "bool",
1909        example = r#"
1910            emit-index-annotation = true
1911        "#
1912    )]
1913    pub emit_index_annotation: Option<bool>,
1914    /// The style of the annotation comments included in the output file, used to indicate the
1915    /// source of each package.
1916    #[option(
1917        default = "\"split\"",
1918        value_type = "str",
1919        example = r#"
1920            annotation-style = "line"
1921        "#,
1922        possible_values = true
1923    )]
1924    pub annotation_style: Option<AnnotationStyle>,
1925    /// The method to use when installing packages from the global cache.
1926    ///
1927    /// Defaults to `clone` (also known as Copy-on-Write) on macOS and Linux, and `hardlink` on
1928    /// Windows.
1929    ///
1930    /// WARNING: The use of symlink link mode is discouraged, as they create tight coupling between
1931    /// the cache and the target environment. For example, clearing the cache (`uv cache clean`)
1932    /// will break all installed packages by way of removing the underlying source files. Use
1933    /// symlinks with caution.
1934    #[option(
1935        default = "\"clone\" (macOS, Linux) or \"hardlink\" (Windows)",
1936        value_type = "str",
1937        example = r#"
1938            link-mode = "copy"
1939        "#,
1940        possible_values = true
1941    )]
1942    pub link_mode: Option<LinkMode>,
1943    /// Compile Python files to bytecode after installation.
1944    ///
1945    /// By default, uv does not compile Python (`.py`) files to bytecode (`__pycache__/*.pyc`);
1946    /// instead, compilation is performed lazily the first time a module is imported. For use-cases
1947    /// in which start time is critical, such as CLI applications and Docker containers, this option
1948    /// can be enabled to trade longer installation times for faster start times.
1949    ///
1950    /// When enabled, uv will process the entire site-packages directory (including packages that
1951    /// are not being modified by the current operation) for consistency. Like pip, it will also
1952    /// ignore errors.
1953    #[option(
1954        default = "false",
1955        value_type = "bool",
1956        example = r#"
1957            compile-bytecode = true
1958        "#
1959    )]
1960    pub compile_bytecode: Option<bool>,
1961    /// Require a matching hash for each requirement.
1962    ///
1963    /// Hash-checking mode is all or nothing. If enabled, _all_ requirements must be provided
1964    /// with a corresponding hash or set of hashes. Additionally, if enabled, _all_ requirements
1965    /// must either be pinned to exact versions (e.g., `==1.0.0`), or be specified via direct URL.
1966    ///
1967    /// Hash-checking mode introduces a number of additional constraints:
1968    ///
1969    /// - Git dependencies are not supported.
1970    /// - Editable installations are not supported.
1971    /// - Local dependencies are not supported, unless they point to a specific wheel (`.whl`) or
1972    ///   source archive (`.zip`, `.tar.gz`), as opposed to a directory.
1973    #[option(
1974        default = "false",
1975        value_type = "bool",
1976        example = r#"
1977            require-hashes = true
1978        "#
1979    )]
1980    pub require_hashes: Option<bool>,
1981    /// Validate any hashes provided in the requirements file.
1982    ///
1983    /// Unlike `--require-hashes`, `--verify-hashes` does not require that all requirements have
1984    /// hashes; instead, it will limit itself to verifying the hashes of those requirements that do
1985    /// include them.
1986    #[option(
1987        default = "true",
1988        value_type = "bool",
1989        example = r#"
1990            verify-hashes = true
1991        "#
1992    )]
1993    pub verify_hashes: Option<bool>,
1994    /// Ignore the `tool.uv.sources` table when resolving dependencies. Used to lock against the
1995    /// standards-compliant, publishable package metadata, as opposed to using any local or Git
1996    /// sources.
1997    #[option(
1998        default = "false",
1999        value_type = "bool",
2000        example = r#"
2001            no-sources = true
2002        "#
2003    )]
2004    pub no_sources: Option<bool>,
2005    /// Ignore `tool.uv.sources` for the specified packages.
2006    #[option(
2007        default = "[]",
2008        value_type = "list[str]",
2009        example = r#"
2010            no-sources-package = ["ruff"]
2011        "#
2012    )]
2013    pub no_sources_package: Option<Vec<PackageName>>,
2014    /// Allow package upgrades, ignoring pinned versions in any existing output file.
2015    #[option(
2016        default = "false",
2017        value_type = "bool",
2018        example = r#"
2019            upgrade = true
2020        "#
2021    )]
2022    pub upgrade: Option<bool>,
2023    /// Allow upgrades for a specific package, ignoring pinned versions in any existing output
2024    /// file.
2025    ///
2026    /// Accepts both standalone package names (`ruff`) and version specifiers (`ruff<0.5.0`).
2027    #[option(
2028        default = "[]",
2029        value_type = "list[str]",
2030        example = r#"
2031            upgrade-package = ["ruff"]
2032        "#
2033    )]
2034    pub upgrade_package: Option<Vec<Requirement<VerbatimParsedUrl>>>,
2035    /// Reinstall all packages, regardless of whether they're already installed. Implies `refresh`.
2036    #[option(
2037        default = "false",
2038        value_type = "bool",
2039        example = r#"
2040            reinstall = true
2041        "#
2042    )]
2043    pub reinstall: Option<bool>,
2044    /// Reinstall a specific package, regardless of whether it's already installed. Implies
2045    /// `refresh-package`.
2046    #[option(
2047        default = "[]",
2048        value_type = "list[str]",
2049        example = r#"
2050            reinstall-package = ["ruff"]
2051        "#
2052    )]
2053    pub reinstall_package: Option<Vec<PackageName>>,
2054    /// The backend to use when fetching packages in the PyTorch ecosystem.
2055    ///
2056    /// When set, uv will ignore the configured index URLs for packages in the PyTorch ecosystem,
2057    /// and will instead use the defined backend.
2058    ///
2059    /// For example, when set to `cpu`, uv will use the CPU-only PyTorch index; when set to `cu126`,
2060    /// uv will use the PyTorch index for CUDA 12.6.
2061    ///
2062    /// The `auto` mode will attempt to detect the appropriate PyTorch index based on the currently
2063    /// installed CUDA drivers.
2064    ///
2065    /// This setting is only respected by `uv pip` commands.
2066    ///
2067    /// This option is in preview and may change in any future release.
2068    #[option(
2069        default = "null",
2070        value_type = "str",
2071        example = r#"
2072            torch-backend = "auto"
2073        "#
2074    )]
2075    pub torch_backend: Option<TorchMode>,
2076}
2077
2078impl PipOptions {
2079    /// Resolve the [`PipOptions`] relative to the given root directory.
2080    fn relative_to(self, root_dir: &Path) -> Result<Self, IndexUrlError> {
2081        Ok(Self {
2082            index: self
2083                .index
2084                .map(|index| {
2085                    index
2086                        .into_iter()
2087                        .map(|index| index.relative_to(root_dir))
2088                        .collect::<Result<Vec<_>, _>>()
2089                })
2090                .transpose()?,
2091            index_url: self
2092                .index_url
2093                .map(|index_url| index_url.relative_to(root_dir))
2094                .transpose()?,
2095            extra_index_url: self
2096                .extra_index_url
2097                .map(|extra_index_url| {
2098                    extra_index_url
2099                        .into_iter()
2100                        .map(|extra_index_url| extra_index_url.relative_to(root_dir))
2101                        .collect::<Result<Vec<_>, _>>()
2102                })
2103                .transpose()?,
2104            find_links: self
2105                .find_links
2106                .map(|find_links| {
2107                    find_links
2108                        .into_iter()
2109                        .map(|find_link| find_link.relative_to(root_dir))
2110                        .collect::<Result<Vec<_>, _>>()
2111                })
2112                .transpose()?,
2113            ..self
2114        })
2115    }
2116}
2117
2118impl From<ResolverInstallerSchema> for ResolverOptions {
2119    fn from(value: ResolverInstallerSchema) -> Self {
2120        Self {
2121            index: value.index,
2122            index_url: value.index_url,
2123            extra_index_url: value.extra_index_url,
2124            no_index: value.no_index,
2125            find_links: value.find_links,
2126            index_strategy: value.index_strategy,
2127            keyring_provider: value.keyring_provider,
2128            resolution: value.resolution,
2129            prerelease: value.prerelease,
2130            fork_strategy: value.fork_strategy,
2131            dependency_metadata: value.dependency_metadata,
2132            config_settings: value.config_settings,
2133            config_settings_package: value.config_settings_package,
2134            exclude_newer: value.exclude_newer,
2135            exclude_newer_package: value.exclude_newer_package,
2136            link_mode: value.link_mode,
2137            upgrade: Upgrade::from_args(
2138                value.upgrade,
2139                value
2140                    .upgrade_package
2141                    .into_iter()
2142                    .flatten()
2143                    .map(Into::into)
2144                    .collect(),
2145                Vec::new(),
2146            ),
2147            no_build: value.no_build,
2148            no_build_package: value.no_build_package,
2149            no_binary: value.no_binary,
2150            no_binary_package: value.no_binary_package,
2151            build_isolation: BuildIsolation::from_args(
2152                value.no_build_isolation,
2153                value.no_build_isolation_package.unwrap_or_default(),
2154            ),
2155            extra_build_dependencies: value.extra_build_dependencies,
2156            extra_build_variables: value.extra_build_variables,
2157            no_sources: value.no_sources,
2158            no_sources_package: value.no_sources_package,
2159            torch_backend: value.torch_backend,
2160        }
2161    }
2162}
2163
2164impl From<ResolverInstallerSchema> for InstallerOptions {
2165    fn from(value: ResolverInstallerSchema) -> Self {
2166        Self {
2167            index: value.index,
2168            index_url: value.index_url,
2169            extra_index_url: value.extra_index_url,
2170            no_index: value.no_index,
2171            find_links: value.find_links,
2172            index_strategy: value.index_strategy,
2173            keyring_provider: value.keyring_provider,
2174            config_settings: value.config_settings,
2175            exclude_newer: value.exclude_newer,
2176            link_mode: value.link_mode,
2177            compile_bytecode: value.compile_bytecode,
2178            reinstall: Reinstall::from_args(
2179                value.reinstall,
2180                value.reinstall_package.unwrap_or_default(),
2181            ),
2182            build_isolation: BuildIsolation::from_args(
2183                value.no_build_isolation,
2184                value.no_build_isolation_package.unwrap_or_default(),
2185            ),
2186            no_build: value.no_build,
2187            no_build_package: value.no_build_package,
2188            no_binary: value.no_binary,
2189            no_binary_package: value.no_binary_package,
2190            no_sources: value.no_sources,
2191            no_sources_package: value.no_sources_package,
2192        }
2193    }
2194}
2195
2196/// The options persisted alongside an installed tool.
2197///
2198/// A mirror of [`ResolverInstallerSchema`], without upgrades and reinstalls, which shouldn't be
2199/// persisted in a tool receipt.
2200#[derive(
2201    Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, CombineOptions, OptionsMetadata,
2202)]
2203#[serde(deny_unknown_fields, rename_all = "kebab-case")]
2204#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2205pub struct ToolOptions {
2206    index: Option<Vec<Index>>,
2207    index_url: Option<PipIndex>,
2208    extra_index_url: Option<Vec<PipExtraIndex>>,
2209    no_index: Option<bool>,
2210    find_links: Option<Vec<PipFindLinks>>,
2211    index_strategy: Option<IndexStrategy>,
2212    keyring_provider: Option<KeyringProviderType>,
2213    resolution: Option<ResolutionMode>,
2214    prerelease: Option<PrereleaseMode>,
2215    fork_strategy: Option<ForkStrategy>,
2216    dependency_metadata: Option<Vec<StaticMetadata>>,
2217    config_settings: Option<ConfigSettings>,
2218    config_settings_package: Option<PackageConfigSettings>,
2219    build_isolation: Option<BuildIsolation>,
2220    extra_build_dependencies: Option<ExtraBuildDependencies>,
2221    extra_build_variables: Option<ExtraBuildVariables>,
2222    exclude_newer: Option<ExcludeNewerOverride>,
2223    exclude_newer_package: Option<ExcludeNewerPackage>,
2224    link_mode: Option<LinkMode>,
2225    compile_bytecode: Option<bool>,
2226    no_sources: Option<bool>,
2227    no_sources_package: Option<Vec<PackageName>>,
2228    no_build: Option<bool>,
2229    no_build_package: Option<Vec<PackageName>>,
2230    no_binary: Option<bool>,
2231    no_binary_package: Option<Vec<PackageName>>,
2232    torch_backend: Option<TorchMode>,
2233}
2234
2235/// The on-disk representation of [`ToolOptions`] in a tool receipt.
2236#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2237#[serde(deny_unknown_fields, rename_all = "kebab-case")]
2238pub struct ToolOptionsWire {
2239    index: Option<Vec<Index>>,
2240    index_url: Option<PipIndex>,
2241    extra_index_url: Option<Vec<PipExtraIndex>>,
2242    no_index: Option<bool>,
2243    find_links: Option<Vec<PipFindLinks>>,
2244    index_strategy: Option<IndexStrategy>,
2245    keyring_provider: Option<KeyringProviderType>,
2246    resolution: Option<ResolutionMode>,
2247    prerelease: Option<PrereleaseMode>,
2248    fork_strategy: Option<ForkStrategy>,
2249    dependency_metadata: Option<Vec<StaticMetadata>>,
2250    config_settings: Option<ConfigSettings>,
2251    config_settings_package: Option<PackageConfigSettings>,
2252    build_isolation: Option<BuildIsolation>,
2253    extra_build_dependencies: Option<ExtraBuildDependencies>,
2254    extra_build_variables: Option<ExtraBuildVariables>,
2255    exclude_newer: Option<ExcludeNewerOverride>,
2256    exclude_newer_span: Option<ExcludeNewerSpan>,
2257    #[serde(serialize_with = "serialize_exclude_newer_package_with_spans")]
2258    exclude_newer_package: Option<ExcludeNewerPackage>,
2259    link_mode: Option<LinkMode>,
2260    compile_bytecode: Option<bool>,
2261    no_sources: Option<bool>,
2262    no_sources_package: Option<Vec<PackageName>>,
2263    no_build: Option<bool>,
2264    no_build_package: Option<Vec<PackageName>>,
2265    no_binary: Option<bool>,
2266    no_binary_package: Option<Vec<PackageName>>,
2267    torch_backend: Option<TorchMode>,
2268}
2269
2270impl From<ResolverInstallerOptions> for ToolOptions {
2271    fn from(value: ResolverInstallerOptions) -> Self {
2272        Self {
2273            index: value.index.map(|indexes| {
2274                indexes
2275                    .into_iter()
2276                    .map(Index::with_promoted_auth_policy)
2277                    .collect()
2278            }),
2279            index_url: value.index_url,
2280            extra_index_url: value.extra_index_url,
2281            no_index: value.no_index,
2282            find_links: value.find_links,
2283            index_strategy: value.index_strategy,
2284            keyring_provider: value.keyring_provider,
2285            resolution: value.resolution,
2286            prerelease: value.prerelease,
2287            fork_strategy: value.fork_strategy,
2288            dependency_metadata: value.dependency_metadata,
2289            config_settings: value.config_settings,
2290            config_settings_package: value.config_settings_package,
2291            build_isolation: value.build_isolation,
2292            extra_build_dependencies: value.extra_build_dependencies,
2293            extra_build_variables: value.extra_build_variables,
2294            exclude_newer: value.exclude_newer,
2295            exclude_newer_package: value.exclude_newer_package,
2296            link_mode: value.link_mode,
2297            compile_bytecode: value.compile_bytecode,
2298            no_sources: value.no_sources,
2299            no_sources_package: value.no_sources_package,
2300            no_build: value.no_build,
2301            no_build_package: value.no_build_package,
2302            no_binary: value.no_binary,
2303            no_binary_package: value.no_binary_package,
2304            torch_backend: value.torch_backend,
2305        }
2306    }
2307}
2308
2309impl From<ToolOptionsWire> for ToolOptions {
2310    fn from(value: ToolOptionsWire) -> Self {
2311        let exclude_newer = value
2312            .exclude_newer
2313            .map(|exclude_newer| match exclude_newer {
2314                ExcludeNewerOverride::Disabled => ExcludeNewerOverride::Disabled,
2315                ExcludeNewerOverride::Enabled(exclude_newer) => {
2316                    let exclude_newer = *exclude_newer;
2317                    if let Some(span) = value.exclude_newer_span
2318                        && exclude_newer.span().is_none()
2319                    {
2320                        ExcludeNewerValue::relative(span).into()
2321                    } else {
2322                        exclude_newer.into()
2323                    }
2324                }
2325            });
2326
2327        Self {
2328            index: value.index,
2329            index_url: value.index_url,
2330            extra_index_url: value.extra_index_url,
2331            no_index: value.no_index,
2332            find_links: value.find_links,
2333            index_strategy: value.index_strategy,
2334            keyring_provider: value.keyring_provider,
2335            resolution: value.resolution,
2336            prerelease: value.prerelease,
2337            fork_strategy: value.fork_strategy,
2338            dependency_metadata: value.dependency_metadata,
2339            config_settings: value.config_settings,
2340            config_settings_package: value.config_settings_package,
2341            build_isolation: value.build_isolation,
2342            extra_build_dependencies: value.extra_build_dependencies,
2343            extra_build_variables: value.extra_build_variables,
2344            exclude_newer,
2345            exclude_newer_package: value.exclude_newer_package,
2346            link_mode: value.link_mode,
2347            compile_bytecode: value.compile_bytecode,
2348            no_sources: value.no_sources,
2349            no_sources_package: value.no_sources_package,
2350            no_build: value.no_build,
2351            no_build_package: value.no_build_package,
2352            no_binary: value.no_binary,
2353            no_binary_package: value.no_binary_package,
2354            torch_backend: value.torch_backend,
2355        }
2356    }
2357}
2358
2359impl From<ToolOptions> for ToolOptionsWire {
2360    fn from(value: ToolOptions) -> Self {
2361        let (exclude_newer, exclude_newer_span) = match &value.exclude_newer {
2362            Some(ExcludeNewerOverride::Disabled) => (Some(ExcludeNewerOverride::Disabled), None),
2363            Some(ExcludeNewerOverride::Enabled(value)) => match value.as_ref() {
2364                ExcludeNewerValue::Absolute(_) => {
2365                    (Some(ExcludeNewerOverride::Enabled(value.clone())), None)
2366                }
2367                ExcludeNewerValue::Relative(span) => (
2368                    Some(ExcludeNewerValue::absolute(value.timestamp()).into()),
2369                    Some(*span),
2370                ),
2371            },
2372            None => (None, None),
2373        };
2374
2375        Self {
2376            index: value.index,
2377            index_url: value.index_url,
2378            extra_index_url: value.extra_index_url,
2379            no_index: value.no_index,
2380            find_links: value.find_links,
2381            index_strategy: value.index_strategy,
2382            keyring_provider: value.keyring_provider,
2383            resolution: value.resolution,
2384            prerelease: value.prerelease,
2385            fork_strategy: value.fork_strategy,
2386            dependency_metadata: value.dependency_metadata,
2387            config_settings: value.config_settings,
2388            config_settings_package: value.config_settings_package,
2389            build_isolation: value.build_isolation,
2390            extra_build_dependencies: value.extra_build_dependencies,
2391            extra_build_variables: value.extra_build_variables,
2392            exclude_newer,
2393            exclude_newer_span,
2394            exclude_newer_package: value.exclude_newer_package,
2395            link_mode: value.link_mode,
2396            compile_bytecode: value.compile_bytecode,
2397            no_sources: value.no_sources,
2398            no_sources_package: value.no_sources_package,
2399            no_build: value.no_build,
2400            no_build_package: value.no_build_package,
2401            no_binary: value.no_binary,
2402            no_binary_package: value.no_binary_package,
2403            torch_backend: value.torch_backend,
2404        }
2405    }
2406}
2407
2408impl From<ToolOptions> for ResolverInstallerOptions {
2409    fn from(value: ToolOptions) -> Self {
2410        Self {
2411            index: value.index,
2412            index_url: value.index_url,
2413            extra_index_url: value.extra_index_url,
2414            no_index: value.no_index,
2415            find_links: value.find_links,
2416            index_strategy: value.index_strategy,
2417            keyring_provider: value.keyring_provider,
2418            resolution: value.resolution,
2419            prerelease: value.prerelease,
2420            fork_strategy: value.fork_strategy,
2421            dependency_metadata: value.dependency_metadata,
2422            config_settings: value.config_settings,
2423            config_settings_package: value.config_settings_package,
2424            build_isolation: value.build_isolation,
2425            extra_build_dependencies: value.extra_build_dependencies,
2426            extra_build_variables: value.extra_build_variables,
2427            exclude_newer: value.exclude_newer,
2428            exclude_newer_package: value.exclude_newer_package,
2429            link_mode: value.link_mode,
2430            compile_bytecode: value.compile_bytecode,
2431            no_sources: value.no_sources,
2432            no_sources_package: value.no_sources_package,
2433            upgrade: None,
2434            reinstall: None,
2435            no_build: value.no_build,
2436            no_build_package: value.no_build_package,
2437            no_binary: value.no_binary,
2438            no_binary_package: value.no_binary_package,
2439            torch_backend: value.torch_backend,
2440        }
2441    }
2442}
2443
2444/// Like [`Options]`, but with any `#[serde(flatten)]` fields inlined. This leads to far, far
2445/// better error messages when deserializing.
2446#[derive(Debug, Clone, Default, Deserialize)]
2447#[serde(rename_all = "kebab-case", deny_unknown_fields)]
2448struct OptionsWire {
2449    // #[serde(flatten)]
2450    // globals: GlobalOptions
2451    required_version: Option<RequiredVersion>,
2452    system_certs: Option<bool>,
2453    native_tls: Option<bool>,
2454    offline: Option<bool>,
2455    no_cache: Option<bool>,
2456    cache_dir: Option<PathBuf>,
2457    preview: Option<bool>,
2458    preview_features: Option<PreviewFeaturesOption>,
2459    python_preference: Option<PythonPreference>,
2460    python_downloads: Option<PythonDownloads>,
2461    concurrent_downloads: Option<NonZeroUsize>,
2462    concurrent_builds: Option<NonZeroUsize>,
2463    concurrent_installs: Option<NonZeroUsize>,
2464
2465    // #[serde(flatten)]
2466    // top_level: ResolverInstallerOptions
2467    index: Option<Vec<Index>>,
2468    index_url: Option<PipIndex>,
2469    extra_index_url: Option<Vec<PipExtraIndex>>,
2470    no_index: Option<bool>,
2471    find_links: Option<Vec<PipFindLinks>>,
2472    index_strategy: Option<IndexStrategy>,
2473    keyring_provider: Option<KeyringProviderType>,
2474    http_proxy: Option<ProxyUrl>,
2475    https_proxy: Option<ProxyUrl>,
2476    no_proxy: Option<Vec<String>>,
2477    allow_insecure_host: Option<Vec<TrustedHost>>,
2478    resolution: Option<ResolutionMode>,
2479    prerelease: Option<PrereleaseMode>,
2480    fork_strategy: Option<ForkStrategy>,
2481    dependency_metadata: Option<Vec<StaticMetadata>>,
2482    config_settings: Option<ConfigSettings>,
2483    config_settings_package: Option<PackageConfigSettings>,
2484    no_build_isolation: Option<bool>,
2485    no_build_isolation_package: Option<Vec<PackageName>>,
2486    extra_build_dependencies: Option<ExtraBuildDependencies>,
2487    extra_build_variables: Option<ExtraBuildVariables>,
2488    exclude_newer: Option<ExcludeNewerOverride>,
2489    exclude_newer_package: Option<ExcludeNewerPackage>,
2490    link_mode: Option<LinkMode>,
2491    compile_bytecode: Option<bool>,
2492    no_sources: Option<bool>,
2493    no_sources_package: Option<Vec<PackageName>>,
2494    upgrade: Option<bool>,
2495    upgrade_package: Option<Vec<Requirement<VerbatimParsedUrl>>>,
2496    reinstall: Option<bool>,
2497    reinstall_package: Option<Vec<PackageName>>,
2498    no_build: Option<bool>,
2499    no_build_package: Option<Vec<PackageName>>,
2500    no_binary: Option<bool>,
2501    no_binary_package: Option<Vec<PackageName>>,
2502    torch_backend: Option<TorchMode>,
2503
2504    // #[serde(flatten)]
2505    // install_mirror: PythonInstallMirrors,
2506    python_install_mirror: Option<String>,
2507    pypy_install_mirror: Option<String>,
2508    python_downloads_json_url: Option<String>,
2509
2510    // #[serde(flatten)]
2511    // publish: PublishOptions
2512    publish_url: Option<DisplaySafeUrl>,
2513    trusted_publishing: Option<TrustedPublishing>,
2514    check_url: Option<IndexUrl>,
2515
2516    // #[serde(flatten)]
2517    // add: AddOptions
2518    add_bounds: Option<AddBoundsKind>,
2519
2520    audit: Option<AuditOptions>,
2521    pip: Option<PipOptions>,
2522    cache_keys: Option<Vec<CacheKey>>,
2523
2524    // NOTE(charlie): These fields are shared with `ToolUv` in
2525    // `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct.
2526    // They're respected in both `pyproject.toml` and `uv.toml` files.
2527    override_dependencies: Option<Vec<OverrideDependency>>,
2528    exclude_dependencies: Option<Vec<ExcludeDependency>>,
2529    constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
2530    build_constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
2531    environments: Option<SupportedEnvironments>,
2532    required_environments: Option<SupportedEnvironments>,
2533
2534    // NOTE(charlie): These fields should be kept in-sync with `ToolUv` in
2535    // `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct.
2536    // They're only respected in `pyproject.toml` files, and should be rejected in `uv.toml` files.
2537    conflicts: Option<serde::de::IgnoredAny>,
2538    workspace: Option<serde::de::IgnoredAny>,
2539    sources: Option<serde::de::IgnoredAny>,
2540    managed: Option<serde::de::IgnoredAny>,
2541    r#package: Option<serde::de::IgnoredAny>,
2542    default_groups: Option<serde::de::IgnoredAny>,
2543    dependency_groups: Option<serde::de::IgnoredAny>,
2544    dev_dependencies: Option<serde::de::IgnoredAny>,
2545
2546    // Build backend
2547    build_backend: Option<serde::de::IgnoredAny>,
2548}
2549
2550impl TryFrom<OptionsWire> for Options {
2551    type Error = &'static str;
2552
2553    #[allow(deprecated)]
2554    fn try_from(value: OptionsWire) -> Result<Self, Self::Error> {
2555        let OptionsWire {
2556            required_version,
2557            system_certs,
2558            native_tls,
2559            offline,
2560            no_cache,
2561            cache_dir,
2562            preview,
2563            preview_features,
2564            python_preference,
2565            python_downloads,
2566            python_install_mirror,
2567            pypy_install_mirror,
2568            python_downloads_json_url,
2569            concurrent_downloads,
2570            concurrent_builds,
2571            concurrent_installs,
2572            index,
2573            index_url,
2574            extra_index_url,
2575            no_index,
2576            find_links,
2577            index_strategy,
2578            keyring_provider,
2579            http_proxy,
2580            https_proxy,
2581            no_proxy,
2582            allow_insecure_host,
2583            resolution,
2584            prerelease,
2585            fork_strategy,
2586            dependency_metadata,
2587            config_settings,
2588            config_settings_package,
2589            no_build_isolation,
2590            no_build_isolation_package,
2591            exclude_newer,
2592            exclude_newer_package,
2593            link_mode,
2594            compile_bytecode,
2595            no_sources,
2596            no_sources_package,
2597            upgrade,
2598            upgrade_package,
2599            reinstall,
2600            reinstall_package,
2601            no_build,
2602            no_build_package,
2603            no_binary,
2604            no_binary_package,
2605            torch_backend,
2606            audit,
2607            pip,
2608            cache_keys,
2609            override_dependencies,
2610            exclude_dependencies,
2611            constraint_dependencies,
2612            build_constraint_dependencies,
2613            environments,
2614            required_environments,
2615            conflicts,
2616            publish_url,
2617            trusted_publishing,
2618            check_url,
2619            workspace,
2620            sources,
2621            default_groups,
2622            dependency_groups,
2623            extra_build_dependencies,
2624            extra_build_variables,
2625            dev_dependencies,
2626            managed,
2627            package,
2628            add_bounds: bounds,
2629            // Used by the build backend
2630            build_backend,
2631        } = value;
2632
2633        Ok(Self {
2634            globals: GlobalOptions {
2635                required_version,
2636                system_certs,
2637                native_tls,
2638                offline,
2639                no_cache,
2640                cache_dir,
2641                preview: PreviewOption::try_from(preview, preview_features)?,
2642                python_preference,
2643                python_downloads,
2644                concurrent_downloads,
2645                concurrent_builds,
2646                concurrent_installs,
2647                http_proxy,
2648                https_proxy,
2649                no_proxy,
2650                // Used twice for backwards compatibility
2651                allow_insecure_host: allow_insecure_host.clone(),
2652            },
2653            top_level: ResolverInstallerSchema {
2654                index,
2655                index_url,
2656                extra_index_url,
2657                no_index,
2658                find_links,
2659                index_strategy,
2660                keyring_provider,
2661                resolution,
2662                prerelease,
2663                fork_strategy,
2664                dependency_metadata,
2665                config_settings,
2666                config_settings_package,
2667                no_build_isolation,
2668                no_build_isolation_package,
2669                extra_build_dependencies,
2670                extra_build_variables,
2671                exclude_newer,
2672                exclude_newer_package,
2673                link_mode,
2674                compile_bytecode,
2675                no_sources,
2676                no_sources_package,
2677                upgrade,
2678                upgrade_package,
2679                reinstall,
2680                reinstall_package,
2681                no_build,
2682                no_build_package,
2683                no_binary,
2684                no_binary_package,
2685                torch_backend,
2686            },
2687            pip,
2688            cache_keys,
2689            build_backend,
2690            override_dependencies,
2691            exclude_dependencies,
2692            constraint_dependencies,
2693            build_constraint_dependencies,
2694            environments,
2695            required_environments,
2696            install_mirrors: PythonInstallMirrors {
2697                python_install_mirror,
2698                pypy_install_mirror,
2699                python_downloads_json_url,
2700            },
2701            conflicts,
2702            publish: PublishOptions {
2703                publish_url,
2704                trusted_publishing,
2705                check_url,
2706            },
2707            add: AddOptions { add_bounds: bounds },
2708            audit,
2709            workspace,
2710            sources,
2711            dev_dependencies,
2712            default_groups,
2713            dependency_groups,
2714            managed,
2715            package,
2716        })
2717    }
2718}
2719
2720#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, CombineOptions, OptionsMetadata)]
2721#[serde(rename_all = "kebab-case")]
2722#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2723pub struct PublishOptions {
2724    /// The URL for publishing packages to the Python package index (by default:
2725    /// <https://upload.pypi.org/legacy/>).
2726    #[option(
2727        default = "\"https://upload.pypi.org/legacy/\"",
2728        value_type = "str",
2729        example = r#"
2730            publish-url = "https://test.pypi.org/legacy/"
2731        "#
2732    )]
2733    pub publish_url: Option<DisplaySafeUrl>,
2734
2735    /// Configure trusted publishing.
2736    ///
2737    /// By default, uv checks for trusted publishing when running in a supported environment, but
2738    /// ignores it if it isn't configured.
2739    ///
2740    /// uv's supported environments for trusted publishing include GitHub Actions and GitLab CI/CD.
2741    #[option(
2742        default = "automatic",
2743        value_type = "str",
2744        example = r#"
2745            trusted-publishing = "always"
2746        "#
2747    )]
2748    pub trusted_publishing: Option<TrustedPublishing>,
2749
2750    /// Check an index URL for existing files to skip duplicate uploads.
2751    ///
2752    /// This option allows retrying publishing that failed after only some, but not all files have
2753    /// been uploaded, and handles error due to parallel uploads of the same file.
2754    ///
2755    /// Before uploading, the index is checked. If the exact same file already exists in the index,
2756    /// the file will not be uploaded. If an error occurred during the upload, the index is checked
2757    /// again, to handle cases where the identical file was uploaded twice in parallel.
2758    ///
2759    /// The exact behavior will vary based on the index. When uploading to PyPI, uploading the same
2760    /// file succeeds even without `--check-url`, while most other indexes error.
2761    ///
2762    /// The index must provide one of the supported hashes (SHA-256, SHA-384, or SHA-512).
2763    #[option(
2764        default = "None",
2765        value_type = "str",
2766        example = r#"
2767            check-url = "https://test.pypi.org/simple"
2768        "#
2769    )]
2770    pub check_url: Option<IndexUrl>,
2771}
2772
2773#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, CombineOptions, OptionsMetadata)]
2774#[serde(rename_all = "kebab-case")]
2775#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2776pub struct AddOptions {
2777    /// The default version specifier when adding a dependency.
2778    ///
2779    /// When adding a dependency to the project, if no constraint or URL is provided, a constraint
2780    /// is added based on the latest compatible version of the package. By default, a lower bound
2781    /// constraint is used, e.g., `>=1.2.3`.
2782    ///
2783    /// When `--frozen` is provided, no resolution is performed, and dependencies are always added
2784    /// without constraints.
2785    ///
2786    /// This option is in preview and may change in any future release.
2787    #[option(
2788        default = "\"lower\"",
2789        value_type = "str",
2790        example = r#"
2791            add-bounds = "major"
2792        "#,
2793        possible_values = true
2794    )]
2795    pub add_bounds: Option<AddBoundsKind>,
2796}
2797
2798#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, CombineOptions, OptionsMetadata)]
2799#[serde(rename_all = "kebab-case")]
2800#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2801pub struct AuditOptions {
2802    /// A list of vulnerability IDs to ignore during auditing.
2803    ///
2804    /// Vulnerabilities matching any of the provided IDs (including aliases) will be excluded from
2805    /// the audit results.
2806    #[option(
2807        default = "[]",
2808        value_type = "list[str]",
2809        example = r#"
2810            ignore = ["PYSEC-2022-43017", "GHSA-5239-wwwm-4pmq"]
2811        "#
2812    )]
2813    pub ignore: Option<Vec<String>>,
2814
2815    /// A list of vulnerability IDs to ignore during auditing, but only while no fix is available.
2816    ///
2817    /// Vulnerabilities matching any of the provided IDs (including aliases) will be excluded from
2818    /// the audit results as long as they have no known fix versions. Once a fix version becomes
2819    /// available, the vulnerability will be reported again.
2820    #[option(
2821        default = "[]",
2822        value_type = "list[str]",
2823        example = r#"
2824            ignore-until-fixed = ["PYSEC-2022-43017"]
2825        "#
2826    )]
2827    pub ignore_until_fixed: Option<Vec<String>>,
2828}
2829
2830#[derive(Debug, Clone)]
2831pub struct MalwareCheckSettings {
2832    /// Whether the malware check is enabled.
2833    pub enabled: bool,
2834    /// The OSV-shaped service URL to use for malware checks.
2835    pub malware_check_url: Option<DisplaySafeUrl>,
2836}
2837
2838impl From<&crate::EnvironmentOptions> for MalwareCheckSettings {
2839    fn from(options: &crate::EnvironmentOptions) -> Self {
2840        Self {
2841            enabled: options.malware_check.value == Some(true),
2842            malware_check_url: options.malware_check_url.clone(),
2843        }
2844    }
2845}
2846
2847/// Represents the `preview-features` configuration option.
2848#[derive(Debug, Clone)]
2849#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2850#[cfg_attr(feature = "schemars", schemars(untagged))]
2851pub enum PreviewFeaturesOption {
2852    Toggle(bool),
2853    Features(Vec<MaybePreviewFeature>),
2854}
2855
2856// A derived `#[serde(untagged)]` implementation collapses detailed type and element errors into
2857// "data did not match any variant", so use a type-directed visitor to preserve useful diagnostics.
2858impl<'de> Deserialize<'de> for PreviewFeaturesOption {
2859    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2860    where
2861        D: serde::Deserializer<'de>,
2862    {
2863        serde_untagged::UntaggedEnumVisitor::new()
2864            .expecting("a boolean or a list of preview feature names")
2865            .bool(|value| Ok(Self::Toggle(value)))
2866            .seq(|sequence| sequence.deserialize().map(Self::Features))
2867            .deserialize(deserializer)
2868    }
2869}
2870
2871#[expect(
2872    dead_code,
2873    reason = "Fields are only used by the OptionsMetadata and JsonSchema derives"
2874)]
2875#[derive(OptionsMetadata)]
2876#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2877#[cfg_attr(feature = "schemars", schemars(rename_all = "kebab-case"))]
2878struct PreviewOptionsDefinition {
2879    // This legacy setting remains supported and included in the JSON schema, but is omitted from
2880    // option metadata so the generated settings reference documents only `preview-features`.
2881    /// Whether to enable all experimental, preview features.
2882    ///
2883    /// Use `preview-features` instead.
2884    #[deprecated(note = "use `preview-features` instead")]
2885    preview: Option<bool>,
2886    /// Whether to enable specific or all experimental preview features.
2887    ///
2888    /// Unknown feature names are ignored with a warning.
2889    #[option(
2890        default = "false",
2891        value_type = "bool | list[str]",
2892        example = r#"
2893            preview-features = true
2894            # or
2895            preview-features = ["python-upgrade"]
2896        "#
2897    )]
2898    preview_features: Option<PreviewFeaturesOption>,
2899}
2900
2901/// Represents the user's preview configuration from either `preview` or `preview-features`.
2902#[derive(Debug, Clone)]
2903pub enum PreviewOption {
2904    /// Whether to enable all experimental, preview features.
2905    Preview(bool),
2906    /// Whether to enable specific or all experimental preview features.
2907    PreviewFeatures(PreviewFeaturesOption),
2908}
2909
2910impl uv_options_metadata::OptionsMetadata for PreviewOption {
2911    fn record(visit: &mut dyn uv_options_metadata::Visit) {
2912        <PreviewOptionsDefinition as uv_options_metadata::OptionsMetadata>::record(visit);
2913    }
2914}
2915
2916#[cfg(feature = "schemars")]
2917impl schemars::JsonSchema for PreviewOption {
2918    fn schema_name() -> Cow<'static, str> {
2919        Cow::Borrowed("PreviewOption")
2920    }
2921
2922    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
2923        let mut schema = <PreviewOptionsDefinition as schemars::JsonSchema>::json_schema(generator);
2924        schema.insert(
2925            "not".to_string(),
2926            schemars::json_schema!({
2927                "properties": {
2928                    "preview": {},
2929                    "preview-features": {},
2930                },
2931                "required": ["preview", "preview-features"],
2932            })
2933            .into(),
2934        );
2935        schema
2936    }
2937}
2938
2939impl PreviewOption {
2940    fn try_from(
2941        preview: Option<bool>,
2942        preview_features: Option<PreviewFeaturesOption>,
2943    ) -> Result<Option<Self>, &'static str> {
2944        match (preview, preview_features) {
2945            (Some(_), Some(_)) => Err("cannot specify both `preview` and `preview-features`"),
2946            (Some(b), None) => Ok(Some(Self::Preview(b))),
2947            (None, Some(features)) => Ok(Some(Self::PreviewFeatures(features))),
2948            (None, None) => Ok(None),
2949        }
2950    }
2951
2952    /// Resolve the preview configuration, warning and ignoring unknown feature names.
2953    pub fn resolve(&self) -> Preview {
2954        use PreviewFeaturesOption::{Features, Toggle};
2955
2956        match self {
2957            Self::Preview(false) | Self::PreviewFeatures(Toggle(false)) => Preview::default(),
2958            Self::Preview(true) | Self::PreviewFeatures(Toggle(true)) => Preview::all(),
2959            Self::PreviewFeatures(Features(features)) => Preview::from_feature_names(features),
2960        }
2961    }
2962}