mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 10:58:28 +00:00
Add support for path dependencies (#471)
## Summary This PR adds support for local path dependencies. The approach mostly just falls out of our existing approach and infrastructure for Git and URL dependencies. Closes https://github.com/astral-sh/puffin/issues/436. (We'll open a separate issue for editable installs.) ## Test Plan Added `pip-compile` tests that pre-download a wheel or source distribution, then install it via local path.
This commit is contained in:
parent
f1aa70d9d3
commit
17228ba04e
23 changed files with 580 additions and 24 deletions
|
@ -71,6 +71,7 @@ assert_fs = { version = "1.0.13" }
|
|||
insta-cmd = { version = "0.4.0" }
|
||||
insta = { version = "1.34.0", features = ["filters"] }
|
||||
predicates = { version = "3.0.4" }
|
||||
reqwest = { version = "0.11.22", features = ["blocking", "rustls"], default-features = false }
|
||||
|
||||
[features]
|
||||
# Introduces a dependency on a local Python installation.
|
||||
|
|
|
@ -1421,3 +1421,77 @@ fn compile_exclude_newer() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolve a local path dependency on a specific wheel.
|
||||
#[test]
|
||||
fn compile_wheel_path_dependency() -> Result<()> {
|
||||
let temp_dir = TempDir::new()?;
|
||||
let cache_dir = TempDir::new()?;
|
||||
let venv = make_venv_py312(&temp_dir, &cache_dir);
|
||||
|
||||
// Download a wheel.
|
||||
let response = reqwest::blocking::get("https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl")?;
|
||||
let flask_wheel = temp_dir.child("flask-3.0.0-py3-none-any.whl");
|
||||
let mut flask_wheel_file = std::fs::File::create(&flask_wheel)?;
|
||||
std::io::copy(&mut response.bytes()?.as_ref(), &mut flask_wheel_file)?;
|
||||
|
||||
let requirements_in = temp_dir.child("requirements.in");
|
||||
requirements_in.write_str(&format!("flask @ file://{}", flask_wheel.path().display()))?;
|
||||
|
||||
// In addition to the standard filters, remove the temporary directory from the snapshot.
|
||||
let mut filters = INSTA_FILTERS.to_vec();
|
||||
filters.push((r"file://.*/", "file://[TEMP_DIR]/"));
|
||||
|
||||
insta::with_settings!({
|
||||
filters => filters
|
||||
}, {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.arg("pip-compile")
|
||||
.arg("requirements.in")
|
||||
.arg("--cache-dir")
|
||||
.arg(cache_dir.path())
|
||||
.arg("--exclude-newer")
|
||||
.arg(EXCLUDE_NEWER)
|
||||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||
.current_dir(&temp_dir));
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolve a local path dependency on a specific source distribution.
|
||||
#[test]
|
||||
fn compile_source_distribution_path_dependency() -> Result<()> {
|
||||
let temp_dir = TempDir::new()?;
|
||||
let cache_dir = TempDir::new()?;
|
||||
let venv = make_venv_py312(&temp_dir, &cache_dir);
|
||||
|
||||
// Download a source distribution.
|
||||
let response = reqwest::blocking::get("https://files.pythonhosted.org/packages/d8/09/c1a7354d3925a3c6c8cfdebf4245bae67d633ffda1ba415add06ffc839c5/flask-3.0.0.tar.gz")?;
|
||||
let flask_wheel = temp_dir.child("flask-3.0.0.tar.gz");
|
||||
let mut flask_wheel_file = std::fs::File::create(&flask_wheel)?;
|
||||
std::io::copy(&mut response.bytes()?.as_ref(), &mut flask_wheel_file)?;
|
||||
|
||||
let requirements_in = temp_dir.child("requirements.in");
|
||||
requirements_in.write_str(&format!("flask @ file://{}", flask_wheel.path().display()))?;
|
||||
|
||||
// In addition to the standard filters, remove the temporary directory from the snapshot.
|
||||
let mut filters = INSTA_FILTERS.to_vec();
|
||||
filters.push((r"file://.*/", "file://[TEMP_DIR]/"));
|
||||
|
||||
insta::with_settings!({
|
||||
filters => filters
|
||||
}, {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.arg("pip-compile")
|
||||
.arg("requirements.in")
|
||||
.arg("--cache-dir")
|
||||
.arg(cache_dir.path())
|
||||
.arg("--exclude-newer")
|
||||
.arg(EXCLUDE_NEWER)
|
||||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||
.current_dir(&temp_dir));
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1049,3 +1049,97 @@ fn warn_on_yanked_version() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolve a local wheel.
|
||||
#[test]
|
||||
fn install_local_wheel() -> Result<()> {
|
||||
let temp_dir = assert_fs::TempDir::new()?;
|
||||
let cache_dir = assert_fs::TempDir::new()?;
|
||||
let venv = temp_dir.child(".venv");
|
||||
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.arg("venv")
|
||||
.arg(venv.as_os_str())
|
||||
.arg("--cache-dir")
|
||||
.arg(cache_dir.path())
|
||||
.arg("--python")
|
||||
.arg("python3.12")
|
||||
.current_dir(&temp_dir)
|
||||
.assert()
|
||||
.success();
|
||||
venv.assert(predicates::path::is_dir());
|
||||
|
||||
// Download a wheel.
|
||||
let response = reqwest::blocking::get("https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl")?;
|
||||
let flask_wheel = temp_dir.child("flask-3.0.0-py3-none-any.whl");
|
||||
let mut flask_wheel_file = std::fs::File::create(&flask_wheel)?;
|
||||
std::io::copy(&mut response.bytes()?.as_ref(), &mut flask_wheel_file)?;
|
||||
|
||||
let requirements_txt = temp_dir.child("requirements.txt");
|
||||
requirements_txt.write_str(&format!("flask @ file://{}", flask_wheel.path().display()))?;
|
||||
|
||||
// In addition to the standard filters, remove the temporary directory from the snapshot.
|
||||
let mut filters = INSTA_FILTERS.to_vec();
|
||||
filters.push((r"file://.*/", "file://[TEMP_DIR]/"));
|
||||
|
||||
insta::with_settings!({
|
||||
filters => filters
|
||||
}, {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.arg("pip-sync")
|
||||
.arg("requirements.txt")
|
||||
.arg("--cache-dir")
|
||||
.arg(cache_dir.path())
|
||||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||
.current_dir(&temp_dir));
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Install a local source distribution.
|
||||
#[test]
|
||||
fn install_local_source_distribution() -> Result<()> {
|
||||
let temp_dir = assert_fs::TempDir::new()?;
|
||||
let cache_dir = assert_fs::TempDir::new()?;
|
||||
let venv = temp_dir.child(".venv");
|
||||
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.arg("venv")
|
||||
.arg(venv.as_os_str())
|
||||
.arg("--cache-dir")
|
||||
.arg(cache_dir.path())
|
||||
.arg("--python")
|
||||
.arg("python3.12")
|
||||
.current_dir(&temp_dir)
|
||||
.assert()
|
||||
.success();
|
||||
venv.assert(predicates::path::is_dir());
|
||||
|
||||
// Download a source distribution.
|
||||
let response = reqwest::blocking::get("https://files.pythonhosted.org/packages/d8/09/c1a7354d3925a3c6c8cfdebf4245bae67d633ffda1ba415add06ffc839c5/flask-3.0.0.tar.gz")?;
|
||||
let flask_wheel = temp_dir.child("flask-3.0.0.tar.gz");
|
||||
let mut flask_wheel_file = std::fs::File::create(&flask_wheel)?;
|
||||
std::io::copy(&mut response.bytes()?.as_ref(), &mut flask_wheel_file)?;
|
||||
|
||||
let requirements_txt = temp_dir.child("requirements.txt");
|
||||
requirements_txt.write_str(&format!("flask @ file://{}", flask_wheel.path().display()))?;
|
||||
|
||||
// In addition to the standard filters, remove the temporary directory from the snapshot.
|
||||
let mut filters = INSTA_FILTERS.to_vec();
|
||||
filters.push((r"file://.*/", "file://[TEMP_DIR]/"));
|
||||
|
||||
insta::with_settings!({
|
||||
filters => filters
|
||||
}, {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.arg("pip-sync")
|
||||
.arg("requirements.txt")
|
||||
.arg("--cache-dir")
|
||||
.arg(cache_dir.path())
|
||||
.env("VIRTUAL_ENV", venv.as_os_str())
|
||||
.current_dir(&temp_dir));
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
source: crates/puffin-cli/tests/pip_compile.rs
|
||||
info:
|
||||
program: puffin
|
||||
args:
|
||||
- pip-compile
|
||||
- requirements.in
|
||||
- "--cache-dir"
|
||||
- /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpz0R3e9
|
||||
- "--exclude-newer"
|
||||
- "2023-11-18T12:00:00Z"
|
||||
env:
|
||||
VIRTUAL_ENV: /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmp2xo89r/.venv
|
||||
---
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||||
blinker==1.7.0
|
||||
# via flask
|
||||
click==8.1.7
|
||||
# via flask
|
||||
flask @ file://[TEMP_DIR]/flask-3.0.0.tar.gz
|
||||
itsdangerous==2.1.2
|
||||
# via flask
|
||||
jinja2==3.1.2
|
||||
# via flask
|
||||
markupsafe==2.1.3
|
||||
# via
|
||||
# jinja2
|
||||
# werkzeug
|
||||
werkzeug==3.0.1
|
||||
# via flask
|
||||
|
||||
----- stderr -----
|
||||
Resolved 7 packages in [TIME]
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
source: crates/puffin-cli/tests/pip_compile.rs
|
||||
info:
|
||||
program: puffin
|
||||
args:
|
||||
- pip-compile
|
||||
- requirements.in
|
||||
- "--cache-dir"
|
||||
- /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpKtoCCH
|
||||
- "--exclude-newer"
|
||||
- "2023-11-18T12:00:00Z"
|
||||
env:
|
||||
VIRTUAL_ENV: /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmp0htf0U/.venv
|
||||
---
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by Puffin v0.0.1 via the following command:
|
||||
# puffin pip-compile requirements.in --cache-dir [CACHE_DIR]
|
||||
blinker==1.7.0
|
||||
# via flask
|
||||
click==8.1.7
|
||||
# via flask
|
||||
flask @ file://[TEMP_DIR]/flask-3.0.0-py3-none-any.whl
|
||||
itsdangerous==2.1.2
|
||||
# via flask
|
||||
jinja2==3.1.2
|
||||
# via flask
|
||||
markupsafe==2.1.3
|
||||
# via
|
||||
# jinja2
|
||||
# werkzeug
|
||||
werkzeug==3.0.1
|
||||
# via flask
|
||||
|
||||
----- stderr -----
|
||||
Resolved 7 packages in [TIME]
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
source: crates/puffin-cli/tests/pip_sync.rs
|
||||
info:
|
||||
program: puffin
|
||||
args:
|
||||
- pip-sync
|
||||
- requirements.txt
|
||||
- "--cache-dir"
|
||||
- /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmp1HfKKY
|
||||
env:
|
||||
VIRTUAL_ENV: /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpBmYNQk/.venv
|
||||
---
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
Downloaded 1 package in [TIME]
|
||||
Built 1 package in [TIME]
|
||||
Unzipped 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ flask @ file://[TEMP_DIR]/flask-3.0.0.tar.gz
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
source: crates/puffin-cli/tests/pip_sync.rs
|
||||
info:
|
||||
program: puffin
|
||||
args:
|
||||
- pip-sync
|
||||
- requirements.txt
|
||||
- "--cache-dir"
|
||||
- /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpKx7cY5
|
||||
env:
|
||||
VIRTUAL_ENV: /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpEz5kWW/.venv
|
||||
---
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
Downloaded 1 package in [TIME]
|
||||
Unzipped 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ flask @ file://[TEMP_DIR]/flask-3.0.0-py3-none-any.whl
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue