Поиск по блогу

воскресенье, 13 апреля 2014 г.

Практикум 1 по статье "pdb – Interactive Debugger"

Есть такие материалы, которые просто тупо надо выучить. Статья pdb – Interactive Debugger - это практикум для освоения дебаггера Python PDB. Здесь я начинаю повторять примеры из статьи..., но полагаю, что надо разбить практикум на несколько частей.
Да, чуть не забыл, я еще посмотрел видео, откуда, собственно, и взял ссылку на эту статью.
Однако, мне было проще работаь со статьей..., так что видео смотреть не обязательно.
Далее немного записей из видео, потом текст справочного руководства, и только потом начинаем практикум.

Вот, что я записал, просматривая видео - 4 режима работы дебаггера

Script mode
Postmortem mode
Run mode
Trace mode
  1. Script mode python -m pdb buggy.py
1.1) enhanced Script mode python -m pdb -c continue buggy.py
lets the script run until an exeption occurs, then present prompt
python -m pdb -c 'until 2' buggy.py
lets the script run until it reaches line 2, then presents prompt
  1. Postmortem mode
pdb.pm() Used after uncaught exeption has been raised
  1. Run mode pdb.run('some.expression()')
Executes some.expression() under pdb control. Uses current locals and globals to interpret th expression.
  1. Trace mode pdb.set_trace()
Convenient to stick in development and test code/ Just another Python expression so you can conditionalize its execution. I use this all the time. We`ll be using it from now on

bdb.BdbQuit

Вот справка из консоли (help -> topics -> DEBUGGING)

In []:
help> DEBUGGING
``pdb`` --- The Python Debugger
*******************************

The module ``pdb`` defines an interactive source code debugger for
Python programs.  It supports setting (conditional) breakpoints and
single stepping at the source line level, inspection of stack frames,
source code listing, and evaluation of arbitrary Python code in the
context of any stack frame.  It also supports post-mortem debugging
and can be called under program control.

The debugger is extensible --- it is actually defined as the class
``Pdb``. This is currently undocumented but easily understood by
reading the source.  The extension interface uses the modules ``bdb``
and ``cmd``.

The debugger's prompt is ``(Pdb)``. Typical usage to run a program
under control of the debugger is:

   >>> import pdb
   >>> import mymodule
   >>> pdb.run('mymodule.test()')
   > <string>(0)?()
   (Pdb) continue
   > <string>(1)?()
   (Pdb) continue
   NameError: 'spam'
   > <string>(1)?()
   (Pdb)

``pdb.py`` can also be invoked as a script to debug other scripts.
For example:

   python -m pdb myscript.py

When invoked as a script, pdb will automatically enter post-mortem
debugging if the program being debugged exits abnormally. After post-
mortem debugging (or after normal exit of the program), pdb will
restart the program. Automatic restarting preserves pdb's state (such
as breakpoints) and in most cases is more useful than quitting the
debugger upon program's exit.

New in version 2.4: Restarting post-mortem behavior added.

The typical usage to break into the debugger from a running program is
to insert

   import pdb; pdb.set_trace()

at the location you want to break into the debugger.  You can then
step through the code following this statement, and continue running
without the debugger using the ``c`` command.

The typical usage to inspect a crashed program is:

   >>> import pdb
   >>> import mymodule
   >>> mymodule.test()
   Traceback (most recent call last):
     File "<stdin>", line 1, in ?
     File "./mymodule.py", line 4, in test
       test2()
     File "./mymodule.py", line 3, in test2
       print spam
   NameError: spam
   >>> pdb.pm()
   > ./mymodule.py(3)test2()
   -> print spam
   (Pdb)

The module defines the following functions; each enters the debugger
in a slightly different way:

pdb.run(statement[, globals[, locals]])

   Execute the *statement* (given as a string) under debugger control.
   The debugger prompt appears before any code is executed; you can
   set breakpoints and type ``continue``, or you can step through the
   statement using ``step`` or ``next`` (all these commands are
   explained below).  The optional *globals* and *locals* arguments
   specify the environment in which the code is executed; by default
   the dictionary of the module ``__main__`` is used.  (See the
   explanation of the ``exec`` statement or the ``eval()`` built-in
   function.)

pdb.runeval(expression[, globals[, locals]])

   Evaluate the *expression* (given as a string) under debugger
   control.  When ``runeval()`` returns, it returns the value of the
   expression.  Otherwise this function is similar to ``run()``.

pdb.runcall(function[, argument, ...])

   Call the *function* (a function or method object, not a string)
   with the given arguments.  When ``runcall()`` returns, it returns
   whatever the function call returned.  The debugger prompt appears
   as soon as the function is entered.

pdb.set_trace()

   Enter the debugger at the calling stack frame.  This is useful to
   hard-code a breakpoint at a given point in a program, even if the
   code is not otherwise being debugged (e.g. when an assertion
   fails).

pdb.post_mortem([traceback])

   Enter post-mortem debugging of the given *traceback* object.  If no
   *traceback* is given, it uses the one of the exception that is
   currently being handled (an exception must be being handled if the
   default is to be used).

pdb.pm()

   Enter post-mortem debugging of the traceback found in
   ``sys.last_traceback``.

The ``run*`` functions and ``set_trace()`` are aliases for
instantiating the ``Pdb`` class and calling the method of the same
name.  If you want to access further features, you have to do this
yourself:

class class pdb.Pdb(completekey='tab', stdin=None, stdout=None, skip=None)

   ``Pdb`` is the debugger class.

   The *completekey*, *stdin* and *stdout* arguments are passed to the
   underlying ``cmd.Cmd`` class; see the description there.

   The *skip* argument, if given, must be an iterable of glob-style
   module name patterns.  The debugger will not step into frames that
   originate in a module that matches one of these patterns. [1]

   Example call to enable tracing with *skip*:

      import pdb; pdb.Pdb(skip=['django.*']).set_trace()

   New in version 2.7: The *skip* argument.

   run(statement[, globals[, locals]])
   runeval(expression[, globals[, locals]])
   runcall(function[, argument, ...])
   set_trace()

      See the documentation for the functions explained above.

Related help topics: pdb

help>

You are now leaving help and returning to the Python interpreter.
If you want to ask for help on a particular object directly from the
interpreter, you can type "help(object)".  Executing "help('string')"
has the same effect as typing a particular string at the help> prompt.
>>>
>>>
>>>
>>>
>>> help(pdb)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'pdb' is not defined
>>> DEBUGGING
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'DEBUGGING' is not defined
>>> help(DEBUGGING)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'DEBUGGING' is not defined
>>>
Справка эта мне категорически не понравилась, для первого чтения абсолютно не подходит. Так что будем раскладывать все по полочкам, как в базовой статье (ссылка в началае поста)

Библиотека pdb запускается с о опцией -m (python -m pdb myscript.py). Что это за опция?

In []:
C:\Users\kiss\Anaconda>python --help
usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ...
Options and arguments (and corresponding environment variables):
-B     : don't write .py[co] files on import; also PYTHONDONTWRITEBYTECODE=x
-c cmd : program passed in as string (terminates option list)
-d     : debug output from parser; also PYTHONDEBUG=x
-E     : ignore PYTHON* environment variables (such as PYTHONPATH)
-h     : print this help message and exit (also --help)
-i     : inspect interactively after running script; forces a prompt even
         if stdin does not appear to be a terminal; also PYTHONINSPECT=x
-m mod : run library module as a script (terminates option list)
-O     : optimize generated bytecode slightly; also PYTHONOPTIMIZE=x
-OO    : remove doc-strings in addition to the -O optimizations
-R     : use a pseudo-random salt to make hash() values of various types be
         unpredictable between separate invocations of the interpreter, as
         a defense against denial-of-service attacks
-Q arg : division options: -Qold (default), -Qwarn, -Qwarnall, -Qnew
-s     : don't add user site directory to sys.path; also PYTHONNOUSERSITE
-S     : don't imply 'import site' on initialization
-t     : issue warnings about inconsistent tab usage (-tt: issue errors)
-u     : unbuffered binary stdout and stderr; also PYTHONUNBUFFERED=x
         see man page for details on internal buffering relating to '-u'
-v     : verbose (trace import statements); also PYTHONVERBOSE=x
         can be supplied multiple times to increase verbosity
-V     : print the Python version number and exit (also --version)
-W arg : warning control; arg is action:message:category:module:lineno
         also PYTHONWARNINGS=arg
-x     : skip first line of source, allowing use of non-Unix forms of #!cmd
-3     : warn about Python 3.x incompatibilities that 2to3 cannot trivially fix
file   : program read from script file
-      : program read from stdin (default; interactive mode if a tty)
arg ...: arguments passed to program in sys.argv[1:]

Other environment variables:
PYTHONSTARTUP: file executed on interactive startup (no default)
PYTHONPATH   : ';'-separated list of directories prefixed to the
               default module search path.  The result is sys.path.
PYTHONHOME   : alternate <prefix> directory (or <prefix>;<exec_prefix>).
               The default module search path uses <prefix>\lib.
PYTHONCASEOK : ignore case in 'import' statements (Windows).
PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.
PYTHONHASHSEED: if this variable is set to 'random', the effect is the same
   as specifying the -R option: a random value is used to seed the hashes of
   str, bytes and datetime objects.  It can also be set to an integer
   in the range [0,4294967295] to get hash values with a predictable seed.

Инструменты для работы со статьей

Там примеры из небольших файлов. Будем копировать текст в ячейки. Некоторые ячейки, при необходимости записывать на локальный диск, чтобы потом обращаться к ним, как к файлам.
Кроме того, чтобы не мусорить в этой книге, подключим к ней консоль (к этому же ядру). Так что это будет практикум "по-взрослому"

From the Command Line (Script mode)

In [2]:
%qtconsole
# программа в Windows долго открывается
In [6]:
%connect_info
{
  "stdin_port": 49615, 
  "ip": "127.0.0.1", 
  "control_port": 49616, 
  "hb_port": 49617, 
  "signature_scheme": "hmac-sha256", 
  "key": "889cb761-a4ff-48f8-8f7a-406937f85902", 
  "shell_port": 49613, 
  "transport": "tcp", 
  "iopub_port": 49614
}

Paste the above JSON into a file, and connect with:
    $> ipython <app> --existing <file>
or, if you are local, you can connect with just:
    $> ipython <app> --existing kernel-c0333278-fe02-4298-a05c-33ee6249a064.json 
or even just:
    $> ipython <app> --existing 
if this is the most recent IPython session you have started.

Ну вот, консоль подключили, инфо посмотрели. И теперь в меню консоли находим список magic, в консоли вызываем вправку, например "? %%writefile " и читаем, как выполнить команду. Теперь попробуем записать файл в новую папку "pdb/pdb_script.py" ...Папку pdb надо создать заранее, иначе ipython выдаст ошибку.
In [10]:
%%writefile pdb/pdb_script.py
#!/usr/bin/env python
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann.  All rights reserved.
#

class MyObj(object):

    def __init__(self, num_loops):
        self.count = num_loops

    def go(self):
        for i in range(self.count):
            print i
        return

if __name__ == '__main__':
    MyObj(5).go()
Writing pdb/pdb_script.py

Наивная попытка вызвать pdb из консоли заставила учить матчасть.

In [*]:
!python -m pdb pdb/pdb_script.py
Эта команда не проходит, GT console подвисла насмерть, notebook тоже пришлось перегружать. Действительно, для IPython есть magic команды. Обратимся к документации:
И не надо было вызывать из консоли (системной), надо было использовать вызов из консоли Python, но сначала не грех ознакомиться с документацией Ipython на этот счет, так что, читаем дальше:

From IPython: Debugging

After an exception occurs, you can call %debug to jump into the Python debugger (pdb) and examine the problem. Alternatively, if you call %pdb, IPython will automatically start the debugger on any uncaught exception.
You can print variables, see code, execute statements and even walk up and down the call stack to track down the true source of the problem.
This can be an efficient way to develop and debug code, in many cases eliminating the need for print statements or external debugging tools.
You can also step through a program from the beginning by calling %run -d theprogram.py.
In [1]:
%edit pdb/pdb_script.py

Running entire programs via pdb

pdb, the Python debugger, is a powerful interactive debugger which allows you to step through code, set breakpoints, watch variables, etc. IPython makes it very easy to start any script under the control of pdb, regardless of whether you have wrapped it into a ‘main()’ function or not. For this, simply type
%run -d myscript
at an IPython prompt. See the %run command’s documentation for more details, including how to control where pdb will stop execution first.
For more information on the use of the pdb debugger, see Debugger Commands in the Python documentation.
In [5]:
%run
%run:
Run the named file inside IPython as a program.

Usage:
  %run [-n -i -e -G]
       [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )]
       ( -m mod | file ) [args]

Parameters after the filename are passed as command-line arguments to
the program (put in sys.argv). Then, control returns to IPython's
prompt.

This is similar to running at a system prompt:\
  $ python file args\
but with the advantage of giving you IPython's tracebacks, and of
loading all variables into your interactive namespace for further use
(unless -p is used, see below).

The file is executed in a namespace initially consisting only of
__name__=='__main__' and sys.argv constructed as indicated. It thus
sees its environment as if it were being run as a stand-alone program
(except for sharing global objects such as previously imported
modules). But after execution, the IPython interactive namespace gets
updated with all variables defined in the program (except for __name__
and sys.argv). This allows for very convenient loading of code for
interactive work, while giving each program a 'clean sheet' to run in.

Arguments are expanded using shell-like glob match.  Patterns
'*', '?', '[seq]' and '[!seq]' can be used.  Additionally,
tilde '~' will be expanded into user's home directory.  Unlike
real shells, quotation does not suppress expansions.  Use
*two* back slashes (e.g., '\\*') to suppress expansions.
To completely disable these expansions, you can use -G flag.

Options:

-n: __name__ is NOT set to '__main__', but to the running file's name
without extension (as python does under import).  This allows running
scripts and reloading the definitions in them without calling code
protected by an ' if __name__ == "__main__" ' clause.

-i: run the file in IPython's namespace instead of an empty one. This
is useful if you are experimenting with code written in a text editor
which depends on variables defined interactively.

-e: ignore sys.exit() calls or SystemExit exceptions in the script
being run.  This is particularly useful if IPython is being used to
run unittests, which always exit with a sys.exit() call.  In such
cases you are interested in the output of the test results, not in
seeing a traceback of the unittest module.

-t: print timing information at the end of the run.  IPython will give
you an estimated CPU time consumption for your script, which under
Unix uses the resource module to avoid the wraparound problems of
time.clock().  Under Unix, an estimate of time spent on system tasks
is also given (for Windows platforms this is reported as 0.0).

If -t is given, an additional -N<N> option can be given, where <N>
must be an integer indicating how many times you want the script to
run.  The final timing report will include total and per run results.

For example (testing the script uniq_stable.py)::

    In [1]: run -t uniq_stable

    IPython CPU timings (estimated):\
      User  :    0.19597 s.\
      System:        0.0 s.\

    In [2]: run -t -N5 uniq_stable

    IPython CPU timings (estimated):\
    Total runs performed: 5\
      Times :      Total       Per run\
      User  :   0.910862 s,  0.1821724 s.\
      System:        0.0 s,        0.0 s.

-d: run your program under the control of pdb, the Python debugger.
This allows you to execute your program step by step, watch variables,
etc.  Internally, what IPython does is similar to calling:

  pdb.run('execfile("YOURFILENAME")')

with a breakpoint set on line 1 of your file.  You can change the line
number for this automatic breakpoint to be <N> by using the -bN option
(where N must be an integer).  For example::

  %run -d -b40 myscript

will set the first breakpoint at line 40 in myscript.py.  Note that
the first breakpoint must be set on a line which actually does
something (not a comment or docstring) for it to stop execution.

Or you can specify a breakpoint in a different file::

  %run -d -b myotherfile.py:20 myscript

When the pdb debugger starts, you will see a (Pdb) prompt.  You must
first enter 'c' (without quotes) to start execution up to the first
breakpoint.

Entering 'help' gives information about the use of the debugger.  You
can easily see pdb's full documentation with "import pdb;pdb.help()"
at a prompt.

-p: run program under the control of the Python profiler module (which
prints a detailed report of execution times, function calls, etc).

You can pass other options after -p which affect the behavior of the
profiler itself. See the docs for %prun for details.

In this mode, the program's variables do NOT propagate back to the
IPython interactive namespace (because they remain in the namespace
where the profiler executes them).

Internally this triggers a call to %prun, see its documentation for
details on the options available specifically for profiling.

There is one special usage for which the text above doesn't apply:
if the filename ends with .ipy, the file is run as ipython script,
just as if the commands were written on IPython prompt.

-m: specify module name to load instead of script path. Similar to
the -m option for the python interpreter. Use this option last if you
want to combine with other %run options. Unlike the python interpreter
only source modules are allowed no .pyc or .pyo files.
For example::

    %run -m example

will run the example module.

-G: disable shell-like glob expansion of arguments.

WARNING: you must provide at least a filename.

Post-mortem debugging

Going into a debugger when an exception occurs can be extremely useful in order to find the origin of subtle bugs, because pdb opens up at the point in your code which triggered the exception, and while your program is at this point ‘dead’, all the data is still available and you can walk up and down the stack frame and understand the origin of the problem.
You can use the %debug magic after an exception has occurred to start post-mortem debugging. IPython can also call debugger every time your code triggers an uncaught exception. This feature can be toggled with the %pdb magic command, or you can start IPython with the --pdb option.
For a post-mortem debugger in your programs outside IPython, put the following lines toward the top of your ‘main’ routine:
import sys

from IPython.core import ultratb

sys.excepthook = ultratb.FormattedTB(mode='Verbose',

color_scheme='Linux', call_pdb=1)

The mode keyword can be either ‘Verbose’ or ‘Plain’, giving either very detailed or normal tracebacks respectively. The color_scheme keyword can be one of ‘NoColor’, ‘Linux’ (default) or ‘LightBG’. These are the same options which can be set in IPython with --colors and --xmode.
This will give any of your programs detailed, colored tracebacks with automatic invocation of pdb.
In [6]:
%pdb
Automatic pdb calling has been turned ON

In [8]:
%debug help
NOTE: Enter 'c' at the ipdb>  prompt to continue execution.
> <string>(1)<module>()

ipdb> c

Published on Apr 23, 2013
This video shows how the powerful, interactive python shell IPython can be combined with the Python debugger to provide a powerful debugging environment


Посты чуть ниже также могут вас заинтересовать

1 комментарий:

  1. Непонятный вывод вида
    ←[1;32mc:\users\kiss\documents\gitmyscrapy\scrapy_xml_1\xmlfeedspider\xmlfeedspider\pipelines.py←[0m(36)←[0;36mprocess_item←[1;34m
    )←[0m
    [1;32m 35 ←[1;33m ←[0mipdb←[0m←[1;33m.←[0m←[0mset_trace←[0m←[1;33m(←[0m←[1;33m)←[0m←[1;33m←[0m←[0m
    [0m←[1;32m---> 36 ←[1;33m ←[0mip←[0m←[1;33m=←[0m←[0mitem←[0m←[1;33m[←[0m←[1;34m'prxip'←[0m←[1;33m]←[0m ←[1;31m#the list of
    P←[0m←[1;33m←[0m←[0m

    Оказался небольшой ошибкой форматирования
    http://stackoverflow.com/questions/3880596/ipython-showing-gibberish-when-debugging-django-with-netbeans

    ОтветитьУдалить