97 lines
2.4 KiB
Python
97 lines
2.4 KiB
Python
#!/usr/bin/env python
|
|
# -*- encoding: utf-8 -*-
|
|
"""
|
|
Topic: 解析与分析Python源码
|
|
Desc :
|
|
"""
|
|
|
|
import ast
|
|
|
|
|
|
class CodeAnalyzer(ast.NodeVisitor):
|
|
def __init__(self):
|
|
self.loaded = set()
|
|
self.stored = set()
|
|
self.deleted = set()
|
|
|
|
def visit_Name(self, node):
|
|
if isinstance(node.ctx, ast.Load):
|
|
self.loaded.add(node.id)
|
|
elif isinstance(node.ctx, ast.Store):
|
|
self.stored.add(node.id)
|
|
elif isinstance(node.ctx, ast.Del):
|
|
self.deleted.add(node.id)
|
|
|
|
# Sample usage
|
|
if __name__ == '__main__':
|
|
# Some Python code
|
|
code = '''
|
|
for i in range(10):
|
|
print(i)
|
|
del i
|
|
'''
|
|
|
|
# Parse into an AST
|
|
top = ast.parse(code, mode='exec')
|
|
|
|
# Feed the AST to analyze name usage
|
|
c = CodeAnalyzer()
|
|
c.visit(top)
|
|
print('Loaded:', c.loaded)
|
|
print('Stored:', c.stored)
|
|
print('Deleted:', c.deleted)
|
|
|
|
|
|
# namelower.py
|
|
import ast
|
|
import inspect
|
|
|
|
# Node visitor that lowers globally accessed names into
|
|
# the function body as local variables.
|
|
class NameLower(ast.NodeVisitor):
|
|
def __init__(self, lowered_names):
|
|
self.lowered_names = lowered_names
|
|
|
|
def visit_FunctionDef(self, node):
|
|
# Compile some assignments to lower the constants
|
|
code = '__globals = globals()\n'
|
|
code += '\n'.join("{0} = __globals['{0}']".format(name)
|
|
for name in self.lowered_names)
|
|
code_ast = ast.parse(code, mode='exec')
|
|
|
|
# Inject new statements into the function body
|
|
node.body[:0] = code_ast.body
|
|
|
|
# Save the function object
|
|
self.func = node
|
|
|
|
|
|
# Decorator that turns global names into locals
|
|
def lower_names(*namelist):
|
|
def lower(func):
|
|
srclines = inspect.getsource(func).splitlines()
|
|
# Skip source lines prior to the @lower_names decorator
|
|
for n, line in enumerate(srclines):
|
|
if '@lower_names' in line:
|
|
break
|
|
|
|
src = '\n'.join(srclines[n + 1:])
|
|
# Hack to deal with indented code
|
|
if src.startswith((' ', '\t')):
|
|
src = 'if 1:\n' + src
|
|
top = ast.parse(src, mode='exec')
|
|
|
|
# Transform the AST
|
|
cl = NameLower(namelist)
|
|
cl.visit(top)
|
|
|
|
# Execute the modified AST
|
|
temp = {}
|
|
exec(compile(top, '', 'exec'), temp, temp)
|
|
|
|
# Pull out the modified code object
|
|
func.__code__ = temp[func.__name__].__code__
|
|
return func
|
|
|
|
return lower
|