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