Skip to content

More descriptive error message for uv run pythonx.xx #12201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

JWLee89
Copy link

@JWLee89 JWLee89 commented Mar 16, 2025

Related Issue

#11796

Summary

  1. Add meaningful message when user invokes uv with run python<valid-python-version>.

✅ "python", "python3", "python3.9", "python4", "python3.10", "python3.13.3", "python39" (yes, python39 might still be released in 100 years 😆 )
❌ "python3abc", "python3.12b3", "", "python-foo"

  1. Contextual information added depending on whether the command was run inside of a uv project or not. See: https://github.com/astral-sh/uv/pull/12201/files#diff-423c3a82aab6dba4068dd7675391fc9328a66682131dbf2043e7847ab4ba8661R1092

Automated Tests

  1. Added unit test for new functions created for parsing and validating python executable entered by user
  2. TODO: Once specs are finalized through discussions in PR, will create integration tests if necessary

Need to Discuss

  1. Invoking python via its patch version - E.g. python3.11.9 is invalid. However, uv supports uv run -p 3.11.9 python, which is why when we do the following:
uv run python3.11.9

We will get the following error:

error: Failed to spawn: `python3.11.9`
  Caused by: `python3.11.9` not available in the environment, which uses python `3.11.9`. Did you mean to search for a Python 3.11.9 environment with `uv run -p 3.11.9 python`?

Is this okay? If not, we need to define the specs in the PR here.

  1. Supported python executable specs are defined as:
  • is stable version
  • is not post version

See:https://github.com/astral-sh/uv/pull/12201/files#diff-423c3a82aab6dba4068dd7675391fc9328a66682131dbf2043e7847ab4ba8661R1572-R1574

Would this be okay?
We can change the specs if needed. Lets discuss.

Manual Test

Case 1: run outside of project

  1. Case where no project is created. Look for a python executable version that is not available in my local machine (in my case, it was python3.9)
  2. Run cargo run -- run python3.9.3
error: Failed to spawn: `python3.9.3`
  Caused by: `python3.9.3` not available in the environment, which uses python `3.11.9`. Did you mean to search for a Python 3.9.3 environment with `uv run -p 3.9.3 python`?
  1. Run cargo run -- run python3.9
error: Failed to spawn: `python3.9`
  Caused by: `python3.9` not available in the environment, which uses python `3.11.9`. Did you mean to search for a Python 3.9 environment with `uv run -p 3.9 python`?
  1. Run cargo run -- run python3.11
Running `target/debug/uv run python3.11`
Python 3.11.9 (main, Apr  2 2024, 08:25:04) [Clang 15.0.0 (clang-1500.3.9.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
  1. Run cargo run -- run python3.11.9
 Running `target/debug/uv run python3.11.9`
error: Failed to spawn: `python3.11.9`
  Caused by: `python3.11.9` not available in the environment, which uses python `3.11.9`. Did you mean to search for a Python 3.11.9 environment with `uv run -p 3.11.9 python`?

IMPORTANT: Is this acceptable? Invoking python by its patch version is not supported, so this is the expected behavior, but we need to discuss whether this behavior is acceptable.

Case 2: run inside of project

Create project using uv init example-app

  1. uv run python3.13
error: Failed to spawn: `python3.13`
  Caused by: `python3.13` not available in the project environment, which uses python `3.12.5`. Did you mean to change the environment to Python 3.13 with `uv run -p 3.13 python`?
  1. uv run python3
uv run python3
Python 3.12.5 (main, Aug  6 2024, 19:08:49) [Clang 15.0.0 (clang-1500.3.9.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

@zanieb
Copy link
Member

zanieb commented Apr 16, 2025

Hey @JWLee89, thanks for the contribution! This looks like a nice improvement. We can provide some feedback to unblock adding tests.

if err.kind() == std::io::ErrorKind::NotFound && is_python_executable(&executable) {
// Get version from python command string
// e.g. python3.12 -> "3.12" or "" if python version not specified.
let version_part = executable.strip_prefix("python").unwrap_or("");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should is_python_executable be python_executable_version -> Some(str) instead? Then you can avoid the unwrap_or?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied: 0cb2d5e

@zanieb
Copy link
Member

zanieb commented Apr 16, 2025

I think the semantics make sense here. I wonder if we can reuse anything from the uv-python crate, but nothing obvious comes to mind.

@zanieb zanieb added the error messages Messaging when something goes wrong label Apr 16, 2025
@jtfmumm jtfmumm self-assigned this Apr 16, 2025
if err.kind() == std::io::ErrorKind::NotFound && is_python_executable(&executable) {
// Get version from python command string
// e.g. python3.12 -> "3.12" or "" if python version not specified.
let version_part = executable.strip_prefix("python").unwrap_or("");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe specified_version instead of version_part?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied: e4a2fa1

"python4",
"python",
"python3.11.3",
"python39", // Still a valid executable, although likely a typo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is likely a typo and we probably don't have to worry about the distant future at the moment, should we detect cases over 37 and treat them as typos? Not necessary for this PR though

Copy link
Author

@JWLee89 JWLee89 Apr 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be a good idea, especially since python version 37 will not likely be released anytime in the future while we are still alive 😆 .

Still I think we should carefully discuss this and come up with a reasonable spec before making any feature changes.

Would it be okay if I create a separate PR for this once we discuss and decide on specifications here?

/// - ❌ "python3abc", "python3.12b3", "", "python-foo"
fn is_python_executable(executable_command: &str) -> bool {
executable_command
.strip_prefix("python")
Copy link
Member

@zanieb zanieb Apr 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting this isn't sufficient for Windows, where you'll have a .exe suffix (though versioned executables are not common there)

Copy link
Author

@JWLee89 JWLee89 Apr 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zanieb

Thank you for the followup!

Windows handling logic

Good point! I don't a windows machine available to me right now, but my guess is that the access pattern for invoking python will be something like the following:

# versioned executable
python3.11.exe
# General 
python.exe

Would this be correct?

A simple approach could be also stripping suffix .exe. I don't think we should introduce complexity by checking whether the host is running windows unless this contextual information is already available without extra compute somewhere else.

Additional Possible Cases for Windows.

From some quick ChatGPT search, I found the following:

Command Notes
python After installing via the official installer, adds to PATH.
py Python launcher for Windows. Supports version flags like py -3.11.
python.exe Explicit executable, useful in scripts or batch files.
py -X.Y e.g., py -3.10 to launch a specific version via Python launcher.
pythonw.exe Launches Python without a console window (GUI apps).

Out of these items above, do we need to support the py launcher (E.g. py)? Just wanted to be sure :)

Edge cases

After thinking I just realized that people might invoke python using the following paths:

# absolute path
/usr/bin/python3
# venv path 
/Users/username/repos/uv/venv/bin/python3.10

Should these kinds of absolute path specifications also be covered by this feature update?

CC: @jtfmumm

Thank you!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied here: e6a801d

Specs include:

  • strip .exe suffix
  • Support absolute path python. E.g. /Users/username/repos/uv/venv/bin/python3.10

Since spec was defined on the fly, might be good to review and discuss this together.

Thank you! 🙇

@JWLee89 JWLee89 requested review from zanieb and jtfmumm April 20, 2025 13:21
zanieb added a commit that referenced this pull request Apr 21, 2025
As I suspected quite some time ago
(#6738 (comment)),
it's problematic that we don't handle _every_ signal here. This PR adds
handling for all of the Unix signals except `SIGCHLD`, `SIGIO`, and
`SIGPOLL` which seem incorrect to forward. Also notable, we _cannot_
handle `SIGKILL` so if someone sends that to the PID instead of the
PGID, they will leave dangling subprocesses.

Instead, we could use `exec` and avoid this handling. However, we'd lose
the ability to add nice error message on failure (e.g., as someone is
trying to add in #12201) and, more
critically, we'd need to figure out how to clean up resources properly
(i.e., temporary directories) which currently happens on `Drop`. In the
long-term, we'll probably want an option to use `exec` — but we'll need
to figure out when to clean up resources or accept that they will
dangle. This was last discussed in
#3095 — discussion on that
approach should continue there.

A note on the implementation: I spent time time trying to write the
handler using a tokio stream, so we could dynamically iterate over a
list of signals instead of copy/pasting the implementation — I couldn't
get it to work though and it didn't seem critical.

Closes #12830
@konstin konstin changed the title Issue-11796: Provide more descriptive message for uv run pythonx.xx Provide more descriptive message for uv run pythonx.xx Apr 24, 2025
@konstin konstin changed the title Provide more descriptive message for uv run pythonx.xx More descriptive error message for uv run pythonx.xx Apr 24, 2025
@jtfmumm
Copy link
Contributor

jtfmumm commented Apr 24, 2025

I pushed some snapshot (integration) tests for a handful of cases.

I think we might want to update the error messages for uv run pythonX.X.X (the case you called out in your description). At least when it matches the patch version in use, it's a bit confusing. Though it's nice to have the rest of the message when it's not matched. But it seems like an opportunity to indicate that this is not expected usage. The first snapshot test I added captures this behavior.

@JWLee89
Copy link
Author

JWLee89 commented Apr 26, 2025

I pushed some snapshot (integration) tests for a handful of cases.

Thank you so much for adding the snapshot (integration) tests! 🙇
I will keep this in mind when working on future PRs to add integration tests when necessary.

I think we might want to update the error messages for uv run pythonX.X.X (the case you called out in your description). At least when it matches the patch version in use, it's a bit confusing. Though it's nice to have the rest of the message when it's not matched. But it seems like an opportunity to indicate that this is not expected usage. The first snapshot test I added captures this behavior.

I absolutely agree and thank you for bringing this up!
Applied: 9976500

I should have clarified about this point properly in this PR.
In my environment, I reproduced it with the following command:

uv run python3.13.2

Results:

`python3.13.2` not available in the virtual environment, which uses python `3.13.2`. Did you mean to search for a Python 3.13.2 environment with `uv run -p 3.13.2 python`?

Description of update

When users specify up to the patch version:

uv run python3.3.3

I updated so that if users specify up to the patch version. E.g. python3.12.x, we get a error message that tells users to either use uv run -p or use uv run without the patch version (see below).

error: Failed to spawn: `python3.3.3`
  Caused by: Please omit patch version. Try: `uv run python3.3`. Did you mean to search for a Python 3.3.3 environment with `uv run -p 3.3.3 python`?

However, if we omit patch version, we will get the following (same behavior as before):

error: Failed to spawn: `python3.3`
  Caused by: `3.3` not available in the virtual environment, which uses python `3.13.2`. Did you mean to search for a Python 3.3 environment with `uv run -p 3.3 python`?

Note: message is subject to discussion and I think after iterating together, we can come up with the finalized message soon :).
For now, I just want to emphasize that I implemented the logic to change the error message depending on whether patch version is omitted or not.

@JWLee89 JWLee89 force-pushed the issue-11796 branch 3 times, most recently from 489d8b2 to 87c1ad7 Compare April 26, 2025 09:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
error messages Messaging when something goes wrong
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants