Deep Engineering
Intermediate·Published·3.11 · 3.12 · 3.13 · 3.14·25 MIN

Environments and dependencies: what venv does, and what pip does

Questions about virtual environments seem mundane until you are asked how the interpreter knows it is in an environment. A run answers: an environment is a folder with its own bin/python and a pyvenv.cfg file; env/bin/python sets sys.prefix to the environment without any activation; activate is a shell script that puts env/bin first in PATH and sets VIRTUAL_ENV and VIRTUAL_ENV_PROMPT; a package installed into the environment is not visible to the base interpreter; pip freeze prints a snapshot of what is installed, not a lock file; a fresh environment has two packages on 3.11 — pip and setuptools — and one, pip, since 3.12; and since 3.14 the environment's sys.prefix is set by path initialization, so python -S no longer resets it.

Full technical treatment

TL;DR

A virtual environment is a separate folder with its own interpreter and its own place for packages. It is created by python -m venv env, and packages are installed into it by pip. That way each project has its own versions of its dependencies, and installing a package for one project does not break another.

Hence the main point: an environment is the interpreter env/bin/python, not a "switched-on mode". At the root of the environment, one level above bin/python, lies pyvenv.cfg, and from that file the interpreter sets sys.prefix — the directory it considers its installation and from which it looks for packages — to the environment by itself; in the run, without any activation. activate is for the shell: it puts env/bin first in PATH so that the python command finds the right interpreter.

Beyond that comes what separates understanding from having read about it. A package installed into the environment is not visible to the base interpreter: ModuleNotFoundError. pip freeze prints a snapshot of what is installed in requirements format: a package from PyPI as name==version, an editable or local install as a line with a path; requirements.txt is made from the snapshot. It installs the same versions if they are available for that platform and Python version — but it is not a lock file and not a byte-for-byte copy. A fresh environment has two packages on 3.11, pip and setuptools, and one, pip, since 3.12. Since 3.13 python -m venv puts a .gitignore into the environment, and git ignores the environment. Since 3.14 the environment's sys.prefix is set by path initialization rather than by the site module, and running with -S no longer resets it.

Where to start
Before this lesson it is enough to understand
  • how to run a command in a terminal;
  • what import does and what a package is — the lesson "Modules and imports";
  • what the PATH environment variable is;
  • that the lesson's commands are for a POSIX shell (bash or zsh on Linux and macOS); the Windows variant is given next to them.
You do not need to know in advance
  • python -m venv, pyvenv.cfg, sys.prefix and sys.base_prefix;
  • what activate does, pip install, pip freeze, requirements.txt;
  • what changed in environments in 3.12, 3.13 and 3.14.

What it was checked on: CPython 3.11.15 and 3.12.3 (Ubuntu builds), 3.13.7 and 3.14.7; linux/x86_64, bash; pip is the one ensurepip of each version puts into a fresh environment. The documentation and sources were checked on 14.10.2026. By that date Python 3.15 had also been released (9 October 2026) — the lesson was not run on it.

Base: why an environment and how it is created

A full example — commands for bash/zsh (POSIX: Linux, macOS); they can be run in order in an empty folder, and installing from PyPI needs the network:

BASH
python3 -m venv env                          # create an environment in the env folder
source env/bin/activate                      # activate it (bash/zsh)
python -m pip install requests==2.32.3       # install a package into the environment
python -m pip freeze > requirements.txt      # record a snapshot of what is installed
deactivate                                   # leave the environment
python3 -m venv env2                         # create a second, new environment
env2/bin/python -m pip install -r requirements.txt   # install into it from the snapshot

python -m pip rather than plain pip: that way you see which interpreter — and so which environment — the package goes into. The last command installs packages into the second environment without activation — by the full path to its interpreter. On Windows the environment's scripts folder is Scripts, not bin; activation according to the venv documentation is env\Scripts\activate.bat in cmd.exe and env\Scripts\Activate.ps1 in PowerShell. The lesson's runs are Linux and bash only.

A junior-level answer to "why a virtual environment": so that each project has its own package versions, independent of the system and other projects; it is created with python -m venv, activated with activate, packages are installed with pip, versions are pinned in requirements.txt. That is correct, and it is followed by questions: what an environment physically is, whether it must be activated, what exactly activate does, and why you should not install packages into the system Python. The mechanisms below answer them with a run.

Mechanism 1: an environment is a folder and pyvenv.cfg

language contractThe output is a run of bench/venv/basics.py, sections 1 and 6, CPython 3.13.7. On 3.11 and 3.12 there is no .gitignore, and on 3.14 bin/ has one more name — see the version history; section 6 is identical on 3.11–3.14 apart from the version in the path.

Output of bench/venv/basics.py, section 1, CPython 3.13.7; labels translated:

1) what is inside the environment folder (top level and bin/)
    ['.gitignore', 'bin', 'include', 'lib', 'lib64', 'pyvenv.cfg']
   bin/: ['Activate.ps1', 'pip', 'pip3', 'pip3.13', 'python', 'python3', 'python3.13'] + activate* scripts
   bin/python is a symbolic link: True -> <python>/bin/python3.13
   the environment's absolute path is written in bin/activate: True
   first line of bin/pip: #!<folder>/env/bin/python3.13
   pyvenv.cfg:
      home = <python>/bin
      include-system-site-packages = false
      version = 3.13.7

There is no copy of the standard library inside the environment: bin/python is a link to the base interpreter, lib/…/site-packages is the environment's own folder for packages (a fresh environment has only pip there), lib64 is a link to lib, and pyvenv.cfg records where the interpreter comes from (home) and whether system packages are visible (include-system-site-packages = false — they are not).

What the environment does record are absolute paths: in bin/activate and in the first line of bin/pip — the line the system uses to choose which interpreter runs the script. A copy of the environment in another folder on the same machine shows what that means. Output of bench/venv/basics.py, section 6, CPython 3.13.7; labels translated, error texts verbatim:

6) a copy of the environment in another folder on the same machine
   moved/bin/python: sys.prefix      -> <folder>/moved
   moved/bin/pip --version           -> <folder>/env/lib/python3.13/site-packages/pip
   moved/bin/python -m pip --version -> <folder>/moved/lib/python3.13/site-packages/pip
   old folder gone: moved/bin/pip    -> FileNotFoundError: No such file or directory

The copy's interpreter sees the new folder, while bin/pip, through its shebang, starts the old environment's interpreter — and would install packages there. Without the old folder it does not start at all. python -m pip works with the copy, but the other scripts in bin/ are broken the same way as pip. On another machine, in addition, home and the bin/python link point to a Python installation that may not exist there. That is why an environment is not moved by copying: it is recreated and the dependencies are installed again.

Mechanism 2: how the interpreter knows it is in an environment

language contractThe rule is the docstring of the site module (on 3.14.7 it is the same, but what sets sys.prefix changed in 3.14 — see Deeper); the output is a run of bench/venv/basics.py, section 2, identical on CPython 3.11–3.14 apart from the version in the path.

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).

— The site module — how the interpreter finds an environment

The environment's interpreter is run by its path, without activation. Output of bench/venv/basics.py, section 2, CPython 3.13.7; labels translated:

2) the environment's interpreter, without activation
   sys.prefix               -> <folder>/env
   sys.base_prefix          -> <python>
   prefix != base_prefix    -> True
   where packages go        -> <folder>/env/lib/python3.13/site-packages

sys.prefix points to the environment, sys.base_prefix to the base installation, and packages are looked up and installed in the environment's site-packages. All of this is decided by the interpreter itself from the pyvenv.cfg file one level above it — at the root of the environment. Hence a practical consequence: in cron, a Dockerfile or systemd the environment is not activated — you write the full path env/bin/python script.py. The venv documentation says the same: 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. And the check "am I in an environment" from code is sys.prefix != sys.base_prefix.

Exactly what sets sys.prefix depends on the version: up to and including 3.13 it is the site module at interpreter startup, since 3.14 it is path initialization, before site. A normal start is not affected — section 2 of the run is identical on 3.11–3.14. The difference shows only with the -S flag, which turns site off — more in the "Deeper" section.

Mechanism 3: package isolation and pip freeze

language contractThe output is a run of bench/venv/basics.py, sections 3, 4 and 7, CPython 3.13.7. The contents of a fresh environment differ by version — see below. Section 4 is identical on 3.11–3.14 except for the base interpreter's package folder: for the Ubuntu builds of 3.11 and 3.12 it is <python>/local/lib/python3.1X/dist-packages. Section 7 is identical on 3.11–3.14 except for the freeze --all line. Steps 4 and 7 download packages from PyPI. What freeze skips is pip/_internal/commands/freeze.py.

Output of bench/venv/basics.py, sections 3 and 4, CPython 3.13.7; labels translated, error texts verbatim:

3) pip list in a fresh environment
    pip==25.2

4) isolation: pip install tomli-w==1.0.0 into the environment
   environment interpreter -> imported, version 1.0.0
   base interpreter        -> ModuleNotFoundError: No module named 'tomli_w'
   base sys.prefix         -> <python>
   base packages in        -> <python>/lib/python3.13/site-packages
   Name in package metadata -> tomli_w
   pip freeze              -> ['tomli_w==1.0.0']

A fresh environment on 3.13 contains only pip. A package installed with pip install into the environment is imported by the environment's interpreter but not by the base one: the packages live in different site-packages — the environment's under <folder>/env/lib/…, the base interpreter's its own. That is isolation: projects with different versions of one library live on one machine without disturbing each other or the system.

pip freeze prints a snapshot of what is installed — in requirements format, as the command's description says: Output installed packages in requirements format. A package from PyPI goes into the snapshot as name==version, but not every line looks like that. In section 7 of the run a package from a local folder and an editable package (pip install -e — an install that links to the folder with the sources) are also installed into the environment. Output of bench/venv/basics.py, section 7, CPython 3.13.7; labels translated, pip's lines verbatim:

7) pip freeze: what goes into the snapshot
   python -m pip freeze:
      # Editable install with no version control (editpkg==0.1)
      -e <folder>/editpkg
      localpkg @ file://<folder>/localpkg
      tomli_w==1.0.0
   only in python -m pip freeze --all: ['pip==25.2']

The package from a local folder is written as a direct reference (name @ file://…), the editable package as a -e line with a path; these paths point to folders on this machine. freeze skips pip itself, and --all shows it. What to skip is decided by pip from the Python version: up to and including 3.11 — pip, setuptools, distribute and wheel, since 3.12 — only pip (pip/_internal/commands/freeze.py, lines 12–20 in pip 24.0 from 3.11.15). In the run on 3.11 --all added pip==24.0 and setuptools==79.0.1.

The freeze output is what is saved in requirements.txt, so that python -m pip install -r requirements.txt in a new environment installs the same versions. That works if those versions are available for that platform and Python version; the snapshot does not promise a byte-for-byte identical build — it stores names, versions and paths, not the package files themselves. pip itself separates freeze from a lock file: pip freeze reports what is installed; it does not compute a lockfile or a solver result. freeze prints the name as it is written in the package's metadata — Name: tomli_w there — not as it was typed in pip install tomli-w.

Mechanism 4: what activate does

language contractThe output is a run of bench/venv/basics.py, section 5, CPython 3.13.7; on 3.11 and 3.12 VIRTUAL_ENV_PROMPT is '(env) ' with parentheses, the rest is the same. What else activate does is the template Lib/venv/scripts/common/activate.

Output of bench/venv/basics.py, section 5, CPython 3.13.7, bash on Linux; labels translated:

5) activation in bash: what it changes
    VIRTUAL_ENV=<folder>/env
    VIRTUAL_ENV_PROMPT=env
    first element of PATH: <folder>/env/bin
    python in PATH: <folder>/env/bin/python
    PATH restored after deactivate: yes

activate is a shell script, not part of Python. For which python gets found, only PATH matters: env/bin goes first in it, and the python and pip commands find the environment's interpreter. Besides that, activate sets VIRTUAL_ENV and VIRTUAL_ENV_PROMPT, adds the environment's name to the start of the shell prompt (PS1) and unsets PYTHONHOME if it is set; deactivate undoes all of it. Inside the interpreter activation changes nothing: when PYTHONHOME is not set, the environment's interpreter works the same whether it is started through PATH or by its full path.

This is the POSIX variant for bash/zsh; fish and csh have their own scripts in bin/ (activate.fish, activate.csh), and on Windows, according to the venv documentation, it is env\Scripts\activate.bat in cmd.exe and env\Scripts\Activate.ps1 in PowerShell. The lesson's run checked bash on Linux only.

Deeper: what changed in environments

language contractThe output is the runs of bench/venv/basics.py on CPython 3.11.15, 3.12.3, 3.13.7 and 3.14.7; the reasons are Lib/venv/__init__.py, Lib/ensurepip/__init__.py and Lib/site.py of the corresponding versions and the documentation of the site module for 3.14.

Comparing the runs on four versions shows four changes:

  • 3.12: no setuptools in a fresh environment. On 3.11 pip list in a new environment gives pip and setuptools, since 3.12 only pip. A fresh environment is filled by ensurepip, and the set is defined there: on 3.11 _PACKAGE_NAMES = ('setuptools', 'pip'), on 3.12 ('pip',). A project that implicitly relied on setuptools in the environment has to declare it as a dependency explicitly on 3.12.
  • 3.13: python -m venv puts a .gitignore into the environment. Its contents cause the entire environment directory to be ignored by git — the environment will not end up in the repository even if it was not added to the project's .gitignore. The file is created by the python -m venv command, the --without-scm-ignore-files flag turns it off, and venv.create() called from code does not create it by default.
  • 3.14: the environment's sys.prefix is set by path initialization, not by the site module, and the -S flag no longer resets it. Details below, after the list.
  • 3.14: bin/ has one more name for the interpreter — 𝜋thon. It is added only on 3.14 and only when the file system encoding is UTF-8; it does not affect how the environment works.

The pip version in a fresh environment is specific to each interpreter: in the runs it is 24.0 on 3.11 and 3.12, 25.2 on 3.13, 26.2.1 on 3.14. It is upgraded inside the environment: python -m pip install --upgrade pip — or right at creation: python -m venv --upgrade-deps env. That flag is what the CORE_VENV_DEPS list in the venv sources is for: pip and setuptools on 3.11, pip since 3.12.

Now the prefix on 3.14. The site documentation for 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 — and so the environment's prefixes no longer depend on -S, the flag that starts the interpreter without the site module. Section 8 of the run is the same env/bin/python, but with -S. Output of bench/venv/basics.py, section 8, CPython 3.13.7; labels translated:

8) env/bin/python -S: the site module is not imported
   sys.prefix               -> <python>
   sys.base_prefix          -> <python>
   prefix != base_prefix    -> False
   site in sys.modules      -> False
   site-packages in sys.path -> False

On 3.11 and 3.12 it is the same: the environment's prefix was set by site, and without it the environment's interpreter considered itself the base installation. The same section on CPython 3.14.7:

8) env/bin/python -S: the site module is not imported
   sys.prefix               -> <folder>/env
   sys.base_prefix          -> <python>
   prefix != base_prefix    -> True
   site in sys.modules      -> False
   site-packages in sys.path -> False

Setting the prefix and adding site-packages are now separate: the prefix is set by path initialization, while the environment's site-packages is still added to sys.path by site, and with -S — the last line — it is not in sys.path on any version. Without -S there is no difference between versions: section 2 of the run is identical on 3.11–3.14, and the earlier explanation "the interpreter finds the environment by itself from pyvenv.cfg" stays correct — in 3.14 only the part of the interpreter that does it changes.

How to answer in an interview

Short answer: a virtual environment is a folder with its own interpreter and its own site-packages; it is created with python -m venv. The environment's interpreter finds it by itself from pyvenv.cfg, and activate is for the shell — through PATH the python command finds the environment's interpreter. Packages are installed with python -m pip inside the environment, the installed versions are recorded with pip freeze in requirements.txt — a snapshot of what is installed, not a lock file. Packages are not installed into the system Python: an environment isolates the versions of projects.

That is enough for a correct answer. What follows is what you add if the interviewer digs.

If the interviewer digs deeper

On "do you have to activate an environment". "No. The interpreter env/bin/python sets sys.prefix to the environment by itself from pyvenv.cfg. In my run, without activation sys.prefix != sys.base_prefix was True. In Docker and cron that is how it is run — by the full path."

On "what does activate do". "Mainly it puts env/bin first in PATH, so python and pip find the environment. It also sets VIRTUAL_ENV and VIRTUAL_ENV_PROMPT, changes the shell prompt and unsets PYTHONHOME; deactivate undoes it all. It does not change Python itself — only the shell variables Python inherits."

On "can you copy an environment to another machine". "No. bin/activate and the first line of bin/pip record the environment's absolute path. In my run, even in another folder the copy's bin/pip started the old environment, and without the old folder it did not start at all. On another machine home in pyvenv.cfg and the bin/python link also point to a Python installation that may not exist there. You move the dependency list — requirements.txt or a lock file — and create the environment anew, on a compatible platform and Python version."

On "how does pip freeze differ from a hand-written requirements.txt". "pip freeze prints a snapshot of everything installed in requirements format, with exact versions, including transitive dependencies — except pip itself (on Python up to 3.11 also setuptools, wheel and distribute); it writes editable and local installs as lines with a path. By hand people usually list only direct dependencies. And it is not a lock file: pip states plainly that freeze reports what is installed and does not compute a lock file."

Next they ask

Next they ask

How does venv differ from poetry and uv?

Short answer

venv only creates an environment. pip installs packages and resolves compatible versions while installing, but it does not keep the project's dependency list and does not write a lock file by default. Poetry and uv do: they keep the dependencies in pyproject.toml, record the exact versions of the whole tree in a lock file, and create the environment themselves. Underneath it is the same environment with pyvenv.cfg.

Next they ask

What is --system-site-packages for?

Short answer

It writes include-system-site-packages = true into pyvenv.cfg, and the environment sees the packages of the base installation — heavy system libraries, for example, that you do not want to install again. Isolation is weaker then: in the lesson's run this key is false, and base packages are not visible.

Next they ask

Why is installing packages into the system Python bad?

Short answer

On Linux distributions the system Python is used by the operating system itself, and its tools rely on specific package versions. Installing on top may break them, and two projects with different versions of one library cannot coexist in one site-packages. An environment solves both problems.

Common misconceptions

Claim

An environment does not work without activate

Actually

The interpreter env/bin/python finds the environment by itself from pyvenv.cfg: in the run, without activation sys.prefix != sys.base_prefix was True.

Claim

An environment contains a copy of Python

Actually

With python -m venv on Linux and macOS bin/python is a symbolic link to the base interpreter, and home in pyvenv.cfg points to its folder. With the --copies flag the executable itself is copied, and on Windows the environment gets a small launcher that starts the base python.exe. In no case is there a standard library inside: it comes from the base installation.

Claim

activate changes something inside Python

Actually

It does not touch the interpreter itself: it changes shell variables — PATH, VIRTUAL_ENV, VIRTUAL_ENV_PROMPT, the prompt. It unsets PYTHONHOME so that a set variable does not lead the environment's interpreter into another installation. After deactivate PATH is as before.

Claim

A new environment always has setuptools

Actually

Only up to and including 3.11. Since 3.12 a fresh environment has just pip.

Claim

A requirements.txt from pip freeze reproduces the environment on any machine

Actually

It is a snapshot of what is installed: names, versions, and for editable and local installs, paths on this machine. The same versions are installed if they are available for that platform and Python version; the snapshot does not promise a byte-for-byte copy, and, in pip's own words, it is not a lock file.

Claim

An environment can be copied to another folder or machine

Actually

bin/activate and the first line of bin/pip hold the environment's absolute path: in the run the copy's bin/pip started the old environment, and without the old folder it did not start. On another machine the Python installation that home points to may also be missing. An environment is recreated from requirements.txt.

Version history

VersionChangeWhat it means for code
3.11The reference point of the lesson. A fresh environment has pip and setuptools.
3.12venv no longer installs setuptools: a fresh environment has just pip. A build that implicitly relied on setuptools has to declare it explicitly.
3.13The python -m venv command creates a .gitignore with an asterisk in the environment folder — git does not see the environment.
3.14The environment's sys.prefix is set by path initialization, not by the site module: in the run env/bin/python -S on 3.14.7 sees the environment (prefix != base_prefix is True), on 3.11–3.13 it does not. Without -S everything is as before. Also bin/ has one more name for the interpreter, 𝜋thon, only when the file system encoding is UTF-8; it does not affect how the environment works.
3.15Released on 9 October 2026. The lesson was not run on it: the runs are CPython 3.11–3.14, checked on 14.10.2026.

Practice

Two problems. Answer first, then check against the real output: in both, the correct answer is taken from a run of the script rather than assigned.

Practice · predict the output

The environment env has been created but not activated. This code is run with env/bin/python. What will it print?
import sys
print(sys.prefix == sys.base_prefix)
print(sys.prefix.endswith("env"))

Practice · estimate

How many packages will pip list show in a just-created environment on CPython 3.13?
package

Check yourself

Question 1 of 4

A cron script runs as /app/env/bin/python job.py; activate is not called. Which packages will it see?

How it was measured

The observations of this lesson come from these scripts. Each one opens right from here — together with the record of its run: what it ran on and what came out.

Sources & further reading

7 SOURCES

  1. The site module — how the interpreter finds an environmentOfficial documentation. The docstring of the site module: «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)». Checked by calling site.__doc__ on 3.11.15, 3.12.3, 3.13.7 and 3.14.7.https://docs.python.org/3.13/library/site.html
  2. Lib/venv/__init__.py — what venv puts into an environmentCPython source code. On 3.13.7: lines 444–446 are the _setup_pip method, which fills a fresh environment by calling ensurepip; line 17 is CORE_VENV_DEPS, the package list for the --upgrade-deps flag (the upgrade_dependencies method, lines 585–590; on 3.11.15 — ('pip', 'setuptools'), since 3.12.3 — ('pip',)). Lines 279–290 are the create_git_ignore_file method: «The contents of the file cause the entire environment directory to be ignored by git»; the python -m venv command line turns it on by default (line 662, default=frozenset(['git'])), while EnvBuilder and venv.create default to scm_ignore_files=frozenset() (lines 51 and 595). Lines 185–190 create lib64 as a symbolic link to lib on 64-bit POSIX systems other than macOS. Lines 318–345: on Windows, instead of a link, the launcher venvlauncher.exe is copied into the environment as python.exe; lines 623–626: on Windows the command line turns symbolic links off by default. On 3.14.7, lines 318–319 are suffixes.append('𝜋thon'), only when sys.version_info[:2] == (3, 14) and the file system encoding is UTF-8.https://github.com/python/cpython/blob/v3.13.7/Lib/venv/__init__.py
  3. Lib/ensurepip/__init__.py — what goes into a fresh environmentCPython source code. On 3.11.15, lines 12–14: _PACKAGE_NAMES = ('setuptools', 'pip'), _SETUPTOOLS_VERSION = "79.0.1", _PIP_VERSION = "24.0". On 3.12.3, lines 40–41: _PACKAGE_NAMES = ('pip',), _PIP_VERSION = "24.0". On 3.13.7 and 3.14.7 there is no _PACKAGE_NAMES or _SETUPTOOLS_VERSION, only pip is installed; _PIP_VERSION (line 13) is "25.2" and "26.2.1". Checked against the installed files of these versions; 3.11.15 and 3.12.3 are Ubuntu builds, the line numbers are given for them and may differ in the CPython source.https://github.com/python/cpython/blob/v3.12.3/Lib/ensurepip/__init__.py
  4. Lib/venv/scripts/common/activate — what activation doesCPython source code. The activate template on 3.13.7: lines 57–58 — export VIRTUAL_ENV_PROMPT; 60–66 — unsets PYTHONHOME if it is set; 69–71 — adds the environment's name to the start of PS1 unless VIRTUAL_ENV_DISABLE_PROMPT is set; 76 — hash -r; lines 4–34 — the deactivate function, which undoes all of it.https://github.com/python/cpython/blob/v3.13.7/Lib/venv/scripts/common/activate
  5. The site module in 3.14 — path initialization sets the environment's prefixesOfficial documentation. The site documentation for 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». In the source: the venv() function in Lib/site.py on 3.13.7, line 628, is sys.prefix = sys.exec_prefix = site_prefix (in the Ubuntu builds of 3.11.15 and 3.12.3, lines 554 and 555); on 3.14.7, lines 634–637, it only warns if sys.prefix does not match the environment folder. The docstring of the site module (site.__doc__) on 3.14.7 is unchanged. The run is bench/venv/basics.py, section 8. Checked on 14.10.2026.https://docs.python.org/3.14/library/site.html
  6. pip/_internal/commands/freeze.py — what pip freeze skipsSource code. In pip 24.0, which ensurepip installs on 3.11.15: line 27 is the command description «Output installed packages in requirements format»; lines 12–20 — _should_suppress_build_backends() returns sys.version_info < (3, 12), and then setuptools, distribute and wheel are added to the skipped pip; since Python 3.12 only pip is skipped. In pip 25.2 (3.13.7) and 26.2.1 (3.14.7) the rule is the same, lines 11–19. The --all flag turns the skipping off. The pip documentation on the difference from a lock file: «pip freeze reports what is installed; it does not compute a lockfile or a solver result». Checked against the installed files and pip.pypa.io on 14.10.2026.https://pip.pypa.io/en/stable/cli/pip_freeze/
  7. venv — activation commands on different platformsOfficial documentation. The table in the venv 3.14 documentation: 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. And: «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». The lesson's run is bash on Linux only. Checked on 14.10.2026.https://docs.python.org/3.14/library/venv.html