test_debugger.py
326 lines
| 8.1 KiB
| text/x-python
|
PythonLexer
Fernando Perez
|
r7087 | """Tests for debugging machinery. | ||
""" | ||||
Min RK
|
r22742 | # Copyright (c) IPython Development Team. | ||
# Distributed under the terms of the Modified BSD License. | ||||
Fernando Perez
|
r7087 | |||
Matthias Bussonnier
|
r25844 | import bdb | ||
import builtins | ||||
import os | ||||
Matthias Bussonnier
|
r25661 | import signal | ||
Matthias Bussonnier
|
r25844 | import subprocess | ||
Bradley M. Froehle
|
r7929 | import sys | ||
Matthias Bussonnier
|
r25661 | import time | ||
Min RK
|
r22742 | import warnings | ||
Matthias Bussonnier
|
r25844 | from subprocess import PIPE, CalledProcessError, check_output | ||
Matthias Bussonnier
|
r25661 | from tempfile import NamedTemporaryFile | ||
Matthias Bussonnier
|
r25844 | from textwrap import dedent | ||
Matthias Bussonnier
|
r25661 | from unittest.mock import patch | ||
Bradley M. Froehle
|
r7929 | |||
Fernando Perez
|
r7087 | import nose.tools as nt | ||
from IPython.core import debugger | ||||
Matthias Bussonnier
|
r25844 | from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE | ||
from IPython.testing.decorators import skip_win32 | ||||
Fernando Perez
|
r7087 | |||
#----------------------------------------------------------------------------- | ||||
Bradley M. Froehle
|
r7929 | # Helper classes, from CPython's Pdb test suite | ||
#----------------------------------------------------------------------------- | ||||
class _FakeInput(object): | ||||
""" | ||||
A fake input stream for pdb's interactive debugger. Whenever a | ||||
line is read, print it (to simulate the user typing it), and then | ||||
return it. The set of lines to return is specified in the | ||||
constructor; they should not have trailing newlines. | ||||
""" | ||||
def __init__(self, lines): | ||||
self.lines = iter(lines) | ||||
def readline(self): | ||||
line = next(self.lines) | ||||
Thomas Kluyver
|
r13348 | print(line) | ||
Bradley M. Froehle
|
r7929 | return line+'\n' | ||
class PdbTestInput(object): | ||||
"""Context manager that makes testing Pdb in doctests easier.""" | ||||
def __init__(self, input): | ||||
self.input = input | ||||
def __enter__(self): | ||||
self.real_stdin = sys.stdin | ||||
sys.stdin = _FakeInput(self.input) | ||||
def __exit__(self, *exc): | ||||
sys.stdin = self.real_stdin | ||||
#----------------------------------------------------------------------------- | ||||
Fernando Perez
|
r7087 | # Tests | ||
#----------------------------------------------------------------------------- | ||||
def test_longer_repr(): | ||||
Thomas Kluyver
|
r13375 | try: | ||
from reprlib import repr as trepr # Py 3 | ||||
except ImportError: | ||||
from repr import repr as trepr # Py 2 | ||||
Fernando Perez
|
r7087 | |||
a = '1234567890'* 7 | ||||
ar = "'1234567890123456789012345678901234567890123456789012345678901234567890'" | ||||
a_trunc = "'123456789012...8901234567890'" | ||||
Bradley M. Froehle
|
r7875 | nt.assert_equal(trepr(a), a_trunc) | ||
Fernando Perez
|
r7087 | # The creation of our tracer modifies the repr module's repr function | ||
# in-place, since that global is used directly by the stdlib's pdb module. | ||||
Min RK
|
r22742 | with warnings.catch_warnings(): | ||
warnings.simplefilter('ignore', DeprecationWarning) | ||||
debugger.Tracer() | ||||
Bradley M. Froehle
|
r7875 | nt.assert_equal(trepr(a), ar) | ||
Bradley M. Froehle
|
r7929 | |||
def test_ipdb_magics(): | ||||
'''Test calling some IPython magics from ipdb. | ||||
First, set up some test functions and classes which we can inspect. | ||||
>>> class ExampleClass(object): | ||||
... """Docstring for ExampleClass.""" | ||||
... def __init__(self): | ||||
... """Docstring for ExampleClass.__init__""" | ||||
... pass | ||||
... def __str__(self): | ||||
... return "ExampleClass()" | ||||
>>> def example_function(x, y, z="hello"): | ||||
... """Docstring for example_function.""" | ||||
... pass | ||||
Thomas Kluyver
|
r12286 | >>> old_trace = sys.gettrace() | ||
Bradley M. Froehle
|
r7929 | Create a function which triggers ipdb. | ||
>>> def trigger_ipdb(): | ||||
... a = ExampleClass() | ||||
... debugger.Pdb().set_trace() | ||||
>>> with PdbTestInput([ | ||||
... 'pdef example_function', | ||||
... 'pdoc ExampleClass', | ||||
Matthias Bussonnier
|
r21851 | ... 'up', | ||
... 'down', | ||||
... 'list', | ||||
Bradley M. Froehle
|
r7929 | ... 'pinfo a', | ||
Matthias Bussonnier
|
r21851 | ... 'll', | ||
Bradley M. Froehle
|
r7929 | ... 'continue', | ||
... ]): | ||||
... trigger_ipdb() | ||||
--Return-- | ||||
None | ||||
> <doctest ...>(3)trigger_ipdb() | ||||
1 def trigger_ipdb(): | ||||
2 a = ExampleClass() | ||||
----> 3 debugger.Pdb().set_trace() | ||||
<BLANKLINE> | ||||
ipdb> pdef example_function | ||||
example_function(x, y, z='hello') | ||||
ipdb> pdoc ExampleClass | ||||
MinRK
|
r15712 | Class docstring: | ||
Bradley M. Froehle
|
r7929 | Docstring for ExampleClass. | ||
MinRK
|
r15712 | Init docstring: | ||
Bradley M. Froehle
|
r7929 | Docstring for ExampleClass.__init__ | ||
Matthias Bussonnier
|
r21851 | ipdb> up | ||
> <doctest ...>(11)<module>() | ||||
Thomas Kluyver
|
r21945 | 7 'pinfo a', | ||
8 'll', | ||||
Matthias Bussonnier
|
r21851 | 9 'continue', | ||
10 ]): | ||||
---> 11 trigger_ipdb() | ||||
<BLANKLINE> | ||||
ipdb> down | ||||
None | ||||
> <doctest ...>(3)trigger_ipdb() | ||||
1 def trigger_ipdb(): | ||||
2 a = ExampleClass() | ||||
----> 3 debugger.Pdb().set_trace() | ||||
<BLANKLINE> | ||||
ipdb> list | ||||
1 def trigger_ipdb(): | ||||
2 a = ExampleClass() | ||||
----> 3 debugger.Pdb().set_trace() | ||||
<BLANKLINE> | ||||
Bradley M. Froehle
|
r7929 | ipdb> pinfo a | ||
MinRK
|
r15711 | Type: ExampleClass | ||
MinRK
|
r15712 | String form: ExampleClass() | ||
MinRK
|
r15711 | Namespace: Local... | ||
Docstring: Docstring for ExampleClass. | ||||
MinRK
|
r15712 | Init docstring: Docstring for ExampleClass.__init__ | ||
Matthias Bussonnier
|
r21851 | ipdb> ll | ||
1 def trigger_ipdb(): | ||||
2 a = ExampleClass() | ||||
----> 3 debugger.Pdb().set_trace() | ||||
<BLANKLINE> | ||||
Bradley M. Froehle
|
r7929 | ipdb> continue | ||
Thomas Kluyver
|
r12286 | |||
Restore previous trace function, e.g. for coverage.py | ||||
>>> sys.settrace(old_trace) | ||||
Bradley M. Froehle
|
r7929 | ''' | ||
Bradley M. Froehle
|
r8929 | |||
Thomas Kluyver
|
r11124 | def test_ipdb_magics2(): | ||
Bradley M. Froehle
|
r8929 | '''Test ipdb with a very short function. | ||
Thomas Kluyver
|
r12286 | |||
>>> old_trace = sys.gettrace() | ||||
Bradley M. Froehle
|
r8929 | |||
>>> def bar(): | ||||
... pass | ||||
Run ipdb. | ||||
>>> with PdbTestInput([ | ||||
... 'continue', | ||||
... ]): | ||||
... debugger.Pdb().runcall(bar) | ||||
> <doctest ...>(2)bar() | ||||
1 def bar(): | ||||
----> 2 pass | ||||
<BLANKLINE> | ||||
ipdb> continue | ||||
Thomas Kluyver
|
r12286 | |||
Restore previous trace function, e.g. for coverage.py | ||||
>>> sys.settrace(old_trace) | ||||
Bradley M. Froehle
|
r8929 | ''' | ||
Matthias Bussonnier
|
r22569 | |||
def can_quit(): | ||||
'''Test that quit work in ipydb | ||||
>>> old_trace = sys.gettrace() | ||||
>>> def bar(): | ||||
... pass | ||||
>>> with PdbTestInput([ | ||||
... 'quit', | ||||
... ]): | ||||
... debugger.Pdb().runcall(bar) | ||||
> <doctest ...>(2)bar() | ||||
1 def bar(): | ||||
----> 2 pass | ||||
<BLANKLINE> | ||||
ipdb> quit | ||||
Restore previous trace function, e.g. for coverage.py | ||||
>>> sys.settrace(old_trace) | ||||
''' | ||||
def can_exit(): | ||||
'''Test that quit work in ipydb | ||||
>>> old_trace = sys.gettrace() | ||||
>>> def bar(): | ||||
... pass | ||||
>>> with PdbTestInput([ | ||||
... 'exit', | ||||
... ]): | ||||
... debugger.Pdb().runcall(bar) | ||||
> <doctest ...>(2)bar() | ||||
1 def bar(): | ||||
----> 2 pass | ||||
<BLANKLINE> | ||||
ipdb> exit | ||||
Restore previous trace function, e.g. for coverage.py | ||||
>>> sys.settrace(old_trace) | ||||
''' | ||||
Matthias Bussonnier
|
r25661 | |||
def test_interruptible_core_debugger(): | ||||
"""The debugger can be interrupted. | ||||
The presumption is there is some mechanism that causes a KeyboardInterrupt | ||||
(this is implemented in ipykernel). We want to ensure the | ||||
KeyboardInterrupt cause debugging to cease. | ||||
""" | ||||
def raising_input(msg="", called=[0]): | ||||
called[0] += 1 | ||||
if called[0] == 1: | ||||
raise KeyboardInterrupt() | ||||
else: | ||||
raise AssertionError("input() should only be called once!") | ||||
with patch.object(builtins, "input", raising_input): | ||||
debugger.InterruptiblePdb().set_trace() | ||||
# The way this test will fail is by set_trace() never exiting, | ||||
# resulting in a timeout by the test runner. The alternative | ||||
# implementation would involve a subprocess, but that adds issues with | ||||
# interrupting subprocesses that are rather complex, so it's simpler | ||||
# just to do it this way. | ||||
Matthias Bussonnier
|
r25844 | |||
@skip_win32 | ||||
def test_xmode_skip(): | ||||
"""that xmode skip frames | ||||
Not as a doctest as pytest does not run doctests. | ||||
""" | ||||
import pexpect | ||||
env = os.environ.copy() | ||||
env["IPY_TEST_SIMPLE_PROMPT"] = "1" | ||||
child = pexpect.spawn( | ||||
sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env | ||||
) | ||||
child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE | ||||
child.expect("IPython") | ||||
child.expect("\n") | ||||
child.expect_exact("In [1]") | ||||
block = dedent( | ||||
""" | ||||
def f(): | ||||
__tracebackhide__ = True | ||||
g() | ||||
def g(): | ||||
raise ValueError | ||||
f() | ||||
""" | ||||
) | ||||
for line in block.splitlines(): | ||||
child.sendline(line) | ||||
child.expect_exact(line) | ||||
child.expect_exact("skipping") | ||||
block = dedent( | ||||
""" | ||||
def f(): | ||||
__tracebackhide__ = True | ||||
g() | ||||
def g(): | ||||
from IPython.core.debugger import set_trace | ||||
set_trace() | ||||
f() | ||||
""" | ||||
) | ||||
for line in block.splitlines(): | ||||
child.sendline(line) | ||||
child.expect_exact(line) | ||||
child.expect("ipdb>") | ||||
child.sendline("w") | ||||
child.expect("hidden") | ||||
child.expect("ipdb>") | ||||
child.sendline("skip_hidden false") | ||||
child.sendline("w") | ||||
child.expect("__traceba") | ||||
child.expect("ipdb>") | ||||
child.close() | ||||