Python-проект в 2026: uv, ruff, pyproject.toml. Настраиваем за 5 минут

от автора

pip install, requirements.txt, virtualenv, black, isort, flake8, mypy, setup.py… Если вы настраиваете Python-проект так же, как в 2020 году, эта статья для вас. Показываю современный стек, который заменяет всё вышеперечисленное.

В 2026 году экосистема Python-инструментов наконец собралась в нечто цельное. Два инструмента (uv и ruff) + один файл (pyproject.toml) заменяют 7+ отдельных утилит. Вот как это работает.

Что заменяем и на что

Было

Стало

pip + pip-tools

uv

virtualenv / venv

uv

pyenv

uv

poetry / pipenv

uv

black (форматирование)

ruff format

isort (сортировка импортов)

ruff

flake8 (линтинг)

ruff check

setup.py / setup.cfg

pyproject.toml

requirements.txt

pyproject.toml + uv.lock

Два инструмента вместо девяти. Оба написаны на Rust, оба от Astral. Работают в 10-100 раз быстрее аналогов на Python.

Шаг 1. Установка uv

Одна команда:

# macOS / Linuxcurl -LsSf https://astral.sh/uv/install.sh | sh# Windowspowershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"# Или через pip (если хочется по-старому)pip install uv

Проверяем:

$ uv --versionuv 0.7.x

ruff ставить отдельно не нужно. uv умеет запускать его через uvx ruff. Но если хотите глобально:

uv tool install ruff

Шаг 2. Создаём проект

$ uv init myprojectInitialized project `myproject` at `./myproject`$ cd myproject$ lspyproject.toml  src/  README.md  .python-version

uv создал структуру проекта с pyproject.toml, папку src/ и файл .python-version. Никаких setup.pyrequirements.txtMakefile.

Если нужен Python конкретной версии:

# uv сам скачает и установит нужную версию Pythonuv python install 3.13uv python pin 3.13

Да, uv заменяет pyenv. Он сам управляет версиями Python.

Шаг 3. Зависимости

Забудьте pip install. Теперь так:

# Добавить зависимостьuv add requestsuv add pandas numpy# Добавить dev-зависимостьuv add --dev pytest ruff# Удалить зависимостьuv remove pandas

uv автоматически:

1. Создаёт виртуальное окружение (если его нет).
2. Добавляет пакет в pyproject.toml.
3. Обновляет lock-файл uv.lock.
4. Устанавливает пакет.

Всё за одну команду. Никаких pip freeze > requirements.txt.

Скорость

uv устанавливает пакеты в 10-100 раз быстрее pip. Холодная установка numpy + pandas + requests:

Инструмент

Время

pip

~12 сек

poetry

~8 сек

uv

~0.5 сек

Это не опечатка. uv кэширует пакеты глобально и использует жёсткие ссылки вместо копирования файлов.

Шаг 4. pyproject.toml

Вот как выглядит типичный pyproject.toml после настройки:

[project]name = "myproject"version = "0.1.0"description = "My awesome project"readme = "README.md"requires-python = ">=3.12"dependencies = [    "requests>=2.32",    "numpy>=2.0",][dependency-groups]dev = [    "pytest>=8.0",    "ruff>=0.9",][tool.ruff]target-version = "py312"line-length = 88[tool.ruff.lint]select = [    "E",    # pycodestyle errors    "W",    # pycodestyle warnings    "F",    # pyflakes    "I",    # isort    "UP",   # pyupgrade    "B",    # flake8-bugbear    "SIM",  # flake8-simplify][tool.ruff.format]quote-style = "double"[tool.pytest.ini_options]testpaths = ["tests"]

Один файл. Зависимости, линтер, форматтер, тесты. Всё здесь.

Шаг 5. Линтинг и форматирование (ruff)

ruff заменяет flake8 + black + isort + pyupgrade. Одна команда вместо четырёх.

# Проверить кодuvx ruff check .# Проверить и автоматически исправитьuvx ruff check --fix .# Отформатировать код (замена black)uvx ruff format .# Проверить форматирование без измененийuvx ruff format --check .

Скорость ruff

ruff проверяет код в 10-100 раз быстрее flake8. На проекте из 1000 файлов:

Инструмент

Время

flake8

~12 сек

flake8 + isort + black

~25 сек

ruff check + ruff format

~0.3 сек

Пример: что ruff ловит

# Доimport osimport sysfrom typing import Optional, List, Dictimport jsondef process(data: Optional[List[Dict[str, str]]] = None):    if data == None:        data = list()    for item in data:        if len(item.keys()) > 0:            print(item)
# После ruff check --fix + ruff formatimport jsondef process(data: list[dict[str, str]] | None = None):    if data is None:        data = []    for item in data:        if item:            print(item)

Что ruff сделал:

1. Убрал неиспользуемые импорты (ossys).
2. Отсортировал оставшиеся импорты.
3. Заменил Optional[List[Dict]] на list[dict] | None (Python 3.10+).
4. Заменил == None на is None.
5. Заменил list() на [].
6. Упростил len(item.keys()) > 0 до item.

Шаг 6. Запуск скриптов

Для запуска скриптов внутри виртуального окружения:

# Запуск скриптаuv run python src/myproject/main.py# Запуск тестовuv run pytest# Запуск любой команды в окруженииuv run mycommand

uv run автоматически активирует виртуальное окружение. Не нужно никаких source .venv/bin/activate.

Одноразовые скрипты

Нужно быстро запустить скрипт с зависимостями, не создавая проект?

# Запустить скрипт, который требует requests, без установкиuv run --with requests python script.py# Запустить инструмент одноразово (npx-стиль)uvx black .uvx httpie https://api.github.com

uvx = npx для Python. Скачивает, запускает, не засоряет систему.

Шаг 7. CI/CD

Минимальный GitHub Actions workflow:

# .github/workflows/ci.ymlname: CIon: [push, pull_request]jobs:  check:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v4      - uses: astral-sh/setup-uv@v5      - run: uv sync      - run: uvx ruff check .      - run: uvx ruff format --check .      - run: uv run pytest

4 строки. Установка зависимостей, линтинг, форматирование, тесты. Всё.

Время CI на типичном проекте:

Стек

Время

pip + flake8 + black + pytest

~45 сек

poetry + flake8 + black + pytest

~60 сек

uv + ruff + pytest

~12 сек

Шаг 8. Docker

Минимальный Dockerfile:

FROM python:3.13-slim# Установить uvCOPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv# Копировать файлы проектаWORKDIR /appCOPY pyproject.toml uv.lock ./# Установить зависимости (кэшируется Docker layer)RUN uv sync --no-dev --frozen# Копировать кодCOPY src/ src/CMD ["uv", "run", "python", "-m", "myproject"]

Важный момент: pyproject.toml и uv.lock копируются отдельно от кода. Это позволяет Docker кэшировать слой с зависимостями. Если код изменился, а зависимости нет, переустановки не будет.

Миграция с pip/poetry за 2 минуты

С requirements.txt

# Инициализировать проектuv init# Импортировать зависимости из requirements.txtuv add $(cat requirements.txt)

С poetry

uv понимает pyproject.toml в формате Poetry. Просто:

# Удалить poetry.lock, создать uv.lockrm poetry.lockuv lockuv sync

Если в pyproject.toml есть секция [tool.poetry], uv прочитает зависимости оттуда.

Полный чеклист нового проекта

# 1. Создать проектuv init myproject && cd myproject# 2. Добавить зависимостиuv add requests pydantic# 3. Добавить dev-зависимостиuv add --dev pytest ruff# 4. Написать код# ...# 5. Проверить и отформатироватьuvx ruff check --fix .uvx ruff format .# 6. Запустить тестыuv run pytest# 7. Готово

Пять минут. Никаких setup.pyMANIFEST.inrequirements.txttox.ini.flake8.isort.cfgpyproject.toml на 200 строк.

uv + ruff + pyproject.toml = всё, что нужно для Python-проекта в 2026 году.

Ссылки

uv на GitHub (80k+ звёзд)
ruff на GitHub (40k+ звёзд)
Документация uv
Документация ruff
PEP 621: стандарт pyproject.toml

ссылка на оригинал статьи https://habr.com/ru/articles/1044550/