mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Merge 3048922f28
into f609e1ddaf
This commit is contained in:
commit
5e7874953a
3 changed files with 483 additions and 6 deletions
|
@ -119,10 +119,9 @@ impl FilesystemOptions {
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|content| toml::from_str::<PyProjectToml>(&content).ok())
|
.and_then(|content| toml::from_str::<PyProjectToml>(&content).ok())
|
||||||
{
|
{
|
||||||
if pyproject.tool.is_some_and(|tool| tool.uv.is_some()) {
|
if let Some(options) = pyproject.tool.as_ref().and_then(|tool| tool.uv.as_ref())
|
||||||
warn_user!(
|
{
|
||||||
"Found both a `uv.toml` file and a `[tool.uv]` section in an adjacent `pyproject.toml`. The `[tool.uv]` section will be ignored in favor of the `uv.toml` file."
|
warn_uv_toml_masked_fields(options);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,6 +224,253 @@ fn validate_uv_toml(path: &Path, options: &Options) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validate that an [`Options`] contains no fields that `uv.toml` would mask
|
||||||
|
///
|
||||||
|
/// This is essentially the inverse of [`validated_uv_toml`][].
|
||||||
|
fn warn_uv_toml_masked_fields(options: &Options) {
|
||||||
|
let Options {
|
||||||
|
globals:
|
||||||
|
GlobalOptions {
|
||||||
|
required_version,
|
||||||
|
native_tls,
|
||||||
|
offline,
|
||||||
|
no_cache,
|
||||||
|
cache_dir,
|
||||||
|
preview,
|
||||||
|
python_preference,
|
||||||
|
python_downloads,
|
||||||
|
concurrent_downloads,
|
||||||
|
concurrent_builds,
|
||||||
|
concurrent_installs,
|
||||||
|
allow_insecure_host,
|
||||||
|
},
|
||||||
|
top_level:
|
||||||
|
ResolverInstallerOptions {
|
||||||
|
index,
|
||||||
|
index_url,
|
||||||
|
extra_index_url,
|
||||||
|
no_index,
|
||||||
|
find_links,
|
||||||
|
index_strategy,
|
||||||
|
keyring_provider,
|
||||||
|
resolution,
|
||||||
|
prerelease,
|
||||||
|
fork_strategy,
|
||||||
|
dependency_metadata,
|
||||||
|
config_settings,
|
||||||
|
no_build_isolation,
|
||||||
|
no_build_isolation_package,
|
||||||
|
exclude_newer,
|
||||||
|
link_mode,
|
||||||
|
compile_bytecode,
|
||||||
|
no_sources,
|
||||||
|
upgrade,
|
||||||
|
upgrade_package,
|
||||||
|
reinstall,
|
||||||
|
reinstall_package,
|
||||||
|
no_build,
|
||||||
|
no_build_package,
|
||||||
|
no_binary,
|
||||||
|
no_binary_package,
|
||||||
|
},
|
||||||
|
install_mirrors:
|
||||||
|
PythonInstallMirrors {
|
||||||
|
python_install_mirror,
|
||||||
|
pypy_install_mirror,
|
||||||
|
python_downloads_json_url,
|
||||||
|
},
|
||||||
|
publish:
|
||||||
|
PublishOptions {
|
||||||
|
publish_url,
|
||||||
|
trusted_publishing,
|
||||||
|
check_url,
|
||||||
|
},
|
||||||
|
add: AddOptions { add_bounds },
|
||||||
|
pip,
|
||||||
|
cache_keys,
|
||||||
|
override_dependencies,
|
||||||
|
constraint_dependencies,
|
||||||
|
build_constraint_dependencies,
|
||||||
|
environments,
|
||||||
|
required_environments,
|
||||||
|
conflicts: _,
|
||||||
|
workspace: _,
|
||||||
|
sources: _,
|
||||||
|
dev_dependencies: _,
|
||||||
|
default_groups: _,
|
||||||
|
dependency_groups: _,
|
||||||
|
managed: _,
|
||||||
|
package: _,
|
||||||
|
build_backend: _,
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
let mut masked_fields = vec![];
|
||||||
|
|
||||||
|
if required_version.is_some() {
|
||||||
|
masked_fields.push("required-version");
|
||||||
|
}
|
||||||
|
if native_tls.is_some() {
|
||||||
|
masked_fields.push("native-tls");
|
||||||
|
}
|
||||||
|
if offline.is_some() {
|
||||||
|
masked_fields.push("offline");
|
||||||
|
}
|
||||||
|
if no_cache.is_some() {
|
||||||
|
masked_fields.push("no-cache");
|
||||||
|
}
|
||||||
|
if cache_dir.is_some() {
|
||||||
|
masked_fields.push("cache-dir");
|
||||||
|
}
|
||||||
|
if preview.is_some() {
|
||||||
|
masked_fields.push("preview");
|
||||||
|
}
|
||||||
|
if python_preference.is_some() {
|
||||||
|
masked_fields.push("python-preference");
|
||||||
|
}
|
||||||
|
if python_downloads.is_some() {
|
||||||
|
masked_fields.push("python-downloads");
|
||||||
|
}
|
||||||
|
if concurrent_downloads.is_some() {
|
||||||
|
masked_fields.push("concurrent-downloads");
|
||||||
|
}
|
||||||
|
if concurrent_builds.is_some() {
|
||||||
|
masked_fields.push("concurrent-builds");
|
||||||
|
}
|
||||||
|
if concurrent_installs.is_some() {
|
||||||
|
masked_fields.push("concurrent-installs");
|
||||||
|
}
|
||||||
|
if allow_insecure_host.is_some() {
|
||||||
|
masked_fields.push("allow-insecure-host");
|
||||||
|
}
|
||||||
|
if index.is_some() {
|
||||||
|
masked_fields.push("index");
|
||||||
|
}
|
||||||
|
if index_url.is_some() {
|
||||||
|
masked_fields.push("index-url");
|
||||||
|
}
|
||||||
|
if extra_index_url.is_some() {
|
||||||
|
masked_fields.push("extra-index-url");
|
||||||
|
}
|
||||||
|
if no_index.is_some() {
|
||||||
|
masked_fields.push("no-index");
|
||||||
|
}
|
||||||
|
if find_links.is_some() {
|
||||||
|
masked_fields.push("find-links");
|
||||||
|
}
|
||||||
|
if index_strategy.is_some() {
|
||||||
|
masked_fields.push("index-strategy");
|
||||||
|
}
|
||||||
|
if keyring_provider.is_some() {
|
||||||
|
masked_fields.push("keyring-provider");
|
||||||
|
}
|
||||||
|
if resolution.is_some() {
|
||||||
|
masked_fields.push("resolution");
|
||||||
|
}
|
||||||
|
if prerelease.is_some() {
|
||||||
|
masked_fields.push("prerelease");
|
||||||
|
}
|
||||||
|
if fork_strategy.is_some() {
|
||||||
|
masked_fields.push("fork-strategy");
|
||||||
|
}
|
||||||
|
if dependency_metadata.is_some() {
|
||||||
|
masked_fields.push("dependency-metadata");
|
||||||
|
}
|
||||||
|
if config_settings.is_some() {
|
||||||
|
masked_fields.push("config-settings");
|
||||||
|
}
|
||||||
|
if no_build_isolation.is_some() {
|
||||||
|
masked_fields.push("no-build-isolation");
|
||||||
|
}
|
||||||
|
if no_build_isolation_package.is_some() {
|
||||||
|
masked_fields.push("no-build-isolation-package");
|
||||||
|
}
|
||||||
|
if exclude_newer.is_some() {
|
||||||
|
masked_fields.push("exclude-newer");
|
||||||
|
}
|
||||||
|
if link_mode.is_some() {
|
||||||
|
masked_fields.push("link-mode");
|
||||||
|
}
|
||||||
|
if compile_bytecode.is_some() {
|
||||||
|
masked_fields.push("compile-bytecode");
|
||||||
|
}
|
||||||
|
if no_sources.is_some() {
|
||||||
|
masked_fields.push("no-sources");
|
||||||
|
}
|
||||||
|
if upgrade.is_some() {
|
||||||
|
masked_fields.push("upgrade");
|
||||||
|
}
|
||||||
|
if upgrade_package.is_some() {
|
||||||
|
masked_fields.push("upgrade-package");
|
||||||
|
}
|
||||||
|
if reinstall.is_some() {
|
||||||
|
masked_fields.push("reinstall");
|
||||||
|
}
|
||||||
|
if reinstall_package.is_some() {
|
||||||
|
masked_fields.push("reinstall-package");
|
||||||
|
}
|
||||||
|
if no_build.is_some() {
|
||||||
|
masked_fields.push("no-build");
|
||||||
|
}
|
||||||
|
if no_build_package.is_some() {
|
||||||
|
masked_fields.push("no-build-package");
|
||||||
|
}
|
||||||
|
if no_binary.is_some() {
|
||||||
|
masked_fields.push("no-binary");
|
||||||
|
}
|
||||||
|
if no_binary_package.is_some() {
|
||||||
|
masked_fields.push("no-binary-package");
|
||||||
|
}
|
||||||
|
if python_install_mirror.is_some() {
|
||||||
|
masked_fields.push("python-install-mirror");
|
||||||
|
}
|
||||||
|
if pypy_install_mirror.is_some() {
|
||||||
|
masked_fields.push("pypy-install-mirror");
|
||||||
|
}
|
||||||
|
if python_downloads_json_url.is_some() {
|
||||||
|
masked_fields.push("python-downloads-json-url");
|
||||||
|
}
|
||||||
|
if publish_url.is_some() {
|
||||||
|
masked_fields.push("publish-url");
|
||||||
|
}
|
||||||
|
if trusted_publishing.is_some() {
|
||||||
|
masked_fields.push("trusted-publishing");
|
||||||
|
}
|
||||||
|
if check_url.is_some() {
|
||||||
|
masked_fields.push("check-url");
|
||||||
|
}
|
||||||
|
if add_bounds.is_some() {
|
||||||
|
masked_fields.push("add-bounds");
|
||||||
|
}
|
||||||
|
if pip.is_some() {
|
||||||
|
masked_fields.push("pip");
|
||||||
|
}
|
||||||
|
if cache_keys.is_some() {
|
||||||
|
masked_fields.push("cache_keys");
|
||||||
|
}
|
||||||
|
if override_dependencies.is_some() {
|
||||||
|
masked_fields.push("override-dependencies");
|
||||||
|
}
|
||||||
|
if constraint_dependencies.is_some() {
|
||||||
|
masked_fields.push("constraint-dependencies");
|
||||||
|
}
|
||||||
|
if build_constraint_dependencies.is_some() {
|
||||||
|
masked_fields.push("build-constraint-dependencies");
|
||||||
|
}
|
||||||
|
if environments.is_some() {
|
||||||
|
masked_fields.push("environments");
|
||||||
|
}
|
||||||
|
if required_environments.is_some() {
|
||||||
|
masked_fields.push("required-environments");
|
||||||
|
}
|
||||||
|
if !masked_fields.is_empty() {
|
||||||
|
let field_listing = masked_fields.join("\n- ");
|
||||||
|
warn_user!(
|
||||||
|
"Found both a `uv.toml` file and a `[tool.uv]` section in an adjacent `pyproject.toml`. The following fields from `[tool.uv]` will be ignored in favor of the `uv.toml` file:\n- {}",
|
||||||
|
field_listing,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
|
|
@ -103,7 +103,7 @@ pub struct Options {
|
||||||
cache-keys = [{ file = "pyproject.toml" }, { file = "requirements.txt" }, { git = { commit = true } }]
|
cache-keys = [{ file = "pyproject.toml" }, { file = "requirements.txt" }, { git = { commit = true } }]
|
||||||
"#
|
"#
|
||||||
)]
|
)]
|
||||||
cache_keys: Option<Vec<CacheKey>>,
|
pub cache_keys: Option<Vec<CacheKey>>,
|
||||||
|
|
||||||
// NOTE(charlie): These fields are shared with `ToolUv` in
|
// NOTE(charlie): These fields are shared with `ToolUv` in
|
||||||
// `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct.
|
// `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct.
|
||||||
|
|
|
@ -3441,6 +3441,8 @@ fn resolve_poetry_toml() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read from both a `uv.toml` and `pyproject.toml` file in the current directory.
|
/// Read from both a `uv.toml` and `pyproject.toml` file in the current directory.
|
||||||
|
///
|
||||||
|
/// Some fields in `[tool.uv]` are masked by `uv.toml` being defined, and should be warned about.
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
windows,
|
windows,
|
||||||
|
@ -3465,6 +3467,10 @@ fn resolve_both() -> anyhow::Result<()> {
|
||||||
name = "example"
|
name = "example"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[tool.uv]
|
||||||
|
offline = true
|
||||||
|
dev-dependencies = ["pytest"]
|
||||||
|
|
||||||
[tool.uv.pip]
|
[tool.uv.pip]
|
||||||
resolution = "highest"
|
resolution = "highest"
|
||||||
extra-index-url = ["https://test.pypi.org/simple"]
|
extra-index-url = ["https://test.pypi.org/simple"]
|
||||||
|
@ -3650,7 +3656,232 @@ fn resolve_both() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
warning: Found both a `uv.toml` file and a `[tool.uv]` section in an adjacent `pyproject.toml`. The `[tool.uv]` section will be ignored in favor of the `uv.toml` file.
|
warning: Found both a `uv.toml` file and a `[tool.uv]` section in an adjacent `pyproject.toml`. The following fields from `[tool.uv]` will be ignored in favor of the `uv.toml` file:
|
||||||
|
- offline
|
||||||
|
- pip
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read from both a `uv.toml` and `pyproject.toml` file in the current directory.
|
||||||
|
///
|
||||||
|
/// But the fields `[tool.uv]` defines aren't allowed in `uv.toml` so there's no warning.
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(
|
||||||
|
windows,
|
||||||
|
ignore = "Configuration tests are not yet supported on Windows"
|
||||||
|
)]
|
||||||
|
fn resolve_both_special_fields() -> anyhow::Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
// Write a `uv.toml` file to the directory.
|
||||||
|
let config = context.temp_dir.child("uv.toml");
|
||||||
|
config.write_str(indoc::indoc! {r#"
|
||||||
|
[pip]
|
||||||
|
resolution = "lowest-direct"
|
||||||
|
generate-hashes = true
|
||||||
|
index-url = "https://pypi.org/simple"
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
// Write a `pyproject.toml` file to the directory
|
||||||
|
let config = context.temp_dir.child("pyproject.toml");
|
||||||
|
config.write_str(indoc::indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "example"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
mygroup = ["iniconfig"]
|
||||||
|
|
||||||
|
[tool.uv]
|
||||||
|
dev-dependencies = ["pytest"]
|
||||||
|
|
||||||
|
[tool.uv.dependency-groups]
|
||||||
|
mygroup = {requires-python = ">=3.12"}
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
let requirements_in = context.temp_dir.child("requirements.in");
|
||||||
|
requirements_in.write_str("anyio>3.0.0")?;
|
||||||
|
|
||||||
|
// Resolution should succeed, but warn that the `pip` section in `pyproject.toml` is ignored.
|
||||||
|
uv_snapshot!(context.filters(), add_shared_args(context.pip_compile(), context.temp_dir.path())
|
||||||
|
.arg("--show-settings")
|
||||||
|
.arg("requirements.in"), @r#"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
GlobalSettings {
|
||||||
|
required_version: None,
|
||||||
|
quiet: 0,
|
||||||
|
verbose: 0,
|
||||||
|
color: Auto,
|
||||||
|
network_settings: NetworkSettings {
|
||||||
|
connectivity: Online,
|
||||||
|
native_tls: false,
|
||||||
|
allow_insecure_host: [],
|
||||||
|
},
|
||||||
|
concurrency: Concurrency {
|
||||||
|
downloads: 50,
|
||||||
|
builds: 16,
|
||||||
|
installs: 8,
|
||||||
|
},
|
||||||
|
show_settings: true,
|
||||||
|
preview: Disabled,
|
||||||
|
python_preference: Managed,
|
||||||
|
python_downloads: Automatic,
|
||||||
|
no_progress: false,
|
||||||
|
installer_metadata: true,
|
||||||
|
}
|
||||||
|
CacheSettings {
|
||||||
|
no_cache: false,
|
||||||
|
cache_dir: Some(
|
||||||
|
"[CACHE_DIR]/",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
PipCompileSettings {
|
||||||
|
format: None,
|
||||||
|
src_file: [
|
||||||
|
"requirements.in",
|
||||||
|
],
|
||||||
|
constraints: [],
|
||||||
|
overrides: [],
|
||||||
|
build_constraints: [],
|
||||||
|
constraints_from_workspace: [],
|
||||||
|
overrides_from_workspace: [],
|
||||||
|
build_constraints_from_workspace: [],
|
||||||
|
environments: SupportedEnvironments(
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
refresh: None(
|
||||||
|
Timestamp(
|
||||||
|
SystemTime {
|
||||||
|
tv_sec: [TIME],
|
||||||
|
tv_nsec: [TIME],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
settings: PipSettings {
|
||||||
|
index_locations: IndexLocations {
|
||||||
|
indexes: [
|
||||||
|
Index {
|
||||||
|
name: None,
|
||||||
|
url: Pypi(
|
||||||
|
VerbatimUrl {
|
||||||
|
url: DisplaySafeUrl {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
given: Some(
|
||||||
|
"https://pypi.org/simple",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
explicit: false,
|
||||||
|
default: true,
|
||||||
|
origin: None,
|
||||||
|
format: Simple,
|
||||||
|
publish_url: None,
|
||||||
|
authenticate: Auto,
|
||||||
|
ignore_error_codes: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
flat_index: [],
|
||||||
|
no_index: false,
|
||||||
|
},
|
||||||
|
python: None,
|
||||||
|
install_mirrors: PythonInstallMirrors {
|
||||||
|
python_install_mirror: None,
|
||||||
|
pypy_install_mirror: None,
|
||||||
|
python_downloads_json_url: None,
|
||||||
|
},
|
||||||
|
system: false,
|
||||||
|
extras: ExtrasSpecification(
|
||||||
|
ExtrasSpecificationInner {
|
||||||
|
include: Some(
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
exclude: [],
|
||||||
|
only_extras: false,
|
||||||
|
history: ExtrasSpecificationHistory {
|
||||||
|
extra: [],
|
||||||
|
only_extra: [],
|
||||||
|
no_extra: [],
|
||||||
|
all_extras: false,
|
||||||
|
no_default_extras: false,
|
||||||
|
defaults: List(
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
groups: [],
|
||||||
|
break_system_packages: false,
|
||||||
|
target: None,
|
||||||
|
prefix: None,
|
||||||
|
index_strategy: FirstIndex,
|
||||||
|
keyring_provider: Disabled,
|
||||||
|
torch_backend: None,
|
||||||
|
no_build_isolation: false,
|
||||||
|
no_build_isolation_package: [],
|
||||||
|
build_options: BuildOptions {
|
||||||
|
no_binary: None,
|
||||||
|
no_build: None,
|
||||||
|
},
|
||||||
|
allow_empty_requirements: false,
|
||||||
|
strict: false,
|
||||||
|
dependency_mode: Transitive,
|
||||||
|
resolution: LowestDirect,
|
||||||
|
prerelease: IfNecessaryOrExplicit,
|
||||||
|
fork_strategy: RequiresPython,
|
||||||
|
dependency_metadata: DependencyMetadata(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
output_file: None,
|
||||||
|
no_strip_extras: false,
|
||||||
|
no_strip_markers: false,
|
||||||
|
no_annotate: false,
|
||||||
|
no_header: false,
|
||||||
|
custom_compile_command: None,
|
||||||
|
generate_hashes: true,
|
||||||
|
config_setting: ConfigSettings(
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
python_version: None,
|
||||||
|
python_platform: None,
|
||||||
|
universal: false,
|
||||||
|
exclude_newer: None,
|
||||||
|
no_emit_package: [],
|
||||||
|
emit_index_url: false,
|
||||||
|
emit_find_links: false,
|
||||||
|
emit_build_options: false,
|
||||||
|
emit_marker_expression: false,
|
||||||
|
emit_index_annotation: false,
|
||||||
|
annotation_style: Split,
|
||||||
|
link_mode: Clone,
|
||||||
|
compile_bytecode: false,
|
||||||
|
sources: Enabled,
|
||||||
|
hash_checking: Some(
|
||||||
|
Verify,
|
||||||
|
),
|
||||||
|
upgrade: None,
|
||||||
|
reinstall: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue