Skip to main content

uv_settings/
settings.rs

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