Пакет компилятора Python
Устаревшее начиная с версии 2.6: Пакет compiler был удалён в Python 3.
Пакет компилятора Python — это инструмент для анализа исходного кода Python и генерации байт-кода Python. Компилятор содержит библиотеки для генерации дерева абстрактного синтаксиса из исходного кода Python и генерации байт-кода Python из этого дерева.
Пакет compiler представляет собой транслятор исходного кода Python в байт-код, написанный на Python. Он использует встроенный анализатор и стандартный модуль parser для генерации дерева конкретного синтаксиса. Это дерево используется для генерации дерева абстрактного синтаксиса (AST) и затем байт-кода Python.
Полная функциональность пакета дублирует встроенный компилятор, предоставляемый с интерпретатором Python. Он предназначен для почти точного соответствия его поведению. Зачем реализовывать другой компилятор, который делает то же самое? Пакет полезен для различных целей. Его можно изменить легче, чем встроенный компилятор. AST, который он генерирует, полезен для анализа исходного кода Python.
В этом разделе объясняется, как работают различные компоненты пакета compiler. Он сочетает справочный материал с обучающим руководством.
1. Основной интерфейс
На верхнем уровне пакета определены четыре функции. Если вы импортируете compiler, вы получите эти функции и набор модулей, содержащихся в пакете.
-
compiler.parse(buf) -
Возвращает дерево абстрактного синтаксиса для исходного кода Python в buf. Функция вызывает исключение
SyntaxError, если в исходном коде есть ошибка. Возвращаемое значение — экземплярcompiler.ast.Module, содержащий дерево.
-
compiler.parseFile(path) -
Возвращает дерево абстрактного синтаксиса для исходного кода Python в файле, указанном по path. Это эквивалентно
parse(open(path).read()).
-
compiler.walk(ast, visitor[, verbose]) -
Выполняет обход дерева абстрактного синтаксиса ast в порядке обхода. Вызывает соответствующий метод экземпляра visitor для каждого узла, который встречается.
-
compiler.compile(source, filename, mode, flags=None, dont_inherit=None) -
Компилирует строку source, модуль, оператор или выражение Python в объект кода, который может быть выполнен оператором exec или
eval(). Эта функция является заменой для встроенной функцииcompile().filename будет использоваться для сообщений об ошибках во время выполнения.
mode должен быть ‘exec’ для компиляции модуля, ‘single’ для компиляции отдельного (интерактивного) оператора или ‘eval’ для компиляции выражения.
Аргументы flags и dont_inherit влияют на операторы, относящиеся к будущим версиям, но пока не поддерживаются.
-
compiler.compileFile(source) -
Компилирует файл source и генерирует файл .pyc.
Пакет compiler содержит следующие модули: ast, consts, future, misc, pyassem, pycodegen, symbols, transformer, и visitor.
2. Ограничения
У пакета компилятора есть некоторые проблемы с проверкой ошибок. Интерпретатор обнаруживает синтаксические ошибки на двух разных этапах. Одна группа ошибок обнаруживается анализатором интерпретатора, другая — компилятором. Пакет компилятора полагается на анализатор интерпретатора, поэтому он получает первую фазу проверки ошибок бесплатно. Он реализует вторую фазу сам, и эта реализация неполная. Например, пакет компилятора не выдаёт ошибку, если имя появляется более одного раза в списке аргументов: def f(x, x):
...
В будущей версии пакета компилятора эти проблемы будут исправлены.
3. Абстрактный синтаксис Python
Модуль compiler.ast определяет абстрактный синтаксис для Python. В дереве абстрактного синтаксиса каждый узел представляет синтаксическую конструкцию. Корень дерева — это Module объект.
Абстрактный синтаксис предоставляет интерфейс более высокого уровня для проанализированного исходного кода Python. Модуль parser и компилятор, написанный на C для интерпретатора Python, используют дерево конкретного синтаксиса. Конкретный синтаксис тесно связан с описанием грамматики, используемой для анализатора Python. Вместо одного узла для конструкции часто существуют несколько уровней вложенных узлов, которые вводятся правилами приоритета Python.
Дерево абстрактного синтаксиса создаётся модулем compiler.transformer. Преобразователь полагается на встроенный анализатор Python для генерации дерева конкретного синтаксиса. Он генерирует дерево абстрактного синтаксиса из дерева конкретного синтаксиса.
Модуль transformer был создан Грегом Штайном и Билом Туттом для экспериментального компилятора Python в C. Текущая версия содержит ряд изменений и улучшений, но основная форма абстрактного синтаксиса и преобразователя принадлежит Штейну и Тутту.
3.1. Узлы AST
Модуль compiler.ast генерируется из текстового файла, который описывает каждый тип узла и его элементы. Каждый тип узла представлен классом, который наследуется от абстрактного базового класса compiler.ast.Node и определяет набор именованных атрибутов для дочерних узлов.
-
class compiler.ast.Node -
Экземпляры
Nodeсоздаются автоматически генератором анализатора. Рекомендуемый интерфейс для конкретных экземпляровNode— использование общедоступных атрибутов для доступа к дочерним узлам. Общедоступный атрибут может быть связан с одним узлом или с последовательностью узлов, в зависимости от типаNode. Например, атрибутbasesузлаClassсвязан со списком узлов базового класса, а атрибутdocсвязан с одним узлом.Каждый экземпляр
Nodeимеет атрибутlineno, который может бытьNone. XXX Не уверен, каковы правила, какие узлы будут иметь полезное значение lineno.Все объекты
Nodeпредлагают следующие методы:-
getChildren() -
Возвращает уплощённый список дочерних узлов и объектов в порядке их появления. В частности, порядок узлов — порядок их появления в грамматике Python. Не все дочерние элементы — экземпляры
Node. Например, имена функций и классов — это обычные строки.
-
getChildNodes() -
Возвращает уплощённый список дочерних узлов в порядке их появления. Этот метод похож на
getChildren(), за исключением того, что он возвращает только те дочерние элементы, которые являются экземплярамиNode.
-
Два примера иллюстрируют общую структуру классов Node. Оператор while определяется следующим производственным правилом грамматики:
while_stmt: "while" expression ":" suite
["else" ":" suite]
Узел While имеет три атрибута: test, body, и else_. (Если естественное имя атрибута также является зарезервированным словом Python, его нельзя использовать в качестве имени атрибута. К слову добавляется нижнее подчёркивание, чтобы сделать его допустимым идентификатором, отсюда else_ вместо else.)
Оператор if более сложен, потому что может содержать несколько тестов.
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
Узел If определяет только два атрибута: tests и else_. Атрибут tests представляет собой последовательность пар выражения теста и тела следствия. Для каждой части if/elif существует одна пара. Первый элемент пары — это выражение теста. Второй элемент — это узел Stmt , содержащий код, который нужно выполнить, если тест верен.
Метод getChildren() объекта If возвращает плоский список дочерних узлов. Если есть три if/elif блока и нет else блока, то getChildren() вернёт список из шести элементов: первое выражение проверки, первое Stmt, второе выражение текста и так далее.
В следующей таблице перечислены каждый из подклассов Node, определённых в compiler.ast, и каждый из публичных атрибутов, доступных для их экземпляров. Значения большинства атрибутов сами являются экземплярами Node или последовательностями экземпляров. Если значение не является экземпляром, тип указан в комментарии. Атрибуты перечислены в порядке, в котором они возвращаются функциями getChildren() и getChildNodes().
Тип узла | Атрибут | Значение |
|---|---|---|
|
| левый операнд |
| правый операнд | |
|
| список операндов |
| атрибут в качестве цели присваивания | |
| выражение слева от точки | |
| имя атрибута, строка | |
| XXX | |
|
| список элементов списка, которым присваивается значение |
|
| присваиваемое имя |
| XXX | |
|
| список элементов кортежа, которым присваивается значение |
| выражение, которое нужно проверить | |
| значение | |
|
| список целей присваивания, по одному на знак равно |
| присваиваемое значение | |
|
| |
| ||
| ||
|
| |
|
| |
|
| |
|
| |
| ||
|
| выражение для вызываемого объекта |
| список аргументов | |
| расширенное значение *-arg | |
| расширенное значение **-arg | |
|
| имя класса, строка |
| список базовых классов | |
| строка документации, строка или | |
тело оператора класса | ||
|
| |
| ||
|
| |
| ||
|
| Список выражений декораторов функции |
|
| |
|
| |
|
| |
| ||
|
| |
|
| |
|
| |
| ||
|
| |
| ||
| ||
| ||
|
| |
| ||
|
|
|
| имя, используемое в def, строка | |
| список имён аргументов, как строки | |
| список значений по умолчанию | |
| xxx | |
| строка документации, строка или | |
тело функции | ||
| ||
|
| |
| ||
| ||
|
| |
| ||
|
| |
| ||
|
| |
|
| |
| ||
|
| |
|
| |
|
| |
| ||
|
| |
| ||
| ||
|
| |
| ||
|
| |
|
| |
| ||
|
| |
| ||
| ||
| ||
|
| |
| ||
|
| строка документации, строка или |
| тело модуля, | |
|
| |
| ||
|
| |
|
| |
|
| |
| ||
|
| |
| ||
|
| |
|
|
| |
| ||
|
| |
| ||
| ||
|
| |
|
| |
| ||
|
| |
| ||
| ||
| ||
|
| список инструкций |
|
| |
|
| |
| ||
|
| |
| ||
| ||
|
| |
| ||
| ||
|
| |
| ||
|
| |
|
| |
|
| |
| ||
| ||
| ||
|
| |
| ||
|
|
3.2. Узлы присваивания
Существует набор узлов, используемых для представления присваиваний. Каждая инструкция присваивания в исходном коде становится отдельным узлом Assign в дереве абстрактного синтаксиса (AST). Атрибут nodes представляет собой список, содержащий узел для каждого целевого объекта присваивания. Это необходимо, потому что присваивание может быть цепочным, например a = b =
2. Каждый Node в списке будет одним из следующих классов: AssAttr, AssList, AssName, или AssTuple.
Каждый узел присваивания целевого объекта описывает тип объекта, которому присваивается значение: AssName для простого имени, например a = 1. AssAttr для присваивания атрибута, например a.x = 1. AssList и AssTuple для распаковки списков и кортежей соответственно, например a, b, c = a_tuple.
Узлы присваивания целевых объектов также имеют атрибут flags, который указывает, используется ли узел для присваивания или в инструкции удаления. Атрибут AssName также используется для представления инструкции удаления, например del
x.
Когда выражение содержит несколько ссылок на атрибуты, инструкция присваивания или удаления будет содержать только один узел AssAttr – для конечной ссылки на атрибут. Другие ссылки на атрибуты будут представлены как узлы Getattr в атрибуте expr экземпляра AssAttr.
3.3. Примеры
В этом разделе представлены несколько простых примеров AST для Python-кода. Примеры демонстрируют, как использовать функцию parse(), как выглядит repr AST и как получить доступ к атрибутам узла AST.
Первый модуль определяет одну функцию. Предполагается, что она хранится в doublelib.py.
"""This is an example module.
This is the docstring.
"""
def double(x):
"Return twice the argument"
return x * 2
В приведенном ниже интерактивном сеансе интерпретатора я переформатировал длинные представленные AST для лучшей читабельности. В представлении AST используются имена классов без квалификаторов. Если вы хотите создать экземпляр из представления, вам нужно импортировать имена классов из модуля compiler.ast.
>>> import compiler
>>> mod = compiler.parseFile("doublelib.py")
>>> mod
Module('This is an example module.\n\nThis is the docstring.\n',
Stmt([Function(None, 'double', ['x'], [], 0,
'Return twice the argument',
Stmt([Return(Mul((Name('x'), Const(2))))]))]))
>>> from compiler.ast import *
>>> Module('This is an example module.\n\nThis is the docstring.\n',
... Stmt([Function(None, 'double', ['x'], [], 0,
... 'Return twice the argument',
... Stmt([Return(Mul((Name('x'), Const(2))))]))]))
Module('This is an example module.\n\nThis is the docstring.\n',
Stmt([Function(None, 'double', ['x'], [], 0,
'Return twice the argument',
Stmt([Return(Mul((Name('x'), Const(2))))]))]))
>>> mod.doc
'This is an example module.\n\nThis is the docstring.\n'
>>> for node in mod.node.nodes:
... print node
...
Function(None, 'double', ['x'], [], 0, 'Return twice the argument',
Stmt([Return(Mul((Name('x'), Const(2))))]))
>>> func = mod.node.nodes[0]
>>> func.code
Stmt([Return(Mul((Name('x'), Const(2))))])
4. Использование посетителей для обхода AST
Паттерн посетителя… Пакет compiler использует вариант паттерна посетителя, который использует возможности интроспекции Python для устранения необходимости в большей части инфраструктуры посетителя.
Посещаемые классы не нуждаются в программировании для принятия посетителей. Посетитель должен только определить методы посещения для классов, которые его интересуют; метод по умолчанию может обрабатывать все остальное.
XXX Магический метод visit() для посетителей.
-
compiler.visitor.walk(tree, visitor[, verbose])
-
class compiler.visitor.ASTVisitor -
Класс
ASTVisitorотвечает за обход дерева в правильном порядке. Обход начинается с вызоваpreorder(). Для каждого узла проверяется аргумент visitor вpreorder()на наличие метода с именем ‘visitNodeType’, где NodeType — имя класса узла, например, для узлаWhileвызывается методvisitWhile(). Если метод существует, он вызывается с узлом в качестве первого аргумента.Метод посетителя для конкретного типа узла может управлять тем, как посещаются дочерние узлы во время обхода. Класс
ASTVisitorизменяет аргумент visitor, добавляя метод посещения для конкретного дочернего узла. Если для определенного типа узла не найден посетитель, вызывается методdefault().Объекты
ASTVisitorимеют следующие методы:XXX описание дополнительных аргументов
-
default(node[, ...])
-
dispatch(node[, ...])
-
preorder(tree, visitor)
-
5. Генерация байткода
Генератор кода — это посетитель, который генерирует байткод. Каждый метод visit может вызвать метод emit() для генерации новой инструкции байткода. Основной генератор кода специализируется на модулях, классах и функциях. Ассемблер преобразует сгенерированные инструкции в низкоуровневый формат байткода. Он обрабатывает такие вещи, как генерацию списков констант, объектов кода и вычисление смещений переходов.
© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/2.7/library/compiler.html