Debugging a program with Python pdb
is trivial. You just run import pdb;
pdb.set_trace()
(or just breakpoint()
introduced in Python 3.7), and the
debugger will take action.
However, when the program being debugged has a stdin redirection, this is a
source of trouble. This happens because your program may be reading, for
instance, input with input()
(which comes from file because of the
redirection) and, when entering the debugger, it continues to read what is on
the file as commands to Pdb
, which almost certainly is not what you want.
More specifically, Python uses sys.stdin
for functions such as input()
. The
pdb
module, which contains Pdb
class, uses the cmd
module, which contains
Cmd
. Cmd
, by itself, uses input()
, which is why you have the behaviour
described above. Pdb
class accepts a stdin
kwarg, but for Cmd
to accept
stdin
, you must set use_rawinput
to False
, which is only set by Pdb
when you also set stdout
.
The most common shells has 3 file descriptors already passed to every command run from console, which are stdin (fd 0), stdout (fd 1) and stderr (fd 2). This comes from UNIX.
A common Python program reads its input (for instance, when you run input()
to read the content a user might type) from fd 0, which the shell is telling
stdin
by default. When you run something like this
$ python3 <file.txt
the input of your program comes not from what you type, but from what it's in the file. In effect, the command above is the same as
$ python3 0<file.txt
at least for the most common shells.
So, to the solution. The first and the most obvious way is not to use stdin
redirection but to open the file normally with open()
. In this case, the file
is hardcoded in the file. The second way is to read the file name from the
command-line interface, but involves more boilerplate than the next solution.
The third solution is to read from another fd other than the 3 default fd's already supplied by default (see above). So you can use something like this:
import sys
sys.stdin = open('/dev/fd/3')
When using the debugger, as said above, you have to specify you're going to use
stdin explicly. Otherwise, Pdb
, which uses Cwd
, will use what you set
sys.stdin
to. So, instead of import pdb; pdb.set_trace()
as suggested by
the documentation, do:
import pdb; pdb.Pdb(stdin=sys.__stdin__, stdout=sys.__stdout__).set_trace()
at the point you want to breakpoint. Don't forget to import sys
if you didn't
already done.
sys.__stdin__
is what sys.stdin
is set when you run Python. Now, on the
shell, to run your program, you do something like this:
$ python3 3<file.txt
and that's it. Now your program are reading the file from fd 3, and your fd 0
is freed to be used by Pdb
.