AI SWE Prep
0/9in Python Foundations
Python Foundations

Environments, Packaging & Tooling

Virtual environments, dependency pinning, and the modern toolchain (uv, ruff, pytest) that professional Python rests on.

~12 minLesson 3 of 60
Your progressNot started

Python’s greatest strength — a massive ecosystem of packages — is also its most common source of pain. Two projects on one machine will want conflicting versions of the same library, and “works on my machine” is almost always an environment problem. Professional Python starts with getting this under control.

Virtual environments: isolation by default

A virtual environment is an isolated directory with its own Python and its own installed packages. One per project. Nothing leaks between projects, and nothing pollutes your system Python.

python -m venv .venv          # create
source .venv/bin/activate      # activate (macOS/Linux)
pip install requests           # installs only into this env

The moment you install packages globally with pip, you’ve made a future mistake. Treat “create a venv” as the reflexive first step of any new project.

Pinning: reproducible installs

A modern Python toolchain
  1. 1
    uv / venvisolated environment
  2. 2
    Lockfilepin exact versions
  3. 3
    Rufflint + format
  4. 4
    mypystatic type check
  5. 5
    pytesttests in CI
Each tool is a gate; together they make installs and quality reproducible.

“It worked last week” breaks when a dependency releases a new version. You prevent this by pinning exact versions so every install is reproducible.

The old-school way is a requirements.txt with exact versions. The modern standard is pyproject.toml, which declares your dependencies, and a lock file that records the exact resolved versions (including transitive dependencies) for byte-for-byte reproducible installs.

The modern toolchain

The Python tooling landscape consolidated dramatically over the last few years. The current professional defaults:

  • uv — an extremely fast, all-in-one replacement for pip, virtualenv, and dependency resolution. uv venv and uv pip install are drop-in and dramatically faster; uv also manages Python versions and lock files.
  • Ruff — a linter and formatter that replaces the old stack of flake8, isort, and much of Black, running in milliseconds.
  • pytest — the de-facto testing framework; plain functions with assert, no boilerplate.
  • mypy / pyright — static type checkers (covered in the typing lesson).
uv venv && source .venv/bin/activate
uv pip install -r requirements.txt
ruff check . && ruff format .
pytest

A sane project layout

myproject/
├── pyproject.toml      # metadata + dependencies
├── src/
│   └── myproject/      # your package
│       └── __init__.py
└── tests/
    └── test_core.py

The src/ layout prevents a classic bug where your tests accidentally import the local source folder instead of the installed package — a difference that hides packaging mistakes until production.

Why this matters for the role

Senior engineers are trusted to set up projects that other people can run. Reproducible environments, fast CI, and consistent formatting aren’t busywork — they’re what makes a team’s velocity survive past the first month. Getting the foundation right here pays compounding interest across everything that follows.

When to reach for this

Variations

Try it

These are workflow exercises. The code is mostly shell and config because the point is to make Python projects reproducible before you write feature code.

ExerciseEasy
  • venv
  • uv

Bootstrap an isolated project

You cloned a new repo and need to install dependencies without touching system Python. Show the modern uv path and the built-in fallback.

Approach. Put the environment in .venv, activate it for local work, and install from the project’s declared dependency file rather than ad-hoc globals.

Show solution
# Modern path
uv venv
source .venv/bin/activate
uv pip install -r requirements.txt

# Built-in fallback when uv is not available
python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt

Both paths isolate packages inside .venv. The exact install command may differ by project (uv sync, uv pip install -e ., or pip install -e .), but the principle does not: create the environment first, then install into it.

ExerciseMedium
  • pyproject
  • ruff
  • pytest

Wire ruff and pytest into pyproject.toml

Add minimal project configuration so contributors can run formatting, linting, and tests the same way locally and in CI.

Approach. Keep tool configuration in pyproject.toml so there is one obvious place to look. Then document the commands CI should run.

Show solution
[project]
name = "ai-prep-demo"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
  "httpx==0.27.2",
]

[dependency-groups]
dev = [
  "pytest==8.3.3",
  "ruff==0.6.9",
]

[tool.ruff]
line-length = 88

[tool.pytest.ini_options]
testpaths = ["tests"]
uv sync --dev
ruff check .
ruff format .
pytest

The useful habit is consistency: the command you run before opening a PR should match the command CI runs after you open it.

GotchaMedium
  • Pinning
  • Lock files

Spot the dependency-pinning gotcha

A service declares requests>=2 and has no lock file. It worked yesterday and fails today after a clean install. What went wrong, and what should an application repo do instead?

Approach. A broad lower bound lets new compatible-looking releases enter the environment at any time. Applications should commit a lock file or exact pins so installs are reproducible.

Show solution
[project]
name = "image-worker"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
  "requests==2.32.3",
]
uv lock
uv sync --locked
pytest

Exact top-level pins plus a committed lock file freeze the full dependency graph, including transitive dependencies. Upgrade deliberately in a separate change, run the tests, and make the dependency diff reviewable.

Your progressNot started