pycode-kg 0.19.1__tar.gz → 0.19.3__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.
Files changed (66) hide show
  1. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/PKG-INFO +7 -8
  2. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/README.md +1 -1
  3. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/pyproject.toml +21 -20
  4. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/__init__.py +1 -1
  5. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_build.py +11 -5
  6. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_build_full.py +12 -6
  7. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_init.py +1 -1
  8. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_model.py +1 -1
  9. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/graph.py +5 -3
  10. pycode_kg-0.19.3/src/pycode_kg/index.py +11 -0
  11. pycode_kg-0.19.3/src/pycode_kg/module/base.py +33 -0
  12. pycode_kg-0.19.3/src/pycode_kg/module/extractor.py +127 -0
  13. pycode_kg-0.19.3/src/pycode_kg/module/types.py +31 -0
  14. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/pycodekg.py +2 -1
  15. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/snapshots.py +1 -1
  16. pycode_kg-0.19.3/src/pycode_kg/store.py +7 -0
  17. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/viz3d.py +40 -40
  18. pycode_kg-0.19.1/src/pycode_kg/index.py +0 -575
  19. pycode_kg-0.19.1/src/pycode_kg/module/base.py +0 -720
  20. pycode_kg-0.19.1/src/pycode_kg/module/extractor.py +0 -276
  21. pycode_kg-0.19.1/src/pycode_kg/module/types.py +0 -532
  22. pycode_kg-0.19.1/src/pycode_kg/store.py +0 -766
  23. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/LICENSE +0 -0
  24. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/.DS_Store +0 -0
  25. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/__main__.py +0 -0
  26. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/analysis/__init__.py +0 -0
  27. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/analysis/bridge.py +0 -0
  28. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/analysis/centrality.py +0 -0
  29. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/analysis/framework_detector.py +0 -0
  30. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/analysis/hybrid_rank.py +0 -0
  31. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/app.py +0 -0
  32. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/architecture.py +0 -0
  33. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/build_pycodekg_lancedb.py +0 -0
  34. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/build_pycodekg_sqlite.py +0 -0
  35. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/__init__.py +0 -0
  36. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_analyze.py +0 -0
  37. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_architecture.py +0 -0
  38. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_bridges.py +0 -0
  39. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_centrality.py +0 -0
  40. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_explain.py +0 -0
  41. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_framework_nodes.py +0 -0
  42. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_hooks.py +0 -0
  43. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_mcp.py +0 -0
  44. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_query.py +0 -0
  45. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_snapshot.py +0 -0
  46. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/cmd_viz.py +0 -0
  47. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/main.py +0 -0
  48. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/cli/options.py +0 -0
  49. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/config.py +0 -0
  50. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/explain.py +0 -0
  51. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/kg.py +0 -0
  52. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/layout3d.py +0 -0
  53. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/mcp_server.py +0 -0
  54. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/module/__init__.py +0 -0
  55. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/pycodekg_query.py +0 -0
  56. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/pycodekg_snippet_packer.py +0 -0
  57. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/pycodekg_thorough_analysis.py +0 -0
  58. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/pycodekg_viz.py +0 -0
  59. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/pycodekg_viz3d.py +0 -0
  60. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/ranking/__init__.py +0 -0
  61. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/ranking/cli_rank.py +0 -0
  62. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/ranking/coderank.py +0 -0
  63. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/sql/004_add_centrality_table.sql +0 -0
  64. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/utils.py +0 -0
  65. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/visitor.py +0 -0
  66. {pycode_kg-0.19.1 → pycode_kg-0.19.3}/src/pycode_kg/viz3d_timeline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycode-kg
3
- Version: 0.19.1
3
+ Version: 0.19.3
4
4
  Summary: A tool to build a searchable knowledge graph from Python repositories
5
5
  License-Expression: Elastic-2.0
6
6
  License-File: LICENSE
@@ -25,15 +25,12 @@ Requires-Dist: PyQt5 (>=5.15.0) ; extra == "viz3d"
25
25
  Requires-Dist: click (>=8.1.0,<9)
26
26
  Requires-Dist: detect-secrets (>=1.5.0) ; extra == "all"
27
27
  Requires-Dist: detect-secrets (>=1.5.0) ; extra == "dev"
28
- Requires-Dist: doc-kg (>=0.11.0) ; extra == "dev"
29
- Requires-Dist: doc-kg (>=0.11.0) ; extra == "kgdeps"
30
- Requires-Dist: kgmodule-utils (>=0.2.1)
31
- Requires-Dist: lancedb (>=0.29.0)
28
+ Requires-Dist: doc-kg (>=0.15.2) ; extra == "dev"
29
+ Requires-Dist: doc-kg (>=0.15.2) ; extra == "kgdeps"
30
+ Requires-Dist: kgmodule-utils[semantic] (>=0.3.1)
32
31
  Requires-Dist: markdown (>=3.6) ; extra == "all"
33
32
  Requires-Dist: markdown (>=3.6) ; extra == "viz3d"
34
33
  Requires-Dist: mcp (>=1.0.0)
35
- Requires-Dist: mypy (>=1.10.0) ; extra == "all"
36
- Requires-Dist: mypy (>=1.10.0) ; extra == "dev"
37
34
  Requires-Dist: numpy (>=1.24.0)
38
35
  Requires-Dist: pandas (>=2.0.0)
39
36
  Requires-Dist: param (>=2.0.0) ; extra == "all"
@@ -67,6 +64,8 @@ Requires-Dist: torch (>=2.5.1)
67
64
  Requires-Dist: trame-vtk (>=2.0.0) ; extra == "all"
68
65
  Requires-Dist: trame-vtk (>=2.0.0) ; extra == "viz3d"
69
66
  Requires-Dist: transformers (>=4.40.0,<4.57)
67
+ Requires-Dist: ty (>=0.0.41) ; extra == "all"
68
+ Requires-Dist: ty (>=0.0.41) ; extra == "dev"
70
69
  Project-URL: Homepage, https://github.com/Flux-Frontiers/pycode_kg
71
70
  Project-URL: Repository, https://github.com/Flux-Frontiers/pycode_kg
72
71
  Description-Content-Type: text/markdown
@@ -78,7 +77,7 @@ Description-Content-Type: text/markdown
78
77
 
79
78
  [![Python](https://img.shields.io/badge/python-3.12%20%7C%203.13-blue.svg)](https://www.python.org/)
80
79
  [![License: Elastic-2.0](https://img.shields.io/badge/License-Elastic%202.0-blue.svg)](https://www.elastic.co/licensing/elastic-license)
81
- [![Version](https://img.shields.io/badge/version-0.19.1-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
80
+ [![Version](https://img.shields.io/badge/version-0.19.3-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
82
81
  [![CI](https://github.com/Flux-Frontiers/pycode_kg/actions/workflows/ci.yml/badge.svg)](https://github.com/Flux-Frontiers/pycode_kg/actions/workflows/ci.yml)
83
82
  [![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)
84
83
  [![DOI](https://zenodo.org/badge/1202379010.svg)](https://zenodo.org/badge/latestdoi/1202379010)
@@ -5,7 +5,7 @@
5
5
 
6
6
  [![Python](https://img.shields.io/badge/python-3.12%20%7C%203.13-blue.svg)](https://www.python.org/)
7
7
  [![License: Elastic-2.0](https://img.shields.io/badge/License-Elastic%202.0-blue.svg)](https://www.elastic.co/licensing/elastic-license)
8
- [![Version](https://img.shields.io/badge/version-0.19.1-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
8
+ [![Version](https://img.shields.io/badge/version-0.19.3-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
9
9
  [![CI](https://github.com/Flux-Frontiers/pycode_kg/actions/workflows/ci.yml/badge.svg)](https://github.com/Flux-Frontiers/pycode_kg/actions/workflows/ci.yml)
10
10
  [![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)
11
11
  [![DOI](https://zenodo.org/badge/1202379010.svg)](https://zenodo.org/badge/latestdoi/1202379010)
@@ -7,7 +7,7 @@
7
7
  #
8
8
  # Quick install (pip)
9
9
  # -------------------
10
- # pip install -e ".[dev]" core + dev tools (pytest, ruff, mypy, etc.)
10
+ # pip install -e ".[dev]" core + dev tools (pytest, ruff, ty, etc.)
11
11
  # pip install -e ".[viz]" core + Streamlit/Plotly/PyVis visualizer
12
12
  # pip install -e ".[viz3d]" core + PyVista 3D visualizer
13
13
  # pip install -e ".[kgdeps]" core + KG integrations (doc-kg, agent-kg)
@@ -43,17 +43,12 @@
43
43
  requires = ["poetry-core>=2.0.0"]
44
44
  build-backend = "poetry.core.masonry.api"
45
45
 
46
- [[tool.poetry.source]]
47
- name = "testpypi"
48
- url = "https://test.pypi.org/simple/"
49
- priority = "supplemental"
50
-
51
46
  [tool.poetry]
52
47
  packages = [{ include = "pycode_kg", from = "src" }]
53
48
 
54
49
  [tool.poetry.group.dev.dependencies]
55
50
  detect-secrets = ">=1.5.0"
56
- mypy = ">=1.10.0"
51
+ ty = "^0.0.41"
57
52
  pdoc = ">=14.0.0"
58
53
  pre-commit = ">=4.5.1"
59
54
  pylint = ">=4.0.5"
@@ -66,7 +61,7 @@ ruff = ">=0.4.0"
66
61
  # ---------------------------------------------------------------------------
67
62
  [project]
68
63
  name = "pycode-kg"
69
- version = "0.19.1"
64
+ version = "0.19.3"
70
65
  description = "A tool to build a searchable knowledge graph from Python repositories"
71
66
  readme = "README.md"
72
67
  license = "Elastic-2.0"
@@ -86,7 +81,6 @@ classifiers = [
86
81
  requires-python = ">=3.12,<3.14"
87
82
  dependencies = [
88
83
  "click>=8.1.0,<9",
89
- "lancedb>=0.29.0",
90
84
  "mcp>=1.0.0",
91
85
  "numpy>=1.24.0",
92
86
  "pandas>=2.0.0",
@@ -95,20 +89,20 @@ dependencies = [
95
89
  "sentence-transformers>=5.4.1",
96
90
  "torch>=2.5.1",
97
91
  "transformers>=4.40.0,<4.57",
98
- "kgmodule-utils>=0.2.1",
92
+ "kgmodule-utils[semantic]>=0.3.1",
99
93
  ]
100
94
 
101
95
  [project.optional-dependencies]
102
96
  dev = [
103
97
  "detect-secrets>=1.5.0",
104
- "mypy>=1.10.0",
98
+ "ty>=0.0.41",
105
99
  "pdoc>=14.0.0",
106
100
  "pre-commit>=4.5.1",
107
101
  "pylint>=4.0.5",
108
102
  "pytest>=8.0.0",
109
103
  "pytest-cov>=5.0.0",
110
104
  "ruff>=0.4.0",
111
- "doc-kg>=0.11.0",
105
+ "doc-kg>=0.15.2",
112
106
  # agent-kg not yet on PyPI — install manually: pip install git+https://github.com/Flux-Frontiers/agent_kg.git
113
107
  ]
114
108
  viz = [
@@ -127,12 +121,11 @@ viz3d = [
127
121
 
128
122
  # note that dockg is on pypi; agent-kg is not yet — install manually from git
129
123
  kgdeps = [
130
- "doc-kg>=0.11.0",
124
+ "doc-kg>=0.15.2",
131
125
  ]
132
126
  all = [
133
127
  "detect-secrets>=1.5.0",
134
128
  "markdown>=3.6",
135
- "mypy>=1.10.0",
136
129
  "param>=2.0.0",
137
130
  "pdoc>=14.0.0",
138
131
  "plotly>=5.14.0",
@@ -147,6 +140,7 @@ all = [
147
140
  "ruff>=0.4.0",
148
141
  "streamlit>=1.35.0",
149
142
  "trame-vtk>=2.0.0",
143
+ "ty>=0.0.41",
150
144
  ]
151
145
 
152
146
  [project.urls]
@@ -183,12 +177,19 @@ target-version = "py312"
183
177
  select = ["E", "F", "W", "I", "UP"]
184
178
  ignore = ["E501"]
185
179
 
186
- [tool.mypy]
187
- python_version = "3.12"
188
- strict = false
189
- ignore_missing_imports = true
190
- mypy_path = "src"
191
- explicit_package_bases = true
180
+ [tool.ty.environment]
181
+ python-version = "3.12"
182
+ root = ["src"]
183
+
184
+ [tool.ty.rules]
185
+ # Be lenient about third-party stubs (mirrors mypy's ignore_missing_imports).
186
+ unresolved-import = "ignore"
187
+ # viz3d.py carries `# ty: ignore` suppressions for param/PyQt5/pyvista false
188
+ # positives. Those libraries live in the optional viz/viz3d extras, so in the
189
+ # lean CI install the imports are Unknown, the errors don't fire, and the
190
+ # ignores look "unused". Don't fail the build on that — the suppressions are
191
+ # still honored locally where the extras (and the errors) are present.
192
+ unused-ignore-comment = "ignore"
192
193
 
193
194
  [tool.pytest.ini_options]
194
195
  testpaths = ["tests"]
@@ -32,7 +32,7 @@ KGModule SDK (build new domain KGs)::
32
32
  from pycode_kg import KGModule, KGExtractor, PyCodeKGExtractor, NodeSpec, EdgeSpec
33
33
  """
34
34
 
35
- __version__ = "0.19.1"
35
+ __version__ = "0.19.3"
36
36
  __author__ = "Eric G. Suchanek, PhD"
37
37
 
38
38
  # Low-level primitives (locked v0 contract)
@@ -16,12 +16,12 @@ import click
16
16
  from pycode_kg.cli.main import cli
17
17
  from pycode_kg.cli.options import exclude_option, include_option, repo_option
18
18
  from pycode_kg.config import load_exclude_dirs, load_include_dirs
19
- from pycode_kg.graph import CodeGraph
20
19
  from pycode_kg.index import (
21
20
  SemanticIndex,
22
21
  SentenceTransformerEmbedder,
23
22
  suppress_ingestion_logging,
24
23
  )
24
+ from pycode_kg.module.extractor import EdgeSpec, NodeSpec, PyCodeKGExtractor
25
25
  from pycode_kg.pycodekg import DEFAULT_MODEL
26
26
  from pycode_kg.store import GraphStore
27
27
 
@@ -53,19 +53,25 @@ def build_sqlite(
53
53
  include = load_include_dirs(repo_root) | set(include_dir)
54
54
  exclude = load_exclude_dirs(repo_root) | set(exclude_dir)
55
55
 
56
- graph = CodeGraph(
56
+ extractor = PyCodeKGExtractor(
57
57
  repo_root,
58
58
  include=include if include else None,
59
59
  exclude=exclude if exclude else None,
60
60
  )
61
- nodes, edges = graph.extract().result()
61
+ node_specs: list[NodeSpec] = []
62
+ edge_specs: list[EdgeSpec] = []
63
+ for item in extractor.extract():
64
+ if isinstance(item, NodeSpec):
65
+ node_specs.append(item)
66
+ else:
67
+ edge_specs.append(item)
62
68
 
63
69
  store = GraphStore(db_path)
64
- store.write(nodes, edges, wipe=wipe)
70
+ store.write(node_specs, edge_specs, wipe=wipe)
65
71
  resolved = store.resolve_symbols()
66
72
  store.close()
67
73
 
68
- print(f"OK: nodes={len(nodes)} edges={len(edges)} resolved={resolved} db={db_path}")
74
+ print(f"OK: nodes={len(node_specs)} edges={len(edge_specs)} resolved={resolved} db={db_path}")
69
75
 
70
76
 
71
77
  @cli.command("build-lancedb")
@@ -22,12 +22,12 @@ from pycode_kg.cli.options import (
22
22
  repo_option,
23
23
  )
24
24
  from pycode_kg.config import load_exclude_dirs, load_include_dirs
25
- from pycode_kg.graph import CodeGraph
26
25
  from pycode_kg.index import (
27
26
  SemanticIndex,
28
27
  SentenceTransformerEmbedder,
29
28
  suppress_ingestion_logging,
30
29
  )
30
+ from pycode_kg.module.extractor import EdgeSpec, NodeSpec, PyCodeKGExtractor
31
31
  from pycode_kg.store import GraphStore
32
32
 
33
33
 
@@ -126,15 +126,21 @@ def _run_pipeline(
126
126
 
127
127
  # Step 1: Build graph store
128
128
  t1 = time.monotonic()
129
- graph = CodeGraph(
129
+ extractor = PyCodeKGExtractor(
130
130
  repo_root,
131
131
  include=include if include else None,
132
132
  exclude=exclude if exclude else None,
133
133
  )
134
- nodes, edges = graph.extract().result()
134
+ node_specs: list[NodeSpec] = []
135
+ edge_specs: list[EdgeSpec] = []
136
+ for item in extractor.extract():
137
+ if isinstance(item, NodeSpec):
138
+ node_specs.append(item)
139
+ else:
140
+ edge_specs.append(item)
135
141
 
136
142
  store = GraphStore(db_path)
137
- store.write(nodes, edges, wipe=wipe)
143
+ store.write(node_specs, edge_specs, wipe=wipe)
138
144
  resolved = store.resolve_symbols()
139
145
  store.close()
140
146
 
@@ -142,8 +148,8 @@ def _run_pipeline(
142
148
  1,
143
149
  "graph store",
144
150
  [
145
- ("nodes", str(len(nodes))),
146
- ("edges", str(len(edges))),
151
+ ("nodes", str(len(node_specs))),
152
+ ("edges", str(len(edge_specs))),
147
153
  ("resolved", str(resolved)),
148
154
  ],
149
155
  elapsed=time.monotonic() - t1,
@@ -18,6 +18,7 @@ import tomllib
18
18
  from pathlib import Path
19
19
 
20
20
  import click
21
+ from kg_utils.semantic import _local_model_path
21
22
 
22
23
  from pycode_kg.cli.cmd_build_full import _run_pipeline
23
24
  from pycode_kg.cli.cmd_hooks import _PRE_COMMIT_HOOK
@@ -28,7 +29,6 @@ from pycode_kg.cli.options import (
28
29
  model_option,
29
30
  repo_option,
30
31
  )
31
- from pycode_kg.index import _local_model_path
32
32
  from pycode_kg.kg import PyCodeKG
33
33
  from pycode_kg.pycodekg_thorough_analysis import PyCodeKGAnalyzer
34
34
  from pycode_kg.snapshots import SnapshotManager
@@ -9,9 +9,9 @@ CLI command for managing the PyCodeKG embedding model cache.
9
9
  from __future__ import annotations
10
10
 
11
11
  import click
12
+ from kg_utils.semantic import _local_model_path
12
13
 
13
14
  from pycode_kg.cli.main import cli
14
- from pycode_kg.index import _local_model_path
15
15
  from pycode_kg.pycodekg import DEFAULT_MODEL
16
16
 
17
17
 
@@ -78,14 +78,16 @@ class CodeGraph:
78
78
  """Extracted nodes (calls :meth:`extract` if needed)."""
79
79
  if self._nodes is None:
80
80
  self.extract()
81
- return self._nodes # type: ignore[return-value]
81
+ assert self._nodes is not None
82
+ return self._nodes
82
83
 
83
84
  @property
84
85
  def edges(self) -> list[Edge]:
85
86
  """Extracted edges (calls :meth:`extract` if needed)."""
86
87
  if self._edges is None:
87
88
  self.extract()
88
- return self._edges # type: ignore[return-value]
89
+ assert self._edges is not None
90
+ return self._edges
89
91
 
90
92
  def result(self) -> tuple[list[Node], list[Edge]]:
91
93
  """Return the extracted nodes and edges as a tuple.
@@ -120,6 +122,6 @@ class CodeGraph:
120
122
  if extracted:
121
123
  return (
122
124
  f"CodeGraph(repo_root={self.repo_root!r}, "
123
- f"nodes={len(self._nodes)}, edges={len(self._edges)})" # type: ignore[arg-type]
125
+ f"nodes={len(self._nodes)}, edges={len(self._edges)})" # ty: ignore[invalid-argument-type]
124
126
  )
125
127
  return f"CodeGraph(repo_root={self.repo_root!r}, not yet extracted)"
@@ -0,0 +1,11 @@
1
+ """pycode_kg/index.py — Re-exports semantic index from kg_utils.semantic."""
2
+
3
+ from kg_utils.semantic import ( # noqa: F401
4
+ DEFAULT_MODEL,
5
+ Embedder,
6
+ SeedHit,
7
+ SemanticIndex,
8
+ SentenceTransformerEmbedder,
9
+ resolve_model_path,
10
+ suppress_ingestion_logging,
11
+ )
@@ -0,0 +1,33 @@
1
+ """pycode_kg/module/base.py — KGModule for pycode_kg: thin subclass of kg_utils.pipeline."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import abstractmethod
6
+
7
+ from kg_utils.extractor import KGExtractor
8
+ from kg_utils.pipeline import KGModule as _KGModuleBase
9
+ from kg_utils.store import GraphStore
10
+
11
+
12
+ class KGModule(_KGModuleBase):
13
+ """Abstract base for Python code knowledge graphs.
14
+
15
+ Domain authors implement :meth:`make_extractor`, :meth:`kind`, and
16
+ :meth:`analyze`. Build/query/pack infrastructure comes from
17
+ :class:`kg_utils.pipeline.KGModule`.
18
+ """
19
+
20
+ _default_dir: str = ".pycodekg"
21
+
22
+ def _post_build_hook(self, store: GraphStore) -> None:
23
+ """Run symbol resolution after the graph is written."""
24
+ store.resolve_symbols()
25
+
26
+ @abstractmethod
27
+ def make_extractor(self) -> KGExtractor: ...
28
+
29
+ @abstractmethod
30
+ def kind(self) -> str: ...
31
+
32
+ @abstractmethod
33
+ def analyze(self) -> str: ...
@@ -0,0 +1,127 @@
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 imported from kg_utils.specs and re-exported here.
8
+ PyCodeKGExtractor is the Python-AST-backed implementation.
9
+
10
+ Author: Eric G. Suchanek, PhD
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from collections.abc import Iterator
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ from kg_utils.extractor import KGExtractor # noqa: F401
20
+ from kg_utils.specs import EdgeSpec, NodeSpec # noqa: F401
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # PyCodeKGExtractor — Python-AST-backed extractor
24
+ # ---------------------------------------------------------------------------
25
+
26
+
27
+ class PyCodeKGExtractor(KGExtractor):
28
+ """KGExtractor backed by CodeGraph (Python AST analysis).
29
+
30
+ Wraps :class:`~pycode_kg.graph.CodeGraph` and converts its :class:`Node`
31
+ and :class:`Edge` v0 primitives to :class:`NodeSpec` / :class:`EdgeSpec`
32
+ for the generic :class:`~pycode_kg.module.base.KGModule` build pipeline.
33
+
34
+ This is the extractor used by :class:`~pycode_kg.kg.PyCodeKG` and is the
35
+ reference implementation for source-code KG extractors.
36
+
37
+ :param repo_path: Path to the Python repository root.
38
+ :param include: Set of top-level directory names to include (all if empty).
39
+ :param exclude: Set of directory names to exclude at every depth.
40
+ :param config: Optional config dict (forwarded to super).
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ repo_path: Path,
46
+ *,
47
+ include: set[str] | None = None,
48
+ exclude: set[str] | None = None,
49
+ config: dict[str, Any] | None = None,
50
+ ) -> None:
51
+ """Initialise the extractor for a Python repository.
52
+
53
+ :param repo_path: Path to the Python repository root.
54
+ :param include: Top-level directory names to include (empty = all).
55
+ :param exclude: Directory names to exclude at every walk depth.
56
+ :param config: Optional domain-specific configuration dict.
57
+ """
58
+ super().__init__(repo_path, config)
59
+ self._include: set[str] = include or set()
60
+ self._exclude: set[str] = exclude or set()
61
+
62
+ def node_kinds(self) -> list[str]:
63
+ """Return the Python AST node kinds.
64
+
65
+ :return: ``['module', 'class', 'function', 'method', 'symbol']``
66
+ """
67
+ return ["module", "class", "function", "method", "symbol"]
68
+
69
+ def edge_kinds(self) -> list[str]:
70
+ """Return the Python AST edge relation types.
71
+
72
+ :return: List of all relation strings emitted by the Python extractor.
73
+ """
74
+ return [
75
+ "CONTAINS",
76
+ "CALLS",
77
+ "IMPORTS",
78
+ "INHERITS",
79
+ "READS",
80
+ "WRITES",
81
+ "ATTR_ACCESS",
82
+ "DEPENDS_ON",
83
+ "RESOLVES_TO",
84
+ ]
85
+
86
+ def meaningful_node_kinds(self) -> list[str]:
87
+ """Return the node kinds indexed by LanceDB and counted in coverage.
88
+
89
+ Excludes ``'symbol'`` (unresolved import stubs).
90
+
91
+ :return: ``['module', 'class', 'function', 'method']``
92
+ """
93
+ return ["module", "class", "function", "method"]
94
+
95
+ def extract(self) -> Iterator[NodeSpec | EdgeSpec]:
96
+ """Run Python AST extraction and yield NodeSpec / EdgeSpec objects.
97
+
98
+ Delegates to :class:`~pycode_kg.graph.CodeGraph` for the AST walk, then
99
+ converts the v0-locked :class:`~pycode_kg.pycodekg.Node` and
100
+ :class:`~pycode_kg.pycodekg.Edge` primitives to the generic spec types.
101
+
102
+ :return: Iterator of :class:`NodeSpec` and :class:`EdgeSpec` objects.
103
+ """
104
+ from pycode_kg.graph import CodeGraph # pylint: disable=import-outside-toplevel
105
+
106
+ graph = CodeGraph(self.repo_path, include=self._include, exclude=self._exclude)
107
+ nodes, edges = graph.result()
108
+
109
+ for n in nodes:
110
+ yield NodeSpec(
111
+ node_id=n.id,
112
+ kind=n.kind,
113
+ name=n.name,
114
+ qualname=n.qualname or "",
115
+ source_path=n.module_path or "",
116
+ lineno=n.lineno,
117
+ end_lineno=n.end_lineno,
118
+ docstring=n.docstring or "",
119
+ )
120
+
121
+ for e in edges:
122
+ yield EdgeSpec(
123
+ source_id=e.src,
124
+ target_id=e.dst,
125
+ relation=e.rel,
126
+ metadata={"evidence": e.evidence} if e.evidence else {},
127
+ )
@@ -0,0 +1,31 @@
1
+ """pycode_kg/module/types.py — Re-exports from kg_utils for backward compat."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from kg_utils.pipeline import ( # noqa: F401
6
+ compute_span,
7
+ docstring_signal,
8
+ lexical_overlap_score,
9
+ make_module_summary,
10
+ make_snippet,
11
+ normalize_query_text,
12
+ query_tokens,
13
+ read_lines,
14
+ safe_join,
15
+ semantic_score_from_distance,
16
+ spans_overlap,
17
+ )
18
+ from kg_utils.specs import BuildStats, QueryResult, SnippetPack # noqa: F401
19
+
20
+
21
+ @dataclass
22
+ class Snippet:
23
+ """Backward-compat shim. Snippets are now dicts on node["snippet"]."""
24
+
25
+ path: str
26
+ start: int
27
+ end: int
28
+ text: str
29
+
30
+ def to_dict(self) -> dict:
31
+ return {"path": self.path, "start": self.start, "end": self.end, "text": self.text}
@@ -37,7 +37,8 @@ from pathlib import Path
37
37
  # ============================================================================
38
38
  # Configuration
39
39
  # ============================================================================
40
- from pycode_kg.index import DEFAULT_MODEL as DEFAULT_MODEL # noqa: F401 — re-exported
40
+ from kg_utils.semantic import DEFAULT_MODEL as DEFAULT_MODEL # noqa: F401 — re-exported
41
+
41
42
  from pycode_kg.utils import node_id, rel_module_path
42
43
  from pycode_kg.visitor import PyCodeKGVisitor
43
44
 
@@ -284,7 +284,7 @@ class SnapshotManager(_BaseSnapshotManager):
284
284
  # capture — backwards-compat wrapper
285
285
  # ------------------------------------------------------------------
286
286
 
287
- def capture( # type: ignore[override]
287
+ def capture( # ty: ignore[invalid-method-override]
288
288
  self,
289
289
  version: str | None = None,
290
290
  branch: str | None = None,
@@ -0,0 +1,7 @@
1
+ """pycode_kg/store.py — Re-exports GraphStore from kg_utils.store."""
2
+
3
+ from kg_utils.store import ( # noqa: F401
4
+ DEFAULT_RELS,
5
+ GraphStore,
6
+ ProvMeta,
7
+ )