Skip to main content

uv_settings/
settings.rs

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