Окружение: запуск скриптов и виртуальные среды

Чтобы автоматизация работала надёжно, скрипт должен запускаться одной командой, а его зависимости — жить в изолированном виртуальном окружении.
Суть: один проект — одно виртуальное окружение (venv). Туда ставятся библиотеки через pip, и проект перестаёт зависеть от того, что установлено в системе.

Представьте, что вы написали скрипт для рассылки отчётов и он использует openpyxl версии 3.1. Через полгода для другого проекта вы поставили openpyxl 3.0, и старый скрипт сломался. Чтобы такого не было, каждый проект держит свои библиотеки отдельно — в виртуальном окружении.

Виртуальное окружение — это просто папка с копией Python и набором установленных в неё пакетов. Создаётся встроенным модулем venv. Пока окружение активно, команда pip install кладёт пакеты именно в эту папку, а не в систему.

python -m venv .venv          # создать окружение в папке .venv
source .venv/bin/activate     # активировать (Linux/macOS)
.venv\Scripts\activate        # активировать (Windows)
pip install openpyxl pandas pypdf python-docx schedule
pip freeze > requirements.txt # зафиксировать версии

Файл requirements.txt — это список зависимостей с версиями. По нему любой коллега (или вы на другом компьютере) воссоздаст точно такое же окружение командой pip install -r requirements.txt. Это и есть воспроизводимость.

ИЗОЛЯЦИЯ ОКРУЖЕНИЙ

  Система Python
    |
    +-- проект-A/.venv/  -> openpyxl 3.1, pandas 2.2
    |
    +-- проект-B/.venv/  -> openpyxl 3.0, pypdf 4.0

  Пакеты одного проекта не мешают другому.

Пока разберём важную вещь, которую можно проверить прямо в браузере: как Python понимает версии и сравнивает их. Версии вида 3.1.2 сравниваются покомпонентно, и наивное сравнение строк здесь ошибается.

Попробуй сам ▶

# Корректное сравнение версий: разбиваем на числа
def to_tuple(v):
    return tuple(int(x) for x in v.split('.'))

installed = '3.10.4'
required = '3.9.0'

# Наивное сравнение строк ошибается:
print('Как строки:', installed > required)   # '3.1...' < '3.9...' -> False
# Правильно — как кортежи чисел:
ok = to_tuple(installed) >= to_tuple(required)
print('Как версии:', ok)
print('Подходит' if ok else 'Нужно обновить')

Ещё одна причина любить виртуальные окружения — воспроизводимость во времени. Скрипт, который вы написали год назад, должен запуститься и сегодня. Но мир движется: библиотеки выпускают новые версии, иногда несовместимые со старым кодом. Зафиксированные в requirements.txt версии — это машина времени: вы всегда можете воссоздать ровно то окружение, в котором скрипт точно работал. На серверах это особенно важно — там обновления происходят без вашего ведома, и только закреплённые версии спасают от внезапных поломок среди ночи.

Как работает под капотом

Активация окружения меняет переменную PATH так, что слово python указывает на интерпретатор внутри .venv. Поэтому pip install ставит пакеты рядом с ним, в подпапку site-packages. Деактивация (deactivate) возвращает PATH в исходное состояние.

Когда скрипт делает import openpyxl, Python ищет пакет по списку путей в sys.path. Первым в этом списке стоит site-packages активного окружения — потому импортируется именно его версия библиотеки.

Частые ошибки

  • Ставить пакеты в систему. Без активного venv pip install засоряет глобальный Python и порождает конфликты версий.
  • Коммитить папку .venv в git. Она большая и привязана к ОС. В репозиторий идёт только requirements.txt.
  • Забыть активировать окружение. Тогда скрипт запускается системным Python без нужных библиотек и падает с ImportError.

Best practices

  • Добавьте .venv/ в .gitignore.
  • Фиксируйте версии в requirements.txt сразу после установки.
  • Называйте окружение единообразно (.venv) во всех проектах — проще автоматизировать активацию.

Итоги. venv изолирует зависимости проекта, pip ставит в него библиотеки, requirements.txt делает окружение воспроизводимым. Это фундамент, без которого автоматизация ломается при первом же обновлении. Дальше мы начнём работать с файлами и папками.

Проверьте себя
1. Зачем нужно виртуальное окружение (venv)?
AЧтобы Python работал быстрее
BЧтобы изолировать библиотеки проекта от системы и других проектов
CЧтобы не писать requirements.txt
DЧтобы скрипт запускался без интерпретатора
2. Что обычно кладут в git-репозиторий, а что — нет?
AПапку .venv кладут, requirements.txt — нет
BИ .venv, и requirements.txt кладут
Crequirements.txt кладут, папку .venv — нет
DНичего из этого в git не нужно