How to define a new function in pdb - python

Why can't I define new functions when I run pdb?
For example take myscript.py:
#!/gpfs0/export/opt/anaconda-2.3.0/bin/python
print "Hello World"
print "I see you"
If I run python -m pdb myscript.py and try to interactively define a new function:
def foo():
I get the error:
*** SyntaxError: unexpected EOF while parsing (<stdin>, line 1)
Why is this?

I don't think it supports multi-line input. You can workaround by spawning up an interactive session from within pdb. Once you are done in the interactive session, exit it with Ctrl+D.
>>> import pdb
>>> pdb.set_trace()
(Pdb) !import code; code.interact(local=vars())
(InteractiveConsole)
In : def foo():
...: print('hello in pdb')
...:
In : # use ctrl+d here to return to pdb shell...
(Pdb) foo()
hello in pdb

You can define your function in a one line statement using ; instead of indentation, like this:
(Pdb) def foo(): print 'Hello world'; print 'I see you'
(Pdb) foo()
Hello world
I see you

i was able to import python modules from the pdb command line.
if you can import python modules, then you should be able to define your functions in a file and just do an import of the file.

If your application happens to have IPython as a dependency, you could drop yourself into a feature-rich IPython REPL right from ipdb:
import IPython; IPython.embed()
From inside, if you run IPython's magic command whos, you should see all the locally defined variables in the current pdb frame.

Related

Step through code called with exec in pdb

I am creating a DSL by doing some pre-processing on a string, and then using exec to call the pre-processed code using the python interpreter. I would like to be able to step through the pre-processed code using pdb, and be able to show the actual line of python code (after pre-processing) that I'm stepping through.
For example:
In [1]: s = '''print "hello"
...: print 'world'
...: '''
In [2]: s
Out[2]: 'print "hello"\nprint \'world\'\n'
In [3]: import pdb
In [4]: pdb.run(s)
> <string>(1)<module>()
(Pdb) list
[EOF]
I would like the list command in pdb to output the code and show what line I'm one, the same way that it does when I stop at a breakpoint in a regular python file. Any hints on how to do this, or an alternative approach/mindset would be greatly appreciated!
[edit]
I pass in a bunch of complicated objects to the exec using the optional globals positional argument to exec, so writing out the string to a file and then running that in pdb won't work. For example:
s = '''some_complicated_stateful_object.method(foo)'''
exec(s, {'some_complicated_stateful_object': an_object,
'foo': some_other_object})
Thanks to for the good suggestion though!

Why does changing directory within a python script affect displaying context in pdb?

Changing directories within a script seems to prevent pdb from displaying the current line while stepping through code. Compare the scripts and output below.
import os
import pdb
os.chdir('..')
print 'hello'
pdb.set_trace()
print 'world'
outputs:
hello
/Users/username/test.py(6)<module>()
(pdb)
By contrast,
import os
import pdb
print 'hello'
pdb.set_trace()
print 'world'
outputs:
hello
/Users/username/test.py(5)<module>()
-> print 'world'
(pdb)
Why does this happen, and is there a way to prevent it (i.e., to get pdb to continue print the current line in a script with os.chdir)?
One cause of this problem is launching a module/script from the current directory. For example, if I start my script from the shell using:
$ ./myscript.py
And then myscript uses os.chdir() to change the current working directory pdb will stop printing the current line each time it stops (though list will still work). On the other hand, if you expand the path to your script before running it like this:
$ /Users/me/myproject/myscript.py
Then pdb is no longer confused and will print the line source each time it stops.

How to write an ipython alias which executes in python instead of shell?

We can define an alias in ipython with the %alias magic function, like this:
>>> d
NameError: name 'd' is not defined
>>> %alias d date
>>> d
Fri May 15 00:12:20 AEST 2015
This escapes to the shell command date when you type d into ipython.
But I want to define an alias to execute some python code, in the current interpreter scope, rather than a shell command. Is that possible? How can we make this kind of alias?
I work in the interactive interpreter a lot, and this could save me a lot of commands I find myself repeating often, and also prevent some common typos.
The normal way to do this would be to simply write a python function, with a def. But if you want to alias a statement, rather than a function call, then it's actually a bit tricky.
You can achieve this by writing a custom magic function. Here is an example, which effectively aliases the import statement to get, within the REPL.
from IPython.core.magic import register_line_magic
#register_line_magic
def get(line):
code = f"import {line}"
print("-->", code)
exec(code, globals())
del get # in interactive mode-remove from scope so function doesn't shadow magic
edit: below is the previous code, for older versions of IPython
from IPython.core.magic_arguments import argument, magic_arguments
#magic_arguments()
#argument('module')
def magic_import(self, arg):
code = 'import {}'.format(arg)
print('--> {}'.format(code))
self.shell.run_code(code)
ip = get_ipython()
ip.define_magic('get', magic_import)
Now it is possible to execute get statements which are aliased to import statements.
Demo:
In [1]: get json
--> import json
In [2]: json.loads
Out[2]: <function json.loads>
In [3]: get potato
--> import potato
---------------------------------------------------------------------------
ImportError Traceback (most recent call last)
<string> in <module>()
ImportError: No module named potato
In [4]:
Of course, this is extendible to arbitrary python code, and optional arguments are supported aswell.
I don't know since when IPython provides with macro. And now you can simply do this:
ipy = get_ipython()
ipy.define_macro('d', 'date')
You can put this code into any file located in ~/.ipython/profile_default/startup/, and then this macro will be automatically available when you start IPython.
However, a macro doesn't accept arguments. So pleaes keep this in mind before you choose to define a macro.

Breakpoint-induced interactive debugging of Python with IPython

Say I have an IPython session, from which I call some script:
> run my_script.py
Is there a way to induce a breakpoint in my_script.py from which I can inspect my workspace from IPython?
I remember reading that in previous versions of IPython one could do:
from IPython.Debugger import Tracer;
def my_function():
x = 5
Tracer()
print 5;
but the submodule Debugger does not seem to be available anymore.
Assuming that I have an IPython session open already: how can I stop my program a location of my choice and inspect my workspace with IPython?
In general, I would prefer solutions that do not require me to pre-specify line numbers, since I would like to possibly have more than one such call to Tracer() above and not have to keep track of the line numbers where they are.
The Tracer() still exists in ipython in a different module. You can do the following:
from IPython.core.debugger import Tracer
def my_function():
x = 5
Tracer()()
print 5
Note the additional call parentheses around Tracer
edit: For IPython 6 onwards Tracer is deprecated so you should use set_trace() instead:
from IPython.core.debugger import set_trace
def my_function():
x = 5
set_trace()
print 5
You can run it and set a breakpoint at a given line with:
run -d -b12 myscript
Where -b12 sets a breakpoint at line 12. When you enter this line, you'll immediately drop into pdb, and you'll need to enter c to execute up to that breakpoint.
This is the version using the set_trace() method instead of the deprecated Tracer() one.
from IPython.core.debugger import Pdb
def my_function():
x = 5
Pdb().set_trace()
print 5
Inside the IPython shell, you can do
from IPython.core.debugger import Pdb
pdb = Pdb()
pdb.runcall(my_function)
for example, or do the normal pdb.set_trace() inside your function.
With Python 3 (v3.7+), there's the new breakpoint() function. You can modify it's behaviour so it'll call ipython's debugger for you.
Basically you can set an environment variable that points to a debugger function. (If you don't set the variable, breakpoint() defaults to calling pdb.)
To set breakpoint() to call ipython's debugger, set the environment variable (in your shell) like so:
# for bash/zsh users
export PYTHONBREAKPOINT='IPython.core.debugger.set_trace'
# powershell users
$env:PYTHONBREAKPOINT='IPython.core.debugger.set_trace'
(Note, obviously if you want to permanently set the environment variable, you'll need to modify your shell profile or system preferences.)
You can write:
def my_function():
x = 5
breakpoint()
print(5)
And it'll break into ipython's debugger for you. I think it's handier than having to import from IPython.core.debugger import set_trace and call set_trace().
I have always had the same question and the best workaround I have found which is pretty hackey is to add a line that will break my code, like so:
...
a = 1+2
STOP
...
Then when I run that code it will break, and I can do %debug to go there and inspect. You can also turn on %pdb to always go to point where your code breaks but this can be bothersome if you don't want to inspect everywhere and everytime your code breaks. I would love a more elegant solution.
I see a lot of options here, but maybe not the following simple option.
Fire up ipython in the directory where my_script.py is.
Turn the debugger on if you want the code to go into debug mode when it fails. Type %pdb.
In [1]: %pdb
Automatic pdb calling has been turned ON
Next type
In [2]: %run -d ./my_script.py
*** Blank or comment
*** Blank or comment
NOTE: Enter 'c' at the ipdb> prompt to continue execution.
> c:\users\c81196\lgd\mortgages-1\nmb\lgd\run_lgd.py(2)<module>()
1 # system imports
----> 2 from os.path import join
Now you can set a breakpoint where ever you want it.
Type b 100 to have a breakpoint at line 100, or b whatever.py:102 to have a breakpoint at line 102 in whatever.py.
For instance:
ipdb> b 100
Then continue to run, or continue.
ipdb> c
Once the code fails, or reaches the breakpoint you can start using the full power of the python debugger pdb.
Note that pdb also allows the setting of a breakpoint at a function.
b(reak) [([filename:]lineno | function) [, condition]]
So you do not necessarily need to use line numbers.

Debugging code in the Python interpreter

I like testing functions in the Python interpreter. Is it possible to debug a function in the Python interpreter when I want to see more than a return value and a side effect?
If so, could you show basic debugger operations (launching the function with arguments, setting breakpoint, next step, step into, watching variable)? If not, how would you debug a function another way?
The point is, I want to debug only a particular function which will be supplied with arguments. I don't want to debug whole module code.
thank you for advice
If you want to debug specific function you can using this -
>>> import pdb
>>> import yourmodule
>>> pdb.run('yourmodule.foo()')
over the command line. pdb.set_trace() should be added in your function to break there.
More info on pdb can be seen here - http://docs.python.org/library/pdb.html
See pdb module. Insert into code:
import pdb
pdb.set_trace()
... makes a breakpoint.
The code-to-debug does not need to be modified to include pdb.set_trace(). That call can be made directly in the interpreter just before the code-to-debug:
>>> import pdb
>>> pdb.set_trace(); <code-to-debug>
For example, given test_script.py with the following code:
def some_func(text):
print 'Given text is {}'.format(repr(text))
for index,char in enumerate(text):
print ' '*index, char
an interpreter session to debug some_func using the debugger commands step-into (s), next (n) and continue (c) would look like:
>>> import pdb
>>> import test_script
>>> pdb.set_trace(); test_script.some_func('hello')
--Call--
> c:\src\test_script.py(1)some_func()
-> def some_func(text):
(Pdb) s
> c:\src\test_script.py(2)some_func()
-> print 'Given text is {}'.format(repr(text))
(Pdb) n
Given text is 'hello'
> c:\src\test_script.py(3)some_func()
-> for index,char in enumerate(text):
(Pdb) c
h
e
l
l
o
>>>
See the docs for the pdb module for more information on how to use the debugger: http://docs.python.org/library/pdb.html
Additionally, while using the debugger, the help command provides a nice list of commands and help <command> gives help specific to the given command.

Categories

Resources