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