Recently, I started to center my python development around uv
and other tooling developed by astral.sh. It allows me to manage python environments, linting and formatting.
Managing Python Packages using uv
uv
is an extremely fast Python package and recently also became a full on package manager.
You can install it using curl
.
curl -LsSf https://astral.sh/uv/install.sh | sh
You can then create a new a new Python project, or use an existing one by running the uv init
command:
uv init <project-name>
# or if already in project
uv init
This will create the following structure:
.
├── .python-version # project's default python version
├── README.md
├── hello.py
└── pyproject.toml # metadata about the project
Run uv add
to add dependencies. This will create an virtual environment (.venv
), if you didn’t already have one and it then installs the dependencies. The installed dependency will be present in the pyproject.toml
.
uv add <dependency>
Your project will have the following structure:
.
├── .venv # project's virtual environment
├── .python-version
├── README.md
├── hello.py
├── pyproject.toml
└── uv.lock # lockfile containing exact info about projects dependencies
Development
I like to have several things in my python development setup. A good linter and formatter. Some kind of tool that allows me to test my code.
And finally pre-commit hooks that run some checks (including formatting, linting and tests) before my commits.
Linting & formatting using ruff
I used to use black, flake8 and isort for my linting and code formatting. However since ruff
was released I haven’t looked back.
uv add --dev ruff
Using ruff after black, isort and flake8 is delightful. ruff
is a super fast linter (fixing issues and organizing imports) and code formatter.
# ruff linting
uv run ruff check --fix --select I
# ruff formatting
uv run ruff format
pre-commit
hooks
pre-commit hooks run before your commits are made. These hooks can perform a variety of checks.
uv add --dev pre-commit
With the following .pre-commit.yaml
.
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-toml
- id: check-json
- id: check-yaml
- id: check-xml
- id: check-ast
- id: check-docstring-first
- id: check-executables-have-shebangs
- id: check-added-large-files
- id: check-case-conflict
- id: check-merge-conflict
- id: check-symlinks
- id: detect-private-key
- id: no-commit-to-branch
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.4
hooks:
- id: ruff
args: [ --fix, --select, I ]
- id: ruff-format
Testing using pytest
I don’t always write tests, but if I do, I’ll most likely use pytest, and a pytest coverage plugin.
uv add --dev pytest pytest-cov
To run those test (with test coverage) against my python files I run the following.
uv run pytest <test-dir> --cov <source-dir>
CI with GitHub actions
I also like to run linting, formatting and tests in CI. Below is a GitHub Action at .github/workflows/ci.yaml
.
name: CI
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
# Checkout repo
- uses: actions/checkout@v4
# Setup uv
- name: Install uv
uses: astral-sh/setup-uv@v2
# Setup python and install dependencies
- name: Install dependencies
run: uv sync --all-extras --dev
# Run linting and formatting
- name: Run ruff
run: |
uv run ruff check --fix --select I --output-format=github .
uv run ruff format
# Run pytest for tests
- name: Run tests
run: uv run python -m pytest
Links
[[python]] [[tool]]