codeanalyzer-python 1.3.0__tar.gz → 1.4.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.3.0 → codeanalyzer_python-1.4.0}/PKG-INFO +21 -15
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/README.md +20 -14
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/__main__.py +7 -13
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/artifacts/dependencies.py +3 -3
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/artifacts/discovery.py +8 -34
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/core.py +3 -4
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/builder.py +7 -2
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/sdg.py +17 -2
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/summaries.py +27 -4
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/neo4j/bolt.py +35 -6
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/neo4j/emit.py +3 -2
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/neo4j/project.py +0 -1
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/neo4j/schema.py +18 -1
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/options/options.py +2 -3
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/schema/py_schema.py +2 -3
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/pyproject.toml +1 -1
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/schema.neo4j.json +7 -2
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/.gitignore +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/LICENSE +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/NOTICE +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/artifacts/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/artifacts/config_keys.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/artifacts/config_use.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/artifacts/config_use_rules.yml +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/artifacts/parsers.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/access_paths.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/alias.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/cfg.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/defuse.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/dominance.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/identity.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/pdg.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/LICENSE +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/README.md +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/SSA/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/SSA/const.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/cfg/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/cfg/builder.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/cfg/model.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/core/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/core/func_call_visitor.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/core/vars_visitor.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel_oracle.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scc.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/slicing.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/syntactic.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/entrypoints/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/entrypoints/detect.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/entrypoints/matching.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/entrypoints/pipeline.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/entrypoints/rules.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/entrypoints/rules.yml +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/jedi/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/jedi/jedi.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/neo4j/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/neo4j/cypher.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/neo4j/rows.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/options/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/provenance.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/py.typed +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/schema/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/schema/assign_ids.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/schema/call_graph_ids.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/schema/ids.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/schema/l1_body.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/schema/l2_callees.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/semantic_analysis/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/semantic_analysis/call_graph.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/semantic_analysis/defuse_linker.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/syntactic_analysis/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/syntactic_analysis/exceptions.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/syntactic_analysis/import_resolver.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/syntactic_analysis/symbol_table_builder.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/utils/__init__.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/utils/logging.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/utils/progress_bar.py +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/docs/handoff/README.md +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/docs/handoff/schema.neo4j.json +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/schema-uml.drawio +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/flask/README.md +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/flask/examples/celery/README.md +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/flask/requirements-skip/README.md +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/flask/src/flask/sansio/README.md +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/requests/LICENSE +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/requests/NOTICE +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/requests/README.md +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/requests/docs/_themes/LICENSE +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/requests/ext/LICENSE +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/xarray/LICENSE +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/xarray/README.md +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/xarray/properties/README.md +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/xarray/xarray/datatree_/LICENSE +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/xarray/xarray/datatree_/README.md +0 -0
- {codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/test/fixtures/whole_applications/xarray/xarray/datatree_/docs/README.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: codeanalyzer-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
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
|
|
@@ -99,7 +99,9 @@ needs.
|
|
|
99
99
|
**interprocedural SDG** (synthetic parameter vertices, `param_in`/`param_out`/`summary`,
|
|
100
100
|
alias-aware DDG) at level 4 — all built in-process from the stdlib `ast`.
|
|
101
101
|
- **Neo4j output** — project the analysis into a labeled property graph: a self-contained
|
|
102
|
-
`graph.cypher` snapshot, or an **incremental** push to a live database over Bolt.
|
|
102
|
+
`graph.cypher` snapshot, or an **incremental** push to a live database over Bolt. A push is
|
|
103
|
+
**additive by default** and never deletes: `--eager` is what permits it to remove declarations
|
|
104
|
+
and edges the source no longer has.
|
|
103
105
|
- **Versioned schema** — a machine-readable, version-stamped Neo4j schema contract (`--emit schema`),
|
|
104
106
|
checked in as `schema.neo4j.json` (`2.0.0`) and shipped with every release.
|
|
105
107
|
- **Incremental cache** — per-file results are cached under `.codeanalyzer`; `--lazy` (default)
|
|
@@ -300,7 +302,19 @@ $ canpy --help
|
|
|
300
302
|
│ --eager --lazy Enable eager or │
|
|
301
303
|
│ lazy analysis. │
|
|
302
304
|
│ Defaults to │
|
|
303
|
-
│ lazy.
|
|
305
|
+
│ lazy. Also gates │
|
|
306
|
+
│ every │
|
|
307
|
+
│ destructive step │
|
|
308
|
+
│ of a '--emit │
|
|
309
|
+
│ neo4j' Bolt │
|
|
310
|
+
│ push: a lazy │
|
|
311
|
+
│ push only adds │
|
|
312
|
+
│ and updates, an │
|
|
313
|
+
│ eager one also │
|
|
314
|
+
│ removes │
|
|
315
|
+
│ declarations and │
|
|
316
|
+
│ edges the source │
|
|
317
|
+
│ no longer has. │
|
|
304
318
|
│ [default: lazy] │
|
|
305
319
|
│ --skip-tests --include-tests Skip test files │
|
|
306
320
|
│ in analysis. │
|
|
@@ -361,26 +375,18 @@ $ canpy --help
|
|
|
361
375
|
│ `source` text on │
|
|
362
376
|
│ discovered │
|
|
363
377
|
│ artifacts. │
|
|
378
|
+
│ `source` is the │
|
|
379
|
+
│ whole file; │
|
|
364
380
|
│ --no-artifact-t… │
|
|
365
|
-
│ empties
|
|
381
|
+
│ empties it │
|
|
366
382
|
│ everywhere │
|
|
367
383
|
│ (inventory │
|
|
368
384
|
│ unchanged). │
|
|
369
|
-
│ [default: │
|
|
370
|
-
│ artifact-text] │
|
|
371
|
-
│ --artifact-text-… <int range> Per-file byte │
|
|
372
|
-
│ [x>=1] cap on captured │
|
|
373
|
-
│ artifact │
|
|
374
|
-
│ `source`; a │
|
|
375
|
-
│ decodable file │
|
|
376
|
-
│ over the cap is │
|
|
377
|
-
│ truncated │
|
|
378
|
-
│ (text_truncated… │
|
|
379
385
|
│ sha256/size_byt… │
|
|
380
386
|
│ always reflect │
|
|
381
387
|
│ the full file. │
|
|
382
388
|
│ [default: │
|
|
383
|
-
│
|
|
389
|
+
│ artifact-text] │
|
|
384
390
|
│ --help Show this │
|
|
385
391
|
│ message and │
|
|
386
392
|
│ exit. │
|
|
@@ -66,7 +66,9 @@ needs.
|
|
|
66
66
|
**interprocedural SDG** (synthetic parameter vertices, `param_in`/`param_out`/`summary`,
|
|
67
67
|
alias-aware DDG) at level 4 — all built in-process from the stdlib `ast`.
|
|
68
68
|
- **Neo4j output** — project the analysis into a labeled property graph: a self-contained
|
|
69
|
-
`graph.cypher` snapshot, or an **incremental** push to a live database over Bolt.
|
|
69
|
+
`graph.cypher` snapshot, or an **incremental** push to a live database over Bolt. A push is
|
|
70
|
+
**additive by default** and never deletes: `--eager` is what permits it to remove declarations
|
|
71
|
+
and edges the source no longer has.
|
|
70
72
|
- **Versioned schema** — a machine-readable, version-stamped Neo4j schema contract (`--emit schema`),
|
|
71
73
|
checked in as `schema.neo4j.json` (`2.0.0`) and shipped with every release.
|
|
72
74
|
- **Incremental cache** — per-file results are cached under `.codeanalyzer`; `--lazy` (default)
|
|
@@ -267,7 +269,19 @@ $ canpy --help
|
|
|
267
269
|
│ --eager --lazy Enable eager or │
|
|
268
270
|
│ lazy analysis. │
|
|
269
271
|
│ Defaults to │
|
|
270
|
-
│ lazy.
|
|
272
|
+
│ lazy. Also gates │
|
|
273
|
+
│ every │
|
|
274
|
+
│ destructive step │
|
|
275
|
+
│ of a '--emit │
|
|
276
|
+
│ neo4j' Bolt │
|
|
277
|
+
│ push: a lazy │
|
|
278
|
+
│ push only adds │
|
|
279
|
+
│ and updates, an │
|
|
280
|
+
│ eager one also │
|
|
281
|
+
│ removes │
|
|
282
|
+
│ declarations and │
|
|
283
|
+
│ edges the source │
|
|
284
|
+
│ no longer has. │
|
|
271
285
|
│ [default: lazy] │
|
|
272
286
|
│ --skip-tests --include-tests Skip test files │
|
|
273
287
|
│ in analysis. │
|
|
@@ -328,26 +342,18 @@ $ canpy --help
|
|
|
328
342
|
│ `source` text on │
|
|
329
343
|
│ discovered │
|
|
330
344
|
│ artifacts. │
|
|
345
|
+
│ `source` is the │
|
|
346
|
+
│ whole file; │
|
|
331
347
|
│ --no-artifact-t… │
|
|
332
|
-
│ empties
|
|
348
|
+
│ empties it │
|
|
333
349
|
│ everywhere │
|
|
334
350
|
│ (inventory │
|
|
335
351
|
│ unchanged). │
|
|
336
|
-
│ [default: │
|
|
337
|
-
│ artifact-text] │
|
|
338
|
-
│ --artifact-text-… <int range> Per-file byte │
|
|
339
|
-
│ [x>=1] cap on captured │
|
|
340
|
-
│ artifact │
|
|
341
|
-
│ `source`; a │
|
|
342
|
-
│ decodable file │
|
|
343
|
-
│ over the cap is │
|
|
344
|
-
│ truncated │
|
|
345
|
-
│ (text_truncated… │
|
|
346
352
|
│ sha256/size_byt… │
|
|
347
353
|
│ always reflect │
|
|
348
354
|
│ the full file. │
|
|
349
355
|
│ [default: │
|
|
350
|
-
│
|
|
356
|
+
│ artifact-text] │
|
|
351
357
|
│ --help Show this │
|
|
352
358
|
│ message and │
|
|
353
359
|
│ exit. │
|
|
@@ -175,7 +175,10 @@ def main(
|
|
|
175
175
|
bool,
|
|
176
176
|
typer.Option(
|
|
177
177
|
"--eager/--lazy",
|
|
178
|
-
help="Enable eager or lazy analysis. Defaults to lazy."
|
|
178
|
+
help="Enable eager or lazy analysis. Defaults to lazy. Also gates every "
|
|
179
|
+
"destructive step of a '--emit neo4j' Bolt push: a lazy push only adds and "
|
|
180
|
+
"updates, an eager one also removes declarations and edges the source no "
|
|
181
|
+
"longer has.",
|
|
179
182
|
),
|
|
180
183
|
] = False,
|
|
181
184
|
skip_tests: Annotated[
|
|
@@ -239,19 +242,11 @@ def main(
|
|
|
239
242
|
typer.Option(
|
|
240
243
|
"--artifact-text/--no-artifact-text",
|
|
241
244
|
help="Capture verbatim `source` text on discovered artifacts. "
|
|
242
|
-
"--no-artifact-text empties
|
|
245
|
+
"`source` is the whole file; --no-artifact-text empties it "
|
|
246
|
+
"everywhere (inventory unchanged). sha256/size_bytes always "
|
|
247
|
+
"reflect the full file.",
|
|
243
248
|
),
|
|
244
249
|
] = True,
|
|
245
|
-
artifact_text_max_bytes: Annotated[
|
|
246
|
-
int,
|
|
247
|
-
typer.Option(
|
|
248
|
-
"--artifact-text-max-bytes",
|
|
249
|
-
help="Per-file byte cap on captured artifact `source`; a decodable "
|
|
250
|
-
"file over the cap is truncated (text_truncated=True). "
|
|
251
|
-
"sha256/size_bytes always reflect the full file.",
|
|
252
|
-
min=1,
|
|
253
|
-
),
|
|
254
|
-
] = 262144,
|
|
255
250
|
):
|
|
256
251
|
# Determinism: pin the interpreter hash seed before any analysis (no-op
|
|
257
252
|
# when PYTHONHASHSEED is already set; --version exits before this).
|
|
@@ -336,7 +331,6 @@ def main(
|
|
|
336
331
|
verbosity=verbosity,
|
|
337
332
|
entrypoint_rules=tuple(entrypoint_rules or ()),
|
|
338
333
|
artifact_text=artifact_text,
|
|
339
|
-
artifact_text_max_bytes=artifact_text_max_bytes,
|
|
340
334
|
)
|
|
341
335
|
|
|
342
336
|
_set_log_level(options.verbosity)
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/artifacts/dependencies.py
RENAMED
|
@@ -81,9 +81,9 @@ def _resolve_ref(manifest_path: str, ref: str) -> Optional[str]:
|
|
|
81
81
|
|
|
82
82
|
def _full_text(project_dir: Path, path: str, art: PyArtifact) -> str:
|
|
83
83
|
"""Manifest/lock extraction must never depend on the stored ``source`` --
|
|
84
|
-
that's
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
that's emptied by ``capture_text=False`` (a payload-size control on the
|
|
85
|
+
JSON/Neo4j payload, not an extraction control). Read the real file fresh
|
|
86
|
+
instead; fall back to ``art.source``
|
|
87
87
|
only if it is gone (e.g. a synthetic artifact in a unit test, or the file
|
|
88
88
|
vanished mid-run).
|
|
89
89
|
|
|
@@ -76,27 +76,11 @@ def _classify(rel_posix: str) -> Tuple[str, List[str]] | None:
|
|
|
76
76
|
return None
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
def _capture_source(
|
|
80
|
-
raw: bytes, text: str, capture_text: bool, text_max_bytes: int
|
|
81
|
-
) -> Tuple[str, bool]:
|
|
82
|
-
"""Decide ``(source, text_truncated)`` for a decodable file.
|
|
83
|
-
|
|
84
|
-
Slices ``raw`` (not ``text``) for the cap, so it is a true byte cap even
|
|
85
|
-
when it lands inside a multi-byte character -- ``errors="ignore"`` drops
|
|
86
|
-
the dangling partial char at the cut, so this never raises."""
|
|
87
|
-
if not capture_text:
|
|
88
|
-
return "", False
|
|
89
|
-
if len(raw) <= text_max_bytes:
|
|
90
|
-
return text, False
|
|
91
|
-
return raw[:text_max_bytes].decode("utf-8", errors="ignore"), True
|
|
92
|
-
|
|
93
|
-
|
|
94
79
|
def discover_artifacts(
|
|
95
80
|
project_dir: Path,
|
|
96
81
|
app_name: str,
|
|
97
82
|
*,
|
|
98
83
|
capture_text: bool = True,
|
|
99
|
-
text_max_bytes: int = 262144,
|
|
100
84
|
) -> Dict[str, PyArtifact]:
|
|
101
85
|
"""Walk the project and return every file as an artifact, sorted by path.
|
|
102
86
|
|
|
@@ -109,15 +93,11 @@ def discover_artifacts(
|
|
|
109
93
|
deliberate exception -- it IS rule-matched (a dependency-manifest), so it
|
|
110
94
|
is captured like any other manifest despite the `.py` suffix.
|
|
111
95
|
|
|
112
|
-
``
|
|
113
|
-
|
|
114
|
-
``source``
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
parses, not bulk/incidental content, so the byte cap does not apply to
|
|
118
|
-
it (``capture_text=False`` still empties it like everything else).
|
|
119
|
-
``sha256``/``size_bytes`` always reflect the full file regardless of
|
|
120
|
-
either knob."""
|
|
96
|
+
``source`` is the WHOLE file or nothing -- never a prefix (#172). A
|
|
97
|
+
decodable file is captured in full; ``capture_text=False`` empties
|
|
98
|
+
``source`` everywhere (inventory otherwise identical), and an undecodable
|
|
99
|
+
file gets ``""`` as ``binary``. ``sha256``/``size_bytes`` always reflect
|
|
100
|
+
the full file regardless."""
|
|
121
101
|
out: Dict[str, PyArtifact] = {}
|
|
122
102
|
for path in sorted(project_dir.rglob("*")):
|
|
123
103
|
if not path.is_file():
|
|
@@ -148,20 +128,14 @@ def discover_artifacts(
|
|
|
148
128
|
if decodable and "." not in name and text.startswith("#!"):
|
|
149
129
|
roles = ["script"]
|
|
150
130
|
if decodable:
|
|
151
|
-
|
|
152
|
-
# dependency_view parses it) -- the byte cap targets bulk/incidental
|
|
153
|
-
# assets, never the files extraction depends on, so manifests are
|
|
154
|
-
# exempt from it. capture_text=False still empties source (handled
|
|
155
|
-
# inside _capture_source); only the byte CAP is bypassed here.
|
|
156
|
-
cap = len(raw) if "dependency-manifest" in roles else text_max_bytes
|
|
157
|
-
source, text_truncated = _capture_source(raw, text, capture_text, cap)
|
|
131
|
+
source = text if capture_text else ""
|
|
158
132
|
else:
|
|
159
|
-
fmt, source
|
|
133
|
+
fmt, source = "binary", ""
|
|
160
134
|
|
|
161
135
|
out[rel_posix] = PyArtifact(
|
|
162
136
|
id=artifact_id(app_name, rel_posix), path=rel_posix, format=fmt,
|
|
163
137
|
roles=list(roles), size_bytes=len(raw),
|
|
164
138
|
sha256=hashlib.sha256(raw).hexdigest(),
|
|
165
|
-
source=source,
|
|
139
|
+
source=source,
|
|
166
140
|
)
|
|
167
141
|
return out
|
|
@@ -40,9 +40,9 @@ from codeanalyzer.provenance import analyzer_info, repository_info
|
|
|
40
40
|
def _artifact_full_text(project_dir: Path, path: str, art) -> str:
|
|
41
41
|
"""Mirrors ``artifacts.dependencies._full_text`` verbatim (not imported
|
|
42
42
|
-- that name is module-private to ``dependencies.py``): config-key
|
|
43
|
-
extraction (#152) must never depend on the stored ``source`` --
|
|
44
|
-
by ``
|
|
45
|
-
|
|
43
|
+
extraction (#152) must never depend on the stored ``source`` -- emptied
|
|
44
|
+
by ``capture_text=False`` (a payload-size control, not an extraction
|
|
45
|
+
control). Read the real file fresh instead;
|
|
46
46
|
fall back to ``art.source`` only if it's gone (e.g. a synthetic artifact
|
|
47
47
|
in a unit test, or the file vanished mid-run). Keep the two in sync if
|
|
48
48
|
this logic changes."""
|
|
@@ -673,7 +673,6 @@ class Codeanalyzer:
|
|
|
673
673
|
app.artifacts = discover_artifacts(
|
|
674
674
|
self.project_dir, app_name,
|
|
675
675
|
capture_text=self.options.artifact_text,
|
|
676
|
-
text_max_bytes=self.options.artifact_text_max_bytes,
|
|
677
676
|
)
|
|
678
677
|
app.dependencies, app.unresolved_imports = build_dependency_view(
|
|
679
678
|
app.artifacts,
|
|
@@ -442,8 +442,13 @@ def build_program_graphs(
|
|
|
442
442
|
for t in cs.targets:
|
|
443
443
|
call_edges.append((sig, t))
|
|
444
444
|
|
|
445
|
-
|
|
446
|
-
|
|
445
|
+
# The converged (facts, ddg) per function are threaded straight into the
|
|
446
|
+
# assembler rather than re-derived there (#155).
|
|
447
|
+
solutions: Dict[str, object] = {}
|
|
448
|
+
summaries = compute_summaries(
|
|
449
|
+
infos, sorted(set(call_edges)), solutions=solutions
|
|
450
|
+
)
|
|
451
|
+
return assemble_sdg(infos, summaries, k, solutions=solutions)
|
|
447
452
|
|
|
448
453
|
|
|
449
454
|
def emit_l4(
|
|
@@ -386,8 +386,18 @@ def assemble_sdg(
|
|
|
386
386
|
infos: Dict[str, FunctionInfo],
|
|
387
387
|
summaries: Dict[str, FunctionSummary],
|
|
388
388
|
k: int,
|
|
389
|
+
*,
|
|
390
|
+
solutions: Optional[Dict[str, Tuple[Dict[int, object], List[object]]]] = None,
|
|
389
391
|
) -> ProgramGraphsIR:
|
|
390
|
-
"""Stitch every function's PDG into the whole-program SDG.
|
|
392
|
+
"""Stitch every function's PDG into the whole-program SDG.
|
|
393
|
+
|
|
394
|
+
*solutions* optionally carries the converged ``(facts, ddg)`` that
|
|
395
|
+
:func:`~codeanalyzer.dataflow.summaries.compute_summaries` already
|
|
396
|
+
derived, sparing a second identical solve per function (#155). Omit it and
|
|
397
|
+
every function is re-solved, which is the historical behaviour and the
|
|
398
|
+
right posture whenever *summaries* did not come from an immediately
|
|
399
|
+
preceding run over these same *infos*.
|
|
400
|
+
"""
|
|
391
401
|
ir = ProgramGraphsIR(k_limit=k)
|
|
392
402
|
|
|
393
403
|
# Pass 1: solve each function against the final summaries and lay out its
|
|
@@ -396,7 +406,12 @@ def assemble_sdg(
|
|
|
396
406
|
formal_ids: Dict[str, Dict[str, int]] = {}
|
|
397
407
|
for sig in sorted(infos):
|
|
398
408
|
info = infos[sig]
|
|
399
|
-
|
|
409
|
+
cached = solutions.get(sig) if solutions is not None else None
|
|
410
|
+
if cached is None:
|
|
411
|
+
summary, facts, ddg = solve_function(info, summaries)
|
|
412
|
+
else:
|
|
413
|
+
facts, ddg = cached
|
|
414
|
+
summary = summaries[sig]
|
|
400
415
|
asm = _FunctionAssembler(info, summary, facts, ddg)
|
|
401
416
|
asm.build_formals()
|
|
402
417
|
assemblers[sig] = asm
|
|
@@ -199,19 +199,42 @@ def solve_function(
|
|
|
199
199
|
def compute_summaries(
|
|
200
200
|
infos: Dict[str, FunctionInfo],
|
|
201
201
|
call_edges: List[Tuple[str, str]],
|
|
202
|
+
*,
|
|
203
|
+
solutions: Optional[Dict[str, Tuple[Dict[int, object], List[DDGEdge]]]] = None,
|
|
202
204
|
) -> Dict[str, FunctionSummary]:
|
|
203
205
|
"""Bottom-up composition over the SCC condensation DAG, monotone fixpoint
|
|
204
|
-
within each SCC.
|
|
206
|
+
within each SCC.
|
|
207
|
+
|
|
208
|
+
A **singleton SCC with no self-edge** is solved exactly once: the
|
|
209
|
+
condensation is processed bottom-up, so every callee summary it reads is
|
|
210
|
+
already final and a second pass could only recompute the same answer to
|
|
211
|
+
observe that nothing changed. Genuinely recursive SCCs (several members,
|
|
212
|
+
or one member calling itself) still iterate to fixpoint.
|
|
213
|
+
|
|
214
|
+
When *solutions* is supplied it receives each signature's converged
|
|
215
|
+
``(facts, ddg)`` — the by-products of the final solve, which
|
|
216
|
+
:func:`~codeanalyzer.dataflow.sdg.assemble_sdg` would otherwise recompute
|
|
217
|
+
from scratch. They are the same values that a fresh solve against the
|
|
218
|
+
final summaries produces, because a converged pass is by definition one
|
|
219
|
+
in which no member's summary changed (#155).
|
|
220
|
+
"""
|
|
205
221
|
order = strongly_connected_components(sorted(infos), call_edges)
|
|
222
|
+
self_calls = {src for src, dst in call_edges if src == dst}
|
|
206
223
|
summaries: Dict[str, FunctionSummary] = {}
|
|
207
224
|
for scc in order:
|
|
208
225
|
members = [s for s in scc if s in infos]
|
|
209
|
-
|
|
210
|
-
|
|
226
|
+
if not members:
|
|
227
|
+
continue
|
|
228
|
+
recursive = len(members) > 1 or members[0] in self_calls
|
|
229
|
+
while True:
|
|
211
230
|
changed = False
|
|
212
231
|
for sig in members:
|
|
213
|
-
new,
|
|
232
|
+
new, facts, ddg = solve_function(infos[sig], summaries)
|
|
233
|
+
if solutions is not None:
|
|
234
|
+
solutions[sig] = (facts, ddg)
|
|
214
235
|
if summaries.get(sig) != new:
|
|
215
236
|
summaries[sig] = new
|
|
216
237
|
changed = True
|
|
238
|
+
if not (recursive and changed):
|
|
239
|
+
break
|
|
217
240
|
return summaries
|
|
@@ -27,11 +27,24 @@ Algorithm (the module subgraph is the unit of idempotent replacement):
|
|
|
27
27
|
4. upsert edges owned by changed modules (+ the shared edges).
|
|
28
28
|
5. on a FULL run only, prune modules whose source file vanished.
|
|
29
29
|
|
|
30
|
+
**A push never deletes by default** (#171). Steps 3 and 5 are the only destructive
|
|
31
|
+
ones and both run on ``eager`` (``--eager``) only; a default ``--lazy`` push is purely
|
|
32
|
+
additive — MERGE-upsert of nodes and edges, nothing removed. The cost of the default is
|
|
33
|
+
staleness: a declaration or a call edge the source no longer has stays in the graph until
|
|
34
|
+
an ``--eager`` push reconciles it. That is the deliberate trade — an incremental push into
|
|
35
|
+
a shared database should not be able to destroy anything, and the destructive rebuild is
|
|
36
|
+
opt-in under the same flag that already forces a clean analysis rebuild.
|
|
37
|
+
|
|
30
38
|
Nodes are MERGE-upserted, never blindly deleted, so a declaration another
|
|
31
39
|
(unchanged) module still references survives and its incoming edges stay valid.
|
|
32
40
|
``:PyExternal`` / ``:PyPackage`` / ``:PyDecorator`` are shared (no ``_module``) and are
|
|
33
41
|
MERGE-only.
|
|
34
42
|
|
|
43
|
+
Every ``_module`` match is anchored on the python-owned labels
|
|
44
|
+
(``schema.MODULE_OWNED_PATTERN``). ``_module`` is a shared convention, not a python-private
|
|
45
|
+
one -- codeanalyzer-java and codeanalyzer-typescript set it on their nodes too -- so an
|
|
46
|
+
unlabelled match reaches a sibling analyzer's graph in a shared database (#171).
|
|
47
|
+
|
|
35
48
|
The ``neo4j`` driver is imported lazily so it stays an optional dependency and
|
|
36
49
|
off the default (json) output path entirely.
|
|
37
50
|
"""
|
|
@@ -41,7 +54,7 @@ from dataclasses import dataclass
|
|
|
41
54
|
from typing import Dict, List, Optional
|
|
42
55
|
|
|
43
56
|
from codeanalyzer.neo4j.rows import EdgeRow, GraphRows, NodeRow, chunk
|
|
44
|
-
from codeanalyzer.neo4j.schema import CONSTRAINTS, INDEXES
|
|
57
|
+
from codeanalyzer.neo4j.schema import CONSTRAINTS, INDEXES, MODULE_OWNED_PATTERN
|
|
45
58
|
from codeanalyzer.utils import logger
|
|
46
59
|
|
|
47
60
|
DESCENDANTS = (
|
|
@@ -59,7 +72,7 @@ class BoltConfig:
|
|
|
59
72
|
database: Optional[str] = None
|
|
60
73
|
|
|
61
74
|
|
|
62
|
-
def bolt_writer(rows: GraphRows, cfg: BoltConfig, full_run: bool) -> None:
|
|
75
|
+
def bolt_writer(rows: GraphRows, cfg: BoltConfig, full_run: bool, eager: bool = False) -> None:
|
|
63
76
|
try:
|
|
64
77
|
import neo4j # noqa: WPS433 (lazy, optional dependency)
|
|
65
78
|
except ImportError as exc: # pragma: no cover - exercised only without the extra
|
|
@@ -119,15 +132,26 @@ def bolt_writer(rows: GraphRows, cfg: BoltConfig, full_run: bool) -> None:
|
|
|
119
132
|
_upsert_nodes(session, neo4j, shared)
|
|
120
133
|
|
|
121
134
|
# 4. per changed module: purge owned edges + vanished decls, then upsert its nodes.
|
|
135
|
+
# The purge is the only destructive step in a push, so it runs on --eager only.
|
|
122
136
|
for m in changed:
|
|
123
137
|
nodes = by_module[m]
|
|
124
138
|
keys = [n.value for n in nodes]
|
|
139
|
+
if not eager:
|
|
140
|
+
_upsert_nodes(session, neo4j, nodes)
|
|
141
|
+
continue
|
|
125
142
|
with session() as s:
|
|
126
143
|
def _purge(tx, module=m, node_keys=keys):
|
|
127
|
-
|
|
144
|
+
# Anchored on python-owned labels: `_module` is also set by the java
|
|
145
|
+
# and typescript analyzers, so an unlabelled match would delete a
|
|
146
|
+
# sibling's nodes wherever a file key collides (#171).
|
|
128
147
|
tx.run(
|
|
129
|
-
"MATCH (x
|
|
130
|
-
"
|
|
148
|
+
f"MATCH (x:{MODULE_OWNED_PATTERN}) WHERE x._module = $m "
|
|
149
|
+
"MATCH (x)-[r]->() DELETE r",
|
|
150
|
+
m=module,
|
|
151
|
+
)
|
|
152
|
+
tx.run(
|
|
153
|
+
f"MATCH (x:{MODULE_OWNED_PATTERN}) WHERE x._module = $m "
|
|
154
|
+
"AND NOT coalesce(x.signature, x.id, x.file_key) IN $keys "
|
|
131
155
|
"DETACH DELETE x",
|
|
132
156
|
m=module,
|
|
133
157
|
keys=node_keys,
|
|
@@ -147,7 +171,7 @@ def bolt_writer(rows: GraphRows, cfg: BoltConfig, full_run: bool) -> None:
|
|
|
147
171
|
# 6. orphan prune — only safe on a full run (a targeted run can't tell deleted from untargeted).
|
|
148
172
|
# Scope to THIS application's anchor so a full run for application B never
|
|
149
173
|
# deletes application A's modules from a shared database.
|
|
150
|
-
if full_run and app_name is not None:
|
|
174
|
+
if full_run and eager and app_name is not None:
|
|
151
175
|
present = list(by_module.keys())
|
|
152
176
|
with session() as s:
|
|
153
177
|
res = s.run(
|
|
@@ -161,6 +185,11 @@ def bolt_writer(rows: GraphRows, cfg: BoltConfig, full_run: bool) -> None:
|
|
|
161
185
|
pruned = res.single()
|
|
162
186
|
pruned_count = pruned["pruned"] if pruned else 0
|
|
163
187
|
logger.info(f"neo4j(bolt): pruned {pruned_count} vanished module(s)")
|
|
188
|
+
elif not eager:
|
|
189
|
+
logger.info(
|
|
190
|
+
"neo4j(bolt): additive push (--lazy) — nothing deleted; "
|
|
191
|
+
"re-run with --eager to reconcile removed declarations and edges"
|
|
192
|
+
)
|
|
164
193
|
else:
|
|
165
194
|
logger.info(
|
|
166
195
|
"neo4j(bolt): targeted run — orphan pruning skipped (deleted files not removed)"
|
|
@@ -67,9 +67,10 @@ def emit_neo4j(analysis: Analysis, options: AnalysisOptions) -> None:
|
|
|
67
67
|
password=options.neo4j_password,
|
|
68
68
|
database=options.neo4j_database,
|
|
69
69
|
)
|
|
70
|
-
# A full run (no single-file restriction) makes orphan pruning safe
|
|
70
|
+
# A full run (no single-file restriction) makes orphan pruning safe; --eager
|
|
71
|
+
# is what permits any deletion at all (#171).
|
|
71
72
|
full_run = options.file_name is None
|
|
72
|
-
bolt_writer(rows, cfg, full_run)
|
|
73
|
+
bolt_writer(rows, cfg, full_run, eager=options.rebuild_analysis)
|
|
73
74
|
return
|
|
74
75
|
|
|
75
76
|
out_dir = options.output if options.output is not None else Path.cwd()
|
|
@@ -309,7 +309,6 @@ def _project_artifacts(b: RowBuilder, app: PyApplication, app_name: str, app_ref
|
|
|
309
309
|
"size_bytes": art.size_bytes,
|
|
310
310
|
"sha256": art.sha256,
|
|
311
311
|
"source": art.source,
|
|
312
|
-
"text_truncated": art.text_truncated,
|
|
313
312
|
"extraction": art.extraction,
|
|
314
313
|
}
|
|
315
314
|
),
|
|
@@ -210,7 +210,7 @@ NODE_LABELS: List[NodeLabel] = [
|
|
|
210
210
|
NodeLabel("Artifact", "Artifact", "id", {
|
|
211
211
|
"id": "string", "path": "string", "format": "string",
|
|
212
212
|
"roles": "string[]", "size_bytes": "integer", "sha256": "string",
|
|
213
|
-
"source": "string", "
|
|
213
|
+
"source": "string", "extraction": "string",
|
|
214
214
|
}),
|
|
215
215
|
NodeLabel("Package", "Package", "id", {
|
|
216
216
|
"id": "string", "ecosystem": "string", "name": "string",
|
|
@@ -332,10 +332,27 @@ def uniqueness_constraints() -> list[str]:
|
|
|
332
332
|
|
|
333
333
|
CONSTRAINTS: List[str] = uniqueness_constraints()
|
|
334
334
|
|
|
335
|
+
# The labels this analyzer owns per module -- the ones carrying the internal ``_module``
|
|
336
|
+
# provenance property. Derived from NODE_LABELS so a new module-scoped label is covered
|
|
337
|
+
# without a second list to maintain. `_module` is NOT python-private: codeanalyzer-java
|
|
338
|
+
# and codeanalyzer-typescript set the same property on their nodes, so every statement
|
|
339
|
+
# matching on it must be anchored to these labels or it matches a sibling analyzer's graph
|
|
340
|
+
# in a shared database (#171).
|
|
341
|
+
MODULE_OWNED_LABELS: List[str] = [n.label for n in NODE_LABELS if "_module" in n.properties]
|
|
342
|
+
|
|
343
|
+
# The label disjunction to anchor such a statement with: ``MATCH (x:PyModule|PyClass|...)``.
|
|
344
|
+
MODULE_OWNED_PATTERN: str = "|".join(MODULE_OWNED_LABELS)
|
|
345
|
+
|
|
335
346
|
INDEXES: List[str] = [
|
|
336
347
|
"CREATE INDEX py_callable_name IF NOT EXISTS FOR (c:PyCallable) ON (c.name)",
|
|
337
348
|
"CREATE INDEX py_class_name IF NOT EXISTS FOR (c:PyClass) ON (c.name)",
|
|
338
349
|
"CREATE FULLTEXT INDEX py_code_fts IF NOT EXISTS FOR (c:PyCallable) ON EACH [c.code, c.docstring]",
|
|
350
|
+
] + [
|
|
351
|
+
# One per module-owned label: the incremental writer's per-module purge matches on
|
|
352
|
+
# `_module` once per changed module, which without these is a label scan per label per
|
|
353
|
+
# module -- quadratic on a full push (#171).
|
|
354
|
+
f"CREATE INDEX {label.lower()}_module IF NOT EXISTS FOR (x:{label}) ON (x._module)"
|
|
355
|
+
for label in MODULE_OWNED_LABELS
|
|
339
356
|
]
|
|
340
357
|
|
|
341
358
|
|
|
@@ -43,7 +43,6 @@ class AnalysisOptions:
|
|
|
43
43
|
clear_cache: bool = False
|
|
44
44
|
verbosity: int = 0
|
|
45
45
|
entrypoint_rules: Tuple[Path, ...] = ()
|
|
46
|
-
# Artifact text
|
|
47
|
-
# `source`
|
|
46
|
+
# Artifact text capture (#157 follow-up): whether to capture `source` at
|
|
47
|
+
# all. There is no byte cap -- `source` is the whole file or "" (#172).
|
|
48
48
|
artifact_text: bool = True
|
|
49
|
-
artifact_text_max_bytes: int = 262144
|
|
@@ -512,9 +512,8 @@ class PyArtifact(BaseModel):
|
|
|
512
512
|
format: str # toml|yaml|json|ini|properties|requirements|dockerfile|text|binary
|
|
513
513
|
roles: List[str] = []
|
|
514
514
|
size_bytes: int = 0
|
|
515
|
-
sha256: str = "" # always the full file's hash, even when source is
|
|
516
|
-
source: str = "" #
|
|
517
|
-
text_truncated: bool = False # True when `source` is a prefix, not the full file
|
|
515
|
+
sha256: str = "" # always the full file's hash, even when source is empty
|
|
516
|
+
source: str = "" # the WHOLE file, or "" for binary / when capture is disabled -- never a prefix
|
|
518
517
|
extraction: str = "none" # none|partial|full
|
|
519
518
|
config_keys: List[PyConfigKey] = [] # flattened config keys (#152); [] when not namespace-eligible
|
|
520
519
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codeanalyzer-python"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.4.0"
|
|
4
4
|
description = "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
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -164,7 +164,6 @@
|
|
|
164
164
|
"size_bytes": "integer",
|
|
165
165
|
"sha256": "string",
|
|
166
166
|
"source": "string",
|
|
167
|
-
"text_truncated": "boolean",
|
|
168
167
|
"extraction": "string"
|
|
169
168
|
}
|
|
170
169
|
},
|
|
@@ -510,6 +509,12 @@
|
|
|
510
509
|
"indexes": [
|
|
511
510
|
"CREATE INDEX py_callable_name IF NOT EXISTS FOR (c:PyCallable) ON (c.name)",
|
|
512
511
|
"CREATE INDEX py_class_name IF NOT EXISTS FOR (c:PyClass) ON (c.name)",
|
|
513
|
-
"CREATE FULLTEXT INDEX py_code_fts IF NOT EXISTS FOR (c:PyCallable) ON EACH [c.code, c.docstring]"
|
|
512
|
+
"CREATE FULLTEXT INDEX py_code_fts IF NOT EXISTS FOR (c:PyCallable) ON EACH [c.code, c.docstring]",
|
|
513
|
+
"CREATE INDEX pymodule_module IF NOT EXISTS FOR (x:PyModule) ON (x._module)",
|
|
514
|
+
"CREATE INDEX pyclass_module IF NOT EXISTS FOR (x:PyClass) ON (x._module)",
|
|
515
|
+
"CREATE INDEX pycallable_module IF NOT EXISTS FOR (x:PyCallable) ON (x._module)",
|
|
516
|
+
"CREATE INDEX pyattribute_module IF NOT EXISTS FOR (x:PyAttribute) ON (x._module)",
|
|
517
|
+
"CREATE INDEX pyvariable_module IF NOT EXISTS FOR (x:PyVariable) ON (x._module)",
|
|
518
|
+
"CREATE INDEX pybodynode_module IF NOT EXISTS FOR (x:PyBodyNode) ON (x._module)"
|
|
514
519
|
]
|
|
515
520
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/artifacts/config_keys.py
RENAMED
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/artifacts/config_use.py
RENAMED
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/artifacts/config_use_rules.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/access_paths.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/LICENSE
RENAMED
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/README.md
RENAMED
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/SSA/const.py
RENAMED
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/cfg/builder.py
RENAMED
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel/cfg/model.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/dataflow/scalpel_oracle.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/entrypoints/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/entrypoints/matching.py
RENAMED
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/entrypoints/pipeline.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/schema/call_graph_ids.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/semantic_analysis/__init__.py
RENAMED
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/semantic_analysis/call_graph.py
RENAMED
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.3.0 → codeanalyzer_python-1.4.0}/codeanalyzer/syntactic_analysis/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|