Skip to content
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

uv run: List available scripts when a script is not specified #7687

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

Conversation

kakkoyun
Copy link

@kakkoyun kakkoyun commented Sep 25, 2024

Signed-off-by: Kemal Akkoyun kakkoyun@gmail.com

Summary

This PR adds the ability to list available scripts in the environment
when uv run is invoked without any arguments.
It somewhat mimics the behavior of rye run command
(See https://rye.astral.sh/guide/commands/run).

This is an attempt to fix #4024.

Test Plan

I added test cases. The CI pipeline should pass.

Manuel Tests

❯ uv run
Provide a command or script to invoke with `uv run <command>` or `uv run script.py`.

The following scripts are available:

normalizer
python
python3
python3.12

See `uv run --help` for more information.

@kakkoyun kakkoyun force-pushed the list_available_scripts_and_executables branch from 3b07e17 to e95ee42 Compare September 25, 2024 15:48
.filter(|entry| {
entry
.file_type()
.is_ok_and(|file_type| file_type.is_file() || file_type.is_symlink())
Copy link
Member

Choose a reason for hiding this comment

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

Should we handle symlinks like we do in uv python list? i.e. displaying "/opt/homebrew/opt/python@3.12/bin/python3.12 -> ../Frameworks/Python.framework/Versions/3.12/bin/python3.12"

Copy link
Author

Choose a reason for hiding this comment

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

I think we should show the filename. We don't need to be super descriptive here. Propabaly this usage flow will be like:

❯ cargo run -q -- run
Provide a command or script to invoke with `uv run <command>` or `uv run script.py`.

The following scripts are available:

python3
python3.12
python
normalizer

See `uv run --help` for more information.
❯ cargo run -q -- run python
No entry for terminal type "xterm-256color";
using dumb terminal settings.
Python 3.12.1 (main, Jan  8 2024, 05:57:25) [Clang 17.0.6 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

})
.map(|entry| entry.path())
.filter(|path| is_executable(path))
.for_each(|path| println!("{}", path.display()));
Copy link
Member

Choose a reason for hiding this comment

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

We should probably just display the name of the file, not the full path since we're making suggestions for uv run <name>.

Some(RunCommand::try_from(command)?)
match command {
Some(command) => Some(RunCommand::try_from(command)?),
None => Some(RunCommand::Empty),
Copy link
Member

@zanieb zanieb Sep 25, 2024

Choose a reason for hiding this comment

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

How is Some(RunCommand::Empty) different than None?

Copy link
Author

Choose a reason for hiding this comment

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

I'm just following the existing function signatures here. I preferred to use Empty since it was available rather than changing to Option<RunCommand>.

I'm not sure if there is a general convention/style to follow here.

Copy link
Member

Choose a reason for hiding this comment

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

Isn't run_command already Option<RunCommand> though?

Copy link
Author

Choose a reason for hiding this comment

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

Not in this particular context

pub(crate) async fn run(
project_dir: &Path,
script: Option<Pep723Script>,
command: RunCommand,
requirements: Vec<RequirementsSource>,
show_resolution: bool,
locked: bool,
frozen: bool,
no_sync: bool,
isolated: bool,
package: Option<PackageName>,
no_project: bool,
no_config: bool,
extras: ExtrasSpecification,
dev: DevMode,
editable: EditableMode,
python: Option<String>,
settings: ResolverInstallerSettings,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
connectivity: Connectivity,
concurrency: Concurrency,
native_tls: bool,
cache: &Cache,
printer: Printer,
) -> anyhow::Result<ExitStatus> {

Copy link
Member

@zanieb zanieb Sep 25, 2024

Choose a reason for hiding this comment

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

Ah but it is at

let run_command = if let Commands::Project(command) = &*cli.command {

and

command: Option<RunCommand>,

then we unwrap at

let command = command.expect("run command is required");

The Empty variant is stale code i.e. we actually don't do this anymore

/// Execute an empty command (in practice, `python` with no arguments).
Empty,

I'd think I'd prefer to just make it an Option<...> and remove the expect call, but I don't feel strongly. I guess if it's an Option someone could make a mistake upstream while developing and we wouldn't notice? Regardless we should update the RunCommand::Empty documentation.

@kakkoyun kakkoyun force-pushed the list_available_scripts_and_executables branch from e95ee42 to 26a3552 Compare September 25, 2024 17:28
Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com>
@kakkoyun kakkoyun force-pushed the list_available_scripts_and_executables branch from 26a3552 to 3c1be57 Compare September 25, 2024 17:44
@kakkoyun kakkoyun marked this pull request as ready for review September 25, 2024 17:44
Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com>
@kakkoyun kakkoyun force-pushed the list_available_scripts_and_executables branch from acc79e2 to 1c3691e Compare September 25, 2024 19:45
Comment on lines +252 to +258
activate.bat
activate.csh
activate.fish
activate.nu
activate.ps1
activate_this.py
deactivate.bat
Copy link
Member

Choose a reason for hiding this comment

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

I think we'll want to exclude all the shell activation scripts

Comment on lines +260 to +261
python.exe
pythonw.exe
Copy link
Member

Choose a reason for hiding this comment

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

We'll probably also want to exclude the .exe suffix since it's implied during invocation.

success: true
exit_code: 0
----- stdout -----
Provide a command or script to invoke with `uv run <command>` or `uv run script.py`.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Provide a command or script to invoke with `uv run <command>` or `uv run script.py`.
Provide a command or script to invoke with `uv run <command>` or `uv run <script>.py`.

----- stdout -----
Provide a command or script to invoke with `uv run <command>` or `uv run script.py`.
The following scripts are available:
Copy link
Member

Choose a reason for hiding this comment

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

We should probably say "commands" here not "scripts" since above you suggest running a script as script.py and it risks user's conflating the concepts.

"\nSee `uv run --help` for more information."
)?;

return Ok(ExitStatus::Success);
Copy link
Member

Choose a reason for hiding this comment

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

Should we exit with a non-zero exit code? I'm not sure but I think so?

Comment on lines +229 to +232
python
python3
python3.12
Copy link
Member

Choose a reason for hiding this comment

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

I think I have a minor preference for omitting the leading empty newline and listing these with the prefix - .

pub(crate) fn is_executable(path: &Path) -> bool {
pub fn is_executable(path: &Path) -> bool {
Copy link
Member

Choose a reason for hiding this comment

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

We might want to move this to uv-fs instead of uv-python now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

uv run and uv tool run should list available scripts and executables
2 participants