codeanalyzer-python 1.1.1__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.
- codeanalyzer_python-1.1.1/README.md → codeanalyzer_python-1.2.0/PKG-INFO +65 -151
- codeanalyzer_python-1.1.1/PKG-INFO → codeanalyzer_python-1.2.0/README.md +33 -190
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/__main__.py +95 -123
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/core.py +21 -45
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/access_paths.py +26 -4
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/builder.py +7 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/identity.py +1 -1
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/pdg.py +7 -2
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scc.py +1 -1
- codeanalyzer_python-1.2.0/codeanalyzer/entrypoints/__init__.py +3 -0
- codeanalyzer_python-1.2.0/codeanalyzer/entrypoints/detect.py +124 -0
- codeanalyzer_python-1.2.0/codeanalyzer/entrypoints/matching.py +182 -0
- codeanalyzer_python-1.2.0/codeanalyzer/entrypoints/pipeline.py +131 -0
- codeanalyzer_python-1.2.0/codeanalyzer/entrypoints/rules.py +159 -0
- codeanalyzer_python-1.2.0/codeanalyzer/entrypoints/rules.yml +88 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/bolt.py +1 -1
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/project.py +85 -60
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/schema.py +35 -34
- codeanalyzer_python-1.2.0/codeanalyzer/options/__init__.py +3 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/options/options.py +2 -26
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/schema/__init__.py +48 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/schema/l1_body.py +11 -1
- codeanalyzer_python-1.2.0/codeanalyzer/schema/l2_callees.py +52 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/schema/py_schema.py +95 -103
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/semantic_analysis/call_graph.py +20 -4
- codeanalyzer_python-1.2.0/codeanalyzer/semantic_analysis/defuse_linker.py +1499 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/syntactic_analysis/symbol_table_builder.py +88 -3
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/pyproject.toml +15 -14
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/schema.neo4j.json +41 -55
- codeanalyzer_python-1.1.1/codeanalyzer/config/__init__.py +0 -3
- codeanalyzer_python-1.1.1/codeanalyzer/config/config.py +0 -8
- codeanalyzer_python-1.1.1/codeanalyzer/options/__init__.py +0 -3
- codeanalyzer_python-1.1.1/codeanalyzer/schema/l2_callees.py +0 -36
- codeanalyzer_python-1.1.1/codeanalyzer/semantic_analysis/pycg/__init__.py +0 -20
- codeanalyzer_python-1.1.1/codeanalyzer/semantic_analysis/pycg/pycg_analysis.py +0 -1115
- codeanalyzer_python-1.1.1/codeanalyzer/semantic_analysis/pycg/pycg_exceptions.py +0 -23
- codeanalyzer_python-1.1.1/codeanalyzer/semantic_analysis/pycg/shard_planner.py +0 -401
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/.gitignore +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/LICENSE +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/NOTICE +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/__init__.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/__init__.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/alias.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/cfg.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/defuse.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/dominance.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/LICENSE +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/README.md +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/SSA/__init__.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/SSA/const.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/__init__.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/cfg/__init__.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/cfg/builder.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/cfg/model.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/core/__init__.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/core/func_call_visitor.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel/core/vars_visitor.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/scalpel_oracle.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/sdg.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/slicing.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/summaries.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/dataflow/syntactic.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/jedi/__init__.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/jedi/jedi.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/__init__.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/cypher.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/emit.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/neo4j/rows.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/provenance.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/py.typed +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/schema/assign_ids.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/schema/call_graph_ids.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/schema/ids.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/semantic_analysis/__init__.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/syntactic_analysis/__init__.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/syntactic_analysis/exceptions.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/syntactic_analysis/import_resolver.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/utils/__init__.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/utils/logging.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/codeanalyzer/utils/progress_bar.py +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/docs/handoff/README.md +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/docs/handoff/schema.neo4j.json +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/schema-uml.drawio +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/flask/README.md +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/flask/examples/celery/README.md +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/flask/requirements-skip/README.md +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/flask/src/flask/sansio/README.md +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/requests/LICENSE +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/requests/NOTICE +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/requests/README.md +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/requests/docs/_themes/LICENSE +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/requests/ext/LICENSE +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/xarray/LICENSE +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/xarray/README.md +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/xarray/properties/README.md +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/xarray/xarray/datatree_/LICENSE +0 -0
- {codeanalyzer_python-1.1.1 → codeanalyzer_python-1.2.0}/test/fixtures/whole_applications/xarray/xarray/datatree_/README.md +0 -0
- {codeanalyzer_python-1.1.1 → 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
|
-
|
|
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
|
|
62
|
-
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
│ --
|
|
309
|
-
│
|
|
310
|
-
│
|
|
311
|
-
│
|
|
312
|
-
│
|
|
313
|
-
│
|
|
314
|
-
│
|
|
315
|
-
│
|
|
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
|
|
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 **
|
|
462
|
-
|
|
463
|
-
|
|
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
|
-
(`:
|
|
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` |
|
|
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
|
|
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": ["
|
|
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** — `:
|
|
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
|