Spec-Zone.ru › Python 2.7

Пакет компилятора 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 , содержащий код, который нужно выполнить, если тест верен.

END_OF_DOCUMENT_MARKER

Метод 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

расширенное значение *-arg

dstar_args

расширенное значение **-arg

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

END_OF_DOCUMENT_MARKER

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

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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API