cg-code-graph 0.10.1__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.
- cg_code_graph-0.10.1.dist-info/METADATA +678 -0
- cg_code_graph-0.10.1.dist-info/RECORD +174 -0
- cg_code_graph-0.10.1.dist-info/WHEEL +5 -0
- cg_code_graph-0.10.1.dist-info/entry_points.txt +3 -0
- cg_code_graph-0.10.1.dist-info/licenses/LICENSE +21 -0
- cg_code_graph-0.10.1.dist-info/top_level.txt +1 -0
- codegraph/__init__.py +2 -0
- codegraph/aitools.py +129 -0
- codegraph/apps.py +76 -0
- codegraph/blindspots.py +428 -0
- codegraph/bridges.py +1701 -0
- codegraph/cli.py +725 -0
- codegraph/concepts.py +362 -0
- codegraph/config.py +559 -0
- codegraph/core/__init__.py +0 -0
- codegraph/core/cache.py +375 -0
- codegraph/core/detect.py +80 -0
- codegraph/core/extractors.py +187 -0
- codegraph/core/fsutil.py +61 -0
- codegraph/core/generated.py +575 -0
- codegraph/core/model.py +174 -0
- codegraph/core/paths.py +175 -0
- codegraph/core/plugin.py +160 -0
- codegraph/core/store.py +80 -0
- codegraph/core/syntax_errors.py +132 -0
- codegraph/coverage.py +928 -0
- codegraph/doctor.py +453 -0
- codegraph/external.py +613 -0
- codegraph/indexer.py +336 -0
- codegraph/link.py +434 -0
- codegraph/lint_async.py +524 -0
- codegraph/mcp_server.py +1303 -0
- codegraph/parity.py +473 -0
- codegraph/parity_structure.py +307 -0
- codegraph/payload.py +321 -0
- codegraph/plans.py +1285 -0
- codegraph/platform_scan.py +643 -0
- codegraph/platforms.py +1369 -0
- codegraph/plugins/__init__.py +0 -0
- codegraph/plugins/cfamily/__init__.py +0 -0
- codegraph/plugins/cfamily/plugin.py +930 -0
- codegraph/plugins/cfamily/syntax.py +881 -0
- codegraph/plugins/dart/__init__.py +0 -0
- codegraph/plugins/dart/bridges.py +345 -0
- codegraph/plugins/dart/extractor/bin/extract.dart +717 -0
- codegraph/plugins/dart/extractor/pubspec.lock +149 -0
- codegraph/plugins/dart/extractor/pubspec.yaml +7 -0
- codegraph/plugins/dart/http.py +904 -0
- codegraph/plugins/dart/models.py +308 -0
- codegraph/plugins/dart/plugin.py +625 -0
- codegraph/plugins/dart/program.py +907 -0
- codegraph/plugins/django/__init__.py +0 -0
- codegraph/plugins/django/extras.py +378 -0
- codegraph/plugins/django/models.py +508 -0
- codegraph/plugins/django/plugin.py +728 -0
- codegraph/plugins/django/schemas.py +339 -0
- codegraph/plugins/django/shapes.py +216 -0
- codegraph/plugins/django/urls.py +603 -0
- codegraph/plugins/express/__init__.py +0 -0
- codegraph/plugins/express/plugin.py +428 -0
- codegraph/plugins/flutter/__init__.py +0 -0
- codegraph/plugins/flutter/plugin.py +538 -0
- codegraph/plugins/kotlin/__init__.py +0 -0
- codegraph/plugins/kotlin/exact.py +457 -0
- codegraph/plugins/kotlin/plugin.py +1961 -0
- codegraph/plugins/kotlin/reparse.py +234 -0
- codegraph/plugins/laravel/__init__.py +0 -0
- codegraph/plugins/laravel/broadcast.py +351 -0
- codegraph/plugins/laravel/plugin.py +863 -0
- codegraph/plugins/laravel/tests.py +262 -0
- codegraph/plugins/laravel/values.py +728 -0
- codegraph/plugins/native/__init__.py +0 -0
- codegraph/plugins/native/gates.py +286 -0
- codegraph/plugins/native/runner.py +183 -0
- codegraph/plugins/native/scipread.py +194 -0
- codegraph/plugins/native/ts.py +54 -0
- codegraph/plugins/nest/__init__.py +0 -0
- codegraph/plugins/nest/plugin.py +654 -0
- codegraph/plugins/nextjs/__init__.py +0 -0
- codegraph/plugins/nextjs/plugin.py +336 -0
- codegraph/plugins/nuxt/__init__.py +0 -0
- codegraph/plugins/nuxt/plugin.py +308 -0
- codegraph/plugins/php/__init__.py +0 -0
- codegraph/plugins/php/extractor/composer.json +5 -0
- codegraph/plugins/php/extractor/composer.lock +76 -0
- codegraph/plugins/php/extractor/extract.php +743 -0
- codegraph/plugins/php/gating.py +573 -0
- codegraph/plugins/php/plugin.py +668 -0
- codegraph/plugins/php/strings.py +197 -0
- codegraph/plugins/python/__init__.py +0 -0
- codegraph/plugins/python/aitools.py +664 -0
- codegraph/plugins/python/external.py +245 -0
- codegraph/plugins/python/fields.py +107 -0
- codegraph/plugins/python/plugin.py +1733 -0
- codegraph/plugins/python/refs.py +485 -0
- codegraph/plugins/python/roots.py +412 -0
- codegraph/plugins/python/socketio.py +210 -0
- codegraph/plugins/python/subproc.py +864 -0
- codegraph/plugins/python/tests.py +1040 -0
- codegraph/plugins/python/values.py +179 -0
- codegraph/plugins/pyweb/__init__.py +0 -0
- codegraph/plugins/pyweb/plugin.py +1334 -0
- codegraph/plugins/pyweb/values.py +68 -0
- codegraph/plugins/rust/__init__.py +0 -0
- codegraph/plugins/rust/cargo.py +226 -0
- codegraph/plugins/rust/plugin.py +980 -0
- codegraph/plugins/rust/syntax.py +678 -0
- codegraph/plugins/scip/__init__.py +0 -0
- codegraph/plugins/scip/importer.py +129 -0
- codegraph/plugins/scip/scip.proto +962 -0
- codegraph/plugins/scip/scip_pb2.py +97 -0
- codegraph/plugins/stubs/__init__.py +0 -0
- codegraph/plugins/stubs/plugins.py +38 -0
- codegraph/plugins/swift/__init__.py +0 -0
- codegraph/plugins/swift/baseurl.py +109 -0
- codegraph/plugins/swift/exact.py +415 -0
- codegraph/plugins/swift/indexstore.py +209 -0
- codegraph/plugins/swift/packages.py +174 -0
- codegraph/plugins/swift/plugin.py +2890 -0
- codegraph/plugins/ts/__init__.py +0 -0
- codegraph/plugins/ts/baseurl.py +185 -0
- codegraph/plugins/ts/extractor/extract.mjs +2652 -0
- codegraph/plugins/ts/extractor/fw.mjs +685 -0
- codegraph/plugins/ts/extractor/package-lock.json +205 -0
- codegraph/plugins/ts/extractor/package.json +9 -0
- codegraph/plugins/ts/plugin.py +480 -0
- codegraph/plugins/tsweb/__init__.py +0 -0
- codegraph/plugins/tsweb/common.py +290 -0
- codegraph/plugins/tsweb/data.py +276 -0
- codegraph/presets/__init__.py +146 -0
- codegraph/presets/c_cpp.yaml +9 -0
- codegraph/presets/common.yaml +66 -0
- codegraph/presets/dart.yaml +9 -0
- codegraph/presets/django-ninja.yaml +15 -0
- codegraph/presets/django.yaml +25 -0
- codegraph/presets/djangorestframework.yaml +17 -0
- codegraph/presets/express.yaml +17 -0
- codegraph/presets/kotlin.yaml +11 -0
- codegraph/presets/laravel.yaml +40 -0
- codegraph/presets/nest.yaml +11 -0
- codegraph/presets/nextjs.yaml +15 -0
- codegraph/presets/nuxt.yaml +9 -0
- codegraph/presets/php.yaml +5 -0
- codegraph/presets/python.yaml +10 -0
- codegraph/presets/rust.yaml +5 -0
- codegraph/presets/swift.yaml +10 -0
- codegraph/presets/typescript.yaml +13 -0
- codegraph/process_runs.py +328 -0
- codegraph/protocols/__init__.py +299 -0
- codegraph/protocols/builtin.py +67 -0
- codegraph/protocols/matchers.py +144 -0
- codegraph/protocols/view.py +334 -0
- codegraph/query.py +2089 -0
- codegraph/realtime.py +260 -0
- codegraph/roundtrip.py +346 -0
- codegraph/routes.py +442 -0
- codegraph/starters.py +218 -0
- codegraph/tests_index.py +117 -0
- codegraph/viz/__init__.py +0 -0
- codegraph/viz/graph.py +369 -0
- codegraph/viz/server.py +198 -0
- codegraph/viz/static/app.css +148 -0
- codegraph/viz/static/app.js +1082 -0
- codegraph/viz/static/index.html +81 -0
- codegraph/viz/static/layered.js +237 -0
- codegraph/viz/static/vendor/VERSIONS.txt +4 -0
- codegraph/viz/static/vendor/cose-base.js +3214 -0
- codegraph/viz/static/vendor/cytoscape-fcose.js +1549 -0
- codegraph/viz/static/vendor/cytoscape.min.js +31 -0
- codegraph/viz/static/vendor/layout-base.js +5230 -0
- codegraph/viz/tools/package-lock.json +303 -0
- codegraph/viz/tools/package.json +7 -0
- codegraph/viz/tools/shoot.mjs +165 -0
- codegraph/xcode.py +251 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""Enum members and module constants (#84): `enum_case:<module>.<Enum>.<MEMBER>` for the members of a class derived
|
|
2
|
+
from `enum.Enum` (IntEnum, StrEnum, Flag, IntFlag, a project enum base), `constant:<module>.<NAME>` for a module-level
|
|
3
|
+
UPPER_CASE or `Final` name, and USES_VALUE edges where the binding is certain: `Color.RED`, `mod.NAME`, an imported
|
|
4
|
+
`NAME`, or a bare `NAME` of the module that no local binding of the function shadows. Call edges are untouched."""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import ast
|
|
8
|
+
import re
|
|
9
|
+
|
|
10
|
+
from ...core.model import EXACT
|
|
11
|
+
|
|
12
|
+
CONST_NAME = re.compile(r"^_?[A-Z][A-Z0-9_]*$")
|
|
13
|
+
ENUM_BASES = ("Enum", "IntEnum", "StrEnum", "Flag", "IntFlag", "ReprEnum")
|
|
14
|
+
NOT_CONSTANT_CALLS = {"TypeVar", "ParamSpec", "TypeVarTuple", "NewType", "namedtuple", "NamedTuple", "TypedDict"}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _final(ann) -> bool:
|
|
18
|
+
if ann is None:
|
|
19
|
+
return False
|
|
20
|
+
a = ann.value if isinstance(ann, ast.Subscript) else ann
|
|
21
|
+
return (isinstance(a, ast.Name) and a.id == "Final") or (isinstance(a, ast.Attribute) and a.attr == "Final")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _called(v) -> str | None:
|
|
25
|
+
if isinstance(v, ast.Call):
|
|
26
|
+
f = v.func
|
|
27
|
+
return f.id if isinstance(f, ast.Name) else f.attr if isinstance(f, ast.Attribute) else None
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _top_assigns(body):
|
|
32
|
+
"""Module-level assignments, including those under a top-level `if` / `try` (`try: X = 1 except: X = 2`)."""
|
|
33
|
+
for st in body:
|
|
34
|
+
if isinstance(st, (ast.Assign, ast.AnnAssign)):
|
|
35
|
+
yield st
|
|
36
|
+
elif isinstance(st, (ast.If, ast.Try)) or type(st).__name__ == "TryStar":
|
|
37
|
+
yield from _top_assigns(getattr(st, "body", []) + getattr(st, "orelse", []) + getattr(st, "finalbody", []))
|
|
38
|
+
for h in getattr(st, "handlers", []):
|
|
39
|
+
yield from _top_assigns(h.body)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _targets(st) -> list[str]:
|
|
43
|
+
tg = st.targets if isinstance(st, ast.Assign) else [st.target]
|
|
44
|
+
return [t.id for t in tg if isinstance(t, ast.Name)]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Values:
|
|
48
|
+
def __init__(self, prog, b):
|
|
49
|
+
self.prog, self.b = prog, b
|
|
50
|
+
self.cases: dict[str, dict[str, str]] = {} # enum class qual -> member -> id
|
|
51
|
+
self.consts: dict[str, dict[str, str]] = {} # module name -> NAME -> id
|
|
52
|
+
self.st = {"enum_cases": 0, "constants": 0, "value_refs": 0}
|
|
53
|
+
|
|
54
|
+
def run(self) -> dict:
|
|
55
|
+
prog = self.prog
|
|
56
|
+
for c in prog.classes.values():
|
|
57
|
+
if c.name in ENUM_BASES or not prog.subclass_of(c, *ENUM_BASES):
|
|
58
|
+
continue
|
|
59
|
+
for st in c.node.body:
|
|
60
|
+
if not isinstance(st, (ast.Assign, ast.AnnAssign)) or getattr(st, "value", None) is None:
|
|
61
|
+
continue
|
|
62
|
+
if isinstance(st.value, (ast.Lambda,)) or _called(st.value) in ("property", "staticmethod", "classmethod"):
|
|
63
|
+
continue
|
|
64
|
+
for nm in _targets(st):
|
|
65
|
+
if nm.startswith("_"):
|
|
66
|
+
continue # `_ignore_`, `_order_`, private names: not members
|
|
67
|
+
self._node("enum_case", c.qual, nm, c.id, c.file, st)
|
|
68
|
+
self.cases.setdefault(c.qual, {})[nm] = f"enum_case:{c.qual}.{nm}"
|
|
69
|
+
for m in prog.modules.values():
|
|
70
|
+
for st in _top_assigns(m.tree.body):
|
|
71
|
+
if getattr(st, "value", None) is None or _called(st.value) in NOT_CONSTANT_CALLS:
|
|
72
|
+
continue
|
|
73
|
+
for nm in _targets(st):
|
|
74
|
+
if nm in m.classes or nm in m.funcs or nm.startswith("__") or \
|
|
75
|
+
not (CONST_NAME.match(nm) or _final(getattr(st, "annotation", None))):
|
|
76
|
+
continue
|
|
77
|
+
if nm in self.consts.get(m.name, {}):
|
|
78
|
+
continue # `X = 1` re-assigned later (or in an `else`): one constant
|
|
79
|
+
self._node("constant", m.name, nm, m.id, m.file, st)
|
|
80
|
+
self.consts.setdefault(m.name, {})[nm] = f"constant:{m.name}.{nm}"
|
|
81
|
+
if self.cases or self.consts:
|
|
82
|
+
self._refs()
|
|
83
|
+
return self.st
|
|
84
|
+
|
|
85
|
+
def _node(self, kind, owner_q, nm, owner_id, file, st):
|
|
86
|
+
nid = self.b.add_node(kind, f"{owner_q}.{nm}", name=nm, fqn=f"{owner_q}.{nm}", file=file, line=st.lineno,
|
|
87
|
+
end_line=getattr(st, "end_lineno", None), lang="python")
|
|
88
|
+
self.b.add_edge(owner_id, nid, "CONTAINS", file, st.lineno, EXACT)
|
|
89
|
+
self.st["enum_cases" if kind == "enum_case" else "constants"] += 1
|
|
90
|
+
|
|
91
|
+
# ------------------------------------------------------------------ references
|
|
92
|
+
def _refs(self):
|
|
93
|
+
prog = self.prog
|
|
94
|
+
for f in list(prog.funcs.values()):
|
|
95
|
+
self._scan(f.id, f.module, f.node, f.file, self._locals(f.node), prog.func_imports(f))
|
|
96
|
+
for m in prog.modules.values():
|
|
97
|
+
self._scan_body(m.id, m, m.tree.body, m.file)
|
|
98
|
+
for c in m.all_classes:
|
|
99
|
+
self._scan_body(c.id, m, c.node.body, m.file, cls=c)
|
|
100
|
+
|
|
101
|
+
def _scan_body(self, owner, m, body, file, cls=None):
|
|
102
|
+
for st in body:
|
|
103
|
+
if isinstance(st, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
|
|
104
|
+
for d in getattr(st, "decorator_list", []):
|
|
105
|
+
self._scan(owner, m, d, file, set(), {})
|
|
106
|
+
continue
|
|
107
|
+
# class body: its own names are class attributes (an enum's members among them), not module constants
|
|
108
|
+
local = {t for s in body if isinstance(s, (ast.Assign, ast.AnnAssign)) for t in _targets(s)} \
|
|
109
|
+
if cls is not None else set()
|
|
110
|
+
self._scan(owner, m, st, file, local, {})
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def _locals(fn) -> set[str]:
|
|
114
|
+
"""Names a def binds (Python scoping: a name assigned anywhere in the function is local throughout)."""
|
|
115
|
+
out, glob = set(), set()
|
|
116
|
+
a = fn.args
|
|
117
|
+
for x in a.posonlyargs + a.args + a.kwonlyargs + [a.vararg, a.kwarg]:
|
|
118
|
+
if x is not None:
|
|
119
|
+
out.add(x.arg)
|
|
120
|
+
for n in ast.walk(fn):
|
|
121
|
+
if isinstance(n, ast.Name) and isinstance(n.ctx, (ast.Store, ast.Del)):
|
|
122
|
+
out.add(n.id)
|
|
123
|
+
elif isinstance(n, (ast.Global, ast.Nonlocal)):
|
|
124
|
+
glob.update(n.names)
|
|
125
|
+
elif isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)) and n is not fn:
|
|
126
|
+
out.add(n.name)
|
|
127
|
+
elif isinstance(n, ast.arg):
|
|
128
|
+
out.add(n.arg) # a nested def's / lambda's parameter: shadows conservatively
|
|
129
|
+
elif isinstance(n, (ast.Import, ast.ImportFrom)):
|
|
130
|
+
out.update((al.asname or al.name).split(".")[0] for al in n.names)
|
|
131
|
+
elif isinstance(n, ast.ExceptHandler) and n.name:
|
|
132
|
+
out.add(n.name)
|
|
133
|
+
return out - glob
|
|
134
|
+
|
|
135
|
+
def _resolve(self, m, e, local, fimps, depth=0):
|
|
136
|
+
"""What expression `e` names: ("type", ClassInfo) / ("mod", ModInfo) / ("var", ModInfo, name) / None."""
|
|
137
|
+
prog = self.prog
|
|
138
|
+
if depth > 6:
|
|
139
|
+
return None
|
|
140
|
+
if isinstance(e, ast.Name):
|
|
141
|
+
if e.id in local and e.id not in fimps:
|
|
142
|
+
return None
|
|
143
|
+
if e.id in fimps:
|
|
144
|
+
return prog._resolve_import(fimps[e.id])
|
|
145
|
+
return prog.resolve_name(m, e.id)
|
|
146
|
+
if isinstance(e, ast.Attribute):
|
|
147
|
+
base = self._resolve(m, e.value, local, fimps, depth + 1)
|
|
148
|
+
if base is None:
|
|
149
|
+
return None
|
|
150
|
+
if base[0] == "mod":
|
|
151
|
+
return prog.lookup(base[1].name, e.attr)
|
|
152
|
+
if base[0] == "type":
|
|
153
|
+
inner = getattr(base[1], "inner_infos", {}).get(e.attr)
|
|
154
|
+
return ("type", inner) if inner is not None else None
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
def _value_of(self, r, attr: str | None = None):
|
|
158
|
+
if r is None:
|
|
159
|
+
return None
|
|
160
|
+
if attr is not None:
|
|
161
|
+
if r[0] == "type":
|
|
162
|
+
return self.cases.get(r[1].qual, {}).get(attr)
|
|
163
|
+
if r[0] == "mod":
|
|
164
|
+
return self.consts.get(r[1].name, {}).get(attr)
|
|
165
|
+
return None
|
|
166
|
+
if r[0] == "var":
|
|
167
|
+
return self.consts.get(r[1].name, {}).get(r[2])
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
def _scan(self, owner, m, node, file, local, fimps):
|
|
171
|
+
for n in ast.walk(node):
|
|
172
|
+
vid = None
|
|
173
|
+
if isinstance(n, ast.Attribute) and isinstance(n.ctx, ast.Load):
|
|
174
|
+
vid = self._value_of(self._resolve(m, n.value, local, fimps), n.attr)
|
|
175
|
+
elif isinstance(n, ast.Name) and isinstance(n.ctx, ast.Load) and (n.id in fimps or n.id not in local):
|
|
176
|
+
vid = self._value_of(self._resolve(m, n, local, fimps))
|
|
177
|
+
if vid is not None and vid != owner:
|
|
178
|
+
self.b.add_edge(owner, vid, "USES_VALUE", file, n.lineno, EXACT)
|
|
179
|
+
self.st["value_refs"] += 1
|
|
File without changes
|