pycode-kg 0.16.0__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 (63) hide show
  1. pycode_kg/.DS_Store +0 -0
  2. pycode_kg/__init__.py +91 -0
  3. pycode_kg/__main__.py +11 -0
  4. pycode_kg/analysis/__init__.py +15 -0
  5. pycode_kg/analysis/bridge.py +108 -0
  6. pycode_kg/analysis/centrality.py +412 -0
  7. pycode_kg/analysis/framework_detector.py +103 -0
  8. pycode_kg/analysis/hybrid_rank.py +53 -0
  9. pycode_kg/app.py +1335 -0
  10. pycode_kg/architecture.py +624 -0
  11. pycode_kg/build_pycodekg_lancedb.py +15 -0
  12. pycode_kg/build_pycodekg_sqlite.py +15 -0
  13. pycode_kg/cli/__init__.py +29 -0
  14. pycode_kg/cli/cmd_analyze.py +96 -0
  15. pycode_kg/cli/cmd_architecture.py +150 -0
  16. pycode_kg/cli/cmd_bridges.py +26 -0
  17. pycode_kg/cli/cmd_build.py +154 -0
  18. pycode_kg/cli/cmd_build_full.py +242 -0
  19. pycode_kg/cli/cmd_centrality.py +131 -0
  20. pycode_kg/cli/cmd_explain.py +180 -0
  21. pycode_kg/cli/cmd_framework_nodes.py +18 -0
  22. pycode_kg/cli/cmd_hooks.py +137 -0
  23. pycode_kg/cli/cmd_init.py +312 -0
  24. pycode_kg/cli/cmd_mcp.py +71 -0
  25. pycode_kg/cli/cmd_model.py +53 -0
  26. pycode_kg/cli/cmd_query.py +211 -0
  27. pycode_kg/cli/cmd_snapshot.py +421 -0
  28. pycode_kg/cli/cmd_viz.py +180 -0
  29. pycode_kg/cli/main.py +23 -0
  30. pycode_kg/cli/options.py +63 -0
  31. pycode_kg/config.py +78 -0
  32. pycode_kg/graph.py +125 -0
  33. pycode_kg/index.py +542 -0
  34. pycode_kg/kg.py +220 -0
  35. pycode_kg/layout3d.py +470 -0
  36. pycode_kg/mcp/bridge_tools.py +19 -0
  37. pycode_kg/mcp/framework_tools.py +18 -0
  38. pycode_kg/mcp_server.py +1965 -0
  39. pycode_kg/module/__init__.py +83 -0
  40. pycode_kg/module/base.py +720 -0
  41. pycode_kg/module/extractor.py +276 -0
  42. pycode_kg/module/types.py +532 -0
  43. pycode_kg/pycodekg.py +543 -0
  44. pycode_kg/pycodekg_query.py +1 -0
  45. pycode_kg/pycodekg_snippet_packer.py +1 -0
  46. pycode_kg/pycodekg_thorough_analysis.py +2751 -0
  47. pycode_kg/pycodekg_viz.py +1 -0
  48. pycode_kg/pycodekg_viz3d.py +1 -0
  49. pycode_kg/ranking/__init__.py +1 -0
  50. pycode_kg/ranking/cli_rank.py +92 -0
  51. pycode_kg/ranking/coderank.py +555 -0
  52. pycode_kg/snapshots.py +612 -0
  53. pycode_kg/sql/004_add_centrality_table.sql +12 -0
  54. pycode_kg/store.py +766 -0
  55. pycode_kg/utils.py +39 -0
  56. pycode_kg/visitor.py +413 -0
  57. pycode_kg/viz3d.py +1353 -0
  58. pycode_kg/viz3d_timeline.py +364 -0
  59. pycode_kg-0.16.0.dist-info/METADATA +305 -0
  60. pycode_kg-0.16.0.dist-info/RECORD +63 -0
  61. pycode_kg-0.16.0.dist-info/WHEEL +4 -0
  62. pycode_kg-0.16.0.dist-info/entry_points.txt +19 -0
  63. pycode_kg-0.16.0.dist-info/licenses/LICENSE +94 -0
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ module/extractor.py
4
+
5
+ KGExtractor — abstract extraction protocol for any knowledge graph domain.
6
+
7
+ NodeSpec and EdgeSpec are the canonical intermediate types between
8
+ domain-specific source parsing and the generic GraphStore / SemanticIndex
9
+ infrastructure provided by KGModule.
10
+
11
+ Also provides PyCodeKGExtractor — the Python-AST-backed implementation that
12
+ drives the existing PyCodeKG build pipeline.
13
+
14
+ Author: Eric G. Suchanek, PhD
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from abc import ABC, abstractmethod
20
+ from collections.abc import Iterator
21
+ from dataclasses import dataclass, field
22
+ from pathlib import Path
23
+ from typing import Any
24
+
25
+ # ---------------------------------------------------------------------------
26
+ # Intermediate node / edge representations
27
+ # ---------------------------------------------------------------------------
28
+
29
+
30
+ @dataclass
31
+ class NodeSpec:
32
+ """Specification for a single graph node emitted by a KGExtractor.
33
+
34
+ This is the canonical intermediate type between domain-specific source
35
+ parsing and the generic GraphStore persistence layer.
36
+
37
+ :param node_id: Stable unique ID (e.g. ``'fn:src/foo.py:bar'``).
38
+ :param kind: Domain node type (``'function'``, ``'gene'``, ``'statute'``, …).
39
+ :param name: Human-readable short name.
40
+ :param qualname: Fully-qualified name within its source (may equal ``name``).
41
+ :param source_path: Repo-relative path to source file or document.
42
+ :param lineno: 1-based start line (source-code KGs); ``None`` for doc/domain KGs.
43
+ :param end_lineno: 1-based end line; ``None`` if not applicable.
44
+ :param docstring: Docstring, description, or excerpt used for embedding.
45
+ :param metadata: Domain-specific extension data (serialized as JSON in the store).
46
+ """
47
+
48
+ node_id: str
49
+ kind: str
50
+ name: str
51
+ qualname: str
52
+ source_path: str
53
+ lineno: int | None = None
54
+ end_lineno: int | None = None
55
+ docstring: str = ""
56
+ metadata: dict[str, Any] = field(default_factory=dict)
57
+
58
+
59
+ @dataclass
60
+ class EdgeSpec:
61
+ """Specification for a single directed graph edge emitted by a KGExtractor.
62
+
63
+ :param source_id: ``node_id`` of the source node.
64
+ :param target_id: ``node_id`` of the target node (may be an unresolved stub ID).
65
+ :param relation: Edge type (``'CALLS'``, ``'IMPORTS'``, ``'CONTAINS'``, …).
66
+ :param weight: Edge weight for PageRank (default 1.0).
67
+ :param metadata: Domain-specific edge data (serialized as evidence JSON in the store).
68
+ """
69
+
70
+ source_id: str
71
+ target_id: str
72
+ relation: str
73
+ weight: float = 1.0
74
+ metadata: dict[str, Any] = field(default_factory=dict)
75
+
76
+
77
+ # ---------------------------------------------------------------------------
78
+ # KGExtractor — abstract domain extraction protocol
79
+ # ---------------------------------------------------------------------------
80
+
81
+
82
+ class KGExtractor(ABC):
83
+ """Abstract extraction protocol for a knowledge graph domain.
84
+
85
+ Subclass this to teach :class:`~pycode_kg.module.base.KGModule` how to
86
+ traverse your source and emit :class:`NodeSpec` / :class:`EdgeSpec`
87
+ objects. The base infrastructure (:class:`~pycode_kg.store.GraphStore`,
88
+ :class:`~pycode_kg.index.SemanticIndex`, snapshot management, MCP server,
89
+ CLI) is provided by :class:`~pycode_kg.module.base.KGModule` — you only
90
+ implement the domain-specific parsing.
91
+
92
+ Minimal subclass::
93
+
94
+ class MyExtractor(KGExtractor):
95
+ def node_kinds(self): return ["entity", "relation"]
96
+ def edge_kinds(self): return ["LINKED_TO"]
97
+ def extract(self):
98
+ for item in my_parser(self.repo_path):
99
+ yield NodeSpec(node_id=item.id, kind="entity", ...)
100
+
101
+ :param repo_path: Absolute path to the repository or corpus root.
102
+ :param config: Domain-specific configuration dict.
103
+ """
104
+
105
+ def __init__(self, repo_path: Path, config: dict[str, Any] | None = None) -> None:
106
+ """Initialise the extractor.
107
+
108
+ :param repo_path: Path to the repository or corpus root.
109
+ :param config: Optional domain-specific configuration dict.
110
+ """
111
+ self.repo_path = Path(repo_path).resolve()
112
+ self.config = config or {}
113
+
114
+ @abstractmethod
115
+ def node_kinds(self) -> list[str]:
116
+ """Return the canonical list of node kinds this extractor emits.
117
+
118
+ Example: ``['module', 'class', 'function', 'method']``
119
+
120
+ Used to parameterize the LanceDB ``index_kinds`` and the MCP
121
+ ``graph_stats`` tool enum.
122
+ """
123
+
124
+ @abstractmethod
125
+ def edge_kinds(self) -> list[str]:
126
+ """Return the canonical list of edge relation types this extractor emits.
127
+
128
+ Example: ``['CALLS', 'IMPORTS', 'CONTAINS', 'INHERITS']``
129
+ """
130
+
131
+ @abstractmethod
132
+ def extract(self) -> Iterator[NodeSpec | EdgeSpec]:
133
+ """Traverse the source and yield nodes and edges.
134
+
135
+ May yield :class:`NodeSpec` and :class:`EdgeSpec` objects in any
136
+ order. The build pipeline separates them into two lists before
137
+ calling :meth:`~pycode_kg.store.GraphStore.write`.
138
+
139
+ Implementations should be deterministic: the same source should
140
+ produce the same stream on every call.
141
+ """
142
+
143
+ def meaningful_node_kinds(self) -> list[str]:
144
+ """Return the subset of ``node_kinds()`` that are indexed semantically.
145
+
146
+ Default: all of :meth:`node_kinds`. Override to exclude structural
147
+ stubs (e.g., unresolved import placeholders, synthetic wrapper nodes)
148
+ from the LanceDB index and from coverage metrics.
149
+
150
+ :return: List of node kind strings to index and measure.
151
+ """
152
+ return self.node_kinds()
153
+
154
+ def coverage_metric(self, nodes: list[NodeSpec]) -> float:
155
+ """Compute a coverage quality metric for snapshots (0.0–1.0).
156
+
157
+ Default: fraction of meaningful nodes that have a non-empty docstring.
158
+ Override for domain-appropriate metrics (e.g., pathway annotation
159
+ completeness, legal cross-reference density).
160
+
161
+ :param nodes: All extracted :class:`NodeSpec` objects.
162
+ :return: Coverage score in ``[0.0, 1.0]``.
163
+ """
164
+ meaningful = [n for n in nodes if n.kind in self.meaningful_node_kinds()]
165
+ if not meaningful:
166
+ return 0.0
167
+ covered = sum(1 for n in meaningful if n.docstring.strip())
168
+ return covered / len(meaningful)
169
+
170
+
171
+ # ---------------------------------------------------------------------------
172
+ # PyCodeKGExtractor — Python-AST-backed extractor
173
+ # ---------------------------------------------------------------------------
174
+
175
+
176
+ class PyCodeKGExtractor(KGExtractor):
177
+ """KGExtractor backed by CodeGraph (Python AST analysis).
178
+
179
+ Wraps :class:`~pycode_kg.graph.CodeGraph` and converts its :class:`Node`
180
+ and :class:`Edge` v0 primitives to :class:`NodeSpec` / :class:`EdgeSpec`
181
+ for the generic :class:`~pycode_kg.module.base.KGModule` build pipeline.
182
+
183
+ This is the extractor used by :class:`~pycode_kg.kg.PyCodeKG` and is the
184
+ reference implementation for source-code KG extractors.
185
+
186
+ :param repo_path: Path to the Python repository root.
187
+ :param include: Set of top-level directory names to include (all if empty).
188
+ :param exclude: Set of directory names to exclude at every depth.
189
+ :param config: Optional config dict (forwarded to super).
190
+ """
191
+
192
+ def __init__(
193
+ self,
194
+ repo_path: Path,
195
+ *,
196
+ include: set[str] | None = None,
197
+ exclude: set[str] | None = None,
198
+ config: dict[str, Any] | None = None,
199
+ ) -> None:
200
+ """Initialise the extractor for a Python repository.
201
+
202
+ :param repo_path: Path to the Python repository root.
203
+ :param include: Top-level directory names to include (empty = all).
204
+ :param exclude: Directory names to exclude at every walk depth.
205
+ :param config: Optional domain-specific configuration dict.
206
+ """
207
+ super().__init__(repo_path, config)
208
+ self._include: set[str] = include or set()
209
+ self._exclude: set[str] = exclude or set()
210
+
211
+ def node_kinds(self) -> list[str]:
212
+ """Return the Python AST node kinds.
213
+
214
+ :return: ``['module', 'class', 'function', 'method', 'symbol']``
215
+ """
216
+ return ["module", "class", "function", "method", "symbol"]
217
+
218
+ def edge_kinds(self) -> list[str]:
219
+ """Return the Python AST edge relation types.
220
+
221
+ :return: List of all relation strings emitted by the Python extractor.
222
+ """
223
+ return [
224
+ "CONTAINS",
225
+ "CALLS",
226
+ "IMPORTS",
227
+ "INHERITS",
228
+ "READS",
229
+ "WRITES",
230
+ "ATTR_ACCESS",
231
+ "DEPENDS_ON",
232
+ "RESOLVES_TO",
233
+ ]
234
+
235
+ def meaningful_node_kinds(self) -> list[str]:
236
+ """Return the node kinds indexed by LanceDB and counted in coverage.
237
+
238
+ Excludes ``'symbol'`` (unresolved import stubs).
239
+
240
+ :return: ``['module', 'class', 'function', 'method']``
241
+ """
242
+ return ["module", "class", "function", "method"]
243
+
244
+ def extract(self) -> Iterator[NodeSpec | EdgeSpec]:
245
+ """Run Python AST extraction and yield NodeSpec / EdgeSpec objects.
246
+
247
+ Delegates to :class:`~pycode_kg.graph.CodeGraph` for the AST walk, then
248
+ converts the v0-locked :class:`~pycode_kg.pycodekg.Node` and
249
+ :class:`~pycode_kg.pycodekg.Edge` primitives to the generic spec types.
250
+
251
+ :return: Iterator of :class:`NodeSpec` and :class:`EdgeSpec` objects.
252
+ """
253
+ from pycode_kg.graph import CodeGraph # pylint: disable=import-outside-toplevel
254
+
255
+ graph = CodeGraph(self.repo_path, include=self._include, exclude=self._exclude)
256
+ nodes, edges = graph.result()
257
+
258
+ for n in nodes:
259
+ yield NodeSpec(
260
+ node_id=n.id,
261
+ kind=n.kind,
262
+ name=n.name,
263
+ qualname=n.qualname or "",
264
+ source_path=n.module_path or "",
265
+ lineno=n.lineno,
266
+ end_lineno=n.end_lineno,
267
+ docstring=n.docstring or "",
268
+ )
269
+
270
+ for e in edges:
271
+ yield EdgeSpec(
272
+ source_id=e.src,
273
+ target_id=e.dst,
274
+ relation=e.rel,
275
+ metadata={"evidence": e.evidence} if e.evidence else {},
276
+ )