Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding tests and fixing behaviour on Python 3 #9

Merged
merged 1 commit into from
Jun 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions ipdbugger/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"""
from __future__ import print_function
from __future__ import absolute_import
# pylint: disable=misplaced-bare-raise,protected-access,bare-except
# pylint: disable=protected-access,bare-except
# pylint: disable=missing-docstring,too-many-locals,too-many-branches
import re
import ast
Expand All @@ -28,6 +28,7 @@

import colorama
from termcolor import colored
from future.utils import raise_
from IPython.core.debugger import Pdb

# Enable color printing on screen.
Expand All @@ -36,12 +37,19 @@

class IPDBugger(Pdb):
"""Debugger class, adds functionality to the normal pdb."""
def __init__(self, exc_info, *args, **kwargs):
Pdb.__init__(self, *args, **kwargs)
self.exc_info = exc_info

def do_raise(self, arg):
"""Raise the last exception caught."""
self.do_continue(arg)
_, exc_value, _ = sys.exc_info()

# Annotating the exception for a continual re-raise
_, exc_value, _ = self.exc_info
exc_value._ipdbugger_let_raise = True
raise

raise_(*self.exc_info)

def do_retry(self, arg):
"""Rerun the previous command."""
Expand All @@ -52,7 +60,7 @@ def do_retry(self, arg):
self.curframe.f_lineno = prev_line
break

except:
except ValueError:
prev_line -= 1

self.do_jump(prev_line)
Expand All @@ -77,8 +85,9 @@ def start_debugging():
"""
exc_type, exc_value, exc_tb = sys.exc_info()

# If the exception has been annotated to be re-raised, raise the exception
if hasattr(exc_value, '_ipdbugger_let_raise'):
raise
raise_(*sys.exc_info())

print()
for line in traceback.format_exception(exc_type, exc_value, exc_tb):
Expand All @@ -89,13 +98,14 @@ def start_debugging():

from ipdb.__main__ import wrap_sys_excepthook, def_colors
wrap_sys_excepthook()
IPDBugger(def_colors).set_trace(test_frame)
IPDBugger(exc_info=sys.exc_info(),
color_scheme=def_colors).set_trace(test_frame)


class ErrorsCatchTransformer(ast.NodeTransformer):
"""Surround each statement with a try/except block to catch errors."""
def __init__(self, ignore_exceptions=(), catch_exception=None):
if sys.version_info > (3, 0):
if sys.version_info > (3, 0): # pragma: no cover
start_debug_cmd = ast.Expr(
value=ast.Call(
ast.Name("start_debugging", ast.Load()),
Expand All @@ -104,7 +114,7 @@ def __init__(self, ignore_exceptions=(), catch_exception=None):
)
)

else:
else: # pragma: no cover
start_debug_cmd = ast.Expr(
value=ast.Call(ast.Name("start_debugging", ast.Load()),
[], [], None, None))
Expand Down Expand Up @@ -142,14 +152,14 @@ def generic_visit(self, node):
if (isinstance(node, ast.stmt) and
not isinstance(node, ast.FunctionDef)):

if sys.version_info > (3, 0):
if sys.version_info > (3, 0): # pragma: no cover
new_node = ast.Try( # pylint: disable=no-member
orelse=[],
body=[node],
finalbody=[],
handlers=self.exception_handlers)

else:
else: # pragma: no cover
new_node = ast.TryExcept( # pylint: disable=no-member
orelse=[],
body=[node],
Expand All @@ -164,7 +174,8 @@ def debug(victim, ignore_exceptions=(), catch_exception=None):
"""A decorator function to catch exceptions and enter debug mode.

Args:
victim (object): either a class or function to wrap and debug.
victim (typing.Union(type, function)): either a class or function to
wrap and debug.
ignore_exceptions (list): list of classes of exceptions not to catch.
catch_exception (type): class of exception to catch and debug.
default is None, meaning catch all exceptions.
Expand Down
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
astroid==1.5.3
astroid==1.6.5
attrs==17.4.0
backports.functools-lru-cache==1.4
backports.shutil-get-terminal-size==1.0.0
Expand All @@ -8,6 +8,7 @@ coverage==4.5.1
decorator==4.1.2
enum34==1.1.6
flake8==3.4.1
future==0.16.0
funcsigs==1.0.2
ipdb==0.10.3
ipython==5.5.0
Expand All @@ -28,7 +29,7 @@ py==1.5.3
pycodestyle==2.3.1
pyflakes==1.5.0
Pygments==2.2.0
pylint==1.7.2
pylint==1.9.2
pytest==3.5.0
pytest-cov==2.5.1
scandir==1.5
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Setup file for handling packaging and distribution."""
from setuptools import setup

__version__ = "2.0.0"
__version__ = "2.0.1"

setup(
name="ipdbugger",
Expand All @@ -14,6 +14,7 @@
url="https://github.com/gregoil/ipdbugger",
keywords="ipdb debug debugger exception",
install_requires=["ipdb",
"future",
"colorama",
"termcolor"],
packages=["ipdbugger"],
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_debug.py → tests/test_debug.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""Unit tests for the debug decorator in ipdbugger module."""
from __future__ import absolute_import

from IPython.utils.capture import capture_output
import pytest
from IPython.utils.capture import capture_output

from ipdbugger import debug

Expand Down
74 changes: 74 additions & 0 deletions tests/test_shell.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from __future__ import print_function

import bdb
import pytest
from IPython.utils.capture import capture_output

from ipdbugger import debug

try:
from unittest import mock
except ImportError:
import mock


@mock.patch("sys.stdin")
def test_help(stdin):
"""Test that both raise and retry actions are mentioned on help message."""
@debug
def some_func():
raise ValueError("some error")

stdin.readline.side_effect = ["help", "exit"]
with capture_output() as out, pytest.raises(bdb.BdbQuit):
some_func()

assert "raise" in out.stdout
assert "retry" in out.stdout


@mock.patch("sys.stdin")
def test_raise(stdin):
"""Test that exceptions can be raised back to the main program."""
@debug
def some_func():
raise ValueError("some error")

with pytest.raises(ValueError,
match="some error"):
stdin.readline.return_value = "raise"
some_func()


@mock.patch("sys.stdin")
def test_continue(stdin):
"""Test that program execution can continue after an error occurred."""
@debug
def some_func():
raise ValueError("some error")

stdin.readline.return_value = "continue"
some_func()


@mock.patch("sys.stdin")
def test_retry(stdin):
"""Test retrying buggy actions."""
class A:
def __init__(self):
self.count = 0

@debug
def func(self):
self.sometimes_buggy()

def sometimes_buggy(self):
if self.count <= 1:
self.count += 1
raise ValueError("some error")

stdin.readline.side_effect = ["retry", "continue"]
a = A()
a.func()

assert a.count == 2
Empty file removed tests/unit/__init__.py
Empty file.
3 changes: 0 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,3 @@ addopts = --cov-report=html --cov=ipdbugger

[coverage:run]
branch = True

[coverage:report]
exclude_lines = if sys.version_info