Repository: ipython/ipython

Issue:
repr_html exception warning on qtconsole with pandas  #4745 
#4745  - warn on failed formatter calls

since qtconsole html output is limited, pandas choses to provide an
html repr on ipnb only and raise an exception from `_repr_html_` when in qtconsole to fallback
to a text repr. This used to work fine, but #4745 now results in a warning message constantly 
being emitted about the exception being raised.

@minrk, how about supressing a `NotImplementedError` as a form of contract?



Discussion:
There is already an official way to communicate no output - returning None. We can add support for NotImplementedError (and/or NotImplemented), but it would be redundant.


Resolving patch:
diff --git a/IPython/core/formatters.py b/IPython/core/formatters.py
--- a/IPython/core/formatters.py
+++ b/IPython/core/formatters.py
@@ -183,22 +183,31 @@ def format_types(self):
 # Formatters for specific format types (text, html, svg, etc.)
 #-----------------------------------------------------------------------------
 
+class FormatterWarning(UserWarning):
+    """Warning class for errors in formatters"""
+
 @decorator
 def warn_format_error(method, self, *args, **kwargs):
     """decorator for warning on failed format call"""
     try:
         r = method(self, *args, **kwargs)
+    except NotImplementedError as e:
+        # don't warn on NotImplementedErrors
+        return None
     except Exception as e:
-        warn("Exception in %s formatter: %s" % (self.format_type, e))
+        warnings.warn("Exception in %s formatter: %s" % (self.format_type, e),
+            FormatterWarning,
+        )
         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)):
         return r
     else:
-        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])
-        ))
-    
+        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])),
+            FormatterWarning
+        )
 
 
 class FormatterABC(with_metaclass(abc.ABCMeta, object)):


Test patch:
diff --git a/IPython/core/tests/test_formatters.py b/IPython/core/tests/test_formatters.py
--- a/IPython/core/tests/test_formatters.py
+++ b/IPython/core/tests/test_formatters.py
@@ -240,17 +240,29 @@ def _repr_html_(self):
     with capture_output() as captured:
         result = f(bad)
     nt.assert_is(result, None)
-    nt.assert_in("WARNING", captured.stderr)
+    nt.assert_in("FormatterWarning", captured.stderr)
     nt.assert_in("text/html", captured.stderr)
     nt.assert_in("zero", captured.stderr)
 
+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)
+
 def test_warn_error_for_type():
     f = HTMLFormatter()
     f.for_type(int, lambda i: name_error)
     with capture_output() as captured:
         result = f(5)
     nt.assert_is(result, None)
-    nt.assert_in("WARNING", captured.stderr)
+    nt.assert_in("FormatterWarning", captured.stderr)
     nt.assert_in("text/html", captured.stderr)
     nt.assert_in("name_error", captured.stderr)
 
@@ -263,7 +275,7 @@ def _repr_pretty_(self):
     with capture_output() as captured:
         result = f(bad)
     nt.assert_is(result, None)
-    nt.assert_in("WARNING", captured.stderr)
+    nt.assert_in("FormatterWarning", captured.stderr)
     nt.assert_in("text/plain", captured.stderr)
     nt.assert_in("argument", captured.stderr)