Uv: Running a script with dependencies

docs.astral.sh

474 points by Bluestein 21 hours ago


simonw - 21 hours ago

The "declaring script dependencies" thing is incredibly useful: https://docs.astral.sh/uv/guides/scripts/#declaring-script-d...

  # /// script
  # dependencies = [
  #   "requests<3",
  #   "rich",
  # ]
  # ///
  import requests, rich
  # ... script goes here
Save that as script.py and you can use "uv run script.py" to run it with the specified dependencies, magically installed into a temporary virtual environment without you having to think about them at all.

It's an implementation of Python PEP 723: https://peps.python.org/pep-0723/

Claude 4 actually knows about this trick, which means you can ask it to write you a Python script "with inline script dependencies" and it will do the right thing, e.g. https://claude.ai/share/1217b467-d273-40d0-9699-f6a38113f045 - the prompt there was:

  Write a Python script with inline script
  dependencies that uses httpx and click to
  download a large file and show a progress bar
Prior to Claude 4 I had a custom Claude project that included special instructions on how to do this, but that's not necessary any more: https://simonwillison.net/2024/Dec/19/one-shot-python-tools/
hoherd - 18 hours ago

One gotcha I caught myself in with this technique is using it in a script that would remediate a situation where my home has lost internet and needed the router to be power cycled. When the internet is out, `uv` cannot download the dependencies specified in the script, and the script would fail. Thankfully I noticed this problem after writing it but before needing it to actually work, and refactored my setup to pre-install the needed dependencies. But don't make the same mistake I almost made! Don't use this for code that may need to run airgapped! Even with uv caching you may still get a cache miss.

gkfasdfasdf - 9 hours ago

I love this feature of uv but getting linters/language servers to pick up the venv when editing the files is a bit of a pain. I currently have a script 'uv-edit' which I am using to run Neovim with the correct environment:

  #!/bin/bash
  SCRIPT="$1"; shift
  uv sync --quiet --script "$SCRIPT" && exec uv run --python "$(uv python find --script "$SCRIPT")" nvim "$SCRIPT" "$@"
gopalv - 21 hours ago

This is my absolute favourite uv features and the reason I switched to uv.

I have a bunch of scripts in my git-hooks which have dependencies which I don't want in my main venv.

#!/usr/bin/env -S uv run --script --python 3.13

This single feature meant that I could use the dependencies without making its own venv, but just include "brew install uv" as instructions to the devs.

theesm - 10 hours ago

I've been doing something similiar utilizing guix shell[0] setting my shebang to e.g.:

#!/usr/bin/env -S guix shell python python-requests python-pandas -- python3

in scripts for including per-script dependencies. This is language agnostic as long as the interpreter and its dependencies are available as guix packages. I think there may be a similiar approach for utilizing nix shells that way as well.

[0]: https://guix.gnu.org/manual/en/html_node/Invoking-guix-shell...

Hackbraten - 14 hours ago

I wish there was a straightforward way to let VS Code pick up the venv that uv transparently creates.

Out of the box, the Python extension redlines all the third-party imports.

As a workaround, I have to plunge into the guts of uv's Cache directory to tell VS Code the cached venv path manually, and cross fingers that it won't recreate that venv too often.

AceJohnny2 - 20 hours ago

Note that this only works for single-file scripts.

If you have a project with modules, and you'd like a module to declare its dependencies, this won't work. uv will only get those dependencies declared in the invoked file.

For a multi-file project, you must have a `pyproject.toml`, see https://docs.astral.sh/uv/guides/projects/#managing-dependen...

In both cases, the script/project writer can use `uv add <dependency>`, just in the single-file case they must add `--script`.

staplung - 17 hours ago

Love this feature of UV. Here's a one-liner to launch jupyter notebook without even "installing" it:

  uv run --with jupyter jupyter notebook
Everything is put into a temporary virtual environment that's cleaned up afterwards. Best thing is that if you run it from a project it will pick up those dependencies as well.
m4r71n - 20 hours ago

Oh nice, I was already a happy user of the uv-specific shebang with in-script dependencies, but the `uv lock --script example.py` command to create a lock file that is specific to one script takes it to another level! Amazing how this feels so natural and yet only appeared after 20+ years of Python packaging.

sam_bristow - 12 hours ago

There are a lot of bits and pieces that are clicking into place lately in the Python ecosystem. Recently I've been using the combination of Marimo and these uv script dependencies for building reproducible rwporting and diagnostic tooling for other teams.

wanderingmind - 10 hours ago

As a tangent, one issue I face is how to force cursor to use uv over pip. I have placed it in rules and also explicitly add a rules.yml file in every agent conversation. It still tries to push through pip 4 out of 10 times. Any recommendations on best practice to force cursor to use uv will make a significant dent in productivity

heisenzombie - 20 hours ago

Quick plug here for a simple Jupyter kernel I created:

https://github.com/tobinjones/uvkernel

It’s a pretty minimal wrapper around “uv” and “iPython” to provide the functionality from the article, but for Jupyter notebooks. It’s similar to other projects, but I think my implementation is the least intrusive and a good “citizen” of the Jupyter ecosystem.

There’s also this work-in-progress:

https://github.com/tobinjones/pep723widget

Which provides a companion Jupyter plugin to manage the embedded script dependencies of noteboooks with a UI. Warning — this one is partially vibe-coded and very early days.

trostaft - 19 hours ago

This is pretty great. Passing python code out to my students is usually also confronted with the question of "How do I run it?", which is usually terrible to answer. Now, I can just tell them to get uv (single command) and run it.

frizlab - 6 hours ago

I do this in Swift, using swift-sh[1].

Works well, but the compilation of the dependencies (first run of the script) can be a bit long. A problem which uv (Python) probably won’t have…

[1] https://github.com/xcode-actions/swift-sh

runjake - 7 hours ago

If you’re at all interested in this topic, I strongly recommend you read Simon Willison’s blog at https://simonwillison.net/

He does all the legwork and boils it down into an easily-understandable manner.

He’s also a user her in this thread at simonw. Just an immensely useful guy.

rr808 - 19 hours ago

How many package managers can one language have? Its a simple language but setting it up is just incredibly bad. Maybe this is the one or should I wait for the next?

ezquerra - 14 hours ago

I recently found a small issue with `uv run`. If you run a script from outside the project folder, it looks for the pyproject.toml on the folder from which you are calling `uv run`, not on the folder where the python script is located (or its parents)! Because of that scripts that store their dependencies in a pyproject.toml cannot be run successfully using a “bare” `uv run path/to/my/script.py` from outside the project folder.

You can work around this surprising behavior by always using inline dependencies, or by using the `--project` argument but this requires that you type the script path twice which is pretty inconvenient.

Other than that uv is awesome, but this small quirk is quite annoying.

satertek - 21 hours ago

Why doesn't pip support PEP 723? I'm all for spreading the love of our lord and savior uv, but it should be necessary to have an official implementation.

stephenhandley - 11 hours ago

This almost as great as how much I love that this gets posted like once every two weeks

damnever - 11 hours ago

I like it, and I once thought about this as well:

> Python doesn't require a requirements.txt file, but if you don't maintain this file, or neglect to provide one, it could result in broken functionalities - an unfortunate circumstance for a scripting language.

https://twitter.com/_damnever/status/1697247813854503250

jemiluv8 - 10 hours ago

I kinda always struggled to grok python's package management and module system. UV is finally making things more understandable and just as good as the nodejs packaging and module system that I'm more comfortable with. This new feature is kinda awesome. Even surpassing the DX of the node ecosystem.

cassiogo - 9 hours ago

Maybe worth mentioning that ruby has a very similar feature https://bundler.io/guides/bundler_in_a_single_file_ruby_scri...

SafeDusk - 19 hours ago

I am using this feature in my MCP servers and it is a god send! Some details on how I do it can be found at https://blog.toolkami.com/toolkami-shttp-server/.

Humphrey - 20 hours ago

Oh this looks amazing! I had pretty much stopped using Python for my one-off scripts because of the hassle of dependencies. I can't wait to try this out.

ayerajath - 17 hours ago

https://docs.astral.sh/uv/guides/scripts/#declaring-script-d...

this is neat af. my throw-away scripts folder will be cleaner now.

hamandcheese - 19 hours ago

My favorite part of this is the `exclude-newer` feature, giving you a somewhat reproducible script for very low effort.

imjonse - 12 hours ago

One case this may not work well is when one of the dependencies is Pytorch. Uv has explicit support for Pytorch ( https://docs.astral.sh/uv/guides/integration/pytorch/ ) but with the script headers I don't see a way to pick the most appropriate wheel index (cpu vs cuda vs rocm)

jbaber - 18 hours ago

I just looked this up yesterday by sheer coincidence and was happy it actually worked. What brought it to your attention today?

exographicskip - 17 hours ago

Heard about this! Finally did something with it and I'm happy with how this tiny gist turned out:

https://gist.github.com/pythoninthegrass/e5b0e23041fe3352666...

tl;dr

Installs 3 deps into the uv cache, then does the following:

1. httpx to call get request from github api

2. sh (library) to run ls command for current directory

3. python-decouple to read an .env file or env var

poulpy123 - 11 hours ago

For this use case I would try first to compile the script with nuitka. A single binary is more manageable than doing that

pavlov - 9 hours ago

As a non-Python person, I find “uv” to be a confusing name.

For a long time I assumed it’s a Python wrapper for libuv which is a much older and well-known project. But apparently it’s Python package manager #137 “this one finally works, really, believe me”

imcritic - 19 hours ago

I hate such poor docs that don't explain how things work, and instead prefer hiding behind some "magic".

> Constraints can be added to the requested dependency if specific versions are needed:

> uv run --with 'rich>12,<13' example.py

Why not mention that this will make uv download specified versions of specified packages somewhere on the disk. Where? Are those packages going to get cached somewhere? Or will it re-download those same packages again and again every time you run this command?

abhisek - 16 hours ago

Uh oh. I am thinking all the ways this can be misused to ship malicious dependencies. Pretty much all SCA tools today will be blind to this.

Svoka - 19 hours ago

More comments the topic - https://news.ycombinator.com/item?id=44369388

Fun with uv and PEP 723

640 points | 227 comments

pmarreck - 7 hours ago

This is pretty easy to do if you have Nix

https://github.com/pmarreck/yt-transcriber

A commandline youtube transcription tool I built that runs on Mac (nix-darwin) which automatically pulls in all the dependencies it needs