Python virtual environments: Inside .venv (Anatomy)
Python virtual environments (python -m venv) are not magic: they are a specific directory layout plus pyvenv.cfg that controls where Python imports and where pip installs. .venv/. This guide maps each folder and file (Scripts/bin, site-packages, .dist-info, pyvenv.cfg) to the behavior it drives, so you can debug environment issues by inspection. A typical environment looks like this: .venv/ ├── pyvenv.cfg ├── bin/ ├── include/ └── lib/ └── python3.12/ └── site-packages/ pyvenv.cfg: declares the environment and base Python. bin/: executables and scripts like python and pip. include/: headers for compiling native extensions. site-packages/: where code installed by pip lives. The logic is the same, but paths differ: .venv/ ├── pyvenv.cfg ├── Scripts/ └── Lib/ └── site-packages/ Key difference: Scripts/ is the Windows equivalent of bin/. Real example: home = C:\Python312 include-system-site-packages = false version = 3.12.4 What matters most: home points to the base interpreter include-system-site-packages controls isolation If set to true, your environment can see global packages. Useful in edge cases, risky by default. Inside the environment, pip is not magic. It is a script that invokes the environment Python. That is why this pattern is so safe: python -m pip install requests Instead of trusting whichever pip is in PATH, you force interpreter-installer consistency. When you install a library, you usually get two things: The package itself (importable source) Its .dist-info directory (metadata) Conceptual example: site-packages/ ├── requests/ └── requests-2.32.0.dist-info/ Inside .dist-info you will find files like: METADATA: version, dependencies, authors RECORD: list of installed files INSTALLER: who installed it (for example, pip) When uninstall fails, RECORD often explains why. from importlib.metadata import version, distributions print("requests:", version("requests")) for dist in distributions(): print(dist.metadata["Name"], dist.version) This technique is gold for quick audits of Python environments. A virtual environment is not portable across different systems. Reasons: absolute paths in scripts platform-specific compiled binaries architecture and ABI differences Practical rule: never copy .venv between machines. Recreate it from pyproject.toml/lock file or requirements. When an environment “breaks,” check this first: Does pyvenv.cfg exist? Do python and pip point to the same environment? Does site-packages contain the expected package? Are there version conflicts in .dist-info? This checklist solves a large percentage of import and install issues. Deleting or editing pyvenv.cfg manually, then wondering why the environment no longer behaves like a venv. Running pip/python from different locations (Scripts/bin vs system PATH), so installs go to one interpreter and your code runs on another. Trying to “fix” installs by deleting random folders in site-packages/ (especially .dist-info/), which can leave the environment inconsistent. Moving or copying .venv/ between machines/OSes and expecting it to remain valid. Across many repos, old .venv/ directories pile up and you stop knowing what is safe to delete. KillPy can scan for environments and list likely-orphaned ones so you can clean up after verifying they are unused. Understanding the anatomy of Python virtual environments changes how you debug: you know where to look you understand what pip is doing you detect inconsistencies before production The natural next step is pyproject.toml: how to declare modern dependencies and build reproducible projects. What is the weirdest venv/virtualenv symptom you have had to debug (wrong pip, broken imports, uninstall failures)? Share it in the comments with your OS, and I will point you to the exact file(s) to inspect. If you have not read the fundamentals yet: → https://dev.to/tlaloces/python-venv-explained-stop-breaking-dependencies-4k9k In the next step, we move from inspecting environments to defining them: → pyproject.toml: Modern Python Dependency Management (coming next)
