9 ms·
Using uv as your shebang line
- thecodemonkey 2y agoWhat is uv?
- deleted 2y ago[deleted]
- woodruffw 2y agohttps://github.com/astral-sh/uv https://github.com/astral-sh/uv
- avidiax 2y agoSeems that it interprets a special comment block, then automatically creates a python venv and gathers dependencies. As the post[1] says, it obviates "faffing" with venv and pip to run a script. https://akrabat.com/defining-python-dependencies-at-the-top-of-the-file/ https://akrabat.com/defining-python-dependencies-at-the-top-...
- benreesman 2y agoIt’s a package manager for Python (among other things) that is simultaneously correct and performant. It’s getting a lot of attention these days. I highly recommend it over all conceivable alternatives.
- deleted 2y ago[deleted]
- IgorPartola 2y agoHow likely is this to be another npm/yarn situation? I have used a number of Python tools that in the long run turned out to be less portable and less usable than plain Pip and virtualenv. I do use pyenv for a personal setup but not for any production use.
- d0mine 2y agoYou can use uv and get the benefits today: it combines the functionality from several tools (including pyenv) and the performance is such that it is transformative.
- IgorPartola 2y agoSo it is in fact a yarn/npm situation: an alternative tool that does too many things and aims to replace a bunch of other tools. I read the same sentiment about poetry a few years ago. Turns out tools that do one thing well are better in the long run.
- baq 2y agoas if it was so simple. don't forget about pnpm. it's the same for uv/pip/poetry except uv is so much better than the alternatives there isn't any contest. pip is the safe default (which doesn't even work out of the box on debian derivatives, which is half the issue) and uv is... just default.
- Arrowmaster 2y agoAs a Debian user that wanted to use a cli tool only available from npm it was horrible trying to find some sane instructions that didn't assume intimate knowledge of node package structures. I did eventually figure out I could just do `corepack pnpm setup` then install packages globally with pnpm.
- einpoklum 2y agoThe idea is as follows: * A recently-published PEP specificies how Python scripts can declare dependencies in opening comments. * uv is a Python script-runner/package manager which scans for those dependencies, arranges for them to be met, and runs the script with those dependency modules available for importing * If, in a Python script, you use the first line comment - the shebang line - to have the file invoked using uv rather than python itself, you'll get that effect automagically when "running" the Python script The result: Python scripts which require setup of dependencies will now "just run".
- bityard 2y ago(If you have `uv` installed in your $PATH.) Also, uv will install the dependencies, but I am not clear on whether or not it will automatically install a Python interpreter for you using this method.
- kjkjadksj 2y agoWill now “just run” after you satisfied the dependency of installing uv on your system and prepending these dependency lines to all your scripts, that is. Imo this saves you no time over just constructing and calling environments the old way. I’m not sure either whether uv lets you load the env only in an interactive session or if its just for script running.
- einpoklum 2y agoI'll first day I'm not a proponent of uv, I just read the post and tried to clarify what it says... but I think what it saves you is the trouble of setting up a virtual environment the "old" way. So, it's about making things easier, not faster.
- fastasucan 2y agoOfcourse it does, after you have installed uv and prepending the dependencies you never have to do it again. Calling environments etc have to be done every time. You also have to keep these env around.
- vunderba 2y agoI've been meaning to look into uv. However, since I make pretty heavy use of conda for my pythons scripts, I'll usually add a bash function that handles conda activation / code execution to my .zshrc file. function transcribe() { # get the active conda environment active_env=$(conda info | grep "active environment" | awk '{print $4}') if [[ "$active_env" != "speechrec-env" ]]; then conda activate speechrec-env fi python /users/vunderba/dev/transcriber/transcribe.py "$@" } It's handy because if it'll always point to the most current version of my script.
- kjkjadksj 2y agoWhat seems to suck about the uvx way of doing things is that your script and dependencies are now in one document. It just seems so much more useful for so many reasons to keep these things discrete in my mind. Especially when we are talking about uvx which is something probably only a fraction of python devs actually use vs a built in paradigm of the language. Yes, conda is not built in but it might as well be with the amount of influence it has on python development and downstream tools to manage conda envs (e.g. mamba or snakemake).
- babel_ 2y agoThat's not the uvx way? Dependencies at the top of a script was outlined in PEP 723, which uv/uvx added support for. Not everything is a "project", some files are just one-and-done scripts which should not have to carry the burden of project/environment management, but absolutely should still be able to make use of dependencies. The "uvx way" of doing it just means that it doesn't have to pollute the global/user install, and can even be isolated into a separate instance. Besides, not everyone uses conda, and it would be quite a stretch to say it "might as well" be a built-in compared to, well, the actual built in, pip! Plus, uv works quite nicely as "just a pip replacement", which is how I started with it, so it aligns quite well to the actual built-in paradigm of the language.
- simonw 2y agoThat's a misundersanding of uv - the inline script dependencies feature is strictly optional. You're encouraged to use pyproject.toml as a default instead of that for most projects. https://docs.astral.sh/uv/guides/projects/ https://docs.astral.sh/uv/guides/projects/
- krick 2y agoWow, this is super obvious (so obvious that it wouldn't occur to me to write a post to brag about it), yet it somehow didn't occur to me. Very cool. Not so long ago I started pip-installing my scripts to keep things tidy. It seems now I can regress back to chaos again...
- nomel 2y agoMy favorite way to pass small scripts to non software colleagues: try: import package except ImportError: import pip print("Installing package...") pip.main(['install', 'package'] import package :D
- hv42 2y agoYou can use this trick with mise (mise-en-place) for small tasks: https://mise.jdx.dev/tasks/toml-tasks.html#shell-shebang https://mise.jdx.dev/tasks/toml-tasks.html#shell-shebang [tools] uv = 'latest' [tasks.python_uv_task] run = """ #!/usr/bin/env -S uv run --script # /// script # dependencies = ["requests<3", "rich"] # /// import requests # your code here """
- kseistrup 2y agoSomething similar is shown for uv here: https://simonwillison.net/2024/Aug/21/usrbinenv-uv-run/ https://simonwillison.net/2024/Aug/21/usrbinenv-uv-run/
- threecheese 2y agoI e experienced a few “gotchas” using uv (or uvx) as a command runner, but when it works (which is most of the time) it’s a big time saver. As a Python dev and curious person my homedir is littered with shallow clones and one-off directories for testing some package or another, and not having to manage this overhead is really useful. OP, you have a great idea and I’m stealing it. Do you use some non-.py suffix, or is seeing the exec bit at on a .py file enough of a signal for you to know that you can just run the file as a script (and it won’t use the system Python)?
- przemub 2y agoFrom what I understand, he uses .py with exec bit and the shebang line as in the article.
- BiteCode_dev 2y agoI've been collecting gotchas from using uv myself over the last year to assess whether to recommend it or not and I found surprisingly few. Not zero, but I'm looking pretty hard. Would love to hear what gotchas to find so I can add that to the list.
- achierius 2y agoIs the list public? I'm trying to figure out whether it's worth taking the plunge.
- BiteCode_dev 2y agoI'll write an article on bitecode.dev at the one year mark (about end of feb) about the various situations I tested it in with clients and the results. I will include a section about the things that it was not good for and the problems with it. Spoiler, there were not that many and they fixed them astonishingly well (even one this week!). But obviously there were some, no software is perfect although they worked hard to not make it too opinionated for the first taste. I can ping you when it comes out.
- EdwardDiego 2y ago
- zahlman 2y agoI really don't understand how it is that other people frequently write one-off scripts that nevertheless need to run in an isolated environment (as opposed to a sandbox that contains a few common workhorse packages like NumPy or Requests). Of course I have plenty of separate environments set up, but that's because I'm developing things I hope to share with others, so I need to keep close track of their specific dependencies (so I can specify them in package metadata). But even this stuff would generally work with a wide range of versions for those dependencies. I'd imagine this is equally true of small, one-off, personal automation scripts. But, sure, if you have this problem, that's a great way to use the shebang line. (You can do similarly with Pipx, and I expect you'll be able to with Paper as well, although I might push that sort of thing into an optional add-on.)
- traverseda 2y agoIt's when you need to give the script to someone else.
- imtringued 2y agoThey don't care about the isolated environment. They care about having the right dependencies available to the script. Competing languages like Java don't need or even have the concept of isolated environments. It's an illogical concept. You have a class path and that is about it.
- orf 2y agoA virtual environment is just a project-specific class path, but for Python.
- traverseda 2y agoYou can also do the same thing with the nix package manager and pretty much any language or dependency. Like if you wanted a portable bash-script with imagemagick #! /usr/bin/env nix-shell #! nix-shell -i bash -p bash imagemagick Handy for passing quick scripts around. Nothing is quite as portable as a single file like this.
- IshKebab 2y ago> Nothing is quite as portable as a single file like this. Well... a file that doesn't require Nix is probably a fair bit more portable!
- traverseda 2y agoSure, an appimage, the UV example above
- nikkindev 2y ago“Lazy self-installing Python scripts with uv”[1] article from Trey Hunner has more details with examples [1] https://treyhunner.com/2024/12/lazy-self-installing-python-scripts-with-uv/ https://treyhunner.com/2024/12/lazy-self-installing-python-s...
- godelski 2y ago> For example, here’s a script I use to print out 80 zeroes (or a specific number of zeroes) Also... # Print 80 0's and flush printf %.1s 0{1..80} $'\n' # Alternatively for i in {1..80}; do echo -n 0; done; echo For the ffmpeg example is this any different from ffmpeg -i in.mp4 -c:v copy -filter:a volumedetect -pass 1 -f null /dev/null &&\ ffmpeg -i in.mp4 -c:v copy -filter:a "loudnorm" -pass 2 out.mp4 The python seems more complicated tbh
- EdwardDiego 2y agoNot in any way relevant to an example of leveraging Python's new inline metadata PEP via uv.
- godelski 2y agofair enough haha. But I think as others point out, there are some portability issues.
- kseistrup 2y agoSee also https://news.ycombinator.com/item?id=42198256 https://news.ycombinator.com/item?id=42198256 from November 2024 (40 comments).
- PhilippGille 2y agoFrom 68 days ago: "/usr/bin/env -S uv run": https://news.ycombinator.com/item?id=42198256 https://news.ycombinator.com/item?id=42198256 125 points, 45 comments
- d0mine 2y agoto drop .py extension, you need to add `--script`
- babel_ 2y agoNow that's a trick I should remember! I recently switched over my python aliases to `uv run python` and it's been really quite pleasant, without needing to manage `.venv`s and the rest. No fuss about system installs for python or anything else either, which resolves the old global/user install problem (and is a boon on Debian). Also means you can invoke the REPL within a project/environment without any `activate`, which saves needing to think about it. Only downside for calling a .py directly with uv is the cwd relative pathing to project/environment files, rather than relative to the .py file itself. There is an explicit switch for it, `--project`, which at least is not much of an ask (`uv run --project <path> <path>/script.py`), though a target relative project switch would be appreciated, to at least avoid the repetition.
- tiltowait 2y agoI’ve seen uv a lot recently. Is there any major benefit over poetry?
- __mharrison__ 2y agoSpeed! You won't go back
- dave4420 2y agoUv will manage python versions for you.
- ddorian43 2y agoThat don't support compiled c/rust/etc libraries so you have to compile them.
- beaugunderson 2y ago- great lock update behavior by default (though --no-update became the default in poetry 2.0) - speed, as others have said (5 minutes → 70 seconds for our docker build) - much easier (and faster) virtual environment management than poetry, should you want a venv - manages the python version, including new GIL-less variants if you want to try them - script dependencies (include requirements as a comment in a script and it will install and use them)
- xboxnolifes 2y agoI've been using uv as a drop-in replacement for poetry. My little usage of it so far has led me to view it as just a better poetry. Does the same things I wanted from poetry, but faster and I haven't hit the dependency resolve issues I hit with poetry in the past (tbf, I think these got fixed in poetry since).
- mynegation 2y agoIt’s way way faster for one.
- rav 2y agoOh wow, today I learned about env -S - when I saw the shebang line in the article, I immediately thought "that doesn't work on Linux, shebang lines can only pass a single argument". Basically, running foo.py starting with #!/usr/bin/env -S uv run --script causes the OS run really run env with only two arguments, namely the shebang line as one argument and the script's filename as the second argument, i.e.: /usr/bin/env '-S uv run --script' foo.py However, the -S flag of env causes env to split everything back into separate arguments! Very cool, very useful.
- sangeeth96 2y agoIt's frustrating this is not the same behavior on macOS: https://unix.stackexchange.com/a/774145 https://unix.stackexchange.com/a/774145
- silverwind 2y ago`brew install coreutils` and update your `PATH`.
- sangeeth96 2y agoI'm aware of this package for getting other utilities but: 1. I'm worried about this conflicting/causing other programs to fail if I set it on PATH. 2. This probably doesn't fix the shebang parsing issue I mentioned since it's an OS thing. Let me know if that's not the case.
- andreineculau 2y agoYou've got nothing to worry Been doing it for more than a decade and yet to get in trouble. Not one issue. Doing it consistently for my teams as we decrease cognitive load (developing on macs but targeting unix). Others would confirm https://news.ycombinator.com/item?id=17943202 https://news.ycombinator.com/item?id=17943202 Basically software will either use absolute paths i.e. wants to use your OS version for a dependency like grep, or will use whatever grep is in your $PATH and stick to safe invocations regardless if it's BSD/GNU or if it's version x or y
- tiberriver256 2y agouv makes Python actually usable. Freaking love that tool.
- 7thpower 2y agoDoes anyone have any intro to UV guides they found helpful? I haven’t dove into uv as it just hasn’t been at the top of my list, but I will probably take the plunge soon. I gave it an initial try thinking it might function as a drop in, but didn’t find any great guides, and just haven’t been back yet. But we’re ~8% into 2025 so it’s time.
- whattheheckheck 2y agoThe docs are great
- picafrost 2y agoAstral are making great tools. There are many points of friction in the Python world and Astral is making them seem obvious to see and obvious to solve. That is hard work.
- dcre 2y agoFor the TypeScript enjoyers: you can do the same with Deno (and I'm sure Bun too, though I haven't done it). #! /usr/bin/env -S deno run You can add permission flags like this: #! /usr/bin/env -S deno run --allow-env --allow-read --allow-net
- IshKebab 2y agoI love Deno and I use this, but I really wish they would fix this dumb bug: https://github.com/denoland/deno/issues/16466 https://github.com/denoland/deno/issues/16466
- sebmellen 2y agoI really wish they would allow you to connect to "unsafe" TLS endpoints. https://github.com/denoland/deno/issues/6427#issuecomment-650666206 https://github.com/denoland/deno/issues/6427#issuecomment-65...
- tempaccount420 2y agoJesus, both of these issues are sad. This is probably why Bun is winning
- sebmellen 2y agoJust absolutely infuriating to work with. We migrated all of our RPA scripts to Deno and then this popped up and killed our hopes and dreams. > "What, you want to connect to an old government website that has an old TLS version? You are a very bad boy. Not allowed!" Back to Node we go.
- IshKebab 2y agoYeah definitely a case of "you're holding it wrong". Never a good look.
- dcre 2y agoThat's interesting! I think I've run into it but it has rarely been a problem.
- mkl 2y agoRelated article and discussion from 16 days ago: Uv's killer feature is making ad-hoc environments easy https://news.ycombinator.com/item?id=42676432 https://news.ycombinator.com/item?id=42676432 (502 points, 417 comments)
- TrueDuality 2y agoOne gotcha that annoys me when do this... The file still needs to end in .py which I generally don't like for the bins and utilities in my path. Everyone using these shebangs either doesn't hit this or never mentions it. Entirely a stylistic personal preference but still makes me a sad lady.
- puzzledobserver 2y agoAre you sure about this? I just wrote a script without a .py extension, and it seemed to work fine.
- d0mine 2y ago`--script` has been added to fix it https://github.com/astral-sh/uv/issues/6360#issuecomment-2450085953 https://github.com/astral-sh/uv/issues/6360#issuecomment-245...
- infogulch 2y agoSpeaking of funny shebangs, I came up with this to shell execute prolog .pl files, but it should work for any scripting language that has /**/-comments but doesn't support #-comments or #! shebangs specifically: /*usr/bin/env scryer-prolog "$0" "$@" ; exit #*/ From my original comment https://github.com/mthom/scryer-prolog/issues/2170#issuecomment-1821713993 https://github.com/mthom/scryer-prolog/issues/2170#issuecomm... : > The way this works is that this test.pl is both a valid shell file and prolog file. When executing as shell the first line finds and executes /usr/bin/env after searching for the glob pattern /*usr/bin/env. env then executes scryer-prolog "test.pl" which runs the prolog file as a module and halts; of course prolog ignores the first line as a comment /* ... */. Then the shell continues and executes the next command after ; which is exit which stops execution so the rest of the file (which is not valid shell) is ignored. The # makes the shell evaluator ignore prolog comment closer */ to prevent it from printing an error. This may be the best and worst thing I have ever created.
- upghost 2y agoOh my god this is amazing. We need to fix some of the IO redirect in Scryer and then this will be a perfect replacement for bash scripts <3 You rock!
- emmelaich 2y agoSimilarly, make go source executable : https://unix.stackexchange.com/questions/162531/shebang-starting-with https://unix.stackexchange.com/questions/162531/shebang-star... also, my contribution, make C programs executable: $ cat cute-use-of-slash-slash.c //usr/bin/env sh -c 'p=$(expr '"_$0"' : "_\(.*\)\.[^.]*"); make $p > /dev/null && $p'; exit #include <stdio.h> int main() { puts("hello world"); } $ chmod +x cute-use-of-slash-slash.c $ ./cute-use-of-slash-slash.c hello world $ $(pwd)/cute-use-of-slash-slash.c hello world $ ../cc/cute-use-of-slash-slash.c hello world $ sed -i -e s/hello/bye/ cute-use-of-slash-slash.c $ ./cute-use-of-slash-slash.c bye world $
- mzs 2y agoI didn't think of this but I use it: % ./hworld.tcl Hello, world. % cat hworld.tcl #!/bin/sh # the next line restarts using tcl \ exec tclsh "$0" "$@" puts "Hello, world." % Turns-out that in TCL you can line continue a comment :)
- Levitating 2y agoRuby's bundler has actually always been able to do inline dependencies https://bundler.io/guides/bundler_in_a_single_file_ruby_script.html https://bundler.io/guides/bundler_in_a_single_file_ruby_scri... Though I think the implementation could be improved upon
- anotherpaulg 2y agoNot using shebang, but I've recently been using uv as an "installer". It's hard to package and distribute python CLI tools with complex dependencies. I used uv in a couple of novel ways: 1. I copied their `curl | sh` install script and added a `uv tool install --python python3.12 my-tool` line at the end. So users can now install my CLI tool with a nice curl-pipe-sh one-liner. 2. I made a tiny pypi "installer" package that has "uv" as its only dependency. All it does is `uv tool install` my CLI tool. Both methods install the CLI tool, python3.12 and all python dependencies in their own isolated environment. Users don't need to manage python virtual envs. They don't need to even have python installed for the curl-pipe-sh method. I now get far fewer GitHub issues from users who have somehow mangled the complex dependencies of my tool. I wrote it up with more detail and links to code: https://aider.chat/2025/01/15/uv.html https://aider.chat/2025/01/15/uv.html
- CoolCold 2y agoI have not checked how that works under the hood, but > Both methods install the CLI tool, python3.12 Won't that require some compiler (GCC?), kernel headers, openssl-dev/gzip/ffi/other_lib headers to be present on end user system and then compiling Python? At least that's my experience with ASDF-VM, which uses someother Python-setup toolkit under the hood.
- Zizizizz 2y agohttps://docs.astral.sh/uv/concepts/python-versions/#cpython-distributions https://docs.astral.sh/uv/concepts/python-versions/#cpython-... it has pre-compiled versions
- deleted 2y ago[deleted]
- oulipo 2y agoNice! I think `mise` and `aqua` also have a notion of tool install which can be useful
- alin23 2y agoI recently learned about swift-sh [1] which is the same thing as “uv run —script” but for swift files. I’ve been having a blast automating some macOS annoyances with small scripts that can use native macOS APIs directly, no more pyobjc. I also wrote an app to bring the startup folder functionality from Windows to the Mac to help me run these scripts at startup more easily [2] [1] https://github.com/mxcl/swift-sh https://github.com/mxcl/swift-sh [2] https://github.com/FuzzyIdeas/StartupFolder https://github.com/FuzzyIdeas/StartupFolder
- Tyrubias 2y agoI love using this technique, but developing these scripts has always bothered me because there’s no code completion without an actual `venv` to point my editor to. Has anyone found a way around this?
- mixmastamyk 2y agoTry pointing to --user, aka ~/.local/lib/python3.??/site-packages
- deleted 2y ago[deleted]
- Eduard 2y agowhy not #!/usr/bin/env python3 ?
- mynegation 2y agoThat works reliably for scripts that use standard library only and you also have to be careful not to use features incompatible with various minor versions of Python 3.
- m3at 2y agoUv's support of inline metadata is super handy. What if the person running the scripts doesn't have uv yet though? For fun I wrote a double shebang script that will handle that too: https://paulw.tokyo/standalone-python-script-with-uv/ https://paulw.tokyo/standalone-python-script-with-uv/
- AdieuToLogic 2y agoThis reminds me of an old Perl magic spell for invoking it within a Bourne/POSIX shell wrapper along with allowing arbitrary command line arguments[0]: #!/bin/sh #! -*- perl -*- -p eval 'exec perl -x -wS $0 ${1+"$@"}' if 0; # It's Perl code from this point. 0 - https://perldoc.perl.org/perlrun https://perldoc.perl.org/perlrun
- recursive_fruit 2y agoOh nice, that's really cool. I'm seeing uv pop up in more and more places. Python really needed this tooling upgrade. Also, I'll make a mental note of -S, awesome. If you want to do the same with javascript or typescript, you can just use bun, it will auto-install[1] the packages included in there. #!/usr/bin/env bun [1]: https://bun.sh/docs/runtime/autoimport https://bun.sh/docs/runtime/autoimport
- zombot 2y agoWhat is this `uv` thing? Is that something pythonic?
- zrail 2y ago> An extremely fast Python package and project manager, written in Rust. https://github.com/astral-sh/uv https://github.com/astral-sh/uv
- megamix 2y agoif it has Rust in it, then...
- mark_l_watson 2y agoNice ideas, especially with “-S” env option. I love having little custom command line apps in my ~/bin directory. I used to, a very long time ago, use Gambit Scheme for this, then moved on to Common Lisp command line apps. More recently I have been using Python for ~/bin scripts but since I use venv for each individual project on my laptop, having a global Python setup with dependencies for ~/bin is annoying. Using uv seems like a great idea, which I will try today: use uv for all command line apps and venv for all other Python coding projects.