From 04bb1e712ab39a61adecad1153e8afee79402c66 2014-09-28 03:25:23 From: MinRK Date: 2014-09-28 03:25:23 Subject: [PATCH] show tracebacks for errors in formatters instead of FormatterWarnings --- diff --git a/IPython/core/formatters.py b/IPython/core/formatters.py index 1e810fe..ec96192 100644 --- a/IPython/core/formatters.py +++ b/IPython/core/formatters.py @@ -5,25 +5,11 @@ Inheritance diagram: .. inheritance-diagram:: IPython.core.formatters :parts: 3 - -Authors: - -* Robert Kern -* Brian Granger """ -#----------------------------------------------------------------------------- -# Copyright (C) 2010-2011, IPython Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file COPYING.txt, distributed with this software. -#----------------------------------------------------------------------------- -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. -# Stdlib imports import abc import inspect import sys @@ -32,8 +18,8 @@ import warnings from IPython.external.decorator import decorator -# Our own imports from IPython.config.configurable import Configurable +from IPython.core.getipython import get_ipython from IPython.lib import pretty from IPython.utils.traitlets import ( Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List, @@ -223,6 +209,18 @@ class DisplayFormatter(Configurable): # Formatters for specific format types (text, html, svg, etc.) #----------------------------------------------------------------------------- + +def _safe_repr(obj): + """Try to return a repr of an object + + always returns a string, at least. + """ + try: + return repr(obj) + except Exception as e: + return "un-repr-able object (%r)" % e + + class FormatterWarning(UserWarning): """Warning class for errors in formatters""" @@ -234,10 +232,13 @@ def warn_format_error(method, self, *args, **kwargs): except NotImplementedError as e: # don't warn on NotImplementedErrors return None - except Exception as e: - warnings.warn("Exception in %s formatter: %s" % (self.format_type, e), - FormatterWarning, - ) + except Exception: + exc_info = sys.exc_info() + ip = get_ipython() + if ip is not None: + ip.showtraceback(exc_info) + else: + traceback.print_exception(*exc_info) return None if r is None or isinstance(r, self._return_type) or \ (isinstance(r, tuple) and r and isinstance(r[0], self._return_type)): @@ -245,7 +246,7 @@ def warn_format_error(method, self, *args, **kwargs): else: warnings.warn( "%s formatter returned invalid type %s (expected %s) for object: %s" % \ - (self.format_type, type(r), self._return_type, pretty._safe_repr(args[0])), + (self.format_type, type(r), self._return_type, _safe_repr(args[0])), FormatterWarning ) @@ -672,7 +673,7 @@ class PlainTextFormatter(BaseFormatter): def __call__(self, obj): """Compute the pretty representation of the object.""" if not self.pprint: - return pretty._safe_repr(obj) + return repr(obj) else: # This uses use StringIO, as cStringIO doesn't handle unicode. stream = StringIO() diff --git a/IPython/core/tests/test_formatters.py b/IPython/core/tests/test_formatters.py index 65105d8..de3cac5 100644 --- a/IPython/core/tests/test_formatters.py +++ b/IPython/core/tests/test_formatters.py @@ -25,6 +25,10 @@ class B(A): class C: pass +class BadRepr(object): + def __repr__(self): + raise ValueError("bad repr") + class BadPretty(object): _repr_pretty_ = None @@ -234,30 +238,30 @@ def test_pop_string(): nt.assert_is(f.pop(type_str, None), None) -def test_warn_error_method(): +def test_error_method(): f = HTMLFormatter() class BadHTML(object): def _repr_html_(self): - return 1/0 + raise ValueError("Bad HTML") bad = BadHTML() with capture_output() as captured: result = f(bad) nt.assert_is(result, None) - nt.assert_in("FormatterWarning", captured.stderr) - nt.assert_in("text/html", captured.stderr) - nt.assert_in("zero", captured.stderr) + nt.assert_in("Traceback", captured.stdout) + nt.assert_in("Bad HTML", captured.stdout) + nt.assert_in("_repr_html_", captured.stdout) def test_nowarn_notimplemented(): f = HTMLFormatter() class HTMLNotImplemented(object): def _repr_html_(self): raise NotImplementedError - return 1/0 h = HTMLNotImplemented() with capture_output() as captured: result = f(h) nt.assert_is(result, None) - nt.assert_not_in("FormatterWarning", captured.stderr) + nt.assert_equal("", captured.stderr) + nt.assert_equal("", captured.stdout) def test_warn_error_for_type(): f = HTMLFormatter() @@ -265,11 +269,11 @@ def test_warn_error_for_type(): with capture_output() as captured: result = f(5) nt.assert_is(result, None) - nt.assert_in("FormatterWarning", captured.stderr) - nt.assert_in("text/html", captured.stderr) - nt.assert_in("name_error", captured.stderr) + nt.assert_in("Traceback", captured.stdout) + nt.assert_in("NameError", captured.stdout) + nt.assert_in("name_error", captured.stdout) -def test_warn_error_pretty_method(): +def test_error_pretty_method(): f = PlainTextFormatter() class BadPretty(object): def _repr_pretty_(self): @@ -278,9 +282,23 @@ def test_warn_error_pretty_method(): with capture_output() as captured: result = f(bad) nt.assert_is(result, None) - nt.assert_in("FormatterWarning", captured.stderr) - nt.assert_in("text/plain", captured.stderr) - nt.assert_in("argument", captured.stderr) + nt.assert_in("Traceback", captured.stdout) + nt.assert_in("_repr_pretty_", captured.stdout) + nt.assert_in("given", captured.stdout) + nt.assert_in("argument", captured.stdout) + + +def test_bad_repr_traceback(): + f = PlainTextFormatter() + bad = BadRepr() + with capture_output() as captured: + result = f(bad) + # catches error, returns None + nt.assert_is(result, None) + nt.assert_in("Traceback", captured.stdout) + nt.assert_in("__repr__", captured.stdout) + nt.assert_in("ValueError", captured.stdout) + class MakePDF(object): def _repr_pdf_(self):