##// END OF EJS Templates
Reset the interactive namespace __warningregistry__ before executing code...
Reset the interactive namespace __warningregistry__ before executing code Fixes #6611. Idea: Right now, people often don't see important warnings when running code in IPython, because (to a first approximation) any given warning will only issue once per session. Blink and you'll miss it! This is a very common contributor to confused emails to numpy-discussion. E.g.: In [5]: 1 / my_array_with_random_contents /home/njs/.user-python2.7-64bit-3/bin/ipython:1: RuntimeWarning: divide by zero encountered in divide #!/home/njs/.user-python2.7-64bit-3/bin/python Out[5]: array([ 1.77073316, -2.29765021, -2.01800811, ..., 1.13871243, -1.08302964, -8.6185091 ]) Oo, right, guess I gotta be careful of those zeros -- thanks, numpy, for giving me that warning! A few days later: In [592]: 1 / some_other_array Out[592]: array([ 3.07735763, 0.50769289, 0.83984078, ..., -0.67563917, -0.85736257, -1.36511271]) Oops, it turns out that this array had a zero in it too, and that's going to bite me later. But no warning this time! The effect of this commit is to make it so that warnings triggered by the code in cell 5 do *not* suppress warnings triggered by the code in cell 592. Note that this only applies to warnings triggered *directly* by code entered interactively -- if somepkg.foo() calls anotherpkg.bad_func() which issues a warning, then this warning will still only be displayed once, even if multiple cells call somepkg.foo(). But if cell 5 and cell 592 both call anotherpkg.bad_func() directly, then both will get warnings. (Important exception: if foo() is defined *interactively*, and calls anotherpkg.bad_func(), then every cell that calls foo() will display the warning again. This is unavoidable without fixes to CPython upstream.) Explanation: Python's warning system has some weird quirks. By default, it tries to suppress duplicate warnings, where "duplicate" means the same warning message triggered twice by the same line of code. This requires determining which line of code is responsible for triggering a warning, and this is controlled by the stacklevel= argument to warnings.warn. Basically, though, the idea is that if foo() calls bar() which calls baz() which calls some_deprecated_api(), then baz() will get counted as being "responsible", and the warning system will make a note that the usage of some_deprecated_api() inside baz() has already been warned about and doesn't need to be warned about again. So far so good. To accomplish this, obviously, there has to be a record of somewhere which line this was. You might think that this would be done by recording the filename:linenumber pair in a dict inside the warnings module, or something like that. You would be wrong. What actually happens is that the warnings module will use stack introspection to reach into baz()'s execution environment, create a global (module-level) variable there named __warningregistry__, and then, inside this dictionary, record just the line number. Basically, it assumes that any given module contains only one line 1, only one line 2, etc., so storing the filename is irrelevant. Obviously for interactive code this is totally wrong -- all cells share the same execution environment and global namespace, and they all contain a new line 1. Currently the warnings module treats these as if they were all the same line. In fact they are not the same line; once we have executed a given chunk of code, we will never see those particular lines again. As soon as a given chunk of code finishes executing, its line number labels become meaningless, and the corresponding warning registry entries become meaningless as well. Therefore, with this patch we delete the __warningregistry__ each time we execute a new block of code.

File last commit:

r18025:04bb1e71
r18548:61431d7d
Show More
test_formatters.py
340 lines | 9.5 KiB | text/x-python | PythonLexer
"""Tests for the Formatters."""
from math import pi
try:
import numpy
except:
numpy = None
import nose.tools as nt
from IPython.config import Config
from IPython.core.formatters import (
PlainTextFormatter, HTMLFormatter, PDFFormatter, _mod_name_key
)
from IPython.utils.io import capture_output
class A(object):
def __repr__(self):
return 'A()'
class B(A):
def __repr__(self):
return 'B()'
class C:
pass
class BadRepr(object):
def __repr__(self):
raise ValueError("bad repr")
class BadPretty(object):
_repr_pretty_ = None
class GoodPretty(object):
def _repr_pretty_(self, pp, cycle):
pp.text('foo')
def __repr__(self):
return 'GoodPretty()'
def foo_printer(obj, pp, cycle):
pp.text('foo')
def test_pretty():
f = PlainTextFormatter()
f.for_type(A, foo_printer)
nt.assert_equal(f(A()), 'foo')
nt.assert_equal(f(B()), 'foo')
nt.assert_equal(f(GoodPretty()), 'foo')
# Just don't raise an exception for the following:
f(BadPretty())
f.pprint = False
nt.assert_equal(f(A()), 'A()')
nt.assert_equal(f(B()), 'B()')
nt.assert_equal(f(GoodPretty()), 'GoodPretty()')
def test_deferred():
f = PlainTextFormatter()
def test_precision():
"""test various values for float_precision."""
f = PlainTextFormatter()
nt.assert_equal(f(pi), repr(pi))
f.float_precision = 0
if numpy:
po = numpy.get_printoptions()
nt.assert_equal(po['precision'], 0)
nt.assert_equal(f(pi), '3')
f.float_precision = 2
if numpy:
po = numpy.get_printoptions()
nt.assert_equal(po['precision'], 2)
nt.assert_equal(f(pi), '3.14')
f.float_precision = '%g'
if numpy:
po = numpy.get_printoptions()
nt.assert_equal(po['precision'], 2)
nt.assert_equal(f(pi), '3.14159')
f.float_precision = '%e'
nt.assert_equal(f(pi), '3.141593e+00')
f.float_precision = ''
if numpy:
po = numpy.get_printoptions()
nt.assert_equal(po['precision'], 8)
nt.assert_equal(f(pi), repr(pi))
def test_bad_precision():
"""test various invalid values for float_precision."""
f = PlainTextFormatter()
def set_fp(p):
f.float_precision=p
nt.assert_raises(ValueError, set_fp, '%')
nt.assert_raises(ValueError, set_fp, '%.3f%i')
nt.assert_raises(ValueError, set_fp, 'foo')
nt.assert_raises(ValueError, set_fp, -1)
def test_for_type():
f = PlainTextFormatter()
# initial return, None
nt.assert_is(f.for_type(C, foo_printer), None)
# no func queries
nt.assert_is(f.for_type(C), foo_printer)
# shouldn't change anything
nt.assert_is(f.for_type(C), foo_printer)
# None should do the same
nt.assert_is(f.for_type(C, None), foo_printer)
nt.assert_is(f.for_type(C, None), foo_printer)
def test_for_type_string():
f = PlainTextFormatter()
mod = C.__module__
type_str = '%s.%s' % (C.__module__, 'C')
# initial return, None
nt.assert_is(f.for_type(type_str, foo_printer), None)
# no func queries
nt.assert_is(f.for_type(type_str), foo_printer)
nt.assert_in(_mod_name_key(C), f.deferred_printers)
nt.assert_is(f.for_type(C), foo_printer)
nt.assert_not_in(_mod_name_key(C), f.deferred_printers)
nt.assert_in(C, f.type_printers)
def test_for_type_by_name():
f = PlainTextFormatter()
mod = C.__module__
# initial return, None
nt.assert_is(f.for_type_by_name(mod, 'C', foo_printer), None)
# no func queries
nt.assert_is(f.for_type_by_name(mod, 'C'), foo_printer)
# shouldn't change anything
nt.assert_is(f.for_type_by_name(mod, 'C'), foo_printer)
# None should do the same
nt.assert_is(f.for_type_by_name(mod, 'C', None), foo_printer)
nt.assert_is(f.for_type_by_name(mod, 'C', None), foo_printer)
def test_lookup():
f = PlainTextFormatter()
f.for_type(C, foo_printer)
nt.assert_is(f.lookup(C()), foo_printer)
with nt.assert_raises(KeyError):
f.lookup(A())
def test_lookup_string():
f = PlainTextFormatter()
type_str = '%s.%s' % (C.__module__, 'C')
f.for_type(type_str, foo_printer)
nt.assert_is(f.lookup(C()), foo_printer)
# should move from deferred to imported dict
nt.assert_not_in(_mod_name_key(C), f.deferred_printers)
nt.assert_in(C, f.type_printers)
def test_lookup_by_type():
f = PlainTextFormatter()
f.for_type(C, foo_printer)
nt.assert_is(f.lookup_by_type(C), foo_printer)
type_str = '%s.%s' % (C.__module__, 'C')
with nt.assert_raises(KeyError):
f.lookup_by_type(A)
def test_lookup_by_type_string():
f = PlainTextFormatter()
type_str = '%s.%s' % (C.__module__, 'C')
f.for_type(type_str, foo_printer)
# verify insertion
nt.assert_in(_mod_name_key(C), f.deferred_printers)
nt.assert_not_in(C, f.type_printers)
nt.assert_is(f.lookup_by_type(type_str), foo_printer)
# lookup by string doesn't cause import
nt.assert_in(_mod_name_key(C), f.deferred_printers)
nt.assert_not_in(C, f.type_printers)
nt.assert_is(f.lookup_by_type(C), foo_printer)
# should move from deferred to imported dict
nt.assert_not_in(_mod_name_key(C), f.deferred_printers)
nt.assert_in(C, f.type_printers)
def test_in_formatter():
f = PlainTextFormatter()
f.for_type(C, foo_printer)
type_str = '%s.%s' % (C.__module__, 'C')
nt.assert_in(C, f)
nt.assert_in(type_str, f)
def test_string_in_formatter():
f = PlainTextFormatter()
type_str = '%s.%s' % (C.__module__, 'C')
f.for_type(type_str, foo_printer)
nt.assert_in(type_str, f)
nt.assert_in(C, f)
def test_pop():
f = PlainTextFormatter()
f.for_type(C, foo_printer)
nt.assert_is(f.lookup_by_type(C), foo_printer)
nt.assert_is(f.pop(C, None), foo_printer)
f.for_type(C, foo_printer)
nt.assert_is(f.pop(C), foo_printer)
with nt.assert_raises(KeyError):
f.lookup_by_type(C)
with nt.assert_raises(KeyError):
f.pop(C)
with nt.assert_raises(KeyError):
f.pop(A)
nt.assert_is(f.pop(A, None), None)
def test_pop_string():
f = PlainTextFormatter()
type_str = '%s.%s' % (C.__module__, 'C')
with nt.assert_raises(KeyError):
f.pop(type_str)
f.for_type(type_str, foo_printer)
f.pop(type_str)
with nt.assert_raises(KeyError):
f.lookup_by_type(C)
with nt.assert_raises(KeyError):
f.pop(type_str)
f.for_type(C, foo_printer)
nt.assert_is(f.pop(type_str, None), foo_printer)
with nt.assert_raises(KeyError):
f.lookup_by_type(C)
with nt.assert_raises(KeyError):
f.pop(type_str)
nt.assert_is(f.pop(type_str, None), None)
def test_error_method():
f = HTMLFormatter()
class BadHTML(object):
def _repr_html_(self):
raise ValueError("Bad HTML")
bad = BadHTML()
with capture_output() as captured:
result = f(bad)
nt.assert_is(result, None)
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
h = HTMLNotImplemented()
with capture_output() as captured:
result = f(h)
nt.assert_is(result, None)
nt.assert_equal("", captured.stderr)
nt.assert_equal("", captured.stdout)
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("Traceback", captured.stdout)
nt.assert_in("NameError", captured.stdout)
nt.assert_in("name_error", captured.stdout)
def test_error_pretty_method():
f = PlainTextFormatter()
class BadPretty(object):
def _repr_pretty_(self):
return "hello"
bad = BadPretty()
with capture_output() as captured:
result = f(bad)
nt.assert_is(result, None)
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):
return 'PDF'
def test_pdf_formatter():
pdf = MakePDF()
f = PDFFormatter()
nt.assert_equal(f(pdf), 'PDF')
def test_print_method_bound():
f = HTMLFormatter()
class MyHTML(object):
def _repr_html_(self):
return "hello"
with capture_output() as captured:
result = f(MyHTML)
nt.assert_is(result, None)
nt.assert_not_in("FormatterWarning", captured.stderr)
with capture_output() as captured:
result = f(MyHTML())
nt.assert_equal(result, "hello")
nt.assert_equal(captured.stderr, "")
def test_format_config():
"""config objects don't pretend to support fancy reprs with lazy attrs"""
f = HTMLFormatter()
cfg = Config()
with capture_output() as captured:
result = f(cfg)
nt.assert_is(result, None)
nt.assert_equal(captured.stderr, "")
with capture_output() as captured:
result = f(Config)
nt.assert_is(result, None)
nt.assert_equal(captured.stderr, "")