Документация Python неофициальный перевод
Содержание страницы

33. Пакет компилятора PythonPython 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. Абстрактный синтаксис PythonPython Abstract Syntax

Модуль compiler.ast определяет абстрактный синтаксис для Python. В абстрактном синтаксическом дереве каждый узел представляет синтаксическую конструкцию. Корнем дерева является объект Module.

Абстрактный синтаксис предоставляет интерфейс более высокого уровня для разобранного исходного кода Python. Модуль parser и компилятор, написанный на C для интерпретатора Python, используют конкретное синтаксическое дерево. Конкретный синтаксис тесно связан с описанием грамматики, используемым парсером Python. Вместо одного узла для конструкции часто существует несколько уровней вложенных узлов, которые вводятся правилами приоритета Python.

Абстрактное синтаксическое дерево создаётся модулем compiler.transformer. Трансформер полагается на встроенный парсер Python для генерации конкретного синтаксического дерева. Из конкретного дерева он генерирует абстрактное синтаксическое дерево.

Модуль transformer был создан Грегом Стейном и Биллом Таттом для экспериментального компилятора из Python в C. Текущая версия содержит ряд модификаций и улучшений, но основная форма абстрактного синтаксиса и трансформера принадлежит Стейну и Татту.

33.3.1. Узлы ASTAST 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().

Тип узла

Атрибут

Значение

Add

left

левый операнд

right

правый операнд

And

nodes

список операндов

AssAttr

атрибут как цель присваивания

expr

выражение слева от точки

attrname

имя атрибута, строка

flags

XXX

AssList

nodes

список элементов списка, которым присваивается значение

AssName

name

имя, которому присваивается значение

flags

XXX

AssTuple

nodes

список элементов кортежа, которым присваивается значение

Assert

test

проверяемое выражение

fail

значение AssertionError

Assign

nodes

список целей присваивания, по одной на знак равенства

expr

присваиваемое значение

AugAssign

node

op

expr

Backquote

expr

Bitand

nodes

Bitor

nodes

Bitxor

nodes

Break

CallFunc

node

выражение для вызываемого объекта

args

список аргументов

star_args

значение расширенного *-аргумента

dstar_args

значение расширенного **-аргумента

Class

name

имя класса, строка

bases

список базовых классов

doc

строка документации, строка или None

code

тело инструкции класса

Compare

expr

ops

Const

value

Continue

Decorators

nodes

Список выражений декораторов функции

Dict

items

Discard

expr

Div

left

right

Ellipsis

Expression

node

Exec

expr

locals

globals

FloorDiv

left

right

For

assign

list

body

else_

From

modname

names

Function

decorators

Decorators или None

name

имя, используемое в def, строка

argnames

список имён аргументов в виде строк

defaults

список значений по умолчанию

flags

xxx

doc

строка документации, строка или None

code

тело функции

GenExpr

code

GenExprFor

assign

iter

ifs

GenExprIf

test

GenExprInner

expr

quals

Getattr

expr

attrname

Global

names

If

tests

else_

Import

names

Invert

expr

Keyword

name

expr

Lambda

argnames

defaults

flags

code

LeftShift

left

right

List

nodes

ListComp

expr

quals

ListCompFor

assign

list

ifs

ListCompIf

test

Mod

left

right

Module

doc

строка документации, строка или None

node

тело модуля, Stmt

Mul

left

right

Name

name

Not

expr

Or

nodes

Pass

Power

left

right

Print

nodes

dest

Printnl

nodes

dest

Raise

expr1

expr2

expr3

Return

value

RightShift

left

right

Slice

expr

flags

lower

upper

Sliceobj

nodes

список инструкций

Stmt

nodes

Sub

left

right

Subscript

expr

flags

subs

TryExcept

body

handlers

else_

TryFinally

body

final

Tuple

nodes

UnaryAdd

expr

UnarySub

expr

While

test

body

else_

With

expr

vars

body

Yield

value

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. Использование посетителей для обхода ASTUsing 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() для генерации нового байт-кода. Базовый генератор кода специализирован для модулей, классов и функций. Ассемблер преобразует сгенерированные инструкции в низкоуровневый формат байт-кода. Он обрабатывает такие задачи, как создание списков констант объектов кода и вычисление смещений переходов.