Running Python scripts with uv
uv
library, what problems it solves, and how to use it for self-contained scripts, massively simplifying the process of running your scripts.
In this tutorial, we are going to migrate from the process of running Python scripts using the command line tool python
(or python3
) to running them with uv
. It is assumed that you are familiar with Python environments, requirements, and running scripts in general.
What is uv
?
Many things actually, it’s a fairly large library concerned with making managing and working Python a streamlined process. We will only focus on one thing in this tutorial which is running scripts.
Why use uv
instead of regular Python?
Traditionally, in order to run a Python script you need the following:
- A Python installation on your machine
- A virtual environment for that installation (highly recommended though not required)
- A set of third party libraries installed in the environment (pinned to certain versions), and typically in a requirements.txt file
- Activating the environment
When all this is available, you can now run:
How is running scripts with uv
different?
What you need is the same script, but with the requirements declared at the top, something like this:
# /// script
# requires-python = ">=3.13"
# dependencies = [
# "advertools",
# ]
# ///
if __name__ == "__main__":
print("Hello, world!")
That’s it.
Now you can run:
As you can see, we aren’t handling or managing Python or the libraries. We are simply declaring them at the top of the file, and once run with uv
, it will handle all the required details for us. Using this script and it metadata at the top, uv
does the following:
- Create a virtual environment using the declared Python version (3.13 in this case)
- Install (or get from cache) the packages listed under
dependencies
- Within this fully functional and active environment, it runs the Python code
Here is a more complete example right from the beginning.
A complete example of building and running a Python script with uv
We start by installing uv
.
Install
uv
Installation is fairly easy, and quick, as with everything uv
:
For Windows:
For Linux/MacOS:
Once you install the script, you will need to restart your shell in order to activate the uv
command (or source the shell config file).
Initialize an empty script
The commented parts declaring dependencies at the beginning might seem a little daunting to create (and the syntax is quite strict), but uv
has tools to help us create, manage, update, and of course run this script.
Let’s start by creating an empty script:
Note that if you run this command without the --script
flag, uv
will create a project (a pyproject.toml file, initialize a git repo, and a few other things), so make sure you set this option
We get a message that confirms that the script was created. We can now take a look at its contents:
cat hello.py
# /// script
# requires-python = ">=3.12"
# dependencies = []
# ///
def main() -> None:
print("Hello from hello.py!")
if __name__ == "__main__":
main()
As you can see we have all the requirements section created for us. It’s a good practice not to edit this by hand and run it with uv
, because one missed space might cause the whole thing to break.
We can see that it created a main
function, so we can now try running the script in its current form:
Note that traditionally we would run this with python hello.py
, but now we are running everything with uv
.
Add library dependencies
- Python: By default we got
requires-python = ">=3.12"
, which we can change:
bash uv run --script hello.py --python 3.12
- Third party libraries: We might also want to add libraries to our dependencies list:
pip
, for example:- Python: By default we got
Let’s take a look at contents of our script:
cat hello.py
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "advertools",
# "plotly",
# ]
# ///
def main() -> None:
print("Hello from hello.py!")
if __name__ == "__main__":
main()
Our dependencies list was updated as you can see.
Now we can write something a bit more useful.
Write the Python code
Here is a simple script that takes the URL of a robots.txt URL, downloads the file, converts it to a DataFrame, and prints it to the console:
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "advertools",
# "plotly",
# ]
# ///
import sys
import advertools as adv
import pandas as pd
pd.options.display.max_columns = None
if __name__ == "__main__":
robots_url = sys.argv[1]
robots_df = adv.robotstxt_to_df(robots_url)
print(robots_df)
Run the script
Reading inline script metadata from `hello.py`
2024-12-22 23:59:20,181 | INFO | robotstxt.py:394 | robotstxt_to_df | Getting: https://brightonseo.com/robots.txt
directive content \
0 User-Agent *
1 Allow /
2 Disallow /private/
3 Sitemap https://brightonseo.com/sitemap.xml
etag robotstxt_url \
0 "5a38b7feaa7fad84c24093fb04aaaf6b" https://brightonseo.com/robots.txt
1 "5a38b7feaa7fad84c24093fb04aaaf6b" https://brightonseo.com/robots.txt
2 "5a38b7feaa7fad84c24093fb04aaaf6b" https://brightonseo.com/robots.txt
3 "5a38b7feaa7fad84c24093fb04aaaf6b" https://brightonseo.com/robots.txt
download_date
0 2024-12-22 23:59:20.329679+00:00
1 2024-12-22 23:59:20.329679+00:00
2 2024-12-22 23:59:20.329679+00:00
3 2024-12-22 23:59:20.329679+00:00
Running Python scripts remotely
A very interesting feature is that you can run scripts that are hosted online using http(s). This makes it even easier to distribute your scripts. You can host them on your server, or on some hosting/SCM service like GitHub for example and ask your users to run the script remotely. One advantage is that they would get the latest version of the script which would have fewer bugs and/or better features:
Conclusion
We went through the process of installing uv
, and then using it to create a script, change/edit its dependencies (Python as well as third party libraries), we then wrote Python code that does something practical, and now we can repeat the process as many times as we want.
We didn’t have to install Python, create a virtual environment, install dependencies, or even activate the virtual environment, not mention resolving the dependencies that we declared. All this was handled by uv
, making our life much easier, and allowing us to focus more on the Python code than the environment in which we are running it.