mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-03 13:14:41 +00:00
Add --with-editable support to uv run (#6262)
Closes https://github.com/astral-sh/uv/issues/6254
This commit is contained in:
parent
c3c05c4602
commit
f10ccc488e
6 changed files with 166 additions and 2 deletions
|
|
@ -2140,6 +2140,14 @@ pub struct RunArgs {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub with: Vec<String>,
|
pub with: Vec<String>,
|
||||||
|
|
||||||
|
/// Run with the given packages installed as editables
|
||||||
|
///
|
||||||
|
/// When used in a project, these dependencies will be layered on top of
|
||||||
|
/// the project environment in a separate, ephemeral environment. These
|
||||||
|
/// dependencies are allowed to conflict with those specified by the project.
|
||||||
|
#[arg(long)]
|
||||||
|
pub with_editable: Vec<String>,
|
||||||
|
|
||||||
/// Run with all packages listed in the given `requirements.txt` files.
|
/// Run with all packages listed in the given `requirements.txt` files.
|
||||||
///
|
///
|
||||||
/// The same environment semantics as `--with` apply.
|
/// The same environment semantics as `--with` apply.
|
||||||
|
|
|
||||||
|
|
@ -554,7 +554,16 @@ pub(crate) async fn run(
|
||||||
eprint!("{report:?}");
|
eprint!("{report:?}");
|
||||||
return Ok(ExitStatus::Failure);
|
return Ok(ExitStatus::Failure);
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(ProjectError::Operation(operations::Error::Named(err))) => {
|
||||||
|
let err = miette::Report::msg(format!("{err}"))
|
||||||
|
.context("Invalid `--with` requirement");
|
||||||
|
eprint!("{err:?}");
|
||||||
|
return Ok(ExitStatus::Failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(err) => {
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.into()
|
environment.into()
|
||||||
|
|
|
||||||
|
|
@ -1011,6 +1011,11 @@ async fn run_project(
|
||||||
.with
|
.with
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(RequirementsSource::from_package)
|
.map(RequirementsSource::from_package)
|
||||||
|
.chain(
|
||||||
|
args.with_editable
|
||||||
|
.into_iter()
|
||||||
|
.map(RequirementsSource::Editable),
|
||||||
|
)
|
||||||
.chain(
|
.chain(
|
||||||
args.with_requirements
|
args.with_requirements
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,7 @@ pub(crate) struct RunSettings {
|
||||||
pub(crate) dev: bool,
|
pub(crate) dev: bool,
|
||||||
pub(crate) command: ExternalCommand,
|
pub(crate) command: ExternalCommand,
|
||||||
pub(crate) with: Vec<String>,
|
pub(crate) with: Vec<String>,
|
||||||
|
pub(crate) with_editable: Vec<String>,
|
||||||
pub(crate) with_requirements: Vec<PathBuf>,
|
pub(crate) with_requirements: Vec<PathBuf>,
|
||||||
pub(crate) isolated: bool,
|
pub(crate) isolated: bool,
|
||||||
pub(crate) show_resolution: bool,
|
pub(crate) show_resolution: bool,
|
||||||
|
|
@ -215,6 +216,7 @@ impl RunSettings {
|
||||||
no_dev,
|
no_dev,
|
||||||
command,
|
command,
|
||||||
with,
|
with,
|
||||||
|
with_editable,
|
||||||
with_requirements,
|
with_requirements,
|
||||||
isolated,
|
isolated,
|
||||||
locked,
|
locked,
|
||||||
|
|
@ -238,6 +240,7 @@ impl RunSettings {
|
||||||
dev: flag(dev, no_dev).unwrap_or(true),
|
dev: flag(dev, no_dev).unwrap_or(true),
|
||||||
command,
|
command,
|
||||||
with,
|
with,
|
||||||
|
with_editable,
|
||||||
with_requirements: with_requirements
|
with_requirements: with_requirements
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(Maybe::into_option)
|
.filter_map(Maybe::into_option)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use indoc::indoc;
|
||||||
|
|
||||||
use uv_python::PYTHON_VERSION_FILENAME;
|
use uv_python::PYTHON_VERSION_FILENAME;
|
||||||
|
|
||||||
use common::{uv_snapshot, TestContext};
|
use common::{copy_dir_all, uv_snapshot, TestContext};
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
|
|
@ -538,6 +538,141 @@ fn run_with() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_with_editable() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let anyio_local = context.temp_dir.child("src").child("anyio_local");
|
||||||
|
copy_dir_all(
|
||||||
|
context.workspace_root.join("scripts/packages/anyio_local"),
|
||||||
|
&anyio_local,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let black_editable = context.temp_dir.child("src").child("black_editable");
|
||||||
|
copy_dir_all(
|
||||||
|
context
|
||||||
|
.workspace_root
|
||||||
|
.join("scripts/packages/black_editable"),
|
||||||
|
&black_editable,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
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.8"
|
||||||
|
dependencies = ["anyio", "sniffio==1.3.1"]
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let test_script = context.temp_dir.child("main.py");
|
||||||
|
test_script.write_str(indoc! { r"
|
||||||
|
import sniffio
|
||||||
|
"
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Requesting an editable requirement should install it in a layer.
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--with-editable").arg("./src/black_editable").arg("main.py"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 6 packages in [TIME]
|
||||||
|
Prepared 4 packages in [TIME]
|
||||||
|
Installed 4 packages in [TIME]
|
||||||
|
+ anyio==4.3.0
|
||||||
|
+ foo==1.0.0 (from file://[TEMP_DIR]/)
|
||||||
|
+ idna==3.6
|
||||||
|
+ sniffio==1.3.1
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ black==0.1.0 (from file://[TEMP_DIR]/src/black_editable)
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Requesting an editable requirement should install it in a layer, even if it satisfied
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--with-editable").arg("./src/anyio_local").arg("main.py"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 6 packages in [TIME]
|
||||||
|
Audited 4 packages in [TIME]
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ anyio==4.3.0+foo (from file://[TEMP_DIR]/src/anyio_local)
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Requesting the project itself should use the base environment.
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--with-editable").arg(".").arg("main.py"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 6 packages in [TIME]
|
||||||
|
Audited 4 packages in [TIME]
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Similarly, an already editable requirement does not require a layer
|
||||||
|
pyproject_toml.write_str(indoc! { r#"
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "1.0.0"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
dependencies = ["anyio", "sniffio==1.3.1"]
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
anyio = { path = "./src/anyio_local", editable = true }
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.sync(), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Uninstalled 3 packages in [TIME]
|
||||||
|
Installed 2 packages in [TIME]
|
||||||
|
- anyio==4.3.0
|
||||||
|
+ anyio==4.3.0+foo (from file://[TEMP_DIR]/src/anyio_local)
|
||||||
|
~ foo==1.0.0 (from file://[TEMP_DIR]/)
|
||||||
|
- idna==3.6
|
||||||
|
"###);
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--with-editable").arg("./src/anyio_local").arg("main.py"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
Audited 3 packages in [TIME]
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// If invalid, we should reference `--with-editable`.
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--with").arg("./foo").arg("main.py"), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
Audited 3 packages in [TIME]
|
||||||
|
× Invalid `--with` requirement
|
||||||
|
╰─▶ Distribution not found at: file://[TEMP_DIR]/foo
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn run_locked() -> Result<()> {
|
fn run_locked() -> Result<()> {
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,10 @@ uv run [OPTIONS] <COMMAND>
|
||||||
|
|
||||||
<p>When used in a project, these dependencies will be layered on top of the project environment in a separate, ephemeral environment. These dependencies are allowed to conflict with those specified by the project.</p>
|
<p>When used in a project, these dependencies will be layered on top of the project environment in a separate, ephemeral environment. These dependencies are allowed to conflict with those specified by the project.</p>
|
||||||
|
|
||||||
|
</dd><dt><code>--with-editable</code> <i>with-editable</i></dt><dd><p>Run with the given packages installed as editables</p>
|
||||||
|
|
||||||
|
<p>When used in a project, these dependencies will be layered on top of the project environment in a separate, ephemeral environment. These dependencies are allowed to conflict with those specified by the project.</p>
|
||||||
|
|
||||||
</dd><dt><code>--with-requirements</code> <i>with-requirements</i></dt><dd><p>Run with all packages listed in the given <code>requirements.txt</code> files.</p>
|
</dd><dt><code>--with-requirements</code> <i>with-requirements</i></dt><dd><p>Run with all packages listed in the given <code>requirements.txt</code> files.</p>
|
||||||
|
|
||||||
<p>The same environment semantics as <code>--with</code> apply.</p>
|
<p>The same environment semantics as <code>--with</code> apply.</p>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue