Add `--python` support to `uv run` (#3189)

This commit is contained in:
Zanie Blue 2024-04-24 11:03:50 -05:00 committed by GitHub
parent 005b76770e
commit eb27d742b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 59 additions and 24 deletions

View File

@ -1706,6 +1706,20 @@ pub(crate) struct RunArgs {
#[arg(long)]
pub(crate) with: Vec<String>,
/// The Python interpreter to use to build the run environment.
///
/// By default, `uv` uses the virtual environment in the current working directory or any parent
/// directory, falling back to searching for a Python executable in `PATH`. The `--python`
/// option allows you to specify a different interpreter.
///
/// Supported formats:
/// - `3.10` looks for an installed Python 3.10 using `py --list-paths` on Windows, or
/// `python3.10` on Linux and macOS.
/// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`.
/// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path.
#[arg(long, short, verbatim_doc_comment, group = "discovery")]
pub(crate) python: Option<String>,
/// Run without the current workspace installed.
#[arg(long)]
pub(crate) no_workspace: bool,

View File

@ -45,6 +45,7 @@ pub(crate) async fn run(
target: Option<String>,
mut args: Vec<OsString>,
mut requirements: Vec<RequirementsSource>,
python: Option<String>,
isolated: bool,
no_workspace: bool,
preview: PreviewMode,
@ -86,7 +87,15 @@ pub(crate) async fn run(
// Detect the current Python interpreter.
// TODO(zanieb): Create ephemeral environments
// TODO(zanieb): Accept `--python`
let run_env = environment_for_run(&requirements, &overrides, isolated, cache, printer).await?;
let run_env = environment_for_run(
&requirements,
&overrides,
python.as_deref(),
isolated,
cache,
printer,
)
.await?;
let python_env = run_env.python;
// Construct the command
@ -164,6 +173,7 @@ fn find_workspace_requirements() -> Result<Option<Vec<RequirementsSource>>> {
async fn environment_for_run(
requirements: &[RequirementsSource],
overrides: &[RequirementsSource],
python: Option<&str>,
isolated: bool,
cache: &Cache,
printer: Printer,
@ -194,35 +204,42 @@ async fn environment_for_run(
)
.await?;
// Check if the current environment satisfies the requirements
if let Some(venv) = current_venv {
// Determine the set of installed packages.
let site_packages = SitePackages::from_executable(&venv)?;
// If the requirements are already satisfied, we're done. Ideally, the resolver would be fast
// enough to let us remove this check. But right now, for large environments, it's an order of
// magnitude faster to validate the environment than to resolve the requirements.
if spec.source_trees.is_empty()
&& site_packages.satisfies(&spec.requirements, &spec.editables, &spec.constraints)?
{
debug!("Current environment satisfies requirements");
return Ok(RunEnvironment {
python: venv,
_temp_dir_drop: None,
});
}
}
// Otherwise, we need a new environment
// Find an interpreter to use
// TODO(zanieb): Populate `python` from the user
let python = None;
// Determine an interpreter to use
let python_env = if let Some(python) = python {
PythonEnvironment::from_requested_python(python, cache)?
} else {
PythonEnvironment::from_default_python(cache)?
};
// Check if the current environment satisfies the requirements
if let Some(venv) = current_venv {
// Ensure it matches the selected interpreter
// TODO(zanieb): We should check if a version was requested and see if the environment meets that
// too but this can wait until we refactor interpreter discovery
if venv.root() == python_env.root() {
// Determine the set of installed packages.
let site_packages = SitePackages::from_executable(&venv)?;
// If the requirements are already satisfied, we're done. Ideally, the resolver would be fast
// enough to let us remove this check. But right now, for large environments, it's an order of
// magnitude faster to validate the environment than to resolve the requirements.
if spec.source_trees.is_empty()
&& site_packages.satisfies(
&spec.requirements,
&spec.editables,
&spec.constraints,
)?
{
debug!("Current environment satisfies requirements");
return Ok(RunEnvironment {
python: venv,
_temp_dir_drop: None,
});
}
}
}
// Otherwise, we need a new environment
// Create a virtual environment
// TODO(zanieb): Move this path derivation elsewhere
let uv_state_path = std::env::current_dir()?.join(".uv");

View File

@ -507,6 +507,7 @@ async fn run() -> Result<ExitStatus> {
args.target,
args.args,
requirements,
args.python,
args.isolated,
args.no_workspace,
globals.preview,

View File

@ -88,6 +88,7 @@ pub(crate) struct RunSettings {
pub(crate) isolated: bool,
pub(crate) with: Vec<String>,
pub(crate) no_workspace: bool,
pub(crate) python: Option<String>,
}
impl RunSettings {
@ -100,6 +101,7 @@ impl RunSettings {
isolated,
with,
no_workspace,
python,
} = args;
Self {
@ -109,6 +111,7 @@ impl RunSettings {
isolated,
with,
no_workspace,
python,
}
}
}