codeanalyzer-python 0.3.1__py3-none-any.whl → 1.0.0__py3-none-any.whl
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/__main__.py +51 -4
- codeanalyzer/core.py +153 -82
- codeanalyzer/dataflow/__init__.py +35 -0
- codeanalyzer/dataflow/access_paths.py +563 -0
- codeanalyzer/dataflow/alias.py +93 -0
- codeanalyzer/dataflow/builder.py +688 -0
- codeanalyzer/dataflow/cfg.py +605 -0
- codeanalyzer/dataflow/defuse.py +113 -0
- codeanalyzer/dataflow/dominance.py +140 -0
- codeanalyzer/dataflow/identity.py +91 -0
- codeanalyzer/dataflow/pdg.py +100 -0
- codeanalyzer/dataflow/scalpel_oracle.py +269 -0
- codeanalyzer/dataflow/scc.py +91 -0
- codeanalyzer/dataflow/sdg.py +424 -0
- codeanalyzer/dataflow/slicing.py +93 -0
- codeanalyzer/dataflow/summaries.py +217 -0
- codeanalyzer/dataflow/syntactic.py +26 -0
- codeanalyzer/neo4j/bolt.py +19 -4
- codeanalyzer/neo4j/cypher.py +9 -3
- codeanalyzer/neo4j/emit.py +8 -3
- codeanalyzer/neo4j/project.py +241 -60
- codeanalyzer/neo4j/rows.py +18 -15
- codeanalyzer/neo4j/schema.py +43 -7
- codeanalyzer/options/options.py +4 -0
- codeanalyzer/schema/__init__.py +19 -0
- codeanalyzer/schema/assign_ids.py +37 -0
- codeanalyzer/schema/call_graph_ids.py +12 -0
- codeanalyzer/schema/ids.py +23 -0
- codeanalyzer/schema/l1_body.py +29 -0
- codeanalyzer/schema/l2_callees.py +36 -0
- codeanalyzer/schema/py_schema.py +141 -30
- codeanalyzer/semantic_analysis/call_graph.py +24 -27
- codeanalyzer/semantic_analysis/pycg/pycg_analysis.py +10 -10
- codeanalyzer/semantic_analysis/pycg/shard_planner.py +7 -7
- codeanalyzer/syntactic_analysis/symbol_table_builder.py +29 -10
- {codeanalyzer_python-0.3.1.dist-info → codeanalyzer_python-1.0.0.dist-info}/METADATA +230 -43
- codeanalyzer_python-1.0.0.dist-info/RECORD +59 -0
- codeanalyzer_python-0.3.1.dist-info/RECORD +0 -39
- {codeanalyzer_python-0.3.1.dist-info → codeanalyzer_python-1.0.0.dist-info}/WHEEL +0 -0
- {codeanalyzer_python-0.3.1.dist-info → codeanalyzer_python-1.0.0.dist-info}/entry_points.txt +0 -0
- {codeanalyzer_python-0.3.1.dist-info → codeanalyzer_python-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {codeanalyzer_python-0.3.1.dist-info → codeanalyzer_python-1.0.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -66,17 +66,17 @@ logger = logging.getLogger(__name__)
|
|
|
66
66
|
|
|
67
67
|
def _walk_callable_sigs(c: PyCallable) -> Iterator[str]:
|
|
68
68
|
yield c.signature
|
|
69
|
-
for inner in c.
|
|
69
|
+
for inner in c.callables.values():
|
|
70
70
|
yield from _walk_callable_sigs(inner)
|
|
71
|
-
for inner_cls in c.
|
|
71
|
+
for inner_cls in c.types.values():
|
|
72
72
|
yield from _walk_class_sigs(inner_cls)
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
def _walk_class_sigs(cls: PyClass) -> Iterator[str]:
|
|
76
76
|
yield cls.signature
|
|
77
|
-
for method in cls.
|
|
77
|
+
for method in cls.callables.values():
|
|
78
78
|
yield from _walk_callable_sigs(method)
|
|
79
|
-
for inner in cls.
|
|
79
|
+
for inner in cls.types.values():
|
|
80
80
|
yield from _walk_class_sigs(inner)
|
|
81
81
|
|
|
82
82
|
|
|
@@ -95,7 +95,7 @@ def _signature_to_file(symbol_table: Dict[str, PyModule]) -> Dict[str, str]:
|
|
|
95
95
|
for fn in module.functions.values():
|
|
96
96
|
for sig in _walk_callable_sigs(fn):
|
|
97
97
|
sig_to_file[sig] = module.file_path
|
|
98
|
-
for cls in module.
|
|
98
|
+
for cls in module.types.values():
|
|
99
99
|
for sig in _walk_class_sigs(cls):
|
|
100
100
|
sig_to_file[sig] = module.file_path
|
|
101
101
|
return sig_to_file
|
|
@@ -152,8 +152,8 @@ def build_module_graph(
|
|
|
152
152
|
g.add_node(module.file_path, module_name=module.module_name)
|
|
153
153
|
|
|
154
154
|
for edge in jedi_edges:
|
|
155
|
-
src = sig_to_file.get(edge.
|
|
156
|
-
dst = sig_to_file.get(edge.
|
|
155
|
+
src = sig_to_file.get(edge.src)
|
|
156
|
+
dst = sig_to_file.get(edge.dst)
|
|
157
157
|
if src is None or dst is None or src == dst:
|
|
158
158
|
continue
|
|
159
159
|
if g.has_edge(src, dst):
|
|
@@ -23,6 +23,8 @@ from codeanalyzer.schema.py_schema import (
|
|
|
23
23
|
PyModule,
|
|
24
24
|
PySymbol,
|
|
25
25
|
PyVariableDeclaration,
|
|
26
|
+
Span,
|
|
27
|
+
byte_offsets,
|
|
26
28
|
)
|
|
27
29
|
|
|
28
30
|
|
|
@@ -177,11 +179,12 @@ class SymbolTableBuilder:
|
|
|
177
179
|
PyModule.builder()
|
|
178
180
|
.file_path(str(py_file))
|
|
179
181
|
.module_name(py_file.stem)
|
|
182
|
+
.source(source)
|
|
180
183
|
.comments(self._pycomments(module, source))
|
|
181
184
|
.imports(self._imports(module))
|
|
182
185
|
.variables(self._module_variables(module, script))
|
|
183
|
-
.
|
|
184
|
-
.functions(self._callables(module, script))
|
|
186
|
+
.types(self._add_class(module, script, source))
|
|
187
|
+
.functions(self._callables(module, script, source))
|
|
185
188
|
.content_hash(content_hash)
|
|
186
189
|
.last_modified(last_modified)
|
|
187
190
|
.file_size(file_size)
|
|
@@ -237,7 +240,7 @@ class SymbolTableBuilder:
|
|
|
237
240
|
|
|
238
241
|
return imports
|
|
239
242
|
|
|
240
|
-
def _add_class(self, node: AST, script: Script, prefix: str = "") -> Dict[str, PyClass]:
|
|
243
|
+
def _add_class(self, node: AST, script: Script, source: str, prefix: str = "") -> Dict[str, PyClass]:
|
|
241
244
|
classes: Dict[str, PyClass] = {}
|
|
242
245
|
|
|
243
246
|
for child in ast.iter_child_nodes(node):
|
|
@@ -248,6 +251,14 @@ class SymbolTableBuilder:
|
|
|
248
251
|
start_line = child.lineno
|
|
249
252
|
end_line = getattr(child, "end_lineno", start_line + len(child.body))
|
|
250
253
|
code = ast.unparse(child).strip()
|
|
254
|
+
span = Span(
|
|
255
|
+
start=(child.lineno, child.col_offset),
|
|
256
|
+
end=(getattr(child, "end_lineno", child.lineno),
|
|
257
|
+
getattr(child, "end_col_offset", child.col_offset)),
|
|
258
|
+
bytes=byte_offsets(source, child.lineno, child.col_offset,
|
|
259
|
+
getattr(child, "end_lineno", child.lineno),
|
|
260
|
+
getattr(child, "end_col_offset", child.col_offset)),
|
|
261
|
+
)
|
|
251
262
|
|
|
252
263
|
# Try resolving full signature with Jedi
|
|
253
264
|
if prefix:
|
|
@@ -265,18 +276,18 @@ class SymbolTableBuilder:
|
|
|
265
276
|
PyClass.builder()
|
|
266
277
|
.name(class_name)
|
|
267
278
|
.signature(signature)
|
|
279
|
+
.span(span)
|
|
268
280
|
.start_line(start_line)
|
|
269
281
|
.end_line(end_line)
|
|
270
|
-
.code(code)
|
|
271
282
|
.comments(self._pycomments(child, code))
|
|
272
283
|
.base_classes([
|
|
273
284
|
ast.unparse(base)
|
|
274
285
|
for base in child.bases
|
|
275
286
|
if isinstance(base, ast.expr)
|
|
276
287
|
])
|
|
277
|
-
.
|
|
288
|
+
.callables(self._callables(child, script, source, prefix=signature)) # Pass class signature as prefix
|
|
278
289
|
.attributes(self._class_attributes(child, script))
|
|
279
|
-
.
|
|
290
|
+
.types(self._add_class(child, script, source, prefix=signature)) # Pass class signature as prefix
|
|
280
291
|
.build()
|
|
281
292
|
)
|
|
282
293
|
|
|
@@ -285,7 +296,7 @@ class SymbolTableBuilder:
|
|
|
285
296
|
return classes
|
|
286
297
|
|
|
287
298
|
|
|
288
|
-
def _callables(self, node: AST, script: Script, prefix: str = "") -> Dict[str, PyCallable]:
|
|
299
|
+
def _callables(self, node: AST, script: Script, source: str, prefix: str = "") -> Dict[str, PyCallable]:
|
|
289
300
|
callables: Dict[str, PyCallable] = {}
|
|
290
301
|
|
|
291
302
|
for child in ast.iter_child_nodes(node):
|
|
@@ -294,6 +305,14 @@ class SymbolTableBuilder:
|
|
|
294
305
|
start_line = child.lineno
|
|
295
306
|
end_line = getattr(child, "end_lineno", start_line + len(child.body))
|
|
296
307
|
code = ast.unparse(child).strip()
|
|
308
|
+
span = Span(
|
|
309
|
+
start=(child.lineno, child.col_offset),
|
|
310
|
+
end=(getattr(child, "end_lineno", child.lineno),
|
|
311
|
+
getattr(child, "end_col_offset", child.col_offset)),
|
|
312
|
+
bytes=byte_offsets(source, child.lineno, child.col_offset,
|
|
313
|
+
getattr(child, "end_lineno", child.lineno),
|
|
314
|
+
getattr(child, "end_col_offset", child.col_offset)),
|
|
315
|
+
)
|
|
297
316
|
decorators = [ast.unparse(d) for d in child.decorator_list]
|
|
298
317
|
|
|
299
318
|
if prefix:
|
|
@@ -318,8 +337,8 @@ class SymbolTableBuilder:
|
|
|
318
337
|
.name(method_name) # Use the actual method name, not the full signature
|
|
319
338
|
.path(str(script.path))
|
|
320
339
|
.signature(signature) # Use the full signature here
|
|
340
|
+
.span(span)
|
|
321
341
|
.decorators(decorators)
|
|
322
|
-
.code(code)
|
|
323
342
|
.start_line(start_line)
|
|
324
343
|
.end_line(end_line)
|
|
325
344
|
.code_start_line(child.body[0].lineno if child.body else start_line)
|
|
@@ -333,8 +352,8 @@ class SymbolTableBuilder:
|
|
|
333
352
|
if child.returns else self._infer_type(script, child.lineno, child.col_offset)
|
|
334
353
|
)
|
|
335
354
|
.comments(self._pycomments(child, code))
|
|
336
|
-
.
|
|
337
|
-
.
|
|
355
|
+
.callables(self._callables(child, script, source, signature)) # Pass current signature as prefix
|
|
356
|
+
.types(self._add_class(child, script, source, signature)) # Pass current signature as prefix
|
|
338
357
|
.build()
|
|
339
358
|
)
|
|
340
359
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeanalyzer-python
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Static
|
|
3
|
+
Version: 1.0.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
5
|
Author-email: Rahul Krishna <i.m.ralk@gmail.com>
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
License-File: NOTICE
|
|
@@ -33,6 +33,8 @@ Requires-Dist: typing-extensions<6.0.0,>=4.5.0; python_version >= '3.11'
|
|
|
33
33
|
Requires-Dist: uv>=0.5.0
|
|
34
34
|
Provides-Extra: neo4j
|
|
35
35
|
Requires-Dist: neo4j<6.0.0,>=5.0.0; extra == 'neo4j'
|
|
36
|
+
Provides-Extra: scalpel
|
|
37
|
+
Requires-Dist: python-scalpel>=1.0b0; extra == 'scalpel'
|
|
36
38
|
Description-Content-Type: text/markdown
|
|
37
39
|
|
|
38
40
|
<div align="center">
|
|
@@ -41,7 +43,7 @@ Description-Content-Type: text/markdown
|
|
|
41
43
|
|
|
42
44
|
# codeanalyzer-python (`canpy`)
|
|
43
45
|
|
|
44
|
-
**A Python static-analysis toolkit — the CLDK backend that emits
|
|
46
|
+
**A Python static-analysis toolkit — the CLDK backend that emits the canonical schema v2 Code Property Graph, as `analysis.json` or a Neo4j property graph.**
|
|
45
47
|
|
|
46
48
|
[](https://pypi.org/project/codeanalyzer-python/)
|
|
47
49
|
[](https://github.com/codellm-devkit/codeanalyzer-python/releases/latest)
|
|
@@ -52,18 +54,19 @@ Description-Content-Type: text/markdown
|
|
|
52
54
|
|
|
53
55
|
---
|
|
54
56
|
|
|
55
|
-
`canpy` is a static analyzer for Python built on [Jedi](https://jedi.readthedocs.io/),
|
|
56
|
-
[
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
[CLDK](https://github.com/codellm-devkit/python-sdk), mirroring its
|
|
57
|
+
`canpy` is a static analyzer for Python built on [Jedi](https://jedi.readthedocs.io/),
|
|
58
|
+
[PyCG](https://github.com/vitsalis/PyCG), and [Tree-sitter](https://tree-sitter.github.io/). It
|
|
59
|
+
emits the **canonical CodeLLM-DevKit (CLDK) schema v2** — a single, additive Code Property Graph
|
|
60
|
+
tree — either as `analysis.json` or projected into a **Neo4j property graph**. It is the Python
|
|
61
|
+
backend behind [CLDK](https://github.com/codellm-devkit/python-sdk), mirroring its
|
|
61
62
|
[TypeScript](https://github.com/codellm-devkit/codeanalyzer-typescript) (`cants`) and
|
|
62
63
|
[Java](https://github.com/codellm-devkit/codeanalyzer-java) siblings.
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
The payload is **one tree grown one layer at a time** across four analysis levels (`-a 1|2|3|4`): a
|
|
66
|
+
symbol table, a call graph, intraprocedural control- and data-dependence graphs, and a whole-program
|
|
67
|
+
interprocedural system dependence graph. Each level is a strict superset of the one below it
|
|
68
|
+
(`analysis.json(-a 1) ⊆ … ⊆ analysis.json(-a 4)`), so a consumer can request exactly the depth it
|
|
69
|
+
needs.
|
|
67
70
|
|
|
68
71
|
## Table of Contents
|
|
69
72
|
|
|
@@ -77,6 +80,9 @@ and merges them with the Jedi-derived edges, also backfilling callees Jedi could
|
|
|
77
80
|
- [Usage](#usage)
|
|
78
81
|
- [Options](#options)
|
|
79
82
|
- [Examples](#examples)
|
|
83
|
+
- [Analysis levels](#analysis-levels)
|
|
84
|
+
- [Architecture & Tooling](#architecture--tooling)
|
|
85
|
+
- [Output shape (canonical schema v2)](#output-shape-canonical-schema-v2)
|
|
80
86
|
- [Output targets](#output-targets)
|
|
81
87
|
- [`analysis.json` (default)](#analysisjson-default)
|
|
82
88
|
- [Neo4j graph](#neo4j-graph)
|
|
@@ -86,15 +92,21 @@ and merges them with the Jedi-derived edges, also backfilling callees Jedi could
|
|
|
86
92
|
|
|
87
93
|
## Features
|
|
88
94
|
|
|
95
|
+
- **Canonical schema v2** — one additive Code Property Graph tree (`schema_version` `2.0.0`),
|
|
96
|
+
stamped with `language`, `max_level`, `analyzer{name,version}`, and (at L3+) `k_limit`, rooted
|
|
97
|
+
at a single `application` node with durable `can://` ids on every callable and above.
|
|
89
98
|
- **Symbol table** — modules, classes, functions, methods, variables, decorators, imports, and
|
|
90
|
-
docstrings, with precise source spans.
|
|
91
|
-
- **Call graph** — Jedi's lexical resolver
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
docstrings, with precise byte-offset source spans; each module carries its `source` once.
|
|
100
|
+
- **Call graph** — Jedi's lexical resolver at level 1, enriched with **PyCG**-resolved edges at
|
|
101
|
+
level 2 (provenance-tagged, coupling-aware sharding for large apps).
|
|
102
|
+
- **Dataflow graphs** — native, per-callable exceptional **CFG** plus **control-** and
|
|
103
|
+
**data-dependence** edges (`cfg`/`cdg`/`ddg`) at level 3, stitched into a whole-program
|
|
104
|
+
**interprocedural SDG** (synthetic parameter vertices, `param_in`/`param_out`/`summary`,
|
|
105
|
+
alias-aware DDG) at level 4 — all built in-process from the stdlib `ast`.
|
|
94
106
|
- **Neo4j output** — project the analysis into a labeled property graph: a self-contained
|
|
95
107
|
`graph.cypher` snapshot, or an **incremental** push to a live database over Bolt.
|
|
96
108
|
- **Versioned schema** — a machine-readable, version-stamped Neo4j schema contract (`--emit schema`),
|
|
97
|
-
checked in as `schema.neo4j.json` and shipped with every release.
|
|
109
|
+
checked in as `schema.neo4j.json` (`2.0.0`) and shipped with every release.
|
|
98
110
|
- **Incremental cache** — per-file results are cached under `.codeanalyzer`; `--lazy` (default)
|
|
99
111
|
reuses them, `--eager` forces a clean rebuild. `--ray` distributes the work across cores.
|
|
100
112
|
- **Compact output** — canonical `analysis.json`, or binary `analysis.msgpack` for smaller artifacts.
|
|
@@ -131,6 +143,13 @@ For the optional **live Neo4j push** (`--emit neo4j --neo4j-uri …`), install t
|
|
|
131
143
|
pip install 'codeanalyzer-python[neo4j]'
|
|
132
144
|
```
|
|
133
145
|
|
|
146
|
+
For the **Scalpel-backed points-to oracle** at level 4, install the `scalpel` extra. It is optional:
|
|
147
|
+
when it is absent, level 4 automatically falls back to the built-in type-based oracle.
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
pip install 'codeanalyzer-python[scalpel]'
|
|
151
|
+
```
|
|
152
|
+
|
|
134
153
|
### Install via shell script
|
|
135
154
|
|
|
136
155
|
Install the CLI as an isolated tool with the one-line installer (provisions via uv / pipx / pip):
|
|
@@ -240,11 +259,47 @@ $ canpy --help
|
|
|
240
259
|
│ [env var: │
|
|
241
260
|
│ NEO4J_DATABASE] │
|
|
242
261
|
│ --analysis-level -a INTEGER RANGE Analysis depth: │
|
|
243
|
-
│ [1<=x<=
|
|
262
|
+
│ [1<=x<=4] 1=symbol │
|
|
244
263
|
│ table+Jedi call │
|
|
245
264
|
│ graph, 2=+PyCG │
|
|
246
|
-
│ call graph
|
|
265
|
+
│ call graph, │
|
|
266
|
+
│ 3=+native │
|
|
267
|
+
│ intraprocedural │
|
|
268
|
+
│ dataflow │
|
|
269
|
+
│ (CFG/PDG), │
|
|
270
|
+
│ 4=+interprocedu… │
|
|
271
|
+
│ SDG │
|
|
272
|
+
│ (param/summary │
|
|
273
|
+
│ edges, │
|
|
274
|
+
│ alias-aware │
|
|
275
|
+
│ DDG). │
|
|
247
276
|
│ [default: 1] │
|
|
277
|
+
│ --graphs TEXT Level 3+ only: │
|
|
278
|
+
│ comma-separated │
|
|
279
|
+
│ program-graph │
|
|
280
|
+
│ sections to emit │
|
|
281
|
+
│ (cfg, dfg, pdg, │
|
|
282
|
+
│ sdg). Default: │
|
|
283
|
+
│ cfg,dfg,pdg. │
|
|
284
|
+
│ `dfg` emits the │
|
|
285
|
+
│ PDG's data edges │
|
|
286
|
+
│ only; `sdg` │
|
|
287
|
+
│ requires -a 4. │
|
|
288
|
+
│ [default: │
|
|
289
|
+
│ cfg,dfg,pdg] │
|
|
290
|
+
│ --graph-field-de… INTEGER RANGE Level 3 only: │
|
|
291
|
+
│ [x>=1] k-limit on │
|
|
292
|
+
│ access-path │
|
|
293
|
+
│ depth (x.f.g.h │
|
|
294
|
+
│ with k=3 becomes │
|
|
295
|
+
│ x.f.g.*). │
|
|
296
|
+
│ Mandatory bound │
|
|
297
|
+
│ — it is what │
|
|
298
|
+
│ guarantees the │
|
|
299
|
+
│ interprocedural │
|
|
300
|
+
│ fixpoint │
|
|
301
|
+
│ terminates. │
|
|
302
|
+
│ [default: 3] │
|
|
248
303
|
│ --ray --no-ray Enable Ray for │
|
|
249
304
|
│ distributed │
|
|
250
305
|
│ analysis. │
|
|
@@ -441,14 +496,13 @@ $ canpy --help
|
|
|
441
496
|
canpy --input ./my-python-project --output ./out --format msgpack # → ./out/analysis.msgpack
|
|
442
497
|
```
|
|
443
498
|
|
|
444
|
-
3. **
|
|
499
|
+
3. **Enrich the call graph with PyCG (level 2):**
|
|
445
500
|
```sh
|
|
446
|
-
canpy --input ./my-python-project
|
|
501
|
+
canpy --input ./my-python-project -a 2
|
|
447
502
|
```
|
|
448
|
-
|
|
449
|
-
(
|
|
450
|
-
Jedi
|
|
451
|
-
integration is experimental; the CLI is downloaded into `<cache_dir>/codeql/` on first use.
|
|
503
|
+
Level 1 edges come from Jedi's lexical resolution. `-a 2` runs **PyCG** and merges its
|
|
504
|
+
flow-sensitive edges in (RPC / third-party / dynamically-dispatched targets), backfilling
|
|
505
|
+
callees Jedi could not resolve. Every edge is provenance-tagged (e.g. `jedi`, `pycg`).
|
|
452
506
|
|
|
453
507
|
4. **Emit a Neo4j snapshot, or push to a live database:**
|
|
454
508
|
```sh
|
|
@@ -468,31 +522,163 @@ $ canpy --help
|
|
|
468
522
|
canpy --input ./my-python-project --eager --cache-dir /path/to/custom-cache
|
|
469
523
|
```
|
|
470
524
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
525
|
+
7. **Dataflow graphs — intraprocedural (level 3) and interprocedural (level 4):**
|
|
526
|
+
```sh
|
|
527
|
+
canpy --input ./my-python-project -a 3 --output ./out # per-callable cfg/cdg/ddg
|
|
528
|
+
canpy --input ./my-python-project -a 4 --output ./out # + interprocedural SDG
|
|
529
|
+
canpy --input ./my-python-project -a 3 --graphs cfg,pdg # scope the emitted sections
|
|
530
|
+
canpy --input ./my-python-project -a 4 --graphs sdg # sdg requires -a 4
|
|
531
|
+
canpy --input ./my-python-project -a 3 --graph-field-depth 2 # tighter access-path k-limit
|
|
532
|
+
```
|
|
533
|
+
Levels 3 and 4 also enrich the Neo4j projection (`--emit neo4j`) with the CPG overlay
|
|
534
|
+
(`:PyCFGNode` nodes wired by `PY_CFG_NEXT`/`PY_CDG`/`PY_DDG`, plus the level-4
|
|
535
|
+
`PY_PARAM_IN`/`PY_PARAM_OUT`/`PY_SUMMARY` edges — the cross-language dataflow vocabulary,
|
|
536
|
+
PY_-namespaced like every other row family so multi-language databases never mingle
|
|
537
|
+
analyzers' edges).
|
|
538
|
+
|
|
539
|
+
## Analysis levels
|
|
540
|
+
|
|
541
|
+
Each level is the same tree grown one layer deeper, plus the edge family over that new layer. The
|
|
542
|
+
levels are cumulative and additive — `analysis.json(-a 1) ⊆ … ⊆ analysis.json(-a 4)`.
|
|
543
|
+
|
|
544
|
+
| Level | Flag | What it adds | Where it lands |
|
|
545
|
+
| --- | --- | --- | --- |
|
|
546
|
+
| **1** | `-a 1` (default) | Symbol table, Jedi call graph, and `call` nodes in each callable's `body` | `body` calls (`callee: null`) |
|
|
547
|
+
| **2** | `-a 2` | PyCG call-graph enrichment; each call's `callee` backfilled to a `can://` id | `call_graph`, `body` callees |
|
|
548
|
+
| **3** | `-a 3` | Native **intraprocedural** CFG/CDG/DDG (syntactic, name-equality, `prov: ["ssa"]`) | `cfg`, `cdg`, `ddg`, `@entry`/`@exit` on each callable |
|
|
549
|
+
| **4** | `-a 4` | **Interprocedural** SDG: synthetic param vertices, alias-aware DDG (`prov: ["points-to"]`) | `param_in`, `param_out`, `summary`, semantic `ddg` |
|
|
550
|
+
|
|
551
|
+
`-a 1`/`-a 2` timings and output are unaffected by the heavier levels — nothing at level 3+ runs
|
|
552
|
+
unless requested. Flag gating: `--graphs sdg` requires `-a 4`; `--graphs cfg,dfg,pdg` and
|
|
553
|
+
`--graph-field-depth` require `-a 3`.
|
|
554
|
+
|
|
555
|
+
## Architecture & Tooling
|
|
556
|
+
|
|
557
|
+
The dataflow substrate is hand-built from the standard library so every graph node joins back to a
|
|
558
|
+
symbol-table signature by construction
|
|
559
|
+
([#67](https://github.com/codellm-devkit/codeanalyzer-python/issues/67)):
|
|
560
|
+
|
|
561
|
+
- **CFG source:** a hand-built **exceptional** control-flow graph from the stdlib `ast` module — the
|
|
562
|
+
same parse the symbol-table builder uses. One synthetic `@entry`/`@exit` per callable,
|
|
563
|
+
statement-level nodes keyed `line:col` in source order, with exception / `yield` / `await` edges
|
|
564
|
+
first-class.
|
|
565
|
+
- **Def-use source:** hand-built **reaching definitions** (a classic forward worklist) over
|
|
566
|
+
k-limited access paths (`--graph-field-depth`, default 3) — there is no usable SSA library for
|
|
567
|
+
Python. This yields the level-3 syntactic DDG (name-equality, `prov: ["ssa"]`).
|
|
568
|
+
- **Points-to oracle (level 4):** the **Scalpel** may-alias oracle — `ScalpelAliasOracle`
|
|
569
|
+
(`codeanalyzer/dataflow/scalpel_oracle.py`) — consumes Scalpel's SSA copy/const facts to answer
|
|
570
|
+
`may_alias(path_a, path_b)`, adding the alias-aware DDG edges (`prov: ["points-to"]`) and the
|
|
571
|
+
interprocedural summaries. `python-scalpel` is an **optional dependency**
|
|
572
|
+
(`pip install 'codeanalyzer-python[scalpel]'`); when it is absent or cannot resolve a construct,
|
|
573
|
+
the analyzer automatically falls back to the built-in `TypeBasedAliasOracle` (Jedi-inferred types;
|
|
574
|
+
unknown types conservatively alias), keeping the `may_alias` interface total. Call dispatch comes
|
|
575
|
+
from the merged Jedi(+PyCG) call graph, treated as a frozen oracle.
|
|
576
|
+
- **Summaries:** relational formal-in → formal-out flows composed bottom-up over the Tarjan SCC
|
|
577
|
+
condensation of the call graph, a monotone fixpoint within SCCs; globals ride as extra formals,
|
|
578
|
+
closure captures bind at definition sites.
|
|
579
|
+
- **Slicing and taint are the SDK's responsibility.** A backward slicer ships in-process
|
|
580
|
+
(`codeanalyzer.dataflow.slicing`), but only as an **internal validation utility** for the L3/L4
|
|
581
|
+
gates — it is not a product surface. Once the SDG is emitted, slicing and taint become
|
|
582
|
+
language-independent labeled reachability and belong to the CLDK SDK across the provider/client
|
|
583
|
+
boundary; the analyzer emits the `summary` substrate and **no `taint_flows` section**.
|
|
584
|
+
- **Precision posture:** sound-leaning and over-approximate — prefer false positives to missed
|
|
585
|
+
flows. **Known unsoundness (documented, not silently absorbed):** `eval`/`exec`, reflection
|
|
586
|
+
(`getattr`/`setattr` with dynamic names), monkey-patching, C extensions, `import` side effects,
|
|
587
|
+
and module top-level statements (globals are modeled as formals instead).
|
|
588
|
+
|
|
589
|
+
## Output shape (canonical schema v2)
|
|
590
|
+
|
|
591
|
+
Every run produces the same envelope — an `Analysis` document — regardless of level; deeper levels
|
|
592
|
+
just populate more of the same tree:
|
|
474
593
|
|
|
475
|
-
|
|
594
|
+
```jsonc
|
|
595
|
+
{
|
|
596
|
+
"schema_version": "2.0.0",
|
|
597
|
+
"language": "python",
|
|
598
|
+
"max_level": 4, // the level this run was produced at
|
|
599
|
+
"k_limit": 3, // access-path depth bound (--graph-field-depth); L3+ only
|
|
600
|
+
"analyzer": { "name": "codeanalyzer-python", "version": "1.0.0" },
|
|
601
|
+
"application": {
|
|
602
|
+
"id": "can://python/<app>",
|
|
603
|
+
"kind": "application",
|
|
604
|
+
"symbol_table": { // relative POSIX path → module
|
|
605
|
+
"pkg/mod.py": {
|
|
606
|
+
"id": "can://python/<app>/pkg/mod.py",
|
|
607
|
+
"kind": "module",
|
|
608
|
+
"source": "…full file text, stored once per module…",
|
|
609
|
+
"types": { "<Class>": { "id": "…", "kind": "class", "callables": { /* methods */ } } },
|
|
610
|
+
"functions": { "<sig>": { /* callable, see below */ } }
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
"call_graph": [ { "src": "can://…/main(a)", "dst": "can://…/helper(x)",
|
|
614
|
+
"weight": 1, "prov": ["jedi", "pycg"] } ],
|
|
615
|
+
"external_symbols": { // imported/builtin call targets, keyed by id
|
|
616
|
+
"can://python/<app>/@external/os/getcwd":
|
|
617
|
+
{ "id": "can://python/<app>/@external/os/getcwd", "kind": "external",
|
|
618
|
+
"name": "getcwd", "module": "os" }
|
|
619
|
+
},
|
|
620
|
+
"param_in": [ { "src": "can://…/main(a)@6:4/actual_in:0", "dst": "can://…/helper(x)@formal_in:0" } ],
|
|
621
|
+
"param_out": [ { "src": "can://…/helper(x)@formal_out", "dst": "can://…/main(a)@6:4/actual_out" } ]
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
```
|
|
476
625
|
|
|
477
|
-
A
|
|
626
|
+
A **callable** (function or method) carries its own CPG, keyed by node id:
|
|
478
627
|
|
|
479
628
|
```jsonc
|
|
480
629
|
{
|
|
481
|
-
"
|
|
482
|
-
"
|
|
630
|
+
"id": "can://…/main(a)", "kind": "function",
|
|
631
|
+
"span": { "start": [5, 0], "end": [7, 12], "bytes": [43, 86] }, // byte offsets into module.source
|
|
632
|
+
"body": { // node id → node
|
|
633
|
+
"@entry": { "kind": "entry" },
|
|
634
|
+
"6:4": { "kind": "statement", "span": { … } },
|
|
635
|
+
"6:8": { "kind": "call", "span": { … }, "callee": "can://…/helper(x)" }, // callee null until L2
|
|
636
|
+
"@formal_in:0": { "kind": "formal_in", "of": "a" }, // L4 param vertices
|
|
637
|
+
"6:4/actual_in:0": { "kind": "actual_in", "of": "a", "parent": "6:4" },
|
|
638
|
+
"@exit": { "kind": "exit" }
|
|
639
|
+
},
|
|
640
|
+
"cfg": [ { "src": "@entry", "dst": "6:4", "kind": "fallthrough" } ], // L3
|
|
641
|
+
"cdg": [ { "src": "@entry", "dst": "6:4" } ], // L3
|
|
642
|
+
"ddg": [ { "src": "6:4", "dst": "7:4", "var": "h", "prov": ["ssa"] } ], // L3 ssa / L4 points-to
|
|
643
|
+
"summary": [ { "src": "6:4/actual_in:0", "dst": "6:4/actual_out" } ] // L4
|
|
483
644
|
}
|
|
484
645
|
```
|
|
485
646
|
|
|
486
|
-
|
|
487
|
-
|
|
647
|
+
Notable properties:
|
|
648
|
+
|
|
649
|
+
- **Durable `can://` ids** identify every node at callable granularity and above
|
|
650
|
+
(`can://python/<app>/<file>/<callable-sig>`); nodes below a callable use ordinal ids
|
|
651
|
+
(`@entry`, `@exit`, `line:col`, `@formal_in:N`, `line:col/actual_in:N`).
|
|
652
|
+
- **`source` lives once per module**; every node's text is the `module.source[span.bytes]` slice.
|
|
653
|
+
- **Cross-function edges** — `call_graph`, `param_in`, `param_out` — live at **application** scope;
|
|
654
|
+
the intraprocedural `cfg`/`cdg`/`ddg` and the `summary` edges live **on the callable**.
|
|
655
|
+
- **No dangling endpoints** — every `call_graph` `src`/`dst` joins the id space: declared
|
|
656
|
+
callables by their tree id, imported/builtin targets by a `…/@external/<module>/<name>` id
|
|
657
|
+
homed in `application.external_symbols`.
|
|
658
|
+
- **Breaking change from v1:** there is no more flat top-level `symbol_table`/`call_graph`, and no
|
|
659
|
+
separate program-graphs section. Everything now hangs off `application`, and the dataflow graphs
|
|
660
|
+
are inlined on each callable. Read `analysis.application.symbol_table` (was
|
|
661
|
+
`analysis.symbol_table`) and `analysis.application.call_graph` (was `analysis.call_graph`).
|
|
662
|
+
|
|
663
|
+
## Output targets
|
|
664
|
+
|
|
665
|
+
`canpy` builds one analysis in memory and can emit it three ways (`--emit`):
|
|
666
|
+
|
|
667
|
+
### `analysis.json` (default)
|
|
668
|
+
|
|
669
|
+
The `Analysis` envelope described above. By default it is printed to stdout as JSON; with `--output`
|
|
670
|
+
it is written to `analysis.json` (or `analysis.msgpack` with `--format msgpack`, a more compact
|
|
671
|
+
binary format).
|
|
488
672
|
|
|
489
673
|
### Neo4j graph
|
|
490
674
|
|
|
491
|
-
`--emit neo4j` projects the same analysis into a labeled property graph. Every node
|
|
492
|
-
`Py`-prefixed and every relationship type is `PY_`-prefixed (e.g. `:PyClass`, `PY_CALLS`)
|
|
493
|
-
language analyzers can share one database without label or relationship-type collisions.
|
|
494
|
-
are keyed by their
|
|
495
|
-
decorators, and call sites are relationships
|
|
675
|
+
`--emit neo4j` projects the same schema v2.0.0 analysis into a labeled property graph. Every node
|
|
676
|
+
label is `Py`-prefixed and every relationship type is `PY_`-prefixed (e.g. `:PyClass`, `PY_CALLS`)
|
|
677
|
+
so multiple language analyzers can share one database without label or relationship-type collisions.
|
|
678
|
+
Declarations are keyed by their **`can://` id** under a shared `:PySymbol` label; calls, imports,
|
|
679
|
+
inheritance, decorators, and call sites are relationships. At `-a 3`/`-a 4` the projection gains the
|
|
680
|
+
**CPG overlay** — `:PyCFGNode` nodes (statements, and at level 4 the parameter vertices) wired by
|
|
681
|
+
`PY_CFG_NEXT`/`PY_CDG`/`PY_DDG`, plus the level-4 `PY_PARAM_IN`/`PY_PARAM_OUT`/`PY_SUMMARY` edges:
|
|
496
682
|
|
|
497
683
|
- **Without `--neo4j-uri`** — writes a self-contained `graph.cypher` (constraints + indexes, a scoped
|
|
498
684
|
wipe, then batched `MERGE`s). Load it with `cypher-shell < graph.cypher`. Needs no extra
|
|
@@ -519,10 +705,11 @@ canpy -i ./my-project --emit neo4j # credentials picked up from the environm
|
|
|
519
705
|
### Schema contract
|
|
520
706
|
|
|
521
707
|
`--emit schema` writes the machine-readable, version-stamped Neo4j schema (`schema.json`: node labels,
|
|
522
|
-
relationships, properties, constraints, and indexes). It needs no
|
|
523
|
-
as `schema.neo4j.json` and bundled in every release as a GitHub
|
|
524
|
-
validate producer/consumer compatibility without invoking the tool.
|
|
525
|
-
the
|
|
708
|
+
relationships, properties, constraints, and indexes; currently `schema_version` `2.0.0`). It needs no
|
|
709
|
+
project and is checked into the repo as `schema.neo4j.json` and bundled in every release as a GitHub
|
|
710
|
+
Release asset, so a consumer can validate producer/consumer compatibility without invoking the tool.
|
|
711
|
+
The shape of the contract matches the
|
|
712
|
+
[`codeanalyzer-typescript`](https://github.com/codellm-devkit/codeanalyzer-typescript) backend.
|
|
526
713
|
|
|
527
714
|
A UML of the `analysis.json` schema (the `PyApplication` containment tree) is checked in as
|
|
528
715
|
[`schema-uml.drawio`](./schema-uml.drawio), and the property-graph schema as
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
codeanalyzer/__init__.py,sha256=BZ3Kuwl-F_F-8H8cepLnVJ4Ku4NNUjjqg0Y6ujPQSsI,108
|
|
2
|
+
codeanalyzer/__main__.py,sha256=_Bh-JbxaMJYwfRQplmfQDYivMWgwxsycbnO3FoRuDIg,14874
|
|
3
|
+
codeanalyzer/core.py,sha256=QGxfKq2ox1YLERx_5wr0xq74GSc-x4OpcVx7bgLyf7Y,35119
|
|
4
|
+
codeanalyzer/provenance.py,sha256=DT-DqwdVwO7g-GyK3sC0_4vDNCUjZYEY1fyYCt-7VRM,2306
|
|
5
|
+
codeanalyzer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
codeanalyzer/config/__init__.py,sha256=9XBxAn1oWGRuhg3bEBUuVGs3hFNXEAKrr-Ce7tq9a2k,61
|
|
7
|
+
codeanalyzer/config/config.py,sha256=ZiKzc5uEUCIvih58-6BDtLLI1hPij41wGQjBcj9KNQM,188
|
|
8
|
+
codeanalyzer/dataflow/__init__.py,sha256=HCNOH24rAGLUMG94N9aC-2SsAWSntk3uys7e39ipU5o,1725
|
|
9
|
+
codeanalyzer/dataflow/access_paths.py,sha256=9BUaupb9_jev3pYcRSBroKt5IDCRhpfssxWUWxwcEDM,22821
|
|
10
|
+
codeanalyzer/dataflow/alias.py,sha256=ZYKY0DiR3GHv6YJr7C001Lode9rZsk6-gxdomUyIvwg,3928
|
|
11
|
+
codeanalyzer/dataflow/builder.py,sha256=QhsezCYCEDw_hdFudD6aUoVah9nE0B7M4dQC3YU1agE,28248
|
|
12
|
+
codeanalyzer/dataflow/cfg.py,sha256=u5YXJjAaDshdgv-9kWWITbBJFP-MwXKq6hbNh0fhDm8,25307
|
|
13
|
+
codeanalyzer/dataflow/defuse.py,sha256=LrX1ToOZzR79NlAGg5T647UAFkpiEqEnT7t0K5RXOiI,4377
|
|
14
|
+
codeanalyzer/dataflow/dominance.py,sha256=X7Ki1QRdVqaseYsJtiUL7m9MbcC2WHEv9b8bczSLFUA,5086
|
|
15
|
+
codeanalyzer/dataflow/identity.py,sha256=jGuyPrtRL5jTfHBVr1aLA7SvJAAS9PyZhb5zouVv2yA,3905
|
|
16
|
+
codeanalyzer/dataflow/pdg.py,sha256=vVrdUrdKB-3Czf9N0wuuA1x_Bfj1KnyEvXhW6UefGcA,3488
|
|
17
|
+
codeanalyzer/dataflow/scalpel_oracle.py,sha256=f1lNtfTHPph0lABfuwMCt36htItLddwSCntfhsSVdd8,11573
|
|
18
|
+
codeanalyzer/dataflow/scc.py,sha256=AfnUCJ2-ZMkdoGL-ucUyb6903u26dfDkliF51KdP_3s,3501
|
|
19
|
+
codeanalyzer/dataflow/sdg.py,sha256=sTUUlYMB9uTKg9Yxiw-ScTBXogaQKcrSP6vksKXG14M,17841
|
|
20
|
+
codeanalyzer/dataflow/slicing.py,sha256=lWZ7jHhlR8m7rECWJQXvwfs5GNZeJY55Cud3Ji5UbNU,3654
|
|
21
|
+
codeanalyzer/dataflow/summaries.py,sha256=DOgesiymL6WgePrtkQBiS_Rd7SuCr3McD4Eq05Msb50,8023
|
|
22
|
+
codeanalyzer/dataflow/syntactic.py,sha256=AbHyXjKX_1xkGgKH48BpXYCWBauActGUwSMF_OD-uys,1124
|
|
23
|
+
codeanalyzer/jedi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
codeanalyzer/jedi/jedi.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
+
codeanalyzer/neo4j/__init__.py,sha256=8Ei6uThTOqmul6GhnKoGOjLXJZbyDwF5Jo-8I8NxnAk,1574
|
|
26
|
+
codeanalyzer/neo4j/bolt.py,sha256=0JHv6bHp2bsKwpFPT7CDzjDaz2GvGyoEaIKVtk_ayuA,10133
|
|
27
|
+
codeanalyzer/neo4j/cypher.py,sha256=a7YSmxxDbPcYc5gI4TW9sM6_WOE3RgLXTGBR9K8W8kw,5321
|
|
28
|
+
codeanalyzer/neo4j/emit.py,sha256=QdrZWG3_IQHMcKCX4bFINpXvqZU2Qfsi9beIJovC2p4,3493
|
|
29
|
+
codeanalyzer/neo4j/project.py,sha256=8MRLhlW0tW-hkGvTwifPonyfa0rDocCN2dzokUsFTKY,23620
|
|
30
|
+
codeanalyzer/neo4j/rows.py,sha256=med0XFa63PPUVUhJMq1TtVcJxgaVW39sgxBRZJDIJ_w,7811
|
|
31
|
+
codeanalyzer/neo4j/schema.py,sha256=SyjaME5z9vgBC70Aswksct8Z0Tiau1PNvbHoeT3JLxE,10582
|
|
32
|
+
codeanalyzer/options/__init__.py,sha256=Ki4qhHFqpyuUWVsntO-NYJMVWrkeFOzPW4nQ7oxiUVI,155
|
|
33
|
+
codeanalyzer/options/options.py,sha256=RLJ298wSnK0FdaN3xJ5Ha7Lj62jaV9UDlhhtb5vh4Ro,2162
|
|
34
|
+
codeanalyzer/schema/__init__.py,sha256=5bdJFV8icBDlTVHEoPumCFHBcq7O98-YNvuKs0qsqEs,2420
|
|
35
|
+
codeanalyzer/schema/assign_ids.py,sha256=pQHluLaO5a6hFREp3vjVRsUeAfTYSJ95RXS9TZvnoys,1590
|
|
36
|
+
codeanalyzer/schema/call_graph_ids.py,sha256=mWAhJDBsW8i-siJiINOHCXEm4qM6F_5se9-HuHWIFqU,568
|
|
37
|
+
codeanalyzer/schema/ids.py,sha256=G5NGPjJ3UdJ1k0opy2MrvgWRWNBZgKU1PwY1MR6hDT4,829
|
|
38
|
+
codeanalyzer/schema/l1_body.py,sha256=9dRyrhHU277MVWAEjumvATbIai2YkuRE8kXyjHD-evo,1365
|
|
39
|
+
codeanalyzer/schema/l2_callees.py,sha256=A4kX4Sm07O06-2CskqshP4QThdaTgCFHB6x3BarRvs8,1438
|
|
40
|
+
codeanalyzer/schema/py_schema.py,sha256=KesvWIODJTl-kVMt1fYvw9yVQNWA0tMsLApqmb9ZkLk,17479
|
|
41
|
+
codeanalyzer/semantic_analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
|
+
codeanalyzer/semantic_analysis/call_graph.py,sha256=PuR7dFTVrKanPatomYDJgkBaJq1Fd9920KspQpHQl1U,10957
|
|
43
|
+
codeanalyzer/semantic_analysis/pycg/__init__.py,sha256=Lsgz25iFM_RGGu_i2psY-LN-KwvYIZQPnKgRPL1JsjU,928
|
|
44
|
+
codeanalyzer/semantic_analysis/pycg/pycg_analysis.py,sha256=paUvKHqTNggo7c7-PTyBdDnPqa8g81_SlVDZbcquEzA,45109
|
|
45
|
+
codeanalyzer/semantic_analysis/pycg/pycg_exceptions.py,sha256=n4tRderrSYw9cUYgR2wsn64UqozY56nD1YfDNnaVcAk,968
|
|
46
|
+
codeanalyzer/semantic_analysis/pycg/shard_planner.py,sha256=7wz821fv18vojzOHFYhXB6MLE1KdU4kFaldbd2kZHt0,15837
|
|
47
|
+
codeanalyzer/syntactic_analysis/__init__.py,sha256=EUQkJEh6wHjWx2qTTKbTbUgwSbfKeNieKHNy7RknVXA,476
|
|
48
|
+
codeanalyzer/syntactic_analysis/exceptions.py,sha256=whs_n0vIu655Jkk1a7iOoXY6iIca4pZqJnU40V9Ejaw,537
|
|
49
|
+
codeanalyzer/syntactic_analysis/import_resolver.py,sha256=Q8noZwSdDNt4P4NMqDTB9FaS-M0_d4ASK9GnJop9MDI,2751
|
|
50
|
+
codeanalyzer/syntactic_analysis/symbol_table_builder.py,sha256=-fMkDcbMknkKTZrrYVfpF5CDqwK3TkhgkMxS4qN6WTk,42240
|
|
51
|
+
codeanalyzer/utils/__init__.py,sha256=hC6VWdR5rerSqBxzu9KQHTASWqwrrYJv-CMDwrTlzkc,137
|
|
52
|
+
codeanalyzer/utils/logging.py,sha256=Fw0tattPAOMs3o0JMjjXhRVLIF64f-SCcygUXF9jqeg,904
|
|
53
|
+
codeanalyzer/utils/progress_bar.py,sha256=C9JtzVdd10lIxTv-KA6PebqjKWueC_vMGwVzAtHuHIw,2818
|
|
54
|
+
codeanalyzer_python-1.0.0.dist-info/METADATA,sha256=vJ6d7LeO0LrqX7YL89TbS8J-YFB3Jm13-4NZPVYFBB0,46764
|
|
55
|
+
codeanalyzer_python-1.0.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
56
|
+
codeanalyzer_python-1.0.0.dist-info/entry_points.txt,sha256=v4Vux0Nnx7sOntVk_CH7W9RX6SkIkvR1FQYq73oVlCQ,105
|
|
57
|
+
codeanalyzer_python-1.0.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
58
|
+
codeanalyzer_python-1.0.0.dist-info/licenses/NOTICE,sha256=YU0Z9NDWqKY-2jfFcbxeZ6fbnzz0oZeKmnUcO8a-bcQ,901
|
|
59
|
+
codeanalyzer_python-1.0.0.dist-info/RECORD,,
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
codeanalyzer/__init__.py,sha256=BZ3Kuwl-F_F-8H8cepLnVJ4Ku4NNUjjqg0Y6ujPQSsI,108
|
|
2
|
-
codeanalyzer/__main__.py,sha256=GOGc6j-euSeRZbUQgfZykQClT32WNxcStKoO96s-VSY,12790
|
|
3
|
-
codeanalyzer/core.py,sha256=BCH5OynbXrnItPvYhcpAChcBtkXdQIMbH_fyFGCxITU,31185
|
|
4
|
-
codeanalyzer/provenance.py,sha256=DT-DqwdVwO7g-GyK3sC0_4vDNCUjZYEY1fyYCt-7VRM,2306
|
|
5
|
-
codeanalyzer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
codeanalyzer/config/__init__.py,sha256=9XBxAn1oWGRuhg3bEBUuVGs3hFNXEAKrr-Ce7tq9a2k,61
|
|
7
|
-
codeanalyzer/config/config.py,sha256=ZiKzc5uEUCIvih58-6BDtLLI1hPij41wGQjBcj9KNQM,188
|
|
8
|
-
codeanalyzer/jedi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
codeanalyzer/jedi/jedi.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
codeanalyzer/neo4j/__init__.py,sha256=8Ei6uThTOqmul6GhnKoGOjLXJZbyDwF5Jo-8I8NxnAk,1574
|
|
11
|
-
codeanalyzer/neo4j/bolt.py,sha256=-IFDb_d67IkGwxvmmbLNI6AvSQHY-XsutI3szibWidw,9635
|
|
12
|
-
codeanalyzer/neo4j/cypher.py,sha256=2zIWXA1AADrwCMhSTeqKjEXRgBjbob6o3bme_cwLu0s,5024
|
|
13
|
-
codeanalyzer/neo4j/emit.py,sha256=NZL5BVY1Fb32igH22986_cUFUIgHNWJHbii2bfX2E3M,3099
|
|
14
|
-
codeanalyzer/neo4j/project.py,sha256=YMVtF1GjLZYwnhFl-7fy9Qj7fpGgd5aEGOp0JMF3VAY,14513
|
|
15
|
-
codeanalyzer/neo4j/rows.py,sha256=5xI3X-l-vwPe_gmKYUg7VuUQARcOlVAZnGmiQr9QyRk,7326
|
|
16
|
-
codeanalyzer/neo4j/schema.py,sha256=tZjnpIFdTV3-GB1x2zTbDMxQOH16GqELYCf2cR2XhgU,8765
|
|
17
|
-
codeanalyzer/options/__init__.py,sha256=Ki4qhHFqpyuUWVsntO-NYJMVWrkeFOzPW4nQ7oxiUVI,155
|
|
18
|
-
codeanalyzer/options/options.py,sha256=5w3DZYlAv-0LVPavF1P5EEJp8XBRrSXidbDusdbXQAo,1976
|
|
19
|
-
codeanalyzer/schema/__init__.py,sha256=cLPjvowrnz8xzi7tZAsKQeIOjdOKRGHy4I7wbG0jHk8,2024
|
|
20
|
-
codeanalyzer/schema/py_schema.py,sha256=yYKkg3ufNDteNvtiPiRJQz-pzK5NnnnNldhXcW7p2RI,13274
|
|
21
|
-
codeanalyzer/semantic_analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
-
codeanalyzer/semantic_analysis/call_graph.py,sha256=H3IkfGp1VCkDxI75JPyLSSA-wOaJp_I-NYPsjCY04Bg,11185
|
|
23
|
-
codeanalyzer/semantic_analysis/pycg/__init__.py,sha256=Lsgz25iFM_RGGu_i2psY-LN-KwvYIZQPnKgRPL1JsjU,928
|
|
24
|
-
codeanalyzer/semantic_analysis/pycg/pycg_analysis.py,sha256=22kiZ2Kjpds024iFLu2bPEcCtAKAivdp1Xlr9rfP9lM,45205
|
|
25
|
-
codeanalyzer/semantic_analysis/pycg/pycg_exceptions.py,sha256=n4tRderrSYw9cUYgR2wsn64UqozY56nD1YfDNnaVcAk,968
|
|
26
|
-
codeanalyzer/semantic_analysis/pycg/shard_planner.py,sha256=PTXMG9YcrZX9hgnlnIlpQx6xmWkizP3aQrf0elcrnmw,15865
|
|
27
|
-
codeanalyzer/syntactic_analysis/__init__.py,sha256=EUQkJEh6wHjWx2qTTKbTbUgwSbfKeNieKHNy7RknVXA,476
|
|
28
|
-
codeanalyzer/syntactic_analysis/exceptions.py,sha256=whs_n0vIu655Jkk1a7iOoXY6iIca4pZqJnU40V9Ejaw,537
|
|
29
|
-
codeanalyzer/syntactic_analysis/import_resolver.py,sha256=Q8noZwSdDNt4P4NMqDTB9FaS-M0_d4ASK9GnJop9MDI,2751
|
|
30
|
-
codeanalyzer/syntactic_analysis/symbol_table_builder.py,sha256=hQOEk2qRYM9Y_UoejNbZFCWSkDjBa-vRcU3kPURrMNc,41148
|
|
31
|
-
codeanalyzer/utils/__init__.py,sha256=hC6VWdR5rerSqBxzu9KQHTASWqwrrYJv-CMDwrTlzkc,137
|
|
32
|
-
codeanalyzer/utils/logging.py,sha256=Fw0tattPAOMs3o0JMjjXhRVLIF64f-SCcygUXF9jqeg,904
|
|
33
|
-
codeanalyzer/utils/progress_bar.py,sha256=C9JtzVdd10lIxTv-KA6PebqjKWueC_vMGwVzAtHuHIw,2818
|
|
34
|
-
codeanalyzer_python-0.3.1.dist-info/METADATA,sha256=F-I41TfuL_typkjLjnjSxR8fPvUu1VgbAX8K1_vnm80,34032
|
|
35
|
-
codeanalyzer_python-0.3.1.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
36
|
-
codeanalyzer_python-0.3.1.dist-info/entry_points.txt,sha256=v4Vux0Nnx7sOntVk_CH7W9RX6SkIkvR1FQYq73oVlCQ,105
|
|
37
|
-
codeanalyzer_python-0.3.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
38
|
-
codeanalyzer_python-0.3.1.dist-info/licenses/NOTICE,sha256=YU0Z9NDWqKY-2jfFcbxeZ6fbnzz0oZeKmnUcO8a-bcQ,901
|
|
39
|
-
codeanalyzer_python-0.3.1.dist-info/RECORD,,
|
|
File without changes
|
{codeanalyzer_python-0.3.1.dist-info → codeanalyzer_python-1.0.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|