haider1998/pyvisualizer
GitHub: haider1998/pyvisualizer
基于 AST 静态分析的确定性 Python 调用图生成工具,提供架构可视化、CI 门禁和 AI 上下文精简能力。
Stars: 4 | Forks: 0
# 🗺️ py-code-visualizer
[](https://pypi.org/project/py-code-visualizer/)
[](https://pypi.org/project/py-code-visualizer/)
[](https://github.com/haider1998/PyVisualizer/actions/workflows/ci.yml)
[](https://www.python.org/downloads/)
[](https://opensource.org/licenses/MIT)
[](#architecture)
--base ` | **PR 审查报告**:变更的函数、影响范围、风险标记、聚焦的子图——每个引用均可点击 `file:line` |
| `context --focus ` | **为 AI agent 验证过的 context pack**:任务作用域、预算受限、零猜测边 |
| `context --task ""` | 相同的 pack,由**自然语言任务描述**作为种子(优先匹配命名符号,将词汇匹配作为带标签的提示;`--strategy graph\|text\|hybrid`) |
| `visualize` | 渲染 `html` · `mermaid` · `json` · `c4` · `svg`/`png` |
| `readme` | 在任何 Markdown 文件中注入/更新 Mermaid 图表(幂等)+ 跳转到源码索引 |
| `json` | 输出规范的、可 diff 的图表 JSON |
| `diff base.json head.json` | 可直接用于 PR 的架构变更报告(+ 新循环门禁) |
| `check` | 强制执行层级规则和循环检查——CI 门禁(也支持 `--dead-code`) |
| `impact ` | 影响范围:传递性调用者/被调用者 + 风险说明行(`--format markdown`) |
| `health` | 架构健康评分(A–F),附带 SVG 徽章 |
| `export` | `ARCHITECTURE.json` + `ARCHITECTURE.md` + AGENTS.md 接线配置(`--check` 新鲜度门禁) |
| `init` | 自愿参与的设置——仅生成你选择的自动化(`review`/`readme`/`context`/`gates`) |
**两个任务,一个引擎。** *review* 让大型代码库上的代码审查变成只需几分钟的聚焦过程;*context* 为 AI agent 提供架构中经过验证、**小了 97%** 的切片,而不是整个代码库。参见 [`VISION.md`](VISION.md) 和[用例演练](https://haider1998.github.io/pyvisualizer/use-cases/)。
### MCP server(实时、任务中)
如果你使用 Claude Code 或 Cursor,MCP server 允许 agent 在需要时查询验证过的图表——无需手动复制粘贴:
```
pip install 'py-code-visualizer[mcp]' # Python 3.10+
pyvisualizer-mcp /path/to/project
```
添加到 `.mcp.json` 后,三个工具即可使用:`search_code`、`context_pack` 和 `impact`。该 server 仅在文件更改时重新构建。
完整使用指南(所有 flag、故障排除、输出说明):**[AI_CONTEXT.md](AI_CONTEXT.md)**
## 用例(真实命令、真实输出)
三个端到端的演练,每个都由 [`examples/scenarios/`](examples/scenarios) 中可运行的 fixture 提供支持——每个命令和每一行输出都是可复现的,没有任何虚假演示:
- 🗺️ **[孤立的巨石应用](https://haider1998.github.io/pyvisualizer/use-cases/orphan-monolith.html)** —
通过 `visualize` + `health` + `check --dead-code` 入职未文档化的代码库。
- 🛡️ **[审计截止日期](https://haider1998.github.io/pyvisualizer/use-cases/soc2-audit.html)** —
在调用图级别强制执行层级规则,并生成带日期的 SOC 2 证据。
- 🧨 **[无所畏惧的重构](https://haider1998.github.io/pyvisualizer/use-cases/fearless-refactor.html)** —
`impact` 影响范围分析,随后是导致 PR 在出现新循环时失败的 `diff` 门禁。
查看[完整的用例索引 + 每个命令的配方](https://haider1998.github.io/pyvisualizer/use-cases/)。
**实测数据**(可通过 `python benchmarks/bench.py` → [`docs/benchmarks.json`](docs/benchmarks.json) 复现):
一个 **98,669 行**的项目可在 **~4.9 秒**内映射为完整的调用图(26,658 个函数),**100%** 的边都带有 `file:line`,输出在不同运行之间是**字节完全一致**的,并且生成的 HTML 会发起 **0** 次网络请求。*(macOS arm64,Python 3.14;速度取决于硬件——溯源性、确定性和零网络是结构性的。)*
## 交互式地图
一个单一的自包含 HTML 文件(无 CDN,离线可用):
- **分层抽象** —— 切换 module → class → function 视图
- **点击任意节点** —— 签名、`file:line`、调用者和被调用者(均可点击)
- **⌘K 命令面板**、实时搜索、module 过滤器
- **深度链接** —— URL 编码了选中的节点;将其粘贴到 Slack,你的团队成员就能直接定位到确切的函数
- **导览模式** —— 从检测到的入口点自动生成演示
- **叠加层** —— 循环(红色)、歧义(虚线)和 `--churn` git 热力图
- 缩略图、平移/缩放/拖拽、明亮/深色模式、SVG 导出
## 将图表喂给你的 AI 工具
```
py-code-visualizer export --for-ai ./your_project
```
让 Cursor / Claude 指向经过验证的 `ARCHITECTURE.json`,而不是让模型从原始源码中重新推导结构。**让你的 agent 指向图表,而不是代码库。**
## 准确性保证
- 嵌套的 class、method 和 closure 在被收集时带有正确的限定名称(`pkg.Outer.Inner.method`、`mod.func..inner`)。
- 链式调用(`get_client().fetch()`)、comprehensions 和 lambdas 均被捕获。
- `super()`/继承的调用通过计算出的 MRO 解析(标记为 `inherited`)。
- 参数和变量类型注解驱动 method 解析。
- 对 stdlib/第三方代码的调用**不会产生边** —— 我们绝不臆造。
- 歧义调用会被标记并作为候选项保留;`--strict` 会丢弃它们。
关于 GitHub Actions、GitLab CI 和 pre-commit 设置,请参见 [`docs/integrations.md`](docs/integrations.md)。
## 配置
```
[tool.pyvisualizer]
exclude = ["tests", "migrations"]
max_nodes = 120
target = "README.md"
detail = "module" # module | class | function
```
## 路线图
- ⏳ **时光旅行** —— 在各个版本中穿梭浏览你架构的演进
- 🔁 **Watch 模式** —— 在你重构时实时重载地图
- ✅ ~~**MCP server**~~ —— 已发布:`pyvisualizer-mcp`(`search_code`、`context_pack`、`impact`)
## 架构
下面的图表由 PyVisualizer 自身生成,并由 CI 保持同步。
*120 个函数 · 214 次调用 · 健康 F (46/100) — 详情:module*
```
flowchart LR
g0["bench"]
g1["genproject"]
g2["main"]
g3["models"]
g4["services"]
g5["urls"]
g6["repos"]
g7["services"]
g8["evaluate"]
g9["features"]
g10["ingest"]
g11["pipeline"]
g12["train"]
g13["cli"]
g14["pipeline"]
g15["transforms"]
g16["core"]
g17["service"]
g18["billing"]
g19["api"]
g20["changes"]
g21["cli"]
g22["config"]
g23["context"]
g24["analyzer"]
g25["graph"]
g26["diff"]
g27["export"]
g28["gates"]
g29["impact"]
g30["inject"]
g31["mcp_server"]
g32["metrics"]
g33["overlays"]
g34["retrieval"]
g35["review"]
g36["c4"]
g37["json_graph"]
g38["setup_init"]
g39["file_discovery"]
g40["d3"]
g41["html"]
g42["mermaid"]
g0 --> g1
g0 --> g19
g0 --> g37
g0 --> g41
g1 --> g6
g4 --> g3
g7 -.-> g4
g7 --> g6
g11 --> g8
g11 --> g9
g11 --> g10
g11 --> g12
g13 --> g14
g14 -.-> g7
g14 --> g15
g15 -.-> g4
g17 --> g16
g19 --> g25
g19 --> g31
g19 --> g39
g20 --> g31
g20 --> g33
g21 --> g14
g21 --> g19
g21 --> g22
g21 --> g23
g21 --> g26
g21 --> g27
g21 --> g28
g21 --> g29
g21 --> g30
g21 --> g31
g21 --> g32
g21 --> g33
g21 --> g35
g21 --> g36
g21 --> g37
g21 --> g40
g21 --> g42
g22 --> g31
g23 --> g4
g23 --> g20
g23 --> g28
g23 --> g29
g23 --> g31
g23 --> g32
g23 --> g33
g23 --> g34
g24 --> g31
g25 --> g4
g25 --> g31
g26 --> g4
g26 --> g31
g26 --> g32
g27 --> g28
g27 --> g30
g27 --> g31
g27 --> g32
g27 --> g37
g28 --> g31
g29 --> g20
g31 --> g19
g31 --> g23
g31 --> g29
g31 --> g34
g32 --> g31
g32 --> g34
g33 --> g31
g34 --> g4
g34 --> g31
g35 --> g20
g35 --> g28
g35 --> g31
g35 --> g32
g35 --> g42
g36 --> g42
g37 --> g31
g38 --> g19
g38 --> g30
g38 --> g31
g38 --> g32
g38 --> g42
g39 --> g4
g39 --> g6
g40 --> g41
g41 --> g37
g42 --> g31
```
🔒 确定性、经过 AST 验证 —— 未执行任何代码。由 [py-code-visualizer](https://github.com/haider1998/PyVisualizer) 生成。
## 贡献
请参见 [CONTRIBUTING.md](CONTRIBUTING.md)。PyVisualizer 采用 MIT 许可协议。
## 作者
**Syed Mohd Haider Rizvi**
[作品集](https://haider1998.github.io/) · [LinkedIn](https://www.linkedin.com/in/s-m-h-rizvi-0a40441ab/) · [GitHub](https://github.com/haider1998)
⚡ 在浏览器中实时体验 →
· 拖入 .py 文件,查看图表,不上传任何内容
📍 跳转到源码(120 个函数)
- [`benchmarks.bench._bench_target`](benchmarks/bench.py#L177) - [`benchmarks.bench._determinism_proof`](benchmarks/bench.py#L113) - [`benchmarks.bench._html_network_proof`](benchmarks/bench.py#L132) - [`benchmarks.bench.run`](benchmarks/bench.py#L221) - [`benchmarks.genproject._gen_module`](_URL_27/>) - [`benchmarks.genproject.generate`](benchmarks/genproject.py#L135) - [`examples.sample_project.main.main`](examples/sample_project/main.py#L7) - [`examples.scenarios.django_shop.models.Cart.for_user`](examples/scenarios/django_shop/models.py#L12) - [`examples.scenarios.django_shop.services.CartService.add`](examples/scenarios/django_shop/services.py#L11) - [`examples.scenarios.django_shop.services.OrderService.checkout`](examples/scenarios/django_shop/services.py#L22) - [`examples.scenarios.django_shop.services.OrderService.get`](examples/scenarios/django_shop/services.py#L29) - [`examples.scenarios.django_shop.urls.dispatch`](examples/scenarios/django_shop/urls.py#L7) - [`examples.scenarios.fastapi_svc.repos.OrderRepo.insert`](examples/scenarios/fastapi_svc/repos.py#L8) - [`examples.scenarios.fastapi_svc.services.OrderFlow.fetch`](examples/scenarios/fastapi_svc/services.py#L22) - [`examples.scenarios.fastapi_svc.services.OrderFlow.place`](examples/scenarios/fastapi_svc/services.py#L16) - [`examples.scenarios.ml_pipeline.evaluate.evaluate_model`](examples/scenarios/ml_pipeline/evaluate.py#L4) - [`examples.scenarios.ml_pipeline.features.build_features`](examples/scenarios/ml_pipeline/features.py#L4) - [`examples.scenarios.ml_pipeline.ingest.load_dataset`](examples/scenarios/ml_pipeline/ingest.py#L4) - [`examples.scenarios.ml_pipeline.pipeline.run`](examples/scenarios/ml_pipeline/pipeline.py#L9) - [`examples.scenarios.ml_pipeline.train.train_model`](examples/scenarios/ml_pipeline/train.py#L4) - [`examples.scenarios.orphan_monolith.app.cli.run`](examples/scenarios/orphan_monolith/app/cli.py#L7) - [`examples.scenarios.orphan_monolith.app.pipeline.ReportPipeline.build`](examples/scenarios/orphan_monolith/app/pipeline.py#L11) - [`examples.scenarios.orphan_monolith.app.pipeline.ReportPipeline.render`](examples/scenarios/orphan_monolith/app/pipeline.py#L16) - [`examples.scenarios.orphan_monolith.app.transforms.summarize`](examples/scenarios/orphan_monolith/app/transforms.py#L11) - [`examples.scenarios.refactor.after.core.persist`](examples/scenarios/refactor/after/core.py#L7) - [`examples.scenarios.refactor.after.service.cancel_order`](examples/scenarios/refactor/after/service.py#L11) - [`examples.scenarios.refactor.after.service.place_order`](examples/scenarios/refactor/after/service.py#L7) - [`examples.scenarios.soc2_audit.domain.billing.BillingService.charge`](examples/scenarios/soc2_audit/domain/billing.py#L12) - [`pyvisualizer.api.build_graph`](pyvisualizer/api.py#L47) - [`pyvisualizer.changes.Linker.ref`](pyvisualizer/changes.py#L202) - [`pyvisualizer.changes.changed_lines_from_git`](pyvisualizer/changes.py#L62) - [`pyvisualizer.changes.map_lines_to_functions`](pyvisualizer/changes.py#L121) - [`pyvisualizer.changes.repo_web_url`](pyvisualizer/changes.py#L149) - [`pyvisualizer.changes.resolve_base_ref`](pyvisualizer/changes.py#L46) - [`pyvisualizer.cli._build`](pyvisualizer/cli.py#L283) - [`pyvisualizer.cli._render_graphviz`](pyvisualizer/cli.py#L368) - [`pyvisualizer.cli.cmd_check`](pyvisualizer/cli.py#L588) - [`pyvisualizer.cli.cmd_context`](pyvisualizer/cli.py#L706) - [`pyvisualizer.cli.cmd_diff`](pyvisualizer/cli.py#L550) - [`pyvisualizer.cli.cmd_export`](pyvisualizer/cli.py#L647) - [`pyvisualizer.cli.cmd_health`](pyvisualizer/cli.py#L623) - [`pyvisualizer.cli.cmd_impact`](pyvisualizer/cli.py#L669) - [`pyvisualizer.cli.cmd_readme`](pyvisualizer/cli.py#L458) - [`pyvisualizer.cli.cmd_review`](pyvisualizer/cli.py#L681) - [`pyvisualizer.cli.cmd_visualize`](pyvisualizer/cli.py#L302) - [`pyvisualizer.cli.main`](pyvisualizer/cli.py#L264) - [`pyvisualizer.config.load_config`](pyvisualizer/config.py#L102) - [`pyvisualizer.context._est_tokens`](pyvisualizer/context.py#L135) - [`pyvisualizer.context._node_line`](pyvisualizer/context.py#L110) - [`pyvisualizer.context._resolve_focus`](pyvisualizer/context.py#L83) - [`pyvisualizer.context._select_nodes`](pyvisualizer/context.py#L246) - [`pyvisualizer.context._select_text`](pyvisualizer/context.py#L297) - [`pyvisualizer.context._upgrade_bodies`](pyvisualizer/context.py#L338) - [`pyvisualizer.context.build_context_pack`](pyvisualizer/context.py#L387) - [`pyvisualizer.core.analyzer.DefinitionCollector._visit_function`](pyvisualizer/core/analyzer.py#L360) - [`pyvisualizer.core.analyzer.DefinitionCollector.visit_ClassDef`](pyvisualizer/core/analyzer.py#L327) - [`pyvisualizer.core.analyzer.ModuleAnalyzer._decorator_names`](pyvisualizer/core/analyzer.py#L158) - [`pyvisualizer.core.analyzer.ModuleAnalyzer._extract_attribute_chain`](pyvisualizer/core/analyzer.py#L189) - [`pyvisualizer.core.analyzer.ModuleAnalyzer._process_annotation`](pyvisualizer/core/analyzer.py#L259) - [`pyvisualizer.core.analyzer.ModuleAnalyzer._process_decorator`](pyvisualizer/core/analyzer.py#L206) - [`pyvisualizer.core.graph.FunctionCallVisitor._extract_attribute_chain`](pyvisualizer/core/graph.py#L421) - [`pyvisualizer.core.graph.FunctionCallVisitor._extract_call_target`](pyvisualizer/core/graph.py#L407) - [`pyvisualizer.core.graph.FunctionCallVisitor._find_method_in_hierarchy`](pyvisualizer/core/graph.py#L329) - [`pyvisualizer.core.graph.FunctionCallVisitor._resolve_call`](pyvisualizer/core/graph.py#L201) - [`pyvisualizer.core.graph.FunctionCallVisitor._seed_param_types`](pyvisualizer/core/graph.py#L121) - [`pyvisualizer.core.graph.FunctionCallVisitor._visit_function_common`](pyvisualizer/core/graph.py#L96) - [`pyvisualizer.core.graph.build_call_graph`](pyvisualizer/core/graph.py#L475) - [`pyvisualizer.diff.diff_graphs`](pyvisualizer/diff.py#L84) - [`pyvisualizer.diff.render_change_mermaid`](pyvisualizer/diff.py#L131) - [`pyvisualizer.diff.render_markdown`](pyvisualizer/diff.py#L170) - [`pyvisualizer.export._agents_md_plan`](pyvisualizer/export.py#L154) - [`pyvisualizer.export._json_content`](pyvisualizer/export.py#L113) - [`pyvisualizer.export.build_ai_markdown`](pyvisualizer/export.py#L32) - [`pyvisualizer.export.export_for_ai`](pyvisualizer/export.py#L173) - [`pyvisualizer.export.export_would_change`](pyvisualizer/export.py#L199) - [`pyvisualizer.gates.check_layer_rules`](pyvisualizer/gates.py#L53) - [`pyvisualizer.gates.find_cycles`](pyvisualizer/gates.py#L83) - [`pyvisualizer.impact.analyze_impact`](pyvisualizer/impact.py#L46) - [`pyvisualizer.impact.render_markdown`](pyvisualizer/impact.py#L96) - [`pyvisualizer.impact.resolve_target`](pyvisualizer/impact.py#L32) - [`pyvisualizer.inject.inject`](pyvisualizer/inject.py#L84) - [`pyvisualizer.inject.inject_block`](pyvisualizer/inject.py#L51) - [`pyvisualizer.inject.update_file`](pyvisualizer/inject.py#L102) - [`pyvisualizer.mcp_server.ProjectSession.get`](pyvisualizer/mcp_server.py#L54) - [`pyvisualizer.mcp_server.tool_context_pack`](pyvisualizer/mcp_server.py#L82) - [`pyvisualizer.mcp_server.tool_impact`](pyvisualizer/mcp_server.py#L107) - [`pyvisualizer.metrics._is_entry_point`](pyvisualizer/metrics.py#L75) - [`pyvisualizer.metrics.compute_health`](pyvisualizer/metrics.py#L88) - [`pyvisualizer.metrics.find_dead_code`](pyvisualizer/metrics.py#L153) - [`pyvisualizer.overlays._git`](pyvisualizer/overlays.py#L23) - [`pyvisualizer.overlays._toplevel`](pyvisualizer/overlays.py#L33) - [`pyvisualizer.overlays.apply_churn`](pyvisualizer/overlays.py#L67) - [`pyvisualizer.overlays.git_churn`](pyvisualizer/overlays.py#L41) - [`pyvisualizer.retrieval.BM25Index.rank`](pyvisualizer/retrieval.py#L120) - [`pyvisualizer.retrieval.BM25Index.search`](pyvisualizer/retrieval.py#L137) - [`pyvisualizer.retrieval.build_bm25`](pyvisualizer/retrieval.py#L142) - [`pyvisualizer.retrieval.derive_seeds`](pyvisualizer/retrieval.py#L189) - [`pyvisualizer.retrieval.function_source`](pyvisualizer/retrieval.py#L75) - [`pyvisualizer.retrieval.rank_seeds`](pyvisualizer/retrieval.py#L219) - [`pyvisualizer.retrieval.tokenize`](pyvisualizer/retrieval.py#L64) - [`pyvisualizer.review._risk_lines`](pyvisualizer/review.py#L183) - [`pyvisualizer.review.analyze_review`](pyvisualizer/review.py#L50) - [`pyvisualizer.review.render_markdown`](pyvisualizer/review.py#L112) - [`pyvisualizer.review.render_text`](pyvisualizer/review.py#L198) - [`pyvisualizer.serializers.c4.generate_c4_dsl`](pyvisualizer/serializers/c4.py#L27) - [`pyvisualizer.serializers.json_graph.graph_to_dict`](pyvisualizer/serializers/json_graph.py#L65) - [`pyvisualizer.serializers.json_graph.graph_to_json`](pyvisualizer/serializers/json_graph.py#L162) - [`pyvisualizer.setup_init._action_readme`](pyvisualizer/setup_init.py#L232) - [`pyvisualizer.setup_init._record_features`](pyvisualizer/setup_init.py#L205) - [`pyvisualizer.setup_init.run_init`](pyvisualizer/setup_init.py#L305) - [`pyvisualizer.utils.file_discovery.analyze_project`](pyvisualizer/utils/file_discovery.py#L151) - [`pyvisualizer.utils.file_discovery.find_project_python_files`](pyvisualizer/utils/file_discovery.py#L38) - [`pyvisualizer.utils.file_discovery.get_module_name`](pyvisualizer/utils/file_discovery.py#L96) - [`pyvisualizer.visualizers.d3.generate_d3_visualization`](pyvisualizer/visualizers/d3.py#L18) - [`pyvisualizer.visualizers.html.generate_html_visualization`](pyvisualizer/visualizers/html.py#L29) - [`pyvisualizer.visualizers.mermaid._categorize_methods`](pyvisualizer/visualizers/mermaid.py#L301) - [`pyvisualizer.visualizers.mermaid._rollup`](pyvisualizer/visualizers/mermaid.py#L39) - [`pyvisualizer.visualizers.mermaid.create_interactive_html`](pyvisualizer/visualizers/mermaid.py#L336) - [`pyvisualizer.visualizers.mermaid.generate_github_mermaid`](pyvisualizer/visualizers/mermaid.py#L82) - [`pyvisualizer.visualizers.mermaid.generate_styled_mermaid`](pyvisualizer/visualizers/mermaid.py#L114)标签:Python, 云安全监控, 代码可视化, 多模态安全, 无后门, 自动化payload嵌入, 调用图, 逆向工具, 静态分析