mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-03 10:33:49 +00:00
Respect verbatim executable name in uvx (#11524)
## Summary If the user provides a PEP 508 requirement (e.g., `uvx change_wheel_version`), then we should us that verbatim for the executable, rather than normalizing the package name. Closes https://github.com/astral-sh/uv/issues/11521.
This commit is contained in:
parent
172305abb6
commit
36b4fd2d2d
2 changed files with 112 additions and 3 deletions
|
@ -529,6 +529,22 @@ async fn get_or_create_environment(
|
|||
// Ex) `ruff>=0.6.0`
|
||||
Target::Unspecified(requirement) => {
|
||||
let spec = RequirementsSpecification::parse_package(requirement)?;
|
||||
|
||||
// Extract the verbatim executable name, if possible.
|
||||
let name = match &spec.requirement {
|
||||
UnresolvedRequirement::Named(..) => {
|
||||
// Identify the package name from the PEP 508 specifier.
|
||||
//
|
||||
// For example, given `ruff>=0.6.0`, extract `ruff`, to use as the executable name.
|
||||
let content = requirement.trim();
|
||||
let index = content
|
||||
.find(|c| !matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '_' | '.'))
|
||||
.unwrap_or(content.len());
|
||||
Some(&content[..index])
|
||||
}
|
||||
UnresolvedRequirement::Unnamed(..) => None,
|
||||
};
|
||||
|
||||
if let UnresolvedRequirement::Named(requirement) = &spec.requirement {
|
||||
if requirement.name.as_str() == "python" {
|
||||
return Err(anyhow::anyhow!(
|
||||
|
@ -539,6 +555,7 @@ async fn get_or_create_environment(
|
|||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
let requirement = resolve_names(
|
||||
vec![spec],
|
||||
&interpreter,
|
||||
|
@ -556,12 +573,14 @@ async fn get_or_create_environment(
|
|||
.pop()
|
||||
.unwrap();
|
||||
|
||||
// Use the executable provided by the user, if possible (as in: `uvx --from package executable`).
|
||||
//
|
||||
// If no such executable was provided, rely on the package name (as in: `uvx git+https://github.com/pallets/flask`).
|
||||
// Prefer, in order:
|
||||
// 1. The verbatim executable provided by the user, independent of the requirement (as in: `uvx --from package executable`).
|
||||
// 2. The verbatim executable provided by the user as a named requirement (as in: `uvx change_wheel_version`).
|
||||
// 3. The resolved package name (as in: `uvx git+https://github.com/pallets/flask`).
|
||||
let executable = request
|
||||
.executable
|
||||
.map(ToString::to_string)
|
||||
.or_else(|| name.map(ToString::to_string))
|
||||
.unwrap_or_else(|| requirement.name.to_string());
|
||||
|
||||
(executable, requirement)
|
||||
|
|
|
@ -1810,3 +1810,93 @@ fn tool_run_from_at() {
|
|||
+ executable-application==0.2.0
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_run_verbatim_name() {
|
||||
let context = TestContext::new("3.12")
|
||||
.with_filtered_counts()
|
||||
.with_filtered_exe_suffix();
|
||||
let tool_dir = context.temp_dir.child("tools");
|
||||
let bin_dir = context.temp_dir.child("bin");
|
||||
|
||||
// The normalized package name is `change-wheel-version`, but the executable is `change_wheel_version`.
|
||||
uv_snapshot!(context.filters(), context.tool_run()
|
||||
.arg("change_wheel_version")
|
||||
.arg("--help")
|
||||
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
usage: change_wheel_version [-h] [--local-version LOCAL_VERSION]
|
||||
[--version VERSION] [--delete-old-wheel]
|
||||
[--allow-same-version]
|
||||
wheel
|
||||
|
||||
positional arguments:
|
||||
wheel
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--local-version LOCAL_VERSION
|
||||
--version VERSION
|
||||
--delete-old-wheel
|
||||
--allow-same-version
|
||||
|
||||
----- stderr -----
|
||||
Resolved [N] packages in [TIME]
|
||||
Prepared [N] packages in [TIME]
|
||||
Installed [N] packages in [TIME]
|
||||
+ change-wheel-version==0.5.0
|
||||
+ installer==0.7.0
|
||||
+ packaging==24.0
|
||||
+ wheel==0.43.0
|
||||
"###);
|
||||
|
||||
uv_snapshot!(context.filters(), context.tool_run()
|
||||
.arg("change-wheel-version")
|
||||
.arg("--help")
|
||||
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
The executable `change-wheel-version` was not found.
|
||||
The following executables are provided by `change-wheel-version`:
|
||||
- change_wheel_version
|
||||
Consider using `uv tool run --from change-wheel-version <EXECUTABLE_NAME>` instead.
|
||||
|
||||
----- stderr -----
|
||||
Resolved [N] packages in [TIME]
|
||||
warning: An executable named `change-wheel-version` is not provided by package `change-wheel-version`.
|
||||
"###);
|
||||
|
||||
uv_snapshot!(context.filters(), context.tool_run()
|
||||
.arg("--from")
|
||||
.arg("change-wheel-version")
|
||||
.arg("change_wheel_version")
|
||||
.arg("--help")
|
||||
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
usage: change_wheel_version [-h] [--local-version LOCAL_VERSION]
|
||||
[--version VERSION] [--delete-old-wheel]
|
||||
[--allow-same-version]
|
||||
wheel
|
||||
|
||||
positional arguments:
|
||||
wheel
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--local-version LOCAL_VERSION
|
||||
--version VERSION
|
||||
--delete-old-wheel
|
||||
--allow-same-version
|
||||
|
||||
----- stderr -----
|
||||
Resolved [N] packages in [TIME]
|
||||
"###);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue