venv / virtualenv / conda / uv venv + PYTHONPATH + editable installs
Без virtual environment всё установленное через pip install идёт в глобальный site-packages system Python. Один project требует pandas==1.5, другой — pandas==2.0. Конфликт. Решение — virtual environment: изолированный Python interpreter + изолированный site-packages, под каждый project отдельно.
В этом уроке:
- Зачем virtual environments — изоляция projects, reproducibility.
- Tool comparison —
venv(stdlib) /virtualenv(3rd-party) /conda/uv venv. PYTHONPATHsemantics —sys.pathmodule search (cross-link M03 урок 05).pip install -e .editable installs (PEP 660) — modern editable spec.site-packagesdirectory layout — где живут установленные packages.- Run-on-Your-Machine #2 —
python -m venv .venv+ activate + editable demo. - Pitfall 47 —
.venv/в.gitignore(иначе ~100 MB venv files в repo).
Зачем virtual environments
# Без venv — всё в global site-packages (требует sudo на system Python)
pip install pandas==1.5 # → /usr/lib/python3.12/site-packages/pandas/...
# С venv — изоляция; нет sudo
python -m venv .venv
source .venv/bin/activate
pip install pandas==1.5 # → .venv/lib/python3.12/site-packages/pandas/...
Преимущества:
- Изоляция projects — два projects могут иметь разные pandas versions без конфликта.
- Reproducibility —
requirements.txt frozen+.venvrecreated 1:1 across machines. - Нет sudo — venv пишет в user-owned directory; не трогает system Python.
- Easy cleanup —
rm -rf .venvудаляет всё; system Python не пострадает. - CI/CD friendly — каждый CI run создаёт fresh venv; deterministic.
Mental model: venv — это clone Python interpreter + clean site-packages, под root directory project. Activation — temporary modification PATH env variable: .venv/bin ставится впереди.
Tool comparison
| Tool | Provenance | Когда выбирать |
|---|---|---|
venv | stdlib since 3.3 (PEP 405) | Default new projects — всегда доступен, zero install |
virtualenv | 3rd-party (older, pre-3.3 era) | Legacy projects + поддержка Python <3.3; чуть быстрее venv |
conda | Anaconda — non-PyPI ecosystem | Data science с native binaries (R interop, MKL, CUDA toolkits); heavier |
uv venv | Astral — Rust-fast | Speed-critical workflows; pairs идеально с uv pip install |
venv (stdlib, PEP 405)
python -m venv .venv # creates .venv directory
source .venv/bin/activate # macOS / Linux
.venv\Scripts\activate.bat # Windows
which python # → .venv/bin/python (verifies activation)
deactivate # leaves venv
venv не ставит pip отдельно — copies из system Python. Если ваш system Python имеет pip, venv тоже имеет. Опционально python -m venv .venv --copies (вместо symlinks) для full copy interpreter.
virtualenv — older 3rd-party
pip install 'virtualenv>=20'
virtualenv .venv -p python3.12
Зрелый предшественник venv; activate scripts identical; немного быстрее create. Recommend только для legacy projects, где нужен Python <3.3.
conda — Anaconda non-PyPI
conda create -n myenv python=3.12 pandas
conda activate myenv
conda управляет non-Python dependencies (CUDA, MKL, system libraries) — преимущество для ML/scientific workloads. Trade-off: heavier (~500 MB Anaconda install); package resolution медленнее (conda historically); миксование conda install + pip install чревато (но рабочее).
uv venv — Astral Rust-fast
uv venv .venv # ~10× faster than python -m venv
source .venv/bin/activate
uv pip install pandas
Drop-in replacement; pairs с uv pip install natively. Recommend для speed-critical workflows + новых projects.
Cite docs.python.org/3/library/venv.html.
PYTHONPATH + sys.path semantics
Когда вы import foo, Python ищет foo в sys.path — порядок поиска:
import sys
print(sys.path)
# ['', # 1. Скрипт directory (или '' для interactive)
# '/path/to/PYTHONPATH/dir', # 2. PYTHONPATH env var dirs (если установлен)
# '/usr/lib/python3.12', # 3. Standard library
# '/usr/lib/python3.12/lib-dynload', # 4. C-extension libs
# '/path/to/.venv/lib/python3.12/site-packages'] # 5. site-packages (vendored / pip installed)
Cross-link M03 урок 05 (modules + imports): мы там разобрали sys.path — Python finder iterates список и берёт первый match. PYTHONPATH расширяет этот список — добавляет ваши custom directories.
# Без PYTHONPATH:
python -c "from mylib import x" # ImportError
# С PYTHONPATH:
PYTHONPATH=/path/to/mylib python -c "from mylib import x" # ok
# Альтернатива — pip install -e . (см. ниже) — добавляет project в site-packages
Activation venv добавляет .venv/lib/python3.12/site-packages в sys.path неявно (через pyvenv.cfg + activation script env vars). Это главный механизм изоляции — global system site-packages отсутствует в sys.path после activation.
source .venv/bin/activate
python -c "import sys; print('\n'.join(sys.path))"
# /path/to/project
# /path/to/.venv/lib/python3.12/site-packages
# (НЕТ /usr/lib/python3.12/site-packages — изолировано)
pip install -e . editable installs (PEP 660)
Editable install — special install mode для development. Stages a project в site-packages по reference (через .pth file или modern __editable__ hooks); changes к source видны immediately без reinstall.
cd myproject/ # имеет pyproject.toml
pip install -e . # editable install
После этого:
# Edit src/myproject/foo.py
def hello():
return "world"
# В Python REPL:
from myproject.foo import hello
hello() # "world"
# Теперь меняем src/myproject/foo.py:
def hello():
return "edited"
# Re-import (или перезапустить REPL):
from importlib import reload
import myproject.foo
reload(myproject.foo)
myproject.foo.hello() # "edited" — БЕЗ pip install
До PEP 660 editable installs работали только через setuptools.develop — python setup.py develop (legacy). PEP 660 (2021) определил standard PEP 517 entry-point build_editable — теперь любой PEP 517 backend может реализовать editable installs (hatchling, poetry-core, pdm, flit-core все поддерживают).
Use cases:
- Library development — изменяешь source, тестируешь в REPL без
pip installкаждый раз. - Monorepo — несколько packages, каждый editable-installed — изменения propagate автоматически.
- CI testing local —
pip install -e .[dev]ставит project + dev-deps; testing reflects текущий state.
Cite peps.python.org/pep-0660.
site-packages directory layout
После pip install httpx pydantic:
.venv/
├── bin/
│ ├── python -> /usr/bin/python3.12 # symlink к system Python
│ ├── pip # venv-local pip
│ └── activate # source-able shell script
├── include/
│ └── python3.12/ # для C-extension headers
├── lib/
│ └── python3.12/
│ └── site-packages/ # ← всё установленное живёт здесь
│ ├── httpx/
│ │ ├── __init__.py
│ │ └── _client.py
│ ├── httpx-0.25.2.dist-info/ # metadata (version, deps, RECORD)
│ ├── pydantic/
│ ├── pydantic-2.5.0.dist-info/
│ └── _virtualenv.py
└── pyvenv.cfg # venv config (Python version, base prefix)
Windows variant: .venv\Scripts\python.exe + .venv\Lib\site-packages\.
.dist-info directory содержит METADATA (PEP 621 metadata уровень), RECORD (file manifest + hashes), WHEEL (build metadata) — это install artifact от вашего wheel/sdist.
Run-on-Your-Machine #2 —
venvcreate + activation + editableШаг 1. Создайте virtual environment в текущем directory:
python -m venv .venv ls .venv/ # bin/ include/ lib/ pyvenv.cfgШаг 2. Активируйте + verify:
source .venv/bin/activate # macOS / Linux # .venv\Scripts\activate.bat # Windows alternative which python # → /your/path/.venv/bin/python python -c "import sys; print(sys.prefix)" # → /your/path/.venv (vs /usr)Шаг 3. Editable install существующего project (нужен
pyproject.tomlс[project]table — см. урок 01):# Минимальный pyproject.toml (cd в project root): # [build-system] # requires = ["hatchling"] # build-backend = "hatchling.build" # [project] # name = "demo" # version = "0.1.0" # requires-python = ">=3.11" pip install -e . # Successfully installed demo-0.1.0Шаг 4. Cleanup:
deactivate rm -rf .venvPitfall 47: добавьте
.venv/в.gitignoreсразу при создании. venv ~100 MB (зависит от установленных deps); commit’ить в git — это disaster для repo size + lock-file conflicts. Standard.gitignoretemplate:.venv/ __pycache__/ *.egg-info/ dist/ build/ .pytest_cache/Что нельзя в browser? Pyodide WASM не имеет subprocess access —
python -m venv .venvтребует filesystem write + spawning subprocess; невозможно. Поэтому venv mechanics — host-only.
Run-on-Your-Machine — ruff linter + formatter
После создания venv типичный workflow — установить tooling для качества кода. С 2024+ дефакто-стандарт — ruff (Astral, Rust) — single tool который заменяет связку flake8 + pylint + pycodestyle + pyflakes + pyupgrade + isort + black. Производительность — 10–100× быстрее Python-equivalents (реальные replays — на средних codebase с 10K LOC ruff завершает full lint за <1 сек, vs 30+ сек для pylint/flake8 chain).
# Установка через pip (внутри активированного venv) или uv tool (global isolated)
pip install 'ruff>=0.6'
# или
uv tool install ruff # standalone binary в ~/.local/share/uv/tools/
ruff --version # → ruff 0.6.x
Two main commands:
# Linting — finds bugs, anti-patterns, style issues
ruff check src/ # lint все .py в src/
ruff check --fix src/ # auto-fix для safe rules (e.g., unused imports)
# Formatting — replaces black; PEP 8 + opinionated formatting
ruff format src/ # форматирует in-place
ruff format --check src/ # check-only (CI mode — exit 1 если нужны изменения)
Конфигурация в pyproject.toml (single source of truth — рядом с [project] table из урока 01):
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
# Включить набор правил (E + W — pycodestyle, F — pyflakes, I — isort, UP — pyupgrade, B — bugbear, SIM — simplify)
select = ["E", "W", "F", "I", "UP", "B", "SIM"]
ignore = ["E501"] # line too long — handled formatter'ом
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true # форматировать code blocks в docstrings
Что заменяет ruff:
| Tool | Заменено в ruff | Notes |
|---|---|---|
flake8 | YES (rules E, W, F) | Полный pycodestyle + pyflakes coverage |
pylint | Частично (rules PL*) | ~70% coverage — некоторые advanced semantic checks остаются за pylint |
pycodestyle | YES (rules E, W) | PEP 8 violations |
pyflakes | YES (rules F) | Unused imports, undefined names |
pyupgrade | YES (rules UP) | Modernize syntax (f-strings vs .format(), list[int] vs List[int]) |
isort | YES (rule I) | Import sorting + grouping |
black | YES (ruff format) | Drop-in replacement; 99% совпадение с black output |
bandit | Частично (rules S*) | Security checks |
Performance — Rust binary + parallel execution + AST cache:
# На pandas codebase (~1500 .py files, ~200K LOC):
time pylint pandas/ # ~120 секунд
time flake8 pandas/ # ~25 секунд
time ruff check pandas/ # ~0.4 секунды
100–300× speedup на больших codebase — это ощутимая разница в CI run time + developer feedback loop.
CI integration (рядом с pytest из M08):
# В CI script:
ruff check --output-format=github src/ # GitHub Actions annotations
ruff format --check src/ # exit 1 если форматирование некорректно
pytest tests/ # unit tests из M08
Pitfall: ruff vs black config compatibility. ruff format aims на 99% black-compatible output; differences в edge cases (magic trailing comma, slice formatting). Если переходите с black на ruff format в существующем codebase — first run может произвести diff на ~5–10% files. Solution — single formatting commit baseline, потом ruff format в CI блокирует regressions. Cite Ruff Formatter Differences from Black.
Что нельзя в browser? ruff — native Rust binary; Pyodide WASM не запускает binaries. Linting feedback в challenges ограничен Wave 0 lints (forbidden imports / forbidden functions); full ruff — host-only Run-on-Your-Machine.
Cite docs.astral.sh/ruff + Ruff GitHub.
Cross-course → Spark Airflow orchestration
Airflow tasks running PySpark jobs обычно изолируются venv-per-task для dependency conflicts (один DAG требует pandas 1.5, другой — pandas 2.0). PythonVirtualenvOperator — Airflow-native механизм:
# Airflow DAG:
PythonVirtualenvOperator(
task_id="ml_inference",
requirements=["scikit-learn==1.3", "pandas==1.5"],
python_callable=run_inference,
)
Cross-course: Spark course Phase 12 — production operations — orchestrating Spark jobs from Airflow + Python virtualenv operators + dependency mgmt patterns. Same idea: Python venv mechanics extend в distributed orchestration; per-task venvs нивелируют version conflicts.
Что в следующем уроке
Урок 04 — dependency resolution. Semver concept (MAJOR.MINOR.PATCH); PEP 440 version specifiers (~=, >=, <, ==, !=); lock files (poetry.lock / uv.lock / requirements.txt frozen); PEP 735 [dependency-groups] table (modern dev-deps grouping). Cross-course → Spark cicd-lifecycle + Airflow Python deps mgmt.