Лучшая обработка KeyboardInterrupt в интерпретаторе командной строки cmd.Cmd
При использовании python cmd.Cmd для создания пользовательского интерфейса командной строки, как мне сообщить обработчику прервать текущую строку и дать мне новое приглашение?
Вот минимальный пример:
# console_min.py
# run: 'python console_min.py'
import cmd, signal
class Console(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self)
self.prompt = "[test] "
signal.signal(signal.SIGINT, handler)
def do_exit(self, args):
return -1
def do_EOF(self, args):
return self.do_exit(args)
def preloop(self):
cmd.Cmd.preloop(self)
self._hist = []
self._locals = {}
self._globals = {}
def postloop(self):
cmd.Cmd.postloop(self)
print "Exiting ..."
def precmd(self, line):
self._hist += [ line.strip() ]
return line
def postcmd(self, stop, line):
return stop
def emptyline(self):
return cmd.Cmd.emptyline(self)
def handler(self, signum, frame):
# self.emptyline() does not work here
# return cmd.Cmd.emptyline(self) does not work here
print "caught ctrl+c, press return to continue"
if __name__ == '__main__':
console = Console()
console.cmdloop()
Дальнейшая помощь приветствуется.
Оригинальный вопрос и более подробная информация:
[В настоящее время приведенные ниже предложения были включены в этот вопрос - все еще ищут ответ. Обновлено для исправления ошибки.]
С тех пор я попытался переместить обработчик на функцию вне цикла, чтобы убедиться, что она более гибкая, но она не выглядит.
Я использую python cmd.Cmd module, чтобы создать собственный интерпретатор командной строки для управления взаимодействием с некоторым программным обеспечением. Я часто нажимаю ctrl + c, ожидая популярного поведения в оболочке, возвращающего новое приглашение, не действуя ни на какую команду. Однако он просто выходит. Я попытался поймать исключения KeyboardInterrupt в разных точках кода (preloop и т.д.) Безрезультатно. Я немного читал в sigints, но не совсем понимаю, как это вписывается здесь.
UPDATE: из приведенных ниже предложений я попытался реализовать сигнал и смог сделать так, чтобы ctrl + c не выходил из CLI, но печатает сообщение. Тем не менее, моя новая проблема заключается в том, что я не могу описать функцию обработчика (см. Ниже), чтобы сделать многое рядом с печатью. Я хотел бы, чтобы ctrl + c в основном прервал текущую строку и дал мне новое приглашение.
Ответы
Ответ 1
В настоящее время я работаю над созданием оболочки с помощью модуля Cmd. Я столкнулся с той же проблемой и нашел решение.
Вот код:
class Shell(Cmd, object)
...
def cmdloop(self, intro=None):
print(self.intro)
while True:
try:
super(Shell, self).cmdloop(intro="")
break
except KeyboardInterrupt:
print("^C")
...
Теперь у вас есть правильный обработчик KeyboardInterrupt (он же CTRL-C) внутри оболочки.
Ответ 2
Вместо использования обработки сигналов вы можете просто поймать KeyboardInterrupt
, который поднимает cmd.Cmd.cmdloop()
. Конечно, вы можете использовать обработку сигналов, но это не требуется.
Запустите вызов cmdloop()
в цикле while, который перезапускает себя при исключении KeyboardInterrupt
, но заканчивается должным образом из-за EOF.
import cmd
import sys
class Console(cmd.Cmd):
def do_EOF(self,line):
return True
def do_foo(self,line):
print "In foo"
def do_bar(self,line):
print "In bar"
def cmdloop_with_keyboard_interrupt(self):
doQuit = False
while doQuit != True:
try:
self.cmdloop()
doQuit = True
except KeyboardInterrupt:
sys.stdout.write('\n')
console = Console()
console.cmdloop_with_keyboard_interrupt()
print 'Done!'
Выполнение CTRL-c просто печатает новое приглашение в новой строке.
(Cmd) help
Undocumented commands:
======================
EOF bar foo help
(Cmd) <----- ctrl-c pressed
(Cmd) <------ctrl-c pressed
(Cmd) ddasfjdfaslkdsafjkasdfjklsadfljk <---- ctrl-c pressed
(Cmd)
(Cmd) bar
In bar
(Cmd) ^DDone!
Ответ 3
В ответ на следующий комментарий в этом ответе:
Это похоже на схождение в решении, но я не знаю, как его интегрировать в мой собственный код (см. выше). Я должен выяснить линию "супер". Мне нужно попытаться заставить это работать в какой-то момент в будущем.
super()
будет работать в этом ответе, если у вас есть класс, расширяющий object
в дополнение к cmd.Cmd
. Вот так:
class Console(cmd.Cmd, object):
Ответ 4
Я хотел сделать то же самое, поэтому я искал его и создал базовый скрипт для понимания целей, мой код можно найти на моем GitHub
Итак, в вашем коде,
вместо этого
if __name__ == '__main__':
console = Console()
console.cmdloop()
Используйте это,
if __name__ == '__main__':
console = Console()
try:
console.cmdloop()
except KeyboardInterrupt:
print ("print sth. ")
raise SystemExit
Надеюсь, это поможет вам. ну, это сработало для меня. 😀
Ответ 5
Вы можете поймать сигнал CTRL-C с помощью обработчика сигнала. Если вы добавите код ниже, интерпретатор откажется покинуть CTRL-C:
import signal
def handler(signum, frame):
print 'Caught CTRL-C, press enter to continue'
signal.signal(signal.SIGINT, handler)
Если вы не хотите нажимать ENTER после каждого CTRL-C, просто дайте обработчику ничего не делать, что не приведет к улавливанию сигнала:
def handler(signum, frame):
""" just do nothing """
Ответ 6
Вы можете проверить модуль signal
: http://docs.python.org/library/signal.html
import signal
oldSignal = None
def handler(signum, frame):
global oldSignal
if signum == 2:
print "ctrl+c!!!!"
else:
oldSignal()
oldSignal = signal.signal(signal.SIGINT, handler)
Ответ 7
Просто добавьте это внутри класса Console(cmd.Cmd)
:
def cmdloop(self):
try:
cmd.Cmd.cmdloop(self)
except KeyboardInterrupt as e:
self.cmdloop()
Забудьте все остальное. Это работает. Он не создает петли внутри петель. Когда он ловит KeyboardInterrupt
, он вызывает do_EOF
, но только выполняет первую строку; так как ваша первая строка в do_EOF
является return do_exit
, это нормально.
do_exit
вызывает postloop
.
Однако, опять же, он выполняет только первую строку после cmd.Cmd.postloop(self)
. В моей программе это "\n". Как ни странно, если вы SPAM ctrl + C, вы, в конце концов, увидите, что он печатает вторую строчку, обычно печатаемую только на выходе ACTUAL (ctrl + Z, затем введите или введите в выход).
Ответ 8
Я предпочитаю сигнальный метод, но с пропуском. На самом деле не заботятся о том, чтобы вызвать у пользователя что-либо в моей среде оболочки.
import signal
def handler(signum, frame):
pass
signal.signal(signal.SIGINT, handler)