Half a Million Lines of Python and 25 Import Cycles: Dissecting Django with 67 AST Tools
We benchmarked all 67 prod-code AST tools against django/django: 527K lines of Python across 2,932 files, 25 circular check paths, 99.9% slicing reduction, and mathematical safety proofs in Python predicate inversion and parameter bundling.

On this page · 9 sections
- 1. Node Topology & Fast-Sync Ingestion
- 2. Architecture DAG: 112 Core Modules & 25 Circular Paths
- 3. Clone Harvester: Detecting Parameterized Duplication
- 4. Structural AST Search & Semantic Intent
- 5. Navigation & Transitive Program Slicing
- 6. The Python Refactoring Suite
- 7. Pre-Validation in In-Memory Shadow (prod-code validate)
- 8. Summary: 67-Tool Evaluation Matrix for Django
- Conclusion
django/django is the gold standard of Python web development. For two decades, it has anchored the Python ecosystem, powering everything from content platforms and massive SaaS APIs to financial engines with its batteries-included philosophy: an ORM with expressive relational queries, an extensible authentication subsystem, built-in migration tooling, and a middleware-driven request pipeline.
Underneath that web framework lies a sprawling Python codebase: 526,995 lines of Python across 2,932 source files, organized into dozens of internal packages (core, db, forms, http, contrib, urls).
Dynamic, dynamically typed languages like Python present distinct hurdles for static analysis and automated refactoring:
- Type inference engines (like
basedpyright) must resolve complex metaprogramming patterns, dynamically attached model managers, class decorators, and conditional runtime imports without choking. - Automated refactorings must handle Python’s lack of braces, indentation-sensitive blocks, and first-class function values without breaking runtime syntax.
- Naive search tools easily get lost in thousands of lines of dynamic dispatch and meta-classes.
To evaluate how our cluster-backed code intelligence architecture handles a flagship Python monolith with 0% local laptop CPU, we subjected django/django (commit 5a4511ad) to the standardized 67-tool evaluation protocol of prod-code on our 32-core Linux node (ram9).
Here is what 67 AST analyzers discovered inside Django.
1. Node Topology & Fast-Sync Ingestion
prod-code mirrors the local working tree to a remote Linux build node over low-latency LAN:
Local Checkout: /Users/alex09x/Documents/workspace/django-eval
Target Host: ram9 (192.168.2.143:9400 / AMD EPYC 32-core, 128 GB RAM)
LAN Latency: 654.88 µs RTT
Python Source Files: 2,932 (.py)
Total Lines of Python: 526,995
Python Engine: basedpyright-langserver (with stubs up to Python 3.15)
Ingestion Telemetry
- Differential Cold Sync: 2,932 Python source files hashed, synchronized, and hydrated into
basedpyrightin server memory. - Local Laptop Load: 0.0% CPU, zero fan spin, zero thermal throttling. All AST parsing, graph traversal, and semantic analysis remained on the remote cluster node.
2. Architecture DAG: 112 Core Modules & 25 Circular Paths
Running prod-code dependencies across django/core revealed the topological structure of Django’s internal machinery:
⚡ Architecture & Dependency Graph Report
Scope: modules | Nodes: 112 | Dependencies: 832 edges
Foundational Bedrock Modules (Afferent Coupling Cₐ)
Modules with the highest incoming dependencies (Cₐ) and zero outgoing instability (I = 0.00) form the structural foundation of Django:
django::core::cache::backends::base(Cₐ = 11, I = 0.00): Abstract interface for cache storage drivers.django::core::files::base(Cₐ = 11, I = 0.00): File wrappers and streaming chunk primitives.django::core::files::storage::base(Cₐ = 11, I = 0.00): The file storage abstraction.django::core::handlers::base(Cₐ = 11, I = 0.08): Core request/response middleware chain handler.django::core::management::base(Cₐ = 11, I = 0.00): The CLI command execution engine (BaseCommand).
Circular Import Topology
Python allows circular imports when imports occur inside functions or modules are lazily evaluated. prod-code dependencies detected 25 circular dependency paths within django::core::checks:
checks::async_checks -> checks::caches -> checks::async_checkschecks::caches -> checks::database -> checks::files -> checks::templates -> checks::translation -> checks::urls -> checks::cacheschecks::databaseemerged as the highest-instability coordinator in the core system (Cₑ = 111, I = 0.92).
3. Clone Harvester: Detecting Parameterized Duplication
Using AST-hash based clone harvesting via prod-code duplicates, we scanned django/core for duplicated logic:
Parameterized Clone Clusters (Type-2 Clones)
The analyzer surfaced over 150 Type-2 clone groups:
- Serializer Exception Handlers: Identical generator exception cascades across
serializers/pyyaml.py:75-80,serializers/jsonl.py:52-57, andserializers/json.py:75-80:def _handle_object(self, obj): try: yield from super()._handle_object(obj) except (GeneratorExit, DeserializationError): raise - Command Option Parsers: Identical
--no-inputargument definitions repeated across 5 management commands (makemigrations,migrate,squashmigrations,test,testserver). - Cache Storage Adapters: Symmetrical
get(self, key, default=None, version=None)method implementations acrossmemcached.pyandredis.py.
4. Structural AST Search & Semantic Intent
1. Semantic Intent Search (code_search)
Searching for conceptual intent across the entire codebase:
prod-code search "resolve request url to view callback"
The server scanned 44,985 declarations across 2,975 files in 204 milliseconds, ranking the exact execution entrypoints at the top:
- Hit 1:
django/views/generic/base.py:97(View.view) - Hit 5:
django/core/handlers/base.py:302(BaseHandler.resolve_request) - Hit 7:
django/urls/resolvers.py:424(URLPattern.__init__) - Hit 8:
django/middleware/csrf.py:416(CsrfViewMiddleware.process_view)
2. Structural AST Search (code_structural_search)
Searching for guard-clause patterns:
prod-code structural-search "if not \$cond: raise \$exc" --path django/core
In 99.70 milliseconds, it located 38 exact syntax matches across 21 files in django/core, binding $cond and $exc to their respective expressions (e.g., $cond = client.exists(key), $exc = ValueError in redis.py).
5. Navigation & Transitive Program Slicing
Transitive Program Slicing (code_slice)
To understand how Django resolves an incoming HTTP request into a view function, developers traditionally trace through multiple modules (base.py, resolvers.py, regex.py).
We executed a depth-2 transitive slice on BaseHandler.resolve_request:
prod-code slice django/core/handlers/base.py --line 302
In 0.12 seconds, prod-code slice extracted:
BaseHandler.resolve_request(seed, depth 0)get_resolver(depth 1)_get_cached_resolver(depth 2)URLResolver.resolve(depth 1)URLPatternclass &URLPattern.resolve(depth 2)URLResolver._extend_triedand_join_route(depth 2)
A 527,000-line repository was reduced down to a self-contained 160-line executable dependency chain (a 99.97% context reduction), revealing the exact call flow of Django’s URL routing engine.
Semantic Navigation
prod-code def: Resolveddjango.core.handlers.base::BaseHandlertodjango/core/handlers/base.py:21:7.prod-code hover: Displayedclass BaseHandler()signature in 15 ms.prod-code callers: Traced both callers ofresolve_request:_get_responseat line 176 (call site 183:57)_get_response_asyncat line 230 (call site 237:57)
prod-code outline: Produced complete symbol tree ofbase.py(1 class, 10 methods, 7 attributes, 54 locals).
6. The Python Refactoring Suite
We subjected prod-code’s polyglot refactoring engines to real-world Python transformations in django/utils/inspect.py.
1. invert-boolean: Negation with First-Class Value Guard
We tested inverting is_module_level_function into is_not_module_level_function:
prod-code invert-boolean is_module_level_function \
--to is_not_module_level_function \
--path django/utils/inspect.py
Results:
- Inverted all 3 return statements in the function body (
return False→return True,return True→return False). - In
django/tasks/backends/base.py, simplifiedif not is_module_level_function:intoif is_not_module_level_function:. - Rewrote 14 call sites in
tests/utils_tests/test_inspect.pywithnot inspect.is_not_module_level_function(...). - Mathematical Safety Refusal: Line 147 of
test_inspect.pypassedis_module_level_functionas a first-class value argument:self.assertIs(inspect.is_module_level_function(inspect.is_module_level_function), True)prod-code invert-booleandetected the value reference and refused the unsafe rewrite:not rewritten (1 reference(s) that are not a call — a function used as a value keeps its old meaning under its new name; nothing is written while any remains): tests/utils_tests/test_inspect.py:147:54 `is_module_level_function` used as a value
2. parameter-object: Bundling Parameters into a Class
We tested bundling (func, name) on func_supports_parameter into FuncParamSpec:
prod-code parameter-object func_supports_parameter \
--param func --param name --name FuncParamSpec \
--path django/utils/inspect.py
Results:
- Generated a new idiomatic Python class in
django/utils/inspect.py:class FuncParamSpec: """The parameters `func_supports_parameter` takes together.""" def __init__(self, func, name): self.func = func self.name = name - Rewrote declaration:
def func_supports_parameter(func_param_spec: FuncParamSpec):. - Updated body accesses:
func_param_spec.nameandfunc_param_spec.func. - Rewrote external call site in
django/db/models/query.py:81:func_supports_parameter(FuncParamSpec(func=from_db, name="fetch_mode")). - Added required import to
query.py:added FuncParamSpec to the import from inspect. - Rewrote test call site in
tests/utils_tests/test_inspect.py:111. - Verified cleanly:
the analyzer accepts the result: 0 errors.
3. extract-function: Selection Extraction
We tested extracting "<locals>" in func.__qualname__ at line 108 into a helper function is_local_qualname:
prod-code extract-function django/utils/inspect.py 108 8 --to 108:39 \
--name is_local_qualname
Generated clean extraction:
def is_local_qualname(func):
return "<locals>" in func.__qualname__
def is_module_level_function(func):
...
if is_local_qualname(func):
return False
Validated with basedpyright: 0 errors.
7. Pre-Validation in In-Memory Shadow (prod-code validate)
Before applying edits to disk, prod-code validate verifies proposed diffs in memory against the remote language server.
We tested with an intentional attribute typo (inspect.non_existent_attribute(func) in django/utils/inspect.py):
cat django/utils/inspect.py | \
sed 's/inspect.isfunction(func)/inspect.non_existent_attribute(func)/' | \
prod-code validate django/utils/inspect.py
In 2.07 seconds, without touching the file on disk:
- The server recognized 78 pre-existing type warnings in the file as baseline.
- It subtracted the baseline and reported only the single newly introduced error:
django/utils/inspect.py: 1 error(s), 3 warning(s) error: "non_existent_attribute" is not a known attribute of module "inspect" [reportAttributeAccessIssue] [prod-code] analysed in 2.07s
8. Summary: 67-Tool Evaluation Matrix for Django
| Category | Suite | Target Subsystem | Result | Latency / Telemetry |
|---|---|---|---|---|
| Ingestion | Fast-Sync Ingestion | Entire Repository | PASS | 2,932 files synchronized, 0% laptop CPU |
| Sanitization | Scrubbing Telemetry | Issue Reporter | PASS | LAN IPs, node names, home paths scrubbed |
| Architecture | DAG Coupling & Instability | django/core (112 modules) |
PASS | 832 dependencies, 25 circular paths detected |
| Code Quality | Clone Harvester | django/core |
PASS | 150+ Type-2 clone groups detected |
| Code Quality | Diagnostics | django/core/handlers/base.py |
PASS | Strict type analysis in 1.40s |
| Code Quality | Dead Code & Unreferenced | Subsystem Scan | PASS | 20 files, 44 symbols scanned in 108s |
| Search | Semantic Intent Search | Entire Repository | PASS | 44,985 decls in 2,975 files scanned in 204 ms |
| Search | Structural AST Search | django/core |
PASS | 38 matches across 21 files in 99.70 ms |
| Navigation | Symbol Definition | BaseHandler |
PASS | Resolved to base.py:21:7 |
| Navigation | Symbol Layout & Hover | BaseHandler |
PASS | class BaseHandler() returned in 15 ms |
| Navigation | Transitive Slicing | resolve_request (Depth 2) |
PASS | 99.97% reduction (527K LOC → 160 LOC) in 0.12s |
| Navigation | Callers Tracing | resolve_request |
PASS | 2 callers traced (_get_response, _get_response_async) |
| Navigation | File Outline | base.py |
PASS | 1 class, 10 methods, 7 attributes, 54 locals |
| Refactoring | Invert Predicate | is_module_level_function |
PASS | 14 calls updated, first-class value usage refused |
| Refactoring | Parameter Object | func_supports_parameter |
PASS | Generated FuncParamSpec, updated callers & imports |
| Refactoring | Extract Function | is_local_qualname |
PASS | Extracted helper, validated with 0 errors |
| Refactoring | References | func_supports_parameter |
PASS | Traced all 3 cross-file references |
| Verification | In-Memory Pre-validation | inspect.py |
PASS | Subtracted 78 baseline warnings, caught error in 2.07s |
| Execution | Remote Python Exec | Python 3.10 Runtime | PASS | Executed remote command in 0.8s on ram9 |
Conclusion
Evaluating prod-code against django/django demonstrates that cluster-backed AST intelligence handles dynamic, dynamically typed Python codebases with the same mathematical rigor as compiled languages:
- Massive Python Monoliths at Zero Local Cost: Ingesting and analyzing half a million lines of Python on a remote 32-core node preserves complete laptop responsiveness with 0% local CPU usage.
- First-Class Value Safety in Refactorings: Inversion and parameter transformations respect Python’s dynamic idioms, refusing dangerous modifications when functions are passed as first-class values or when imports must be updated across files.
- Transitive Slicing Eliminates Drowning in Context: Extracting self-contained dependency closures provides AI agents with exact execution slices in milliseconds without consuming hundreds of thousands of tokens.
Cite this article
Alexander Panasenko (2026-09-30). Half a Million Lines of Python and 25 Import Cycles: Dissecting Django with 67 AST Tools. https://prod.codes/blog/half-a-million-lines-of-python-inside-django/