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