cmd — Поддержка линейно ориентированных командных интерпретаторов


В Cmd класс реализует простой фреймворк для создания линейно- ориентированных командных интерпретаторов. Они часто используются в тестовых контурах, административных инструментах и прототипах, которые впоследствии будут упакованы в более сложный интерфейс.

class cmd.Cmd(completekey='tab', stdin=None, stdout=None)

Cmd сущность или сущность подкласса линейно-ориентированных интерпретаторов фреймворка. Нет никаких веских оснований для создания экземпляра самого Cmd; он полезен как суперкласс интерпретатора, реализуемого самостоятельного класса, наследующего методы Cmd и инкапсулирующий методы действия.

Необязательный аргумент completekey - это readline имя ключа завершения; значение по умолчанию - Tab. Если completekey не None и readline доступен, автозавершение команды выполняется автоматически.

Опциональные аргументы stdin и stdout определяют объекты входного и выходного файла, которые будут использоваться сущностью Cmd или подклассом сущности для входа и выхода. Если не указано, то они по умолчанию будут sys.stdin и sys.stdout.

Если нужно передать используемый stdin, убедитесь, что атрибут use_rawinput сущности содержит значение False, в противном случае stdin будет проигнорирован.

Объекты Cmd

Сущность Cmd содержит следующие методы:

Cmd.cmdloop(intro=None)

Повторно выводит приглашение, принемает ввод, анализирует начальный префикс от полученного ввода и направляет к действие методам, передав им оставшуюся часть строки в качестве аргумента.

Необязательный аргумент - это баннер или интро-строка, которые должны быть выданы перед первым запросом (это переопределяется атрибутом intro класса).

Если модуль readline загружен, ввод автоматически наследует bash-подобное редактирование списка истории (например Control-P прокрутка назад к последней команде, Control-N вперед к следующей, Control-F перемещает курсор вправо без разрушения, Control-B перемещает курсор влево без разрушения и т.д.).

Конец файла на входе передается обратно в качестве строки 'EOF'.

Сущность интерпретатора распознает имя команды foo если и только если она содержит метод do_foo(). В специальном случае строка, начинающаяся с '?' символа, отправляется в метод do_help(). В другом особом случае строка, начинающаяся с '!' символа, отправляется в метод do_shell() (если такой метод определен).

Этот метод будет возвращать когда postcmd() метод возвращает истинное значение. Аргументом stop для postcmd() является возвращение значения из соответствующего do_*() метода команды.

Если автозавершение включено, завершение команд выполняется автоматически, а завершение команд args выполняется путем вызова complete_foo() с аргументами text, line, begidx и endidx. text - это строка префикс, который мы пытаемся сопоставить: все возвращённые совпадения должны начинаться с него. line является текущей входной строкой с удаленным начальным пробелом, begidx и endidx являются начальными и конечными индексами текста префикса, которые могут быть использованы для обеспечения различных автозавершений в зависимости от того, в какой позиции находится аргумент.

Все подклассы Cmd наследуют предопределенный do_help(). Этот метод, вызыванный с аргументом 'bar', вызывает соответствующий метод help_bar(), и если это отсутствует, печатает докстринг do_bar(), при наличии. Без аргументов do_help() перечисляет все доступные разделы справки (то есть все команды с соответствующими help_*() методами или командами, имеющими докстринги), а также перечисляет любые недокументированные команды.

Cmd.onecmd(str)

Интерпретировать аргумент, как если бы он был напечатан в ответ на приглашение. Это может быть переопределено, но обычно не обязательно; для получения полезной информации см. методы precmd() и postcmd(). Возвращаемое значение - это флаг, указывающий, должна ли прекращаться интерпретация команд интерпретатора. Если есть do_*() метод для команды str, возвращает значение возвращаемого метода, иначе возвращает значение из метода default().

Cmd.emptyline()

Вызываемый метод при вводе пустой строки в ответ на запрос. Если метод не переопределен, он повторяет последнюю введенную непустую команду.

Cmd.default(line)

Метод, вызываемый во входной строке, если префикс команды не распознан. Если этот метод не переопределен, он печатает сообщение об ошибке и возвращает управление.

Cmd.completedefault(text, line, begidx, endidx)

Метод, вызываемый для завершения входной строки, когда отсутствует специфичный для команды метод complete_*(). По умолчанию он возвращает пустой список.

Cmd.precmd(line)

Хук метод выполняемый непосредственно перед интерпретированием line командной строки, но после формирования и выдачи запроса на ввод. Этот метод является заглушкой в Cmd и предназначен для возможности переопределения его подклассом. Возвращает значение используемый как команда, выполняемая метод onecmd(); реализация precmd() может переписать команду или просто возвращает line без изменений.

Cmd.postcmd(stop, line)

Хук метод выполняемый сразу после завершения диспетчеризации команды. Этот метод является заглушкой в Cmd для реализации возможности переопределения его подклассом. line - командная строка, которая была выполнена, и stop - флаг, который указывает, будет ли выполнение завершено после вызова postcmd(); это будет возвращаемое значение метода onecmd(). Возвращает значение этого метод будет используемый как новое значение для внутреннего флага, который соответствует stop; возвращение false приведёт к продолжению интерпретации.

Cmd.preloop()

Хук метод выполняемый один раз при вызове cmdloop(). Этот метод является заглушкой в Cmd для реализации возможности переопределения его подклассом.

Cmd.postloop()

Хук метод выполняемый один раз, когда cmdloop() собирается вернуть результат. Этот метод является заглушкой в Cmd для реализации возможности переопределения его подклассом.

У сущности Cmd подкласса есть открытые переменные сущности:

Cmd.prompt

Запрос на ввод данных.

Cmd.identchars

Строка символов, принятых для префикса команды.

Cmd.lastcmd

Последний видимый префикс непустой команды.

Cmd.cmdqueue

Список входящих строк в очереди. Список cmdqueue проверяется в cmdloop() при необходимости нового ввода; если он является непустым, его элементы будут обрабатываться в порядке, как если бы они были введены в приглашение.

Cmd.intro

Строка релиза в виде приглашения или баннера. Может быть переопределена путём передачи методу cmdloop() аргумента.

Cmd.doc_header

Заголовок для вывода справки, содержащий раздел для задокументированных команд.

Cmd.misc_header

Заголовок, выдаваемый в случае, если в выходных данных справки имеется раздел для различных разделов справки (т.е. имеются help_*() методы без соответствующего do_*() методы).

Cmd.undoc_header

Заголовок релиза, если выходной документ справки содержит раздел для недокументированных команд (то есть существуют do_*() методы без соответствующих help_*() методов).

Cmd.ruler

Символ используемый для рисования линии-разделители под заголовками справочного сообщения. Если пустая, линия-разделитель не прорисовывается. Значение по умолчанию - '='.

Cmd.use_rawinput

Флаг, по умолчанию истинна. Если значение равно истина, cmdloop() использует input() для отображения приглашения и чтения следующей команды; при значении ложь используются sys.stdout.write() и sys.stdin.readline(). (Это означает, что при импорте readline в системах его поддерживающих, интерпретатор автоматически поддерживает Emacs-подобное редактирование строк и нажатие клавиш в журнале команд.)

Пример Cmd

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

В этом разделе представлен простой пример построения оболочки вокруг нескольких команд модуля turtle.

Основные команды turtle, такие как forward() добавлены к Cmd подкласс с методом do_forward(). Аргумент преобразуется в число и отправляется в модуль turtle. Докстринг используется в утилите справки, предоставляемая оболочкой.

Пример также включает в себя основное средство записи и воспроизведения, реализованное методом precmd(), которое отвечает за преобразование ввода в нижний регистр и запись команд в файл. do_playback() метод считывает файл и добавляет записанные команды в cmdqueue для немедленного воспроизведения:

import cmd, sys
from turtle import *

class TurtleShell(cmd.Cmd):
    intro = 'Добро пожаловать в черепаший шелл. Введите help или ? для получения перечня команд.\n'
    prompt = '(turtle) '
    file = None

    # ----- базовые черепашьи команды -----
    def do_forward(self, arg):
        'Move the turtle forward by the specified distance:  FORWARD 10'
        forward(*parse(arg))
    def do_right(self, arg):
        'Turn turtle right by given number of degrees:  RIGHT 20'
        right(*parse(arg))
    def do_left(self, arg):
        'Turn turtle left by given number of degrees:  LEFT 90'
        left(*parse(arg))
    def do_goto(self, arg):
        'Move turtle to an absolute position with changing orientation.  GOTO 100 200'
        goto(*parse(arg))
    def do_home(self, arg):
        'Return turtle to the home position:  HOME'
        home()
    def do_circle(self, arg):
        'Draw circle with given radius an options extent and steps:  CIRCLE 50'
        circle(*parse(arg))
    def do_position(self, arg):
        'Print the current turtle position:  POSITION'
        print('Current position is %d %d\n' % position())
    def do_heading(self, arg):
        'Print the current turtle heading in degrees:  HEADING'
        print('Current heading is %d\n' % (heading(),))
    def do_color(self, arg):
        'Set the color:  COLOR BLUE'
        color(arg.lower())
    def do_undo(self, arg):
        'Undo (repeatedly) the last turtle action(s):  UNDO'
    def do_reset(self, arg):
        'Clear the screen and return turtle to center:  RESET'
        reset()
    def do_bye(self, arg):
        'Stop recording, close the turtle window, and exit:  BYE'
        print('Thank you for using Turtle')
        self.close()
        bye()
        return True

    # ----- запись и воспроизведение -----
    def do_record(self, arg):
        'Save future commands to filename:  RECORD rose.cmd'
        self.file = open(arg, 'w')
    def do_playback(self, arg):
        'Playback commands from a file:  PLAYBACK rose.cmd'
        self.close()
        with open(arg) as f:
            self.cmdqueue.extend(f.read().splitlines())
    def precmd(self, line):
        line = line.lower()
        if self.file and 'playback' not in line:
            print(line, file=self.file)
        return line
    def close(self):
        if self.file:
            self.file.close()
            self.file = None

def parse(arg):
    'Преобразовать последовательность из нулей или больших чисел в кортеж аргумента'
    return tuple(map(int, arg.split()))

if __name__ == '__main__':
    TurtleShell().cmdloop()

Вот пример сеанса с черепашьей оболочкой, показывающей функции справки, используя пустые строки для повторения команд, а также простую запись и средство воспроизведения:

Добро пожаловать в черепаший шелл. Введите help или ? для получения перечня команд.\n

(turtle) ?

Documented commands (type help <topic>):
========================================
bye     color    goto     home  playback  record  right
circle  forward  heading  left  position  reset   undo

(turtle) help forward
Move the turtle forward by the specified distance:  FORWARD 10
(turtle) record spiral.cmd
(turtle) position
Current position is 0 0

(turtle) heading
Current heading is 0

(turtle) reset
(turtle) circle 20
(turtle) right 30
(turtle) circle 40
(turtle) right 30
(turtle) circle 60
(turtle) right 30
(turtle) circle 80
(turtle) right 30
(turtle) circle 100
(turtle) right 30
(turtle) circle 120
(turtle) right 30
(turtle) circle 120
(turtle) heading
Current heading is 180

(turtle) forward 100
(turtle)
(turtle) right 90
(turtle) forward 100
(turtle)
(turtle) right 90
(turtle) forward 400
(turtle) right 90
(turtle) forward 500
(turtle) right 90
(turtle) forward 400
(turtle) right 90
(turtle) forward 300
(turtle) playback spiral.cmd
Current position is 0 0

Current heading is 0

Current heading is 180

(turtle) bye
Thank you for using Turtle