pycode-kg 0.19.0__tar.gz → 0.19.2__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.
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/PKG-INFO +37 -21
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/README.md +32 -15
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/pyproject.toml +5 -11
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/__init__.py +1 -1
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_build.py +11 -5
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_build_full.py +12 -6
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_init.py +1 -1
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_model.py +1 -1
- pycode_kg-0.19.2/src/pycode_kg/index.py +11 -0
- pycode_kg-0.19.2/src/pycode_kg/module/base.py +33 -0
- pycode_kg-0.19.2/src/pycode_kg/module/extractor.py +127 -0
- pycode_kg-0.19.2/src/pycode_kg/module/types.py +31 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/pycodekg.py +2 -1
- pycode_kg-0.19.2/src/pycode_kg/store.py +7 -0
- pycode_kg-0.19.0/src/pycode_kg/index.py +0 -575
- pycode_kg-0.19.0/src/pycode_kg/module/base.py +0 -720
- pycode_kg-0.19.0/src/pycode_kg/module/extractor.py +0 -276
- pycode_kg-0.19.0/src/pycode_kg/module/types.py +0 -532
- pycode_kg-0.19.0/src/pycode_kg/store.py +0 -766
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/LICENSE +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/.DS_Store +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/__main__.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/analysis/__init__.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/analysis/bridge.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/analysis/centrality.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/analysis/framework_detector.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/analysis/hybrid_rank.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/app.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/architecture.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/build_pycodekg_lancedb.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/build_pycodekg_sqlite.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/__init__.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_analyze.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_architecture.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_bridges.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_centrality.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_explain.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_framework_nodes.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_hooks.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_mcp.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_query.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_snapshot.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_viz.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/main.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/options.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/config.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/explain.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/graph.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/kg.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/layout3d.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/mcp_server.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/module/__init__.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/pycodekg_query.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/pycodekg_snippet_packer.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/pycodekg_thorough_analysis.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/pycodekg_viz.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/pycodekg_viz3d.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/ranking/__init__.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/ranking/cli_rank.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/ranking/coderank.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/snapshots.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/sql/004_add_centrality_table.sql +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/utils.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/visitor.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/viz3d.py +0 -0
- {pycode_kg-0.19.0 → pycode_kg-0.19.2}/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.
|
|
3
|
+
Version: 0.19.2
|
|
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,10 +25,9 @@ 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.
|
|
29
|
-
Requires-Dist: doc-kg (>=0.
|
|
30
|
-
Requires-Dist: kgmodule-utils (>=0.
|
|
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)
|
|
@@ -66,7 +65,7 @@ Requires-Dist: streamlit (>=1.35.0) ; extra == "viz"
|
|
|
66
65
|
Requires-Dist: torch (>=2.5.1)
|
|
67
66
|
Requires-Dist: trame-vtk (>=2.0.0) ; extra == "all"
|
|
68
67
|
Requires-Dist: trame-vtk (>=2.0.0) ; extra == "viz3d"
|
|
69
|
-
Requires-Dist: transformers (>=4.57
|
|
68
|
+
Requires-Dist: transformers (>=4.40.0,<4.57)
|
|
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
|
[](https://www.python.org/)
|
|
80
79
|
[](https://www.elastic.co/licensing/elastic-license)
|
|
81
|
-
[](https://github.com/Flux-Frontiers/pycode_kg/releases)
|
|
82
81
|
[](https://github.com/Flux-Frontiers/pycode_kg/actions/workflows/ci.yml)
|
|
83
82
|
[](https://python-poetry.org/)
|
|
84
83
|
[](https://zenodo.org/badge/latestdoi/1202379010)
|
|
@@ -117,23 +116,40 @@ PyCodeKG is designed to be useful at both ends — as a standalone command-line
|
|
|
117
116
|
|
|
118
117
|
### 1. Standalone — `pycodekg analyze`
|
|
119
118
|
|
|
120
|
-
This is the
|
|
119
|
+
This single command is the actual reason I wrote this module. I got tired of my LLM doing grep on my src/ in order to get a good analysis of the codebase. This command runs fifteen analysis passes in seconds and the output drops straight into your favorite coding LLM for targeted improvement suggestions. I used this process to iteratively improve PyCodeKG and several of the analysis passes were added as a result.
|
|
120
|
+
|
|
121
|
+
The analysis/ has files *_analysis_<date>.md - I ran PyCodeKG against **numpy** and **matplotlib** for fun.
|
|
121
122
|
|
|
122
123
|
```bash
|
|
124
|
+
pycodekg init # downloads embedder
|
|
123
125
|
pycodekg build --repo . # one-time index
|
|
124
|
-
pycodekg analyze
|
|
126
|
+
pycodekg analyze. # the full report
|
|
125
127
|
```
|
|
126
128
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
129
|
+
**Note**
|
|
130
|
+
The first-time run can take some time to warm up. Be patient.
|
|
131
|
+
|
|
132
|
+
The 15-phase pipeline runs in sequence and surfaces several important metrics:
|
|
133
|
+
|
|
134
|
+
| # | Phase | What it surfaces |
|
|
135
|
+
|---|-------|-----------------|
|
|
136
|
+
| 1 | Baseline metrics | Node/edge counts, graph shape |
|
|
137
|
+
| 2 | CodeRank (global PageRank) | Structurally most important symbols |
|
|
138
|
+
| 3 | Fan-in analysis | Heavily depended-on functions (breaking-change risk) |
|
|
139
|
+
| 4 | Fan-out analysis | Orchestrators and complexity hotspots |
|
|
140
|
+
| 5 | Dependency analysis | Orphaned / dead code candidates |
|
|
141
|
+
| 6 | Pattern detection | Anti-patterns and structural red flags |
|
|
142
|
+
| 7 | Module coupling | Tightly coupled pairs, import graph density |
|
|
143
|
+
| 8 | Critical paths | Longest call chains, bottlenecks |
|
|
144
|
+
| 9 | Public API identification | Exposed vs. internal surface |
|
|
145
|
+
| 10 | Docstring coverage | By module, class, function, method |
|
|
146
|
+
| 11 | Inheritance hierarchy | Depth, multiple inheritance, diamond patterns |
|
|
147
|
+
| 12 | Insight synthesis | Issues + strengths callouts |
|
|
148
|
+
| 13 | Snapshot history | Metric trends across releases |
|
|
149
|
+
| 14 | Structural centrality (SIR) | Bridge nodes, graph removal impact |
|
|
150
|
+
| 15 | Concern-based ranking | Nodes grouped by architectural concern |
|
|
151
|
+
|
|
152
|
+
Every finding maps to a file and line number — no hallucinated signatures, no probabilistic guesses. The Markdown output is LLM-ready; the JSON snapshot is CI-gate-ready. Reach for `analyze` before any non-trivial refactor, at every release, and whenever you inherit an unfamiliar codebase. Full reference: [docs/Analyze.md](docs/Analyze.md).
|
|
137
153
|
|
|
138
154
|
```bash
|
|
139
155
|
pycodekg analyze --quiet --json ~/.claude/pycodekg_analysis_latest.json
|
|
@@ -190,7 +206,7 @@ The graph is built around four node kinds (module, class, function, method) and
|
|
|
190
206
|
|
|
191
207
|
---
|
|
192
208
|
|
|
193
|
-
## What you can
|
|
209
|
+
## What you can do with it
|
|
194
210
|
|
|
195
211
|
| If you want to… | Reach for | Detail |
|
|
196
212
|
|---|---|---|
|
|
@@ -201,7 +217,7 @@ The graph is built around four node kinds (module, class, function, method) and
|
|
|
201
217
|
| **Pull source-grounded context for an LLM** | `pycodekg pack "..." --format md` | [docs/CHEATSHEET.md](docs/CHEATSHEET.md) |
|
|
202
218
|
| **Run a hybrid semantic + structural query** | `pycodekg query "..."` | [docs/CHEATSHEET.md](docs/CHEATSHEET.md) |
|
|
203
219
|
| **Browse the graph interactively** | `pycodekg viz` (Streamlit) | [docs/INSTALLATION.md](docs/INSTALLATION.md) |
|
|
204
|
-
| **See call graphs in 3-D** | `pycodekg viz3d --layout funnel` | [docs/VIZ3D.md](docs/VIZ3D.md) |
|
|
220
|
+
| **See call graphs in 3-D** *(active development — functional but rough)* | `pycodekg viz3d --layout funnel` | [docs/VIZ3D.md](docs/VIZ3D.md) |
|
|
205
221
|
| **Wire it into Claude / Copilot / Cline** | `pycodekg mcp` | [docs/MCP.md](docs/MCP.md) |
|
|
206
222
|
|
|
207
223
|
If you only read one doc after this one, read [docs/Analyze.md](docs/Analyze.md) — that's where most of the day-to-day value lives.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
[](https://www.python.org/)
|
|
7
7
|
[](https://www.elastic.co/licensing/elastic-license)
|
|
8
|
-
[](https://github.com/Flux-Frontiers/pycode_kg/releases)
|
|
9
9
|
[](https://github.com/Flux-Frontiers/pycode_kg/actions/workflows/ci.yml)
|
|
10
10
|
[](https://python-poetry.org/)
|
|
11
11
|
[](https://zenodo.org/badge/latestdoi/1202379010)
|
|
@@ -44,23 +44,40 @@ PyCodeKG is designed to be useful at both ends — as a standalone command-line
|
|
|
44
44
|
|
|
45
45
|
### 1. Standalone — `pycodekg analyze`
|
|
46
46
|
|
|
47
|
-
This is the
|
|
47
|
+
This single command is the actual reason I wrote this module. I got tired of my LLM doing grep on my src/ in order to get a good analysis of the codebase. This command runs fifteen analysis passes in seconds and the output drops straight into your favorite coding LLM for targeted improvement suggestions. I used this process to iteratively improve PyCodeKG and several of the analysis passes were added as a result.
|
|
48
|
+
|
|
49
|
+
The analysis/ has files *_analysis_<date>.md - I ran PyCodeKG against **numpy** and **matplotlib** for fun.
|
|
48
50
|
|
|
49
51
|
```bash
|
|
52
|
+
pycodekg init # downloads embedder
|
|
50
53
|
pycodekg build --repo . # one-time index
|
|
51
|
-
pycodekg analyze
|
|
54
|
+
pycodekg analyze. # the full report
|
|
52
55
|
```
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
**Note**
|
|
58
|
+
The first-time run can take some time to warm up. Be patient.
|
|
59
|
+
|
|
60
|
+
The 15-phase pipeline runs in sequence and surfaces several important metrics:
|
|
61
|
+
|
|
62
|
+
| # | Phase | What it surfaces |
|
|
63
|
+
|---|-------|-----------------|
|
|
64
|
+
| 1 | Baseline metrics | Node/edge counts, graph shape |
|
|
65
|
+
| 2 | CodeRank (global PageRank) | Structurally most important symbols |
|
|
66
|
+
| 3 | Fan-in analysis | Heavily depended-on functions (breaking-change risk) |
|
|
67
|
+
| 4 | Fan-out analysis | Orchestrators and complexity hotspots |
|
|
68
|
+
| 5 | Dependency analysis | Orphaned / dead code candidates |
|
|
69
|
+
| 6 | Pattern detection | Anti-patterns and structural red flags |
|
|
70
|
+
| 7 | Module coupling | Tightly coupled pairs, import graph density |
|
|
71
|
+
| 8 | Critical paths | Longest call chains, bottlenecks |
|
|
72
|
+
| 9 | Public API identification | Exposed vs. internal surface |
|
|
73
|
+
| 10 | Docstring coverage | By module, class, function, method |
|
|
74
|
+
| 11 | Inheritance hierarchy | Depth, multiple inheritance, diamond patterns |
|
|
75
|
+
| 12 | Insight synthesis | Issues + strengths callouts |
|
|
76
|
+
| 13 | Snapshot history | Metric trends across releases |
|
|
77
|
+
| 14 | Structural centrality (SIR) | Bridge nodes, graph removal impact |
|
|
78
|
+
| 15 | Concern-based ranking | Nodes grouped by architectural concern |
|
|
79
|
+
|
|
80
|
+
Every finding maps to a file and line number — no hallucinated signatures, no probabilistic guesses. The Markdown output is LLM-ready; the JSON snapshot is CI-gate-ready. Reach for `analyze` before any non-trivial refactor, at every release, and whenever you inherit an unfamiliar codebase. Full reference: [docs/Analyze.md](docs/Analyze.md).
|
|
64
81
|
|
|
65
82
|
```bash
|
|
66
83
|
pycodekg analyze --quiet --json ~/.claude/pycodekg_analysis_latest.json
|
|
@@ -117,7 +134,7 @@ The graph is built around four node kinds (module, class, function, method) and
|
|
|
117
134
|
|
|
118
135
|
---
|
|
119
136
|
|
|
120
|
-
## What you can
|
|
137
|
+
## What you can do with it
|
|
121
138
|
|
|
122
139
|
| If you want to… | Reach for | Detail |
|
|
123
140
|
|---|---|---|
|
|
@@ -128,7 +145,7 @@ The graph is built around four node kinds (module, class, function, method) and
|
|
|
128
145
|
| **Pull source-grounded context for an LLM** | `pycodekg pack "..." --format md` | [docs/CHEATSHEET.md](docs/CHEATSHEET.md) |
|
|
129
146
|
| **Run a hybrid semantic + structural query** | `pycodekg query "..."` | [docs/CHEATSHEET.md](docs/CHEATSHEET.md) |
|
|
130
147
|
| **Browse the graph interactively** | `pycodekg viz` (Streamlit) | [docs/INSTALLATION.md](docs/INSTALLATION.md) |
|
|
131
|
-
| **See call graphs in 3-D** | `pycodekg viz3d --layout funnel` | [docs/VIZ3D.md](docs/VIZ3D.md) |
|
|
148
|
+
| **See call graphs in 3-D** *(active development — functional but rough)* | `pycodekg viz3d --layout funnel` | [docs/VIZ3D.md](docs/VIZ3D.md) |
|
|
132
149
|
| **Wire it into Claude / Copilot / Cline** | `pycodekg mcp` | [docs/MCP.md](docs/MCP.md) |
|
|
133
150
|
|
|
134
151
|
If you only read one doc after this one, read [docs/Analyze.md](docs/Analyze.md) — that's where most of the day-to-day value lives.
|
|
@@ -43,11 +43,6 @@
|
|
|
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
|
|
|
@@ -66,7 +61,7 @@ ruff = ">=0.4.0"
|
|
|
66
61
|
# ---------------------------------------------------------------------------
|
|
67
62
|
[project]
|
|
68
63
|
name = "pycode-kg"
|
|
69
|
-
version = "0.19.
|
|
64
|
+
version = "0.19.2"
|
|
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",
|
|
@@ -94,8 +88,8 @@ dependencies = [
|
|
|
94
88
|
"safetensors>=0.5.0",
|
|
95
89
|
"sentence-transformers>=5.4.1",
|
|
96
90
|
"torch>=2.5.1",
|
|
97
|
-
"transformers>=4.57
|
|
98
|
-
"kgmodule-utils>=0.
|
|
91
|
+
"transformers>=4.40.0,<4.57",
|
|
92
|
+
"kgmodule-utils[semantic]>=0.3.1",
|
|
99
93
|
]
|
|
100
94
|
|
|
101
95
|
[project.optional-dependencies]
|
|
@@ -108,7 +102,7 @@ dev = [
|
|
|
108
102
|
"pytest>=8.0.0",
|
|
109
103
|
"pytest-cov>=5.0.0",
|
|
110
104
|
"ruff>=0.4.0",
|
|
111
|
-
"doc-kg>=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,7 +121,7 @@ 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.
|
|
124
|
+
"doc-kg>=0.15.2",
|
|
131
125
|
]
|
|
132
126
|
all = [
|
|
133
127
|
"detect-secrets>=1.5.0",
|
|
@@ -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.
|
|
35
|
+
__version__ = "0.19.2"
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
146
|
-
("edges", str(len(
|
|
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
|
|
|
@@ -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
|
|
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
|
|