$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогКак настроить локальную среду для Python-проекта: venv, pyproject.toml и воспроизводимые зависимости

Как настроить локальную среду для Python-проекта: venv, pyproject.toml и воспроизводимые зависимости

$ sudo teach IT
·1 августа 2026 г.·10 мин·93
Как настроить локальную среду для Python-проекта: venv, pyproject.toml и воспроизводимые зависимости

Разберём, как правильно собрать окружение для разработки и CI: структуры проекта, pyproject.toml, lock-файлы и типовые сценарии воспроизведения зависимостей на другой машине. Параллельно покажем, как избежать “работает у меня”.

Содержание
Почему “работает у меня” — системная проблемаАрхитектура проекта: что и где хранитьПочему src/ layout полезенvenv: базовая изоляция для локальной разработкиСоздание venvЧто добавить в .gitignorepyproject.toml: где описывать зависимости и инструментальную конфигурациюНа что обратить вниманиеLock-файл: ключ к воспроизводимым зависимостямПочему lock-файл необходим даже с верхними границамиКакие lock-файлы бываютВариант с uv: pyproject.toml + uv.lock + установка из lock-файлаИнициализация lock-файлаУстановка зависимостей локальноКогда обновлять зависимостиВариант с Poetry (кратко): poetry.lock и предсказуемая установкаВоспроизведение на “другой машине”: пошаговый сценарийСценарий A: “Новая машина”, Python уже установленСценарий B: другой Python версииПодготовка CI: чтобы локальное и “серверное” совпадалиМинимальные требования к CIПример GitHub Actions (универсальный)Важно: uv sync должен опираться на lock-файлКак избежать типовых ошибок (и почему они возникают)Ошибка 1: запускать команды без активации venvОшибка 2: не фиксировать транзитивные зависимостиОшибка 3: обновлять зависимости “вручную” и забывать закоммитить lockОшибка 4: использовать разные способы установки локально и в CIОшибка 5: отсутствие проверки на чистом окруженииРазработка: удобные команды, которые не ломают воспроизводимостьПример: Makefile для единообразияСтратегия управления зависимостями: когда и как обновлятьМини-чеклист: “правильная” локальная среда и воспроизводимостьКакой подход выбрать: один принцип, разные инструментыВывод: воспроизводимость — это дисциплина описания, а не магия инструментов

Разработка на Python часто начинается с простого: установить пару библиотек и “побежать”. Но как только проект выходит за рамки личных экспериментов — начинаются неприятные вопросы: почему тесты падают у коллеги, почему в CI другая версия пакета, почему сборка не совпадает с локальной средой. Эти проблемы почти всегда упираются в одно: окружение и зависимости описаны недостаточно строго.

Ниже — практический разбор, как собрать локальную среду разработки и подготовить проект к CI так, чтобы зависимости воспроизводились на другой машине. Мы разберём:

  • структуру проекта (что держать в корне, что — в подпапках),
  • venv как базовую изоляцию,
  • pyproject.toml как источник правды для метаданных и зависимостей,
  • lock-файлы и их роль,
  • типовые сценарии воспроизведения (локально, на CI, на “чистой” машине),
  • как избегать эффекта “работает у меня”.

Почему “работает у меня” — системная проблема

В Python проблема воспроизводимости чаще всего появляется в одном из этих сценариев:

  1. Зависимости задаются без ограничений по версиям
    Например: requests без верхней границы. В какой-то момент на машине разработчика стоит одна версия, а на другой — другая, и поведение меняется.

  2. Указана версия, но не зафиксирован набор транзитивных зависимостей
    Даже если вы зафиксировали прямой пакет, его зависимости могут обновиться. Это классический случай, когда “версия в requirements.txt одна, но всё другое — другое”.

  3. Локально используются уже установленные пакеты из системы или “случайный” venv
    В результате тесты проходят “по инерции”, когда на машине уже стоит нужная версия, но ваш проект это не гарантирует.

  4. В CI используется отдельная схема установки
    Например, локально вы ставили через pip, а в CI — через другую команду/параметры, или вообще забыли использовать lock-файл.

Итог: окружение становится недетерминированным. Поэтому в корректной настройке “истина” должна быть описана в репозитории и однозначно воспроизводиться.


Архитектура проекта: что и где хранить

Прежде чем писать конфигурации, стоит определить “формат” репозитория. Ниже — типичная структура, которая хорошо работает для Python-проектов (и в dev, и в CI):

  • pyproject.toml — метаданные, зависимости, конфигурации инструментов.
  • src/ — исходники (рекомендуемый layout для избежания конфликтов импортов).
  • tests/ — тесты.
  • README.md, возможно LICENSE.
  • lock-файл (имя зависит от инструмента, но обычно это uv.lock, poetry.lock, Pipfile.lock или файл для pip-tools).
  • .venv/ в корне — локально, но в .gitignore.

Пример структуры:

code
my-project/
├─ pyproject.toml
├─ README.md
├─ src/
│  └─ my_project/
│     ├─ __init__.py
│     └─ app.py
├─ tests/
│  └─ test_app.py
├─ .gitignore
├─ .github/
│  └─ workflows/
│     └─ ci.yml
└─ uv.lock   (или другой lock-файл)

Почему src/ layout полезен

Если проект лежит “плоско” (например, my_project/ в корне), то в некоторых сценариях интерпретатор может подхватить модуль из текущей рабочей директории, а не из установленного пакета. src/ layout обычно снижает вероятность таких “магических” совпадений.


venv: базовая изоляция для локальной разработки

venv не решает вопрос воспроизводимости зависимостей сам по себе, но он обеспечивает предсказуемость окружения на уровне Python и установленного набора пакетов. Практика такая:

  1. Создать виртуальное окружение.
  2. Установить зависимости строго из lock-файла/воспроизводимой схемы.
  3. Использовать это окружение для запуска тестов, линтера и т.п.

Создание venv

На Unix/macOS:

code
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip

На Windows (PowerShell):

code
py -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip

Что добавить в .gitignore

code
# virtual environments
.venv/
venv/
__pycache__/
*.pyc

pyproject.toml: где описывать зависимости и инструментальную конфигурацию

Сегодня “правильный” путь — использовать pyproject.toml как единый файл настроек: зависимости, build-система и часто конфигурации инструментов (линтеры, форматтеры, типизация). Плюс — это стандарт, вокруг которого выстраиваются современные инструменты.

Ниже пример структуры pyproject.toml в современном стиле (минимально, но по делу):

code
[build-system]
requires = ["hatchling>=1.18"]
build-backend = "hatchling.build"

[project]
name = "my-project"
version = "0.1.0"
description = "Example Python project"
readme = "README.md"
requires-python = ">=3.11"

dependencies = [
  "requests>=2.31,<3.0",
]

[project.optional-dependencies]
dev = [
  "pytest>=8.0,<9.0",
  "ruff>=0.5,<1.0",
  "mypy>=1.10,<2.0",
]

[tool.pytest.ini_options]
testpaths = ["tests"]

[tool.ruff]
line-length = 100
target-version = "py311"

[tool.mypy]
python_version = "3.11"

На что обратить внимание

  • requires-python фиксирует диапазон версий интерпретатора
    Это важно: если CI использует другую версию Python, поведение может отличаться. Лучше явно задать минимум/диапазон.

  • Диапазоны версий вместо “свободных” зависимостей
    Практический баланс: для большинства пакетов ставят нижнюю границу (чтобы избежать старых багов) и верхнюю границу (чтобы не попасть в несовместимую мажорную версию).
    Например: requests>=2.31,<3.0.

  • Optional dependencies (dev, test, docs)
    Они отделяют зависимости для разработки от runtime. Это упрощает воспроизведение и уменьшает шум.


Lock-файл: ключ к воспроизводимым зависимостям

Если pyproject.toml описывает желаемое, то lock-файл описывает конкретное решение резолвера — версии прямых и транзитивных зависимостей.

Почему lock-файл необходим даже с верхними границами

Даже при requests>=2.31,<3.0 вы можете получить разные версии на разных машинах в разное время. Lock гарантирует, что набор останется одинаковым, пока вы не выполните обновление осознанно.

Какие lock-файлы бывают

Зависит от инструмента установки. На практике встречаются варианты:

  • uv.lock — если используете uv (быстрый менеджер зависимостей).
  • poetry.lock — если используете Poetry.
  • requirements.txt + pip-tools (.txt-реплика) — если используете pip-tools.
  • Pipfile.lock — если используете pipenv.

В этой статье сосредоточимся на подходе “через инструмент с lock-файлом”, не привязываясь жестко к одному бренду. Однако ниже я дам рабочие примеры под uv, потому что он довольно часто встречается в современных пайплайнах.


Вариант с uv: pyproject.toml + uv.lock + установка из lock-файла

uv умеет читать pyproject.toml, резолвить зависимости и фиксировать результат в uv.lock. Он ускоряет работу по сравнению с “чистым” pip, но важнее не скорость, а предсказуемость.

Инициализация lock-файла

В корне проекта:

code
uv sync --lock-only

Если uv ещё не установлен:

code
pip install uv

После этого появится uv.lock.

Установка зависимостей локально

Чтобы установить строго то, что в lock-файле:

code
uv sync

или с учётом окружения разработки (dev-зависимости):

code
uv sync --extra dev

Когда обновлять зависимости

Обновление делается отдельной командой, чтобы изменения зависимостей были явными в PR:

code
uv lock

или если хотите обновить с контролем селективности — инструменты обычно позволяют это делать (зависит от версии uv и настроек). Суть такая: lock меняется осознанно, а не “само”.


Вариант с Poetry (кратко): poetry.lock и предсказуемая установка

Если ваш проект использует Poetry, логика та же:

  • зависимости описываются в pyproject.toml (в разделе [tool.poetry.dependencies]),
  • фиксируются в poetry.lock,
  • установка происходит из lock-файла командой poetry install --no-root.

С практической точки зрения различается формат файла и некоторые детали, но принцип воспроизводимости остаётся тем же: lock-файл должен попадать в репозиторий, а CI и локальная машина должны опираться на него.


Воспроизведение на “другой машине”: пошаговый сценарий

Давайте смоделируем реальный кейс: вы отправили репозиторий человеку, у которого нет зависимостей вашего проекта. Его шаги должны быть предсказуемыми.

Сценарий A: “Новая машина”, Python уже установлен

  1. Клонировать репозиторий.
  2. Создать venv.
  3. Установить зависимости строго из lock-файла.
  4. Запустить тесты.

Пример команд (под uv):

code
git clone https://example.com/my-project.git
cd my-project

python3 -m venv .venv
source .venv/bin/activate

pip install --upgrade pip
pip install uv

uv sync --extra dev
pytest -q

Если в проекте установлен pre-commit/линтер — их тоже запускают из этого же окружения.

Сценарий B: другой Python версии

Если в pyproject.toml задан requires-python = ">=3.11", то попытка поставить зависимости в Python 3.10 приведёт к ошибке. Это хорошо: проект честно сообщает ограничения.

В CI стоит также явно выбрать версию Python, например 3.11 и/или 3.12.


Подготовка CI: чтобы локальное и “серверное” совпадали

CI — это не только прогон тестов. Это проверка гипотезы: “окружение из репозитория действительно работает”.

Минимальные требования к CI

  • использовать такой же Python (или набор версий, которые вы поддерживаете),
  • установить зависимости из lock-файла,
  • не допускать “подмешивания” глобальных пакетов.

Пример GitHub Actions (универсальный)

Ниже демонстрационный workflow, ориентированный на uv и uv.lock.

code
name: CI

on:
  push:
  pull_request:

jobs:
  tests:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11", "3.12"]

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}

      - name: Install uv
        run: |
          python -m pip install --upgrade pip
          pip install uv

      - name: Sync dependencies (from lock)
        run: |
          uv sync --extra dev

      - name: Run tests
        run: |
          pytest -q

Важно: uv sync должен опираться на lock-файл

Если lock отсутствует, инструмент может пойти в “резолв заново”, и воспроизводимость сломается. Поэтому в процессе разработки рекомендуется держать lock в репозитории и обновлять его осознанно.


Как избежать типовых ошибок (и почему они возникают)

Ошибка 1: запускать команды без активации venv

Вроде мелочь, но это частая причина “у меня всё работает”. Проверка:

code
which python
python -V
pip -V

Если python указывает не на .venv, вы не в том окружении.

Ошибка 2: не фиксировать транзитивные зависимости

Фиксировать только прямые зависимости — недостаточно. Lock-файл сохраняет полный набор решений резолвера.

Ошибка 3: обновлять зависимости “вручную” и забывать закоммитить lock

Если вы обновляете пакеты через pip install -U, lock не изменится (или будет рассинхрон). В идеале: обновление делается через инструмент, который управляет lock.

Ошибка 4: использовать разные способы установки локально и в CI

Например:

  • локально ставите pip install -r requirements.txt,
  • в CI — через poetry install или pip install .,
  • или локально используете --no-deps.

Разница в стратегии приводит к тому, что одна среда “случайно” совпала, а другая — нет. CI должен повторять то, что вы считаете “официальной” установкой.

Ошибка 5: отсутствие проверки на чистом окружении

Даже с lock-файлом стоит тестировать “с нуля”: удалить .venv, создать заново, установить зависимости из lock и прогнать тесты. Это самый быстрый способ обнаружить, что вы опираетесь на уже установленные пакеты (чаще всего — инструменты разработки).


Разработка: удобные команды, которые не ломают воспроизводимость

Обычно проект требует часто запускать похожие команды: тесты, линтеры, форматтер, типизацию. Чтобы не плодить “магические” зависимости в документации, удобно:

  • запускать все инструменты из одного окружения,
  • хранить команды в Makefile или в scripts/,
  • при необходимости — фиксировать версии в dev optional-dependencies.

Пример: Makefile для единообразия

code
.PHONY: test lint format type

test:
	pytest -q

lint:
	ruff check .

format:
	ruff format .

type:
	mypy src

Команды не “знают” про venv. Они предполагают, что вы активировали окружение или что CI его настроил.


Стратегия управления зависимостями: когда и как обновлять

Воспроизводимость — это не запрет обновлений. Это управление циклом обновлений:

  1. Регулярно (например, раз в месяц) обновляете lock-файл.
  2. Проверяете тестами и линтерами.
  3. Если обновление крупное — разделяете PR по зонам влияния (например, runtime vs dev).

Важно понимать: обновление зависимостей — это изменение входных данных для тестов. Поэтому чем лучше тесты покрывают поведение, тем меньше рисков “тихих” регрессий.


Мини-чеклист: “правильная” локальная среда и воспроизводимость

Перед тем как считать систему стабильной, проверьте:

  • В репозитории есть pyproject.toml, описывающий зависимости (runtime и dev).
  • В репозитории есть lock-файл (например, uv.lock/poetry.lock/аналог).
  • Локально вы устанавливаете зависимости из lock-файла, а не “по минимуму и потом докинуть”.
  • CI устанавливает зависимости тем же образом и с тем же lock-файлом.
  • .venv/ не коммитится в репозиторий, но в README есть инструкция “как поднять окружение”.
  • Были прогнаны тесты на чистом окружении (fresh venv).
  • Версии Python согласованы между requires-python и CI.

Какой подход выбрать: один принцип, разные инструменты

Существует несколько “правильных” путей: Poetry, uv, pip-tools и т.д. Они отличаются синтаксисом и workflow, но объединены одной идеей:

  • pyproject.toml — описание намерений,
  • lock-файл — фиксация результата резолва,
  • CI и локальная разработка — должны использовать один и тот же источник истины.

Если вы хотите глубже разобраться в том, как устроены современные практики упаковки и управления зависимостями в Python, полезно посмотреть разборы по теме в образовательных материалах, например в курсах на /course/ — как дополнительный способ структурировать знания и увидеть больше примеров на реальных проектах.


Вывод: воспроизводимость — это дисциплина описания, а не магия инструментов

“Работает у меня” возникает не из-за того, что Python плох, а из-за того, что окружение описано неполно. Надёжная локальная среда для Python-проекта строится вокруг трёх опор:

  1. Изоляция через venv — чтобы локальные пакеты не влияли на воспроизводимость.
  2. Описания в pyproject.toml — как единый контракт по зависимостям и совместимости.
  3. Lock-файл и установка из него

Войдите, чтобы поставить лайк и оставить комментарий.

Автор

$ sudo teach IT

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

С нуля до профессионального уровня. Подходит для всех. Учитесь каждый день и овладейте самым популярным языком программирования.

Перейти к курсу
Solidity DLC - поймай, если сможешь

Solidity DLC - поймай, если сможешь

Аудит смарт-контрактов на реальных взломах. The DAO, Parity, Wormhole, Ronin, Euler и ещё 7 разборов: что пошло не так, сколько потеряли и что бы спасло. Атаки и защиты на Foundry, план входа в профессию аудитора.

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу
Solidity pro - всё или ничего

Solidity pro - всё или ничего

Как пишут контракты, которые работают в основной сети. Наследование и OpenZeppelin, паттерны продакшена, оптимизация газа, ABI, delegatecall и подписи. 7 модулей, 33 урока, а в финале собственное dApp: мини-DAO с токеном, голосованием и казной.

Перейти к курсу

Другие статьи

Python для аналитики на практике: как строить признаки и проверять их воспроизводимость
python

Python для аналитики на практике: как строить признаки и проверять их воспроизводимость

Поймём, где чаще всего ломается воспроизводимость (версии данных, фильтры, дрейф) и как оформить пайплайн признаков так, чтобы результаты совпадали между запусками и окружениями.

17 сентября 2026 г.
1790
Запуск и поддержка проекта на Ruff в команде: формат “как в CI”, а не “как договорились”
запуск

Запуск и поддержка проекта на Ruff в команде: формат “как в CI”, а не “как договорились”

Как правильно выстроить конфигурацию Ruff (линтинг, форматирование, автозамены) и сделать так, чтобы одинаковые правила гарантировались в локальной сборке и на CI без ручного шаманства.

16 сентября 2026 г.
1700
Как выучить программирование самому и бесплатно: с чего начать и в каком порядке
выучить

Как выучить программирование самому и бесплатно: с чего начать и в каком порядке

Разберём понятный маршрут для новичка: как выбрать первый язык, что учить в первую очередь, какие мини-проекты делать и как не забросить занятия. Дам бесплатные ориентиры и план, чтобы знания складывались в портфолио.

21 сентября 2026 г.
1970
Как начать с HTML и сделать первую страницу: пошагово и без сложных слов
html

Как начать с HTML и сделать первую страницу: пошагово и без сложных слов

Разберём, что нужно новичку, чтобы за 1–2 вечера собрать свою первую веб-страницу: структура HTML, базовые теги, ссылки и картинки, плюс простой чек-лист, как понять, что страница работает правильно.

2 ноября 2026 г.
20
Cargo для продакшена: workspaces, фичи и стратегия версий зависимостей
cargo

Cargo для продакшена: workspaces, фичи и стратегия версий зависимостей

Разберём, как организовать Rust workspace, управлять optional features, фиксировать версии библиотек и уменьшать “дрейф” зависимостей при командной разработке и CI.

18 сентября 2026 г.
1960
Что такое HTML и чем он отличается от CSS — объясню простыми словами и на примерах
такое

Что такое HTML и чем он отличается от CSS — объясню простыми словами и на примерах

Разберём, из чего состоит веб-страница: что делает HTML (смысл и структура) и что делает CSS (оформление). В конце соберёте мини-страницу и поймёте, что нужно учить в каком порядке.

30 октября 2026 г.
130

Комментарии

Станьте первым в обсуждении

Поделитесь мнением о статье или задайте вопрос автору

Содержание

Почему “работает у меня” — системная проблемаАрхитектура проекта: что и где хранитьПочему src/ layout полезенvenv: базовая изоляция для локальной разработкиСоздание venvЧто добавить в .gitignorepyproject.toml: где описывать зависимости и инструментальную конфигурациюНа что обратить вниманиеLock-файл: ключ к воспроизводимым зависимостямПочему lock-файл необходим даже с верхними границамиКакие lock-файлы бываютВариант с uv: pyproject.toml + uv.lock + установка из lock-файлаИнициализация lock-файлаУстановка зависимостей локальноКогда обновлять зависимостиВариант с Poetry (кратко): poetry.lock и предсказуемая установкаВоспроизведение на “другой машине”: пошаговый сценарийСценарий A: “Новая машина”, Python уже установленСценарий B: другой Python версииПодготовка CI: чтобы локальное и “серверное” совпадалиМинимальные требования к CIПример GitHub Actions (универсальный)Важно: uv sync должен опираться на lock-файлКак избежать типовых ошибок (и почему они возникают)Ошибка 1: запускать команды без активации venvОшибка 2: не фиксировать транзитивные зависимостиОшибка 3: обновлять зависимости “вручную” и забывать закоммитить lockОшибка 4: использовать разные способы установки локально и в CIОшибка 5: отсутствие проверки на чистом окруженииРазработка: удобные команды, которые не ломают воспроизводимостьПример: Makefile для единообразияСтратегия управления зависимостями: когда и как обновлятьМини-чеклист: “правильная” локальная среда и воспроизводимостьКакой подход выбрать: один принцип, разные инструментыВывод: воспроизводимость — это дисциплина описания, а не магия инструментов