What is the equivalent of GDB's "define" in PDB? - python

I use python debugger PDB as a script debugger. I would like to define a PDB function that runs 2 commands at the same time (for instance, print variable value and go to next line). Debugging C code using GDB I would do this:
(gdb) def f
Type commands for definition of "f".
End with a line saying just "end".
>p i
>n
>end
(gdb) f
But trying the same with PDB doesn't work:
(Pdb) def f
*** SyntaxError: invalid syntax
Is there a way to do it?

Related

how to pass variables from GDB to invoked python interpeter

From some version of GDB (I guess 7 or so) it is possible to invoke python from GDB in interactive or non interactive way.
Here is some example:
(gdb) python print("gdb".capitalize())
Gdb
(gdb)
Is it possible to pass variables from used in GDB into Python? I've tried something like this but with no luck:
Try to pass C variable named c_variable
(gdb) python print(c_variable.capitalize())
Traceback (most recent call last):
File "<string>", line 1, in <module>
NameError: name 'c_variable' is not defined
Error while executing Python code.
Try to pass GDB's variable named $1
(gdb) python print($1.capitalize())
File "<string>", line 1
print($1.capitalize())
^
SyntaxError: invalid syntax
Error while executing Python code.
EDIT
Almost imediatelly after my question I've found this question passing c++ variables to python via gdb
So I've end up with following:
(gdb) whatis p_char
type = char *
(gdb) ptype p_char
type = char *
(gdb) p p_char
$1 = 0x8002004 "hello world"
(gdb) python x=str(gdb.parse_and_eval('p_char')); print(x.split(" "))
['0x8002004', '"hello', 'world"']
This is something that I can work with but I need to do some extra cleanup (remove quotes, address etc), is there any better way? And I still do not know if is possible to pass $1.
Try to pass C variable named c_variable
Try to pass GDB's variable named $1
py print(gdb.parse_and_eval("c_variable"))
py print(gdb.parse_and_eval("$1"))
If you want to work with Python and GDB I would highly recommend reading this. Of special interest to you would be this page that includes parse_and_eval as well as this page on values.
The gdb.parse_and_eval function returns a gdb.Value object. For values that are strings you can use the string method, so:
(gdb) python print(gdb.parse_and_eval("string_var").string())
Hello World
(gdb) python print(gdb.parse_and_eval("string_var").string()[::-1])
dlroW olleH

Update a function during debugging (pdb or ipdb)

Imagine I am debugging the following script:
import ipdb
def slow_function(something):
# I'm a very slow function
return something_else
def fast_function(something_else):
# There's a bug here
return final_output
something = 1
something_else = slow_function(something)
ipdb.set_trace()
final_output = fast_function(something_else)
ipdb.set_trace()
When the ipdb.set_trace() line is met the debugger shell is prompted and I can now execute the final_output = fast_function(something_else) statement to check whether fast_function is behaving as intended. I see there's a bug so I go into the source code and I fix it. Now I want to see whether the fix is correct but I don't want to run the script a second time (because it's slow), nor I want to save something_else on disk (because, maybe, it's very large).
Is there a way to update fast_function() in the debugger shell so that the new source code is used?
You can execute a single-line python statement inside pdb by preceding it with an exclamation sign. The output of help exec as produced by pdb follows:
(!) statement
Execute the (one-line) statement in the context of the current
stack frame. The exclamation point can be omitted unless the
first word of the statement resembles a debugger command. To
assign to a global variable you must always prefix the command
with a 'global' command, e.g.:
(Pdb) global list_options; list_options = ['-l']
(Pdb)
Using this facility, you can save the source code of the function in a file and update that function inside pdb as follows:
!exec(open("fast_function.py", "r").read())
Demonstration:
$ cat test.py
import pdb;
def foo():
print('Foo');
foo()
pdb.set_trace()
foo()
$ python3 test.py
Foo
> test.py(8)<module>()
-> foo()
(Pdb) cont
Foo
$ cat foo.py
def foo():
print('Modified Foo');
$ python3 test.py
Foo
> test.py(8)<module>()
-> foo()
(Pdb) !exec(open("foo.py", "r").read())
(Pdb) cont
Modified Foo
If it's a short function you can just overwrite the existing function in one pdb line. Note that the exclamation sign is optional there.
(pdb)...
(pdb)!def fast_function(something_else): print("Hello world");return True
If the function is a bit larger in code length then you can leverage the normal interactive shell(see this)
(Pdb) !import code; code.interact(local=vars())
(InteractiveConsole)
In : def fast_function(something_else):
...: print 'hello in pdb'
...:
In : # use ctrl+d here to return to pdb shell...
(Pdb) !fast_function(arg)
hello in pdb
If the code of the function is not easily manageable with the interactive shell either, Leons' recommendation will be better I guess.
Just keep in mind that, every pdb line can work like executing a line of normal python code and you can achieve pretty much anything on the fly! pdb is more powerful than some other graphical debugging tools in this sense.
P.S. seems like PyCharm does support Evaluating expression feature according to ADR
edit 19/01/2018
You can write result in a file on memory. for example /dev/shm is a tmpfs partition. size could be optimized with protocol dump's keyargs.
# save result
with open('/dev/shm/data.pk', 'w' ) as data:
pickle.dump(something_else, data, protocole=3)
You can use pickle to store result in file the first time and reload it to debug second function
import pickle
def slow_function(something):
# I'm a very slow function
return something + 42
def fast_function(something_else):
# There's a bug here
return something_else + 42
something = 1
something_else = slow_function(something)
# save result
with open('data.pk', 'w' ) as data:
pickle.dump(something_else, data)
second launch
import ipdb
# load result from disk
with open('data.pk', 'r' ) as data:
something_else = pickle.load(data)
ipdb.set_trace()
fast_function(something_else)

winpdb is not recognising function inputs?

This is my first post! :D
I am trying to learn how to use winpdb to debug some python code and have a problem. Consider the following python function, simple.py:
def simple(a,b):
c = a + b
return c
I am in windows and using the command line in the directory where I have stored this function I attempt to run winpdb with the following command:
winpdb simple.py 2 1
Is this the correct way to debug the function simple.py with a = 2 and b = 1? As when I execute the above in the command line winpdb launches but with a and b undefined, for example (taken from the winpdb console when the above is entered into the cmd window):
> eval a
<type 'exceptions.NameError'>, name 'a' is not defined
I am sorry to have to ask such a basic question, but I can not seem to find any solutions online.
The presented source file defines a function but it never calls the function and doesn't execute any code at all. Code in exactly that form can't be trivially debugged.
Typically a sample call to function is added to the end of file, like
def simple(a, b):
c = a + b
return c
simple(1, 2)
Than you can start winpdb like
winpdb simple.py
place breakpoint in a function by clicking on margin of c = a + b line and press the Go button.
After that the program would stop in a state where you can use eval a and even eval simple(5, 6)

conditional breakpoint using pdb

Sounds like I'm missing something extremely simple, I'm trying to set a breakpoint in my python code using:
if(some condition):
pdb.set_trace()
My error in the code comes after a large number of iterations..difficult to debug using print etc.
I am able to print stuff when the condition hits but I would like to set brk-pt.
--EDIT--
Actual code:
import pdb
if (node_num == 16):
print node_num
pdb.set_trace()
I see you found your solution Sanjay. But for those who arrived here looking for a means to set a conditional breakpoint with pdb read on:
Instead of hard coding conditions such as if node_num == 16:, run pdb in interactive mode. Sample code:
import pdb
for node_num in range(50):
do_something(node_num)
...
In the shell start the script in debug mode using -m pdb:
[rick#rolled ~]$ python -m pdb abc.py
> /home/dcadm/abc.py(1)<module>()
-> import pdb
(Pdb) l
1 -> import pdb
2
3 for node_num in range(50) :
4 foo = 2**node_num
[EOF]
(Pdb) b 4, node_num > 4
Breakpoint 1 at /home/dcadm/abc.py:4
(Pdb) c
> /home/dcadm/abc.py(4)<module>()
-> foo = 2**node_num
(Pdb) node_num
5
(Pdb)
The pdb shell command b 4, node_num > 4 breaks at line 4 when node_num is greater than 4.
To actually use conditional breakpoints in pdb, you can do the following:
b(reak) [([filename:]lineno | function) [, condition]]
https://docs.python.org/3/library/pdb.html#pdbcommand-break
Eg I'm running some test code that iterates over django views. I want to break only when the particular view I'm interested in is reached:
b C:\Users\powlo\project\tests\TestCase.py:350, view.view_name
== 'app.views.export'
I am not sure as to why your code isn't working, but what you can do is on your local machine, create a new file for your minimum example to see if you can do what you want to do
import pdb
for node_num in range(50):
if node_num == 16:
print(node_num)
pdb.set_trace()
Now running it:
16
> /tmp/tmp.py(3)<module>()
-> for node_num in range(50):
(Pdb) p node_num
16
As you can see this worked as intended with this trivial example, it's up to you to figure out how to adapt this to your code, and/or figure out what else did you do to your code/environment that prevented that prompt from showing up.
Alternatively, if you have a function that is dying in an exception and you want to know the exact line that caused it, you should use post_mortem instead. Wrap the problematic section of code with this
try:
problem_function()
except Exception: # or the specific exception type thrown
pdb.post_mortem()
raise
What post_mortem would do is dump a break point right at the point where the exception happened (specifically in that stack frame), and so this allows all values to be inspected and then let you continue execution. However I also put a raise at the end to allow the exception to continue as normal, and this is intended as execution doesn't normally from where it die but just pause at that exception handling block due to the post_mortem call. Might as well just give up after inspecting what went wrong.

Passing command Line argument to Python script within Eclipse(Pydev)

I am new to Python & Eclipse, and having some difficulties understanding how to pass command line argument to script running within Eclipse(Pydev).
The following link explains how to pass command line argument to python script.
To pass command line argument to module argecho.py(code from link above),
#argecho.py
import sys
for arg in sys.argv: 1
print arg
I would need to type into python console
[you#localhost py]$ python argecho.py
argecho.py
or
[you#localhost py]$ python argecho.py abc def
argecho.py
abc
def
How would I pass same arguments to Python script within Eclipse(Pydev) ???
Thanks !
Click on the play button down arrow in the tool bar -> run configurations -> (double click) Python Run -> Arguments tab on the right hand side.
From there you can fill out the Program Arguments text box:
If you want your program to ask for arguments interactively, then they cease to be commandline arguments, as such. However you could do it something like this (for debugging only!), which will allow you to interactively enter values that the program will see as command line arguments.
import sys
sys.argv = raw_input('Enter command line arguments: ').split()
#Rest of the program here
Note that Andrew's way of doing things is much better. Also, if you are using python 3.*, it should be input instead of raw_input,
Select "Properties" -->> "Run/Debug Settings".
Select the related file in right panel and then click on "Edit" button. It will open properties of selected file. There's an "Arguments" tab.
Years later, and not Eclipse,
but a variant of other answers to run my.py M=11 N=None ... in sh or IPython:
import sys
# parameters --
M = 10
N = 20
...
# to change these params in sh or ipython, run this.py M=11 N=None ...
for arg in sys.argv[1:]:
exec( arg )
...
myfunc( M, N ... )
See One-line-arg-parse-for-flexible-testing-in-python
under gist.github.com/denis-bz .
What I do is:
Open the project in debug perspective.
In the console, whenever the debugger breaks at breakpoint, you can type python command in the "console" and hit return (or enter).
There is no ">>" symbol, so it is hard to discover.
But I wonder why eclipse doesn't have a python shell :(

Categories