codeanalyzer-python 1.1.0__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. codeanalyzer_python-1.1.0/README.md → codeanalyzer_python-1.2.0/PKG-INFO +65 -151
  2. codeanalyzer_python-1.1.0/PKG-INFO → codeanalyzer_python-1.2.0/README.md +33 -190
  3. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/__main__.py +95 -123
  4. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/core.py +21 -45
  5. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/access_paths.py +26 -4
  6. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/builder.py +65 -1
  7. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/identity.py +1 -1
  8. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/pdg.py +7 -2
  9. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scc.py +1 -1
  10. codeanalyzer_python-1.2.0/codeanalyzer/entrypoints/__init__.py +3 -0
  11. codeanalyzer_python-1.2.0/codeanalyzer/entrypoints/detect.py +124 -0
  12. codeanalyzer_python-1.2.0/codeanalyzer/entrypoints/matching.py +182 -0
  13. codeanalyzer_python-1.2.0/codeanalyzer/entrypoints/pipeline.py +131 -0
  14. codeanalyzer_python-1.2.0/codeanalyzer/entrypoints/rules.py +159 -0
  15. codeanalyzer_python-1.2.0/codeanalyzer/entrypoints/rules.yml +88 -0
  16. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/bolt.py +1 -1
  17. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/project.py +85 -60
  18. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/schema.py +35 -34
  19. codeanalyzer_python-1.2.0/codeanalyzer/options/__init__.py +3 -0
  20. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/options/options.py +2 -26
  21. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/schema/__init__.py +48 -0
  22. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/schema/l1_body.py +11 -1
  23. codeanalyzer_python-1.2.0/codeanalyzer/schema/l2_callees.py +52 -0
  24. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/schema/py_schema.py +95 -103
  25. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/semantic_analysis/call_graph.py +20 -4
  26. codeanalyzer_python-1.2.0/codeanalyzer/semantic_analysis/defuse_linker.py +1499 -0
  27. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/syntactic_analysis/symbol_table_builder.py +88 -3
  28. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/pyproject.toml +15 -14
  29. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/schema.neo4j.json +41 -55
  30. codeanalyzer_python-1.1.0/codeanalyzer/config/__init__.py +0 -3
  31. codeanalyzer_python-1.1.0/codeanalyzer/config/config.py +0 -8
  32. codeanalyzer_python-1.1.0/codeanalyzer/options/__init__.py +0 -3
  33. codeanalyzer_python-1.1.0/codeanalyzer/schema/l2_callees.py +0 -36
  34. codeanalyzer_python-1.1.0/codeanalyzer/semantic_analysis/pycg/__init__.py +0 -20
  35. codeanalyzer_python-1.1.0/codeanalyzer/semantic_analysis/pycg/pycg_analysis.py +0 -1115
  36. codeanalyzer_python-1.1.0/codeanalyzer/semantic_analysis/pycg/pycg_exceptions.py +0 -23
  37. codeanalyzer_python-1.1.0/codeanalyzer/semantic_analysis/pycg/shard_planner.py +0 -401
  38. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/.gitignore +0 -0
  39. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/LICENSE +0 -0
  40. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/NOTICE +0 -0
  41. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/__init__.py +0 -0
  42. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/__init__.py +0 -0
  43. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/alias.py +0 -0
  44. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/cfg.py +0 -0
  45. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/defuse.py +0 -0
  46. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/dominance.py +0 -0
  47. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/LICENSE +0 -0
  48. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/README.md +0 -0
  49. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/SSA/__init__.py +0 -0
  50. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/SSA/const.py +0 -0
  51. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/__init__.py +0 -0
  52. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/cfg/__init__.py +0 -0
  53. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/cfg/builder.py +0 -0
  54. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/cfg/model.py +0 -0
  55. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/core/__init__.py +0 -0
  56. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/core/func_call_visitor.py +0 -0
  57. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/core/vars_visitor.py +0 -0
  58. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel_oracle.py +0 -0
  59. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/sdg.py +0 -0
  60. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/slicing.py +0 -0
  61. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/summaries.py +0 -0
  62. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/syntactic.py +0 -0
  63. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/jedi/__init__.py +0 -0
  64. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/jedi/jedi.py +0 -0
  65. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/__init__.py +0 -0
  66. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/cypher.py +0 -0
  67. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/emit.py +0 -0
  68. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/rows.py +0 -0
  69. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/provenance.py +0 -0
  70. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/py.typed +0 -0
  71. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/schema/assign_ids.py +0 -0
  72. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/schema/call_graph_ids.py +0 -0
  73. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/schema/ids.py +0 -0
  74. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/semantic_analysis/__init__.py +0 -0
  75. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/syntactic_analysis/__init__.py +0 -0
  76. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/syntactic_analysis/exceptions.py +0 -0
  77. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/syntactic_analysis/import_resolver.py +0 -0
  78. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/utils/__init__.py +0 -0
  79. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/utils/logging.py +0 -0
  80. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/codeanalyzer/utils/progress_bar.py +0 -0
  81. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/docs/handoff/README.md +0 -0
  82. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/docs/handoff/schema.neo4j.json +0 -0
  83. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/schema-uml.drawio +0 -0
  84. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/flask/README.md +0 -0
  85. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/flask/examples/celery/README.md +0 -0
  86. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/flask/requirements-skip/README.md +0 -0
  87. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/flask/src/flask/sansio/README.md +0 -0
  88. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/requests/LICENSE +0 -0
  89. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/requests/NOTICE +0 -0
  90. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/requests/README.md +0 -0
  91. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/requests/docs/_themes/LICENSE +0 -0
  92. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/requests/ext/LICENSE +0 -0
  93. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/xarray/LICENSE +0 -0
  94. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/xarray/README.md +0 -0
  95. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/xarray/properties/README.md +0 -0
  96. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/xarray/xarray/datatree_/LICENSE +0 -0
  97. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/xarray/xarray/datatree_/README.md +0 -0
  98. {codeanalyzer_python-1.1.0 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/xarray/xarray/datatree_/docs/README.md +0 -0
@@ -1,3 +1,35 @@
1
+ Metadata-Version: 2.5
2
+ Name: codeanalyzer-python
3
+ Version: 1.2.0
4
+ Summary: Static analysis for Python — canonical schema v2 (symbol table, call graph, and native CFG/PDG/SDG dataflow) as analysis.json or a Neo4j property graph.
5
+ Author-email: Rahul Krishna <i.m.ralk@gmail.com>
6
+ License-File: LICENSE
7
+ License-File: NOTICE
8
+ Requires-Python: >=3.9
9
+ Requires-Dist: astor<0.9.0,>=0.8.1
10
+ Requires-Dist: jedi<0.20.0,>=0.18.0; python_version < '3.11'
11
+ Requires-Dist: jedi<=0.19.2; python_version >= '3.11'
12
+ Requires-Dist: networkx<3.2.0,>=2.6.0; python_version < '3.11'
13
+ Requires-Dist: networkx<4.0.0,>=3.0.0; python_version >= '3.11'
14
+ Requires-Dist: packaging>=25.0
15
+ Requires-Dist: parso>=0.8.5
16
+ Requires-Dist: pydantic<2.0.0,>=1.8.0; python_version < '3.11'
17
+ Requires-Dist: pydantic<3.0.0,>=2.0.0; python_version >= '3.11'
18
+ Requires-Dist: pyyaml<7.0,>=6.0
19
+ Requires-Dist: ray<3.0.0,>=2.10.0; python_version >= '3.11'
20
+ Requires-Dist: ray==2.0.0; python_version < '3.11'
21
+ Requires-Dist: requests<3.0.0,>=2.20.0; python_version >= '3.11'
22
+ Requires-Dist: rich<14.0.0,>=12.6.0; python_version < '3.11'
23
+ Requires-Dist: rich<15.0.0,>=14.0.0; python_version >= '3.11'
24
+ Requires-Dist: typer<1.0.0,>=0.9.0; python_version < '3.11'
25
+ Requires-Dist: typer<2.0.0,>=0.9.0; python_version >= '3.11'
26
+ Requires-Dist: typing-extensions<5.0.0,>=4.0.0; python_version < '3.11'
27
+ Requires-Dist: typing-extensions<6.0.0,>=4.5.0; python_version >= '3.11'
28
+ Requires-Dist: uv>=0.5.0
29
+ Provides-Extra: neo4j
30
+ Requires-Dist: neo4j<6.0.0,>=5.0.0; extra == 'neo4j'
31
+ Description-Content-Type: text/markdown
32
+
1
33
  <div align="center">
2
34
 
3
35
  <img src="https://github.com/codellm-devkit/codeanalyzer-python/blob/main/docs/assets/logo.png?raw=true" alt="CodeLLM-DevKit" />
@@ -16,7 +48,7 @@
16
48
  ---
17
49
 
18
50
  `canpy` is a static analyzer for Python built on [Jedi](https://jedi.readthedocs.io/),
19
- [PyCG](https://github.com/vitsalis/PyCG), and [Tree-sitter](https://tree-sitter.github.io/). It
51
+ and [Tree-sitter](https://tree-sitter.github.io/). It
20
52
  emits the **canonical CodeLLM-DevKit (CLDK) schema v2** — a single, additive Code Property Graph
21
53
  tree — either as `analysis.json` or projected into a **Neo4j property graph**. It is the Python
22
54
  backend behind [CLDK](https://github.com/codellm-devkit/python-sdk), mirroring its
@@ -58,8 +90,9 @@ needs.
58
90
  at a single `application` node with durable `can://` ids on every callable and above.
59
91
  - **Symbol table** — modules, classes, functions, methods, variables, decorators, imports, and
60
92
  docstrings, with precise byte-offset source spans; each module carries its `source` once.
61
- - **Call graph** — Jedi's lexical resolver at level 1, enriched with **PyCG**-resolved edges at
62
- level 2 (provenance-tagged, coupling-aware sharding for large apps).
93
+ - **Call graph** — Jedi's lexical resolver at level 1, enriched at level 2 by a per-callable
94
+ **defuse linker** that resolves call sites through local def-use chains and module-scope
95
+ bindings (provenance-tagged, deterministic, no global fixpoint).
63
96
  - **Dataflow graphs** — native, per-callable exceptional **CFG** plus **control-** and
64
97
  **data-dependence** edges (`cfg`/`cdg`/`ddg`) at level 3, stitched into a whole-program
65
98
  **interprocedural SDG** (synthetic parameter vertices, `param_in`/`param_out`/`summary`,
@@ -70,7 +103,7 @@ needs.
70
103
  checked in as `schema.neo4j.json` (`2.0.0`) and shipped with every release.
71
104
  - **Incremental cache** — per-file results are cached under `.codeanalyzer`; `--lazy` (default)
72
105
  reuses them, `--eager` forces a clean rebuild. `--ray` distributes the work across cores.
73
- - **Compact output** — canonical `analysis.json`, or binary `analysis.msgpack` for smaller artifacts.
106
+ - **Compact output** — one canonical `analysis.json` per run.
74
107
 
75
108
  ## Installation
76
109
 
@@ -143,8 +176,7 @@ canpy --input /path/to/python/project
143
176
  ```
144
177
 
145
178
  With no `--output`, the analysis is printed to stdout as compact JSON; with `--output <dir>` it is
146
- written to `analysis.json` (or `graph.cypher` for `--emit neo4j`, or `analysis.msgpack` with
147
- `--format msgpack`) in that directory.
179
+ written to `analysis.json` (or `graph.cypher` for `--emit neo4j`) in that directory.
148
180
 
149
181
  ### Options
150
182
 
@@ -155,7 +187,7 @@ $ canpy --help
155
187
 
156
188
  Usage: canpy [OPTIONS] COMMAND [ARGS]...
157
189
 
158
- Static Analysis on Python source code using Jedi, PyCG and Tree sitter.
190
+ Static Analysis on Python source code using Jedi and Tree sitter.
159
191
 
160
192
  ╭─ Options ────────────────────────────────────────────────────────────────────╮
161
193
  │ --version Show the canpy │
@@ -168,10 +200,6 @@ $ canpy --help
168
200
  │ --emit schema). │
169
201
  │ --output -o <path> Output directory │
170
202
  │ for artifacts. │
171
- │ --format -f <json|msgpack> Output format │
172
- │ for --emit json: │
173
- │ json or msgpack. │
174
- │ [default: json] │
175
203
  │ --emit <json|neo4j|sche Output target: │
176
204
  │ ma> json │
177
205
  │ (analysis.json, │
@@ -219,7 +247,8 @@ $ canpy --help
219
247
  │ --analysis-level -a <int range> Analysis depth: │
220
248
  │ [1<=x<=4] 1=symbol │
221
249
  │ table+Jedi call │
222
- │ graph, 2=+PyCG
250
+ │ graph,
251
+ │ 2=+defuse-linker │
223
252
  │ call graph, │
224
253
  │ 3=+native │
225
254
  │ intraprocedural │
@@ -231,7 +260,7 @@ $ canpy --help
231
260
  │ edges, │
232
261
  │ alias-aware │
233
262
  │ DDG). │
234
- │ [default: 1]
263
+ │ [default: (1)]
235
264
  │ --graphs <str> Level 3+ only: │
236
265
  │ comma-separated │
237
266
  │ program-graph │
@@ -243,8 +272,12 @@ $ canpy --help
243
272
  │ PDG's data edges │
244
273
  │ only; `sdg` │
245
274
  │ requires -a 4. │
275
+ │ Incompatible │
276
+ │ with --emit │
277
+ │ neo4j (always │
278
+ │ full-depth). │
246
279
  │ [default: │
247
- │ cfg,dfg,pdg]
280
+ (cfg,dfg,pdg)]
248
281
  │ --graph-field-de… <int range> Level 3 only: │
249
282
  │ [x>=1] k-limit on │
250
283
  │ access-path │
@@ -305,134 +338,14 @@ $ canpy --help
305
338
  │ verbosity: -v, │
306
339
  │ -vv, -vvv │
307
340
  │ [default: 0] │
308
- │ --pycg-shard --no-pycg-shard Shard PyCG
309
- call-graph
310
- analysis by
311
- Python package
312
- (level 2 only).
313
- When the project
314
- exceeds the
315
- 500-file
316
- │ ceiling, PyCG is │
317
- │ run │
318
- │ independently │
319
- │ per top-level │
320
- │ package with │
321
- │ cross-package │
322
- │ imports treated │
323
- │ as ghost nodes. │
324
- │ Without this │
325
- │ flag, projects │
326
- │ over the ceiling │
327
- │ fall back to │
328
- │ Jedi-only edges. │
329
- │ [default: │
330
- │ no-pycg-shard] │
331
- │ --pycg-shard-cei… <int range> Maximum files │
332
- │ [x>=1] per shard when │
333
- │ --pycg-shard is │
334
- │ active (default │
335
- │ 100). Shards │
336
- │ exceeding this │
337
- │ limit are │
338
- │ skipped; their │
339
- │ call edges are │
340
- │ omitted from the │
341
- │ call graph (Jedi │
342
- │ edges for those │
343
- │ packages are │
344
- │ still included). │
345
- │ Lower values are │
346
- │ safer for │
347
- │ packages with │
348
- │ deep class │
349
- │ hierarchies or │
350
- │ heavy import │
351
- │ graphs. │
352
- │ [default: 100] │
353
- │ --pycg-shard-tim… <int range> Per-shard │
354
- │ [x>=0] wall-clock │
355
- │ timeout in │
356
- │ seconds when │
357
- │ --pycg-shard is │
358
- │ active (default │
359
- │ 120). A shard │
360
- │ that exceeds │
361
- │ this limit is │
362
- │ skipped │
363
- │ gracefully. │
364
- │ PyCG's fixpoint │
365
- │ is bimodal: it │
366
- │ either converges │
367
- │ quickly or │
368
- │ diverges │
369
- │ indefinitely, so │
370
- │ the timeout acts │
371
- │ as a final │
372
- │ safety net after │
373
- │ the file-count │
374
- │ ceiling. Set to │
375
- │ 0 to disable. │
376
- │ POSIX only │
377
- │ (macOS / Linux); │
378
- │ ignored on │
379
- │ Windows. │
380
- │ [default: 120] │
381
- │ --pycg-shard-str… <jedi|package> How --pycg-shard │
382
- │ groups files │
383
- │ (level 2 only). │
384
- │ 'jedi' (default) │
385
- │ partitions the │
386
- │ Jedi │
387
- │ module-dependen… │
388
- │ graph (SCC + │
389
- │ Louvain) so │
390
- │ tightly-coupled │
391
- │ modules │
392
- │ co-compute and │
393
- │ few call edges │
394
- │ are severed │
395
- │ between shards; │
396
- │ import cycles │
397
- │ are never split. │
398
- │ 'package' uses │
399
- │ the legacy │
400
- │ one-shard-per-p… │
401
- │ grouping. │
402
- │ [default: jedi] │
403
- │ --pycg-max-iter <int range> Cap on PyCG's │
404
- │ [x>=-1] fixpoint passes │
405
- │ per │
406
- │ shard/project │
407
- │ (level 2; │
408
- │ default 50). │
409
- │ PyCG iterates │
410
- │ until its │
411
- │ points-to state │
412
- │ stops changing, │
413
- │ but its │
414
- │ access-path │
415
- │ domain has no │
416
- │ convergence │
417
- │ bound, so heavy │
418
- │ metaclass/mixin │
419
- │ code (e.g. an │
420
- │ ORM) can loop │
421
- │ with each pass │
422
- │ costing seconds. │
423
- │ The cap returns │
424
- │ a │
425
- │ sound-but-incom… │
426
- │ call graph │
427
- │ instead of │
428
- │ looping until │
429
- │ the timeout │
430
- │ kills it. Set to │
431
- │ -1 for PyCG's │
432
- │ unbounded │
433
- │ run-to-converge… │
434
- │ behaviour. │
435
- │ [default: 50] │
341
+ │ --entrypoint-rul… <path> Extra entrypoint
342
+ rules file
343
+ (YAML).
344
+ Repeatable;
345
+ merges with the
346
+ shipped rules. A
347
+ malformed file
348
+ is an error.
436
349
  │ --help Show this │
437
350
  │ message and │
438
351
  │ exit. │
@@ -454,13 +367,14 @@ $ canpy --help
454
367
  canpy --input ./my-python-project --output ./out --format msgpack # → ./out/analysis.msgpack
455
368
  ```
456
369
 
457
- 3. **Enrich the call graph with PyCG (level 2):**
370
+ 3. **Enrich the call graph with the defuse linker (level 2):**
458
371
  ```sh
459
372
  canpy --input ./my-python-project -a 2
460
373
  ```
461
- Level 1 edges come from Jedi's lexical resolution. `-a 2` runs **PyCG** and merges its
462
- flow-sensitive edges in (RPC / third-party / dynamically-dispatched targets), backfilling
463
- callees Jedi could not resolve. Every edge is provenance-tagged (e.g. `jedi`, `pycg`).
374
+ Level 1 edges come from Jedi's lexical resolution. `-a 2` runs the **defuse linker**
375
+ per-callable resolution over lexical scopes, import bindings, class hierarchies, and a
376
+ bounded type-propagation round and merges its edges with Jedi's, backfilling the
377
+ callees Jedi could not resolve. Every edge is provenance-tagged (`jedi`, `defuse`).
464
378
 
465
379
  4. **Emit a Neo4j snapshot, or push to a live database:**
466
380
  ```sh
@@ -489,7 +403,7 @@ $ canpy --help
489
403
  canpy --input ./my-python-project -a 3 --graph-field-depth 2 # tighter access-path k-limit
490
404
  ```
491
405
  Levels 3 and 4 also enrich the Neo4j projection (`--emit neo4j`) with the CPG overlay
492
- (`:PyCFGNode` nodes wired by `PY_CFG_NEXT`/`PY_CDG`/`PY_DDG`, plus the level-4
406
+ (`:PyBodyNode` nodes wired by `PY_CFG_NEXT`/`PY_CDG`/`PY_DDG`, plus the level-4
493
407
  `PY_PARAM_IN`/`PY_PARAM_OUT`/`PY_SUMMARY` edges — the cross-language dataflow vocabulary,
494
408
  PY_-namespaced like every other row family so multi-language databases never mingle
495
409
  analyzers' edges).
@@ -502,7 +416,7 @@ levels are cumulative and additive — `analysis.json(-a 1) ⊆ … ⊆ analysis
502
416
  | Level | Flag | What it adds | Where it lands |
503
417
  | --- | --- | --- | --- |
504
418
  | **1** | `-a 1` (default) | Symbol table, Jedi call graph, and `call` nodes in each callable's `body` | `body` calls (`callee: null`) |
505
- | **2** | `-a 2` | PyCG call-graph enrichment; each call's `callee` backfilled to a `can://` id | `call_graph`, `body` callees |
419
+ | **2** | `-a 2` | Defuse-linker call-graph enrichment; each call's `callee` backfilled to a `can://` id | `call_graph`, `body` callees |
506
420
  | **3** | `-a 3` | Native **intraprocedural** CFG/CDG/DDG (syntactic, name-equality, `prov: ["ssa"]`) | `cfg`, `cdg`, `ddg`, `@entry`/`@exit` on each callable |
507
421
  | **4** | `-a 4` | **Interprocedural** SDG: synthetic param vertices, alias-aware DDG (`prov: ["points-to"]`) | `param_in`, `param_out`, `summary`, semantic `ddg` |
508
422
 
@@ -531,7 +445,7 @@ symbol-table signature by construction
531
445
  external dependency to install; the analyzer falls back to the built-in `TypeBasedAliasOracle`
532
446
  (Jedi-inferred types; unknown types conservatively alias) only when Scalpel can't resolve a
533
447
  construct or a per-callable build fails, keeping the `may_alias` interface total. Call dispatch
534
- comes from the merged Jedi(+PyCG) call graph, treated as a frozen oracle.
448
+ comes from the merged Jedi + defuse-linker call graph, treated as a frozen oracle.
535
449
  - **Summaries:** relational formal-in → formal-out flows composed bottom-up over the Tarjan SCC
536
450
  condensation of the call graph, a monotone fixpoint within SCCs; globals ride as extra formals,
537
451
  closure captures bind at definition sites.
@@ -570,7 +484,7 @@ just populate more of the same tree:
570
484
  }
571
485
  },
572
486
  "call_graph": [ { "src": "can://…/main(a)", "dst": "can://…/helper(x)",
573
- "weight": 1, "prov": ["jedi", "pycg"] } ],
487
+ "weight": 1, "prov": ["defuse", "jedi"] } ],
574
488
  "external_symbols": { // imported/builtin call targets, keyed by id
575
489
  "can://python/<app>/@external/os/getcwd":
576
490
  { "id": "can://python/<app>/@external/os/getcwd", "kind": "external",
@@ -636,7 +550,7 @@ label is `Py`-prefixed and every relationship type is `PY_`-prefixed (e.g. `:PyC
636
550
  so multiple language analyzers can share one database without label or relationship-type collisions.
637
551
  Declarations are keyed by their **`can://` id** under a shared `:PySymbol` label; calls, imports,
638
552
  inheritance, decorators, and call sites are relationships. At `-a 3`/`-a 4` the projection gains the
639
- **CPG overlay** — `:PyCFGNode` nodes (statements, and at level 4 the parameter vertices) wired by
553
+ **CPG overlay** — `:PyBodyNode` nodes (statements, and at level 4 the parameter vertices) wired by
640
554
  `PY_CFG_NEXT`/`PY_CDG`/`PY_DDG`, plus the level-4 `PY_PARAM_IN`/`PY_PARAM_OUT`/`PY_SUMMARY` edges:
641
555
 
642
556
  - **Without `--neo4j-uri`** — writes a self-contained `graph.cypher` (constraints + indexes, a scoped