mirror of https://github.com/astral-sh/uv
Respect locked script preferences in `uv run --with` (#13283)
## Summary Part of https://github.com/astral-sh/uv/issues/13173, but doesn't close the issue. This just respects preferences if your script uses a lockfile, since we already support that for locked _projects_.
This commit is contained in:
parent
e2d105d045
commit
c12ce84fbd
|
|
@ -31,7 +31,7 @@ use uv_python::{
|
||||||
VersionFileDiscoveryOptions,
|
VersionFileDiscoveryOptions,
|
||||||
};
|
};
|
||||||
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||||
use uv_resolver::Lock;
|
use uv_resolver::{Installable, Lock};
|
||||||
use uv_scripts::Pep723Item;
|
use uv_scripts::Pep723Item;
|
||||||
use uv_settings::PythonInstallMirrors;
|
use uv_settings::PythonInstallMirrors;
|
||||||
use uv_shell::runnable::WindowsRunnable;
|
use uv_shell::runnable::WindowsRunnable;
|
||||||
|
|
@ -187,6 +187,9 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||||
// Initialize any output reporters.
|
// Initialize any output reporters.
|
||||||
let download_reporter = PythonDownloadReporter::single(printer);
|
let download_reporter = PythonDownloadReporter::single(printer);
|
||||||
|
|
||||||
|
// The lockfile used for the base environment.
|
||||||
|
let mut base_lock: Option<(Lock, PathBuf)> = None;
|
||||||
|
|
||||||
// Determine whether the command to execute is a PEP 723 script.
|
// Determine whether the command to execute is a PEP 723 script.
|
||||||
let temp_dir;
|
let temp_dir;
|
||||||
let script_interpreter = if let Some(script) = script {
|
let script_interpreter = if let Some(script) = script {
|
||||||
|
|
@ -318,6 +321,10 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Respect any locked preferences when resolving `--with` dependencies downstream.
|
||||||
|
let install_path = target.install_path().to_path_buf();
|
||||||
|
base_lock = Some((lock, install_path));
|
||||||
|
|
||||||
Some(environment.into_interpreter())
|
Some(environment.into_interpreter())
|
||||||
} else {
|
} else {
|
||||||
// If no lockfile is found, warn against `--locked` and `--frozen`.
|
// If no lockfile is found, warn against `--locked` and `--frozen`.
|
||||||
|
|
@ -443,9 +450,6 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// The lockfile used for the base environment.
|
|
||||||
let mut lock: Option<(Lock, PathBuf)> = None;
|
|
||||||
|
|
||||||
// Discover and sync the base environment.
|
// Discover and sync the base environment.
|
||||||
let workspace_cache = WorkspaceCache::default();
|
let workspace_cache = WorkspaceCache::default();
|
||||||
let temp_dir;
|
let temp_dir;
|
||||||
|
|
@ -659,7 +663,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||||
// If we're not syncing, we should still attempt to respect the locked preferences
|
// If we're not syncing, we should still attempt to respect the locked preferences
|
||||||
// in any `--with` requirements.
|
// in any `--with` requirements.
|
||||||
if !isolated && !requirements.is_empty() {
|
if !isolated && !requirements.is_empty() {
|
||||||
lock = LockTarget::from(project.workspace())
|
base_lock = LockTarget::from(project.workspace())
|
||||||
.read()
|
.read()
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
|
|
@ -802,7 +806,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
lock = Some((
|
base_lock = Some((
|
||||||
result.into_lock(),
|
result.into_lock(),
|
||||||
project.workspace().install_path().to_owned(),
|
project.workspace().install_path().to_owned(),
|
||||||
));
|
));
|
||||||
|
|
@ -901,13 +905,14 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||||
debug!("Syncing ephemeral requirements");
|
debug!("Syncing ephemeral requirements");
|
||||||
|
|
||||||
// Read the build constraints from the lock file.
|
// Read the build constraints from the lock file.
|
||||||
let build_constraints = lock
|
let build_constraints = base_lock
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|(lock, path)| lock.build_constraints(path));
|
.map(|(lock, path)| lock.build_constraints(path));
|
||||||
|
|
||||||
let result = CachedEnvironment::from_spec(
|
let result = CachedEnvironment::from_spec(
|
||||||
EnvironmentSpecification::from(spec).with_lock(
|
EnvironmentSpecification::from(spec).with_lock(
|
||||||
lock.as_ref()
|
base_lock
|
||||||
|
.as_ref()
|
||||||
.map(|(lock, install_path)| (lock, install_path.as_ref())),
|
.map(|(lock, install_path)| (lock, install_path.as_ref())),
|
||||||
),
|
),
|
||||||
build_constraints.unwrap_or_default(),
|
build_constraints.unwrap_or_default(),
|
||||||
|
|
|
||||||
|
|
@ -4983,3 +4983,98 @@ fn run_windows_legacy_scripts() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If a `--with` requirement overlaps with a locked script requirement, respect the lockfile as a
|
||||||
|
/// preference.
|
||||||
|
///
|
||||||
|
/// See: <https://github.com/astral-sh/uv/issues/13173>
|
||||||
|
#[test]
|
||||||
|
fn run_pep723_script_with_constraints_lock() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let test_script = context.temp_dir.child("main.py");
|
||||||
|
test_script.write_str(indoc! { r#"
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.11"
|
||||||
|
# dependencies = [
|
||||||
|
# "iniconfig<2",
|
||||||
|
# ]
|
||||||
|
# ///
|
||||||
|
|
||||||
|
import iniconfig
|
||||||
|
|
||||||
|
print("Hello, world!")
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Explicitly lock the script.
|
||||||
|
uv_snapshot!(context.filters(), context.lock().arg("--script").arg("main.py"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let lock = context.read("main.py.lock");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
lock, @r#"
|
||||||
|
version = 1
|
||||||
|
revision = 2
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
|
||||||
|
[options]
|
||||||
|
exclude-newer = "2024-03-25T00:00:00Z"
|
||||||
|
|
||||||
|
[manifest]
|
||||||
|
requirements = [{ name = "iniconfig", specifier = "<2" }]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iniconfig"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/23/a2/97899f6bd0e873fed3a7e67ae8d3a08b21799430fb4da15cfedf10d6e2c2/iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32", size = 8104, upload-time = "2020-10-14T10:20:18.572Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", size = 4990, upload-time = "2020-10-16T17:37:23.05Z" },
|
||||||
|
]
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! { r#"
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "1.0.0"
|
||||||
|
requires-python = ">=3.10"
|
||||||
|
dependencies = [
|
||||||
|
"iniconfig",
|
||||||
|
]
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--with").arg(".").arg("main.py"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
Hello, world!
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ iniconfig==1.1.1
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 2 packages in [TIME]
|
||||||
|
+ foo==1.0.0 (from file://[TEMP_DIR]/)
|
||||||
|
+ iniconfig==1.1.1
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue