代码对对python项目中的父子类关系进行提取。当然目前只做到了简单的提取工作,具体的分析并没有做。由于python中支持多继承,因此当有非常多的类以后,这个图不知道怎么画比较合适,等以后有时间想出了好的解决方案再来实施。下面是具体的python代码
#!/bin/python3
import re
import os
class InfoCollector(object):
def __init__(self):
self.match_table = self._build_match_table()
#class_relation store the known class relation
self.class_relation = []
#module_table used to map the short module name with full name
self.module_table = {}
self.classs = []
def _build_match_table(self):
''' lines match in the keys order '''
keys = ['class', 'import_as', 'import', 'from_import_as', 'from_import']
match_table = []
for key in keys:
regex = self.__getattribute__('_PATTERN_STR_' + key.upper())
proc = self.__getattribute__('_proc_' + key + '_match')
match_table.append((key, regex, proc, re.compile(regex)))
return match_table
_PATTERN_STR_CLASS = r"^class (.*)\((.*)\):$"
def _proc_class_match(self, match):
child_class = match.groups()[0].strip()
parents_class = match.groups()[1].strip()
print("child_class:%s parent_class:%s" % (child_class, parents_class))
parents = parents_class.split(',')
for parent in parents:
parent = parent.strip()
if '.' in parent:
path, sp, klass = parent.rpartition('.')
abs_path = self.module_table.get(path, path)
parent_class = ':'.join([abs_path, klass])
else:
if parent in self.classs:
parent_class = ':' + parent
print(parent_class)
else:
parent_class = parent
self.classs.append(child_class)
self.class_relation.append((child_class, parent_class))
_PATTERN_STR_IMPORT = r"^import (.*)"
def _proc_import_match(self, match):
module = match.groups()[0].strip()
self.module_table[module] = module
print("module name: %s" % module)
_PATTERN_STR_IMPORT_AS = r"^import (.*) as (.*)"
def _proc_import_as_match(self, match):
module = match.groups()[0]
module = module.strip()
alias = match.groups()[1]
alias = alias.strip()
self.module_table[alias] = module
print("module name: %s alias: %s" % (module, alias))
_PATTERN_STR_FROM_IMPORT = r"^from (.*) import (.*)"
def _proc_from_import_match(self, match):
path = match.groups()[0]
path = path.strip()
module = match.groups()[1]
module.strip()
self.module_table[module] = '.'.join([path, module])
print("path:%s module: %s" % (path, module))
_PATTERN_STR_FROM_IMPORT_AS = r"^from (.*) import (.*) as (.*)"
def _proc_from_import_as_match(self, match):
path = match.groups()[0]
path = path.strip()
module = match.groups()[1]
module = module.strip()
alias = match.groups()[2]
alias = alias.strip()
self.module_table[alias] = '.'.join([path, module])
print("path:%s module: %s alias: %s" % (path, module, alias))
def update_with_single_line(self, line):
for key, regex, proc, pattern in self.match_table:
match = re.search(pattern, line)
if match:
proc(match)
return
return
def update_with_full_file(self, file_name):
self.reset()
with open(file_name) as in_file:
for line in in_file:
self.update_with_single_line(line)
def reset(self):
self.module_table = {}
self.class_relation = []
self.classs = []
def present_relations(self):
print(self.class_relation)
class classFinder(object):
def __init__(self, collector, dir_name):
self._collector = collector
self._dir = dir_name
self.relations = []
def update_with_file(self, full_file_name):
module_name = full_file_name[len(self._dir):]
module_name = module_name.replace(os.path.sep, '.')
module_name = module_name.strip('.')
self._collector.reset()
with open(full_file_name) as in_file:
for line in in_file:
self._collector.update_with_single_line(line)
for child, parent in self._collector.class_relation:
if parent.startswith(':'):
parent = module_name + parent
self.relations.append((':'.join([module_name, child]), parent))
self._collector.reset()
def run(self):
for path, dirs, files in os.walk(self._dir):
files = [x for x in files if x.endswith('.py')]
for _file in files:
full_file_name = os.path.join(path, _file)
self.update_with_file(full_file_name)
def output_to_file(self, file_name):
with open(file_name, 'w') as output_file:
for pair in self.relations:
output_file.write(str(pair) + '\n')
if __name__ == '__main__':
DIR= 'abspath/to/cinder'
collector = InfoCollector()
finder = classFinder(collector, DIR)
finder.run()
finder.output_to_file('class_of_cinder')
该代码还有很多需要完善的地方,当前只是把代码的大体结构给完成了。