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.
Files changed (174) hide show
  1. cg_code_graph-0.10.1.dist-info/METADATA +678 -0
  2. cg_code_graph-0.10.1.dist-info/RECORD +174 -0
  3. cg_code_graph-0.10.1.dist-info/WHEEL +5 -0
  4. cg_code_graph-0.10.1.dist-info/entry_points.txt +3 -0
  5. cg_code_graph-0.10.1.dist-info/licenses/LICENSE +21 -0
  6. cg_code_graph-0.10.1.dist-info/top_level.txt +1 -0
  7. codegraph/__init__.py +2 -0
  8. codegraph/aitools.py +129 -0
  9. codegraph/apps.py +76 -0
  10. codegraph/blindspots.py +428 -0
  11. codegraph/bridges.py +1701 -0
  12. codegraph/cli.py +725 -0
  13. codegraph/concepts.py +362 -0
  14. codegraph/config.py +559 -0
  15. codegraph/core/__init__.py +0 -0
  16. codegraph/core/cache.py +375 -0
  17. codegraph/core/detect.py +80 -0
  18. codegraph/core/extractors.py +187 -0
  19. codegraph/core/fsutil.py +61 -0
  20. codegraph/core/generated.py +575 -0
  21. codegraph/core/model.py +174 -0
  22. codegraph/core/paths.py +175 -0
  23. codegraph/core/plugin.py +160 -0
  24. codegraph/core/store.py +80 -0
  25. codegraph/core/syntax_errors.py +132 -0
  26. codegraph/coverage.py +928 -0
  27. codegraph/doctor.py +453 -0
  28. codegraph/external.py +613 -0
  29. codegraph/indexer.py +336 -0
  30. codegraph/link.py +434 -0
  31. codegraph/lint_async.py +524 -0
  32. codegraph/mcp_server.py +1303 -0
  33. codegraph/parity.py +473 -0
  34. codegraph/parity_structure.py +307 -0
  35. codegraph/payload.py +321 -0
  36. codegraph/plans.py +1285 -0
  37. codegraph/platform_scan.py +643 -0
  38. codegraph/platforms.py +1369 -0
  39. codegraph/plugins/__init__.py +0 -0
  40. codegraph/plugins/cfamily/__init__.py +0 -0
  41. codegraph/plugins/cfamily/plugin.py +930 -0
  42. codegraph/plugins/cfamily/syntax.py +881 -0
  43. codegraph/plugins/dart/__init__.py +0 -0
  44. codegraph/plugins/dart/bridges.py +345 -0
  45. codegraph/plugins/dart/extractor/bin/extract.dart +717 -0
  46. codegraph/plugins/dart/extractor/pubspec.lock +149 -0
  47. codegraph/plugins/dart/extractor/pubspec.yaml +7 -0
  48. codegraph/plugins/dart/http.py +904 -0
  49. codegraph/plugins/dart/models.py +308 -0
  50. codegraph/plugins/dart/plugin.py +625 -0
  51. codegraph/plugins/dart/program.py +907 -0
  52. codegraph/plugins/django/__init__.py +0 -0
  53. codegraph/plugins/django/extras.py +378 -0
  54. codegraph/plugins/django/models.py +508 -0
  55. codegraph/plugins/django/plugin.py +728 -0
  56. codegraph/plugins/django/schemas.py +339 -0
  57. codegraph/plugins/django/shapes.py +216 -0
  58. codegraph/plugins/django/urls.py +603 -0
  59. codegraph/plugins/express/__init__.py +0 -0
  60. codegraph/plugins/express/plugin.py +428 -0
  61. codegraph/plugins/flutter/__init__.py +0 -0
  62. codegraph/plugins/flutter/plugin.py +538 -0
  63. codegraph/plugins/kotlin/__init__.py +0 -0
  64. codegraph/plugins/kotlin/exact.py +457 -0
  65. codegraph/plugins/kotlin/plugin.py +1961 -0
  66. codegraph/plugins/kotlin/reparse.py +234 -0
  67. codegraph/plugins/laravel/__init__.py +0 -0
  68. codegraph/plugins/laravel/broadcast.py +351 -0
  69. codegraph/plugins/laravel/plugin.py +863 -0
  70. codegraph/plugins/laravel/tests.py +262 -0
  71. codegraph/plugins/laravel/values.py +728 -0
  72. codegraph/plugins/native/__init__.py +0 -0
  73. codegraph/plugins/native/gates.py +286 -0
  74. codegraph/plugins/native/runner.py +183 -0
  75. codegraph/plugins/native/scipread.py +194 -0
  76. codegraph/plugins/native/ts.py +54 -0
  77. codegraph/plugins/nest/__init__.py +0 -0
  78. codegraph/plugins/nest/plugin.py +654 -0
  79. codegraph/plugins/nextjs/__init__.py +0 -0
  80. codegraph/plugins/nextjs/plugin.py +336 -0
  81. codegraph/plugins/nuxt/__init__.py +0 -0
  82. codegraph/plugins/nuxt/plugin.py +308 -0
  83. codegraph/plugins/php/__init__.py +0 -0
  84. codegraph/plugins/php/extractor/composer.json +5 -0
  85. codegraph/plugins/php/extractor/composer.lock +76 -0
  86. codegraph/plugins/php/extractor/extract.php +743 -0
  87. codegraph/plugins/php/gating.py +573 -0
  88. codegraph/plugins/php/plugin.py +668 -0
  89. codegraph/plugins/php/strings.py +197 -0
  90. codegraph/plugins/python/__init__.py +0 -0
  91. codegraph/plugins/python/aitools.py +664 -0
  92. codegraph/plugins/python/external.py +245 -0
  93. codegraph/plugins/python/fields.py +107 -0
  94. codegraph/plugins/python/plugin.py +1733 -0
  95. codegraph/plugins/python/refs.py +485 -0
  96. codegraph/plugins/python/roots.py +412 -0
  97. codegraph/plugins/python/socketio.py +210 -0
  98. codegraph/plugins/python/subproc.py +864 -0
  99. codegraph/plugins/python/tests.py +1040 -0
  100. codegraph/plugins/python/values.py +179 -0
  101. codegraph/plugins/pyweb/__init__.py +0 -0
  102. codegraph/plugins/pyweb/plugin.py +1334 -0
  103. codegraph/plugins/pyweb/values.py +68 -0
  104. codegraph/plugins/rust/__init__.py +0 -0
  105. codegraph/plugins/rust/cargo.py +226 -0
  106. codegraph/plugins/rust/plugin.py +980 -0
  107. codegraph/plugins/rust/syntax.py +678 -0
  108. codegraph/plugins/scip/__init__.py +0 -0
  109. codegraph/plugins/scip/importer.py +129 -0
  110. codegraph/plugins/scip/scip.proto +962 -0
  111. codegraph/plugins/scip/scip_pb2.py +97 -0
  112. codegraph/plugins/stubs/__init__.py +0 -0
  113. codegraph/plugins/stubs/plugins.py +38 -0
  114. codegraph/plugins/swift/__init__.py +0 -0
  115. codegraph/plugins/swift/baseurl.py +109 -0
  116. codegraph/plugins/swift/exact.py +415 -0
  117. codegraph/plugins/swift/indexstore.py +209 -0
  118. codegraph/plugins/swift/packages.py +174 -0
  119. codegraph/plugins/swift/plugin.py +2890 -0
  120. codegraph/plugins/ts/__init__.py +0 -0
  121. codegraph/plugins/ts/baseurl.py +185 -0
  122. codegraph/plugins/ts/extractor/extract.mjs +2652 -0
  123. codegraph/plugins/ts/extractor/fw.mjs +685 -0
  124. codegraph/plugins/ts/extractor/package-lock.json +205 -0
  125. codegraph/plugins/ts/extractor/package.json +9 -0
  126. codegraph/plugins/ts/plugin.py +480 -0
  127. codegraph/plugins/tsweb/__init__.py +0 -0
  128. codegraph/plugins/tsweb/common.py +290 -0
  129. codegraph/plugins/tsweb/data.py +276 -0
  130. codegraph/presets/__init__.py +146 -0
  131. codegraph/presets/c_cpp.yaml +9 -0
  132. codegraph/presets/common.yaml +66 -0
  133. codegraph/presets/dart.yaml +9 -0
  134. codegraph/presets/django-ninja.yaml +15 -0
  135. codegraph/presets/django.yaml +25 -0
  136. codegraph/presets/djangorestframework.yaml +17 -0
  137. codegraph/presets/express.yaml +17 -0
  138. codegraph/presets/kotlin.yaml +11 -0
  139. codegraph/presets/laravel.yaml +40 -0
  140. codegraph/presets/nest.yaml +11 -0
  141. codegraph/presets/nextjs.yaml +15 -0
  142. codegraph/presets/nuxt.yaml +9 -0
  143. codegraph/presets/php.yaml +5 -0
  144. codegraph/presets/python.yaml +10 -0
  145. codegraph/presets/rust.yaml +5 -0
  146. codegraph/presets/swift.yaml +10 -0
  147. codegraph/presets/typescript.yaml +13 -0
  148. codegraph/process_runs.py +328 -0
  149. codegraph/protocols/__init__.py +299 -0
  150. codegraph/protocols/builtin.py +67 -0
  151. codegraph/protocols/matchers.py +144 -0
  152. codegraph/protocols/view.py +334 -0
  153. codegraph/query.py +2089 -0
  154. codegraph/realtime.py +260 -0
  155. codegraph/roundtrip.py +346 -0
  156. codegraph/routes.py +442 -0
  157. codegraph/starters.py +218 -0
  158. codegraph/tests_index.py +117 -0
  159. codegraph/viz/__init__.py +0 -0
  160. codegraph/viz/graph.py +369 -0
  161. codegraph/viz/server.py +198 -0
  162. codegraph/viz/static/app.css +148 -0
  163. codegraph/viz/static/app.js +1082 -0
  164. codegraph/viz/static/index.html +81 -0
  165. codegraph/viz/static/layered.js +237 -0
  166. codegraph/viz/static/vendor/VERSIONS.txt +4 -0
  167. codegraph/viz/static/vendor/cose-base.js +3214 -0
  168. codegraph/viz/static/vendor/cytoscape-fcose.js +1549 -0
  169. codegraph/viz/static/vendor/cytoscape.min.js +31 -0
  170. codegraph/viz/static/vendor/layout-base.js +5230 -0
  171. codegraph/viz/tools/package-lock.json +303 -0
  172. codegraph/viz/tools/package.json +7 -0
  173. codegraph/viz/tools/shoot.mjs +165 -0
  174. codegraph/xcode.py +251 -0
codegraph/xcode.py ADDED
@@ -0,0 +1,251 @@
1
+ """Apple build files: the platforms an Xcode project (`*.xcodeproj/project.pbxproj`) and SwiftPM manifests
2
+ (`Package.swift`) build for, and which Xcode target each source file belongs to (#74).
3
+
4
+ `apple_build(root)` returns
5
+ targets {platform: where it came from}: ios / macos / tvos / watchos / visionos
6
+ mac None | "native" (an AppKit macOS target) | "catalyst" (the iOS app built for Mac) | "both"
7
+ membership {file relative to root: {platforms, targets}}: source files of Xcode targets (build phases and Xcode 16
8
+ synchronized folders with their membership exceptions), with the platforms of the targets that
9
+ compile them
10
+ projects the .pbxproj files read
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import re
15
+ from pathlib import Path
16
+
17
+ from . import presets
18
+
19
+ SKIP = presets.skip_dirs("common") | {"Pods", "Carthage", ".build", "checkouts", "DerivedData", "SourcePackages"}
20
+ SDK_PLATFORM = {"iphoneos": "ios", "macosx": "macos", "appletvos": "tvos", "watchos": "watchos", "xros": "visionos"}
21
+ SPM_PLATFORM = {"iOS": "ios", "macOS": "macos", "tvOS": "tvos", "watchOS": "watchos", "visionOS": "visionos",
22
+ "macCatalyst": "macos", "driverKit": None}
23
+ APPLE = ("macos", "ios", "tvos", "watchos", "visionos")
24
+
25
+
26
+ # ------------------------------------------------------------------ OpenStep plist (project.pbxproj)
27
+ _TOKEN = re.compile(r'\s+|//[^\n]*|/\*.*?\*/|"((?:[^"\\]|\\.)*)"|([^\s{}()=;,"]+)|([{}()=;,])', re.S)
28
+
29
+
30
+ def parse_pbxproj(text: str):
31
+ """The project.pbxproj dictionary (nested dicts, lists and strings); ValueError on a malformed file."""
32
+ toks = []
33
+ pos = 0
34
+ while pos < len(text):
35
+ m = _TOKEN.match(text, pos)
36
+ if not m:
37
+ raise ValueError(f"pbxproj: unexpected {text[pos:pos + 20]!r}")
38
+ pos = m.end()
39
+ if m.group(1) is not None:
40
+ toks.append(("s", m.group(1).encode().decode("unicode_escape", "replace") if "\\" in m.group(1) else m.group(1)))
41
+ elif m.group(2) is not None:
42
+ toks.append(("s", m.group(2)))
43
+ elif m.group(3) is not None:
44
+ toks.append(("p", m.group(3)))
45
+ i = 0
46
+
47
+ def value():
48
+ nonlocal i
49
+ kind, v = toks[i]
50
+ i += 1
51
+ if kind == "s":
52
+ return v
53
+ if v == "{":
54
+ d = {}
55
+ while toks[i] != ("p", "}"):
56
+ k = value()
57
+ if toks[i] != ("p", "="):
58
+ raise ValueError("pbxproj: '=' expected")
59
+ i += 1
60
+ d[k] = value()
61
+ if toks[i] == ("p", ";"):
62
+ i += 1
63
+ i += 1
64
+ return d
65
+ if v == "(":
66
+ out = []
67
+ while toks[i] != ("p", ")"):
68
+ out.append(value())
69
+ if toks[i] == ("p", ","):
70
+ i += 1
71
+ i += 1
72
+ return out
73
+ raise ValueError(f"pbxproj: unexpected {v!r}")
74
+ try:
75
+ return value()
76
+ except IndexError as e:
77
+ raise ValueError("pbxproj: truncated") from e
78
+
79
+
80
+ def _settings_platforms(bs: dict) -> tuple[set[str], bool]:
81
+ """Platforms of one build configuration: SUPPORTED_PLATFORMS (else SDKROOT), and SUPPORTS_MACCATALYST."""
82
+ out = set()
83
+ sup = bs.get("SUPPORTED_PLATFORMS")
84
+ for tok in (sup.split() if isinstance(sup, str) else []):
85
+ if tok in SDK_PLATFORM:
86
+ out.add(SDK_PLATFORM[tok])
87
+ if not out:
88
+ sdk = bs.get("SDKROOT")
89
+ if isinstance(sdk, str) and sdk in SDK_PLATFORM:
90
+ out.add(SDK_PLATFORM[sdk])
91
+ cat = str(bs.get("SUPPORTS_MACCATALYST", "")).upper() == "YES"
92
+ return out, cat
93
+
94
+
95
+ def _read_project(pbx: Path, root: Path) -> dict | None:
96
+ try:
97
+ d = parse_pbxproj(pbx.read_text(encoding="utf-8", errors="replace"))
98
+ objs = d["objects"]
99
+ proj = objs[d["rootObject"]]
100
+ except (OSError, ValueError, KeyError, TypeError):
101
+ return None
102
+ base = pbx.parent.parent # the directory holding Foo.xcodeproj
103
+ if proj.get("projectDirPath"):
104
+ base = base / proj["projectDirPath"]
105
+
106
+ def configs(lst_id) -> list[dict]:
107
+ lst = objs.get(lst_id) or {}
108
+ return [(objs.get(c) or {}).get("buildSettings") or {} for c in lst.get("buildConfigurations") or ()]
109
+ proj_bs = configs(proj.get("buildConfigurationList"))
110
+ # file paths: walk the group tree from the main group
111
+ paths: dict[str, Path] = {}
112
+
113
+ def walk(gid, parent: Path):
114
+ g = objs.get(gid) or {}
115
+ st = g.get("sourceTree", "<group>")
116
+ p = g.get("path")
117
+ here = parent if not p else (base / p if st == "SOURCE_ROOT" else Path(p) if st == "<absolute>" else parent / p)
118
+ if st not in ("<group>", "SOURCE_ROOT", "<absolute>"):
119
+ return
120
+ paths[gid] = here
121
+ for c in g.get("children") or ():
122
+ walk(c, here)
123
+ walk(proj.get("mainGroup"), base)
124
+ targets = []
125
+ for tid in proj.get("targets") or ():
126
+ t = objs.get(tid) or {}
127
+ if t.get("isa") != "PBXNativeTarget":
128
+ continue
129
+ plats, cat = set(), False
130
+ for bs in configs(t.get("buildConfigurationList")) or [{}]:
131
+ merged = {**(proj_bs[0] if proj_bs else {}), **bs}
132
+ p, c = _settings_platforms(merged)
133
+ plats |= p
134
+ cat |= c
135
+ files = set()
136
+ for ph in t.get("buildPhases") or ():
137
+ phase = objs.get(ph) or {}
138
+ if phase.get("isa") != "PBXSourcesBuildPhase":
139
+ continue
140
+ for bf in phase.get("files") or ():
141
+ ref = (objs.get(bf) or {}).get("fileRef")
142
+ if ref in paths:
143
+ files.add(paths[ref])
144
+ targets.append({"id": tid, "name": t.get("name") or tid, "platforms": plats, "catalyst": cat, "files": files,
145
+ "groups": set(t.get("fileSystemSynchronizedGroups") or ())})
146
+ # Xcode 16 synchronized folders: every file under the folder belongs to the targets that list the folder; a
147
+ # membership exception for such a target takes the file out, one for another target adds it there
148
+ by_id = {t["id"]: t for t in targets}
149
+ for gid, g in objs.items():
150
+ if not isinstance(g, dict) or g.get("isa") != "PBXFileSystemSynchronizedRootGroup" or gid not in paths:
151
+ continue
152
+ folder = paths[gid]
153
+ owners = [t for t in targets if gid in t["groups"]]
154
+ excl: dict[str, set] = {}
155
+ for ex in g.get("exceptions") or ():
156
+ e = objs.get(ex) or {}
157
+ if e.get("isa") == "PBXFileSystemSynchronizedBuildFileExceptionSet" and e.get("target") in by_id:
158
+ excl.setdefault(e["target"], set()).update(e.get("membershipExceptions") or ())
159
+ try:
160
+ rel_files = [f.relative_to(folder).as_posix() for f in folder.rglob("*")
161
+ if f.is_file() and f.suffix in (".swift", ".m", ".mm", ".c", ".cpp")]
162
+ except OSError:
163
+ rel_files = []
164
+ for t in targets:
165
+ ex = excl.get(t["id"], set())
166
+ if t in owners:
167
+ t["files"] |= {folder / r for r in rel_files if r not in ex}
168
+ elif ex:
169
+ t["files"] |= {folder / r for r in rel_files if r in ex}
170
+ try:
171
+ where = pbx.parent.relative_to(root).as_posix()
172
+ except ValueError:
173
+ where = pbx.parent.as_posix()
174
+ return {"where": where, "targets": targets}
175
+
176
+
177
+ def _projects(root: Path) -> list[Path]:
178
+ out = []
179
+ for pat in ("*.xcodeproj/project.pbxproj", "*/*.xcodeproj/project.pbxproj", "*/*/*.xcodeproj/project.pbxproj"):
180
+ for p in root.glob(pat):
181
+ if SKIP.isdisjoint(p.relative_to(root).parts[:-2]):
182
+ out.append(p)
183
+ return sorted(out)
184
+
185
+
186
+ def _manifests(root: Path, depth: int = 4) -> list[Path]:
187
+ out = []
188
+ for lvl in range(depth):
189
+ for p in root.glob("/".join(["*"] * lvl + ["Package.swift"])):
190
+ if SKIP.isdisjoint(p.relative_to(root).parts[:-1]):
191
+ out.append(p)
192
+ return out
193
+
194
+
195
+ def package_platforms(pk: Path) -> list[str]:
196
+ """`platforms: [.iOS(.v15), .macOS("12.0"), .visionOS(.v1)]` of one Package.swift, as SwiftPM names."""
197
+ try:
198
+ txt = pk.read_text(encoding="utf-8", errors="replace")
199
+ except OSError:
200
+ return []
201
+ m = re.search(r"\bplatforms\s*:\s*\[([^\]]*)\]", txt)
202
+ if not m:
203
+ return []
204
+ names = re.findall(r"\.(\w+)\s*\(", m.group(1)) + re.findall(r"\.(\w+)\s*(?=[,\]]|$)", m.group(1))
205
+ return [n for n in dict.fromkeys(names) if n in SPM_PLATFORM]
206
+
207
+
208
+ def apple_build(root: Path | str) -> dict:
209
+ root = Path(root)
210
+ out = {"targets": {}, "mac": None, "membership": {}, "projects": []}
211
+ native = catalyst = False
212
+ for pbx in _projects(root):
213
+ pr = _read_project(pbx, root)
214
+ if pr is None:
215
+ continue
216
+ out["projects"].append(pr["where"])
217
+ for t in pr["targets"]:
218
+ for p in sorted(t["platforms"], key=APPLE.index):
219
+ out["targets"].setdefault(p, f"{pr['where']} target {t['name']}: SDKROOT / SUPPORTED_PLATFORMS")
220
+ if t["catalyst"]:
221
+ out["targets"].setdefault("macos", f"{pr['where']} target {t['name']}: SUPPORTS_MACCATALYST")
222
+ native |= "macos" in t["platforms"]
223
+ catalyst |= t["catalyst"]
224
+ plats = set(t["platforms"]) | ({"macos"} if t["catalyst"] else set())
225
+ for f in t["files"]:
226
+ try:
227
+ rel = f.resolve().relative_to(root.resolve()).as_posix()
228
+ except (ValueError, OSError):
229
+ continue
230
+ m = out["membership"].setdefault(rel, {"platforms": set(), "targets": []})
231
+ m["platforms"] |= plats
232
+ if t["name"] not in m["targets"]:
233
+ m["targets"].append(t["name"])
234
+ # SwiftPM: the root manifest (one level down for a repo holding the package in a folder); local packages only
235
+ # when no Xcode project or root manifest names a platform
236
+ pks = [p for p in [root / "Package.swift", *root.glob("*/Package.swift")] if p.is_file()]
237
+ for pass_ in (pks, [] if out["targets"] or any(package_platforms(p) for p in pks) else _manifests(root)):
238
+ for pk in pass_:
239
+ names = package_platforms(pk)
240
+ where = pk.relative_to(root).as_posix()
241
+ for n in names:
242
+ p = SPM_PLATFORM[n]
243
+ if p:
244
+ out["targets"].setdefault(p, f"{where} platforms: .{n}")
245
+ native |= "macOS" in names
246
+ catalyst |= "macCatalyst" in names and "macOS" not in names
247
+ if "macos" in out["targets"]:
248
+ out["mac"] = "both" if native and catalyst else "catalyst" if catalyst else "native"
249
+ for m in out["membership"].values():
250
+ m["platforms"] = sorted(m["platforms"], key=APPLE.index)
251
+ return out