top | item 44369388

Fun with uv and PEP 723

640 points| deepakjois | 8 months ago |cottongeeks.com

227 comments

order
[+] ACAVJW4H|8 months ago|reply
finally feels like Python scripts can Just Work™ without a virtualenv scavenger hunt.

Now if only someone could do the same for shell scripts. Packaging, dependency management, and reproducibility in shell land are still stuck in the Stone Ages. Right now it’s still curl | bash and hope for the best, or a README with 12 manual steps and three missing dependencies.

Sure, there’s Nix... if you’ve already transcended time, space, and the Nix manual. Docker? Great, if downloading a Linux distro to run sed sounds reasonable.

There’s got to be a middle ground simple, declarative, and built for humans.

[+] nothrabannosir|8 months ago|reply
Nix is overkill for any of the things it can do. Writing a simple portable script is no exception.

But: it’s the same skill set for every one of those things. This is why it’s an investment worth making IMO. If you’re only going to ever use it for one single thing, it’s not worth it. But once you’ve learned it you’ll be able to leverage it everywhere.

Python scripts with or without dependencies, uv or no uv (through the excellent uv2nix which I can’t plug enough, no affiliation), bash scripts with any dependencies you want, etc. suddenly it’s your choice and you can actually choose the right tool for the job.

Not trying to derail the thread but it feels germane in this context. All these little packaging problems go away with Nix, and are replaced by one single giant problem XD

[+] traverseda|8 months ago|reply
I don't think nix is that hard for this particular use case. Installing nix on other distros is pretty easy, and once it's installed you just do something like this

    #! /usr/bin/env nix-shell
    #! nix-shell -i bash -p imagemagick cowsay

    # scale image by 50%
    convert "$1" -scale 50% "$1.s50.jpg" &&
    cowsay "done $1.q50.jpg"
Sure all of nixos and packaging for nix is a challenge, but just using it for a shell script is not too bad
[+] bigstrat2003|8 months ago|reply
> Packaging, dependency management, and reproducibility in shell land are still stuck in the Stone Ages.

IMO it should stay that way, because any script that needs those things is way past the point where shell is a reasonable choice. Shell scripts should be small, 20 lines or so. The language just plain sucks too much to make it worth using for anything bigger.

[+] wpm|8 months ago|reply
I simply do not write shell scripts that use or reference binaries/libraries that are no pre-installed on the target OS (which is the correct target, writing shell scripts for portability is silly).

There is no package manager that is going to make a shell script I write for macOS work on Linux if that script uses commands that only exist on macOS.

[+] ndr|8 months ago|reply
Why bother writing new shell scripts?

If you're allowed to install any deps go with uv, it'll do the rest.

I'm also kinda in love with https://babashka.org/ check it out if you like Clojure.

[+] wazzaps|8 months ago|reply
Check out mise: https://mise.jdx.dev/

We use it at $work to manage dev envs and its much easier than Docker and Nix.

It also installs things in parallel, which is a huge bonus over plain Dockerfiles

[+] andenacitelli|8 months ago|reply
+1 for Mise, it has just totally solved the 1..N problem for us and made it hilariously easy to be more consistent across local dev and workflows
[+] yard2010|8 months ago|reply
That's a shame as I got to a monk-level python jujitsu. I can fix any problem, you name it, https nightmare, brew version vs pyenv, virtualenv shenanigans. Now all this knowledge is a bad investment of time.
[+] password4321|8 months ago|reply
I'm unable to resist responding that clearly the solution is to run Nix in Docker as your shell since packaging, dependency management, and reproducibility will be at theoretical maximum.
[+] bjackman|8 months ago|reply
For the specific case of solving shell script dependencies, Nix is actually very straightforward. Packaging a script is a writeShellApplication call and calling it is a `nix run`.

I guess the issue is just that nobody has documented how to do that one specific thing so you can only learn this technique by trying to learn Nix as a whole.

So perhaps the thing you're envisaging could just be a wrapper for this Nix logic.

[+] pxc|8 months ago|reply
I use Nix for this with resholve and I like it a lot.
[+] fouronnes3|8 months ago|reply
Consider porting your shell scripts to Python? The language is vastly superior and subprocess.check_call is not so bad.
[+] Narushia|8 months ago|reply
> Great, if downloading a Linux distro to run sed sounds reasonable.

There's a reason why distroless images exist. :)

[+] db48x|8 months ago|reply
Guix is easier to grok than Nix, if anyone is looking to save themselves some effort.
[+] est|8 months ago|reply
> finally feels like Python scripts can Just Work™ without a virtualenv scavenger hunt.

Hmm, last time I checked, uv installs into ~/.local/share/uv/python/cpython-3.xx and can not be installed globally e.g. inside a minimal docker without any other python.

So basically it still runs in a venv.

[+] epistasis|8 months ago|reply
This is really great, and it seems that it's becoming more popular. I saw it first on simonw's blog:

https://simonwillison.net/2024/Dec/19/one-shot-python-tools/

And there was a March discussion of a different blog post:

https://news.ycombinator.com/item?id=43500124

I hope this stays on the front page for a while to help publicize it.

[+] puika|8 months ago|reply
Like the author, I find myself going more for cross-platform Python one-offs and personal scripts for both work and home and ditching Go. I just wish Python typechecking weren't the shitshow it is. Looking forward to ty, pyrefly, etc. to improve the situation a bit
[+] jkingsman|8 months ago|reply
uv has been fantastic to use for little side projects. Combining uv run with `uv tool run` AKA `uvx` means one can fetch, install within a VM, and execute Python scripts from Github super easily. No git clone, no venv creation + entry + pip install.

And uv is fast — I mean REALLY fast. Fast to the point of suspecting something went wrong and silently errored, when it fact it did just what I wanted but 10x faster than pip.

It (and especially its docs) are a little rough around the edges, but it's bold enough and good enough I'm willing to use it nonetheless.

[+] lxgr|8 months ago|reply
Truly. uv somehow resolves and installs dependencies more quickly than pyenv manages to print its own --help output.
[+] mmcnl|8 months ago|reply
I agree uv is amazing, but it's not a virtual machine, it's a virtual environment. It runs the scripts on top of your OS without any hardware virtualization. The virtual environment only isolates the Python dependencies.
[+] nebben64|8 months ago|reply
not sure if OP will see this, but how does uv or uvx forgo `git clone` ? You still need to clone whatever you're trying to run, or am I missing something.
[+] bjornasm|8 months ago|reply
>It (and especially its docs) are a little rough around the edges, but it's bold enough and good enough I'm willing to use it nonetheles

Thought I was the only one thinking this. Got to open an issue, I think it would be nice to have some more examples showcasing different use cases.

[+] TZVdosOWs3kZHus|8 months ago|reply
No more dependency problems with mkdocs I ran into before every other month:

  uvx --with mkdocs-material --with mkdocs-material-extensions --with mkdocs-nav-weight mkdocs serve -a localhost:1337
Funnily enough it also feels like it is starting faster.
[+] satvikpendem|8 months ago|reply
Very nice, I believe Rust is doing something similar too which is where I initially learned of this idea of single-file shell-type scripts in other languages (with dependency management included, which is how it differs from existing ways of writing single-file scripts in e.g. scripting languages) [0].

Hopefully more languages follow suit on this pattern as it can be extremely useful for many cases, such as passing gists around, writing small programs which might otherwise be written in shell scripts, etc.

[0] https://rust-lang.github.io/rfcs/3424-cargo-script.html

[+] rednafi|8 months ago|reply
> Before this I used to prefer Go for one-off scripts because it was easy to create a self-contained binary executable.

I still do because:

- Go gives me a single binary

- Dependencies are statically linked

- I don’t need any third-party libs in most scenarios

- Many of my scripts make network calls, and Go has a better stdlib for HTTP/RPC/Socket work

- Better tooling (built-in formatter, no need for pytest, go vet is handy)

- Easy concurrency. Most of my scripts don’t need it, but when they do, it’s easier since I don’t have to fiddle with colored functions, external libs, or, worse, threads.

That said, uv is a great improvement over the previous status quo. But I don’t write Python scripts for reasons that go beyond just tooling. And since it’s not a standard tool, I worry that more things like this will come along and try to “improve” everything. Already scarred and tired in that area thanks to the JS ecosystem. So I tend to prefer stable, reliable, and boring tools over everything else. Right now, Go does that well enough for my scripting needs.

[+] js2|8 months ago|reply
So far I've only run into one minor ergonomic issue when using `uv run --script` with embedded metadata which is that sometimes I want to test changes to the script via the Python REPL, but that's a bit harder to do since you have to run something like:

  $ uv run --python=3.13 --with-requirements <(uv export --script script.py) -- python
  >>> from script import X
I'd love if there were something more ergonomic like:

  $ uv run --with-script script.py python
Edit: this is better:

  $ "$(uv python find --script script.py)"
  >>> from script import X
That fires up the correct python and venv for the script. You probably have to run the script once to create it.
[+] 4dregress|8 months ago|reply
I’ve been a python dev for nearly a decade and never once thought dep management was a problem.

If I’ve ever had to run a “script” in any type of deployed ENV it’s always been done in that ENVs python shell .

So I still don’t see what the fuss is about?

I work on a massive python code base and the only benefit I’ve seen from moving to UV is it has sped up dep installation which has had positive impact on local and CI setup times.

[+] bboygravity|8 months ago|reply
How did you tell other people/noobs to run your python code (or how did you run it yourself after 5+ years of not touching older projects)?
[+] jshen|8 months ago|reply
Python's dependency management has been terrible until very recently compared to nearly every other mainstream language.
[+] rednafi|8 months ago|reply
I guess this is why people need to get out of this “Python dev” or “JS dev” mindset and try other languages to see why those coming to Python complain so much about dependency management.

People complain because the experience is less confusing in many other languages. Think Go, Rust, or even JS. All the tooling chaos and virtual environment jujitsu are real deterrents for newcomers. And it’s not just beginners complaining about Python tooling. Industry veterans like Armin Ronacher do that all the time.

uv is a great step in the right direction, but the issue is that as long as the basic tooling isn’t built into the language binary, like Go’s tools or Rust’s Cargo, more tools will pop up and fragment the space even further.

[+] petersellers|8 months ago|reply
> it’s always been done in that ENVs python shell .

What if you don't have an environment set up? I'm admittedly not a python expert by any means but that's always been a pain point for me. uvx makes that so much easier.

[+] linsomniac|8 months ago|reply
I've been a python dev for nearly 3 decades and feel that uv is removing a lot of the rough edges around dependency management. So maybe "problem" is the wrong word; I've been able to solve dependency management issues usually without too much trouble, I have also spent a significant amount of time dealing with them. For close to a decade I was managing other peoples Python environments on production systems, and that was a big mess, especially with trying to ensure that they stayed updated and secure.

If you don't see what the fuss is about, I'm happy for you. Sounds like you're living in a fairly isolated environment. But I can assure you that uv is worth a lot of fussing about, it's making a lot of our lives a lot easier.

[+] sambaumann|8 months ago|reply
Between yesterday's thread and this thread I decided to finally give uv a shot today - I'm impressed, both by the speed and how easy it is to manage dependencies for a project.

I think their docs could use a little bit of work, especially there should be a defined path to switch from a requirements.txt based workflow to uv. Also I felt like it's a little confusing how to define a python version for a specific project (it's defined in both .python-version and pyproject.toml)

[+] tpoacher|8 months ago|reply
What's going on? This whole thread reads like paid amazon reviews
[+] gerdesj|8 months ago|reply
I've recently updated a Python script that I originally wrote about 10 years ago. I'm not a programmer - I just have to get stuff done - think sysops.

For me there used to be a clear delineation between scripting languages and compiled languages. Python has always seemed to want to be both and I'm not too sure it can really. I can live with being mildly wrong about a concept.

When Python first came out, our processors were 80486 at best and RAM was measured in MB at roughly £30/MB in the UK.

"For the longest time, ..." - all distros have had scripts that find the relevant Python or Java or whatevs so that's simply daft. They all have shebang incantations too.

So we now have uv written in Rust for Python. Obviously you should install it via a shell script directly from curl!

I love all of the components involved here but please for the love of a nod to security at least suggest that the script is downloaded first, looked over and then run.

I recently came across a Github hosted repo with scripts that changed Debian repos to point somewhere else and install ... software. I'm sure that's all fine too.

curl | bash is cute and easy and very, very insecure.

[+] kristianp|8 months ago|reply
If you want to manually manage envs and you're using conda, you can activate the env in a shell wrapper for your python script, like so (this is with conda)

    #!/usr/bin/env bash
    eval "$(conda shell.bash hook)"
    conda activate myenv
    python myscript
Admittedly this isn't self contained like the PEP 723 solution.
[+] divbzero|8 months ago|reply
If momentum for uv in the community continues, I’d love to see it distributed more broadly. uv can already be installed easily on macOS via Homebrew (like pyenv). uv can also be installed on Windows via WinGet (unlike pyenv). It would be nice to see it packaged for Linux as well.
[+] doctoboggan|8 months ago|reply
There has been a flurry of `uv` posts on HN recently. I don't have any experience with it, is it really the future, or is it a fad?

As Ive gotten older I've grown weary of third party tools, and almost always try to stick with the first party built in methods for a given task.

Does uv provide enough benefit to make me reconsider?

[+] syhol|8 months ago|reply
Mise has a very similar feature with its shebangs: https://mise.jdx.dev/tips-and-tricks.html#shebang

    #!/usr/bin/env -S mise x xh jq fzf gum -- bash
    todo=$(xh 'https://jsonplaceholder.typicode.com/todos' | jq '.[].title' | fzf)
    gum style --border double --padding 1 "$todo"
It makes throwing together a bash scripts with dependencies very enjoyable
[+] pseudosavant|8 months ago|reply
Between how good ChatGPT/Claude are at writing Python, and discovering uv + PEP 723, I'm creating all sorts of single file python scripts. Some of my recent personal tools: compression stats for resources when gzipped, minify SVGs, a duplicate file tool, a ping testing tool, a tool for processing large CSVs through LLMs one row at a time, etc.

uv is the magic that deals with all of the rough edges/environment stuff I usually hate in Python. All I need to do is `uv run myFile.py` and uv solves everything else.

[+] Imustaskforhelp|8 months ago|reply
I have a lot of opinions about this.

Firstly, I have been a HN viewer for so many time and this is the one thing about pep python scripts THAT always get to the top of leaderboard of hackernews by each person discovering it themselves.

I don't mean to discredit the author. His work was simple and clear to understand. I am just sharing this thesis that I have that if someone wants karma on Hackernews for whatever reason, this might be the best topic. (Please don't pitchfork me since I don't mean offense to the author)

Also, can anybody please explain to me on how to create that pep metadata in uv from just a python script and without anything else, like some command which can take a python script and give pep and add that in the script, I am pretty sure that uv has a feature flag but I feel that the author might've missed out on this feature because I don't know when coding one off scripts in python using AI (gemini) it had some options with pep so I always had to paste uv's documentation I don't know, so please if anybody knows a way to create pep easier using the cli, then please tell me! Thanks in advance!!

[+] tyrion|8 months ago|reply
Some years ago I thought it would be interesting to develop a tool to make a python script automatically install its own dependencies (like uvx in the article), but without requiring any other external tool, except python itself, to be installed.

The downside is that there are a bunch of seemingly weird lines you have to paste at the begging of the script :D

If anyone is curios it's on pypi (pysolate).