If you have been working with Python in the last year, probably you have heard about the new package (and project) manager everybody is using: uv.

Unless I had to deploy ML models, for which I used miniconda to avoid the pain of dealing with exotic executables, I was loyal to pip for all the other use cases.

But I could not resist to their claim: uv is “an extremely fast Python package and project manager, written in Rust”.

Therefore, I gave it a try and never looked back. Now I use uv whenever it’s possible. It’s truly fast in installating packages, handles virtual environment and it is so good in many other aspects.

Today, I want to focus on how uv makes quick scripting a breeze with what I use to call “ephimeral environments”. Thanks to this, you can try a few lines in the REPL or build a whole script with specific dependencies without having to worry to set up the virtual environments. You can leverage this using --with or embedding dependencies.

run --with

I often find myself in need to write a couple of lines of Python for making some computations or testing an idea. It’s not rare that I need to manipulate rapidly a dataset.

Before uv, I would either:

  • run python -m venv venv + venv/Scripts/activate + pip install pandas, start the Python REPL, write the code and eventually forget to delete the venv when I was done with it
  • try to remember a project in which I had used pandas and activate its venv
  • have the base installation flooded with packages (some of them way less useful than pandas)

Thanks to uv now I can create an enviroment on the fly using the argument with.

For instance uv run --with pandas python creates immediately a venv with pandas (doh!). Since uv is so fast in downloading and installing the packages is a matter of seconds before you can start working. Bonus point is that it caches the venvs so if your requirements are alredy satisfied you are good to go.

Actually you can require more packages like uv run --with "requests,pandas" python.

script embedded dependencies

Another gem is the possibility to embed the dependencies at the top of the script. This is great for scripts that you want to run without too much structure or if you want to share a script with a colleague so that he just need run it without knowing how to install the dependencies. The fact that is self-contained is just freaking great!

It’s just necessary to have uv installed and run uv run main.py.

This would be perfect for standalone CLI app.

On my laptop I have a subfolder named Tools where I have Python scripts that could be useful more than once. For instance, one of these is merge_pdfs.py that receives as arguments a list of pdf files to merge and the output path where to save the result. Here it is the code:

def parse_arguments():
    ap = argparse.ArgumentParser()
    ap.add_argument("-i", "--inputs", nargs='+')
    ap.add_argument("-o", "--output")
    arguments = vars(ap.parse_args())
    return arguments

if __name__ == "__main__":

    from PyPDF2 import PdfFileMerger

    args = parse_arguments()

    merger = PdfFileMerger()
    pdf_inputs = args["inputs"]
    pdf_output = args["output"]

    for pdf_input in pdf_inputs:
        merger.append(pdf_input)

    merger.write(pdf_output)
    merger.close()

For running it, before uv, I needed a virtual environment with its dependencies, activate it and run the script. Now, using uv I only need to add the dependencies at the top of the script (you can see from the versions that the script is pretty old) as follows:

# /// script
# requires-python = "==3.8.10"
# dependencies = ["PyPDF2==2.3.1", "typing-extensions==4.2.0"]
# ///

and then just running uv run merge_pdfs.py.

This allows to run scripts with dependencies on your laptop (or your colleague’s) without need to handle dependencies. Lately, I have found it useful also for CI/CD pipelines to run an utility script without polluting the pyproject.toml.

You could even make it executable without the need of running with uv run.

Now we have no excuse to be messy with our virtual environments when writing scripts!