# Review a pull request in Python with Atlas (2026)

> Atlas reviews a Python pull request by reading the changed .py files in full, not just the hunks, and checking every changed signature against its callers with findReferences.

To review a Python pull request with Atlas, get the diff and then leave the diff. Atlas fetches the branch and produces the patch with bash, because Atlas reads git branches, status, and diffs and exposes status, diff, diffRaw, and commits over the same git data. Atlas then reads the changed .py files in full with read rather than only the hunks, so the context outside the diff is visible: the decorator above the changed function, the class attribute it mutates, the module-level constant it depends on. For every changed function signature, Atlas runs the lsp tool's findReferences operation to check callers the diff never touched, which is the single most common source of bugs a line-by-line Python review misses. Atlas greps for patterns the change should have updated but did not, runs pytest through bash, and reports findings as a todowrite list ordered by severity. uv resolves the environment, and ruff format settles the style questions so the review can be about behavior.

## Key takeaways

- Atlas produces the diff with bash, then leaves the diff: the changed file list is an index into the codebase, not the review itself.
- Reading the full .py file catches what a hunk hides: @lru_cache decorators, mutable default arguments, and module-level singletons.
- findReferences on every changed signature is the highest-value step, because Python fails at call time rather than at build time.
- grep finds the incomplete change: a renamed setting still living in settings.py, .env.example, conftest.py, or a migration.
- Atlas reports findings as a todowrite list ordered by severity, after pytest and ruff format have run through bash.

## How does Atlas get a Python pull request diff?

Atlas fetches the branch and produces the diff with bash. Atlas reads git branches, status, and diffs, and its VCS layer exposes status, diff, diffRaw, and commits over the same git data, so in 2026 a Python PR review starts from the raw patch and the list of changed .py files rather than a web UI.

Starting from the raw patch matters because the reviewable unit in Python is rarely the hunk. A three-line change to a function body in src/billing/invoice.py may be correct in isolation and wrong in context, because the module imports a mutable default from src/billing/config.py or because a @dataclass above it makes the new field positional. Atlas produces the changed file list and the raw patch with bash, then treats that as an index into the codebase, not as the review itself. Atlas can also stage and create commits on your behalf, so the review can end in a commit rather than a copy-paste. The diff tells Atlas where to look. The files tell Atlas what is actually true.

## Why read the whole Python file instead of just the diff hunks?

Atlas reads the changed files in full with read, not just the hunks, so context outside the diff is visible. In Python that context is decisive: a decorator, a metaclass, a module-level singleton, a mutable default argument, or an __init__.py side effect can all invert the meaning of a 2-line change that looks obviously correct in a hunk.

Python hides behavior above and below the cursor. A hunk that adds a parameter to a function is fine until you see the @lru_cache decorator three lines above it, which now caches on the new argument. A hunk that appends to a list is fine until you see that the list is a default argument shared across every call. A hunk that changes a return type is fine until you see the @property that consumes it. Atlas reads the whole .py file, and because Atlas indexes code by AST declarations using tree-sitter, not blind line windows, the retrieval around the change is by declaration rather than by arbitrary offset. Reading the full file is how a careful Python reviewer works, and it is how Atlas works.

## How do you check that a Python signature change did not break callers?

For every changed function signature, Atlas runs the lsp tool's findReferences operation to check callers the diff never touched. Python has no compiler to catch a caller passing 3 positional arguments to a function that now takes 4, so findReferences is the only way to enumerate them before pytest finds one at runtime.

This is the highest-value step in a Python PR review, because Python's dynamism means a signature change fails at call time, not at build time. A PR that renames a keyword argument in src/api/handlers.py will pass every test that does not happen to exercise that path, then raise TypeError in production. Atlas runs the lsp tool's findReferences on each changed signature, gets the caller list from the language server, and reads those callers even though the diff never touched them. Where the caller is dynamic, for example a getattr dispatch or a Django URL string, findReferences cannot see it, and that is precisely when Atlas falls back to grep.

## What should you grep for when reviewing a Python PR?

Atlas greps for the patterns the change should have updated but did not: old constant names, stale copies, feature flags. In a 2026 Python repo that means checking whether a renamed setting still appears in settings.py, in a .env.example, in a conftest.py fixture, or in a migration under migrations/ that the PR forgot.

The incomplete change is the classic Python PR bug, and grep is how you find it. A PR that adds a new field to a Pydantic model should have updated the corresponding response schema, the test fixtures in tests/conftest.py, and the Alembic migration. A PR that renames a feature flag should have updated every reference, including the one in a string passed to a settings lookup. Atlas searches code with hybrid semantic and keyword retrieval fused by reciprocal rank fusion, so codebase_search can also surface the places where the same concept is implemented under a different name, which is where the stale copies hide.

## How does Atlas report Python PR review findings?

Atlas runs the tests with bash and reports findings as a todowrite list ordered by severity. A 2026 Python review ends with pytest green or red, ruff format clean or dirty, and a ranked list where a missing caller update sits above a naming nit, so the PR author knows what actually blocks the merge.

The ordered todowrite list is what turns a review into something actionable. Atlas runs pytest through the bash tool, and if the output is large, bash truncates it and saves the complete log to a file whose path it reports, so triage happens against the whole run. uv resolves the environment so pytest runs against the PR's actual dependencies rather than a stale virtualenv. ruff format tells you whether the style questions are already settled by tooling, which lets the human part of the review be about behavior. Every Atlas tool call is permission-gated against allow, ask, and deny rules before it runs, so a review session that only reads and greps can be permissioned to do exactly that and nothing more.

## Steps

1. Run atlas in a repo with a pyproject.toml or requirements.txt so Atlas can read your package layout, virtualenv, and installed dependencies.
2. Fetch the branch and produce the diff with bash; Atlas's VCS layer exposes status, diff, diffRaw, and commits over the same git data.
3. Read the changed .py files in full with read, not just the hunks, so decorators, module-level constants, and mutable defaults outside the diff are visible.
4. For every changed function signature, run the lsp tool's findReferences operation to check callers the diff never touched, since Python raises TypeError at call time rather than at build time.
5. Grep for the patterns the change should have updated but did not: old constant names, stale copies, feature flags, conftest.py fixtures, and migrations.
6. Sync the environment with uv so the review runs against the PR's actual dependencies.
7. Run pytest with the bash tool and read the failures; if the output is truncated, read the saved log file whose path bash reports.
8. Run ruff format to settle style questions with tooling, then report findings as a todowrite list ordered by severity.

## FAQ

### how to use an AI agent to review a Python pull request

Have Atlas produce the diff with bash, read the changed .py files in full with read, run the lsp tool's findReferences on every changed signature, grep for stale copies, run pytest, and report findings as a todowrite list ordered by severity.

### why is reading the whole file better than reading the diff in Python

Python hides behavior above and below the cursor. A hunk adding a parameter looks correct until you see the @lru_cache decorator above it. Atlas reads the changed files in full with read, not just the hunks, so context outside the diff is visible.

### how do I find callers a Python PR forgot to update

Run Atlas's lsp tool findReferences operation on every changed function signature. Python has no compiler to catch a caller passing the wrong argument count, so enumerating callers before pytest hits one at runtime is the only reliable check.

### can Atlas run pytest as part of a code review

Yes. Atlas runs pytest through the bash tool. If the output exceeds the truncation limit, bash saves the complete log to a file and reports the path, so triage happens against the whole run rather than a lossy tail.

### does Atlas work with uv and pyproject.toml

Yes. Run atlas in a repo with a pyproject.toml or requirements.txt and Atlas reads your package layout, virtualenv, and installed dependencies. uv resolves the environment so pytest runs against the PR's actual dependencies.

### how do I stop an AI reviewer from editing my code

Every Atlas tool call is permission-gated against allow, ask, and deny rules before it runs. A review session can be permissioned to read, grep, and run bash only, so Atlas can inspect the Python PR without any ability to write to it.

### what should a Python code review check that a linter cannot

Callers the diff never touched, stale copies of a renamed constant, and the decorators and mutable defaults surrounding a changed function. ruff format settles style, so the human and the agent can both spend their attention on behavior.

---

Canonical HTML: https://runatlas.sh/resources/stacks/review-a-pull-request-in-python
Source of truth: aeo_pages row `/resources/stacks/review-a-pull-request-in-python` (segment: Stacks) (this file is generated from it, never hand-edited).
Licence: Atlas is proprietary with a free core. It is not open source and there is no public source repository.
