逆天的python项目分析工具【分析langchain】
共 8681字,需浏览 18分钟
·
2023-09-13 02:56
作者:aC大
原文转载:https://zhuanlan.zhihu.com/p/639056301
前言
之前的文章和视频对autoGPT的源码进行了剖析,有网友就问到,在分析autoGPT源码的时候画的模块图是用什么工具画的,有的甚至直接私信我直接要了模块关系图。应广大网友的要求,这次就分享一下这个逆天的python项目分析工具。
python项目分析的工具有哪些
有个网友对python 程序分析工具进行了比较系统的调研,里边有很多的工具及使用的场景和优缺点对比,具体对比如下:
工具 | 描述 | 开发语言 | 是否开源 |
---|---|---|---|
PySonar2 | 语义索引器,用于批量处理大型代码库。牺牲实时索引能力,使用过程间分析来推断变量、参数和函数的类型。目前作为大型代码索引服务的基础引擎,Sourcegraph 中也有使用该工具。 | java | 是 |
Sourcegraph | 代码搜索和导航引擎,帮助阅读和理解大型项目的代码。目前支持 Go、Java、Python 等多种编程语言,能够以浏览器插件的形式提供在线代码阅读的功能。 | Go、TypeScript | 是 |
coala | 使用配置文件检测和修复代码,可自定义规则和标准检查代码质量,扩展性强。支持 Python、Java、C/C++、JavaScript 等多种编程语言,支持作为编辑器插件使用。 | python | 是 |
vprof | 性能分析工具,为 Python 程序的运行时间、内存使用情况等提供交互式可视化,不适用于大型代码的分析。 | Python、JavaScript | 是 |
Code2Flow | 为动态语言生成生成调用图(Call Graph),目前支持 Python、JavaScript、Ruby、PHP。基础流程:AST -> 函数定义 -> 函数调用点 -> 连接点 ,主要是提供一个粗略的概览。缺点是没有定义的函数、不同命名空间相同名字的函数将被跳过等,因此并不适用于大型代码的分析。 | Python、JavaScript | 是 |
pydeps | Python 模块依赖可视化,导出 .svg 或 .png 文件。只考虑导入的文件(模块必须安装),在 Python 字节码中查找导入操作码,同时支持外部模块的分析。 | Python | 是 |
pycallgraph* | 为 Python 应用生成调用图(Call Graph),包括函数的调用次数、执行时间等信息,提供一定程度的性能分析功能。同时支持过滤函数,避免生成的图太大无法分析,默认导出 .png 文件。 | Python | 是 |
undebt* | 执行大规模自动化代码重构,根据自定义模式对代码进行查找和替换,适用于任何语言。 | Python | 是 |
pyt* | 基于理论基础的 Python Web 应用静态分析(控制流图、定点、数据流分析),能够检测 SSRF、SQL 注入、XSS、目录遍历等攻击,支持自定义源点和汇点、传播污点的函数。目前已停更,作者建议使用 Pyre 。 | Python | 是 |
Pyre (Facebook) | 性能类型检查器,集成静态分析工具 Pysa(污点分析)Pysa 通过跟踪数据流实现安全检查,依赖类型注释,能够高速处理大型代码。 | OCaml、Python | 是 |
Bandit | 构建 AST 并使用插件检查安全问题,最初在 OpenStack 安全项目中开发,后来被重新定位到 PyCQA。主要用于扫描危险函数,支持自定义漏洞测试和扩展插件。 | Python | 是 |
Sourcetrail | 跨平台的源代码浏览器,支持 C/C++、Java、Python,提供搜索、代码、图形三个交互式视图。亮点是给出了源代码的结构,但只有符号名称。 | C++、Java | 是 |
radon | 计算源代码的度量(metrics),包括 McCabe、Halstead、可维护性索引三种度量指标,支持在 coala 中使用。主要用于评估代码的复杂度。 | Python | 是 |
xenon | 基于 radon 的监测工具,检查代码复杂性。可应用于 CI/CD,在每次提交代码时运行,根据自定义代码复杂性阈值返回成功或失败。 | Python | 是 |
jedi | Python 静态分析工具,侧重于自动补全和跳转,通常作为编辑器的插件使用。 | Python | 是 |
mypy | 静态类型检查器(PEP 484),能够对 Python 程序中的类型注释执行静态检查。 | Python | 是 |
pyright (Microsoft) | 可用于大型 Python 源代码库的快速类型检查器,使用内置的类型存根进行类型推断,可以在监视模式下运行,支持增量更新。 | TypeScript、Python | 是 |
pytype (Google) | 静态分析器,无需类型注释就能检查和推断 Python 代码的类型,也利用了类型存根(pyi 文件)。 | Python | 是 |
Vulture | 查找 Python 程序中未使用的代码(死代码),存在漏报,并且隐式调用可能会被误报。支持设置最小置信度、白名单等功能,可以对单个文件或目录的 py 文件执行分析。 | Python | 是 |
PyCG | 使用静态分析为 Python 代码生成调用图,支持高阶函数、类继承、导入的模块、嵌套定义。详见论文 ICSE 2021 paper | Python | 是 |
Wily | 复杂性检查 | Python | 是 |
McCabe | 复杂性检查 | Python | 是 |
it | 代码检查工具/框架 | Python | 是 |
CodeQL | 代码分析引擎 | 未知 | 否 |
Fortify SCA | 静态代码审计工具 | 未知 | 否 |
Pylance (Microsoft) | 基于 pyright 的 Python 代码静态分析工具 | 未知 | 否 |
DeepSource | 支持多种语言的静态分析,个人免费 | 未知 | 否 |
Codacy | 能够通过静态代码分析获得关于安全问题、代码覆盖率、代码重复率、代码复杂性的信息 | 未知 | 否 |
上述包含的工具较多,开源的都可以在github上找到,每个工具适用的场景也不同,具体还是要根据个人的需求选择,我是用来分析autoGPT的模块间调用关系,对比分析发现,pydeps比较适合我。
pydeps的使用(langchain项目分析)
pydeps在github上已经开源,参数如下:
usage: pydeps [-h] [--debug] [--config FILE] [--no-config] [--version] [-L LOG] [--find-package] [-v] [-o file] [-T FORMAT] [--display PROGRAM] [--noshow] [--show-deps]
[--show-raw-deps] [--deps-output DEPS_OUT] [--show-dot] [--dot-output DOT_OUT] [--nodot] [--no-output] [--show-cycles] [--debug-mf INT] [--noise-level INT]
[--max-bacon INT] [--max-module-depth INT] [--pylib] [--pylib-all] [--include-missing] [-x PATTERN [PATTERN ...]] [-xx MODULE [MODULE ...]]
[--only MODULE_PATH [MODULE_PATH ...]] [--externals] [--reverse] [--rankdir {TB,BT,LR,RL}] [--cluster] [--min-cluster-size INT] [--max-cluster-size INT]
[--keep-target-cluster] [--collapse-target-cluster] [--rmprefix PREFIX [PREFIX ...]] [--start-color INT]
fname
从官网的参数介绍来看,感觉跟没介绍一样,只能通过实际的摸索分析参数的作用(边猜边实验)。该工具的安装比较简单可自行参考官网说明。下面以分析langchain工具为例,结合实际的应用需求,介绍几个比较重要的参数。前期工作:
1. 使用conda,创建pydeps的环境
2. 在该环境下安装pydeps: pip install pydeps
3. 激活该环境:conda activate pydeps
4. 下载langchain项目,cd到项目目录下: cd xx/langchain
5. 在langchain目录下,创建.pydeps文件 (为了演示的方便,将参数配置在.pydeps), 也可以不使用配置文件,直接在命令行输入对应的参数
5. 开始后续的工作(以windows环境为例)
.pydeps文件按.ini格式解析,默认值如下:
[pydeps]
max_bacon = 2 # 排除距离超过 n 跳的节点(默认=2, 0 -> 无限)
verbose = 0 # 信息输出
pylib = False # 是否包含 python std lib 模块
exclude = # 排除模块(不在途中话出)
os
re
sys
collections
__future__
langchain的模块调用关系
使用默认命令:
(pydeps) E:\py_workspace\langchain>pydeps langchain/__init__.py -o langchain_init.pngT png -
得到的是14M,无法上传原图,这里放一个缩小的图,主要是看至的模块关系:
这个图是无法看的,增加max_module_depth=2, 控制从入口模块到其他模块的长度
[pydeps]
max_module_depth = 2
得到的图为,控制了入口模块到其他模块的调用长度,这个图就比较清晰了,但是还发现 包含了requtest、jinjia2和urllib3等模块,这些模块我们并不需要分析,:
于是在配置文件中增加exclude信息:
[pydeps]
max_bacon = 2
verbose = 0
pylib = False
max_module_depth = 2
exclude =
os
re
sys
collections
__future__
requests # 新增
urllib3 # 新增
jinja2 # 新增
得到的图,这下出来的模块关系图就是我们需要的了,如果对颜色不喜欢,可以增加start_color=0的参数,值从0-360, 默认是0:
其他重要参数的介绍
参数 | 描述 | 补充说明 |
---|---|---|
--config FILE | 指定配置文件的路径,演示是使用的是当前目录的.pydeps | |
--no-config | 禁用配置文件, 就是说即使当前目录有.pydeps也不会读取,只使用命令行的参数 | |
-o file | 输出文件 | |
-T | 输出的格式(svg或png) | |
--display PROGRAM | 展示svg和png的程序,一般不指定 | |
--max-bacon INT | 排除距离超过 n 跳的节点(默认=2, 0 -> 无限) | 模块将from xx import a, 之间的模块层数,设置合适的值便于分析。 |
--pylib | 是否包含 python std lib 模块 | |
--pylib-all | 是否包含 python 所有 std lib 模块(包括 C 模块) | |
--include-missing | 包含未安装的模块(或在 sys.path 上找不到) | |
--only MODULE_PATH | 只包含以 MODULE_PATH 开头的模块,可以提供多个路径 | 这个在分析某个模块的时候很有用 |
--rankdir | 设置图形的方向,合法值为TB(默认,导入模块在导入模块之上)、BT(与TB相反的方向)、LR(从左到右)和RL(从右到左) | 上面显示的图就是TB的,上下结构,读者可以改成LR试试看 |
--cluster | 将外部依赖项绘制为单独的集群 | 将max-bacon以外的节点绘制成一个icon |
--min-cluster-size INT | 集群之前依赖项必须具有的最小节点数(默认=0) | 在max-bacon以外的节点,如果子图节点小于该值,则不绘制为一个icon(全部展示) |
--max-cluster-size INT | 在集群折叠为单个节点之前依赖项可以拥有的最大节点数(默认=0) | 将小于该值的绘制为一个子图,大于该值的绘制为一个icon |
-x PATTERN, --exclude PATTERN | 要跳过的输入文件(例如 foo.*),可以提供多种模式 | |
--exclude-exact MODULE | (简写 -xx MODULE)与 --exclude 相同,但需要完全匹配。-xx foo.bar 将排除 foo.bar,但不排除 foo.bar.blob | |
--rmprefix PREFIX | 从节点的显示名称中删除 PREFIX(可以提供多个前缀) | 这个在图美观的时候能用到,比如模块http://langchain.chatModel.xxx太长,可以指定将langchain前缀移除,只显示http://chatModel.xxx |
配置较为完整的配置如下:
[pydeps]
verbose = 0
pylib = False # 不显示python lib
max_module_depth = 2 # 显示最长链路
min_cluster_size = 2
max_cluster_size = 2
start_color= 180 # 画图的颜色,指定其实颜色,可选值0-360
exclude_exact = # 去掉指定的模块
langchain.utilities
langchain.utils
exclude = # 去掉以os,re等开头的模块
os
re
sys
collections
__future__
requests
urllib3
jinja2
上述配置的图为:
总结
pydeps的工具可以将整个项目的模块调用关系展示出来,甚至将调用的外部模块也可以展示出来,但是这样的话会让整个图很大,不利于将重点放在当前项目,因为可以通过参数,将模块的粒度控制在一个比较合理的范围便于解读。上文通过分析langchain的入口模块,展示了从最复杂的14M(其实还可以画的更复杂)到最后只有三个模块的过程,配合参数的设定突出要分析的模块更加有利于项目的分析。也为下一步源码剖析提供更清晰的指导。