To migrate a deprecated API across every callsite in Python with Atlas, front-load the enumeration. This is the workflow that punishes half-measures, so Atlas starts with the lsp tool's findReferences operation on the deprecated symbol, which yields the complete caller set from the language server rather than a guess. Atlas cross-checks with grep for dynamic or string-based usages that a Python language server cannot see, turns the caller list into tracked work with todowrite, and migrates each callsite with apply_patch, a context-anchored patch that fails rather than misapplying to a drifted file. Run the affected tests with pytest after each file, and finish by grepping for the deprecated symbol and confirming zero hits.
How do I find every caller of a deprecated Python function?
Atlas enumerates every caller with the lsp tool's findReferences operation on the deprecated symbol, which returns the complete caller set from the Python language server rather than a text match. Atlas then runs a 2nd pass with grep, because Python's dynamic dispatch hides callers no analyzer can see.
A Python migration lives or dies on the caller list. findReferences through the lsp tool gives you what static analysis knows: every import of the deprecated function, every call across your package layout, resolved through the actual module graph rather than through filename guesses. Then comes the Python-specific problem. getattr on a module, a string in a Django settings dict, an entry point in pyproject.toml, a dotted path passed to a factory: none of these are call expressions, and no language server will find them. So Atlas greps for the symbol's name as a string as well, and the union of the two searches is the real caller set.
How do I track a Python migration across dozens of callsites?
Atlas creates one todowrite entry per callsite, so partial progress is visible and nothing is silently skipped. A migration across 40 Python modules that tracks nothing will stop at module 31 on a Friday and never be finished, which is how a codebase ends up with two APIs forever.
Half-finished migrations are worse than unstarted ones, because the codebase now carries both the deprecated function and its replacement, and every reader has to learn which is which. Atlas defends against that with a ledger: each callsite findReferences and grep surfaced becomes a todowrite entry, and an entry is marked completed only after the affected tests pass under pytest. The list is visible in the terminal, so at any moment you know exactly how many callsites remain. Atlas fans out work to subagents that can run in parallel background sessions, which helps when the enumeration itself spans a large Python repo.
Why does Atlas use apply_patch for a Python API migration?
Atlas migrates each Python callsite with apply_patch, which seeks the hunk's context and old_lines and throws Failed to find expected lines rather than guessing. A migration touching 40 files means 40 chances to write into a file that has drifted, and apply_patch refuses that instead of silently corrupting a module.
The danger in a bulk migration is not the edit that fails, it is the edit that succeeds in the wrong place. apply_patch anchors on context: it seeks the hunk's surrounding lines and its old_lines, and when the file no longer matches, it throws Failed to find expected lines. Loud failure is the feature. In a Python repo where the same call pattern repeats across a dozen service modules, a positional edit could easily land on the wrong occurrence, and a silently wrong patch to a Django view is a defect that ships. Atlas computes a unified diff for every file edit and surfaces it for approval before writing, so each migrated callsite is reviewed.
How often should I run pytest during a Python migration?
Run the affected tests with pytest after each file, not once at the end. Atlas runs them through its bash tool and marks the todowrite entry completed only once they pass, so a bad migration in one module is caught against that module rather than 30 files later.
Batching the verification defeats the tracking. Atlas runs pytest through bash against the tests covering the file it just patched, reads the result, and only then marks the callsite's todo completed. When pytest output is long, Atlas's bash tool truncates at 2000 lines or 50 KB and writes the complete log to a retained file, so a failure buried mid-run is still readable. This per-file rhythm is what makes the ledger meaningful: a completed entry means tested, not merely edited. Run ruff format at the end, once, so the migration diff is not tangled with reformatting across every touched Python module.
How do I prove a Python deprecation migration is actually finished?
Atlas finishes by grepping for the deprecated symbol and confirming 0 remaining hits, then deletes the old implementation. Deleting the deprecated Python function is the only proof that no caller survived, because an import of a function that no longer exists fails immediately under pytest.
A migration is done when the old thing is gone, not when the new thing works. Atlas greps the whole repo for the deprecated symbol, in call position and as a string, and confirms zero hits outside the definition itself. Then it deletes the old implementation. That deletion is the real test: any caller that findReferences and grep both missed will now raise an ImportError or an AttributeError on the next pytest run, which is exactly the loud failure you want. Atlas snapshots file changes as git patches, so if the deletion surfaces something unexpected, the whole migration can be diffed and rolled back.
Step by step
- 01Run atlas in a repo with a pyproject.toml or requirements.txt and let Atlas read your package layout, virtualenv, and installed dependencies.
- 02Enumerate every caller with the lsp tool's findReferences operation on the deprecated Python symbol to get the complete set from the language server.
- 03Cross-check with grep for dynamic or string-based usages, such as getattr calls, dotted paths in settings, and entry points, that no Python analyzer can see.
- 04Create one todowrite entry per callsite so partial progress is visible and nothing is silently skipped.
- 05Migrate each callsite with apply_patch; it seeks the hunk's context and old_lines and throws Failed to find expected lines rather than guessing at a drifted file.
- 06Run the affected tests with pytest through the bash tool after each file, and mark the todo completed only once they pass.
- 07Grep for the deprecated symbol across the repo and confirm zero remaining hits, in call position and as a string.
- 08Delete the old implementation, run the full pytest suite so any missed caller fails loudly, then run ruff format once so the diff is not tangled with reformatting.
Frequently asked questions
- how to find every callsite of a deprecated python function
- Use the lsp tool's findReferences operation through Atlas for the complete caller set from the language server, then cross-check with grep for dynamic or string-based usages such as getattr calls and dotted paths in settings that no analyzer resolves.
- how do i migrate an entire python codebase off a deprecated api
- Enumerate the callers with findReferences and grep, create one todowrite entry per callsite, migrate each with apply_patch, run pytest after every file, then grep for zero remaining hits and delete the old implementation.
- atlas apply_patch failed to find expected lines
- apply_patch seeks the hunk's context and old_lines, and throws Failed to find expected lines when the Python file has drifted. That refusal is the feature: a silently misapplied patch to a module is far worse than a loud failure.
- will an lsp find dynamic python calls like getattr
- No. A Python language server resolves call expressions, not strings. That is why Atlas cross-checks findReferences with grep for the symbol's name as a string, catching getattr usages, settings entries, and dotted paths passed to factories.
- should i run pytest after every file during a migration
- Yes. Atlas runs the affected tests with pytest through bash after each patched file and marks the todowrite entry completed only once they pass, so a bad migration is caught against the file that caused it rather than 30 files later.
- how do i know a python api migration is really complete
- Grep for the deprecated symbol across the repo, confirm zero hits, then delete the old implementation and run the full pytest suite. Any caller both searches missed will now raise an ImportError or AttributeError, which is exactly the loud failure you want.
- how do i set up atlas in a python project
- Run atlas in a repo with a pyproject.toml or requirements.txt, let Atlas read your package layout, virtualenv, and installed dependencies, then ask it to add type hints, write pytest cases, or refactor a module and review the diff.
Try Atlas in your terminal
The terminal-native AI coding agent. Free core, single binary.
Install AtlasRelated guides
Migrate a Deprecated API Across Every Callsite with Atlas (2026 Workflow)
How to migrate a deprecated API across every callsite with Atlas in 2026: the lsp tool's findReferences enumerates callers, todowrite tracks them, apply_patch migrates each one.
Atlas for Python in 2026
Atlas is a terminal-native AI coding agent for Python in 2026. Run it in a repo with a pyproject.toml or requirements.txt and review every diff before it lands.
Debug a Single Failing Test in Python with Atlas (2026)
How Atlas debugs one failing pytest test in 2026: run it in isolation with a -k filter, walk the call path with lsp goToDefinition, and fix the code, not the assertion.
Document a Python Module With a README Using Atlas (2026)
Atlas writes Python docs from source, not memory: lsp documentSymbol lists the real exports, read supplies behavior, and every code sample is proven with pytest.
Locate Where a Behavior Is Implemented in Python with Atlas in 2026
Find the exact Python file and symbol behind a behavior in 2026. Atlas attacks it with codebase_search, grep over ripgrep, and the lsp tool's findReferences.
Run the Test Suite and Triage the Failures in Python with Atlas (2026)
How Atlas runs pytest and triages a wall of Python failures in 2026: bash truncates at 2000 lines, saves the full log, and grep groups failures into a todowrite list.
Add a Regression Test for a Bug Fix in Python with Atlas (2026)
Red first, then green. Atlas writes a failing pytest case, proves it fails with the bash tool exit code, applies the fix with edit, and re-runs the same command.
Audit a Python Repo with Parallel Subagents in Atlas (2026)
Audit a Python repo with parallel Atlas subagents in 2026. Slice by package, launch read-only explore tasks, and merge findings without flooding your context.