Содержание страницы
33. Пакет компилятора Python¶Python compiler package
Устарело с версии 2.6: Пакет compiler удалён в Python 3.
Пакет компилятора Python – это инструмент для анализа исходного кода Python и генерации байт-кода Python. Компилятор содержит библиотеки для построения абстрактного синтаксического дерева из исходного кода Python и для генерации Python байт-кода из дерева.
Пакет compiler – это транслятор исходного кода Python в байт-код, написанный на
Python. Он использует встроенный парсер и стандартный модуль parser для
построения конкретного синтаксического дерева. Это дерево используется для построения абстрактного
синтаксического дерева (AST) и затем байт-кода Python.
Полная функциональность пакета дублирует встроенный компилятор, поставляемый с интерпретатором Python. Он предназначен для почти точного совпадения с его поведением. Зачем реализовывать ещё один компилятор, который делает то же самое? Пакет полезен для различных целей. Его можно модифицировать проще, чем встроенный компилятор. Сгенерированное им AST полезно для анализа исходного кода Python.
В этой главе объясняется, как работают различные компоненты пакета compiler.
В ней сочетаются справочный материал и учебное пособие.
33.1. Базовый интерфейс¶The basic interface
Верхний уровень пакета определяет четыре функции. При импорте
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 влияют на операторы, связанные с future, но пока не поддерживаются.
-
compiler.compileFile(source)¶ Компилирует файл source и генерирует файл .pyc.
Пакет compiler содержит следующие модули: ast,
consts, future, misc, pyassem, pycodegen,
symbols, transformer и visitor.
33.2. Ограничения¶Limitations
В проверке ошибок пакета компилятора есть некоторые проблемы. Интерпретатор
обнаруживает синтаксические ошибки в два отдельных этапа. Один набор ошибок
обнаруживается парсером интерпретатора, другой – компилятором. Пакет компилятора
полагается на парсер интерпретатора, поэтому первый этап проверки ошибок он получает
«бесплатно». Второй этап он реализует самостоятельно, и эта реализация неполна.
Например, пакет компилятора не возбуждает ошибку, если имя встречается более одного раза
в списке аргументов: def f(x, x):
...
Будущая версия компилятора должна исправить эти проблемы.
33.3. Абстрактный синтаксис Python¶Python Abstract Syntax
Модуль compiler.ast определяет абстрактный синтаксис для Python. В
абстрактном синтаксическом дереве каждый узел представляет синтаксическую конструкцию. Корнем
дерева является объект Module.
Абстрактный синтаксис предоставляет интерфейс более высокого уровня для разобранного исходного кода
Python. Модуль parser и компилятор, написанный на C для интерпретатора
Python, используют конкретное синтаксическое дерево. Конкретный синтаксис тесно связан с
описанием грамматики, используемым парсером Python. Вместо одного узла для конструкции
часто существует несколько уровней вложенных узлов, которые вводятся правилами приоритета
Python.
Абстрактное синтаксическое дерево создаётся модулем compiler.transformer.
Трансформер полагается на встроенный парсер Python для генерации конкретного
синтаксического дерева. Из конкретного дерева он генерирует абстрактное синтаксическое дерево.
Модуль transformer был создан Грегом Стейном и Биллом Таттом для
экспериментального компилятора из Python в C. Текущая версия содержит ряд
модификаций и улучшений, но основная форма абстрактного синтаксиса и
трансформера принадлежит Стейну и Татту.
33.3.1. Узлы AST¶AST Nodes
Модуль 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 |
|
|
|
список элементов кортежа, которым присваивается значение |
|
проверяемое выражение |
|
|
значение
|
|
|
|
список целей присваивания, по одной на знак равенства |
|
присваиваемое значение |
|
|
|
|
|
||
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
выражение для вызываемого объекта |
|
список аргументов |
|
|
значение расширенного *-аргумента |
|
|
значение расширенного **-аргумента |
|
|
|
имя класса, строка |
|
список базовых классов |
|
|
строка документации, строка или
|
|
тело инструкции класса |
||
|
|
|
|
||
|
|
|
|
||
|
|
Список выражений декораторов функции |
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
||
|
||
|
||
|
|
|
|
||
|
|
|
|
имя, используемое в def, строка |
|
|
список имён аргументов в виде строк |
|
|
список значений по умолчанию |
|
|
xxx |
|
|
строка документации, строка или
|
|
тело функции |
||
|
||
|
|
|
|
||
|
||
|
|
|
|
||
|
|
|
|
||
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
||
|
||
|
|
|
|
||
|
|
|
|
|
|
|
||
|
|
|
|
||
|
||
|
||
|
|
|
|
||
|
|
строка документации, строка или
|
|
тело модуля,
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
||
|
|
|
|
||
|
|
|
|
||
|
|
|
|
||
|
||
|
|
|
|
|
|
|
||
|
|
|
|
||
|
||
|
||
|
|
список инструкций |
|
|
|
|
|
|
|
||
|
|
|
|
||
|
||
|
|
|
|
||
|
||
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
||
|
||
|
||
|
|
|
|
||
|
|
33.3.2. Узлы присваивания¶Assignment nodes
Существует набор узлов, используемых для представления присваиваний. Каждая инструкция
присваивания в исходном коде становится одним узлом 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.
33.3.3. Примеры¶Examples
В этом разделе приведено несколько простых примеров AST для исходного кода Python. Примеры демонстрируют, как использовать функцию parse(), как выглядит repr AST и как получать доступ к атрибутам узла AST.
Первый модуль определяет одну функцию. Предположим, он сохранён в doublelib.py.
"""Это пример модуля.
Это докстринг.
"""
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))))])
33.4. Использование посетителей для обхода AST¶Using Visitors to Walk ASTs
Шаблон «Посетитель» – это … Пакет compiler использует вариант шаблона «Посетитель»,
который использует возможности интроспекции Python для
устранения необходимости в большей части инфраструктуры посетителя.
Посещаемые классы не нужно программировать на приём посетителей. Посетителю достаточно определить методы visit только для тех классов, которые его интересуют; метод visit по умолчанию обработает остальные.
XXX Магический метод visit() для посетителей.
-
compiler.visitor.walk(tree, visitor[, verbose])¶
-
class
compiler.visitor.ASTVisitor¶ ASTVisitorотвечает за обход дерева в правильном порядке. Обход начинается с вызоваpreorder(). Для каждого узла он проверяет аргумент посетитель вpreorder()на наличие метода с именем ‘visitNodeType,’ где NodeType – это имя класса узла, например для узлаWhileбудет вызван методvisitWhile(). Если метод существует, он вызывается с узлом в качестве первого аргумента.Метод посетителя для конкретного типа узла может управлять тем, как дочерние узлы посещаются во время обхода.
ASTVisitorизменяет аргумент посетителя, добавляя к нему метод visit; этот метод можно использовать для посещения конкретного дочернего узла. Если для определённого типа узла посетитель не найден, вызывается методdefault().Объекты
ASTVisitorимеют следующие методы:XXX описать дополнительные аргументы
-
default(node[, ...])¶
-
dispatch(node[, ...])¶
-
preorder(tree, visitor)¶
-
33.5. Генерация байт-кода¶Bytecode Generation
Генератор кода – это посетитель, который генерирует байт-коды. Каждый метод visit может
вызывать метод emit() для генерации нового байт-кода. Базовый генератор кода
специализирован для модулей, классов и функций. Ассемблер преобразует сгенерированные
инструкции в низкоуровневый формат байт-кода. Он обрабатывает такие задачи, как
создание списков констант объектов кода и вычисление смещений переходов.