Deep Engineering
Средний·Опубликовано·3.11 · 3.12 · 3.13 · 3.14·25 МИН

Окружения и зависимости: что делает venv, а что — pip

Вопросы про виртуальные окружения кажутся бытовыми, пока не спросят, как интерпретатор понимает, что он в окружении. Прогон отвечает: окружение — это папка со своим bin/python и файлом pyvenv.cfg; env/bin/python ставит sys.prefix на окружение без всякой активации; activate — скрипт оболочки, который ставит env/bin первым в PATH и задаёт VIRTUAL_ENV и VIRTUAL_ENV_PROMPT; пакет, поставленный в окружение, базовому интерпретатору не виден; pip freeze печатает снимок установленного, а не lock-файл; в свежем окружении на 3.11 два пакета — pip и setuptools, — а с 3.12 один pip; а с 3.14 sys.prefix окружения ставит инициализация путей, и python -S его уже не сбрасывает.

Полное техническое изложение

TL;DR

Виртуальное окружение — это отдельная папка со своим интерпретатором и своим местом для пакетов. Его создаёт python -m venv env, а пакеты в него ставит pip. Так у каждого проекта свои версии зависимостей, и установка пакета для одного проекта не ломает другой.

Отсюда главное: окружение — это интерпретатор env/bin/python, а не «включённый режим». В корне окружения, на уровень выше bin/python, лежит pyvenv.cfg, и по этому файлу интерпретатор сам ставит sys.prefix — каталог, который он считает своей установкой и от которого ищет пакеты, — на окружение; в прогоне без всякой активации. activate нужен оболочке: он кладёт env/bin первым в PATH, чтобы команда python нашла нужный интерпретатор.

Дальше — то, что отличает понимание от пересказа. Пакет, поставленный в окружение, базовому интерпретатору не виден: ModuleNotFoundError. pip freeze печатает снимок установленного в формате requirements: пакет из PyPI — строкой имя==версия, editable- и локальная установка — строкой с путём; из снимка делают requirements.txt. По нему ставятся те же версии, если они доступны для той платформы и версии Python, — но это не lock-файл и не побайтная копия. В свежем окружении на 3.11 два пакета, pip и setuptools, а с 3.12 — один pip. С 3.13 python -m venv кладёт в окружение .gitignore, и git не видит окружение. С 3.14 sys.prefix окружения ставит инициализация путей, а не модуль site, и запуск с -S его уже не сбрасывает.

Порог входа
Перед уроком достаточно понимать
  • как запустить команду в терминале;
  • что делает import и что такое пакет — урок «Модули и импорт»;
  • что такое переменная окружения PATH;
  • что команды урока — для POSIX-оболочки (bash или zsh на Linux и macOS); вариант для Windows дан рядом.
Заранее знать не нужно
  • python -m venv, pyvenv.cfg, sys.prefix и sys.base_prefix;
  • что делает activate, pip install, pip freeze, requirements.txt;
  • что изменилось в окружениях в 3.12, 3.13 и 3.14.

На чём проверено: CPython 3.11.15 и 3.12.3 (сборки Ubuntu), 3.13.7 и 3.14.7; linux/x86_64, bash; pip — тот, что ставит в свежее окружение ensurepip каждой версии. Документация и исходники сверены 14.10.2026. На эту дату вышел и Python 3.15 (9 октября 2026) — урок на нём не запускался.

База: зачем окружение и как его создают

Полный пример — команды для bash/zsh (POSIX: Linux, macOS); их можно выполнить по порядку в пустой папке, для установки из PyPI нужна сеть:

BASH
python3 -m venv env                          # создать окружение в папке env
source env/bin/activate                      # активировать (bash/zsh)
python -m pip install requests==2.32.3       # поставить пакет в окружение
python -m pip freeze > requirements.txt      # записать снимок установленного
deactivate                                   # выйти из окружения
python3 -m venv env2                         # создать второе, новое окружение
env2/bin/python -m pip install -r requirements.txt   # поставить в него по снимку

python -m pip, а не просто pip: так видно, каким интерпретатором — а значит, в какое окружение — ставится пакет. Последняя команда ставит пакеты во второе окружение без активации — по полному пути к его интерпретатору. На Windows папка скриптов окружения — Scripts, а не bin; активация по документации venv — env\Scripts\activate.bat в cmd.exe и env\Scripts\Activate.ps1 в PowerShell. Прогоны урока — только Linux и bash.

Ответ уровня Junior на «зачем виртуальное окружение»: чтобы у каждого проекта были свои версии пакетов, независимые от системы и других проектов; создают python -m venv, активируют activate, ставят пакеты pip, версии фиксируют в requirements.txt. Это верно, и за этим ответом идут уточнения: что такое окружение физически, обязательно ли его активировать, что именно делает activate и почему нельзя ставить пакеты в системный Python. Механизмы ниже отвечают на них прогоном.

Механизм 1: окружение — это папка и pyvenv.cfg

контракт языкаВывод — прогон bench/venv/basics.py, разделы 1 и 6, CPython 3.13.7. На 3.11 и 3.12 нет .gitignore, на 3.14 в bin/ есть ещё одно имя — см. историю версий; раздел 6 одинаков на 3.11–3.14 с точностью до версии в пути.

Вывод bench/venv/basics.py, раздел 1, CPython 3.13.7:

1) что внутри папки окружения (верхний уровень и bin/)
    ['.gitignore', 'bin', 'include', 'lib', 'lib64', 'pyvenv.cfg']
   bin/: ['Activate.ps1', 'pip', 'pip3', 'pip3.13', 'python', 'python3', 'python3.13'] + скрипты activate*
   bin/python — символическая ссылка: True -> <python>/bin/python3.13
   абсолютный путь окружения записан в bin/activate: True
   первая строка bin/pip: #!<папка>/env/bin/python3.13
   pyvenv.cfg:
      home = <python>/bin
      include-system-site-packages = false
      version = 3.13.7

Внутри окружения нет копии стандартной библиотеки: bin/python — ссылка на базовый интерпретатор, lib/…/site-packages — своя папка для пакетов (в свежем окружении в ней только pip), lib64 — ссылка на lib, а pyvenv.cfg записывает, откуда взят интерпретатор (home) и видны ли системные пакеты (include-system-site-packages = false — не видны).

Зато в окружении записаны абсолютные пути: в bin/activate и в первой строке bin/pip — по ней система выбирает, каким интерпретатором запустить скрипт. Что это значит, показывает копия окружения в другую папку той же машины. Вывод bench/venv/basics.py, раздел 6, CPython 3.13.7:

6) копия окружения в другую папку той же машины
   moved/bin/python: sys.prefix      -> <папка>/moved
   moved/bin/pip --version           -> <папка>/env/lib/python3.13/site-packages/pip
   moved/bin/python -m pip --version -> <папка>/moved/lib/python3.13/site-packages/pip
   без старой папки moved/bin/pip    -> FileNotFoundError: No such file or directory

Интерпретатор копии видит новую папку, а bin/pip по шебангу запускает интерпретатор старого окружения — и ставил бы пакеты туда. Без старой папки он не запускается вовсе. python -m pip работает с копией, но остальные скрипты из bin/ сломаны так же, как pip. На другой машине вдобавок home и ссылка bin/python указывают на установку Python, которой там может не быть. Поэтому окружение не переносят копированием: его пересоздают и ставят зависимости заново.

Механизм 2: как интерпретатор понимает, что он в окружении

контракт языкаПравило — строка документации модуля site (на 3.14.7 она та же, но кто ставит sys.prefix, с 3.14 изменилось — см. «Глубже»); вывод — прогон bench/venv/basics.py, раздел 2, одинаковый на CPython 3.11–3.14 с точностью до версии в пути.

If a file named "pyvenv.cfg" exists one directory above sys.executable, sys.prefix and sys.exec_prefix are set to that directory and it is also checked for site-packages (sys.base_prefix and sys.base_exec_prefix will always be the "real" prefixes of the Python installation).

перевод

Если на один каталог выше sys.executable лежит файл pyvenv.cfg, sys.prefix и sys.exec_prefix указывают на этот каталог, и в нём тоже ищется site-packages; sys.base_prefix и sys.base_exec_prefix всегда остаются «настоящими» префиксами установки Python.

— Модуль site — как интерпретатор находит окружение

Интерпретатор окружения запущен по пути, без активации. Вывод bench/venv/basics.py, раздел 2, CPython 3.13.7:

2) интерпретатор окружения, без активации
   sys.prefix               -> <папка>/env
   sys.base_prefix          -> <python>
   prefix != base_prefix    -> True
   куда ставятся пакеты     -> <папка>/env/lib/python3.13/site-packages

sys.prefix указывает на окружение, sys.base_prefix — на базовую установку, и пакеты ищутся и ставятся в site-packages окружения. Всё это решает сам интерпретатор по файлу pyvenv.cfg на уровень выше себя — в корне окружения. Отсюда практическое следствие: в cron, Dockerfile или systemd окружение не активируют — пишут полный путь env/bin/python script.py. Документация venv говорит то же: You don’t specifically need to activate a virtual environment, as you can just specify the full path to that environment’s Python interpreter when invoking Python (активировать окружение не обязательно: можно просто указать полный путь к интерпретатору окружения при запуске Python). А проверка «работаю ли я в окружении» из кода — sys.prefix != sys.base_prefix.

Кто именно ставит sys.prefix, зависит от версии: до 3.13 включительно — модуль site при старте интерпретатора, с 3.14 — инициализация путей, ещё до site. На обычный запуск это не влияет — раздел 2 прогона одинаков на 3.11–3.14. Разница видна только с ключом -S, который отключает site, — об этом в разделе «Глубже».

Механизм 3: изоляция пакетов и pip freeze

контракт языкаВывод — прогон bench/venv/basics.py, разделы 3, 4 и 7, CPython 3.13.7. Состав свежего окружения различается по версиям — см. ниже. Раздел 4 одинаков на 3.11–3.14, кроме папки пакетов базового интерпретатора: у сборок 3.11 и 3.12 из Ubuntu это <python>/local/lib/python3.1X/dist-packages. Раздел 7 одинаков на 3.11–3.14, кроме строки freeze --all. Шаги 4 и 7 скачивают пакеты из PyPI. Что freeze пропускает — pip/_internal/commands/freeze.py.

Вывод bench/venv/basics.py, разделы 3 и 4, CPython 3.13.7:

3) pip list в свежем окружении
    pip==25.2

4) изоляция: pip install tomli-w==1.0.0 в окружение
   интерпретатор окружения -> импортирован, версия 1.0.0
   базовый интерпретатор   -> ModuleNotFoundError: No module named 'tomli_w'
   у базового sys.prefix   -> <python>
   у базового пакеты в     -> <python>/lib/python3.13/site-packages
   Name в метаданных пакета -> tomli_w
   pip freeze              -> ['tomli_w==1.0.0']

В свежем окружении на 3.13 — только pip. Пакет, поставленный pip install в окружение, интерпретатор окружения импортирует, а базовый — нет: пакеты лежат в разных site-packages: у окружения — <папка>/env/lib/…, у базового — своя. Это и есть изоляция: проекты с разными версиями одной библиотеки живут на одной машине, не мешая друг другу и системе.

pip freeze печатает снимок установленного — в формате requirements, как сказано в описании команды: Output installed packages in requirements format (выводит установленные пакеты в формате requirements). Пакет из PyPI попадает в снимок строкой имя==версия, но так выглядят не все строки. В разделе 7 прогона в окружение дополнительно поставлены пакет из локальной папки и editable-пакет (pip install -e — установка ссылкой на папку с исходниками). Вывод bench/venv/basics.py, раздел 7, CPython 3.13.7:

7) pip freeze: что попадает в снимок
   python -m pip freeze:
      # Editable install with no version control (editpkg==0.1)
      -e <папка>/editpkg
      localpkg @ file://<папка>/localpkg
      tomli_w==1.0.0
   только в python -m pip freeze --all: ['pip==25.2']

Пакет из локальной папки записан прямой ссылкой (имя @ file://…), editable-пакет — строкой -e с путём; эти пути указывают на папки этой машины. Сам pip freeze пропускает, а --all его показывает. Что пропускать, решает pip по версии Python: до 3.11 включительно — pip, setuptools, distribute и wheel, с 3.12 — только pip (pip/_internal/commands/freeze.py, строки 12–20 в pip 24.0 из 3.11.15). В прогоне на 3.11 --all добавил pip==24.0 и setuptools==79.0.1.

Вывод freeze сохраняют в requirements.txt, чтобы python -m pip install -r requirements.txt в новом окружении поставил те же версии. Это сработает, если эти версии доступны для той платформы и версии Python; побайтно одинаковой сборки снимок не обещает — он хранит имена, версии и пути, а не сами файлы пакетов. pip сам отделяет freeze от lock-файла: pip freeze reports what is installed; it does not compute a lockfile or a solver result (pip freeze сообщает, что установлено; lock-файл или результат разрешения зависимостей он не вычисляет). Имя freeze печатает так, как оно записано в метаданных пакета, — там Name: tomli_w, — а не так, как его набрали в pip install tomli-w.

Механизм 4: что делает activate

контракт языкаВывод — прогон bench/venv/basics.py, раздел 5, CPython 3.13.7; на 3.11 и 3.12 VIRTUAL_ENV_PROMPT — «(env) » со скобками, остальное то же. Что ещё делает activate — шаблон Lib/venv/scripts/common/activate.

Вывод bench/venv/basics.py, раздел 5, CPython 3.13.7, bash на Linux:

5) активация в bash: что она меняет
    VIRTUAL_ENV=<папка>/env
    VIRTUAL_ENV_PROMPT=env
    первый элемент PATH: <папка>/env/bin
    python в PATH: <папка>/env/bin/python
    после deactivate PATH прежний: да

activate — скрипт оболочки, а не часть Python. Для того, какой python найдётся, важен один PATH: env/bin встаёт в нём первым, и команды python и pip находят интерпретатор окружения. Кроме того, activate задаёт VIRTUAL_ENV и VIRTUAL_ENV_PROMPT, добавляет имя окружения в начало приглашения оболочки (PS1) и снимает PYTHONHOME, если она задана; deactivate всё это возвращает. Внутри интерпретатора активация ничего не меняет: если PYTHONHOME не задана, интерпретатор окружения работает одинаково, запущен ли он через PATH или по полному пути.

Это POSIX-вариант для bash/zsh; для fish и csh в bin/ лежат свои скрипты (activate.fish, activate.csh), а на Windows по документации venv — env\Scripts\activate.bat в cmd.exe и env\Scripts\Activate.ps1 в PowerShell. Прогон урока проверял только bash на Linux.

Глубже: что изменилось в окружениях

контракт языкаВывод — прогоны bench/venv/basics.py на CPython 3.11.15, 3.12.3, 3.13.7 и 3.14.7; причины — Lib/venv/__init__.py, Lib/ensurepip/__init__.py и Lib/site.py соответствующих версий и документация модуля site 3.14.

Сравнение прогонов на четырёх версиях показывает четыре изменения:

  • 3.12: в свежем окружении нет setuptools. На 3.11 pip list в новом окружении — pip и setuptools, с 3.12 — только pip. Свежее окружение наполняет ensurepip, и набор задан в нём: на 3.11 _PACKAGE_NAMES = ('setuptools', 'pip'), на 3.12 — ('pip',). Проект, который неявно опирался на setuptools в окружении, на 3.12 должен объявить его зависимостью явно.
  • 3.13: python -m venv кладёт в окружение .gitignore. Его содержимое cause the entire environment directory to be ignored by git (заставляет git игнорировать весь каталог окружения) — окружение не попадёт в репозиторий, даже если его забыли вписать в .gitignore проекта. Файл создаёт команда python -m venv, флаг --without-scm-ignore-files это отключает; venv.create() из кода по умолчанию его не создаёт.
  • 3.14: sys.prefix окружения ставит инициализация путей, а не модуль site, и ключ -S его больше не сбрасывает. Подробно — ниже, после списка.
  • 3.14: в bin/ есть ещё одно имя интерпретатора — 𝜋thon. Его добавляют только на 3.14 и только при кодировке файловой системы UTF-8; на работу окружения оно не влияет.

Версия pip в свежем окружении своя у каждого интерпретатора: в прогонах — 24.0 на 3.11 и 3.12, 25.2 на 3.13, 26.2.1 на 3.14. Её обновляют внутри окружения: python -m pip install --upgrade pip — или сразу при создании: python -m venv --upgrade-deps env. Для этого флага и нужен список CORE_VENV_DEPS в исходниках venv: на 3.11 — pip и setuptools, с 3.12 — pip.

Теперь о префиксе на 3.14. В документации site 3.14: site is no longer responsible for updating sys.prefix and sys.exec_prefix on Virtual Environments. This is now done during the path initialization (site больше не отвечает за то, чтобы выставить sys.prefix и sys.exec_prefix в виртуальных окружениях. Теперь это делается при инициализации путей) — и поэтому префиксы окружения больше не зависят от -S, ключа, который запускает интерпретатор без модуля site. Раздел 8 прогона — тот же env/bin/python, но с -S. Вывод bench/venv/basics.py, раздел 8, CPython 3.13.7:

8) env/bin/python -S: модуль site не импортируется
   sys.prefix               -> <python>
   sys.base_prefix          -> <python>
   prefix != base_prefix    -> False
   site в sys.modules       -> False
   site-packages в sys.path -> False

На 3.11 и 3.12 — то же: префикс окружения ставил site, и без него интерпретатор окружения считал себя базовой установкой. Тот же раздел на CPython 3.14.7:

8) env/bin/python -S: модуль site не импортируется
   sys.prefix               -> <папка>/env
   sys.base_prefix          -> <python>
   prefix != base_prefix    -> True
   site в sys.modules       -> False
   site-packages в sys.path -> False

Установка префикса и подключение site-packages теперь разделены: префикс ставит инициализация путей, а site-packages окружения в sys.path добавляет по-прежнему site, и при -S — последняя строка — их нет в sys.path ни на одной версии. Без -S разницы между версиями нет: раздел 2 прогона одинаков на 3.11–3.14, и прежнее объяснение «интерпретатор сам находит окружение по pyvenv.cfg» остаётся верным — на 3.14 меняется только то, какая часть интерпретатора это делает.

Как отвечать на собеседовании

Короткий ответ: виртуальное окружение — папка со своим интерпретатором и своим site-packages; создаётся python -m venv. Интерпретатор окружения находит его сам по pyvenv.cfg, а activate нужен оболочке — через PATH команда python находит интерпретатор окружения. Пакеты ставят python -m pip внутрь окружения, установленные версии записывают pip freeze в requirements.txt — это снимок установленного, а не lock-файл. В системный Python пакеты не ставят: окружение изолирует версии проектов.

Этого достаточно, чтобы ответить верно. Дальше — то, что добавляют, если собеседник копает.

Если интервьюер копает глубже

На «обязательно ли активировать окружение». «Нет. Интерпретатор env/bin/python сам ставит sys.prefix на окружение по pyvenv.cfg. В моём прогоне без активации sys.prefix != sys.base_prefix — True. В Docker и cron так и запускают — по полному пути».

На «что делает activate». «Главное — ставит env/bin первым в PATH, поэтому python и pip находят окружение. Ещё задаёт VIRTUAL_ENV и VIRTUAL_ENV_PROMPT, меняет приглашение оболочки и снимает PYTHONHOME; deactivate всё возвращает. Сам Python она не меняет — только переменные оболочки, которые он унаследует».

На «можно ли скопировать окружение на другую машину». «Нет. В bin/activate и в первой строке bin/pip записан абсолютный путь окружения. В моём прогоне даже в другой папке bin/pip копии запустил старое окружение, а без старой папки не запустился вовсе. На другой машине ещё и home в pyvenv.cfg и ссылка bin/python указывают на установку Python, которой там может не быть. Переносят список зависимостей — requirements.txt или lock-файл — и создают окружение заново, на совместимой платформе и версии Python».

На «чем pip freeze отличается от requirements.txt, написанного руками». «pip freeze печатает снимок всего установленного в формате requirements, с точными версиями, включая транзитивные зависимости, — кроме самого pip (на Python до 3.11 — ещё setuptools, wheel и distribute); editable- и локальные установки он пишет строками с путём. Руками обычно пишут только прямые зависимости. И это не lock-файл: pip прямо пишет, что freeze сообщает, что установлено, а lock-файл не вычисляет».

Дальше спросят

Спросят дальше

Чем venv отличается от poetry и uv?

Короткий ответ

venv только создаёт окружение. pip ставит пакеты и при установке сам подбирает совместимые версии, но не ведёт список зависимостей проекта и по умолчанию не пишет lock-файл. Poetry и uv делают это: ведут зависимости в pyproject.toml, записывают точные версии всего дерева в lock-файл и создают окружение сами. В основе у них то же окружение с pyvenv.cfg.

Спросят дальше

Зачем нужен --system-site-packages?

Короткий ответ

Он записывает в pyvenv.cfg include-system-site-packages = true, и окружение видит пакеты базовой установки — например, тяжёлые системные библиотеки, которые не хочется ставить заново. Изоляция при этом ослабевает: в прогоне урока этот ключ — false, и базовые пакеты не видны.

Спросят дальше

Почему ставить пакеты в системный Python — плохо?

Короткий ответ

В Linux-дистрибутивах системный Python использует сама операционная система, и её утилиты рассчитывают на конкретные версии пакетов. Установка поверх может их сломать, а два проекта с разными версиями одной библиотеки в одном site-packages не уживутся. Окружение решает обе проблемы.

Частые заблуждения

Утверждение

Без activate окружение не работает

На самом деле

Интерпретатор env/bin/python сам находит окружение по pyvenv.cfg: в прогоне без активации sys.prefix != sys.base_prefix — True.

Утверждение

Окружение содержит копию Python

На самом деле

У python -m venv на Linux и macOS bin/python — ссылка на базовый интерпретатор, а home в pyvenv.cfg указывает на его папку. С ключом --copies копируется сам исполняемый файл, а на Windows в окружение кладётся небольшой лаунчер, который запускает базовый python.exe. Стандартной библиотеки внутри нет ни в одном случае: она берётся из базовой установки.

Утверждение

activate что-то меняет внутри Python

На самом деле

Сам интерпретатор она не трогает: меняет переменные оболочки — PATH, VIRTUAL_ENV, VIRTUAL_ENV_PROMPT, приглашение. PYTHONHOME снимает, чтобы заданная переменная не увела интерпретатор окружения в чужую установку. После deactivate PATH прежний.

Утверждение

В новом окружении всегда есть setuptools

На самом деле

Только до 3.11 включительно. С 3.12 в свежем окружении один pip.

Утверждение

requirements.txt из pip freeze воспроизведёт окружение на любой машине

На самом деле

Это снимок установленного: имена, версии, а для editable- и локальных установок — пути этой машины. Те же версии поставятся, если они доступны для той платформы и версии Python; побайтной копии снимок не обещает, и lock-файлом, по словам самого pip, он не является.

Утверждение

Окружение можно скопировать в другую папку или на другую машину

На самом деле

В bin/activate и первой строке bin/pip — абсолютный путь окружения: в прогоне bin/pip копии запустил старое окружение, а без старой папки не запустился. На другой машине вдобавок может не быть установки Python, на которую указывает home. Окружение пересоздают по requirements.txt.

История версий

ВерсияИзменениеЧто это значит для кода
3.11Опорная точка урока. В свежем окружении — pip и setuptools.
3.12venv больше не ставит setuptools: в свежем окружении один pip. Сборка, неявно опиравшаяся на setuptools, должна объявить его явно.
3.13Команда python -m venv создаёт в папке окружения .gitignore со звёздочкой — git не видит окружение.
3.14sys.prefix окружения ставит инициализация путей, а не модуль site: в прогоне env/bin/python -S на 3.14.7 видит окружение (prefix != base_prefix — True), на 3.11–3.13 — нет. Без -S всё как раньше. Ещё в bin/ появилось имя интерпретатора 𝜋thon, только при кодировке файловой системы UTF-8; на работу окружения оно не влияет.
3.15Вышел 9 октября 2026. Урок на нём не запускался: прогоны — CPython 3.11–3.14, сверено 14.10.2026.

Практика

Две задачи. Сначала ответьте, потом сверьтесь с настоящим выводом: в обеих правильный ответ взят из прогона скрипта, а не назначен.

Практика · что напечатает

Окружение env создано, но не активировано. Этот код запускают командой env/bin/python. Что он напечатает?
import sys
print(sys.prefix == sys.base_prefix)
print(sys.prefix.endswith("env"))

Практика · оцените

Сколько пакетов покажет pip list в только что созданном окружении на CPython 3.13?
пакет

Проверьте себя

Вопрос 1 из 4

Скрипт в cron запускается как /app/env/bin/python job.py, activate не вызывается. Какие пакеты он увидит?

Чем измерено

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

Источники и что читать дальше

7 ИСТОЧНИКОВ

  1. Модуль site — как интерпретатор находит окружениеОфициальная документация. Строка документации модуля site: «If a file named "pyvenv.cfg" exists one directory above sys.executable, sys.prefix and sys.exec_prefix are set to that directory and it is also checked for site-packages (sys.base_prefix and sys.base_exec_prefix will always be the "real" prefixes of the Python installation)» (Если на один каталог выше sys.executable лежит файл pyvenv.cfg, sys.prefix и sys.exec_prefix указывают на этот каталог, и в нём тоже ищется site-packages; sys.base_prefix и sys.base_exec_prefix всегда остаются „настоящими“ префиксами установки Python). Сверено вызовом site.__doc__ на 3.11.15, 3.12.3, 3.13.7 и 3.14.7.https://docs.python.org/3.13/library/site.html
  2. Lib/venv/__init__.py — что venv кладёт в окружениеИсходный код CPython. На 3.13.7: строки 444–446 — метод _setup_pip, который наполняет свежее окружение вызовом ensurepip; строка 17 — CORE_VENV_DEPS, список пакетов для флага --upgrade-deps (метод upgrade_dependencies, строки 585–590; на 3.11.15 — ('pip', 'setuptools'), с 3.12.3 — ('pip',)). Строки 279–290 — метод create_git_ignore_file: «The contents of the file cause the entire environment directory to be ignored by git» (Содержимое файла заставляет git игнорировать весь каталог окружения); командная строка python -m venv включает его по умолчанию (строка 662, default=frozenset(['git'])), а у EnvBuilder и venv.create по умолчанию scm_ignore_files=frozenset() (строки 51 и 595). Строки 185–190 — lib64 как символическая ссылка на lib на 64-битных POSIX-системах, кроме macOS. Строки 318–345 — на Windows вместо ссылки в окружение копируется лаунчер venvlauncher.exe под именем python.exe; строки 623–626 — у командной строки на Windows символические ссылки по умолчанию выключены. На 3.14.7 строки 318–319 — suffixes.append('𝜋thon'), только при sys.version_info[:2] == (3, 14) и кодировке файловой системы UTF-8.https://github.com/python/cpython/blob/v3.13.7/Lib/venv/__init__.py
  3. Lib/ensurepip/__init__.py — что ставится в свежее окружениеИсходный код CPython. На 3.11.15 строки 12–14: _PACKAGE_NAMES = ('setuptools', 'pip'), _SETUPTOOLS_VERSION = "79.0.1", _PIP_VERSION = "24.0". На 3.12.3 строки 40–41: _PACKAGE_NAMES = ('pip',), _PIP_VERSION = "24.0". На 3.13.7 и 3.14.7 _PACKAGE_NAMES и _SETUPTOOLS_VERSION нет, ставится только pip; _PIP_VERSION (строка 13) — "25.2" и "26.2.1". Сверено по установленным файлам этих версий; 3.11.15 и 3.12.3 — сборки Ubuntu, номера строк даны по ним и в исходном коде CPython могут быть другими.https://github.com/python/cpython/blob/v3.12.3/Lib/ensurepip/__init__.py
  4. Lib/venv/scripts/common/activate — что делает активацияИсходный код CPython. Шаблон activate на 3.13.7: строки 57–58 — export VIRTUAL_ENV_PROMPT; 60–66 — снимает PYTHONHOME, если она задана; 69–71 — добавляет имя окружения в начало PS1, если не задана VIRTUAL_ENV_DISABLE_PROMPT; 76 — hash -r; строки 4–34 — функция deactivate, которая всё это возвращает.https://github.com/python/cpython/blob/v3.13.7/Lib/venv/scripts/common/activate
  5. Модуль site в 3.14 — префиксы окружения ставит инициализация путейОфициальная документация. Документация site 3.14: «Changed in version 3.14: site is no longer responsible for updating sys.prefix and sys.exec_prefix on Virtual Environments. This is now done during the path initialization. As a result, under Virtual Environments, sys.prefix and sys.exec_prefix no longer depend on the site initialization, and are therefore unaffected by -S» (Изменено в 3.14: site больше не отвечает за то, чтобы выставить sys.prefix и sys.exec_prefix в виртуальных окружениях. Теперь это делается при инициализации путей. Поэтому в виртуальных окружениях sys.prefix и sys.exec_prefix больше не зависят от инициализации site и не меняются от -S). В исходнике: функция venv() в Lib/site.py на 3.13.7, строка 628, — sys.prefix = sys.exec_prefix = site_prefix (в сборках Ubuntu 3.11.15 и 3.12.3 — строки 554 и 555); на 3.14.7, строки 634–637, она только предупреждает, если sys.prefix не совпал с папкой окружения. Строка документации модуля site (site.__doc__) на 3.14.7 осталась прежней. Прогон — bench/venv/basics.py, раздел 8. Сверено 14.10.2026.https://docs.python.org/3.14/library/site.html
  6. pip/_internal/commands/freeze.py — что pip freeze пропускаетИсходный код. В pip 24.0, который ставит ensurepip на 3.11.15: строка 27 — описание команды «Output installed packages in requirements format» (Выводит установленные пакеты в формате requirements); строки 12–20 — _should_suppress_build_backends() возвращает sys.version_info < (3, 12), и тогда к пропускаемому pip добавляются setuptools, distribute и wheel; с Python 3.12 пропускается только pip. В pip 25.2 (3.13.7) и 26.2.1 (3.14.7) то же правило — строки 11–19. Флаг --all пропуск отключает. Документация pip о различии со lock-файлом: «pip freeze reports what is installed; it does not compute a lockfile or a solver result» (pip freeze сообщает, что установлено; lock-файл или результат разрешения зависимостей он не вычисляет). Сверено по установленным файлам и pip.pypa.io 14.10.2026.https://pip.pypa.io/en/stable/cli/pip_freeze/
  7. venv — команды активации на разных платформахОфициальная документация. Таблица документации venv 3.14: POSIX, bash/zsh — source <venv>/bin/activate; fish — source <venv>/bin/activate.fish; csh/tcsh — source <venv>/bin/activate.csh; Windows, cmd.exe — <venv>\Scripts\activate.bat; PowerShell — <venv>\Scripts\Activate.ps1. И: «You don’t specifically need to activate a virtual environment, as you can just specify the full path to that environment’s Python interpreter when invoking Python» (Активировать окружение не обязательно: можно просто указать полный путь к интерпретатору окружения при запуске Python). Прогон урока — только bash на Linux. Сверено 14.10.2026.https://docs.python.org/3.14/library/venv.html