2

I have a code separated into three modules; the modules are the following: 1) run_module.py 2) module1.py 3) module2.py

I have an if name == 'main': statement inside run_module.py and I am very comfortable using my CLI to do python -b pdb run_module.py and then set a breakpoint inside run_module.py using the (pdb) b linenumber format.

My question is this: How can I set breakpoints inside module1.py or module2.py from the CLI; ie. not intervening directly into the module1.py and module2.py scripts and typing import pdb; pdb.set_trace()?

Any insight would be extremely appreciated. Many thanks in advance.

maroulator
  • 129
  • 8
  • Does this answer your question? [Simpler way to put PDB breakpoints in Python code?](https://stackoverflow.com/questions/6980749/simpler-way-to-put-pdb-breakpoints-in-python-code) – steviestickman Apr 18 '20 at 21:44
  • I saw that post and no, it does not help; I tried it out and I got the following: ***The specified object [filename] is not a function or was not found along sys.path. *** I found a post also addressing this case, that didn't work for me either. I feel like I am missing smth obvious. Thoughts? Do you need anything else from me to get me an answer? – maroulator Apr 19 '20 at 00:04
  • Can you add the file structure to your question? – steviestickman Apr 19 '20 at 11:18

1 Answers1

2

pdb, like gdb, or trepan3k has a break command:

(Pdb) help break
b(reak) ([file:]lineno | function) [, condition]
With a line number argument, set a break there in the current
file.  With a function name, set a break at first executable line
of that function.  Without argument, list all breaks.  If a second
argument is present, it is a string specifying an expression
which must evaluate to true before the breakpoint is honored.
    
The line number may be prefixed with a filename and a colon,
to specify a breakpoint in another file (probably one that
hasn't been loaded yet).  The file is searched for on sys.path;
the .py suffix may be omitted.

But when you do this there are some things you should be aware of.

If you specify breakpoints by filename and line number, it is possible to get a error message. For example:

(Pdb) break foo.py
*** The specified object 'foo.py' is not a function or was not found along sys.path.

Let's try to understand what the message says. foo.py clearly isn't a function. It is a file. Is it in sys.path?

(Pdb) import sys
(Pdb) sys.path
['', '/usr/lib/python3.6', ...]

No. Ok. well how about if I give the file name as an absolute path?

(Pdb) break /tmp/bug.py:2
Breakpoint 1 at /tmp/bug.py:2

Ok. That works. But again there is a caveat: is it possible to stop at at that line in that file? Watch this:

(Pdb) break /etc/hosts:1
Breakpoint 2 at /etc/hosts:1

/etc/hosts is a file, but it is not a Python program. And while as we saw before pdb warns if the file is missing, it doesn't check whether the file is a Python file.

If instead you run this with trepan3k it prints out a nasty traceback (which I'll fix at some point), but at least it gives some inkling that something is wrong:

(Nasty traceback)...
ValueError: path /etc/hosts must point to a Python source that can be compiled, or Python bytecode (.pyc, .pyo)

[The traceback has been removed in the github source and will not appear in version 1.2.9. In version 1.2.8 of trepan2 this is gone too.]

Also pdb isn't that smart about knowing whether line 2 has code that can be stopped at; pdb will warn about empty or blank lines or lines comment lines, but anything more sophisticated than that, like some random text, and no dice.

Again trepan3k is a little better about this:

(trepan3k) break /tmp/bug.py:2
** File /tmp/bug.py is not stoppable at line 2.

The last caveat when working with breakpoints is that a breakpoint might not be hit because the code never gets there. But you have this also with sys.set_trace() so I imagine that kind of behavior is less of a surprise.

One other difference of note between the breakpoints of trepan3k and pdb. In pdb when you breakpoint on a function, you are really setting breakpoint on the first line recorded for that function. That is, you have already entered the function.

In trepan3k you are setting it on the function call which is independent of where it is located. Note the different syntax for functions, they have trailing parenthesis after the name, e.g. foo() vs foo. A method name given in the context of the object is is a method of is also possible, and I use that a lot. Here is an example:

trepan3k /tmp/pathtest.py
(/tmp/pathtest.py:1): <module>
-> 1 from pathlib import Path
(trepan3k) n
(/tmp/pathtest.py:2 @12): <module>
-- 2 mypath = Path(__file__)
(trepan3k) n
(/tmp/pathtest.py:3 @20): <module>
-- 3 print(mypath.match("p.*"))
(trepan3k) break mypath.match()
Breakpoint 1 set at line 972 of file /home/test/.pyenv/versions/3.8.10/lib/python3.8/pathlib.py
(trepan3k) c
(/home/test/.pyenv/versions/3.8.10/lib/python3.8/pathlib.py:972): match
xx 972     def match(self, path_pattern):
(trepan3k) path_pattern
'p.*'
(trepan3k) info pc
PC offset is -1.
rocky
  • 7,226
  • 3
  • 33
  • 74