Robles.dev

Self-contained python scripts with uv

Lately, I’ve been attacked with Rust propaganda promising us a bright new future for software people. I made two attemps to learn rust, but it was not needed to use the excelent suite of tools that rust programmers are building for the rest of us. This is especially notable with all the software rewrites in Rust that are actually delivering interesting results. One of these Rust-based tools is uv, a Python package manager that’s been catching my attention.

I’ve been using Python since 2016 for pretty much everything, small projects, servers, a wordle game, data analysis, data scraping, and especially those quick scripts that solve a specific problem. In these 9 years, the worst part were fighting with pip, installing and handling pip environments and dependencies. Every single time, and usually multiple times, when I needed to use the same script in a different machine.

UV lets you read dependencies directly from the Python file itself. Now, this isn’t some magical uv only feature, it is actually part of PEP 723, so it is becoming a standard thing. But here’s what makes uv exceptionally comfortable: it also handles the Python installation for you. I don’t need to look for my python path or version again, or choose about ‘python’, ‘python3’, ‘python3.10’, ‘python3.exe’ etc.

PEP 723

PEP 723 is a Python standard that allows you to specify the dependencies of a Python script within the script itself. From the abstract on its own page:

This PEP specifies a metadata format that can be embedded in single-file Python scripts to assist launchers, IDEs and other external tools which may need to interact with such scripts.

Great! I love it. No more creating requirements.txt files or setting up entire PyCharm projects for a simple script. I can just write my code and specify the Python version and dependencies right there in the file. It’s beautiful.

How to do it

Let’s try the example shown on the PEP page:

# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "requests<3",
#   "rich",
# ]
# ///

import requests
from rich.pretty import pprint

resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])

And by running the script with uv:

$ uv run file.py
Installed 9 packages in 316ms
[
    ('1', 'PEP Purpose and Guidelines'),
    ('2', 'Procedure for Adding New Modules'),
    ('3', 'Guidelines for Handling Bug Reports'),
    ('4', 'Deprecation of Standard Modules'),
    ('5', 'Guidelines for Language Evolution'),
    ('6', 'Bug Fix Releases'),
    ('7', 'Style Guide for C Code'),
    ('8', 'Style Guide for Python Code'),
    ('9', 'Sample Plaintext PEP Template'),
    ('10', 'Voting Guidelines')
]

No pip, no venv, no “wait, where is the requierements.txt?”, I don’t have the risk of running pip install outside an environment. Just uv run. That’s it.

Making executable bash scripts with Python

I use Python for scripts because I hate bash. With what I’ve shown above, we can take this even further and create a Python file that runs like any other executable, just ./foo.py and you’re done.

To do this, we just need to add a shebang at the top of the file:

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

Broken down, this is what’s happening:

And when you run ./foo.py, it handles all the dependency and Python version magic automatically. Same output as before, but now it is a proper executable script.

Running it online

If all of this wasn’t enough, you can also run scripts that live online. Check this out:

$ uv run https://raw.githubusercontent.com/MRoblesR/prueba/refs/heads/main/main.py
Installed 9 packages in 316ms
[
    ('1', 'PEP Purpose and Guidelines'),
    ('2', 'Procedure for Adding New Modules'),
    ('3', 'Guidelines for Handling Bug Reports'),
    ('4', 'Deprecation of Standard Modules'),
    ('5', 'Guidelines for Language Evolution'),
    ('6', 'Bug Fix Releases'),
    ('7', 'Style Guide for C Code'),
    ('8', 'Style Guide for Python Code'),
    ('9', 'Sample Plaintext PEP Template'),
    ('10', 'Voting Guidelines')
]

It downloads the file, installs the dependencies, and runs it. Previously, you had to work with curl and bash scripts. Now, it’s possible with Python scripts.

DO NOT RUN ONLINE SCRIPTS FROM RANDOM LINKS YOU DON’T TRUST. Even tough it’s a extremely comfortable feature, it’s also a huge security risk.

Conclusion

We can create a Python file, define its dependencies right inside it, and run it as a regular executable script without even having Python installed on the system. This is genuinely a huge step forward in fixing the most annoying problems with Python scripting.

Honestly? UV is what pip should have been from day one.

#Software Engineering #Python #Uv