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

Using uv run as a task runner #5903

Open
my1e5 opened this issue Aug 8, 2024 · 154 comments
Open

Using uv run as a task runner #5903

my1e5 opened this issue Aug 8, 2024 · 154 comments
Labels
enhancement New feature or improvement to existing functionality

Comments

@my1e5
Copy link
Contributor

my1e5 commented Aug 8, 2024

For those of us migrating over from Rye, one of its nice features is the built-in task runner using rye run and [tool.rye.scripts]. For example:

[tool.rye.scripts]
hello = "echo Hello from Rye!"
$ rye run hello
Hello from Rye!

It could have some more features - here is a selection of feature requests from the community:

A lot of these requested features are things that other 3rd party tools currently offer. I thought it might be useful to highlight a few other tools here, in particular because they also integrate with the pyproject.toml ecosystem and can be used with uv today.

Perhaps these can serve as some inspiration for a future uv run task runner and also in the meantime offer a solution for people coming over from Rye looking for a way to run tasks.

@chrisrodrigue
Copy link

Relevant comment from another issue: #5632 (comment)

@chrisrodrigue
Copy link

PDM supports this: https://pdm-project.org/latest/usage/scripts/

@charliermarsh charliermarsh added enhancement New feature or improvement to existing functionality preview Experimental behavior labels Aug 9, 2024
@charliermarsh
Copy link
Member

Yeah we plan to support something like this! We haven't spent time on the design yet.

@nikhilweee
Copy link

The pyproject standard already supports [project.scripts], so uv may not need to use its own table.
https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#creating-executable-scripts

@charliermarsh
Copy link
Member

[project.scripts] is a little different -- that's used to expose specific Python functions as executable scripts, and we do support that already.

@chrisrodrigue
Copy link

Perhaps naming this section tool.uv.tasks or tool.uv.aliases could help disambiguate that.

@nikhilweee
Copy link

nikhilweee commented Aug 10, 2024

Or maybe [tool.uv.run] to be consistent with the command uv run. Or we could even think about [tool.uv.commands].
I'm not a big fan of [tool.uv.scripts] since it conflicts with [project.scripts] and I myself got confused before.

@cdwilson
Copy link

This is the main thing I missed coming from hatch: https://hatch.pypa.io/dev/config/environment/overview/#scripts

@chrisrodrigue
Copy link

chrisrodrigue commented Aug 23, 2024

+1 to @nikhilweee suggestions. I think “command” reflects the intent/concept.

Hatch has an “environment” concept and supports running commands namespaced to an environment like so

hatch run test:cov

where “test” is a user-defined environment (with a dependency group) and “cov” is a user-defined command for that environment.

[tool.hatch.envs.test]
dependencies = [
 "pytest",
 "pytest-cov",
 "pytest-mock",
 "freezegun",
]

[tool.hatch.envs.test.scripts]
cov = 'pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=src'

[[tool.hatch.envs.test.matrix]]
python = ["3.8", "3.9", "3.10", "3.11", "3.12"]

I would be curious to hear the use cases of nesting dependency groups and commands into “environments” like this rather than defining them at the top-level (i.e. [tool.uv.commands]/[tool.uv.dev-dependencies]).

@pietroppeter
Copy link

since it has not been mentioned yet, adding as a possible inspiration for design of tasks also pixi: https://pixi.sh/latest/features/advanced_tasks/

@metaist
Copy link

metaist commented Aug 28, 2024

I happen to be writing a cross-project task runner that supports a bunch of formats (e.g. rye, pdm, package.json, Cargo.toml; even uv's workspace config).

For what it's worth, almost all python runners use tool.<name>.scripts for task config (presumably inspired by npm's package.json format), so it's somewhat of an easier upgrade path for people coming from other tools.

https://github.com/metaist/ds

@inoa-jboliveira
Copy link

Also related to this thread, I wish uvx was uv run instead of uv tool run when inside a project.

uv tool run seems like something you would do to play with a tool, like ruff. But then you will eventually uv add ruff --dev and forever keep writing uv run ruff check instead of uvx ruff check which won't respect the locked version and will be in different virtualenv. It also means the tool could be running on a different Python (does not apply to ruff, but any other python lib) and all sorts of weird stuff can happen.

I know uv run stuff is still short, but it could be 4 keystrokes shorter.

Regarding uv run being a task runner it means people will type it waaaaay more often than uv tool run.
I would appreciate a dedicated command like uvr

@zanieb
Copy link
Member

zanieb commented Sep 8, 2024

@inoa-jboliveira I opened a dedicated issue for that #7186

@nikhilweee
Copy link

Putting together some thoughts about semantics. This issue is about adding support for running arbitrary instructions specified in pyproject.toml. I deliberately use the term instruction to avoid using any of the other terms under consideration (command, tool, script, etc).

What do we call these instructions?

Lots of existing tools refer to them as "scripts".

  1. npm and yarn have first class support for scripts. Users can define them in package.json
  2. composer also uses the same term. Users can define them in composer.json
  3. pdm takes inspiration from npm and also uses the term scripts. Users can define them in [tool.pdm.scripts]
  4. rye follows suit. Custom scripts are defined in [tool.rye.scripts]
  5. hatch also uses the term scripts, although they are tied to environments. Defined in [tool.hatch.envs.<env>.scripts]

It seems advantageous to just go with the term "scripts" because it is the de-facto standard. As noted by another user #5903 (comment), this would also reduce friction for users coming to uv from other package managers. That said, this approach has a major flaw because it overlaps with the concept of entry points defined in [project.scripts]. Entry points expose certain python functions as executable scripts, but do not allow arbitrary commands. Furthermore, [project.scripts] has already been established in PEP-0621, as the official spec. So what about other terms?

Another option is to use the term "tasks"

  1. pixi uses the term "tasks". Users can define them in the [tasks] table in pixi.toml
  2. bundler uses rake tasks. Although it resembles entry points, sh is supported.
  3. grunt and gulp also use the term "tasks". Although they are task runners, not package managers.
  4. gradle uses the term "tasks", defined in build.gradle

Another option is to call them "executables". dart uses this term in pubspec.yaml

We could also use "commands", although I wasn't able to find existing tools which use this term.

After settling on a name, an obvious thing to do is to let users define instructions in the [tool.uv.<name>] table.

How do we invoke these instructions?

There are two options here.

  1. Overload existing uv run <instruction> (follows from npm run <script>)
  2. Add a new subcommand uv invoke / uv command / uv task

How should these instructions be specified?

PDM's documentation around user scripts is pretty evolved, with support for a bunch of features.

  1. cmd mode, shell mode, composite mode
  2. Specify env vars and env files for each script
  3. Specify working dir and site packages for each script
  4. Specify order of arguments
  5. Specify pre and post scripts
  6. call a function from a python script (entry point)

Rye has its own format, which is a subset of PDM features.

  1. Specify env vars and env files for each script
  2. chain multiple scripts one after the other
  3. call a function from a python script (entry point)

I hope this serves as a starter for discussing additional details for this feature.

@charliermarsh
Copy link
Member

(Nice comment, thank you!)

@Xdynix
Copy link

Xdynix commented Sep 10, 2024

One nice thing about PDM is that if a command is not recognized as a built-in, it is treated as pdm run. Thus, pdm foobar would be shorthand for pdm run foobar, which executes the command defined in [tool.pdm.scripts.foobar].

@gdamjan
Copy link

gdamjan commented Sep 10, 2024

IMHO, pdm has the most extensive support for these scripts and I would personally like to see the same support in uv too. And if so, best to support the same pyproject section too 😱. It's especially nice when one doesn't have to use any other tools like Makefiles (ugh).

Alas, one can argue that the pdm support for scripts is feature-creep for uv, in which case the rye model works too :)

@patrick91
Copy link

Furthermore, [project.scripts] has already been established in PEP-0621, as the official spec. So what about other terms?

I wonder if would be a good time to maybe standardise this? I don't know if a pep is required, but since we have some many package managers for python it would be nice if we can have one way to define these instructions

@brandon-avantus
Copy link

I started with Rye, and was really enjoying its built-in task runner. But it was missing some features, so I started a Python version to experiment with. Then I made the switch to uv and was missing that built-in task runner, so I continued working on, and just published, my Python task runner, pyproject-runner, and the related pyproject-runner-shim.

pyproject-runner is similar to Rye's task runner, and should require few changes for those moving to uv from Rye. See the FAQ answering "why not use an existing tool?"

I am open to using pyproject-runner as a playground to test features that might go into an official uv task runner.

@niraj-khatiwada
Copy link

niraj-khatiwada commented Jan 29, 2025

Man, I just switched from pipenv and was so excited that we finally have a worthy package manager in Python. Just stumbled that we cannot use custom scripts which I miss from pipenv. I'll stick with make for the time being but I really hope this gets implemented soon. Kudos!

@haydenk
Copy link

haydenk commented Feb 10, 2025

I might be missing a key detail which is why it's not working but even being able to run local modules in the project would be nice.

# tests/runner.py

def hello():
    print('Hello')

def world():
    print('World')

Being able to run this as uv run --module tests.runner:hello and uv run --module tests.runner:world would be nice

Hellgartner added a commit to opossum-tool/opossum-file that referenced this issue Feb 13, 2025
* Similar to npm run, the attempt was to unify test commands
* up to now, this is done using individual shell scripts on the devs' machines
* this is the attempt to unify this
* Note, the additional dependency on taskipy can hopefully be removed as soon as
astral-sh/uv#5903
is resolved
@mrh1997
Copy link

mrh1997 commented Feb 22, 2025

As running tasks is an action was usually has to be done frequently it would be great if typing them can be done fast. Thus for example the widespread nodejs tool pnpm allows shortcuting the run command be omiting "run" if the task is not in conflict with any uv command.

Thus instead of writing

pnpm run task

I could write

pnpm task

In my opinion it would be really great if uv provides a similar DX.

@d-k-bo
Copy link

d-k-bo commented Feb 22, 2025

@mrh1997 This could lead to ambiguous behaviour. If you want to type less, you could just alias ur="uv run".

@mrh1997
Copy link

mrh1997 commented Feb 22, 2025

The downside of an alias is, that it is not available in every environment. I.e. if I support a collegue or created a new docker container I had to fall back to "uv run".

How about providing this shortcut by uv? Like you did with uvx.

Then one could run for example

uvr task

@Xdynix
Copy link

Xdynix commented Feb 22, 2025

@mrh1997 @d-k-bo The simplest way is to treat commands as tasks only if they're not conflicting with uv's built-in commads. So uv python will never be expended to uv run python or uv task python. This is also how pdm implement such feature.

There is a builtin shortcut making all scripts available as root commands as long as the script does not conflict with any builtin or plugin-contributed command.

@neutrinoceros
Copy link

This way it limits the design space for new uv commands is concerning. I don't see how it could be worth the cost.

@Xdynix
Copy link

Xdynix commented Feb 22, 2025

@neutrinoceros The shortcut is just an add-on. If a uv command with the same name as my task is added in the future, I can just rename my task, or use uv task foobar/uv run foobar to run the task. I don't see it limiting uv.

@neutrinoceros
Copy link

neutrinoceros commented Feb 23, 2025

Multiply that experience by the number of users and you're guaranteed to generate friction, confusion and complaints every time a new command is introduced. I simply don't think it's worth it.

@Xdynix
Copy link

Xdynix commented Feb 23, 2025

You can choose not to use the shortcut feature then, you don't lose anything. The point is to provide an option that trades the benefit of having to type less often, at the expense of the (very small) chance that your shortcut will break.

@neutrinoceros
Copy link

Would you assume that every user opting in would necessarily understand the risk around upgrades ? What happens when someone integrates such a command into a CI job with an unpinned uv version ? Maybe this person understands the risk, and their reviewers don't.
Is it reasonable to assume that this situation will never create unnecessary friction ? How does it fit with astral's extreme caution with breaking updates (see uv 0.6.0's release notes) ?

@Xdynix
Copy link

Xdynix commented Feb 23, 2025

A reasonable concern. All I can say is that no matter how much design you do, you can never completely prevent users from making mistakes. That will be a tradeoff for Astral to make. I will only express my desire for this feature.

@mrh1997
Copy link

mrh1997 commented Feb 23, 2025

@neutrinoceros I got your arguement about the risk on breaking changes.

But what do you think about my proposal of a standardized shortcut like uvx for the run command (i.e. uvr)?

@neutrinoceros
Copy link

neutrinoceros commented Feb 23, 2025

I don't have a strong opinion there.
pros:

  • I agree it feels on-brand
  • it doesn't create an enormous pile of technical debt/maintenance burden

cons:

  • it creates a small opportunity for confusion (the same goes for uvx)
  • to me uv run is already very minimal typing. Compare with uvx which is a shorthand for uv tool run, and I'm not sure it's worth it

I would probably use it if it existed, though I'm also happy to live without it.

@Chewie
Copy link

Chewie commented Feb 24, 2025

Silly two cents: I tend to always have a dedicated language-agnostic task runner like Task or Just and wrap commands with that to also have non-python scripts under a common interface, so the verbosity of adding run/task to uv isn't a big problem for me.

@mattmess1221
Copy link

mattmess1221 commented Feb 24, 2025

I've recently started using bash scripts with uv run in the shebang. It does the normal uv run things like automatically installing dependencies and activating the venv. I find it a suitable stopgap until uv gets its own shell agnostic task runner.

scripts/test.sh

#!/usr/bin/env -S uv run --group test bash
# shellcheck shell=bash
set -xeuo pipefail

coverage run -pm pytest
coverage combine
coverage report

pyproject.toml

[dependency-groups]
test = [
  "coverage",
  "pytest",
]

@OCopping
Copy link

OCopping commented Feb 27, 2025

Not sure if this has been suggested before, but it would nice if it could also support chaining scripts such as:

uv run script1,script2

This is similar to how tox can run environments, which my company is seriously trying to move away from...

tox -e env1,env2,env3

This would save having to write a separate entry under a [tool.uv.scripts] for each individual combo of scripts you could ever want to run.

hatch is a nice alternative to tox, but one of the downsides of hatch envs/scripts in my opinion is that you either have to write a new entry under [tool.hatch.envs.defaults.scrpts] or chain whole run commands such as:

hatch run script1; hatch run script2

@lucabello
Copy link

@OCopping interestingly enough, this use case would also be covered by a uv task with a default command.

Assuming your pyproject.toml contains:

[tool.uv.tasks]
default = "uvx --from=rust-just just"
lint = "uvx --from=rust-just just lint"
test = "uvx --from=rust-just test"

you could then do:

uv task test lint # internally, this would run: uvx -from=rust-just just test lint

@zgana
Copy link

zgana commented Mar 2, 2025

@mattmess1221 wrote:

I've recently started using bash scripts with uv run in the shebang. It does the normal uv run things like automatically installing dependencies and activating the venv. I find it a suitable stopgap until uv gets its own shell agnostic task runner.

scripts/test.sh

#!/usr/bin/env -S uv run --group test bash

This is great. I prefer to persist per-group environments, similar to Hatch. This can be achieved with a minor tweak:

#!/usr/bin/env -S UV_PROJECT_ENVIRONMENT=.venv-test uv run --group=test bash

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or improvement to existing functionality
Projects
None yet
Development

No branches or pull requests