I don't do as much Python as I used to do. The few projects I still maintain, in Python, have a pyproject.toml and uv.lock. I.e. I'm using uv for getting the right executable version of Python and for installing dependencies. No more pip install ... and no more requirements.(in|txt). And definitely no poetry.lock.
And pyenv stopped working entirely when Python 3.12 came out. I used to use pyenv instead of Homebrew to get different versions of Python for different projects. uv is just that much better. I still use virtual envs, in the form of uv sync && source .venv/bin/activate when working inside a project and want to be able to type python ... and that referring to the exact version of Python with the relevant dependencies (from the pyproject.toml) installed.
However, there's a problem how: Outside of projects (that have a pyproject.toml and uv.lock) I no longer have a valid python executable. There's still a python3 executable that comes from /opt/homebrew/bin/python3 but that one I can't add dependencies to.
And many times I just want to whip up a quick script or start a repl, but with some certain dependencies installed. For example, to run...
import requests
print(requests.get('https://www.peterbe.com').headers['content-type'])
# prints 'text/html; charset=utf-8'
Again, uv to the rescue! I created ~/bin/python (plus chmod +x ~/bin/python) which now looks like this:
#!/bin/bash
set -x
uv run --python 3.12 --with requests python $@
Now I can quickly start a repl. Or if I create a /tmp/test-something.py I can just run that with
python /tmp/test-something.py
The reason for the set -x in that Bash script is simply to remind me that this starting of python is this Bash script that uses uv run ....
The --with requests is admittedly a bit arbitrary. It's only sometimes that a quick Python session needs the requests package. Sometimes that'd be a waste and sometimes I might need some other package. But that's why the set -x above is a good reminder how this works. So, if I need some other package, I can just remind myself how this works. For example:
❯ cat /tmp/test-something.py
import cowsay
cowsay.cow('Hello World')
❯ uv run --python 3.12 --with cowsay python /tmp/test-something.py
Installed 1 package in 19ms
___________
| Hello World |
===========
\
\
^__^
(oo)\_______
(__)\ )\/\
||----w |
|| ||
Comments
Cool. There's also this way: https://mathspp.com/blog/til/standalone-executable-python-scripts-with-uv