Пакет Python ast: обход иерархий объектов - PullRequest
11 голосов
/ 24 февраля 2012

Ниже приведен фрагмент кода Python с использованием ast и symtable пакеты. Я пытаюсь разобрать код и проверить типы. Но я не понимаю, как пройти объекты , чтобы добраться до фактической переменной ссылаясь.

В следующем коде реализован NodeVisitor, и функция представляется компилятору и анализируется компилятором и пользователем. Анализируемой функции (eval_types) передается пара объектов.

Ниже приведены фрагменты кода, которые составляют пример. Я добавил несколько комментариев для каждого куска. Для запуска кода необходимо собрать «куски».

Импорт и функция для отмены отступа блока кода для разбора.

import inspect
import ast
import symtable
from tokenize import generate_tokens, untokenize, INDENT
from cStringIO import StringIO

# _dedent borrowed from the myhdl package (www.myhdl.org)
def _dedent(s):
    """Dedent python code string."""

    result = [t[:2] for t in generate_tokens(StringIO(s).readline)]
    # set initial indent to 0 if any
    if result[0][0] == INDENT:
        result[0] = (INDENT, '')
    return untokenize(result)

Ниже приведен посетитель узла, он имеет общие необработанные и перегруженные имена посетителя.

class NodeVisitor(ast.NodeVisitor):
    def __init__(self, SymbolTable):
        self.symtable = SymbolTable
        for child in SymbolTable.get_children():
            self.symtable = child
            print(child.get_symbols())

    def _visit_children(self, node):
        """Determine if the node has children and visit"""
        for _, value in ast.iter_fields(node):
            if isinstance(value, list):
                for item in value:
                    if isinstance(item, ast.AST):
                        print('  visit item %s' % (type(item).__name__))
                        self.visit(item)

            elif isinstance(value, ast.AST):
                print('  visit value %s' % (type(value).__name__))
                self.visit(value)

    def generic_visit(self, node):
        print(type(node).__name__)
        self._visit_children(node)

    def visit_Name(self, node):
        print('  variable %s type %s' % (node.id,
                                         self.symtable.lookup(node.id)))
        print(dir(self.symtable.lookup(node.id)))

Ниже приведены некоторые простые классы, которые будут использоваться в функции, которая будет проанализирована и проанализирована с помощью AST.

class MyObj(object):
    def __init__(self):
        self.val = None

class MyObjFloat(object):
    def __init__(self):
        self.x = 1.

class MyObjInt(object):
    def __init__(self):
        self.x = 1

class MyObjObj(object):
    def __init__(self):
        self.xi = MyObjInt()
        self.xf = MyObjFloat()

Ниже приведена тестовая функция, функция eval_types - это функция, которая будет проанализирована с помощью AST.

def testFunc(x,y,xo,z):

    def eval_types():
        z.val = x + y + xo.xi.x + xo.xf.x

    return eval_types

Код для выполнения примера, скомпилируйте функцию и проанализируйте.

if __name__ == '__main__':
    z = MyObj()
    print(z.val)
    f = testFunc(1, 2, MyObjObj(), z)
    f()
    print(z.val)
    s = inspect.getsource(f)
    s = _dedent(s)
    print(type(s))
    print(s)

    SymbolTable = symtable.symtable(s,'string','exec')
    tree = ast.parse(s)
    v = NodeVisitor(SymbolTable)
    v.visit(tree)

Ниже приведен пример вывода до первого посещения имени.

Module
  visit item FunctionDef
FunctionDef
  visit value arguments
arguments
  visit item Assign
Assign
  visit item Attribute
Attribute
  visit value Name
  variable z type <symbol 'z'>
['_Symbol__flags', '_Symbol__name', '_Symbol__namespaces', 
 '_Symbol__scope', '__class__', '__delattr__', '__dict__', 
 '__doc__', '__format__', '__getattribute__', '__hash__', 
 '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', 
 '__repr__', '__setattr__', '__sizeof__', '__str__', 
 '__subclasshook__', '__weakref__', 'get_name', 'get_namespace', 
 'get_namespaces', 'is_assigned', 'is_declared_global', 
 'is_free', 'is_global', 'is_imported', 'is_local', 
 'is_namespace', 'is_parameter', 'is_referenced']

Создание посетителя узла не кажется плохим, но я не могу понять, как пройти иерархию объектов. В общем случае переменная доступ к нему может быть похоронен глубоко в объекте. Как добраться до фактической переменной, к которой обращается аст-посетитель? Я только вижу, что объект находится в узле, но не имеет никакой дополнительной информации о том, что является результатом доступа к переменной.

1 Ответ

2 голосов
/ 29 марта 2012

Я не знаю, ищете ли вы это по-прежнему, но, похоже, вам просто нужно добавить visit_Attribute и пройти назад.Если вы добавите это к своему примеру:

def visit_Attribute(self, node):
    print('  attribute %s' % node.attr)
    self._visit_children(node)

Тогда вывод для xo.xf.x будет:

Add
  visit value Attribute
  attribute x
  visit value Attribute
  attribute xf
  visit value Name
  variable xo type <symbol 'xo'>
  visit value Load

В зависимости от того, что вы хотите сделать с этим, вам просто нужно сохранитьатрибуты в списке, пока не встретится Name, затем поменяйте их местами.

...