利用python ast包,绘制python代码的调用关系图(可分析互相调用的多个py文件)

2023-05-16

我的目的是辅助代码阅读(也方便写文档),因此不需要太详细的信息,只需要看用户定义的函数的函数调用关系。

文章目录

  • 1.开源项目staticfg的安装和直接使用
  • 2.绘制python的简单调用关系图(支持多文件)
    • 2.1 python ast前置知识(可酌情跳过)
    • 2.2 代码实现
    • 2.3 代码的使用
  • 3.附录:dot语言中结点的命名规则

1.开源项目staticfg的安装和直接使用

一开始我试图寻找现成的轮子,在github上得偿所愿,https://github.com/coetaur0/staticfg,需要详细函数调用图的朋友可以一试,我总结了staticfg安装使用中的问题:
我的电脑上装了anaconda和pycharm,pycharm的解释器可以设置成anaconda中的python,这样就可以在pycharm中使用anaconda装的包。
在anaconda navigator中打开cmd.exe,可以直接使用pip命令来安装staticfg。pip会先安装astor库,如果超时的话就等它耗尽超时次数,然后重试。
pip成功安装之后,尝试readme中的demo,会报错,大意是说要将graphviz的可执行文件添加到环境变量。需要到http://www.graphviz.org/download/去下载,安装时可以选择自动添加到环境变量。然后readme中的斐波那契demo就可以正常分析了。
如果分析的py文件中有中文注释,会报unicode decode error,说gbk无法解码某一个byte。据说是因为python默认使用gbk。其中一个解决方法是在代码开头增加如下代码:

import _locale
_locale._getdefaultlocale = (lambda *args: ['zh_CN', 'utf8'])

不用修改任何代码的另一个临时解决方法是,python -X utf8 build_cfg.py [input] [output]。其中,build_cfg.py在clone下来的example目录下。
之后还可能会有另一个错误,在builder.py里,大意是+=的运算数不能是str和nonetype。这个问题貌似是输入文件中有strip函数引起的。如果可以顺利执行的话,生成的结果是一字排开的,如果函数很多,观感很差。函数内的很多语句都会被放在图里,真要读图感觉还没直接看代码快。

2.绘制python的简单调用关系图(支持多文件)

首先,我总结一下staticfg中不尽人意的地方:

  1. 代码中有中文注释时,可能会报错。(上面提到了解决方法,我在自己的实现中已经内置了,不会有问题。)
  2. 可能会出现上文提到的nonetype错误。(我改为了输出warnings,避免报错中止。)
  3. 生成的结果中,有一些一字排开的孤立结点(staticfg中对graphviz的使用比较粗糙,解决见下文)
  4. 结果中有大量函数内语句(比如return语句)和系统内置函数(我不需要这些信息)

为了解决这些问题,我借鉴了staticfg,做了自己的实现。

2.1 python ast前置知识(可酌情跳过)

了解前置知识之后,大致可以读懂staticfg的源码,并进行自己的改写。
python官方文档推荐的 https://greentreesnakes.readthedocs.io/
但这个我上不了,所以只好找了一些国内博客,也能解决问题:https://blog.csdn.net/ma89481508/article/details/56017697

print type(node).__name__

注意上面这篇博客中的这句话,可以在没有文档的情况下获取你想知道的node类型(比如你想知道a=10属于哪种类型,就输入一个只有a=10的py文件),从而知道想要override的官方函数的名字。(更笨的办法是慢慢翻ide的代码提示)

此外,这篇博客也很有帮助 https://www.cnblogs.com/yssjun/p/10069199.html

2.2 代码实现

import os
import ast
import sys
from graphviz import Digraph
import _locale
_locale._getdefaultlocale = (lambda *args: ['zh_CN', 'utf8'])


def simplecfg(*args):
    visitor = CodeVisitor()
    for infile in args[0]:
        f = open(infile, "r")
        r_node = ast.parse(f.read())
        f.close()
        visitor.filename = os.path.basename(infile).split('.')[0]
        visitor.visit(r_node)

    fpos = {}#fpos存放函数basename所在的py文件
    for func in visitor.userfunc:
        fr = func.split('.')[0]
        bk = func.split('.')[-1]
        fpos[bk] = fr

    dest = {}#dest存放每个userfunc下调用了哪些userfunc
    for line in visitor.info:
        if line.startswith('User Function Name'):
            defnow = line.split(':')[1]
            dest[defnow] = []
            continue
        for func in visitor.userfunc:
            basename = func.split('.')[-1]
            line_tail = line.split(':')[-1]
            line_tail = line_tail.split('.')[-1]
            if basename == line_tail:
                dest[defnow].append(basename)
                break

    dot = Digraph(comment='The Round Table')

    ctr = 0
    alias = {}
    #在dot语法中,结点有自己的名字,这个名字跟结点在图片上显示的函数名字不同。alias存储两者的映射。

    for func in visitor.userfunc:
        ctr += 1
        alias[func] = 'A'+str(ctr)#跟dot语法的命名规则有关,也可以用其它命名,不必纠结
        dot.node(alias[func], func)
    for key in dest.keys():
        for dst in dest[key]:
            fullname = fpos[dst] + '.' + dst
            dot.edge(alias[key], alias[fullname])

    dot.render('test-output/round-table.gv')
    # print(ast.dump(r_node))


class CodeVisitor(ast.NodeVisitor):
    userfunc = []
    info = []
    filename = ''
    def generic_visit(self, node):
        # print(type(node).__name__)
        ast.NodeVisitor.generic_visit(self, node)
    def visit_FunctionDef(self, node):
        # print('User Function Name:%s' % node.name)
        self.info.append('User Function Name:'+self.filename+'.'+node.name)
        self.userfunc.append(self.filename+'.'+node.name)
        ast.NodeVisitor.generic_visit(self, node)
    def visit_Call(self, node):
        # print(node._fields)
        def recur_visit(node):
            if type(node) == ast.Name:
                return node.id
            elif type(node) == ast.Attribute:
                # Recursion on series of calls to attributes.
                # print(node.attr)
                func_name = recur_visit(node.value)
                if type(node.attr) == str and type(func_name) == str:
                    func_name += '.' + node.attr
                # else:
                    # print('attention!!!', type(node.attr), type(func_name))
                return func_name
            elif type(node) == ast.Str:
                return node.s
            elif type(node) == ast.Subscript:
                return node.value.id

        func = node.func
        # print(type(func), func._fields)
        func_name = recur_visit(func)
        if(type(func_name)==str):
            self.info.append('\tUser function Call:'+self.filename+'.'+func_name)
        ast.NodeVisitor.generic_visit(self, node)


simplecfg(sys.argv[1:])

2.3 代码的使用

在使用上面的代码之前,需要正确安装graphviz,可参考第一部分。
如果用pycharm的话,可以在下方的terminal中用命令行运行上面的py文件,命令如下:
python 上面的py文件.py 输入文件1.py 输入文件2.py 输入文件n.py
执行过后,在上面的py文件的存储位置找test-output目录,可以看见生成了gv文件和pdf文件。
可以先直接打开pdf文件看一下,但我估计你不会满意,因为作的图太宽,根本看不清。
在stackoverflow上获得了如下解决方法:

unflatten -c 5 round-table.gv | dot -Gratio="fill" -Gsize="20,5" -Tpdf -o round-table.gv.pdf

在cmd或者powershell中先进入gv文件的目录,然后执行这条命令(前提是graphviz已经正确安装,参考第一部分),生成的pdf即为最终结果。

unflatten是graphviz/bin下的工具,作用就是让图更加紧凑。命令的解释:
-c会把孤立结点摆成一列,5为最大列长;
-Gratio设为fill后,对-Gsize的修改才有意义,图像会填充你指定大小的画布;
-T指定输出格式,-o指定输出文件。

结果示意:
在这里插入图片描述
虽然内容还是很多、图片还是很宽,但这是因为分析了5个文件,代码量将近3000行。
这个代码我只用过一次,对graphviz的使用也完全是照搬,欢迎评论指正。

3.附录:dot语言中结点的命名规则

graphviz的结点可以超过26个,只要能正确命名就可以,不一定要用单个的大写字母。
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

利用python ast包,绘制python代码的调用关系图(可分析互相调用的多个py文件) 的相关文章

  • 无人机从零到一(组装、校准到起飞)

    一 组装 xff08 螺旋桨可以最后装 xff09 机械结构配置 xff1a 名称型号飞控Pixhawk 2 4 8 xff08 带安全开关 xff0c 蜂鸣器 xff0c 还买了减震板 xff09 螺旋桨8045 ABS xff08 33
  • C++11的多线程线程参数传递,指针与引用

    void foo std this thread sleep for std chrono seconds 1 int main std thread t std cout lt lt 34 before starting joinable
  • 学习(二):I2C、SPI、UART、CAN的特点与区别

    官网教程链接 xff1a https ardupilot org dev docs code overview sensor drivers html I2C 一个主机 xff0c 允许多个从机一种相对简单的通信协议 xff0c 适合短距离
  • C++ Qt 两字节相加(和)的校验位

    函数名称 xff1a checkSum 函数功能 xff1a 校验位 帧头 43 数据段按2字节累加和 若数据段个数为奇数 xff0c 则累加和时在数据段后方补1个字节0x00 函数参数 xff1a QByteArray 帧头 43 数据段
  • 考研复习C++基础问答题(六)

    1 C 43 43 中存储类有哪些 xff1f 存储类定义 C 43 43 程序中变量 函数的范围 xff08 可见性 xff09 和生命周期 这些说明符放置在它们所修饰的类型之前 下面列出 C 43 43 程序中可用的存储类 xff1a
  • STM32CubeMX编辑规范(02)

    STM32CubeMX编辑规范 目录 STM32CubeMX编辑规范前言一 代码编写范围二 项目结构三 单独外设配置四 代码生成设置五 高级设置 前言 采用STM32CubeMX的编辑规范可以更好的在不同的芯片上移植 xff0c 修改外设可
  • 机器人局部避障的动态窗口法DWA (dynamic window approach)-matlab代码修改及动态障碍物避障效果

    具体效果视频 xff1a DWA动态障碍物 哔哩哔哩 https b23 tv pQp6ne 一 源码及问题 最初的源码链接https blog csdn net heyijia0327 article details 44983551 改
  • QQ可以用十六进制登录哦

    又到装逼 时刻了 平常人登QQ是这样的 xff1a 如果我告诉你 xff1a 还可以这样登录 xff0c 你信吗 xff1f 显然我成功了 xff0c 你呢 xff1f 想知道如何快速把QQ号转换为十六进制吗 xff1f 评论里告诉你 xf
  • Keil C51详细安装教程(最新版)

    Keil C51 0x00 说在前面0x01 资源获取0x02 软件安装0x03 还有一步 xff08 重要 xff09 0x04 小问题 0x00 说在前面 x1f4e2 Keil 5常用的分两个版本 xff0c C51和MDK C51用
  • Java学习之JavaWeb篇

    图床路径 Kuang JavaWeb Java Web 1 基本概念 web开发 web xff0c 网页的意思 xff0c www baidu com静态web html xff0c css提供给所有人看的数据 xff0c 始终不会变化
  • Typora自动上传图片到Gitee图床

    Typora自动上传图片到Gitee图床 序言 思路 Gitee作为免费的图床 xff0c Typora作为最好用的blog博客笔记软件 xff0c 通过PicGo软件配置将Typora中的图片自动保存到Gitee图床 xff0c 获得云端
  • 关于百度OCR和EasyOCR的研究记录

    现行方案 xff1a 迅捷OCR软件 采用迅捷OCR文字识别软件 xff0c 购买了一年的期限 操作需要使用pyautogui来操作软件进行操作 xff0c 现在也可以使用USB鼠键模块KM3来真实模拟鼠标键盘的操作 xff0c 可信度更高
  • qt打开项目,只有pro文件其它文件消失:-1: error: msvc-version.conf loaded but QMAKE_MSC_VER isn‘t set

    之前在电脑打开开发的项目 xff0c 突然今天打开突然只有pro文件其它文件消失了 xff0c 在文件管理器里面找也都还有 打开项目只剩下蓝色框里面的 网上查了一些资料 xff0c 发现是开发程序事qt的版本不同所导致 解决方法 点开项目
  • 表格中重复项设置一级和二级序号排序

    摘要 xff1a 一级分类存在重复项 xff0c 需要排序 xff0c 二级分类也存在重复项 xff0c 也需要排序 xff0c 相同一级分类中的二级分类需要重新从一开始排序 xff08 重点和难点 xff09 先来看看效果 xff1a 公
  • 动态数码管显示实验

    文章目录 摘要实验器件简介实验原理硬件电路图硬件电路连线实验代码实验结果思考以及注意事项实验视频教程资源分享 摘要 本实验采用了普中科技的51单片机开发板 本实验以静态数码管显示实验为基础 https blog csdn net weixi
  • C51中intrins_h头文件解释分析

    文章目录 摘要源代码说明举个栗子 摘要 这是关于C51中使用循环移位等函数的头文件定义的分析 源代码 span class token comment INTRINS H Intrinsic functions for C51 Copyri
  • 喜马拉雅音频下载+x2m文件转换

    文章目录 摘要软件免费音频VIP音频 摘要 一款关于喜马拉雅音频的小神器 x2m格式转换为m4a格式 批量下载操作 软件 绿色免费的小工具ximalaya downloader amp x2m decoder xff0c 当然是在电脑上运行
  • http报错:405 (METHOD NOT ALLOWED)

    使用post请求ajax数据时 xff0c 报错 xff1a 405 METHOD NOT ALLOWED 将post请求改为get请求
  • Http怎么解决粘包拆包的?

    发送端 xff1a 应用层协议的每个包到达传输层 xff0c 如果是tcp xff0c 会可能出现以下情况 1 应用层的每个包被拆成多个tcp报文 xff0c 分别发送 xff0c 这是拆包 xff1b 2 应用层的多个包组成一个tcp报文
  • ROS TCP通信

    Reference https blog csdn net deyuzhi article details 51725074 https blog csdn net weixin 43795921 article details 85307

随机推荐

  • Ubuntu+ROS安装及rosdep init失败解决办法

    1 安装Ubuntu16 04 18 04 xff08 1 xff09 双系统硬盘安装 xff1a 参考双硬盘Window10与Ubuntu16 04 18 04安装 脚踩香蕉皮的博客 CSDN博客 Ubuntu 18 04 xff1a 链
  • c++ 编译 报错 未定义的引用

    遇到的问题有两种情况 一个是static没初始化 一个是模板类实现和定义分开写 这里主要记录一下模板类实现和定义分开写报错的问题 xff08 都放在 h文件中就不会报错 xff09 自己尝试用 传统 方法 xff0c 及在 h文件里声明 在
  • JNI基础语法

    一 Java和JNI类型对照 1 1 基本类型对照表 Java类型Native类型C C 43 43 类型大小Booleanjbooleanunsigned char无符号8位Bytejbytechar有符号8位Charjcharunsig
  • STL源码剖析 笔记之三 迭代器

    第三章 迭代器概念与traits编程技法 Design Patterns 中提供有23个设计模式的完整描述 迭代器模式定义如下 xff0c 提供一种方法 xff0c 使之能够依序巡防某个聚合物 容器 所含的各个元素 xff0c 而又无需暴露
  • 带复杂命令启停功能的二自由度自动进样器

    给8051发送 34 Start E 34 后 系统开始工作 发送 34 Stop E 34 后 系统停止工作 与之前以串口中断为主线的控制逻辑不同 这里以定时器0中断为主线 其实也就是以系统序列动作为主线 并提高了串口中断的优先级 以实时
  • Python温度转换实例

    我的第一个程序 xff1a TempCovert py TemStr 61 input 34 请输入带有符号的温度值 xff1a 34 if TemStr 1 in 39 F 39 39 f 39 C 61 eval TemStr 0 1
  • 不同数据类型所占字节

    一 32位处理器 xff1a char xff1a 1个字节 char p xff1a 4个字节 int xff1a 4个字节 unsigned int xff1a 4个字节 double xff1a 8个字节 short xff1a 2个
  • 浏览器中输入www.baidu.com后发生了什么

    一道经典的面试题 xff1a 你输入网址后到页面显示出来 xff0c 中间发生了什么 xff1f 第一步 浏览器查找该域名的 IP 地址 第二步 浏览器根据解析得到的IP地址向 web 服务器发送一个 HTTP 请求 第三步 服务器收到请求
  • 交换机对数据帧的处理规则

    一 概述 以太网交换机对二层帧的转发处理比较复杂 xff0c 很多同事都不是很清楚 本文将对此问题进行总结 xff0c 并通过实验进行验证 二 交换机基本概念 在了解交换机转发规则之前 xff0c 我们先理解交换机的一些概念 xff0c 如
  • NAT详解

    NAT详解 1 概述 1 1 nat简介 NAT xff08 Network Address Translation xff0c 网络地址转换 xff09 是1994年提出的 它是一个IETF Internet Engineering Ta
  • FTP服务

    1 FTP简介 FTP是File Transfer Protocol xff08 文件传输协议 xff09 的英文简称 xff0c 而中文简称为 文传协议 用于Internet上的控制文件的双向传输 同时 xff0c 它也是一个应用程序 x
  • 从Linux服务器下载文件夹到本地

    1 使用scp命令 把本地的source txt文件拷贝到192 168 0 10机器上的 home work目录下 scp home work source txt root 64 192 168 0 10 home work 把192
  • 安卓开发——网络传输工具类HttpUtil(基于okhttp3)使用

    AndroStudio开发 xff1a 使用时需先导入okhttp3依赖 xff1a com squareup okhttp3 okhttp 3 4 1 依赖导入步骤 xff1a File ProjectStructure Dependen
  • 无人系统传感器导航

    文章目录 GPS 全球定位系统 Global positioning system RTK Real time kinematic 距离传感器超声波传感器激光测距传感器毫米波雷达 Lidar Light detection and rang
  • 深度相机选取建议:

    2020 9 4更新 xff1a 现在再回头来看TOF应该会是未来 xff0c 限制TOF发展的硬件问题正在慢慢解决 苹果的ipad上也加上了TOF xff08 用于AR xff09 xff0c ipone12上估计也会有 可能小米的mix
  • JavaHTTP请求工具类HTTPUtils

    HTTP 请求工具类 HTTPUtils xff0c 其中涉及 HTTP 请求相关的各种操作 xff0c 关于这些方法详细的介绍可以查看这些博客 x1f4ac 相关 博客文章 Java发起HTTP请求并解析JSON返回数据 https bl
  • YOLOV7语义分割(日后自用笔记)

    系统win10 本文只是根据readme文件走流程 xff0c 记录一些常用公式 RizwanMunawar yolov7 segmentation at 87b016cda50371d6e2824378d641c2d4417ea1c3 g
  • char型和int型之间的类型转换

    char转换为int型数据 通过赋值方式将char类型变量转换为int型变量 xff0c 变量值为char类型变量的ASCII码值 例如 xff1a int a 61 0 那么打印a的结果为48 xff0c 如果想要得到正确的数字 xff0
  • ROS出现Cannot find source file的错误和add_dependencies,add_executable,target_link_libraries三者先后顺序不对所造成的错误。

    出现上面这个报错 xff0c 往往是 CMakeLists txt中的问题 add executable server tutorial ws src dynamic tutorials src server cpp 这行代码的问题 xff
  • 利用python ast包,绘制python代码的调用关系图(可分析互相调用的多个py文件)

    我的目的是辅助代码阅读 xff08 也方便写文档 xff09 xff0c 因此不需要太详细的信息 xff0c 只需要看用户定义的函数的函数调用关系 文章目录 1 开源项目staticfg的安装和直接使用2 绘制python的简单调用关系图