repos

blog.bythewood.me-flask

mirror archived upstream

Markdown blog on Flask with Vite-built assets and WeasyPrint PDF export, rewritten from the Wagtail build that preceded it.

blogdockerflaskmarkdownpythonself-hostedvite

3.7 KB · 132 lines · markdown Raw History
  1---
  2title: Managing a Python project with uv in 2026
  3slug: managing-a-python-project-with-uv-in-2026
  4date: 2026-04-05
  5publish_date: 2026-04-05
  6tags: coding
  7description: How I set up and manage Python projects in 2026 using uv and ruff. Two tools and one config file replace everything.
  8cover_image: uv-terminal.webp
  9---
 10
 11Setting up a Python project used to mean juggling multiple tools for package management, Python versions, and virtual environments. I used to use pipenv, pyenv, and Black but I've since moved on. In 2026 I use `uv` for project management and `ruff` for linting and formatting. Two tools, one config file.
 12
 13## Installing uv
 14
 15[uv](https://docs.astral.sh/uv/) is a single binary that replaces pip, pipenv, pyenv, and virtualenv. It's written in Rust by Astral and it's extremely fast.
 16
 17```shell
 18curl -LsSf https://astral.sh/uv/install.sh | sh
 19```
 20
 21## Starting a new project
 22
 23Instead of manually creating a `Pipfile` or `requirements.txt`, run `uv init` and you get a project scaffold with a `pyproject.toml` ready to go.
 24
 25```shell
 26uv init my-project
 27cd my-project
 28```
 29
 30This gives you:
 31
 32```
 33my-project/
 34├── .python-version
 35├── README.md
 36├── main.py
 37└── pyproject.toml
 38```
 39
 40No `Pipfile`, no `setup.cfg`, no `requirements.txt`. Just `pyproject.toml`.
 41
 42## Managing Python versions
 43
 44You don't need pyenv anymore. uv handles downloading and managing Python versions for you.
 45
 46```shell
 47uv python pin 3.13
 48uv python install
 49```
 50
 51This writes `3.13` to `.python-version` and installs it if you don't already have it. That's it.
 52
 53## Adding dependencies
 54
 55Adding dependencies updates your `pyproject.toml` and `uv.lock` in one step.
 56
 57```shell
 58uv add flask requests
 59uv add --dev pytest ruff
 60```
 61
 62When you clone the project on another machine or set up CI, `uv sync` installs everything from the lockfile.
 63
 64## Running things
 65
 66`uv run` executes inside the project's virtual environment without you needing to activate anything. No more `pipenv shell` or `source .venv/bin/activate`.
 67
 68```shell
 69uv run python main.py
 70uv run pytest
 71```
 72
 73## Linting and formatting with Ruff
 74
 75I used to use Black, Flake8, and isort separately but [Ruff](https://docs.astral.sh/ruff/) handles all of that now. It's from the same team that makes uv and it's just as fast. The formatter is designed as a drop-in replacement for Black with near-identical output, so switching won't mean a massive reformatting commit across your codebase.
 76
 77```shell
 78uv run ruff check .
 79uv run ruff format .
 80```
 81
 82`ruff check` handles linting and import sorting. `ruff format` handles code formatting. You configure both in `pyproject.toml`:
 83
 84```toml
 85[tool.ruff]
 86line-length = 88
 87target-version = "py313"
 88
 89[tool.ruff.lint]
 90select = ["E", "F", "I", "UP"]
 91
 92[tool.ruff.format]
 93quote-style = "double"
 94```
 95
 96`E` and `F` are the pycodestyle and pyflakes rules that Flake8 used by default. `I` is isort-compatible import sorting. `UP` is pyupgrade which modernizes your syntax automatically. You can browse the full [rule list](https://docs.astral.sh/ruff/rules/) and add what makes sense for your project.
 97
 98## The full pyproject.toml
 99
100Here's what a complete `pyproject.toml` looks like. This replaces the `Pipfile`, `Pipfile.lock`, `.flake8`, and `.isort.cfg` I used to have scattered across my projects.
101
102```toml
103[project]
104name = "my-project"
105version = "0.1.0"
106description = ""
107readme = "README.md"
108requires-python = ">=3.13"
109dependencies = [
110    "flask",
111    "requests",
112]
113
114[dependency-groups]
115dev = [
116    "pytest",
117    "ruff",
118]
119
120[tool.ruff]
121line-length = 88
122target-version = "py313"
123
124[tool.ruff.lint]
125select = ["E", "F", "I", "UP"]
126
127[tool.ruff.format]
128quote-style = "double"
129```
130
131That's all you need to get a Python project off the ground in 2026. If you're still juggling multiple tools and config files give uv and ruff a try, I don't think you'll go back.