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