ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

graphify 语言抽取器迁移指南:把 extract.py 按语言逐条拆分到 extractors 包的完整 Playbook

graphify 语言抽取器迁移指南:把 extract.py 按语言逐条拆分到 extractors 包的完整 Playbook graphify 语言抽取器迁移指南把 extract.py 按语言逐条拆分到 extractors 包的完整 Playbook【免费下载链接】graphifyTurn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, and Gemini CLI: local deterministic AST parsing, every edge explained, no vector store.项目地址: https://gitcode.com/GitHub_Trending/graph/graphify本文围绕 MIGRATION.md 展开这是 graphify 仓库中把巨型单文件graphify/extract.py约 7300 行按每语言一个 PR的节奏、逐条搬运到graphify/extractors/包的官方操作手册。读完后你可以独立完成一次语言抽取器的字节级迁移知道哪些语言已迁完、必须遵守的五条不可妥协不变量、辅助函数如何分类归属、迁移前后的检查命令以及为什么一个测试都不改本身就是行为保真的证明。一、背景为什么要把语言抽取器从 extract.py 拆出去graphify 的核心能力是本地确定性 AST 解析——用 tree-sitter 把代码库、文档、SQL schema、配置和 PDF 变成可查询的知识图谱。所有语言的抽取入口都曾经集中在 graphify/extract.py 这一个文件里每个语言对应一个extract_lang(path: Path) - dict函数输出统一的{nodes: [...], edges: [...]}结构。随着支持语言增多该文件膨胀到 7000 行任何语言的改动都可能与其他语言的改动在同一个巨型文件里产生合并冲突。仓库为此启动了拆分上游 issue #1212目标结构已经落地graphify/extractors/ 包一个语言一个模块例如 terraform.py、go.py、rust.py、sql.py、powershell.py 等graphify/extractors/base.py 存放多语言共享的辅助函数如_make_id、_file_stem、_read_text、内置全局名过滤表_LANGUAGE_BUILTIN_GLOBALSgraphify/extract.py退化为门面facade它仍然 re-export 所有已迁移的名字保证__main__.py、watch.py、pg_introspect.py、测试等既有导入方一行代码都不用改graphify/extractors/init.py 提供LANGUAGE_EXTRACTORS注册表种子——从源码结构看dispatch 目前仍走graphify.extract接线到注册表是后续单独一步MIGRATION.md 也明确禁止在迁移 PR 里提前做这件事。MIGRATION.md 特意写成AI agent 可以在单次会话内执行的 Playbook 形式这一点决定了它的每一步都给出可直接运行的命令。二、迁移状态谁迁完了谁还没迁MIGRATION.md 内嵌一张状态表是当前拆分进度的权威记录modulemigratedbladeyeszigyeselixiryesrazoryesdartyesrustyesgoyespowershell (ps1 psd1 manifest)yesfortranyessqlyesdm (dm/dmm/dmi/dmf)yesbashyesapexyesterraformyesslnyespascal_forms (dfm lfm)yesjson_configyes(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(other bespoke: julia, verilog, markdown, objc, csproj, slnx, lazarus_package, pascal)no注上表按 MIGRATION.md 原文继承仓库演进后部分bespoke条目如 julia、verilog、markdown、objc、pascal 已在 extractors/init.py 的LANGUAGE_EXTRACTORS注册表中出现实际状态应以该注册表与最新提交为准。文档中特别强调一条选择规则不要选 config-driven 语言。python、js、java、c、cpp、csharp、kotlin、scala、php、lua、swift、groovy 这些语言的extract_lang只是几行_extract_generic(path, LanguageConfig(...))包装真正的逻辑在共享的_extract_generic核心约 1300 行里且该核心在 extract.py 中仍有 20 多处调用点。逐个搬这些语言毫无意义——核心必须作为一次协调好的批次整体迁移所以 Playbook 建议Pick a bespoke extractor挑一个自带完整函数体的语言。三、五条不变量non-negotiable这是整篇 Playbook 的核心纪律任何一条被破坏都意味着迁移失败只做逐字搬运Verbatim moves only。不许改名、不许改 docstring、不许重排格式、不许加类型标注、不许顺手改进。验证方法剪下代码块前先存一份临时文件贴到目标模块后确认两个块字节一致。一个 PR 只迁一个语言。小 diff 让评审变得平凡也避免与其他在途迁移在extract.py同一区域产生冲突。门面 re-export 是强制的。extract.py必须继续在标记好的迁移块里导出所有已迁移的名字形如from graphify.extractors.mod import extract_lang # noqa: F401并保持字母序。既有导入方__main__.py、watch.py、pg_introspect.py、tests一行都不能改。在 extract.py 中可以验证这一块的现状第 28 行起有# --- migrated to graphify/extractors/ (see graphify/extractors/MIGRATION.md) ---标记其下是按字母序排列的 re-export 列表包括从base导入的_LANGUAGE_BUILTIN_GLOBALS、_file_stem、_make_id、_read_text等共享辅助名。包内永远不许反向导入graphify.extract。依赖方向严格是extract.py → extractors/。base.py 第 1 行就写着一行注释防线# DO NOT import from graphify.extract here — direction is extract.py → extractors/ only.。如果你需要用到只存在于extract.py的辅助函数走下面的辅助函数分类流程把它也迁走。测试零改动——唯一例外是tests/test_extractors_registry.py。原有语言测试原封不动地通过本身就是行为保真的证明你既没有改函数体字节一致也没有改测试测试仍在测同一个对象那么行为没变。四、辅助函数分类搬函数前必须做的一次 grep被迁移的extract_lang会引用许多_name开头的前置辅助函数。对每一个定义在函数外部的名字Playbook 给出确定性的判定算法先完成你候选函数体的剪切此时extract.py里只剩其他语言对这些名字的使用执行grep -c _name graphify/extract.py剩余使用数 0→ 判定为shared把它迁到 extractors/base.py并在extract.py的门面 re-import 中加上它如现状中的from graphify.extractors.base import (_LANGUAGE_BUILTIN_GLOBALS, _file_stem, _make_id, _read_text)见 extract.py剩余使用数 0→ 判定为private直接搬进你的语言模块内部。两条补充规则函数体内定义的闭包、常量、import语句随函数体免费迁移留在原样即可只给粘贴后的代码在模块作用域引用、但函数体内部没有满足的名字添加模块头部 import并且逐一验证每个头部 import 都被实际使用——这正是 terraform.py 头部只导入Path和_make_id的原因它的闭包_read、_label_text、_add_node都在函数体内自给自足。分类错误的典型症状会在验证阶段暴露ImportError或NameError此时 Playbook 的处方是回到本节重新分类而不是临时打补丁。五、Pre-flight动刀前的三项检查查上游冲突看是否有在途 PR/issue 提到目标语言并检查近三个月的改动热度git log --oneline --since3 months ago upstream/default | grep -i lang热度高 → 换一个语言避免迁移到一半源文件被别人改了。确认你的抽取器是 bespoke 的它的extract_lang必须是一个完整函数体而不是 5 行的_extract_generic(path, LanguageConfig(...))包装对应第二节的选择规则。检查测试覆盖在tests/里 greptest_lang。如果该语言没有行为测试那么步骤 3 中的字节一致性检查就是保真的全部证明——此时 PR 描述中必须附git diff --color-moved的输出作为证据。六、迁移六步法Playbook 的主体是六个步骤每一步都有明确的产出物和失败处理Step 1 — 先写失败测试。往 tests/test_extractors_registry.py 追加一个失败测试覆盖三重断言模块可导入 门面同一性facade identity 注册表同一性registry identity直接复制现有的test_lang_migrated做模板。该测试文件的 docstring 解释了为什么要做同一性而非等价性检查graphify.extract必须 re-export同一个函数对象——如果门面持有一份过期拷贝或发生了影子导入对象会静默分叉。仓库现状中还有一个泛化版守卫test_every_registry_extractor_is_reexported_from_facade它遍历整个LANGUAGE_EXTRACTORS注册表任何遗漏门面 re-export 或 re-export 错对象的未来迁移都会在这里响亮地失败。Step 2 — 定位函数跨度。grep -n def extract_lang graphify/extract.py跨度一直延伸到下一个顶层语句^def或^_CONST之前。文档特别提醒了一个真实踩过的坑函数后面的顶层常量可能属于下一个函数——例如_CONFIG_JSON_*常量位于当年extract_razor曾经驻留的位置之后但它们从来不是 razor 的属于 json_config。Step 3 — 存临时文件、建新模块、验证字节一致。把跨度存到临时文件创建graphify/extractors/lang.py结构固定为模块 docstringLang extractor. Moved verbatim from graphify/extract.py.terraform.py 第 1 行即是标准样板、from __future__ import annotations、最小化的标准库 import、base 导入然后粘贴函数体并与临时文件做字节一致性比对。Step 4 — 从 extract.py 删除并接线。删掉该跨度使现在相邻的两个顶层定义之间恰好留两个空行在标记好的迁移块里按字母序添加门面 re-import在 extractors/init.py 的LANGUAGE_EXTRACTORS字典中按字母序加注册表条目更新 MIGRATION.md 里的 Status 表。Step 5 — 全量跑测试。uv run pytest -q要求 0 失败且除注册表测试外没有任何测试文件被修改。若出现ImportError/NameError说明有辅助函数被错误分类——回到第四节。Step 6 — 单一提交。commit message 固定格式refactor(extract): move extract_lang to extractors/lang.py (verbatim)七、明确不做的事What NOT to doPlaybook 用三条负面清单划出机制层改造的边界不要重接 dispatch、不要加类、不要加懒加载 import——机制层改造属于后续单独协商的事项见 #1212 讨论不属于迁移 PR 的范围不要顺手在一个 PR 里迁两个语言不要碰__main__.py——门面 re-export 的设计目的就是让graphify/extract.py对__main__.py等调用方保持 API 完全不变。八、仓库现状佐证拆分架构已经跑通结合当前仓库代码可以验证这套 Playbook 的落地形态与文档描述完全一致门面块extract.py 第 28 行的迁移标记块内按字母序 re-export 了 apex、bash、blade、csharp、dart、dm、elixir、fortran、go、json_config、commonlisp、markdown、ocaml、pascal_forms、powershell、razor、rust、sln、sql、terraform、verilog、zig 等模块的名字全部带# noqa: F401注册表extractors/init.py 中LANGUAGE_EXTRACTORS: dict[str, Callable[[Path], dict]]把 27 个键含delphi_form、lazarus_form、powershell_manifest这类非语言名 key映射到各自模块的函数对象docstring 明确写着 wiring dispatch through it is a later, separate step与 MIGRATION.md 的边界声明一致同一性守卫测试test_extractors_registry.py 中的test_terraform_migrated是 #1721 的具体锚点——断言facade.extract_terraform is extract_terraform且LANGUAGE_EXTRACTORS[terraform] is extract_terraformis比较的正是对象同一性而非相等性共享 basebase.py 只依赖graphify.ids的标准库与内部模块不 importgraphify.extract方向约束得到物理落实其中的_file_stemdocstring 还记录了迁移后仍持续演进的共享逻辑如 #502 路径编码、#1504 同名文件区分。九、这套 Playbook 背后的工程思想MIGRATION.md 的价值不仅在于步骤本身还在于它把如何安全重构一个 7000 行单文件沉淀成了可机械执行的纪律其思想值得单独提炼用不动测试当回归证明。行为测试没改、函数体字节没改那么通过即保真——这比新增测试更接近对零行为变化的形式化论证没有行为测试的语言则用git diff --color-moved证据补位。门面模式解耦迁移与消费方。graphify.extract的对外 API 在整个迁移周期内保持冻结__main__.py、watch.py、MCP 相关入口和测试零改动迁移只发生在一个方向extract.py → extractors/上。用 grep 计数做依赖分类。移动后剩余使用数 0 即 shared是一个无需判断力的客观判据把最容易出错的这个辅助函数归谁问题变成了可重复执行的命令。用负面清单防止范围蔓延。不重接 dispatch、不加类、不动__main__.py、一次只迁一个——每条都在防止 PR 从纯搬运膨胀成架构改造这是让每次评审都平凡得以成立的最后防线。对贡献者而言按 MIGRATION.md 执行一次完整迁移的入口动作只有三步在状态表里挑一个 bespoke 语言、按 Pre-flight 三条检查、然后照六步法推进到uv run pytest -q全绿、以固定格式的单一 commit 收尾。参考文件graphify/extractors/MIGRATION.md — 迁移 Playbook 原文graphify/extract.py — 门面 re-export 迁移块graphify/extractors/init.py —LANGUAGE_EXTRACTORS注册表graphify/extractors/base.py — 共享辅助函数graphify/extractors/terraform.py — 已迁移语言模块样板tests/test_extractors_registry.py — 门面/注册表同一性守卫【免费下载链接】graphifyTurn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, and Gemini CLI: local deterministic AST parsing, every edge explained, no vector store.项目地址: https://gitcode.com/GitHub_Trending/graph/graphify创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表