【问题标题】:Parsing class and function dependencies from a project从项目中解析类和函数依赖项
【发布时间】:2013-02-21 18:50:04
【问题描述】:

我正在尝试对 Python 代码库中的类和函数依赖项进行一些分析。我的第一步是使用 Python 的 csv 模块和正则表达式创建一个用于导入 Excel 的 .csv 文件。

我所拥有的当前版本如下所示:

import re
import os
import csv 
from os.path import join


class ClassParser(object):
   class_expr = re.compile(r'class (.+?)(?:\((.+?)\))?:')                                                                                                                                                                                    
   python_file_expr = re.compile(r'^\w+[.]py$')

   def findAllClasses(self, python_file):
      """ Read in a python file and return all the class names
      """
      with open(python_file) as infile:
         everything = infile.read()
         class_names = ClassParser.class_expr.findall(everything)
         return class_names

   def findAllPythonFiles(self, directory):
      """ Find all the python files starting from a top level directory
      """
      python_files = []
      for root, dirs, files in os.walk(directory):
         for file in files:
            if ClassParser.python_file_expr.match(file):
               python_files.append(join(root,file))
      return python_files

   def parse(self, directory, output_directory="classes.csv"):
      """ Parse the directory and spit out a csv file
      """
      with open(output_directory,'w') as csv_file:
         writer = csv.writer(csv_file)
         python_files = self.findAllPythonFiles(directory)
         for file in python_files:
            classes = self.findAllClasses(file)
            for classname in classes:
               writer.writerow([classname[0], classname[1], file])

if __name__=="__main__":
   parser = ClassParser()
   parser.parse("/path/to/my/project/main/directory")

这会生成格式为 .csv 的输出:

class name, inherited classes (also comma separated), file
class name, inherited classes (also comma separated), file
... etc. ...

除了类名之外,我还想开始解析函数声明和定义。我的问题:有没有更好的方法来获取类名、继承类名、函数名、参数名等?

注意:我考虑过使用 Python ast 模块,但我没有使用它的经验,也不知道如何使用它来获取所需的信息,或者它是否可以做到这一点。

编辑:响应 Martin Thurau 提供更多信息的要求 - 我试图解决此问题的原因是因为我继承了一个相当冗长(100k+ 行)的项目,该项目没有可辨别的其模块、类和函数的结构;它们都作为文件集合存在于单个源目录中。

一些源文件包含几十个相切相关的类,并且有 10k+ 行长,这使得它们难以维护。我开始对使用The Hitchhiker's Guide to Packaging 作为基础将每个类打包成一个更有凝聚力的结构的相对难度进行分析。对于该分析,我关心的部分内容是一个类与其文件中的其他类的交织程度,以及特定类依赖于哪些导入或继承的类。

【问题讨论】:

  • @Fredrik 我快速浏览了文档。 inspect 似乎只适用于实时(运行时)代码。我错了吗?我认为我需要使用静态分析,因为并非每个函数/类都将用于任何给定的代码运行。
  • pyflakes static code checker用的是AST,或许可以拿来举例?
  • 无论您实际尝试做什么:请记住,您可以在运行时创建、加载和实例化类,因此无法在 100% 的时间内获得 100% 正确。
  • @MartinThurau 感谢您抽出宝贵时间提供反馈。我重新阅读了这个问题,并意识到我没有很好地说明我的目的,所以我添加了关于我实际上想要做什么的新信息。

标签: python regex python-2.6


【解决方案1】:

我已经开始实施这个了。将以下代码放入文件中并运行它,传递文件或目录的名称进行分析。它将打印出它找到的所有类、找到它的文件以及类的基础。它并不智能,因此如果您在代码库中定义了两个 Foo 类,它不会告诉您正在使用哪个类,但这是一个开始。

此代码使用 python ast 模块检查 .py 文件,并找到所有 ClassDef 节点。然后它使用这个meta package 来打印它们的一部分——你需要安装这个包。

$ pip install -e git+https://github.com/srossross/Meta.git#egg=meta

示例输出,针对django-featured-item运行

$ python class-finder.py /path/to/django-featured-item/featureditem/ FeaturedField,../django-featured-item/featureeditem/fields.py,models.BooleanField SingleFeature,../django-featured-item/featureeditem/tests.py,models.Model MultipleFeature,../django-featured-item/featureeditem/tests.py,models.Model 作者,../django-featured-item/featureeditem/tests.py,models.Model 书,../django-featured-item/featureeditem/tests.py,models.Model FeaturedField,../django-featured-item/featureditem/tests.py,TestCase

代码:

# 类-finder.py 导入 ast 导入 csv 导入元 导入操作系统 导入系统 def find_classes(节点,in_file): 如果是实例(节点,ast.ClassDef): 产量(节点,in_file) 如果有属性(节点,'body'): 对于 node.body 中的孩子: # Python 3.x 中的`yield from find_classes(child)` for x in find_classes(child, in_file): yield x def print_classes(类,出): writer = csv.writer(out) 对于 cls,类中的 in_file: writer.writerow([cls.name, in_file] + [meta.asttools.dump_python_source(base).strip() 用于 cls.bases 中的基础]) def 进程文件(文件路径): root = ast.parse(open(file_path, 'r').read(), file_path) 对于 find_classes(root, file_path) 中的 cls: 产量分类 def 进程目录(目录路径): 对于 os.listdir(dir_path) 中的条目: 对于 process_file_or_directory(os.path.join(dir_path, entry)) 中的 cls: 产量分类 def process_file_or_directory(file_or_directory): 如果 os.path.isdir(file_or_directory): 返回进程目录(文件或目录) elif file_or_directory.endswith('.py'): 返回进程文件(文件或目录) 别的: 返回 [] 如果 __name__ == '__main__': 类 = process_file_or_directory(sys.argv[1]) print_classes(类,sys.stdout)

【讨论】:

    猜你喜欢
    • 2017-02-22
    • 2017-03-03
    • 1970-01-01
    • 2022-10-02
    • 2017-05-06
    • 1970-01-01
    • 1970-01-01
    • 2021-01-04
    • 1970-01-01
    相关资源
    最近更新 更多