codeanalyzer-python 1.5.1__tar.gz → 1.5.2__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.5.1 → codeanalyzer_python-1.5.2}/PKG-INFO +1 -1
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/neo4j/project.py +77 -16
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/neo4j/schema.py +52 -3
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2/docs/handoff}/schema.neo4j.json +35 -5
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/pyproject.toml +1 -1
- {codeanalyzer_python-1.5.1/docs/handoff → codeanalyzer_python-1.5.2}/schema.neo4j.json +223 -67
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/.gitignore +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/LICENSE +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/NOTICE +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/README.md +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/__main__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/artifacts/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/artifacts/config_keys.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/artifacts/config_use.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/artifacts/config_use_rules.yml +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/artifacts/dependencies.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/artifacts/discovery.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/artifacts/parsers.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/core.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/access_paths.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/alias.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/builder.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/cfg.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/defuse.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/dominance.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/identity.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/pdg.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/LICENSE +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/README.md +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/SSA/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/SSA/const.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/cfg/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/cfg/builder.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/cfg/model.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/core/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/core/func_call_visitor.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/core/vars_visitor.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel_oracle.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scc.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/sdg.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/slicing.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/summaries.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/syntactic.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/entrypoints/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/entrypoints/detect.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/entrypoints/matching.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/entrypoints/pipeline.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/entrypoints/rules.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/entrypoints/rules.yml +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/jedi/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/jedi/jedi.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/neo4j/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/neo4j/bolt.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/neo4j/cypher.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/neo4j/emit.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/neo4j/rows.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/options/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/options/options.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/provenance.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/py.typed +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/schema/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/schema/assign_ids.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/schema/call_graph_ids.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/schema/ids.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/schema/l1_body.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/schema/l2_callees.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/schema/py_schema.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/semantic_analysis/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/semantic_analysis/call_graph.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/semantic_analysis/defuse_linker.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/syntactic_analysis/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/syntactic_analysis/exceptions.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/syntactic_analysis/import_resolver.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/syntactic_analysis/symbol_table_builder.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/utils/__init__.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/utils/logging.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/utils/progress_bar.py +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/docs/handoff/README.md +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/schema-uml.drawio +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/flask/README.md +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/flask/examples/celery/README.md +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/flask/requirements-skip/README.md +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/flask/src/flask/sansio/README.md +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/requests/LICENSE +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/requests/NOTICE +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/requests/README.md +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/requests/docs/_themes/LICENSE +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/requests/ext/LICENSE +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/xarray/LICENSE +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/xarray/README.md +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/xarray/properties/README.md +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/xarray/xarray/datatree_/LICENSE +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/test/fixtures/whole_applications/xarray/xarray/datatree_/README.md +0 -0
- {codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/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.5.
|
|
3
|
+
Version: 1.5.2
|
|
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
|
|
@@ -50,7 +50,7 @@ from codeanalyzer.schema import (
|
|
|
50
50
|
)
|
|
51
51
|
from codeanalyzer.schema import model_dump
|
|
52
52
|
from codeanalyzer.schema.ids import application_id, external_id, global_ordinal, purl_pypi
|
|
53
|
-
from codeanalyzer.schema.py_schema import PyDecorator
|
|
53
|
+
from codeanalyzer.schema.py_schema import PyDecorator, byte_offsets
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
def project(app: PyApplication, app_name: str, sig_to_id: dict,
|
|
@@ -182,6 +182,15 @@ def _project_program_graphs(
|
|
|
182
182
|
if not c.id:
|
|
183
183
|
continue # unstamped callable — assign_ids must run first
|
|
184
184
|
owner = _sym(c.id) # the :PyCallable node, keyed by its can:// id
|
|
185
|
+
# ``callee_signature`` lives on ``PyCallable.call_sites``, not on the body
|
|
186
|
+
# node, so the graph joins the two on the call site's position (#203).
|
|
187
|
+
# ``argument_types`` is deliberately not joined: it is the legacy field #86
|
|
188
|
+
# split into ``PyCallArgument``, already carried as ``arguments_json``.
|
|
189
|
+
sig_by_pos = {
|
|
190
|
+
(cs.start_line, cs.start_column): cs.callee_signature
|
|
191
|
+
for cs in (c.call_sites or [])
|
|
192
|
+
if cs.callee_signature
|
|
193
|
+
}
|
|
185
194
|
for local_key, node in (c.body or {}).items():
|
|
186
195
|
span = node.span
|
|
187
196
|
# L4 param vertices carry the variable they model (``of``) and
|
|
@@ -194,8 +203,11 @@ def _project_program_graphs(
|
|
|
194
203
|
prune(
|
|
195
204
|
{
|
|
196
205
|
"kind": node.kind,
|
|
197
|
-
|
|
198
|
-
"
|
|
206
|
+
**_span_props(span),
|
|
207
|
+
"callee_signature": (
|
|
208
|
+
sig_by_pos.get((span.start[0], span.start[1]))
|
|
209
|
+
if span else None
|
|
210
|
+
),
|
|
199
211
|
"var": node.of,
|
|
200
212
|
"call_node": node.parent,
|
|
201
213
|
# Call-site detail (#120). The JSON emits one node per
|
|
@@ -346,8 +358,7 @@ def _project_artifacts(b: RowBuilder, app: PyApplication, app_name: str, app_ref
|
|
|
346
358
|
"namespace": ck.namespace,
|
|
347
359
|
"value": ck.value,
|
|
348
360
|
"references": list(ck.references or []),
|
|
349
|
-
|
|
350
|
-
"end_line": ck.span.end[0] if ck.span else None,
|
|
361
|
+
**_span_props(ck.span),
|
|
351
362
|
}
|
|
352
363
|
),
|
|
353
364
|
)
|
|
@@ -569,7 +580,7 @@ def _project_module_body(
|
|
|
569
580
|
_project_class(b, file_key, mod_ref, "PY_DECLARES", cl, externals, sig_to_id,
|
|
570
581
|
mod.source, base_ref)
|
|
571
582
|
for v in mod.variables or []:
|
|
572
|
-
_project_variable(b, file_key, mod_ref, v)
|
|
583
|
+
_project_variable(b, file_key, mod_ref, v, mod.source or "")
|
|
573
584
|
_project_imports(b, mod_ref, mod, module_id_by_key)
|
|
574
585
|
|
|
575
586
|
|
|
@@ -597,9 +608,15 @@ def _project_imports(b: RowBuilder, mod_ref: NodeRef, mod: PyModule,
|
|
|
597
608
|
continue
|
|
598
609
|
key = im.resolved_module or im.module
|
|
599
610
|
a = agg.setdefault(
|
|
600
|
-
key,
|
|
611
|
+
key,
|
|
612
|
+
{"spellings": set(), "names": set(), "aliases": set(), "positions": {},
|
|
613
|
+
"resolved": im.resolved_module},
|
|
601
614
|
)
|
|
602
615
|
a["spellings"].add(im.module)
|
|
616
|
+
# Keyed by spelling, never by index: ``spellings`` is emitted sorted, so a
|
|
617
|
+
# parallel position array has already lost its alignment (#203).
|
|
618
|
+
a["positions"][im.module] = [im.start_line, im.start_column,
|
|
619
|
+
im.end_line, im.end_column]
|
|
603
620
|
if im.name:
|
|
604
621
|
a["names"].add(im.name)
|
|
605
622
|
if im.alias:
|
|
@@ -623,6 +640,7 @@ def _project_imports(b: RowBuilder, mod_ref: NodeRef, mod: PyModule,
|
|
|
623
640
|
"spellings": sorted(a["spellings"]),
|
|
624
641
|
"imported_names": sorted(a["names"]) or None,
|
|
625
642
|
"aliases": sorted(a["aliases"]) or None,
|
|
643
|
+
"positions_json": json.dumps(a["positions"], sort_keys=True),
|
|
626
644
|
}
|
|
627
645
|
),
|
|
628
646
|
)
|
|
@@ -675,7 +693,7 @@ def _project_callable(
|
|
|
675
693
|
_project_decorator(b, ref, d)
|
|
676
694
|
|
|
677
695
|
for v in c.local_variables or []:
|
|
678
|
-
_project_variable(b, file_key, ref, v)
|
|
696
|
+
_project_variable(b, file_key, ref, v, source)
|
|
679
697
|
for ic in (c.callables or {}).values():
|
|
680
698
|
_project_callable(b, file_key, ref, "PY_DECLARES", ic, externals, sig_to_id,
|
|
681
699
|
source, base_ref)
|
|
@@ -700,12 +718,14 @@ def _project_variable(
|
|
|
700
718
|
file_key: str,
|
|
701
719
|
owner: NodeRef,
|
|
702
720
|
v: PyVariableDeclaration,
|
|
721
|
+
source: str,
|
|
703
722
|
) -> None:
|
|
704
723
|
# ``<owner can:// id>/<name>@<line>`` (#173) — the owner is the module or the
|
|
705
724
|
# callable, so a module-level variable sits under ``<module-id>/`` like every
|
|
706
725
|
# other declaration and the module's prefix purge reaches it.
|
|
707
726
|
var_id = f"{owner.value}/{v.name}@{v.start_line}"
|
|
708
|
-
ref = b.node(["PyVariable"], "id", var_id,
|
|
727
|
+
ref = b.node(["PyVariable"], "id", var_id,
|
|
728
|
+
_variable_props(v, var_id, file_key, source))
|
|
709
729
|
b.edge("PY_DECLARES_VAR", owner, ref)
|
|
710
730
|
|
|
711
731
|
|
|
@@ -737,6 +757,7 @@ def _project_decorator(b: RowBuilder, on: NodeRef, decorator: PyDecorator) -> No
|
|
|
737
757
|
"keyword_arguments_json": json.dumps(
|
|
738
758
|
dict(decorator.keyword_arguments or {}), sort_keys=True
|
|
739
759
|
),
|
|
760
|
+
**_span_props(decorator.span),
|
|
740
761
|
},
|
|
741
762
|
)
|
|
742
763
|
|
|
@@ -752,6 +773,9 @@ def _module_props(mod: PyModule, file_key: str) -> Props:
|
|
|
752
773
|
"id": mod.id,
|
|
753
774
|
"file_key": file_key,
|
|
754
775
|
"module_name": mod.module_name,
|
|
776
|
+
# Always present, never pruned: "" for an empty file, so a consumer can
|
|
777
|
+
# never confuse "not carried" with "empty" (#202).
|
|
778
|
+
"source": mod.source or "",
|
|
755
779
|
"content_hash": mod.content_hash,
|
|
756
780
|
"last_modified": mod.last_modified,
|
|
757
781
|
"file_size": mod.file_size,
|
|
@@ -760,6 +784,36 @@ def _module_props(mod: PyModule, file_key: str) -> Props:
|
|
|
760
784
|
)
|
|
761
785
|
|
|
762
786
|
|
|
787
|
+
def _span_props(span) -> Props:
|
|
788
|
+
"""The six flattened span properties from a v2 ``Span`` (#202). Lines and columns
|
|
789
|
+
come straight off the model; ``bytes`` is already utf-8 and is what makes the span
|
|
790
|
+
sliceable out of ``:PyModule.source``."""
|
|
791
|
+
if span is None:
|
|
792
|
+
return {}
|
|
793
|
+
return {
|
|
794
|
+
"start_line": span.start[0], "start_column": span.start[1],
|
|
795
|
+
"end_line": span.end[0], "end_column": span.end[1],
|
|
796
|
+
"start_byte": span.bytes[0], "end_byte": span.bytes[1],
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
|
|
800
|
+
def _flat_span_props(source: str, start_line: int, start_column: int,
|
|
801
|
+
end_line: int, end_column: int) -> Props:
|
|
802
|
+
"""The same six for a model carrying flat ast positions and no ``Span``
|
|
803
|
+
(:PyAttribute, :PyVariable). The byte pair is computed here rather than left
|
|
804
|
+
absent, so ``_SPAN`` means one thing on every label that spreads it (#202)."""
|
|
805
|
+
if start_line < 0 or end_line < 0 or not source:
|
|
806
|
+
return {}
|
|
807
|
+
start_column = max(start_column, 0)
|
|
808
|
+
end_column = max(end_column, 0)
|
|
809
|
+
lo, hi = byte_offsets(source, start_line, start_column, end_line, end_column)
|
|
810
|
+
return {
|
|
811
|
+
"start_line": start_line, "start_column": start_column,
|
|
812
|
+
"end_line": end_line, "end_column": end_column,
|
|
813
|
+
"start_byte": lo, "end_byte": hi,
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
|
|
763
817
|
def _span_code(source: str, span) -> str | None:
|
|
764
818
|
"""A declaration's text: the owning module's ``source`` sliced by the node's
|
|
765
819
|
utf-8 byte span. Schema v2 stores source once per module, so the graph's
|
|
@@ -781,8 +835,8 @@ def _class_props(cl: PyClass, file_key: str, source: str) -> Props:
|
|
|
781
835
|
"base_classes": list(cl.base_classes or []),
|
|
782
836
|
"decorators": [d.qualified_name or d.name for d in (cl.decorators or [])],
|
|
783
837
|
"docstring": _docstring_of(cl.comments),
|
|
784
|
-
"start_line": cl.start_line,
|
|
785
|
-
|
|
838
|
+
**(_span_props(cl.span) or {"start_line": cl.start_line,
|
|
839
|
+
"end_line": cl.end_line}),
|
|
786
840
|
"_module": file_key,
|
|
787
841
|
"is_entrypoint": bool(cl.entrypoints),
|
|
788
842
|
"entrypoint_frameworks": sorted({e.framework for e in (cl.entrypoints or [])}),
|
|
@@ -801,8 +855,8 @@ def _callable_props(c: PyCallable, file_key: str, source: str) -> Props:
|
|
|
801
855
|
"cyclomatic_complexity": c.cyclomatic_complexity,
|
|
802
856
|
"code": _span_code(source, c.span),
|
|
803
857
|
"code_start_line": c.code_start_line,
|
|
804
|
-
"start_line": c.start_line,
|
|
805
|
-
|
|
858
|
+
**(_span_props(c.span) or {"start_line": c.start_line,
|
|
859
|
+
"end_line": c.end_line}),
|
|
806
860
|
"docstring": _docstring_of(c.comments),
|
|
807
861
|
"decorators": [d.qualified_name or d.name for d in (c.decorators or [])],
|
|
808
862
|
"modifiers": list(c.modifiers or []),
|
|
@@ -823,6 +877,8 @@ def _attribute_props(a: PyClassAttribute, attr_id: str, file_key: str) -> Props:
|
|
|
823
877
|
"type": a.type,
|
|
824
878
|
"initializer": a.initializer,
|
|
825
879
|
"docstring": _docstring_of(a.comments),
|
|
880
|
+
# Line-only: PyClassAttribute has no columns, so there is nothing to
|
|
881
|
+
# derive bytes from. See the note on :PyAttribute in schema.py.
|
|
826
882
|
"start_line": a.start_line,
|
|
827
883
|
"end_line": a.end_line,
|
|
828
884
|
"_module": file_key,
|
|
@@ -830,16 +886,21 @@ def _attribute_props(a: PyClassAttribute, attr_id: str, file_key: str) -> Props:
|
|
|
830
886
|
)
|
|
831
887
|
|
|
832
888
|
|
|
833
|
-
def _variable_props(v: PyVariableDeclaration, var_id: str, file_key: str
|
|
889
|
+
def _variable_props(v: PyVariableDeclaration, var_id: str, file_key: str,
|
|
890
|
+
source: str) -> Props:
|
|
834
891
|
return prune(
|
|
835
892
|
{
|
|
836
893
|
"id": var_id,
|
|
837
894
|
"name": v.name,
|
|
838
895
|
"type": v.type,
|
|
839
896
|
"initializer": v.initializer,
|
|
897
|
+
# ``value`` is Optional[Any] and a Neo4j property is a scalar or an array
|
|
898
|
+
# of scalars, so a dict/list value needs a serialization rather than a raw
|
|
899
|
+
# put -- always encoded, one shape for every value (#203).
|
|
900
|
+
"value_json": _stringify_if(v.value) if v.value is not None else None,
|
|
840
901
|
"scope": v.scope,
|
|
841
|
-
|
|
842
|
-
|
|
902
|
+
**_flat_span_props(source, v.start_line, v.start_column,
|
|
903
|
+
v.end_line, v.end_column),
|
|
843
904
|
"_module": file_key,
|
|
844
905
|
}
|
|
845
906
|
)
|
|
@@ -28,6 +28,13 @@ SCHEMA_VERSION is the contract version: bump MAJOR on a breaking change (renamed
|
|
|
28
28
|
relationship or key), MINOR on an additive change (new label/rel/property). It is stamped onto
|
|
29
29
|
the :PyApplication node of every emitted graph so any consumer can detect a producer/consumer
|
|
30
30
|
mismatch at runtime.
|
|
31
|
+
|
|
32
|
+
**The additive-MINOR rule is suspended for the 2.0.0 line.** Per the 2026-09-07 ruling (all three
|
|
33
|
+
analyzers), payload ``schema_version`` and this version both hold at ``2.0.0`` until the 2.0.0 line
|
|
34
|
+
leaves release-candidate, so the additive properties of #202/#203 ship without a bump and are
|
|
35
|
+
detectable only by presence. Consumers gate on the **analyzer version** instead -- the python-sdk
|
|
36
|
+
Neo4j backends carry an analyzer floor and refuse anything below it at attach. The rule above
|
|
37
|
+
resumes at the coordinated re-baseline.
|
|
31
38
|
"""
|
|
32
39
|
|
|
33
40
|
from __future__ import annotations
|
|
@@ -61,7 +68,18 @@ class RelType:
|
|
|
61
68
|
# anchor for the prefix-scoped destructive statements (see ``rows.CAN_NODE``).
|
|
62
69
|
MARKER_LABELS: List[str] = ["PyCanNode"]
|
|
63
70
|
|
|
64
|
-
|
|
71
|
+
# The flattened span. ``start_column``/``end_column``/``start_byte``/``end_byte``
|
|
72
|
+
# are adopted **verbatim** from codeanalyzer-java#255, which coined them: a term
|
|
73
|
+
# coined twice is permanently wrong under the cross-language parity clause. The
|
|
74
|
+
# byte pair makes every span sliceable out of :PyModule.source -- for the labels
|
|
75
|
+
# whose JSON model carries flat ast positions and no ``Span`` (:PyAttribute,
|
|
76
|
+
# :PyVariable) the projector computes it, so this dict means one thing on every
|
|
77
|
+
# label that spreads it (#202).
|
|
78
|
+
_SPAN = {
|
|
79
|
+
"start_line": "integer", "end_line": "integer",
|
|
80
|
+
"start_column": "integer", "end_column": "integer",
|
|
81
|
+
"start_byte": "integer", "end_byte": "integer",
|
|
82
|
+
}
|
|
65
83
|
|
|
66
84
|
|
|
67
85
|
NODE_LABELS: List[NodeLabel] = [
|
|
@@ -90,6 +108,10 @@ NODE_LABELS: List[NodeLabel] = [
|
|
|
90
108
|
"id": "string",
|
|
91
109
|
"file_key": "string",
|
|
92
110
|
"module_name": "string",
|
|
111
|
+
# The primary text: schema v2 stores source once per module and every
|
|
112
|
+
# narrower node's text is a byte slice of it. Always present -- an empty
|
|
113
|
+
# file yields "", so "not carried" is never confusable with "empty" (#202).
|
|
114
|
+
"source": "string",
|
|
93
115
|
"content_hash": "string",
|
|
94
116
|
"last_modified": "float",
|
|
95
117
|
"file_size": "integer",
|
|
@@ -158,7 +180,13 @@ NODE_LABELS: List[NodeLabel] = [
|
|
|
158
180
|
"type": "string",
|
|
159
181
|
"initializer": "string",
|
|
160
182
|
"docstring": "string",
|
|
161
|
-
|
|
183
|
+
# The one _SPAN exception: ``PyClassAttribute`` carries start/end LINE only
|
|
184
|
+
# -- no columns, hence no derivable byte offsets. Emitting a fabricated
|
|
185
|
+
# column 0 would make the span unsliceable while claiming otherwise, so the
|
|
186
|
+
# line pair is declared honestly instead. Filed for the JSON model to gain
|
|
187
|
+
# columns; until then this label is line-granular (#203).
|
|
188
|
+
"start_line": "integer",
|
|
189
|
+
"end_line": "integer",
|
|
162
190
|
},
|
|
163
191
|
),
|
|
164
192
|
NodeLabel(
|
|
@@ -170,6 +198,11 @@ NODE_LABELS: List[NodeLabel] = [
|
|
|
170
198
|
"name": "string",
|
|
171
199
|
"type": "string",
|
|
172
200
|
"initializer": "string",
|
|
201
|
+
# ``PyVariableDeclaration.value`` -- the literal-evaluated result, always
|
|
202
|
+
# JSON-encoded because it is ``Optional[Any]`` and a Neo4j property is a
|
|
203
|
+
# scalar or an array of scalars (the ``arguments_json`` precedent, #203).
|
|
204
|
+
# ``initializer`` stays the raw source text.
|
|
205
|
+
"value_json": "string",
|
|
173
206
|
"scope": "string",
|
|
174
207
|
**_SPAN,
|
|
175
208
|
},
|
|
@@ -197,6 +230,12 @@ NODE_LABELS: List[NodeLabel] = [
|
|
|
197
230
|
"return_type": "string",
|
|
198
231
|
"is_constructor_call": "boolean",
|
|
199
232
|
"arguments_json": "string",
|
|
233
|
+
# What distinguishes overload targets at a resolved call site. Joined
|
|
234
|
+
# from ``PyCallable.call_sites`` at projection time, since ``BodyNode``
|
|
235
|
+
# does not carry it in JSON (#203). ``argument_types`` is deliberately
|
|
236
|
+
# NOT here: it is the legacy field #86 split into ``PyCallArgument``,
|
|
237
|
+
# already carried as ``arguments_json``.
|
|
238
|
+
"callee_signature": "string",
|
|
200
239
|
**_SPAN,
|
|
201
240
|
},
|
|
202
241
|
),
|
|
@@ -247,16 +286,26 @@ REL_TYPES: List[RelType] = [
|
|
|
247
286
|
"PY_IMPORTS",
|
|
248
287
|
["PyModule"],
|
|
249
288
|
["PyModule", "PyPackage"],
|
|
250
|
-
|
|
289
|
+
# ``positions_json`` keys on the spelling, not on an index: this edge
|
|
290
|
+
# pre-aggregates per (module, target) and emits ``spellings`` sorted, so
|
|
291
|
+
# parallel position arrays have already lost their alignment (#203).
|
|
292
|
+
{"spellings": "string[]", "imported_names": "string[]", "aliases": "string[]",
|
|
293
|
+
"positions_json": "string"},
|
|
251
294
|
),
|
|
252
295
|
RelType(
|
|
253
296
|
"PY_DECORATED_BY",
|
|
254
297
|
["PyCallable", "PyClass"],
|
|
255
298
|
["PyDecorator"],
|
|
299
|
+
# The span rides here, not on :PyDecorator: that node is merged on the
|
|
300
|
+
# resolved ``qualified_name`` and carries no ``_module``, so it is never
|
|
301
|
+
# pruned and any per-application fact on it would accumulate across every
|
|
302
|
+
# project in the database -- the same reason ``expression`` and the
|
|
303
|
+
# arguments already ride the relationship (#203).
|
|
256
304
|
{
|
|
257
305
|
"expression": "string",
|
|
258
306
|
"positional_arguments": "string[]",
|
|
259
307
|
"keyword_arguments_json": "string",
|
|
308
|
+
**_SPAN,
|
|
260
309
|
},
|
|
261
310
|
),
|
|
262
311
|
# Level-3 CPG overlay (-a 3 only): the cross-language dataflow vocabulary,
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"id": "string",
|
|
31
31
|
"file_key": "string",
|
|
32
32
|
"module_name": "string",
|
|
33
|
+
"source": "string",
|
|
33
34
|
"content_hash": "string",
|
|
34
35
|
"last_modified": "float",
|
|
35
36
|
"file_size": "integer"
|
|
@@ -49,6 +50,10 @@
|
|
|
49
50
|
"docstring": "string",
|
|
50
51
|
"start_line": "integer",
|
|
51
52
|
"end_line": "integer",
|
|
53
|
+
"start_column": "integer",
|
|
54
|
+
"end_column": "integer",
|
|
55
|
+
"start_byte": "integer",
|
|
56
|
+
"end_byte": "integer",
|
|
52
57
|
"is_entrypoint": "boolean",
|
|
53
58
|
"entrypoint_frameworks": "string[]"
|
|
54
59
|
}
|
|
@@ -68,6 +73,10 @@
|
|
|
68
73
|
"code_start_line": "integer",
|
|
69
74
|
"start_line": "integer",
|
|
70
75
|
"end_line": "integer",
|
|
76
|
+
"start_column": "integer",
|
|
77
|
+
"end_column": "integer",
|
|
78
|
+
"start_byte": "integer",
|
|
79
|
+
"end_byte": "integer",
|
|
71
80
|
"docstring": "string",
|
|
72
81
|
"decorators": "string[]",
|
|
73
82
|
"modifiers": "string[]",
|
|
@@ -127,9 +136,14 @@
|
|
|
127
136
|
"name": "string",
|
|
128
137
|
"type": "string",
|
|
129
138
|
"initializer": "string",
|
|
139
|
+
"value_json": "string",
|
|
130
140
|
"scope": "string",
|
|
131
141
|
"start_line": "integer",
|
|
132
|
-
"end_line": "integer"
|
|
142
|
+
"end_line": "integer",
|
|
143
|
+
"start_column": "integer",
|
|
144
|
+
"end_column": "integer",
|
|
145
|
+
"start_byte": "integer",
|
|
146
|
+
"end_byte": "integer"
|
|
133
147
|
}
|
|
134
148
|
},
|
|
135
149
|
{
|
|
@@ -147,8 +161,13 @@
|
|
|
147
161
|
"return_type": "string",
|
|
148
162
|
"is_constructor_call": "boolean",
|
|
149
163
|
"arguments_json": "string",
|
|
164
|
+
"callee_signature": "string",
|
|
150
165
|
"start_line": "integer",
|
|
151
|
-
"end_line": "integer"
|
|
166
|
+
"end_line": "integer",
|
|
167
|
+
"start_column": "integer",
|
|
168
|
+
"end_column": "integer",
|
|
169
|
+
"start_byte": "integer",
|
|
170
|
+
"end_byte": "integer"
|
|
152
171
|
}
|
|
153
172
|
},
|
|
154
173
|
{
|
|
@@ -187,7 +206,11 @@
|
|
|
187
206
|
"value": "string",
|
|
188
207
|
"references": "string[]",
|
|
189
208
|
"start_line": "integer",
|
|
190
|
-
"end_line": "integer"
|
|
209
|
+
"end_line": "integer",
|
|
210
|
+
"start_column": "integer",
|
|
211
|
+
"end_column": "integer",
|
|
212
|
+
"start_byte": "integer",
|
|
213
|
+
"end_byte": "integer"
|
|
191
214
|
}
|
|
192
215
|
}
|
|
193
216
|
],
|
|
@@ -294,7 +317,8 @@
|
|
|
294
317
|
"properties": {
|
|
295
318
|
"spellings": "string[]",
|
|
296
319
|
"imported_names": "string[]",
|
|
297
|
-
"aliases": "string[]"
|
|
320
|
+
"aliases": "string[]",
|
|
321
|
+
"positions_json": "string"
|
|
298
322
|
}
|
|
299
323
|
},
|
|
300
324
|
{
|
|
@@ -309,7 +333,13 @@
|
|
|
309
333
|
"properties": {
|
|
310
334
|
"expression": "string",
|
|
311
335
|
"positional_arguments": "string[]",
|
|
312
|
-
"keyword_arguments_json": "string"
|
|
336
|
+
"keyword_arguments_json": "string",
|
|
337
|
+
"start_line": "integer",
|
|
338
|
+
"end_line": "integer",
|
|
339
|
+
"start_column": "integer",
|
|
340
|
+
"end_column": "integer",
|
|
341
|
+
"start_byte": "integer",
|
|
342
|
+
"end_byte": "integer"
|
|
313
343
|
}
|
|
314
344
|
},
|
|
315
345
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codeanalyzer-python"
|
|
3
|
-
version = "1.5.
|
|
3
|
+
version = "1.5.2"
|
|
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 = [
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "2.0.0",
|
|
3
3
|
"generator": "codeanalyzer-python",
|
|
4
|
-
"marker_labels": [
|
|
4
|
+
"marker_labels": [
|
|
5
|
+
"PyCanNode"
|
|
6
|
+
],
|
|
5
7
|
"node_labels": [
|
|
6
8
|
{
|
|
7
9
|
"label": "PyApplication",
|
|
8
10
|
"merge_label": "PyApplication",
|
|
9
|
-
"key": "
|
|
11
|
+
"key": "id",
|
|
10
12
|
"properties": {
|
|
13
|
+
"id": "string",
|
|
11
14
|
"name": "string",
|
|
12
15
|
"schema_version": "string",
|
|
13
16
|
"analyzer_name": "string",
|
|
14
17
|
"analyzer_version": "string",
|
|
15
18
|
"repo_uri": "string",
|
|
16
19
|
"source_revision": "string",
|
|
17
|
-
"repo_dirty": "boolean"
|
|
20
|
+
"repo_dirty": "boolean",
|
|
21
|
+
"entrypoint_frameworks": "string[]",
|
|
22
|
+
"entrypoint_report_json": "string"
|
|
18
23
|
}
|
|
19
24
|
},
|
|
20
25
|
{
|
|
@@ -25,10 +30,10 @@
|
|
|
25
30
|
"id": "string",
|
|
26
31
|
"file_key": "string",
|
|
27
32
|
"module_name": "string",
|
|
33
|
+
"source": "string",
|
|
28
34
|
"content_hash": "string",
|
|
29
35
|
"last_modified": "float",
|
|
30
|
-
"file_size": "integer"
|
|
31
|
-
"_module": "string"
|
|
36
|
+
"file_size": "integer"
|
|
32
37
|
}
|
|
33
38
|
},
|
|
34
39
|
{
|
|
@@ -41,10 +46,16 @@
|
|
|
41
46
|
"name": "string",
|
|
42
47
|
"code": "string",
|
|
43
48
|
"base_classes": "string[]",
|
|
49
|
+
"decorators": "string[]",
|
|
44
50
|
"docstring": "string",
|
|
45
51
|
"start_line": "integer",
|
|
46
52
|
"end_line": "integer",
|
|
47
|
-
"
|
|
53
|
+
"start_column": "integer",
|
|
54
|
+
"end_column": "integer",
|
|
55
|
+
"start_byte": "integer",
|
|
56
|
+
"end_byte": "integer",
|
|
57
|
+
"is_entrypoint": "boolean",
|
|
58
|
+
"entrypoint_frameworks": "string[]"
|
|
48
59
|
}
|
|
49
60
|
},
|
|
50
61
|
{
|
|
@@ -62,11 +73,17 @@
|
|
|
62
73
|
"code_start_line": "integer",
|
|
63
74
|
"start_line": "integer",
|
|
64
75
|
"end_line": "integer",
|
|
76
|
+
"start_column": "integer",
|
|
77
|
+
"end_column": "integer",
|
|
78
|
+
"start_byte": "integer",
|
|
79
|
+
"end_byte": "integer",
|
|
65
80
|
"docstring": "string",
|
|
66
81
|
"decorators": "string[]",
|
|
82
|
+
"modifiers": "string[]",
|
|
67
83
|
"parameters_json": "string",
|
|
68
84
|
"accessed_symbols_json": "string",
|
|
69
|
-
"
|
|
85
|
+
"is_entrypoint": "boolean",
|
|
86
|
+
"entrypoint_frameworks": "string[]"
|
|
70
87
|
}
|
|
71
88
|
},
|
|
72
89
|
{
|
|
@@ -92,28 +109,8 @@
|
|
|
92
109
|
"merge_label": "PyDecorator",
|
|
93
110
|
"key": "name",
|
|
94
111
|
"properties": {
|
|
95
|
-
"name": "string"
|
|
96
|
-
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
"label": "PyCallSite",
|
|
100
|
-
"merge_label": "PyCallSite",
|
|
101
|
-
"key": "id",
|
|
102
|
-
"properties": {
|
|
103
|
-
"id": "string",
|
|
104
|
-
"method_name": "string",
|
|
105
|
-
"receiver_expr": "string",
|
|
106
|
-
"receiver_type": "string",
|
|
107
|
-
"argument_types": "string[]",
|
|
108
|
-
"arguments_json": "string",
|
|
109
|
-
"return_type": "string",
|
|
110
|
-
"callee_signature": "string",
|
|
111
|
-
"is_constructor_call": "boolean",
|
|
112
|
-
"start_line": "integer",
|
|
113
|
-
"start_column": "integer",
|
|
114
|
-
"end_line": "integer",
|
|
115
|
-
"end_column": "integer",
|
|
116
|
-
"_module": "string"
|
|
112
|
+
"name": "string",
|
|
113
|
+
"qualified_name": "string"
|
|
117
114
|
}
|
|
118
115
|
},
|
|
119
116
|
{
|
|
@@ -127,8 +124,7 @@
|
|
|
127
124
|
"initializer": "string",
|
|
128
125
|
"docstring": "string",
|
|
129
126
|
"start_line": "integer",
|
|
130
|
-
"end_line": "integer"
|
|
131
|
-
"_module": "string"
|
|
127
|
+
"end_line": "integer"
|
|
132
128
|
}
|
|
133
129
|
},
|
|
134
130
|
{
|
|
@@ -140,24 +136,81 @@
|
|
|
140
136
|
"name": "string",
|
|
141
137
|
"type": "string",
|
|
142
138
|
"initializer": "string",
|
|
139
|
+
"value_json": "string",
|
|
143
140
|
"scope": "string",
|
|
144
141
|
"start_line": "integer",
|
|
145
142
|
"end_line": "integer",
|
|
146
|
-
"
|
|
143
|
+
"start_column": "integer",
|
|
144
|
+
"end_column": "integer",
|
|
145
|
+
"start_byte": "integer",
|
|
146
|
+
"end_byte": "integer"
|
|
147
147
|
}
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
|
-
"label": "
|
|
151
|
-
"merge_label": "
|
|
150
|
+
"label": "PyBodyNode",
|
|
151
|
+
"merge_label": "PyBodyNode",
|
|
152
152
|
"key": "id",
|
|
153
153
|
"properties": {
|
|
154
154
|
"id": "string",
|
|
155
155
|
"kind": "string",
|
|
156
156
|
"var": "string",
|
|
157
157
|
"call_node": "string",
|
|
158
|
+
"method_name": "string",
|
|
159
|
+
"receiver_expr": "string",
|
|
160
|
+
"receiver_type": "string",
|
|
161
|
+
"return_type": "string",
|
|
162
|
+
"is_constructor_call": "boolean",
|
|
163
|
+
"arguments_json": "string",
|
|
164
|
+
"callee_signature": "string",
|
|
165
|
+
"start_line": "integer",
|
|
166
|
+
"end_line": "integer",
|
|
167
|
+
"start_column": "integer",
|
|
168
|
+
"end_column": "integer",
|
|
169
|
+
"start_byte": "integer",
|
|
170
|
+
"end_byte": "integer"
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"label": "Artifact",
|
|
175
|
+
"merge_label": "Artifact",
|
|
176
|
+
"key": "id",
|
|
177
|
+
"properties": {
|
|
178
|
+
"id": "string",
|
|
179
|
+
"path": "string",
|
|
180
|
+
"format": "string",
|
|
181
|
+
"roles": "string[]",
|
|
182
|
+
"size_bytes": "integer",
|
|
183
|
+
"sha256": "string",
|
|
184
|
+
"source": "string",
|
|
185
|
+
"extraction": "string"
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"label": "Package",
|
|
190
|
+
"merge_label": "Package",
|
|
191
|
+
"key": "id",
|
|
192
|
+
"properties": {
|
|
193
|
+
"id": "string",
|
|
194
|
+
"ecosystem": "string",
|
|
195
|
+
"name": "string"
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"label": "ConfigKey",
|
|
200
|
+
"merge_label": "ConfigKey",
|
|
201
|
+
"key": "id",
|
|
202
|
+
"properties": {
|
|
203
|
+
"id": "string",
|
|
204
|
+
"key": "string",
|
|
205
|
+
"namespace": "string",
|
|
206
|
+
"value": "string",
|
|
207
|
+
"references": "string[]",
|
|
158
208
|
"start_line": "integer",
|
|
159
209
|
"end_line": "integer",
|
|
160
|
-
"
|
|
210
|
+
"start_column": "integer",
|
|
211
|
+
"end_column": "integer",
|
|
212
|
+
"start_byte": "integer",
|
|
213
|
+
"end_byte": "integer"
|
|
161
214
|
}
|
|
162
215
|
}
|
|
163
216
|
],
|
|
@@ -216,20 +269,10 @@
|
|
|
216
269
|
],
|
|
217
270
|
"properties": {}
|
|
218
271
|
},
|
|
219
|
-
{
|
|
220
|
-
"type": "PY_HAS_CALLSITE",
|
|
221
|
-
"from": [
|
|
222
|
-
"PyCallable"
|
|
223
|
-
],
|
|
224
|
-
"to": [
|
|
225
|
-
"PyCallSite"
|
|
226
|
-
],
|
|
227
|
-
"properties": {}
|
|
228
|
-
},
|
|
229
272
|
{
|
|
230
273
|
"type": "PY_RESOLVES_TO",
|
|
231
274
|
"from": [
|
|
232
|
-
"
|
|
275
|
+
"PyBodyNode"
|
|
233
276
|
],
|
|
234
277
|
"to": [
|
|
235
278
|
"PyCallable",
|
|
@@ -274,36 +317,48 @@
|
|
|
274
317
|
"properties": {
|
|
275
318
|
"spellings": "string[]",
|
|
276
319
|
"imported_names": "string[]",
|
|
277
|
-
"aliases": "string[]"
|
|
320
|
+
"aliases": "string[]",
|
|
321
|
+
"positions_json": "string"
|
|
278
322
|
}
|
|
279
323
|
},
|
|
280
324
|
{
|
|
281
325
|
"type": "PY_DECORATED_BY",
|
|
282
326
|
"from": [
|
|
283
|
-
"PyCallable"
|
|
327
|
+
"PyCallable",
|
|
328
|
+
"PyClass"
|
|
284
329
|
],
|
|
285
330
|
"to": [
|
|
286
331
|
"PyDecorator"
|
|
287
332
|
],
|
|
288
|
-
"properties": {
|
|
333
|
+
"properties": {
|
|
334
|
+
"expression": "string",
|
|
335
|
+
"positional_arguments": "string[]",
|
|
336
|
+
"keyword_arguments_json": "string",
|
|
337
|
+
"start_line": "integer",
|
|
338
|
+
"end_line": "integer",
|
|
339
|
+
"start_column": "integer",
|
|
340
|
+
"end_column": "integer",
|
|
341
|
+
"start_byte": "integer",
|
|
342
|
+
"end_byte": "integer"
|
|
343
|
+
}
|
|
289
344
|
},
|
|
290
345
|
{
|
|
291
|
-
"type": "
|
|
346
|
+
"type": "PY_HAS_BODY_NODE",
|
|
292
347
|
"from": [
|
|
293
348
|
"PyCallable"
|
|
294
349
|
],
|
|
295
350
|
"to": [
|
|
296
|
-
"
|
|
351
|
+
"PyBodyNode"
|
|
297
352
|
],
|
|
298
353
|
"properties": {}
|
|
299
354
|
},
|
|
300
355
|
{
|
|
301
356
|
"type": "PY_CFG_NEXT",
|
|
302
357
|
"from": [
|
|
303
|
-
"
|
|
358
|
+
"PyBodyNode"
|
|
304
359
|
],
|
|
305
360
|
"to": [
|
|
306
|
-
"
|
|
361
|
+
"PyBodyNode"
|
|
307
362
|
],
|
|
308
363
|
"properties": {
|
|
309
364
|
"kind": "string",
|
|
@@ -313,20 +368,20 @@
|
|
|
313
368
|
{
|
|
314
369
|
"type": "PY_CDG",
|
|
315
370
|
"from": [
|
|
316
|
-
"
|
|
371
|
+
"PyBodyNode"
|
|
317
372
|
],
|
|
318
373
|
"to": [
|
|
319
|
-
"
|
|
374
|
+
"PyBodyNode"
|
|
320
375
|
],
|
|
321
376
|
"properties": {}
|
|
322
377
|
},
|
|
323
378
|
{
|
|
324
379
|
"type": "PY_DDG",
|
|
325
380
|
"from": [
|
|
326
|
-
"
|
|
381
|
+
"PyBodyNode"
|
|
327
382
|
],
|
|
328
383
|
"to": [
|
|
329
|
-
"
|
|
384
|
+
"PyBodyNode"
|
|
330
385
|
],
|
|
331
386
|
"properties": {
|
|
332
387
|
"var": "string",
|
|
@@ -337,10 +392,10 @@
|
|
|
337
392
|
{
|
|
338
393
|
"type": "PY_PARAM_IN",
|
|
339
394
|
"from": [
|
|
340
|
-
"
|
|
395
|
+
"PyBodyNode"
|
|
341
396
|
],
|
|
342
397
|
"to": [
|
|
343
|
-
"
|
|
398
|
+
"PyBodyNode"
|
|
344
399
|
],
|
|
345
400
|
"properties": {
|
|
346
401
|
"var": "string"
|
|
@@ -349,10 +404,10 @@
|
|
|
349
404
|
{
|
|
350
405
|
"type": "PY_PARAM_OUT",
|
|
351
406
|
"from": [
|
|
352
|
-
"
|
|
407
|
+
"PyBodyNode"
|
|
353
408
|
],
|
|
354
409
|
"to": [
|
|
355
|
-
"
|
|
410
|
+
"PyBodyNode"
|
|
356
411
|
],
|
|
357
412
|
"properties": {
|
|
358
413
|
"var": "string"
|
|
@@ -361,28 +416,129 @@
|
|
|
361
416
|
{
|
|
362
417
|
"type": "PY_SUMMARY",
|
|
363
418
|
"from": [
|
|
364
|
-
"
|
|
419
|
+
"PyBodyNode"
|
|
420
|
+
],
|
|
421
|
+
"to": [
|
|
422
|
+
"PyBodyNode"
|
|
423
|
+
],
|
|
424
|
+
"properties": {}
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
"type": "HAS_ARTIFACT",
|
|
428
|
+
"from": [
|
|
429
|
+
"PyApplication"
|
|
430
|
+
],
|
|
431
|
+
"to": [
|
|
432
|
+
"Artifact"
|
|
433
|
+
],
|
|
434
|
+
"properties": {}
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
"type": "DEFINES_CONFIG",
|
|
438
|
+
"from": [
|
|
439
|
+
"Artifact"
|
|
440
|
+
],
|
|
441
|
+
"to": [
|
|
442
|
+
"ConfigKey"
|
|
443
|
+
],
|
|
444
|
+
"properties": {}
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
"type": "DECLARES_DEPENDENCY",
|
|
448
|
+
"from": [
|
|
449
|
+
"Artifact"
|
|
450
|
+
],
|
|
451
|
+
"to": [
|
|
452
|
+
"Package"
|
|
453
|
+
],
|
|
454
|
+
"properties": {
|
|
455
|
+
"spec": "string",
|
|
456
|
+
"kind": "string",
|
|
457
|
+
"extras": "string[]",
|
|
458
|
+
"prov": "string[]",
|
|
459
|
+
"direct": "boolean",
|
|
460
|
+
"_k": "string"
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
"type": "LOCKS",
|
|
465
|
+
"from": [
|
|
466
|
+
"Artifact"
|
|
467
|
+
],
|
|
468
|
+
"to": [
|
|
469
|
+
"Package"
|
|
470
|
+
],
|
|
471
|
+
"properties": {
|
|
472
|
+
"version": "string"
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
"type": "PY_PROVIDES",
|
|
477
|
+
"from": [
|
|
478
|
+
"Package"
|
|
365
479
|
],
|
|
366
480
|
"to": [
|
|
367
|
-
"
|
|
481
|
+
"PyExternal"
|
|
368
482
|
],
|
|
369
483
|
"properties": {}
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
"type": "PY_UNRESOLVED_IMPORT",
|
|
487
|
+
"from": [
|
|
488
|
+
"PyApplication"
|
|
489
|
+
],
|
|
490
|
+
"to": [
|
|
491
|
+
"PyExternal"
|
|
492
|
+
],
|
|
493
|
+
"properties": {
|
|
494
|
+
"prov": "string[]"
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
"type": "PY_USES_CONFIG",
|
|
499
|
+
"from": [
|
|
500
|
+
"PyBodyNode"
|
|
501
|
+
],
|
|
502
|
+
"to": [
|
|
503
|
+
"ConfigKey"
|
|
504
|
+
],
|
|
505
|
+
"properties": {
|
|
506
|
+
"prov": "string[]"
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
"type": "PY_READS_CONFIG_UNRESOLVED",
|
|
511
|
+
"from": [
|
|
512
|
+
"PyApplication"
|
|
513
|
+
],
|
|
514
|
+
"to": [
|
|
515
|
+
"PyExternal"
|
|
516
|
+
],
|
|
517
|
+
"properties": {
|
|
518
|
+
"key": "string",
|
|
519
|
+
"reason": "string",
|
|
520
|
+
"prov": "string[]",
|
|
521
|
+
"_k": "string"
|
|
522
|
+
}
|
|
370
523
|
}
|
|
371
524
|
],
|
|
372
525
|
"constraints": [
|
|
373
|
-
"CREATE CONSTRAINT
|
|
526
|
+
"CREATE CONSTRAINT pyapplication_id IF NOT EXISTS FOR (x:PyApplication) REQUIRE x.id IS UNIQUE",
|
|
374
527
|
"CREATE CONSTRAINT pymodule_id IF NOT EXISTS FOR (x:PyModule) REQUIRE x.id IS UNIQUE",
|
|
375
528
|
"CREATE CONSTRAINT pysymbol_id IF NOT EXISTS FOR (x:PySymbol) REQUIRE x.id IS UNIQUE",
|
|
376
529
|
"CREATE CONSTRAINT pypackage_name IF NOT EXISTS FOR (x:PyPackage) REQUIRE x.name IS UNIQUE",
|
|
377
530
|
"CREATE CONSTRAINT pydecorator_name IF NOT EXISTS FOR (x:PyDecorator) REQUIRE x.name IS UNIQUE",
|
|
378
|
-
"CREATE CONSTRAINT pycallsite_id IF NOT EXISTS FOR (x:PyCallSite) REQUIRE x.id IS UNIQUE",
|
|
379
531
|
"CREATE CONSTRAINT pyattribute_id IF NOT EXISTS FOR (x:PyAttribute) REQUIRE x.id IS UNIQUE",
|
|
380
532
|
"CREATE CONSTRAINT pyvariable_id IF NOT EXISTS FOR (x:PyVariable) REQUIRE x.id IS UNIQUE",
|
|
381
|
-
"CREATE CONSTRAINT
|
|
533
|
+
"CREATE CONSTRAINT pybodynode_id IF NOT EXISTS FOR (x:PyBodyNode) REQUIRE x.id IS UNIQUE",
|
|
534
|
+
"CREATE CONSTRAINT artifact_id IF NOT EXISTS FOR (x:Artifact) REQUIRE x.id IS UNIQUE",
|
|
535
|
+
"CREATE CONSTRAINT package_id IF NOT EXISTS FOR (x:Package) REQUIRE x.id IS UNIQUE",
|
|
536
|
+
"CREATE CONSTRAINT configkey_id IF NOT EXISTS FOR (x:ConfigKey) REQUIRE x.id IS UNIQUE"
|
|
382
537
|
],
|
|
383
538
|
"indexes": [
|
|
384
539
|
"CREATE INDEX py_callable_name IF NOT EXISTS FOR (c:PyCallable) ON (c.name)",
|
|
385
540
|
"CREATE INDEX py_class_name IF NOT EXISTS FOR (c:PyClass) ON (c.name)",
|
|
386
|
-
"CREATE FULLTEXT INDEX py_code_fts IF NOT EXISTS FOR (c:PyCallable) ON EACH [c.code, c.docstring]"
|
|
541
|
+
"CREATE FULLTEXT INDEX py_code_fts IF NOT EXISTS FOR (c:PyCallable) ON EACH [c.code, c.docstring]",
|
|
542
|
+
"CREATE INDEX py_can_node_id IF NOT EXISTS FOR (n:PyCanNode) ON (n.id)"
|
|
387
543
|
]
|
|
388
544
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/artifacts/config_keys.py
RENAMED
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/artifacts/config_use.py
RENAMED
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/artifacts/config_use_rules.yml
RENAMED
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/artifacts/dependencies.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/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
|
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/LICENSE
RENAMED
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/README.md
RENAMED
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/SSA/const.py
RENAMED
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/cfg/builder.py
RENAMED
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel/cfg/model.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/dataflow/scalpel_oracle.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/entrypoints/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/entrypoints/matching.py
RENAMED
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/schema/call_graph_ids.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/semantic_analysis/__init__.py
RENAMED
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/codeanalyzer/semantic_analysis/call_graph.py
RENAMED
|
File without changes
|
|
File without changes
|
{codeanalyzer_python-1.5.1 → codeanalyzer_python-1.5.2}/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
|