用Tkinter打造GUI开发工具(21)改进ast模块的分析代码和Python程序标色思路
在上一篇文章,我们尝试用ast模块开发分析Python3程序的代码,在使用中有些小问题.
网友(Likianta-上海)给出了新的代码,我按照他的代码,进行了改进,新程序“py010b.py”基本能满足分析的要求.
我们对同一个Python程序“py010a.py”进行了测试,对比结果如下:
“py010a.py”输出结果如下:
库 {}
类 {'ParseTree': [12]}
函数 {'generic_visit': [14], 'visit_ClassDef': [17], 'visit_ImportFrom': [21], 'visit_FunctionDef': [28], 'visit_Assign': [32], 'MyParse': [39], 'readtext': [54]}
变量 {'lib_dict': [7], 'var_dict': [8], 'func_dict': [9], 'class_dict': [10], 'py': [61], 'py_root': [63]}
“py010b.py”输出结果如下:
库 {'ast': [1]}
类 {'ParseTree': [12]}
函数 {'MyParse': [39], 'readtext': [54], 'generic_visit': [14], 'visit_ClassDef': [17], 'visit_ImportFrom': [21], 'visit_FunctionDef': [28], 'visit_Assign': [32]}
变量 {'lib_dict': [7, 44, 23], 'var_dict': [8, 45, 35], 'func_dict': [9, 46, 29], 'class_dict': [10, 47, 18], 'py': [61], 'py_root': [63, 48], 'pt': [49], 'ss': [55, 58], 's': [57]}
很明显,“py010b.py”程序优于“py010a.py”程序。其他不多说了,直接给出“py010b.py”全部程序代码。
from _ast import *
from ast import parse as ast_parse, walk as ast_walk
global lib_dict #库字典
global var_dict #变量字典
global func_dict #函数字典
global class_dict #类字典
lib_dict={}
var_dict={}
func_dict={}
class_dict={}
class AstHelper:
def main(self, file):
out = {}
with open(file, mode='r', encoding='utf-8-sig') as f:
text = f.read()
root = ast_parse(text)
for node in ast_walk(root):
if not hasattr(node, 'lineno'):
continue
x = out.setdefault(node.lineno, [])
x.append((str(type(node)), self.eval_node(node)))
return out
def main2(self, text):
out = {}
root = ast_parse(text)
for node in ast_walk(root):
if not hasattr(node, 'lineno'):
continue
x = out.setdefault(node.lineno, [])
x.append((str(type(node)), self.eval_node(node)))
return out
def eval_node(self, node):
result = None
while result is None:
# ------------------------------------------------ output result
if isinstance(node, Attribute):
result = node.attr
elif isinstance(node, ClassDef):
result = node.name
elif isinstance(node, FunctionDef):
result = node.name
elif isinstance(node, Name):
result = node.id
elif isinstance(node, Str):
result = node.s
# ------------------------------------------------ compound obj
elif isinstance(node, Import):
result = {}
for imp in node.names:
if imp.asname is None:
result[imp.name] = imp.name
else:
result[imp.name] = imp.asname
elif isinstance(node, ImportFrom):
result = {}
module = node.module
for imp in node.names:
if imp.asname is None:
result[module + '.' + imp.name] = \
module + '.' + imp.name
else:
result[module + '.' + imp.name] = \
module + '.' + imp.asname
elif isinstance(node, Assign):
result = {}
a, b = node.targets, node.value
v = self.eval_node(b)
for i in a:
k = self.eval_node(i)
result[k] = v
# ------------------------------------------------ take reloop
elif isinstance(node, Call):
node = node.func
elif isinstance(node, Expr):
node = node.value
elif isinstance(node, Subscript):
node = node.value
else:
result = str(node._fields)
return result
def MyParse(ss):
global lib_dict #库字典
global var_dict #变量字典
global func_dict #函数字典
global class_dict #类字典
lib_dict={}
var_dict={}
func_dict={}
class_dict={}
helper = AstHelper()
res = helper.main2(ss)
dict_filter = {
"<class '_ast.Import'>" : lib_dict,
"<class '_ast.ImportFrom'>" : lib_dict,
"<class '_ast.Assign'>" : var_dict,
"<class '_ast.FunctionDef'>": func_dict,
"<class '_ast.ClassDef'>" : class_dict,
}
for lineno, data in res.items():
for i in data:
type_, value = i
if type_ in dict_filter:
d = dict_filter.get(type_)
if isinstance(value, str):
node = d.setdefault(value, [])
node.append(lineno)
else:
for k in value.keys():
node = d.setdefault(k, [])
node.append(lineno)
def readtext(filename): #读入文本文件
ss=''
with open(filename,encoding='utf-8') as f:
s=f.read()
ss=ss+s
return ss
py=readtext('py010a.py') #读取py文件
py_root=MyParse(py)
print('库',lib_dict)
print('类',class_dict)
print('函数',func_dict)
print('变量',var_dict)
尽管ast模块很不错了,它解决了库、类、函数、变量树功能。通过双击名称,可快速定位于所在的程序行。
我下来要完成的工作是使Python代码彩色化,即不同代码元素,标注不同的颜色。这个程序我已经编写完成了,将来随我的《用Tkinter打造GUI开发工具》书中提供全部代码。
下面给朋友介绍一些开发思路。
上图左边的函数树就是使用“py010b.py”模块。右边使用了Tkinter的Text控件设计了上下2个代码编辑运行模块。
尽管离我们目标类似VS的可视化设计还有点距离。但也不妨碍我们能组合一个Python教学玩具。见下图。
跑题了。下面继续讲代码彩色化思路。
首先, 我们要识别程序中的Python关键字。设计了iskeyword(s)函数,代码如下。
def iskeyword(s):
#保留字400
keywords = ['False', 'None', 'True', 'and', 'as', 'assert', 'break',
'class', 'continue', 'def', 'del', 'elif', 'else',
'except', 'finally', 'for', 'from', 'global', 'if',
'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or',
'print','input','self', 'pass', 'raise', 'return', 'try',
'while', 'with', 'yield','next']
f=0
if s in keywords :
f=400+keywords.index(s)
return f
其次,要识别Python程序中的分割符号。设计了isdelimiter(s)函数,代码如下。
def isdelimiter(s):
#界符300
delimiters = ['{','}','[',']','(',')',',',':',';']
f=-1
if s in delimiters :
f=300+delimiters.index(s)+1
return f
最后要识别Python程序中的运算和操作符号。设计了isoperator(s)函数,代码如下。
def isoperator(s):
#运算符200
operator = ['+','-','*','/','%','++','--','+=','-=','+=','/=',#算术运算符
'==','!=','>','<','>=','<=', #关系运算符
'&','|','^','~','<<','>>','>>>',#位运算符
'&&','||','!',#逻辑运算符
'=','+=','-=','*=','/=','%=','<<=','>>=','&=','^=','|=',#赋值运算符
'?:']#条件运算符
f=0
if s in operator :
f=200+operator.index(s)
return f
重复分析程序,直到程序分析完成。
无法识别字符串均为用户创建的名称,例如模块名,类名,函数名,变量名等等。
根据识别出的字符串,进行颜色标注,就会实现程序代码的彩色化。
前面我曾演示了Tkinter鼠标移动控件的图片。
一直没有进行可视化设计的根本原因,我一直无法设计出可用滑块控制的超长窗口控件,上周末在家尝试,已经实现了这个技术,并且做出了示例,下一步就进行设计出新标准控件,这样就能修改窗口中新放置的控件属性了。
是不是离类似“VB”那样的“Visual Python”可视化开发又进了一步。
上面是正在开发的中文Python教学软件,中英文关键字都能够识别和运行。等完善后,给大家提供。
独狼荷蒲qq:2775205
小白量化群:524949939
微信公众号:独狼股票分析