Summary
You can test things in your venv without polluting it using uv run --with, create almost standalone scripts with uv init --script or convert a PDF to markdown by running uvx markitdown file.pdf. 
It's fast, it saves space, it avoids bootstrapping problems, and it makes Python workflows just a little nicer every day.
Soon my friend, soon...
While it is true I'm not ready to recommend uv quite yet (I'm planning to write a full review in a few months to conclude my year of testing it), I have been using it extensively on my own projects, in training and with certain clients.
I also know a lot of you are as well, there is a real enthusiasm about the tool, so for those that made the jump, why not share cool tips about it?
Because one thing about uv, is that it is independent of the Python runtime, yet capable of fully bootstrapping a whole project, and very quickly at that, so it enables novel use cases and patterns. Some are just new, some are things that were not practical before, and some are just old stuff but getting better.
uv run
uv run will run any command inside the currently activated virtual environment, and if it's not activated but exists in the current directory with a common name (E.G: .venv), then it will transparently use that.
But this command comes with some cool goodies.
First, it can run a command with a temporary dependency made available, without polluting your virtual environment. For example, let's say you want to create a jupyter notebook and load your code in it. Normally, you'd have to install it in your project. But jupyter is huge! It has many dependencies, and installing it would pollute your project.
With uv you can use the --with argument to provide a temporary dependency to your project and run a command:
uv run --with jupyter jupyter notebookThis will:
Create a temporary venv.
Install
jupyterin it.Run the command with your project, your venv and the temporary venv available together transparently.
The result is that you can run jupyter in your project without having to really install it in your project! Plus, the first time it's slow (it has to install everything), but the follow-up times, it will use a cache and it will be fast. Also, because of the way uv deals with the cache (using hard links), if you have 10 projects that use the same version of jupyter, you only install one copy of it instead of one for each project as before.
This works with everything. Want to run your project with ipython? Wanna run ruff or mypy once only?
E.G: I like qtconsole to do exploratory programming, but it installs jupyter and qt5, which means it's massive, so I rarely used it. Now it's just:
uv run --with pyqt5 --with qtconsole jupyter qtconsoleuv run also understand PEP 723 inline dependencies. You have a quick script and you want to use typer for argument parsing? Before uv, I used a shared venv for all my local scripts and installed the deps manually.
Now my script looks like this:
# /// script
# dependencies = [
#   "typer",
# ]
# ///
import typer
def main(name: str):
    print(f"Hello {name}")
if __name__ == "__main__":
    typer.run(main)This will automatically install the dependencies in a temp venv and run the script:
❯ uv run hello.py bitecode
Reading inline script metadata from: hello.py
Hello bitecodeOn linux you can even add this shebang (then chmod +x it), and ./hello.py will automatically call uv run:
#!/usr/bin/env -S uv runIf you don't remember how to create those scripts with inline deps, no worries, uv has your back with uv init --script:
❯ uv init --script my_script.py
Initialized script at `my_script.py`
❯ cat my_script.py
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
def main() -> None:
    print("Hello from my_script.py!")
if __name__ == "__main__":
    main()uv init is useful in general (it initializes a full project, including a .gitigore and a minimal pyproject.toml), but with —script it only creates a single file, ready for scripting.
You can even use uv init to easily create global utilities you write in Python:
uv init --package your_toolYou get a whole project dir named “your_tool
”, cdto it.Edit the
main()function in “your_tool/src/your_tool/__init__.py”.Run
uv tool install . -eto install it.
your_tool is now a command globally available. This works because the pyproject.toml file contains:
[project.scripts]
your_tool = "your_tool:main"uvx
uvx is a second command that is installed next to uv when you set it up, and it's the equivalent of npx or pipx, but with uv goodness. Meaning it's fast, it has great caching, supports git repositories, is in full rust (so no chicken and egg or PATH problem), and has the --with option.
This is great for two things:
quickly testing stuff.
running Python utilities without worrying about setup.
You want to test pendulum in an ipython shell with python 3.13?
❯ uvx --with pendulum -p 3.13 ipython
Installed 21 packages in 27ms
Python 3.11.11 (main, Dec  4 2024, 08:55:08) [GCC 13.2.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.30.0 -- An enhanced Interactive Python. Type '?' for help.
Exception reporting mode: Plain
Doctest mode is: ON
>>> import pendulum
>>> pendulum.now()
DateTime(2024, 12, 15, 21, 52, 56, 856087, tzinfo=Timezone('Europe/Paris')You want to run the latest textual demo from the Will McGugan?
Note how my first attempt failed, but the excellent error message made it instantly solvable.
Want to download the mp3 of our Charlie Marsh (uv's team leader) interview (this does require ffmpeg to be installed prior)?
uvx --no-cache --from yt-dlp[default] yt-dlp --extract-audio --audio-format mp3 https://www.bitecode.dev/p/charlie-marsh-on-astral-uv-and-theThis works on YouTube or Soundcloud, thanks to the excellent yt-dlp project. --no-cache forces yt-dlp to update every time, which is required given the countermeasures change so often and the setup time is tiny compared to the video download time.
Python has many such tools you want to fire once in a while and forget it:
cog: the weird and excellent inline templating tool.
httpie: the better alternative to
curlfor simple HTTP requests.copier: the success of cookiecutter to initialize projects.
markitdown: the office/pdf to markdown converter.
All those little things that used to be frictions are now an uv away, making our Python workflows a bit nicer every day.
The cache can grow quite a lot and quickly though, because it's so tempting to use it all the time. Cleaning up may be a good idea once in a while:
$ uv cache clean
Clearing cache at: /home/user/.cache/uv
Removed 269615 files (23.8GiB)

Thank you for the article. Regarding scripts, I find the uv add —script method very convenient instead of the init script method. (https://docs.astral.sh/uv/guides/scripts/#declaring-script-dependencies)
Still not into uv; it feels like giving up on python to be writing its packaging tools in a rival language. At least conda is almost entirely in python.