Mastering UV Scripts: Simplifying Python Development with UV Package Manager
UV has revolutionized Python package management with its incredible speed and simplicity. One of its most powerful features is the ability to create and run self-contained Python scripts with inline dependency management, making development faster and more efficient.
In this post, I’ll show you how to leverage UV’s script functionality to streamline your Python development workflow, from creating simple scripts to managing complex dependencies.
Creating Your First UV Script
Let’s start with a simple example. First, create a script with inline metadata:
uv init --script hello.py --python 3.12
This creates a script file with the proper header structure. You can also generate the header for an existing file:
uv init --script http://script.py
Now add dependencies to your script:
uv add --script hello.py requests rich
Your script will now include inline metadata like this:
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "requests",
# "rich",
# ]
# ///
import requests
from rich.console import Console
console = Console()
response = requests.get("https://api.github.com/users/astral-sh")
console.print(f"UV by Astral has [bold green]{response.json()['public_repos']}[/bold green] public repos!")
Run your script with:
uv run hello.py
UV automatically creates an isolated environment, installs the dependencies, and executes your script!
Advanced Script Features
Executable Scripts with Shebang
Make your scripts directly executable by adding a shebang line:
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.12"
# dependencies = ["httpx"]
# ///
import httpx
print(httpx.get("https://example.com"))
chmod +x my_script.py
./my_script.py
Running Scripts with Temporary Dependencies
Need to test with different package versions? Use the --with flag:
uv run --with httpx==0.26.0 my_script.py
uv run --with "pandas>=2.0" data_analysis.py
Running Scripts from URLs or stdin
UV can even run scripts directly from remote sources:
echo 'print("Hello from UV!")' | uv run -
My Personal Development Environment Shortcuts
Here’s my custom function that I use to quickly initialize development environments with UV:
function uvnit() {
VENV_PATH="./.venv"
# Check if the venv folder exists
if [ -d "$VENV_PATH" ]; then
echo "Activating existing Python virtual environment..."
source "$VENV_PATH/bin/activate"
else
echo "Creating Python virtual environment..."
uv init .
uv venv "$VENV_PATH"
source "$VENV_PATH/bin/activate"
fi
}
alias pyoff="deactivate"
This function is my go-to shortcut for initializing development environments. It:
- Checks if a virtual environment already exists
- If not, creates a new UV project and virtual environment
- Activates the environment automatically
- I pair it with
pyoffalias to quickly deactivate environments
Add these to your .bashrc or .zshrc for instant access to UV-powered development environments!
Comparing UV Scripts to Traditional Approaches
| Aspect | UV Scripts | Traditional venv + pip |
|---|---|---|
| Setup Speed | ✓ Instant script execution ✓ Automatic dependency resolution ✓ Built-in caching | ✗ Manual venv creation ✗ Separate pip installs ✗ No built-in caching |
| Dependency Management | ✓ Inline metadata ✓ Version pinning ✓ Automatic isolation | ✗ External requirements.txt ✗ Manual version management ✗ Potential conflicts |
| Performance | ✓ Rust-powered speed ✓ Parallel installation ✓ Smart caching | ✗ Python-based tools ✗ Sequential installs ✗ Limited caching |
| Portability | ✓ Self-contained scripts ✓ No external config files ✓ Easy sharing | ✗ Multiple files needed ✗ Manual setup required ✗ Environment reproduction issues |
| Development Experience | ✓ Zero-config execution ✓ Immediate script running ✓ Built-in best practices | ✗ Multi-step setup ✗ Manual activation needed ✗ Easy to forget steps |
Best Practices for UV Scripts
- Use inline metadata: Keep dependencies close to your code for better maintainability
- Pin Python versions: Specify
requires-pythonfor consistency across environments - Leverage caching: UV’s intelligent caching makes repeated runs lightning-fast
- Make scripts executable: Use shebang lines for direct script execution
- Version your dependencies: Be specific with version constraints for reproducible results
- Use temporary dependencies: Experiment with
--withbefore committing to script metadata
Real-World Use Cases
UV scripts excel in various scenarios:
- Data analysis scripts: Quickly prototype with pandas, numpy, and visualization libraries
- API integrations: Test API endpoints with httpx or requests without environment setup
- Automation tasks: Create self-contained scripts for CI/CD pipelines
- Learning and experimentation: Try new libraries without polluting your global environment
- Microservices: Deploy lightweight, self-contained Python services
Why UV Scripts Matter for Modern Development
In today’s fast-paced development environment, UV scripts provide the perfect balance of simplicity and power. They eliminate the friction of environment management while maintaining the isolation and reproducibility that professional development demands.