mirror of https://github.com/astral-sh/uv
Add a `--show-settings` option for configuration testing (#4304)
## Summary The fixtures here are pretty large, but it lets us test what we actually care about (the resolved settings) rather than inferring the resolved settings from behavior, which I think is a big improvement. I also broke the tests down into more granular cases.
This commit is contained in:
parent
1d6d98f3a3
commit
f01ab57518
|
|
@ -99,9 +99,13 @@ pub(crate) struct GlobalArgs {
|
|||
/// parent directories.
|
||||
#[arg(global = true, long, hide = true)]
|
||||
pub(crate) isolated: bool,
|
||||
|
||||
/// Show the resolved settings for the current command.
|
||||
#[arg(global = true, long, hide = true)]
|
||||
pub(crate) show_settings: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, clap::ValueEnum)]
|
||||
#[derive(Debug, Copy, Clone, clap::ValueEnum)]
|
||||
pub(crate) enum ColorChoice {
|
||||
/// Enables colored output only when the output is going to a terminal or TTY with support.
|
||||
Auto,
|
||||
|
|
@ -188,7 +192,7 @@ pub(crate) enum CacheCommand {
|
|||
Dir,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
#[derive(Args, Debug)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub(crate) struct CleanArgs {
|
||||
/// The packages to remove from the cache.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use std::env;
|
||||
use std::fmt::Write;
|
||||
use std::io::stdout;
|
||||
use std::path::PathBuf;
|
||||
use std::process::ExitCode;
|
||||
|
|
@ -139,7 +140,10 @@ async fn run() -> Result<ExitStatus> {
|
|||
};
|
||||
|
||||
// Resolve the global settings.
|
||||
let globals = GlobalSettings::resolve(cli.global_args, filesystem.as_ref());
|
||||
let globals = GlobalSettings::resolve(&cli.global_args, filesystem.as_ref());
|
||||
|
||||
// Resolve the cache settings.
|
||||
let cache_settings = CacheSettings::resolve(cli.cache_args, filesystem.as_ref());
|
||||
|
||||
// Configure the `tracing` crate, which controls internal logging.
|
||||
#[cfg(feature = "tracing-durations-export")]
|
||||
|
|
@ -186,9 +190,25 @@ async fn run() -> Result<ExitStatus> {
|
|||
|
||||
debug!("uv {}", version::version());
|
||||
|
||||
// Resolve the cache settings.
|
||||
let cache = CacheSettings::resolve(cli.cache_args, filesystem.as_ref());
|
||||
let cache = Cache::from_settings(cache.no_cache, cache.cache_dir)?;
|
||||
// Write out any resolved settings.
|
||||
macro_rules! show_settings {
|
||||
($arg:expr) => {
|
||||
if globals.show_settings {
|
||||
writeln!(printer.stdout(), "{:#?}", $arg)?;
|
||||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
};
|
||||
($arg:expr, false) => {
|
||||
if globals.show_settings {
|
||||
writeln!(printer.stdout(), "{:#?}", $arg)?;
|
||||
}
|
||||
};
|
||||
}
|
||||
show_settings!(globals, false);
|
||||
show_settings!(cache_settings, false);
|
||||
|
||||
// Configure the cache.
|
||||
let cache = Cache::from_settings(cache_settings.no_cache, cache_settings.cache_dir)?;
|
||||
|
||||
match cli.command {
|
||||
Commands::Pip(PipNamespace {
|
||||
|
|
@ -198,6 +218,8 @@ async fn run() -> Result<ExitStatus> {
|
|||
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipCompileSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(args.settings.concurrency.installs)
|
||||
.build_global()
|
||||
|
|
@ -275,6 +297,8 @@ async fn run() -> Result<ExitStatus> {
|
|||
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipSyncSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(args.settings.concurrency.installs)
|
||||
.build_global()
|
||||
|
|
@ -335,6 +359,8 @@ async fn run() -> Result<ExitStatus> {
|
|||
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipInstallSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(args.settings.concurrency.installs)
|
||||
.build_global()
|
||||
|
|
@ -410,6 +436,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipUninstallSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
|
@ -445,6 +472,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipFreezeSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
|
@ -466,6 +494,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipListSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
|
@ -488,6 +517,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipShowSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
|
@ -507,6 +537,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipCheckSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
|
@ -522,7 +553,10 @@ async fn run() -> Result<ExitStatus> {
|
|||
Commands::Cache(CacheNamespace {
|
||||
command: CacheCommand::Clean(args),
|
||||
})
|
||||
| Commands::Clean(args) => commands::cache_clean(&args.package, &cache, printer),
|
||||
| Commands::Clean(args) => {
|
||||
show_settings!(args);
|
||||
commands::cache_clean(&args.package, &cache, printer)
|
||||
}
|
||||
Commands::Cache(CacheNamespace {
|
||||
command: CacheCommand::Prune,
|
||||
}) => commands::cache_prune(&cache, printer),
|
||||
|
|
@ -537,6 +571,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::VenvSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
|
@ -573,6 +608,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
Commands::Project(ProjectCommand::Run(args)) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::RunSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?.with_refresh(args.refresh);
|
||||
|
|
@ -605,6 +641,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
Commands::Project(ProjectCommand::Sync(args)) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::SyncSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?.with_refresh(args.refresh);
|
||||
|
|
@ -626,6 +663,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
Commands::Project(ProjectCommand::Lock(args)) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::LockSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?.with_refresh(args.refresh);
|
||||
|
|
@ -645,6 +683,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
Commands::Project(ProjectCommand::Add(args)) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::AddSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
|
@ -664,6 +703,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
Commands::Project(ProjectCommand::Remove(args)) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::RemoveSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
|
@ -697,6 +737,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::ToolRunSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?.with_refresh(args.refresh);
|
||||
|
|
@ -723,6 +764,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::ToolchainListSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
|
@ -742,6 +784,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::ToolchainInstallSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
|
|
|||
|
|
@ -41,12 +41,13 @@ pub(crate) struct GlobalSettings {
|
|||
pub(crate) native_tls: bool,
|
||||
pub(crate) connectivity: Connectivity,
|
||||
pub(crate) isolated: bool,
|
||||
pub(crate) show_settings: bool,
|
||||
pub(crate) preview: PreviewMode,
|
||||
}
|
||||
|
||||
impl GlobalSettings {
|
||||
/// Resolve the [`GlobalSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn resolve(args: GlobalArgs, workspace: Option<&FilesystemOptions>) -> Self {
|
||||
pub(crate) fn resolve(args: &GlobalArgs, workspace: Option<&FilesystemOptions>) -> Self {
|
||||
Self {
|
||||
quiet: args.quiet,
|
||||
verbose: args.verbose,
|
||||
|
|
@ -79,6 +80,7 @@ impl GlobalSettings {
|
|||
Connectivity::Online
|
||||
},
|
||||
isolated: args.isolated,
|
||||
show_settings: args.show_settings,
|
||||
preview: PreviewMode::from(
|
||||
flag(args.preview, args.no_preview)
|
||||
.combine(workspace.and_then(|workspace| workspace.globals.preview))
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ pub const INSTA_FILTERS: &[(&str, &str)] = &[
|
|||
(r"(\s|\()(\d+m )?(\d+\.)?\d+(ms|s)", "$1[TIME]"),
|
||||
// File sizes
|
||||
(r"(\s|\()(\d+\.)?\d+([KM]i)?B", "$1[SIZE]"),
|
||||
// Timestamps
|
||||
(r"tv_sec: \d+", "tv_sec: [TIME]"),
|
||||
(r"tv_nsec: \d+", "tv_nsec: [TIME]"),
|
||||
// Rewrite Windows output to Unix output
|
||||
(r"\\([\w\d])", "/$1"),
|
||||
(r"uv.exe", "uv"),
|
||||
|
|
|
|||
|
|
@ -8962,524 +8962,6 @@ fn python_platform() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify that command-line arguments take precedence over on-disk configuration.
|
||||
#[test]
|
||||
fn resolve_configuration() -> 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"
|
||||
"#})?;
|
||||
|
||||
let requirements_in = context.temp_dir.child("requirements.in");
|
||||
requirements_in.write_str("anyio>3.0.0")?;
|
||||
|
||||
// Resolution should use the lowest direct version, and generate hashes.
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
|
||||
anyio==3.0.1 \
|
||||
--hash=sha256:1ef7622396ab55829d4236a6f75e2199df6d26a4ba79bea0cb942a5fd2f79a23 \
|
||||
--hash=sha256:ed71f7542ef39875b65def219794d9dcb0a48c571317b13612c12b1f292701b5
|
||||
# via -r requirements.in
|
||||
idna==3.6 \
|
||||
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
|
||||
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
|
||||
# via anyio
|
||||
sniffio==1.3.1 \
|
||||
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
|
||||
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Resolution should use the highest version, and generate hashes.
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in")
|
||||
.arg("--resolution=highest"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in --resolution=highest
|
||||
anyio==4.3.0 \
|
||||
--hash=sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8 \
|
||||
--hash=sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6
|
||||
# via -r requirements.in
|
||||
idna==3.6 \
|
||||
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
|
||||
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
|
||||
# via anyio
|
||||
sniffio==1.3.1 \
|
||||
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
|
||||
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Resolution should use the highest version, and omit hashes.
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in")
|
||||
.arg("--resolution=highest")
|
||||
.arg("--no-generate-hashes"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in --resolution=highest --no-generate-hashes
|
||||
anyio==4.3.0
|
||||
# via -r requirements.in
|
||||
idna==3.6
|
||||
# via anyio
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Write a `pyproject.toml` file to the directory.
|
||||
let pyproject = context.temp_dir.child("pyproject.toml");
|
||||
pyproject.write_str(indoc::indoc! {r#"
|
||||
[project]
|
||||
name = "example"
|
||||
version = "0.0.0"
|
||||
"#})?;
|
||||
|
||||
// Resolution should use the lowest direct version, and generate hashes.
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
|
||||
anyio==3.0.1 \
|
||||
--hash=sha256:1ef7622396ab55829d4236a6f75e2199df6d26a4ba79bea0cb942a5fd2f79a23 \
|
||||
--hash=sha256:ed71f7542ef39875b65def219794d9dcb0a48c571317b13612c12b1f292701b5
|
||||
# via -r requirements.in
|
||||
idna==3.6 \
|
||||
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
|
||||
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
|
||||
# via anyio
|
||||
sniffio==1.3.1 \
|
||||
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
|
||||
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Remove the `uv.toml` file.
|
||||
fs_err::remove_file(config.path())?;
|
||||
|
||||
// Resolution should use the highest version, and omit hashes.
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
|
||||
anyio==4.3.0
|
||||
# via -r requirements.in
|
||||
idna==3.6
|
||||
# via anyio
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Add configuration to the `pyproject.toml` file.
|
||||
pyproject.write_str(indoc::indoc! {r#"
|
||||
[project]
|
||||
name = "example"
|
||||
version = "0.0.0"
|
||||
|
||||
[tool.uv.pip]
|
||||
resolution = "lowest-direct"
|
||||
generate-hashes = true
|
||||
index-url = "https://pypi.org/simple"
|
||||
"#})?;
|
||||
|
||||
// Resolution should use the lowest direct version, and generate hashes.
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
|
||||
anyio==3.0.1 \
|
||||
--hash=sha256:1ef7622396ab55829d4236a6f75e2199df6d26a4ba79bea0cb942a5fd2f79a23 \
|
||||
--hash=sha256:ed71f7542ef39875b65def219794d9dcb0a48c571317b13612c12b1f292701b5
|
||||
# via -r requirements.in
|
||||
idna==3.6 \
|
||||
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
|
||||
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
|
||||
# via anyio
|
||||
sniffio==1.3.1 \
|
||||
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
|
||||
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Add an extra index URL entry to the `pyproject.toml` file.
|
||||
pyproject.write_str(indoc::indoc! {r#"
|
||||
[project]
|
||||
name = "example"
|
||||
version = "0.0.0"
|
||||
|
||||
[tool.uv.pip]
|
||||
index-url = "https://test.pypi.org/simple"
|
||||
extra-index-url = ["https://pypi.org/simple"]
|
||||
"#})?;
|
||||
|
||||
// Resolution should succeed, since the PyPI index is preferred.
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
|
||||
anyio==4.3.0
|
||||
# via -r requirements.in
|
||||
idna==3.6
|
||||
# via anyio
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Providing an additional index URL on the command-line should fail, since it will be
|
||||
// preferred (but the test index alone can't satisfy the requirements).
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in")
|
||||
.arg("--extra-index-url")
|
||||
.arg("https://test.pypi.org/simple"), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
× No solution found when resolving dependencies:
|
||||
╰─▶ Because only idna<2.8 is available and anyio==3.5.0 depends on idna>=2.8, we can conclude that anyio==3.5.0 cannot be used.
|
||||
And because only the following versions of anyio are available:
|
||||
anyio<=3.0.0
|
||||
anyio==3.5.0
|
||||
and you require anyio, we can conclude that the requirements are unsatisfiable.
|
||||
"###
|
||||
);
|
||||
|
||||
// If we allow the resolver to use _any_ index, it should succeed, since it now has _both_
|
||||
// the test and PyPI indexes in its `--extra-index-url`.
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in")
|
||||
.arg("--extra-index-url")
|
||||
.arg("https://test.pypi.org/simple")
|
||||
.arg("--index-strategy")
|
||||
.arg("unsafe-best-match"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in --index-strategy unsafe-best-match
|
||||
anyio==4.3.0
|
||||
# via -r requirements.in
|
||||
idna==3.6
|
||||
# via anyio
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Write out a `--find-links` entry.
|
||||
pyproject.write_str(indoc::indoc! {r#"
|
||||
[project]
|
||||
name = "example"
|
||||
version = "0.0.0"
|
||||
|
||||
[tool.uv.pip]
|
||||
no-index = true
|
||||
find-links = ["https://download.pytorch.org/whl/torch_stable.html"]
|
||||
"#})?;
|
||||
|
||||
let requirements_in = context.temp_dir.child("requirements.in");
|
||||
requirements_in.write_str("tqdm")?;
|
||||
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
|
||||
tqdm==4.66.2
|
||||
# via -r requirements.in
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Write out to the top-level (`tool.uv`, rather than `tool.uv.pip`).
|
||||
pyproject.write_str(indoc::indoc! {r#"
|
||||
[project]
|
||||
name = "example"
|
||||
version = "0.0.0"
|
||||
|
||||
[tool.uv]
|
||||
resolution = "lowest-direct"
|
||||
"#})?;
|
||||
|
||||
let requirements_in = context.temp_dir.child("requirements.in");
|
||||
requirements_in.write_str("anyio>3.0.0")?;
|
||||
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
|
||||
anyio==3.0.1
|
||||
# via -r requirements.in
|
||||
idna==3.6
|
||||
# via anyio
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Write out to both the top-level (`tool.uv`) and the pip section (`tool.uv.pip`). The
|
||||
// `tool.uv.pip` section should take precedence.
|
||||
pyproject.write_str(indoc::indoc! {r#"
|
||||
[project]
|
||||
name = "example"
|
||||
version = "0.0.0"
|
||||
|
||||
[tool.uv]
|
||||
resolution = "lowest-direct"
|
||||
|
||||
[tool.uv.pip]
|
||||
resolution = "highest"
|
||||
"#})?;
|
||||
|
||||
let requirements_in = context.temp_dir.child("requirements.in");
|
||||
requirements_in.write_str("anyio>3.0.0")?;
|
||||
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
|
||||
anyio==4.3.0
|
||||
# via -r requirements.in
|
||||
idna==3.6
|
||||
# via anyio
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// But the command-line should take precedence over both.
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in")
|
||||
.arg("--resolution=lowest-direct"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in --resolution=lowest-direct
|
||||
anyio==3.0.1
|
||||
# via -r requirements.in
|
||||
idna==3.6
|
||||
# via anyio
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify that user configuration is respected.
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn resolve_user_configuration() -> Result<()> {
|
||||
// Create a temporary directory to store the user configuration.
|
||||
let xdg = assert_fs::TempDir::new().expect("Failed to create temp dir");
|
||||
let uv = xdg.child("uv");
|
||||
let config = uv.child("uv.toml");
|
||||
config.write_str(indoc::indoc! {r#"
|
||||
[pip]
|
||||
resolution = "lowest-direct"
|
||||
"#})?;
|
||||
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let requirements_in = context.temp_dir.child("requirements.in");
|
||||
requirements_in.write_str("anyio>3.0.0")?;
|
||||
|
||||
// Resolution should use the lowest direct version.
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in")
|
||||
.env("XDG_CONFIG_HOME", xdg.path()), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
|
||||
anyio==3.0.1
|
||||
# via -r requirements.in
|
||||
idna==3.6
|
||||
# via anyio
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Add a local configuration to generate hashes.
|
||||
let config = context.temp_dir.child("uv.toml");
|
||||
config.write_str(indoc::indoc! {r"
|
||||
[pip]
|
||||
generate-hashes = true
|
||||
"})?;
|
||||
|
||||
// Resolution should use the lowest direct version and generate hashes.
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in")
|
||||
.env("XDG_CONFIG_HOME", xdg.path()), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
|
||||
anyio==3.0.1 \
|
||||
--hash=sha256:1ef7622396ab55829d4236a6f75e2199df6d26a4ba79bea0cb942a5fd2f79a23 \
|
||||
--hash=sha256:ed71f7542ef39875b65def219794d9dcb0a48c571317b13612c12b1f292701b5
|
||||
# via -r requirements.in
|
||||
idna==3.6 \
|
||||
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
|
||||
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
|
||||
# via anyio
|
||||
sniffio==1.3.1 \
|
||||
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
|
||||
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Add a local configuration to override the user configuration.
|
||||
let config = context.temp_dir.child("uv.toml");
|
||||
config.write_str(indoc::indoc! {r#"
|
||||
[pip]
|
||||
resolution = "highest"
|
||||
"#})?;
|
||||
|
||||
// Resolution should use the highest version.
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in")
|
||||
.env("XDG_CONFIG_HOME", xdg.path()), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
|
||||
anyio==4.3.0
|
||||
# via -r requirements.in
|
||||
idna==3.6
|
||||
# via anyio
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// However, the user-level `tool.uv.pip` settings override the project-level `tool.uv` settings.
|
||||
// This is awkward, but we merge the user configuration into the workspace configuration, so
|
||||
// the resulting configuration has both `tool.uv.pip.resolution` (from the user configuration)
|
||||
// and `tool.uv.resolution` (from the workspace settings), so we choose the former.
|
||||
let config = context.temp_dir.child("uv.toml");
|
||||
config.write_str(indoc::indoc! {r#"
|
||||
resolution = "highest"
|
||||
"#})?;
|
||||
|
||||
// Resolution should use the highest version.
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in")
|
||||
.env("XDG_CONFIG_HOME", xdg.path()), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
|
||||
anyio==3.0.1
|
||||
# via -r requirements.in
|
||||
idna==3.6
|
||||
# via anyio
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolve a specific source distribution via a Git HTTPS dependency.
|
||||
#[test]
|
||||
#[cfg(feature = "git")]
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue