Я надеюсь, что не пропустил ничего важного, но кажется, что вы не можете сделать это совершенно тривиальным способом, потому что readline
будет использоваться, только если pdb.Pdb
(соответственно cmd.Cmd
это sublcasses) имеет use_rawinput
установлен на ненулевое значение, что, однако, приведет к игнорированию stdin
и смешиванию входных данных для отладчика и самого скрипта.Тем не менее, лучшее, что я придумала до сих пор:
#!/usr/bin/env python3
import os
import sys
import pdb
pdb_inst = pdb.Pdb()
stdin_called = os.fdopen(os.dup(0))
console_new = open('/dev/tty')
os.dup2(console_new.fileno(), 0)
console_new.close()
sys.stdin = os.fdopen(0)
for line in stdin_called:
pdb_inst.set_trace()
sys.stdout.write(line)
Это относительно инвазивно для вашего исходного сценария, даже если его можно было бы по крайней мере поместить за его пределы и импортировать, вызывать или использоватьв качестве оболочки.
Я перенаправил (продублировал) входящий STDIN
в дескриптор файла и открыл его как stdin_called
.Затем (на основе вашего примера) я открыл /dev/tty
для чтения, заменил дескриптор файла процесса 0
(для STDIN
; лучше использовать значение, возвращаемое sys.stdin.fileno()
) на этот, который я только что открыла также переназначил соответствующий файловый объект на sys.stdin
.Таким образом, программы цикл и pdb
используют свои собственные входные потоки, в то время как pdb
взаимодействует с тем, что кажется просто «нормальной» консолью STDIN
, она рада включить readline
on.
Это не красиво, но следует делать то, что вы хотели, и, надеюсь, даст полезные советы.Используется (если доступно) readline
(редактирование строки, история, завершение), когда в pdb
:
$ { echo one; echo two; } | python3 cat.py
> /tmp/so/cat.py(16)<module>()
-> sys.stdout.write(line)
(Pdb) c
one
> /tmp/so/cat.py(15)<module>()
-> pdb_inst.set_trace()
(Pdb) con[TAB][TAB]
condition cont continue
(Pdb) cont
two
Примечание, начиная с версии 3.7, вы можете использовать breakpoint()
вместо import pdb; pdb.Pdb().set_trace()
для удобстваи вы также можете проверить результат вызова dup2
, чтобы убедиться, что дескриптор файла был создан / заменен, как и ожидалось.
РЕДАКТИРОВАТЬ: Как упоминалось ранее и отмечено в комментарии OP, это уродливои инвазивен к сценарию.Это не делает его красивее, но мы можем использовать несколько хитростей, чтобы уменьшить влияние на окружающую среду.Один такой вариант, который я взломал вместе:
import sys
# Add this: BEGIN
import os
import pdb
import inspect
pdb_inst = pdb.Pdb()
class WrapSys:
def __init__(self):
self.__stdin = os.fdopen(os.dup(0))
self.__console = open('/dev/tty')
os.dup2(self.__console.fileno(), 0)
self.__console.close()
self.__console = os.fdopen(0)
self.__sys = sys
def __getattr__(self, name):
if name == 'stdin':
if any((f.filename.endswith("pdb.py") for f in inspect.stack())):
return self.__console
else:
return self.__stdin
else:
return getattr(self.__sys, name)
sys = WrapSys()
# Add this: END
for line in sys.stdin:
pdb_inst.set_trace() # Inject breakpoint
sys.stdout.write(line)
Я не копал весь путь, но, как есть, pdb
/ cmd
действительно, кажется, не только нужен sys.stdin
, но и дляиспользовать fd 0
для включения readline
. Приведенный выше пример поднимает уровень и в нашем скрипте перехватывает то, что означает sys
, чтобы установить другое значение для sys.stdin
, когда код из pdb.py
находится в стеке.Одно очевидное предостережение.Если что-то еще, кроме pdb
, также ожидает и зависит от sys.stdin
fd, равным 0
, это все равно будет неудачей (или чтением его входных данных из другого потока, если он просто пошел на это).