graphitect 0.2.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.
- graphify/__init__.py +30 -0
- graphify/__main__.py +757 -0
- graphify/_minhash.py +107 -0
- graphify/affected.py +318 -0
- graphify/always_on/agents-md.md +12 -0
- graphify/always_on/antigravity-rules.md +14 -0
- graphify/always_on/claude-md.md +9 -0
- graphify/always_on/gemini-md.md +9 -0
- graphify/always_on/kiro-steering.md +5 -0
- graphify/always_on/vscode-instructions.md +17 -0
- graphify/analyze.py +769 -0
- graphify/benchmark.py +152 -0
- graphify/build.py +2300 -0
- graphify/cache.py +1746 -0
- graphify/callflow_html.py +2051 -0
- graphify/cargo_introspect.py +109 -0
- graphify/cli.py +4745 -0
- graphify/cluster.py +409 -0
- graphify/command-kilo.md +15 -0
- graphify/cross_repo_calls.py +216 -0
- graphify/cross_repo_types.py +75 -0
- graphify/csharp_dispatch.py +154 -0
- graphify/dedup.py +1213 -0
- graphify/detect.py +2566 -0
- graphify/diagnostics.py +406 -0
- graphify/export.py +1349 -0
- graphify/exporters/__init__.py +1 -0
- graphify/exporters/base.py +14 -0
- graphify/exporters/graphdb.py +173 -0
- graphify/exporters/html.py +637 -0
- graphify/extract.py +7856 -0
- graphify/extractors/MIGRATION.md +107 -0
- graphify/extractors/__init__.py +66 -0
- graphify/extractors/apex.py +215 -0
- graphify/extractors/base.py +85 -0
- graphify/extractors/bash.py +579 -0
- graphify/extractors/blade.py +53 -0
- graphify/extractors/commonlisp.py +540 -0
- graphify/extractors/csharp.py +448 -0
- graphify/extractors/dart.py +564 -0
- graphify/extractors/dm.py +494 -0
- graphify/extractors/elixir.py +241 -0
- graphify/extractors/engine.py +6509 -0
- graphify/extractors/fortran.py +311 -0
- graphify/extractors/go.py +527 -0
- graphify/extractors/json_config.py +240 -0
- graphify/extractors/julia.py +289 -0
- graphify/extractors/markdown.py +408 -0
- graphify/extractors/models.py +131 -0
- graphify/extractors/objc.py +566 -0
- graphify/extractors/ocaml.py +289 -0
- graphify/extractors/pascal.py +688 -0
- graphify/extractors/pascal_forms.py +196 -0
- graphify/extractors/powershell.py +522 -0
- graphify/extractors/razor.py +192 -0
- graphify/extractors/resolution.py +3584 -0
- graphify/extractors/robot.py +296 -0
- graphify/extractors/rust.py +470 -0
- graphify/extractors/sln.py +92 -0
- graphify/extractors/sql.py +720 -0
- graphify/extractors/terraform.py +181 -0
- graphify/extractors/verilog.py +329 -0
- graphify/extractors/zig.py +181 -0
- graphify/file_slice.py +246 -0
- graphify/global_graph.py +194 -0
- graphify/google_workspace.py +237 -0
- graphify/hooks.py +933 -0
- graphify/ids.py +93 -0
- graphify/ingest.py +358 -0
- graphify/install.py +2366 -0
- graphify/llm.py +3544 -0
- graphify/manifest.py +4 -0
- graphify/manifest_ingest.py +311 -0
- graphify/mcp_ingest.py +386 -0
- graphify/multigraph_compat.py +212 -0
- graphify/pascal_resolution.py +129 -0
- graphify/paths.py +436 -0
- graphify/pg_introspect.py +165 -0
- graphify/prs.py +770 -0
- graphify/querylog.py +80 -0
- graphify/reflect.py +882 -0
- graphify/report.py +346 -0
- graphify/resolver_registry.py +85 -0
- graphify/ruby_resolution.py +242 -0
- graphify/scip_ingest.py +363 -0
- graphify/security.py +460 -0
- graphify/semantic_cleanup.py +336 -0
- graphify/serve.py +2608 -0
- graphify/skill-agents.md +710 -0
- graphify/skill-aider.md +1283 -0
- graphify/skill-amp.md +710 -0
- graphify/skill-claw.md +713 -0
- graphify/skill-codex.md +710 -0
- graphify/skill-copilot.md +713 -0
- graphify/skill-devin.md +1410 -0
- graphify/skill-droid.md +710 -0
- graphify/skill-kilo.md +722 -0
- graphify/skill-kiro.md +713 -0
- graphify/skill-opencode.md +705 -0
- graphify/skill-pi.md +713 -0
- graphify/skill-trae.md +711 -0
- graphify/skill-vscode.md +709 -0
- graphify/skill-windows.md +755 -0
- graphify/skill.md +713 -0
- graphify/skills/agents/references/add-watch.md +56 -0
- graphify/skills/agents/references/exports.md +87 -0
- graphify/skills/agents/references/extraction-spec.md +70 -0
- graphify/skills/agents/references/github-and-merge.md +46 -0
- graphify/skills/agents/references/hooks.md +33 -0
- graphify/skills/agents/references/query.md +311 -0
- graphify/skills/agents/references/transcribe.md +52 -0
- graphify/skills/agents/references/update.md +210 -0
- graphify/skills/amp/references/add-watch.md +56 -0
- graphify/skills/amp/references/exports.md +87 -0
- graphify/skills/amp/references/extraction-spec.md +70 -0
- graphify/skills/amp/references/github-and-merge.md +46 -0
- graphify/skills/amp/references/hooks.md +33 -0
- graphify/skills/amp/references/query.md +311 -0
- graphify/skills/amp/references/transcribe.md +52 -0
- graphify/skills/amp/references/update.md +210 -0
- graphify/skills/claude/references/add-watch.md +56 -0
- graphify/skills/claude/references/exports.md +87 -0
- graphify/skills/claude/references/extraction-spec.md +70 -0
- graphify/skills/claude/references/github-and-merge.md +46 -0
- graphify/skills/claude/references/hooks.md +33 -0
- graphify/skills/claude/references/query.md +311 -0
- graphify/skills/claude/references/transcribe.md +52 -0
- graphify/skills/claude/references/update.md +210 -0
- graphify/skills/claw/references/add-watch.md +56 -0
- graphify/skills/claw/references/exports.md +87 -0
- graphify/skills/claw/references/extraction-spec.md +31 -0
- graphify/skills/claw/references/github-and-merge.md +46 -0
- graphify/skills/claw/references/hooks.md +33 -0
- graphify/skills/claw/references/query.md +311 -0
- graphify/skills/claw/references/transcribe.md +52 -0
- graphify/skills/claw/references/update.md +210 -0
- graphify/skills/codex/references/add-watch.md +56 -0
- graphify/skills/codex/references/exports.md +87 -0
- graphify/skills/codex/references/extraction-spec.md +31 -0
- graphify/skills/codex/references/github-and-merge.md +46 -0
- graphify/skills/codex/references/hooks.md +33 -0
- graphify/skills/codex/references/query.md +311 -0
- graphify/skills/codex/references/transcribe.md +52 -0
- graphify/skills/codex/references/update.md +210 -0
- graphify/skills/copilot/references/add-watch.md +56 -0
- graphify/skills/copilot/references/exports.md +87 -0
- graphify/skills/copilot/references/extraction-spec.md +70 -0
- graphify/skills/copilot/references/github-and-merge.md +46 -0
- graphify/skills/copilot/references/hooks.md +33 -0
- graphify/skills/copilot/references/query.md +311 -0
- graphify/skills/copilot/references/transcribe.md +52 -0
- graphify/skills/copilot/references/update.md +210 -0
- graphify/skills/droid/references/add-watch.md +56 -0
- graphify/skills/droid/references/exports.md +87 -0
- graphify/skills/droid/references/extraction-spec.md +70 -0
- graphify/skills/droid/references/github-and-merge.md +46 -0
- graphify/skills/droid/references/hooks.md +33 -0
- graphify/skills/droid/references/query.md +311 -0
- graphify/skills/droid/references/transcribe.md +52 -0
- graphify/skills/droid/references/update.md +210 -0
- graphify/skills/kilo/references/add-watch.md +56 -0
- graphify/skills/kilo/references/exports.md +87 -0
- graphify/skills/kilo/references/extraction-spec.md +70 -0
- graphify/skills/kilo/references/github-and-merge.md +46 -0
- graphify/skills/kilo/references/hooks.md +33 -0
- graphify/skills/kilo/references/query.md +311 -0
- graphify/skills/kilo/references/transcribe.md +52 -0
- graphify/skills/kilo/references/update.md +210 -0
- graphify/skills/kiro/references/add-watch.md +56 -0
- graphify/skills/kiro/references/exports.md +87 -0
- graphify/skills/kiro/references/extraction-spec.md +31 -0
- graphify/skills/kiro/references/github-and-merge.md +46 -0
- graphify/skills/kiro/references/hooks.md +33 -0
- graphify/skills/kiro/references/query.md +311 -0
- graphify/skills/kiro/references/transcribe.md +52 -0
- graphify/skills/kiro/references/update.md +210 -0
- graphify/skills/opencode/references/add-watch.md +56 -0
- graphify/skills/opencode/references/exports.md +87 -0
- graphify/skills/opencode/references/extraction-spec.md +70 -0
- graphify/skills/opencode/references/github-and-merge.md +46 -0
- graphify/skills/opencode/references/hooks.md +33 -0
- graphify/skills/opencode/references/query.md +311 -0
- graphify/skills/opencode/references/transcribe.md +52 -0
- graphify/skills/opencode/references/update.md +210 -0
- graphify/skills/pi/references/add-watch.md +56 -0
- graphify/skills/pi/references/exports.md +87 -0
- graphify/skills/pi/references/extraction-spec.md +31 -0
- graphify/skills/pi/references/github-and-merge.md +46 -0
- graphify/skills/pi/references/hooks.md +33 -0
- graphify/skills/pi/references/query.md +311 -0
- graphify/skills/pi/references/transcribe.md +52 -0
- graphify/skills/pi/references/update.md +210 -0
- graphify/skills/trae/references/add-watch.md +56 -0
- graphify/skills/trae/references/exports.md +87 -0
- graphify/skills/trae/references/extraction-spec.md +70 -0
- graphify/skills/trae/references/github-and-merge.md +46 -0
- graphify/skills/trae/references/hooks.md +35 -0
- graphify/skills/trae/references/query.md +311 -0
- graphify/skills/trae/references/transcribe.md +52 -0
- graphify/skills/trae/references/update.md +210 -0
- graphify/skills/vscode/references/add-watch.md +56 -0
- graphify/skills/vscode/references/exports.md +87 -0
- graphify/skills/vscode/references/extraction-spec.md +70 -0
- graphify/skills/vscode/references/github-and-merge.md +46 -0
- graphify/skills/vscode/references/hooks.md +33 -0
- graphify/skills/vscode/references/query.md +311 -0
- graphify/skills/vscode/references/transcribe.md +52 -0
- graphify/skills/vscode/references/update.md +210 -0
- graphify/skills/windows/references/add-watch.md +56 -0
- graphify/skills/windows/references/exports.md +87 -0
- graphify/skills/windows/references/extraction-spec.md +70 -0
- graphify/skills/windows/references/github-and-merge.md +46 -0
- graphify/skills/windows/references/hooks.md +33 -0
- graphify/skills/windows/references/query.md +311 -0
- graphify/skills/windows/references/transcribe.md +52 -0
- graphify/skills/windows/references/update.md +210 -0
- graphify/symbol_resolution.py +556 -0
- graphify/transcribe.py +186 -0
- graphify/tree_html.py +603 -0
- graphify/validate.py +95 -0
- graphify/watch.py +2280 -0
- graphify/wiki.py +405 -0
- graphitect/__init__.py +28 -0
- graphitect/__main__.py +4 -0
- graphitect/_vendor/__init__.py +2 -0
- graphitect/_vendor/archify/LICENSE +22 -0
- graphitect/_vendor/archify/SKILL.md +137 -0
- graphitect/_vendor/archify/THIRD_PARTY_NOTICES.md +69 -0
- graphitect/_vendor/archify/assets/JetBrainsMono-OFL.txt +93 -0
- graphitect/_vendor/archify/assets/template.html +14935 -0
- graphitect/_vendor/archify/bin/archify.mjs +2091 -0
- graphitect/_vendor/archify/bin/open-artifact.mjs +86 -0
- graphitect/_vendor/archify/bin/preview.mjs +653 -0
- graphitect/_vendor/archify/bin/visual-check.mjs +829 -0
- graphitect/_vendor/archify/brand-marks/README.md +31 -0
- graphitect/_vendor/archify/brand-marks/catalog.json +131 -0
- graphitect/_vendor/archify/delta/architecture-delta.mjs +1221 -0
- graphitect/_vendor/archify/examples/agent-run.lifecycle.json +60 -0
- graphitect/_vendor/archify/examples/agent-tool-call.workflow.json +94 -0
- graphitect/_vendor/archify/examples/async-job-roundtrip.sequence.json +61 -0
- graphitect/_vendor/archify/examples/brand-aware-delivery.architecture.json +47 -0
- graphitect/_vendor/archify/examples/cache-miss-request.sequence.json +82 -0
- graphitect/_vendor/archify/examples/checkout-platform.base.architecture.json +31 -0
- graphitect/_vendor/archify/examples/checkout-platform.head.architecture.json +31 -0
- graphitect/_vendor/archify/examples/dataflow-product-analytics.html +15045 -0
- graphitect/_vendor/archify/examples/deployment-release.lifecycle.json +49 -0
- graphitect/_vendor/archify/examples/event-stream.dataflow.json +57 -0
- graphitect/_vendor/archify/examples/incident-response.workflow.json +64 -0
- graphitect/_vendor/archify/examples/lifecycle-agent-run.html +14980 -0
- graphitect/_vendor/archify/examples/product-analytics.dataflow.json +76 -0
- graphitect/_vendor/archify/examples/production-deployment.architecture.json +71 -0
- graphitect/_vendor/archify/examples/release-delivery.workflow.json +62 -0
- graphitect/_vendor/archify/examples/sequence-cache-miss-request.html +15060 -0
- graphitect/_vendor/archify/examples/web-app-rendered.html +15009 -0
- graphitect/_vendor/archify/examples/web-app.architecture.json +46 -0
- graphitect/_vendor/archify/examples/workflow-agent-tool-call-rendered.html +15051 -0
- graphitect/_vendor/archify/migrations/workflow-v2.mjs +279 -0
- graphitect/_vendor/archify/package-lock.json +149 -0
- graphitect/_vendor/archify/package.json +39 -0
- graphitect/_vendor/archify/recipes/scenarios.mjs +391 -0
- graphitect/_vendor/archify/references/authoring-contract.md +243 -0
- graphitect/_vendor/archify/references/brand-marks.md +65 -0
- graphitect/_vendor/archify/references/delivery-contract.md +120 -0
- graphitect/_vendor/archify/references/viewer-runtime.md +45 -0
- graphitect/_vendor/archify/renderers/architecture/grid.mjs +62 -0
- graphitect/_vendor/archify/renderers/architecture/render-architecture.mjs +1078 -0
- graphitect/_vendor/archify/renderers/dataflow/README.md +104 -0
- graphitect/_vendor/archify/renderers/dataflow/render-dataflow.mjs +483 -0
- graphitect/_vendor/archify/renderers/lifecycle/README.md +115 -0
- graphitect/_vendor/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
- graphitect/_vendor/archify/renderers/sequence/README.md +114 -0
- graphitect/_vendor/archify/renderers/sequence/render-sequence.mjs +464 -0
- graphitect/_vendor/archify/renderers/shared/brand-marks.mjs +563 -0
- graphitect/_vendor/archify/renderers/shared/cli.mjs +218 -0
- graphitect/_vendor/archify/renderers/shared/desktop-readability.mjs +26 -0
- graphitect/_vendor/archify/renderers/shared/diagnostics.mjs +127 -0
- graphitect/_vendor/archify/renderers/shared/engineering-profiles.mjs +157 -0
- graphitect/_vendor/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
- graphitect/_vendor/archify/renderers/shared/generated-validators.mjs +13 -0
- graphitect/_vendor/archify/renderers/shared/geometry.mjs +1423 -0
- graphitect/_vendor/archify/renderers/shared/i18n.mjs +595 -0
- graphitect/_vendor/archify/renderers/shared/layout-report.mjs +40 -0
- graphitect/_vendor/archify/renderers/shared/legend.mjs +217 -0
- graphitect/_vendor/archify/renderers/shared/output-path.mjs +340 -0
- graphitect/_vendor/archify/renderers/shared/repository-evidence.mjs +238 -0
- graphitect/_vendor/archify/renderers/shared/repository-location.mjs +58 -0
- graphitect/_vendor/archify/renderers/shared/text-fit.mjs +49 -0
- graphitect/_vendor/archify/renderers/shared/utils.mjs +232 -0
- graphitect/_vendor/archify/renderers/shared/validator.mjs +86 -0
- graphitect/_vendor/archify/renderers/workflow/README.md +223 -0
- graphitect/_vendor/archify/renderers/workflow/render-workflow.mjs +35 -0
- graphitect/_vendor/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
- graphitect/_vendor/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
- graphitect/_vendor/archify/schemas/README.md +211 -0
- graphitect/_vendor/archify/schemas/architecture.schema.json +178 -0
- graphitect/_vendor/archify/schemas/common.schema.json +115 -0
- graphitect/_vendor/archify/schemas/dataflow.schema.json +243 -0
- graphitect/_vendor/archify/schemas/lifecycle.schema.json +266 -0
- graphitect/_vendor/archify/schemas/sequence.schema.json +223 -0
- graphitect/_vendor/archify/schemas/workflow.schema.json +428 -0
- graphitect/_vendor/archify/scripts/check-render-output.mjs +836 -0
- graphitect/_vendor/archify/scripts/check-update.mjs +1667 -0
- graphitect/_vendor/archify/scripts/generate-brand-marks.mjs +141 -0
- graphitect/_vendor/archify/scripts/generate-validators.mjs +66 -0
- graphitect/_vendor/archify/scripts/render-examples.mjs +26 -0
- graphitect/_vendor/archify/scripts/update-contract.mjs +182 -0
- graphitect/_vendor/archify/skill-release.json +10 -0
- graphitect/cli.py +981 -0
- graphitect/deliver/__init__.py +5 -0
- graphitect/deliver/archify_adapter.py +1877 -0
- graphitect/deliver/archify_ir.py +160 -0
- graphitect/deliver/archify_repair.py +135 -0
- graphitect/deliver/doc_compiler.py +916 -0
- graphitect/ground/__init__.py +5 -0
- graphitect/ground/describe_source.py +27 -0
- graphitect/ground/fullread_source.py +56 -0
- graphitect/ground/graphify_source.py +107 -0
- graphitect/models.py +118 -0
- graphitect/skill/SKILL.md +80 -0
- graphitect/skill/agents/openai.yaml +4 -0
- graphitect/synthesize/__init__.py +5 -0
- graphitect/synthesize/engine.py +281 -0
- graphitect/synthesize/llm_backend.py +331 -0
- graphitect/synthesize/questions.py +139 -0
- graphitect/synthesize/rubric.py +104 -0
- graphitect-0.2.0.dist-info/METADATA +284 -0
- graphitect-0.2.0.dist-info/RECORD +336 -0
- graphitect-0.2.0.dist-info/WHEEL +5 -0
- graphitect-0.2.0.dist-info/entry_points.txt +2 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE +21 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE-ARCHIFY-MIT +22 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE-GRAPHIFY-APACHE-2.0 +202 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE-GRAPHIFY-MIT +21 -0
- graphitect-0.2.0.dist-info/licenses/NOTICE-ARCHIFY-THIRD-PARTY.md +69 -0
- graphitect-0.2.0.dist-info/licenses/NOTICE-GRAPHIFY +8 -0
- graphitect-0.2.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Migrating a language extractor out of extract.py
|
|
2
|
+
|
|
3
|
+
`graphify/extract.py` is being split into this package, one language per PR
|
|
4
|
+
(upstream issue #1212). This is the playbook for porting ONE language. It is
|
|
5
|
+
written so an AI agent can execute it in a single session.
|
|
6
|
+
|
|
7
|
+
## Status
|
|
8
|
+
|
|
9
|
+
| module | migrated |
|
|
10
|
+
|---|---|
|
|
11
|
+
| blade | yes |
|
|
12
|
+
| zig | yes |
|
|
13
|
+
| elixir | yes |
|
|
14
|
+
| razor | yes |
|
|
15
|
+
| dart | yes |
|
|
16
|
+
| rust | yes |
|
|
17
|
+
| go | yes |
|
|
18
|
+
| powershell (ps1 + psd1 manifest) | yes |
|
|
19
|
+
| fortran | yes |
|
|
20
|
+
| sql | yes |
|
|
21
|
+
| dm (dm/dmm/dmi/dmf) | yes |
|
|
22
|
+
| bash | yes |
|
|
23
|
+
| apex | yes |
|
|
24
|
+
| terraform | yes |
|
|
25
|
+
| sln | yes |
|
|
26
|
+
| pascal_forms (dfm + lfm) | yes |
|
|
27
|
+
| json_config | yes |
|
|
28
|
+
| (config-driven core: python, js, java, c, cpp, csharp, kotlin, scala, php, lua, swift, groovy, vue, svelte, astro, xaml, groovy) | no — shared _extract_generic core, move as one batch |
|
|
29
|
+
| (other bespoke: julia, verilog, markdown, objc, csproj, slnx, lazarus_package, pascal) | no |
|
|
30
|
+
|
|
31
|
+
Note: config-driven extractors (python, js, java, c, cpp, ruby, csharp,
|
|
32
|
+
kotlin, scala, php, lua, swift, groovy) depend on the shared
|
|
33
|
+
`_extract_generic` core (~1,300 lines). Do NOT port them one-by-one; the core
|
|
34
|
+
must move first as its own coordinated batch. Pick a bespoke extractor.
|
|
35
|
+
|
|
36
|
+
## Invariants (non-negotiable)
|
|
37
|
+
|
|
38
|
+
1. **Verbatim moves only.** No renames, no docstring edits, no reformatting,
|
|
39
|
+
no added annotations, no "improvements". Verify: save the block before
|
|
40
|
+
cutting and confirm the pasted block is byte-identical.
|
|
41
|
+
2. **One language per PR.** Small diffs keep review trivial and avoid
|
|
42
|
+
conflicts with other in-flight ports.
|
|
43
|
+
3. **Facade re-export is mandatory.** `extract.py` must keep exporting every
|
|
44
|
+
moved name (`from graphify.extractors.<mod> import extract_<lang> # noqa: F401`
|
|
45
|
+
in the marked migration block, kept alphabetical). Existing importers
|
|
46
|
+
(`__main__.py`, `watch.py`, `pg_introspect.py`, tests) must not change.
|
|
47
|
+
4. **Never import from `graphify.extract` inside this package.** Import
|
|
48
|
+
direction is strictly extract.py -> extractors/. If you need a helper that
|
|
49
|
+
lives in extract.py, classify it (below) and move it.
|
|
50
|
+
5. **Zero test edits** outside `tests/test_extractors_registry.py`. The
|
|
51
|
+
untouched language tests passing IS the proof of behavior preservation.
|
|
52
|
+
|
|
53
|
+
## Helper classification
|
|
54
|
+
|
|
55
|
+
For every `_name` your function references that is defined OUTSIDE it:
|
|
56
|
+
|
|
57
|
+
- run `grep -c '_name' graphify/extract.py` AFTER your candidate move;
|
|
58
|
+
- remaining uses > 0 -> **shared**: move it to `base.py` and add it to the
|
|
59
|
+
facade re-import in extract.py;
|
|
60
|
+
- remaining uses = 0 -> **private**: move it into your language module.
|
|
61
|
+
|
|
62
|
+
Closures, constants, and `import` statements defined INSIDE your function
|
|
63
|
+
move with it for free — leave them exactly where they are. Only add a
|
|
64
|
+
module-header import for names the pasted code references at module scope
|
|
65
|
+
that are not satisfied internally, and verify each header import is used.
|
|
66
|
+
|
|
67
|
+
## Pre-flight
|
|
68
|
+
|
|
69
|
+
1. Check upstream for conflicts: open PRs/issues mentioning your language,
|
|
70
|
+
and churn: `git log --oneline --since="3 months ago" upstream/<default> | grep -i <lang>`.
|
|
71
|
+
High churn -> pick another language.
|
|
72
|
+
2. Confirm your extractor is bespoke (its `extract_<lang>` is a full function,
|
|
73
|
+
not a 5-line `_extract_generic(path, LanguageConfig(...))` wrapper).
|
|
74
|
+
3. Check whether tests/ exercises your language's behavior (grep for
|
|
75
|
+
`test_<lang>`). If it has no behavioral tests, the byte-identity check in
|
|
76
|
+
step 3 below is the ENTIRE proof of preservation — include the
|
|
77
|
+
`git diff --color-moved` evidence in your PR description.
|
|
78
|
+
|
|
79
|
+
## Steps
|
|
80
|
+
|
|
81
|
+
1. Append a failing test to `tests/test_extractors_registry.py`:
|
|
82
|
+
module import + facade identity + registry identity (copy an existing
|
|
83
|
+
`test_<lang>_migrated` as the template).
|
|
84
|
+
2. `grep -n 'def extract_<lang>' graphify/extract.py`; the span ends at the
|
|
85
|
+
line before the next top-level statement (`^def ` or `^_CONST`). Beware
|
|
86
|
+
neighbors: top-level constants AFTER your function may belong to the NEXT
|
|
87
|
+
function (e.g. `_CONFIG_JSON_*` sit after where extract_razor used to be
|
|
88
|
+
but were never razor's).
|
|
89
|
+
3. Save the span to a temp file. Create `graphify/extractors/<lang>.py` with
|
|
90
|
+
module docstring (`"""<Lang> extractor. Moved verbatim from graphify/extract.py."""`),
|
|
91
|
+
`from __future__ import annotations`, minimal stdlib imports, base imports,
|
|
92
|
+
then paste the function. Verify byte-identity against the temp file.
|
|
93
|
+
4. Delete the span from extract.py, leaving exactly two blank lines between
|
|
94
|
+
the now-adjacent top-level definitions; add the facade re-import; add the
|
|
95
|
+
registry entry in `__init__.py` (alphabetical); update the Status table
|
|
96
|
+
above.
|
|
97
|
+
5. `uv run pytest -q` -> 0 failures, no test file changed except the registry
|
|
98
|
+
test. If ImportError/NameError: a helper was misclassified — go to
|
|
99
|
+
Helper classification.
|
|
100
|
+
6. One commit: `refactor(extract): move extract_<lang> to extractors/<lang>.py (verbatim)`.
|
|
101
|
+
|
|
102
|
+
## What NOT to do
|
|
103
|
+
|
|
104
|
+
- Do not rewire dispatch, add classes, or add lazy imports — mechanism layers
|
|
105
|
+
come later, by separate agreement (see #1212 discussion).
|
|
106
|
+
- Do not port two languages in one PR "while you're at it".
|
|
107
|
+
- Do not touch `__main__.py`.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Per-language extractors, incrementally migrated out of graphify/extract.py.
|
|
2
|
+
|
|
3
|
+
Dispatch still flows through graphify.extract (the facade re-exports every
|
|
4
|
+
moved name), so importing from graphify.extract keeps working unchanged.
|
|
5
|
+
LANGUAGE_EXTRACTORS is the registry seed; wiring dispatch through it is a
|
|
6
|
+
later, separate step. See MIGRATION.md for how to port another language.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Callable
|
|
12
|
+
|
|
13
|
+
from graphify.extractors.apex import extract_apex
|
|
14
|
+
from graphify.extractors.bash import extract_bash
|
|
15
|
+
from graphify.extractors.blade import extract_blade
|
|
16
|
+
from graphify.extractors.commonlisp import extract_commonlisp
|
|
17
|
+
from graphify.extractors.dart import extract_dart
|
|
18
|
+
from graphify.extractors.dm import extract_dm, extract_dmf, extract_dmi, extract_dmm
|
|
19
|
+
from graphify.extractors.elixir import extract_elixir
|
|
20
|
+
from graphify.extractors.fortran import extract_fortran
|
|
21
|
+
from graphify.extractors.go import extract_go
|
|
22
|
+
from graphify.extractors.json_config import extract_json
|
|
23
|
+
from graphify.extractors.julia import extract_julia
|
|
24
|
+
from graphify.extractors.markdown import extract_markdown
|
|
25
|
+
from graphify.extractors.objc import extract_objc
|
|
26
|
+
from graphify.extractors.pascal import extract_pascal
|
|
27
|
+
from graphify.extractors.pascal_forms import extract_delphi_form, extract_lazarus_form
|
|
28
|
+
from graphify.extractors.powershell import extract_powershell, extract_powershell_manifest
|
|
29
|
+
from graphify.extractors.razor import extract_razor
|
|
30
|
+
from graphify.extractors.rust import extract_rust
|
|
31
|
+
from graphify.extractors.sln import extract_sln
|
|
32
|
+
from graphify.extractors.sql import extract_sql
|
|
33
|
+
from graphify.extractors.terraform import extract_terraform
|
|
34
|
+
from graphify.extractors.verilog import extract_verilog
|
|
35
|
+
from graphify.extractors.zig import extract_zig
|
|
36
|
+
|
|
37
|
+
LANGUAGE_EXTRACTORS: dict[str, Callable[[Path], dict]] = {
|
|
38
|
+
"apex": extract_apex,
|
|
39
|
+
"bash": extract_bash,
|
|
40
|
+
"blade": extract_blade,
|
|
41
|
+
"commonlisp": extract_commonlisp,
|
|
42
|
+
"dart": extract_dart,
|
|
43
|
+
"delphi_form": extract_delphi_form,
|
|
44
|
+
"dm": extract_dm,
|
|
45
|
+
"dmf": extract_dmf,
|
|
46
|
+
"dmi": extract_dmi,
|
|
47
|
+
"dmm": extract_dmm,
|
|
48
|
+
"elixir": extract_elixir,
|
|
49
|
+
"fortran": extract_fortran,
|
|
50
|
+
"go": extract_go,
|
|
51
|
+
"json": extract_json,
|
|
52
|
+
"julia": extract_julia,
|
|
53
|
+
"lazarus_form": extract_lazarus_form,
|
|
54
|
+
"markdown": extract_markdown,
|
|
55
|
+
"objc": extract_objc,
|
|
56
|
+
"pascal": extract_pascal,
|
|
57
|
+
"powershell": extract_powershell,
|
|
58
|
+
"powershell_manifest": extract_powershell_manifest,
|
|
59
|
+
"razor": extract_razor,
|
|
60
|
+
"rust": extract_rust,
|
|
61
|
+
"sln": extract_sln,
|
|
62
|
+
"sql": extract_sql,
|
|
63
|
+
"terraform": extract_terraform,
|
|
64
|
+
"verilog": extract_verilog,
|
|
65
|
+
"zig": extract_zig,
|
|
66
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Apex extractor. Moved verbatim from graphify/extract.py."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from graphify.extractors.base import _file_stem, _make_id
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def extract_apex(path: Path) -> dict:
|
|
10
|
+
"""Extract classes, interfaces, enums, methods, and Salesforce constructs from
|
|
11
|
+
Apex .cls and .trigger files using regex (no tree-sitter grammar on PyPI)."""
|
|
12
|
+
import re as _re
|
|
13
|
+
try:
|
|
14
|
+
source = path.read_text(encoding="utf-8", errors="replace")
|
|
15
|
+
except OSError:
|
|
16
|
+
return {"nodes": [], "edges": []}
|
|
17
|
+
|
|
18
|
+
str_path = str(path)
|
|
19
|
+
stem = _file_stem(path)
|
|
20
|
+
file_nid = _make_id(str_path)
|
|
21
|
+
|
|
22
|
+
nodes: list[dict] = []
|
|
23
|
+
edges: list[dict] = []
|
|
24
|
+
seen_ids: set[str] = set()
|
|
25
|
+
|
|
26
|
+
def add_node(nid: str, label: str, line: int) -> None:
|
|
27
|
+
if nid not in seen_ids:
|
|
28
|
+
seen_ids.add(nid)
|
|
29
|
+
nodes.append({
|
|
30
|
+
"id": nid,
|
|
31
|
+
"label": label,
|
|
32
|
+
"file_type": "code",
|
|
33
|
+
"source_file": str_path,
|
|
34
|
+
"source_location": f"L{line}",
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
def add_edge(src: str, tgt: str, relation: str, line: int,
|
|
38
|
+
confidence: str = "EXTRACTED") -> None:
|
|
39
|
+
edges.append({
|
|
40
|
+
"source": src,
|
|
41
|
+
"target": tgt,
|
|
42
|
+
"relation": relation,
|
|
43
|
+
"confidence": confidence,
|
|
44
|
+
"source_file": str_path,
|
|
45
|
+
"source_location": f"L{line}",
|
|
46
|
+
"weight": 1.0,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
add_node(file_nid, path.name, 1)
|
|
50
|
+
|
|
51
|
+
lines = source.splitlines()
|
|
52
|
+
|
|
53
|
+
_ACCESS = r"(?:public|private|protected|global|webService)?"
|
|
54
|
+
_SHARING = r"(?:\s+(?:with|without|inherited)\s+sharing)?"
|
|
55
|
+
_MOD = r"(?:\s+(?:abstract|virtual|override|static|final|transient|testMethod))?"
|
|
56
|
+
_ANNOTATION = r"(?:\s*@\w+(?:\s*\([^)]*\))?\s*)*"
|
|
57
|
+
|
|
58
|
+
cls_re = _re.compile(
|
|
59
|
+
rf"^{_ANNOTATION}\s*{_ACCESS}{_SHARING}{_MOD}\s*class\s+(\w+)"
|
|
60
|
+
rf"(?:\s+extends\s+(\w+))?(?:\s+implements\s+([\w,\s]+))?\s*\{{?",
|
|
61
|
+
_re.IGNORECASE,
|
|
62
|
+
)
|
|
63
|
+
iface_re = _re.compile(
|
|
64
|
+
rf"^{_ANNOTATION}\s*{_ACCESS}{_SHARING}{_MOD}\s*interface\s+(\w+)"
|
|
65
|
+
rf"(?:\s+extends\s+([\w,\s]+))?\s*\{{?",
|
|
66
|
+
_re.IGNORECASE,
|
|
67
|
+
)
|
|
68
|
+
enum_re = _re.compile(
|
|
69
|
+
rf"^{_ANNOTATION}\s*{_ACCESS}{_SHARING}{_MOD}\s*enum\s+(\w+)\s*\{{?",
|
|
70
|
+
_re.IGNORECASE,
|
|
71
|
+
)
|
|
72
|
+
trigger_re = _re.compile(
|
|
73
|
+
r"^\s*trigger\s+(\w+)\s+on\s+(\w+)\s*\(",
|
|
74
|
+
_re.IGNORECASE,
|
|
75
|
+
)
|
|
76
|
+
method_re = _re.compile(
|
|
77
|
+
rf"^{_ANNOTATION}\s*{_ACCESS}{_MOD}\s*(?:static\s+)?[\w<>\[\]]+\s+(\w+)\s*\([^)]*\)\s*(?:throws\s+\w+\s*)?\{{?",
|
|
78
|
+
_re.IGNORECASE,
|
|
79
|
+
)
|
|
80
|
+
annotation_re = _re.compile(r"@(\w+)", _re.IGNORECASE)
|
|
81
|
+
soql_re = _re.compile(r"\[\s*SELECT\b[^\]]+FROM\s+(\w+)", _re.IGNORECASE)
|
|
82
|
+
dml_re = _re.compile(r"\b(insert|update|delete|upsert|merge|undelete)\s+\w", _re.IGNORECASE)
|
|
83
|
+
|
|
84
|
+
_CONTROL_FLOW = frozenset({
|
|
85
|
+
"if", "else", "for", "while", "do", "switch", "try", "catch",
|
|
86
|
+
"finally", "return", "throw", "new", "void", "null",
|
|
87
|
+
"true", "false", "this", "super", "class", "interface", "enum",
|
|
88
|
+
"trigger", "on",
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
current_class_nid: str | None = None
|
|
92
|
+
pending_annotations: list[str] = []
|
|
93
|
+
|
|
94
|
+
for lineno, line_text in enumerate(lines, start=1):
|
|
95
|
+
stripped = line_text.strip()
|
|
96
|
+
|
|
97
|
+
if stripped.startswith("@"):
|
|
98
|
+
for m in annotation_re.finditer(stripped):
|
|
99
|
+
pending_annotations.append(m.group(1).lower())
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
tm = trigger_re.match(stripped)
|
|
103
|
+
if tm:
|
|
104
|
+
trig_name, sobject = tm.group(1), tm.group(2)
|
|
105
|
+
trig_nid = _make_id(stem, trig_name)
|
|
106
|
+
add_node(trig_nid, trig_name, lineno)
|
|
107
|
+
add_edge(file_nid, trig_nid, "contains", lineno)
|
|
108
|
+
sob_nid = _make_id(sobject)
|
|
109
|
+
if sob_nid not in seen_ids:
|
|
110
|
+
add_node(sob_nid, sobject, lineno)
|
|
111
|
+
add_edge(trig_nid, sob_nid, "uses", lineno, confidence="INFERRED")
|
|
112
|
+
current_class_nid = trig_nid
|
|
113
|
+
pending_annotations = []
|
|
114
|
+
continue
|
|
115
|
+
|
|
116
|
+
cm = cls_re.match(stripped)
|
|
117
|
+
if cm:
|
|
118
|
+
class_name = cm.group(1)
|
|
119
|
+
if class_name.lower() in _CONTROL_FLOW:
|
|
120
|
+
pending_annotations = []
|
|
121
|
+
continue
|
|
122
|
+
class_nid = _make_id(stem, class_name)
|
|
123
|
+
add_node(class_nid, class_name, lineno)
|
|
124
|
+
add_edge(file_nid, class_nid, "contains", lineno)
|
|
125
|
+
if cm.group(2):
|
|
126
|
+
base = cm.group(2).strip()
|
|
127
|
+
base_nid = _make_id(stem, base)
|
|
128
|
+
if base_nid not in seen_ids:
|
|
129
|
+
base_nid = _make_id(base)
|
|
130
|
+
if base_nid not in seen_ids:
|
|
131
|
+
add_node(base_nid, base, lineno)
|
|
132
|
+
add_edge(class_nid, base_nid, "extends", lineno, confidence="INFERRED")
|
|
133
|
+
if cm.group(3):
|
|
134
|
+
for iface in cm.group(3).split(","):
|
|
135
|
+
iface = iface.strip()
|
|
136
|
+
if iface:
|
|
137
|
+
iface_nid = _make_id(stem, iface)
|
|
138
|
+
if iface_nid not in seen_ids:
|
|
139
|
+
iface_nid = _make_id(iface)
|
|
140
|
+
if iface_nid not in seen_ids:
|
|
141
|
+
add_node(iface_nid, iface, lineno)
|
|
142
|
+
add_edge(class_nid, iface_nid, "implements", lineno, confidence="INFERRED")
|
|
143
|
+
current_class_nid = class_nid
|
|
144
|
+
pending_annotations = []
|
|
145
|
+
continue
|
|
146
|
+
|
|
147
|
+
im = iface_re.match(stripped)
|
|
148
|
+
if im:
|
|
149
|
+
iface_name = im.group(1)
|
|
150
|
+
if iface_name.lower() in _CONTROL_FLOW:
|
|
151
|
+
pending_annotations = []
|
|
152
|
+
continue
|
|
153
|
+
iface_nid = _make_id(stem, iface_name)
|
|
154
|
+
add_node(iface_nid, iface_name, lineno)
|
|
155
|
+
add_edge(file_nid if current_class_nid is None else current_class_nid,
|
|
156
|
+
iface_nid, "contains", lineno)
|
|
157
|
+
if im.group(2):
|
|
158
|
+
for parent in im.group(2).split(","):
|
|
159
|
+
parent = parent.strip()
|
|
160
|
+
if parent:
|
|
161
|
+
parent_nid = _make_id(stem, parent)
|
|
162
|
+
if parent_nid not in seen_ids:
|
|
163
|
+
parent_nid = _make_id(parent)
|
|
164
|
+
if parent_nid not in seen_ids:
|
|
165
|
+
add_node(parent_nid, parent, lineno)
|
|
166
|
+
add_edge(iface_nid, parent_nid, "extends", lineno, confidence="INFERRED")
|
|
167
|
+
pending_annotations = []
|
|
168
|
+
continue
|
|
169
|
+
|
|
170
|
+
em = enum_re.match(stripped)
|
|
171
|
+
if em:
|
|
172
|
+
enum_name = em.group(1)
|
|
173
|
+
if enum_name.lower() in _CONTROL_FLOW:
|
|
174
|
+
pending_annotations = []
|
|
175
|
+
continue
|
|
176
|
+
enum_nid = _make_id(stem, enum_name)
|
|
177
|
+
add_node(enum_nid, enum_name, lineno)
|
|
178
|
+
add_edge(file_nid if current_class_nid is None else current_class_nid,
|
|
179
|
+
enum_nid, "contains", lineno)
|
|
180
|
+
pending_annotations = []
|
|
181
|
+
continue
|
|
182
|
+
|
|
183
|
+
if current_class_nid is not None:
|
|
184
|
+
mm = method_re.match(stripped)
|
|
185
|
+
if mm:
|
|
186
|
+
method_name = mm.group(1)
|
|
187
|
+
if method_name.lower() not in _CONTROL_FLOW:
|
|
188
|
+
method_nid = _make_id(current_class_nid, method_name)
|
|
189
|
+
method_label = f".{method_name}()"
|
|
190
|
+
add_node(method_nid, method_label, lineno)
|
|
191
|
+
add_edge(current_class_nid, method_nid, "method", lineno)
|
|
192
|
+
if "auraenabled" in pending_annotations or "invocablemethod" in pending_annotations:
|
|
193
|
+
add_edge(file_nid, method_nid, "contains", lineno, confidence="INFERRED")
|
|
194
|
+
pending_annotations = []
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
pending_annotations = []
|
|
198
|
+
|
|
199
|
+
for sm in soql_re.finditer(line_text):
|
|
200
|
+
sobject = sm.group(1)
|
|
201
|
+
sob_nid = _make_id(sobject)
|
|
202
|
+
if sob_nid not in seen_ids:
|
|
203
|
+
add_node(sob_nid, sobject, lineno)
|
|
204
|
+
src = current_class_nid or file_nid
|
|
205
|
+
add_edge(src, sob_nid, "uses", lineno, confidence="INFERRED")
|
|
206
|
+
|
|
207
|
+
for dm in dml_re.finditer(line_text):
|
|
208
|
+
dml_op = dm.group(1).lower()
|
|
209
|
+
dml_nid = _make_id(f"dml_{dml_op}")
|
|
210
|
+
if dml_nid not in seen_ids:
|
|
211
|
+
add_node(dml_nid, dml_op, lineno)
|
|
212
|
+
src = current_class_nid or file_nid
|
|
213
|
+
add_edge(src, dml_nid, "uses", lineno, confidence="INFERRED")
|
|
214
|
+
|
|
215
|
+
return {"nodes": nodes, "edges": edges}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# DO NOT import from graphify.extract here — direction is extract.py → extractors/ only.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from graphify.ids import make_id
|
|
7
|
+
|
|
8
|
+
# Language built-in globals that AST may classify as call targets when used as
|
|
9
|
+
# constructors or coercion functions (e.g. String(x), Number(x), Boolean(x)).
|
|
10
|
+
# Without this filter they become god-nodes accumulating spurious edges from
|
|
11
|
+
# every call site. Filter applied at same-file and cross-file resolution.
|
|
12
|
+
# See issue #726.
|
|
13
|
+
_LANGUAGE_BUILTIN_GLOBALS: frozenset[str] = frozenset({
|
|
14
|
+
# JavaScript / TypeScript ECMAScript built-ins
|
|
15
|
+
"String", "Number", "Boolean", "Object", "Array", "Symbol", "BigInt",
|
|
16
|
+
"Date", "RegExp", "Error", "TypeError", "RangeError", "SyntaxError",
|
|
17
|
+
"ReferenceError", "EvalError", "URIError",
|
|
18
|
+
"Promise", "Map", "Set", "WeakMap", "WeakSet", "JSON", "Math",
|
|
19
|
+
"Reflect", "Proxy", "Intl",
|
|
20
|
+
"parseInt", "parseFloat", "isNaN", "isFinite",
|
|
21
|
+
"encodeURIComponent", "decodeURIComponent", "encodeURI", "decodeURI",
|
|
22
|
+
# Browser / Node common globals
|
|
23
|
+
"URL", "URLSearchParams", "FormData", "Blob", "File",
|
|
24
|
+
"Headers", "Request", "Response", "AbortController", "AbortSignal",
|
|
25
|
+
"TextEncoder", "TextDecoder", "console",
|
|
26
|
+
# Python built-in callables
|
|
27
|
+
"str", "int", "float", "bool", "list", "dict", "set", "tuple", "bytes",
|
|
28
|
+
"len", "range", "enumerate", "zip", "map", "filter", "sum", "min", "max",
|
|
29
|
+
"print", "open", "isinstance", "type", "super", "sorted", "reversed",
|
|
30
|
+
"any", "all", "abs", "round", "next", "iter", "hash", "id", "repr",
|
|
31
|
+
"callable", "getattr", "setattr", "hasattr", "delattr", "vars", "dir",
|
|
32
|
+
# Swift standard library / Foundation / SwiftUI (#2147). Value-type
|
|
33
|
+
# initializers (Data(x), Int(x), UUID()) and protocol conformance targets
|
|
34
|
+
# appear from virtually every file of a Swift codebase, exactly like the
|
|
35
|
+
# ECMAScript constructors above. String/Date/URL/Error are already listed.
|
|
36
|
+
"Int", "Int8", "Int16", "Int32", "Int64",
|
|
37
|
+
"UInt", "UInt8", "UInt16", "UInt32", "UInt64",
|
|
38
|
+
"Double", "Float", "Bool", "Character",
|
|
39
|
+
"Sendable", "Codable", "Decodable", "Encodable", "Equatable", "Hashable",
|
|
40
|
+
"Identifiable", "Comparable", "CaseIterable", "RawRepresentable",
|
|
41
|
+
"CustomStringConvertible", "CustomDebugStringConvertible", "AnyObject",
|
|
42
|
+
"LocalizedError",
|
|
43
|
+
"Data", "UUID", "Decimal", "Calendar", "Locale", "TimeZone", "Bundle",
|
|
44
|
+
"IndexPath", "IndexSet", "NotificationCenter", "UserDefaults",
|
|
45
|
+
"FileManager", "URLSession", "URLRequest", "URLComponents",
|
|
46
|
+
"JSONDecoder", "JSONEncoder", "DateFormatter", "NumberFormatter",
|
|
47
|
+
"ISO8601DateFormatter",
|
|
48
|
+
"NSObject", "NSString", "NSError", "NSLock", "NSAttributedString",
|
|
49
|
+
"DispatchQueue", "DispatchGroup", "OperationQueue", "RunLoop",
|
|
50
|
+
"View", "Color", "Font",
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _make_id(*parts: str) -> str:
|
|
55
|
+
return make_id(*parts)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _file_stem(path: Path) -> str:
|
|
59
|
+
"""Stem used as the node-ID prefix for a file and its symbols.
|
|
60
|
+
|
|
61
|
+
The full path (extension dropped) is preserved as path segments; ``make_id``
|
|
62
|
+
later collapses the separators to underscores. Using every segment — not just
|
|
63
|
+
the immediate parent dir (#1504) — means same-named files in different
|
|
64
|
+
directories get distinct IDs instead of colliding into one
|
|
65
|
+
last-writer-wins node:
|
|
66
|
+
|
|
67
|
+
docs/v1/api/README.md -> docs/v1/api/README -> docs_v1_api_readme
|
|
68
|
+
docs/v2/api/README.md -> docs/v2/api/README -> docs_v2_api_readme
|
|
69
|
+
|
|
70
|
+
Top-level files keep a bare stem (``setup.py`` -> ``setup``). When passed an
|
|
71
|
+
absolute path the whole path is encoded; the extract() id-remap post-pass
|
|
72
|
+
re-derives the canonical repo-relative form from ``source_file`` so the on-disk
|
|
73
|
+
location can't leak into the persisted IDs (#502).
|
|
74
|
+
|
|
75
|
+
Returns "" for a path with no name (``Path('.')`` — a source_file that equals
|
|
76
|
+
the scan root, so it has no per-file stem). Guarding here keeps
|
|
77
|
+
``path.with_suffix("")`` from raising ``ValueError: '.' has an empty name`` and
|
|
78
|
+
protects every caller, not just ``_semantic_id_remap`` (#1618)."""
|
|
79
|
+
if not path.name:
|
|
80
|
+
return ""
|
|
81
|
+
return path.with_suffix("").as_posix()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _read_text(node, source: bytes) -> str:
|
|
85
|
+
return source[node.start_byte:node.end_byte].decode("utf-8", errors="replace")
|