codeanalyzer-python 1.5.0__py3-none-any.whl → 1.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codeanalyzer/dataflow/builder.py +1 -0
- codeanalyzer/neo4j/project.py +83 -16
- codeanalyzer/neo4j/schema.py +52 -3
- codeanalyzer/schema/py_schema.py +5 -0
- {codeanalyzer_python-1.5.0.dist-info → codeanalyzer_python-1.5.2.dist-info}/METADATA +3 -2
- {codeanalyzer_python-1.5.0.dist-info → codeanalyzer_python-1.5.2.dist-info}/RECORD +10 -10
- {codeanalyzer_python-1.5.0.dist-info → codeanalyzer_python-1.5.2.dist-info}/WHEEL +0 -0
- {codeanalyzer_python-1.5.0.dist-info → codeanalyzer_python-1.5.2.dist-info}/entry_points.txt +0 -0
- {codeanalyzer_python-1.5.0.dist-info → codeanalyzer_python-1.5.2.dist-info}/licenses/LICENSE +0 -0
- {codeanalyzer_python-1.5.0.dist-info → codeanalyzer_python-1.5.2.dist-info}/licenses/NOTICE +0 -0
codeanalyzer/dataflow/builder.py
CHANGED
codeanalyzer/neo4j/project.py
CHANGED
|
@@ -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
|
|
@@ -257,17 +269,23 @@ def _project_program_graphs(
|
|
|
257
269
|
# endpoint functions' identity maps), so they land on the very PyBodyNode ids
|
|
258
270
|
# projected above — a formal_in global id equals _global_ordinal(callee.id,
|
|
259
271
|
# "@formal_in:0"). No dangling references.
|
|
272
|
+
# `var` (#195): the callee-side formal's variable, set on every edge by the
|
|
273
|
+
# SDG assembler and carried on `ParamEdge.var`. The catalog declared it and
|
|
274
|
+
# nothing wrote it, so `r.var` predicates went three-valued across every
|
|
275
|
+
# call boundary.
|
|
260
276
|
for e in app.param_in or []:
|
|
261
277
|
b.edge(
|
|
262
278
|
"PY_PARAM_IN",
|
|
263
279
|
NodeRef("PyBodyNode", "id", e.src),
|
|
264
280
|
NodeRef("PyBodyNode", "id", e.dst),
|
|
281
|
+
prune({"var": e.var}),
|
|
265
282
|
)
|
|
266
283
|
for e in app.param_out or []:
|
|
267
284
|
b.edge(
|
|
268
285
|
"PY_PARAM_OUT",
|
|
269
286
|
NodeRef("PyBodyNode", "id", e.src),
|
|
270
287
|
NodeRef("PyBodyNode", "id", e.dst),
|
|
288
|
+
prune({"var": e.var}),
|
|
271
289
|
)
|
|
272
290
|
|
|
273
291
|
|
|
@@ -340,8 +358,7 @@ def _project_artifacts(b: RowBuilder, app: PyApplication, app_name: str, app_ref
|
|
|
340
358
|
"namespace": ck.namespace,
|
|
341
359
|
"value": ck.value,
|
|
342
360
|
"references": list(ck.references or []),
|
|
343
|
-
|
|
344
|
-
"end_line": ck.span.end[0] if ck.span else None,
|
|
361
|
+
**_span_props(ck.span),
|
|
345
362
|
}
|
|
346
363
|
),
|
|
347
364
|
)
|
|
@@ -563,7 +580,7 @@ def _project_module_body(
|
|
|
563
580
|
_project_class(b, file_key, mod_ref, "PY_DECLARES", cl, externals, sig_to_id,
|
|
564
581
|
mod.source, base_ref)
|
|
565
582
|
for v in mod.variables or []:
|
|
566
|
-
_project_variable(b, file_key, mod_ref, v)
|
|
583
|
+
_project_variable(b, file_key, mod_ref, v, mod.source or "")
|
|
567
584
|
_project_imports(b, mod_ref, mod, module_id_by_key)
|
|
568
585
|
|
|
569
586
|
|
|
@@ -591,9 +608,15 @@ def _project_imports(b: RowBuilder, mod_ref: NodeRef, mod: PyModule,
|
|
|
591
608
|
continue
|
|
592
609
|
key = im.resolved_module or im.module
|
|
593
610
|
a = agg.setdefault(
|
|
594
|
-
key,
|
|
611
|
+
key,
|
|
612
|
+
{"spellings": set(), "names": set(), "aliases": set(), "positions": {},
|
|
613
|
+
"resolved": im.resolved_module},
|
|
595
614
|
)
|
|
596
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]
|
|
597
620
|
if im.name:
|
|
598
621
|
a["names"].add(im.name)
|
|
599
622
|
if im.alias:
|
|
@@ -617,6 +640,7 @@ def _project_imports(b: RowBuilder, mod_ref: NodeRef, mod: PyModule,
|
|
|
617
640
|
"spellings": sorted(a["spellings"]),
|
|
618
641
|
"imported_names": sorted(a["names"]) or None,
|
|
619
642
|
"aliases": sorted(a["aliases"]) or None,
|
|
643
|
+
"positions_json": json.dumps(a["positions"], sort_keys=True),
|
|
620
644
|
}
|
|
621
645
|
),
|
|
622
646
|
)
|
|
@@ -669,7 +693,7 @@ def _project_callable(
|
|
|
669
693
|
_project_decorator(b, ref, d)
|
|
670
694
|
|
|
671
695
|
for v in c.local_variables or []:
|
|
672
|
-
_project_variable(b, file_key, ref, v)
|
|
696
|
+
_project_variable(b, file_key, ref, v, source)
|
|
673
697
|
for ic in (c.callables or {}).values():
|
|
674
698
|
_project_callable(b, file_key, ref, "PY_DECLARES", ic, externals, sig_to_id,
|
|
675
699
|
source, base_ref)
|
|
@@ -694,12 +718,14 @@ def _project_variable(
|
|
|
694
718
|
file_key: str,
|
|
695
719
|
owner: NodeRef,
|
|
696
720
|
v: PyVariableDeclaration,
|
|
721
|
+
source: str,
|
|
697
722
|
) -> None:
|
|
698
723
|
# ``<owner can:// id>/<name>@<line>`` (#173) — the owner is the module or the
|
|
699
724
|
# callable, so a module-level variable sits under ``<module-id>/`` like every
|
|
700
725
|
# other declaration and the module's prefix purge reaches it.
|
|
701
726
|
var_id = f"{owner.value}/{v.name}@{v.start_line}"
|
|
702
|
-
ref = b.node(["PyVariable"], "id", var_id,
|
|
727
|
+
ref = b.node(["PyVariable"], "id", var_id,
|
|
728
|
+
_variable_props(v, var_id, file_key, source))
|
|
703
729
|
b.edge("PY_DECLARES_VAR", owner, ref)
|
|
704
730
|
|
|
705
731
|
|
|
@@ -731,6 +757,7 @@ def _project_decorator(b: RowBuilder, on: NodeRef, decorator: PyDecorator) -> No
|
|
|
731
757
|
"keyword_arguments_json": json.dumps(
|
|
732
758
|
dict(decorator.keyword_arguments or {}), sort_keys=True
|
|
733
759
|
),
|
|
760
|
+
**_span_props(decorator.span),
|
|
734
761
|
},
|
|
735
762
|
)
|
|
736
763
|
|
|
@@ -746,6 +773,9 @@ def _module_props(mod: PyModule, file_key: str) -> Props:
|
|
|
746
773
|
"id": mod.id,
|
|
747
774
|
"file_key": file_key,
|
|
748
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 "",
|
|
749
779
|
"content_hash": mod.content_hash,
|
|
750
780
|
"last_modified": mod.last_modified,
|
|
751
781
|
"file_size": mod.file_size,
|
|
@@ -754,6 +784,36 @@ def _module_props(mod: PyModule, file_key: str) -> Props:
|
|
|
754
784
|
)
|
|
755
785
|
|
|
756
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
|
+
|
|
757
817
|
def _span_code(source: str, span) -> str | None:
|
|
758
818
|
"""A declaration's text: the owning module's ``source`` sliced by the node's
|
|
759
819
|
utf-8 byte span. Schema v2 stores source once per module, so the graph's
|
|
@@ -775,8 +835,8 @@ def _class_props(cl: PyClass, file_key: str, source: str) -> Props:
|
|
|
775
835
|
"base_classes": list(cl.base_classes or []),
|
|
776
836
|
"decorators": [d.qualified_name or d.name for d in (cl.decorators or [])],
|
|
777
837
|
"docstring": _docstring_of(cl.comments),
|
|
778
|
-
"start_line": cl.start_line,
|
|
779
|
-
|
|
838
|
+
**(_span_props(cl.span) or {"start_line": cl.start_line,
|
|
839
|
+
"end_line": cl.end_line}),
|
|
780
840
|
"_module": file_key,
|
|
781
841
|
"is_entrypoint": bool(cl.entrypoints),
|
|
782
842
|
"entrypoint_frameworks": sorted({e.framework for e in (cl.entrypoints or [])}),
|
|
@@ -795,8 +855,8 @@ def _callable_props(c: PyCallable, file_key: str, source: str) -> Props:
|
|
|
795
855
|
"cyclomatic_complexity": c.cyclomatic_complexity,
|
|
796
856
|
"code": _span_code(source, c.span),
|
|
797
857
|
"code_start_line": c.code_start_line,
|
|
798
|
-
"start_line": c.start_line,
|
|
799
|
-
|
|
858
|
+
**(_span_props(c.span) or {"start_line": c.start_line,
|
|
859
|
+
"end_line": c.end_line}),
|
|
800
860
|
"docstring": _docstring_of(c.comments),
|
|
801
861
|
"decorators": [d.qualified_name or d.name for d in (c.decorators or [])],
|
|
802
862
|
"modifiers": list(c.modifiers or []),
|
|
@@ -817,6 +877,8 @@ def _attribute_props(a: PyClassAttribute, attr_id: str, file_key: str) -> Props:
|
|
|
817
877
|
"type": a.type,
|
|
818
878
|
"initializer": a.initializer,
|
|
819
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.
|
|
820
882
|
"start_line": a.start_line,
|
|
821
883
|
"end_line": a.end_line,
|
|
822
884
|
"_module": file_key,
|
|
@@ -824,16 +886,21 @@ def _attribute_props(a: PyClassAttribute, attr_id: str, file_key: str) -> Props:
|
|
|
824
886
|
)
|
|
825
887
|
|
|
826
888
|
|
|
827
|
-
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:
|
|
828
891
|
return prune(
|
|
829
892
|
{
|
|
830
893
|
"id": var_id,
|
|
831
894
|
"name": v.name,
|
|
832
895
|
"type": v.type,
|
|
833
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,
|
|
834
901
|
"scope": v.scope,
|
|
835
|
-
|
|
836
|
-
|
|
902
|
+
**_flat_span_props(source, v.start_line, v.start_column,
|
|
903
|
+
v.end_line, v.end_column),
|
|
837
904
|
"_module": file_key,
|
|
838
905
|
}
|
|
839
906
|
)
|
codeanalyzer/neo4j/schema.py
CHANGED
|
@@ -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,
|
codeanalyzer/schema/py_schema.py
CHANGED
|
@@ -171,7 +171,12 @@ class SummaryEdge(BaseModel):
|
|
|
171
171
|
|
|
172
172
|
@builder
|
|
173
173
|
class ParamEdge(BaseModel):
|
|
174
|
+
"""A `param_in` (actual_in → formal_in) or `param_out` (formal_out → actual_out)
|
|
175
|
+
edge at application scope. `var` is the callee-side formal's variable — the
|
|
176
|
+
parameter name, or `<return>` for the return port — always set (#195), the
|
|
177
|
+
same value codeanalyzer-typescript carries on its `param_in[].var`."""
|
|
174
178
|
src: str; dst: str
|
|
179
|
+
var: Optional[str] = None
|
|
175
180
|
|
|
176
181
|
|
|
177
182
|
@builder
|
|
@@ -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
|
|
@@ -535,7 +535,8 @@ just populate more of the same tree:
|
|
|
535
535
|
{ "id": "can://<app>/@external/os/getcwd", "kind": "external",
|
|
536
536
|
"name": "getcwd", "module": "os" }
|
|
537
537
|
},
|
|
538
|
-
"param_in": [ { "src": "can://…/main(a)@6:4/actual_in:0", "dst": "can://…/helper(x)@formal_in:0"
|
|
538
|
+
"param_in": [ { "src": "can://…/main(a)@6:4/actual_in:0", "dst": "can://…/helper(x)@formal_in:0",
|
|
539
|
+
"var": "x" } ], // var = the callee formal
|
|
539
540
|
"param_out": [ { "src": "can://…/helper(x)@formal_out", "dst": "can://…/main(a)@6:4/actual_out" } ]
|
|
540
541
|
}
|
|
541
542
|
}
|
|
@@ -13,7 +13,7 @@ codeanalyzer/artifacts/parsers.py,sha256=xOjC53tT0Mv0k7ONGwzl5XUvpBLnZlEcGmyrX5D
|
|
|
13
13
|
codeanalyzer/dataflow/__init__.py,sha256=HCNOH24rAGLUMG94N9aC-2SsAWSntk3uys7e39ipU5o,1725
|
|
14
14
|
codeanalyzer/dataflow/access_paths.py,sha256=wC8Q9qD-RZzkoFWMVvu_6uNNmYP8z48OGp9h9v3F1d4,23623
|
|
15
15
|
codeanalyzer/dataflow/alias.py,sha256=ZYKY0DiR3GHv6YJr7C001Lode9rZsk6-gxdomUyIvwg,3928
|
|
16
|
-
codeanalyzer/dataflow/builder.py,sha256=
|
|
16
|
+
codeanalyzer/dataflow/builder.py,sha256=grzgpl82KzNiwlgdTx60INMxkTZvddT3JzgaBRO4xh4,32839
|
|
17
17
|
codeanalyzer/dataflow/cfg.py,sha256=u5YXJjAaDshdgv-9kWWITbBJFP-MwXKq6hbNh0fhDm8,25307
|
|
18
18
|
codeanalyzer/dataflow/defuse.py,sha256=LrX1ToOZzR79NlAGg5T647UAFkpiEqEnT7t0K5RXOiI,4377
|
|
19
19
|
codeanalyzer/dataflow/dominance.py,sha256=X7Ki1QRdVqaseYsJtiUL7m9MbcC2WHEv9b8bczSLFUA,5086
|
|
@@ -48,9 +48,9 @@ codeanalyzer/neo4j/__init__.py,sha256=8Ei6uThTOqmul6GhnKoGOjLXJZbyDwF5Jo-8I8NxnA
|
|
|
48
48
|
codeanalyzer/neo4j/bolt.py,sha256=8l70iGDjY3Kdm-W1sb3b3O1uqLjDrVPYTpXhnkXdZOY,13694
|
|
49
49
|
codeanalyzer/neo4j/cypher.py,sha256=-wledvXVrcLJRtSC5MCn1N3tqgC06rNfPBn0w6POqHo,6239
|
|
50
50
|
codeanalyzer/neo4j/emit.py,sha256=OBCO77TDTaNpqk9Di3JIQzBouLpHPdgoXuPSs44iJuU,3587
|
|
51
|
-
codeanalyzer/neo4j/project.py,sha256=
|
|
51
|
+
codeanalyzer/neo4j/project.py,sha256=gUAmIXLt0agWB1q1OS1Nzvlo1M6rqzzQymoy7lWxTps,42348
|
|
52
52
|
codeanalyzer/neo4j/rows.py,sha256=n_J3NVIusLPsHMjPCWaYvlL5sFm6V-kl_Z6B_BTH8Fk,10140
|
|
53
|
-
codeanalyzer/neo4j/schema.py,sha256=
|
|
53
|
+
codeanalyzer/neo4j/schema.py,sha256=0qZ1qJgmutlMHyIjdfD5N9QtA-gwl8Qcgh4NGguQEMs,18089
|
|
54
54
|
codeanalyzer/options/__init__.py,sha256=6NewN0a-1HAEgKcxQjYyVn2WEDLrhax8h3WLgZddeqI,94
|
|
55
55
|
codeanalyzer/options/options.py,sha256=2jDCcs74iSEOhz90LoRGts6PC8yV9IDPVxnLPNmonOA,1609
|
|
56
56
|
codeanalyzer/schema/__init__.py,sha256=hIyz02abUJNPW8yUgguaeKflZjTmOZWbDiKICNBD7yk,4141
|
|
@@ -59,7 +59,7 @@ codeanalyzer/schema/call_graph_ids.py,sha256=mWAhJDBsW8i-siJiINOHCXEm4qM6F_5se9-
|
|
|
59
59
|
codeanalyzer/schema/ids.py,sha256=YuioNK3Y-M2NcCBiGH4AB4rDezlqu6ycNO0R3O-Jxho,4388
|
|
60
60
|
codeanalyzer/schema/l1_body.py,sha256=_sca0mTkMRb-5lasep87faGjDxZJd9jGB0hmBx3uCgk,1757
|
|
61
61
|
codeanalyzer/schema/l2_callees.py,sha256=CTcBeGoO04DstDC6h-1sHMbbIrHDEOMxP01U9VLBcfc,2327
|
|
62
|
-
codeanalyzer/schema/py_schema.py,sha256=
|
|
62
|
+
codeanalyzer/schema/py_schema.py,sha256=4xJto32nWqHx0dkge4qHsV2JZSjbyv4ojnqpyBm78sQ,24588
|
|
63
63
|
codeanalyzer/semantic_analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
64
64
|
codeanalyzer/semantic_analysis/call_graph.py,sha256=6YEB_wTn5-oQYLrbIhJYE0BsHl4fpWPoy5Hwd9mTnGc,11918
|
|
65
65
|
codeanalyzer/semantic_analysis/defuse_linker.py,sha256=bSn1rCun28OQqSd2NOc9aVBSm47Gt9Iul4r_gwfqO1w,64930
|
|
@@ -70,9 +70,9 @@ codeanalyzer/syntactic_analysis/symbol_table_builder.py,sha256=eTknBDlUuuQd3JEwb
|
|
|
70
70
|
codeanalyzer/utils/__init__.py,sha256=hC6VWdR5rerSqBxzu9KQHTASWqwrrYJv-CMDwrTlzkc,137
|
|
71
71
|
codeanalyzer/utils/logging.py,sha256=Fw0tattPAOMs3o0JMjjXhRVLIF64f-SCcygUXF9jqeg,904
|
|
72
72
|
codeanalyzer/utils/progress_bar.py,sha256=C9JtzVdd10lIxTv-KA6PebqjKWueC_vMGwVzAtHuHIw,2818
|
|
73
|
-
codeanalyzer_python-1.5.
|
|
74
|
-
codeanalyzer_python-1.5.
|
|
75
|
-
codeanalyzer_python-1.5.
|
|
76
|
-
codeanalyzer_python-1.5.
|
|
77
|
-
codeanalyzer_python-1.5.
|
|
78
|
-
codeanalyzer_python-1.5.
|
|
73
|
+
codeanalyzer_python-1.5.2.dist-info/METADATA,sha256=hivDuG8vX-aDD0NW-giMx8A4XXclXcFCtnFR5dYJF2g,44845
|
|
74
|
+
codeanalyzer_python-1.5.2.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
75
|
+
codeanalyzer_python-1.5.2.dist-info/entry_points.txt,sha256=v4Vux0Nnx7sOntVk_CH7W9RX6SkIkvR1FQYq73oVlCQ,105
|
|
76
|
+
codeanalyzer_python-1.5.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
77
|
+
codeanalyzer_python-1.5.2.dist-info/licenses/NOTICE,sha256=MdVkNYqHJ20on2FmWvgD4WpMsX5mir33xdyPvTXd0A0,1223
|
|
78
|
+
codeanalyzer_python-1.5.2.dist-info/RECORD,,
|
|
File without changes
|
{codeanalyzer_python-1.5.0.dist-info → codeanalyzer_python-1.5.2.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{codeanalyzer_python-1.5.0.dist-info → codeanalyzer_python-1.5.2.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|