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

parser – Доступ к деревьям разбора Pythonparser – Access Python parse trees

Модуль parser предоставляет интерфейс к внутреннему парсеру Python и компилятору байт-кода. Основная цель этого интерфейса – позволить коду Python редактировать дерево разбора выражения Python и создавать из него исполняемый код. Это лучше, чем пытаться разобрать и изменить произвольный фрагмент кода Python как строку, потому что разбор выполняется так же, как и для кода, образующего приложение. Кроме того, это быстрее.

Примечание

Начиная с Python 2.5 гораздо удобнее внедряться на этапе генерации и компиляции абстрактного синтаксического дерева (AST), используя модуль ast.

Следует отметить несколько важных моментов, касающихся этого модуля, которые важны для использования создаваемых структур данных. Это не учебник по редактированию деревьев разбора для кода Python, но здесь приведены некоторые примеры использования модуля parser.

Самое главное – требуется хорошее понимание грамматики Python, которую обрабатывает внутренний парсер. Полную информацию о синтаксисе языка можно найти в Справочнике по языку Python. Сам парсер создаётся по спецификации грамматики, определённой в файле Grammar/Grammar в стандартной поставке Python. Деревья разбора, хранящиеся в объектах ST, создаваемых этим модулем, – это фактический результат работы внутреннего парсера при вызове функций expr() или suite(), описанных ниже. Объекты ST, созданные функцией sequence2st(), точно воспроизводят эти структуры. Имейте в виду, что значения последовательностей, считающиеся «правильными», могут меняться от версии Python к версии по мере изменения формальной грамматики языка. Однако перенос кода из одной версии Python в другую в виде исходного текста всегда позволит создать правильные деревья разбора в целевой версии, с единственным ограничением: при переходе на более старую версию интерпретатора более новые языковые конструкции не поддерживаются. Деревья разбора обычно не совместимы между версиями, тогда как исходный код всегда был совместим вперёд.

Каждый элемент последовательностей, возвращаемых st2list() или st2tuple(), имеет простую форму. Последовательности, представляющие нетерминальные элементы грамматики, всегда имеют длину больше единицы. Первый элемент – целое число, идентифицирующее продукцию в грамматике. Этим целым числам даны символьные имена в заголовочном файле C Include/graminit.h и в модуле Python symbol. Каждый дополнительный элемент последовательности представляет компонент продукции, распознанный во входной строке: это всегда последовательности, имеющие ту же форму, что и родительская. Важная особенность этой структуры, которую следует отметить: ключевые слова, используемые для идентификации типа родительского узла, например ключевое слово if в if_stmt, включаются в дерево узлов без какой-либо специальной обработки. Например, ключевое слово if представлено кортежем (1, 'if'), где 1 – числовое значение, связанное со всеми токенами NAME, включая имена переменных и функций, определённые пользователем. В альтернативной форме, возвращаемой при запросе информации о номерах строк, тот же токен может быть представлен как (1, 'if', 12), где 12 обозначает номер строки, в которой был найден терминальный символ.

Терминальные элементы представлены почти так же, но без дочерних элементов и с добавлением исходного текста, который был распознан. Пример ключевого слова if выше показателен. Различные типы терминальных символов определены в заголовочном файле C Include/token.h и в модуле Python token.

ST-объекты не обязательны для поддержки функциональности этого модуля, но предоставляются для трёх целей: позволить приложению распределить затраты на обработку сложных деревьев разбора, предоставить представление дерева разбора, экономящее память по сравнению с представлением в виде списков или кортежей Python, и облегчить создание дополнительных модулей на C, работающих с деревьями разбора. В Python можно создать простой класс-«обёртку», чтобы скрыть использование ST-объектов.

Модуль parser определяет функции для нескольких различных целей. Наиболее важные цели – создание объектов ST и преобразование объектов ST в другие представления, такие как деревья разбора и скомпилированные объекты кода, но также есть функции, которые служат для запроса типа дерева разбора, представленного объектом ST.

См. также

Модуль symbol
Полезные константы, представляющие внутренние узлы дерева разбора.
Модуль token
Полезные константы, представляющие листовые узлы дерева разбора, и функции для проверки значений узлов.

Создание ST-объектовCreating ST Objects

Объекты ST могут быть созданы из исходного кода или из дерева разбора. При создании объекта ST из исходного кода используются разные функции для создания форм 'eval' и 'exec'.

parser.expr(source)
Функция expr() разбирает параметр source так, как если бы он был входными данными для compile(source, 'file.py', 'eval'). Если разбор прошёл успешно, создаётся объект ST для хранения внутреннего представления дерева разбора; в противном случае выбрасывается соответствующее исключение.
parser.suite(source)
Функция suite() разбирает параметр source так, как если бы он был входными данными для compile(source, 'file.py', 'exec'). Если разбор прошёл успешно, создаётся объект ST для хранения внутреннего представления дерева разбора; в противном случае выбрасывается соответствующее исключение.
parser.sequence2st(sequence)

Эта функция принимает дерево разбора, представленное в виде последовательности, и строит его внутреннее представление, если это возможно. Если удаётся проверить, что дерево соответствует грамматике Python и все узлы имеют допустимые типы для данной версии Python, создаётся объект ST на основе внутреннего представления и возвращается вызывающему коду. Если при создании внутреннего представления возникла проблема или дерево не прошло проверку, выбрасывается исключение ParserError. Не следует предполагать, что объект ST, созданный таким образом, скомпилируется корректно; обычные исключения, возникающие при компиляции, всё ещё могут быть инициированы при передаче объекта ST в compilest(). Это может указывать на проблемы, не связанные с синтаксисом (например, исключение MemoryError), но также может быть связано с конструкциями вроде результата разбора del f(0), который пропускается синтаксическим анализатором Python, но проверяется компилятором байт-кода.

Последовательности, представляющие терминальные токены, могут быть представлены либо как двухэлементные списки вида (1, 'name'), либо как трёхэлементные списки вида (1, 'name', 56). Если присутствует третий элемент, он считается допустимым номером строки. Номер строки может быть указан для любого подмножества терминальных символов во входном дереве.

parser.tuple2st(sequence)
Это та же функция, что и sequence2st(). Эта точка входа сохранена для обратной совместимости.

Преобразование ST-объектовConverting ST Objects

ST-объекты, независимо от входных данных, использованных для их создания, могут быть преобразованы в деревья разбора, представленные в виде списков или кортежей, или скомпилированы в исполняемые объекты кода. Деревья разбора могут быть извлечены с информацией о номерах строк или без неё.

parser.st2list(st[, line_info])

Эта функция принимает объект ST от вызывающего в параметре st и возвращает список Python, представляющий эквивалентное дерево разбора. Полученное представление в виде списка можно использовать для просмотра или создания нового дерева разбора в форме списка. Эта функция не завершается ошибкой, пока есть достаточно памяти для построения спискового представления. Если дерево разбора будет использоваться только для просмотра, следует использовать st2tuple(), чтобы уменьшить потребление памяти и фрагментацию. Когда требуется представление в виде списка, эта функция значительно быстрее, чем получение кортежного представления и его преобразование во вложенные списки.

Если line_info равен true, для всех терминальных токенов будет включена информация о номере строки в качестве третьего элемента списка, представляющего токен. Обратите внимание, что указанный номер строки определяет строку, в которой токен заканчивается. Эта информация опускается, если флаг равен false или опущен.

parser.st2tuple(st[, line_info])

Эта функция принимает объект ST от вызывающего в параметре st и возвращает кортеж Python, представляющий эквивалентное дерево разбора. За исключением того, что возвращается кортеж вместо списка, эта функция идентична st2list().

Если line_info равен true, для всех терминальных токенов будет включена информация о номере строки в качестве третьего элемента списка, представляющего токен. Эта информация опускается, если флаг равен false или опущен.

parser.compilest(st[, filename='<syntax-tree>'])

Компилятор байт-кода Python может быть вызван для объекта ST, чтобы получить объекты кода, которые можно использовать как часть вызова встроенных функций exec() или eval(). Эта функция предоставляет интерфейс к компилятору, передавая внутреннее дерево разбора из st парсеру, используя имя исходного файла, заданное параметром filename. Значение по умолчанию для filename указывает, что исходный код был объектом ST.

Компиляция объекта ST может привести к исключениям, связанным с компиляцией; примером может служить SyntaxError, вызванная деревом разбора для del f(0): эта инструкция считается допустимой в формальной грамматике Python, но не является допустимой языковой конструкцией. Исключение SyntaxError, возбуждаемое в этой ситуации, обычно генерируется компилятором байт-кода Python, поэтому оно может быть возбуждено на этом этапе модулем parser. Большинство причин сбоя компиляции можно диагностировать программно, исследуя дерево разбора.

Запросы к объектам STQueries on ST Objects

Предоставляются две функции, которые позволяют приложению определить, был ли объект ST создан как выражение или как набор инструкций. Ни одну из этих функций нельзя использовать для определения того, был ли объект ST создан из исходного кода с помощью expr() или suite(), или из дерева разбора с помощью sequence2st().

parser.isexpr(st)

Если st представляет форму 'eval', эта функция возвращает истину, в противном случае – ложь. Это полезно, поскольку объекты кода обычно нельзя запросить на эту информацию с помощью существующих встроенных функций. Обратите внимание, что объекты кода, созданные compilest(), также нельзя так запросить, и они идентичны объектам, созданным встроенной функцией compile().

parser.issuite(st)
Эта функция аналогична isexpr() в том, что она сообщает, представляет ли объект ST форму 'exec', обычно называемую «набором». Не стоит считать, что эта функция эквивалентна not isexpr(st), поскольку в будущем могут поддерживаться дополнительные синтаксические фрагменты.

Исключения и обработка ошибокExceptions and Error Handling

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

exception parser.ParserError
Исключение, возникающее при сбое в модуле parser. Обычно оно порождается из-за ошибок валидации, а не из-за встроенного SyntaxError, который выбрасывается при обычном разборе. Аргументом исключения является либо строка с описанием причины сбоя, либо кортеж, содержащий последовательность, вызвавшую сбой (из дерева разбора, переданного в sequence2st()), и пояснительную строку. Вызовы sequence2st() должны уметь обрабатывать оба типа исключений, в то время как вызовам других функций модуля достаточно знать только о простых строковых значениях.

Обратите внимание, что функции compilest(), expr() и suite() могут выбрасывать исключения, которые обычно возникают в процессе разбора и компиляции. К ним относятся встроенные исключения MemoryError, OverflowError, SyntaxError и SystemError. В этих случаях данные исключения несут весь обычный для них смысл. Обратитесь к описаниям каждой функции за подробной информацией.

Объекты STST Objects

Между объектами ST поддерживаются сравнения на порядок и равенство. Также поддерживается сериализация объектов ST с помощью модуля pickle.

parser.STType
Тип объектов, возвращаемых функциями expr(), suite() и sequence2st().

Объекты ST имеют следующие методы:

ST.compile([filename])
То же, что и compilest(st, filename).
ST.isexpr()
То же, что и isexpr(st).
ST.issuite()
То же, что и issuite(st).
ST.tolist([line_info])
То же, что и st2list(st, line_info).
ST.totuple([line_info])
То же, что и st2tuple(st, line_info).

ПримерыExamples

Модуль parser позволяет выполнять операции над деревом разбора исходного кода Python до генерации байт-кода, а также предоставляет возможность инспектировать дерево разбора для сбора информации. Приведены два примера. Простой пример демонстрирует эмуляцию встроенной функции compile(), а сложный пример показывает использование дерева разбора для извлечения информации.

Эмуляция compile()Emulation of compile()

Хотя между разбором и генерацией байт-кода может выполняться множество полезных операций, самая простая операция – ничего не делать. Для этой цели использование модуля parser для создания промежуточной структуры данных эквивалентно следующему коду

>>> code = compile('a + 5', 'file.py', 'eval')
>>> a = 5
>>> eval(code)
10

Эквивалентная операция с использованием модуля parser несколько длиннее и позволяет сохранить промежуточное внутреннее дерево разбора в виде объекта ST:

>>> import parser
>>> st = parser.expr('a + 5')
>>> code = st.compile('file.py')
>>> a = 5
>>> eval(code)
10

Приложение, которому нужны как объекты ST, так и объекты кода, может упаковать этот код в готовые функции:

import parser

def load_suite(source_string):
    st = parser.suite(source_string)
    return st, st.compile()

def load_expression(source_string):
    st = parser.expr(source_string)
    return st, st.compile()

Извлечение информацииInformation Discovery

Некоторым приложениям полезен прямой доступ к дереву разбора. Оставшаяся часть этого раздела демонстрирует, как с помощью дерева разбора получить доступ к документации модуля, определённой в строках документации, без необходимости загружать исследуемый код в работающий интерпретатор через import. Это может быть очень полезно для анализа недоверенного кода.

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

Динамическая природа Python предоставляет программисту большую гибкость, но большинству модулей требуется лишь ограниченная её мера при определении классов, функций и методов. В этом примере будут рассматриваться только те определения, которые находятся на верхнем уровне своего контекста, например, функция, определённая оператором def в нулевой колонке модуля, но не функция, определённая внутри ветви конструкции if ... else, хотя в некоторых ситуациях для этого есть веские причины. Вложенность определений будет обрабатываться кодом, разработанным в примере.

Чтобы построить методы извлечения информации верхнего уровня, нужно знать, как устроено дерево разбора и какая его часть нас на самом деле интересует. Python использует довольно глубокое дерево разбора, поэтому существует множество промежуточных узлов. Важно прочитать и понять формальную грамматику, используемую Python. Она задана в файле Grammar/Grammar дистрибутива. Рассмотрим простейший интересующий нас случай при поиске строк документации: модуль, состоящий только из строки документации и ничего больше. (См. файл docstring.py.)

"""Некоторая документация.
"""

Используя интерпретатор для просмотра дерева разбора, мы обнаруживаем ошеломляющую массу чисел и скобок, с документацией, глубоко погребённой во вложенных кортежах.

>>> import parser
>>> import pprint
>>> st = parser.suite(open('docstring.py').read())
>>> tup = st.totuple()
>>> pprint.pprint(tup)
(257,
 (264,
  (265,
   (266,
    (267,
     (307,
      (287,
       (288,
        (289,
         (290,
          (292,
           (293,
            (294,
             (295,
              (296,
               (297,
                (298,
                 (299,
                  (300, (3, '"""Some documentation.\n"""'))))))))))))))))),
   (4, ''))),
 (4, ''),
 (0, ''))

Числа в первом элементе каждого узла дерева – это типы узлов; они напрямую соответствуют терминальным и нетерминальным символам грамматики. К сожалению, во внутреннем представлении они представлены целыми числами, и генерируемые структуры Python этого не меняют. Однако модули symbol и token предоставляют символьные имена для типов узлов и словари, которые отображают целые числа в символьные имена типов узлов.

В выходных данных, представленных выше, внешний кортеж содержит четыре элемента: целое число 257 и три дополнительных кортежа. Тип узла 257 имеет символическое имя file_input. Каждый из этих внутренних кортежей содержит целое число в качестве первого элемента; эти целые числа – 264, 4 и 0 – представляют типы узлов stmt, NEWLINE и ENDMARKER соответственно. Обратите внимание, что эти значения могут меняться в зависимости от используемой версии Python; подробности отображения см. в symbol.py и token.py. Должно быть достаточно ясно, что внешний узел связан в первую очередь с исходным кодом, а не с содержимым файла, и пока его можно не учитывать. Узел stmt гораздо интереснее. В частности, все строки документации находятся в поддеревьях, которые формируются точно так же, как и этот узел, с той лишь разницей, что строки различаются. Связь между строкой документации в подобном дереве и определяемой сущностью (классом, функцией или модулем), которую она описывает, определяется положением поддерева строки документации в дереве, определяющем описываемую структуру.

Заменяя фактическую строку документации на что-то, обозначающее переменную составляющую дерева, мы получаем возможность использовать простой подход сопоставления с образцом для проверки любого заданного поддерева на соответствие общему шаблону строк документации. Поскольку пример демонстрирует извлечение информации, можно смело потребовать, чтобы дерево было представлено в форме кортежа, а не списка, что позволяет использовать простое представление переменной в виде ['variable_name']. Простая рекурсивная функция может реализовать сопоставление с образцом, возвращая логическое значение и словарь соответствий имён переменных значениям. (См. файл example.py.)

def match(pattern, data, vars=None):
    if vars is None:
        vars = {}
    if isinstance(pattern, list):
        vars[pattern[0]] = data
        return True, vars
    if not instance(pattern, tuple):
        return (pattern == data), vars
    if len(data) != len(pattern):
        return False, vars
    for pattern, data in zip(pattern, data):
        same, vars = match(pattern, data, vars)
        if not same:
            break
    return same, vars

Благодаря этому простому представлению синтаксических переменных и символических типов узлов шаблон для поддеревьев-кандидатов строк документации становится вполне читаемым. (См. файл example.py.)

import symbol
import token

DOCSTRING_STMT_PATTERN = (
    symbol.stmt,
    (symbol.simple_stmt,
     (symbol.small_stmt,
      (symbol.expr_stmt,
       (symbol.testlist,
        (symbol.test,
         (symbol.and_test,
          (symbol.not_test,
           (symbol.comparison,
            (symbol.expr,
             (symbol.xor_expr,
              (symbol.and_expr,
               (symbol.shift_expr,
                (symbol.arith_expr,
                 (symbol.term,
                  (symbol.factor,
                   (symbol.power,
                    (symbol.atom,
                     (token.STRING, ['docstring'])
                     )))))))))))))))),
     (token.NEWLINE, '')
     ))

Используя функцию match() с этим шаблоном, извлечь строку документации модуля из ранее созданного дерева разбора очень просто:

>>> found, vars = match(DOCSTRING_STMT_PATTERN, tup[1])
>>> found
True
>>> vars
{'docstring': '"""Some documentation.\n"""'}

Как только конкретные данные могут быть извлечены из места, где они ожидаются, возникает вопрос о том, где можно ожидать информацию. При работе со строками документации ответ довольно прост: строка документации – это первый узел stmt в блоке кода (типы узлов file_input или suite). Модуль состоит из одного узла file_input, а определения класса и функции содержат ровно один узел suite. Классы и функции легко идентифицируются как поддеревья узлов блока кода, которые начинаются с (stmt, (compound_stmt, (classdef, ... или (stmt, (compound_stmt, (funcdef, .... Обратите внимание, что эти поддеревья не могут быть сопоставлены с помощью match(), поскольку она не поддерживает сопоставление нескольких дочерних узлов без учёта их количества. Более сложная функция сопоставления могла бы преодолеть это ограничение, но для примера этого достаточно.

Имея возможность определить, может ли оператор быть строкой документации, и извлечь из него саму строку, необходимо выполнить некоторую работу по обходу дерева разбора для всего модуля, извлечению информации об именах, определённых в каждом контексте модуля, и связыванию строк документации с этими именами. Код для выполнения этой работы несложен, но требует некоторых пояснений.

Публичный интерфейс классов прост и, вероятно, должен быть несколько более гибким. Каждый «основной» блок модуля описывается объектом, предоставляющим несколько методов для запросов и конструктор, который принимает как минимум поддерево полного дерева разбора, которое он представляет. Конструктор ModuleInfo принимает необязательный параметр name, так как иначе он не может определить имя модуля.

Публичные классы включают ClassInfo, FunctionInfo и ModuleInfo. Все объекты предоставляют методы get_name(), get_docstring(), get_class_names() и get_class_info(). Объекты ClassInfo поддерживают get_method_names() и get_method_info(), тогда как другие классы предоставляют get_function_names() и get_function_info().

В каждой из форм блока кода, которые представляют публичные классы, большая часть требуемой информации имеет одинаковую форму и доступ к ней осуществляется одинаково, с тем отличием, что в классах функции, определённые на верхнем уровне, называются «методами». Поскольку различие в терминологии отражает реальное семантическое отличие от функций, определённых вне класса, реализация должна поддерживать это различие. Следовательно, большая часть функциональности публичных классов может быть реализована в общем базовом классе SuiteInfoBase, а аксессоры для информации о функциях и методах предоставляются отдельно. Обратите внимание, что существует только один класс, представляющий информацию о функциях и методах; это соответствует использованию оператора def для определения обоих типов элементов.

Большинство функций доступа объявлены в SuiteInfoBase и не требуют переопределения в подклассах. Более важно то, что извлечение большей части информации из дерева разбора выполняется методом, вызываемым конструктором SuiteInfoBase. Пример кода для большинства классов понятен при чтении вместе с формальной грамматикой, но метод, который рекурсивно создаёт новые информационные объекты, требует дополнительного изучения. Вот соответствующая часть определения SuiteInfoBase из example.py:

class SuiteInfoBase:
    _docstring = ''
    _name = ''

    def __init__(self, tree = None):
        self._class_info = {}
        self._function_info = {}
        if tree:
            self._extract_info(tree)

    def _extract_info(self, tree):
        # extract docstring
        if len(tree) == 2:
            found, vars = match(DOCSTRING_STMT_PATTERN[1], tree[1])
        else:
            found, vars = match(DOCSTRING_STMT_PATTERN, tree[3])
        if found:
            self._docstring = eval(vars['docstring'])
        # discover inner definitions
        for node in tree[1:]:
            found, vars = match(COMPOUND_STMT_PATTERN, node)
            if found:
                cstmt = vars['compound']
                if cstmt[0] == symbol.funcdef:
                    name = cstmt[2][1]
                    self._function_info[name] = FunctionInfo(cstmt)
                elif cstmt[0] == symbol.classdef:
                    name = cstmt[2][1]
                    self._class_info[name] = ClassInfo(cstmt)

После инициализации некоторого внутреннего состояния конструктор вызывает метод _extract_info(). Этот метод выполняет основную часть извлечения информации, которая происходит во всём примере. Извлечение состоит из двух отдельных фаз: нахождение строки документации для переданного дерева разбора и обнаружение дополнительных определений в блоке кода, представленном этим деревом разбора.

Первая проверка if определяет, является ли вложенный блок кода «краткой формой» или «полной формой». Краткая форма используется, когда блок кода находится на той же строке, что и определение блока кода, например:

def square(x): "Square an argument."; return x ** 2

тогда как полная форма использует блок с отступом и допускает вложенные определения:

def make_power(exp):
    "Make a function that raises an argument to the exponent `exp`."
    def raiser(x, y=exp):
        return x ** y
    return raiser

При использовании краткой формы блок кода может содержать строку документации как первый и, возможно, единственный элемент small_stmt. Извлечение такой строки документации несколько отличается и требует лишь части полного шаблона, используемого в более распространённом случае. В текущей реализации строка документации будет найдена, только если в узле simple_stmt есть только один узел small_stmt. Поскольку большинство функций и методов, использующих краткую форму, не предоставляют строки документации, это можно считать достаточным. Извлечение строки документации выполняется с помощью функции match(), как описано выше, и значение строки документации сохраняется как атрибут объекта SuiteInfoBase.

После извлечения строки документации простой алгоритм поиска определений работает с узлами stmt узла suite. Особый случай краткой формы не проверяется; поскольку в краткой форме нет узлов stmt, алгоритм молча пропустит единственный узел simple_stmt и корректно не обнаружит никаких вложенных определений.

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

Публичные классы предоставляют любые необходимые методы доступа, которые являются более специфичными, чем те, что предоставляет класс SuiteInfoBase, но сам алгоритм извлечения остаётся общим для всех форм блоков кода. Для извлечения полного набора информации из исходного файла можно использовать функцию высокого уровня. (См. файл example.py.)

def get_docs(fileName):
    import os
    import parser

    source = open(fileName).read()
    basename = os.path.basename(os.path.splitext(fileName)[0])
    st = parser.suite(source)
    return ModuleInfo(st.totuple(), basename)

Это обеспечивает простой в использовании интерфейс к документации модуля. Если требуется информация, которая не извлекается кодом из этого примера, код может быть расширен в чётко определённых точках для предоставления дополнительных возможностей.