test_pretty.py
540 lines
| 14.5 KiB
| text/x-python
|
PythonLexer
|
r20662 | # coding: utf-8 | ||
|
r18024 | """Tests for IPython.lib.pretty.""" | ||
# Copyright (c) IPython Development Team. | ||||
|
r6295 | # Distributed under the terms of the Modified BSD License. | ||
|
r27204 | from collections import Counter, defaultdict, deque, OrderedDict, UserList | ||
|
r24372 | import os | ||
|
r27061 | import pytest | ||
|
r23706 | import types | ||
import string | ||||
|
r27061 | import sys | ||
|
r23706 | import unittest | ||
|
r21375 | |||
|
r26183 | import pytest | ||
|
r6295 | |||
from IPython.lib import pretty | ||||
|
r18420 | |||
|
r22960 | from io import StringIO | ||
|
r6295 | |||
class MyList(object): | ||||
def __init__(self, content): | ||||
self.content = content | ||||
def _repr_pretty_(self, p, cycle): | ||||
if cycle: | ||||
p.text("MyList(...)") | ||||
else: | ||||
with p.group(3, "MyList(", ")"): | ||||
for (i, child) in enumerate(self.content): | ||||
if i: | ||||
p.text(",") | ||||
p.breakable() | ||||
else: | ||||
p.breakable("") | ||||
p.pretty(child) | ||||
|
r6313 | class MyDict(dict): | ||
def _repr_pretty_(self, p, cycle): | ||||
p.text("MyDict(...)") | ||||
|
r16238 | class MyObj(object): | ||
def somemethod(self): | ||||
pass | ||||
|
r6313 | |||
|
r7831 | class Dummy1(object): | ||
def _repr_pretty_(self, p, cycle): | ||||
p.text("Dummy1(...)") | ||||
class Dummy2(Dummy1): | ||||
_repr_pretty_ = None | ||||
|
r10789 | class NoModule(object): | ||
pass | ||||
NoModule.__module__ = None | ||||
|
r11875 | class Breaking(object): | ||
def _repr_pretty_(self, p, cycle): | ||||
with p.group(4,"TG: ",":"): | ||||
p.text("Breaking(") | ||||
p.break_() | ||||
p.text(")") | ||||
|
r11876 | class BreakingRepr(object): | ||
def __repr__(self): | ||||
return "Breaking(\n)" | ||||
|
r13425 | class BadRepr(object): | ||
def __repr__(self): | ||||
return 1/0 | ||||
|
r11876 | |||
|
r7831 | |||
|
r6295 | def test_indentation(): | ||
"""Test correct indentation in groups""" | ||||
count = 40 | ||||
gotoutput = pretty.pretty(MyList(range(count))) | ||||
expectedoutput = "MyList(\n" + ",\n".join(" %d" % i for i in range(count)) + ")" | ||||
|
r26718 | assert gotoutput == expectedoutput | ||
|
r6313 | |||
def test_dispatch(): | ||||
""" | ||||
Test correct dispatching: The _repr_pretty_ method for MyDict | ||||
must be found before the registered printer for dict. | ||||
""" | ||||
gotoutput = pretty.pretty(MyDict()) | ||||
expectedoutput = "MyDict(...)" | ||||
|
r26718 | assert gotoutput == expectedoutput | ||
|
r7831 | |||
def test_callability_checking(): | ||||
""" | ||||
Test that the _repr_pretty_ method is tested for callability and skipped if | ||||
not. | ||||
""" | ||||
gotoutput = pretty.pretty(Dummy2()) | ||||
expectedoutput = "Dummy1(...)" | ||||
|
r26718 | assert gotoutput == expectedoutput | ||
|
r8888 | |||
|
r10234 | |||
|
r26183 | @pytest.mark.parametrize( | ||
"obj,expected_output", | ||||
zip( | ||||
[ | ||||
set(), | ||||
frozenset(), | ||||
set([1]), | ||||
frozenset([1]), | ||||
set([1, 2]), | ||||
frozenset([1, 2]), | ||||
set([-1, -2, -3]), | ||||
], | ||||
[ | ||||
"set()", | ||||
"frozenset()", | ||||
"{1}", | ||||
"frozenset({1})", | ||||
"{1, 2}", | ||||
"frozenset({1, 2})", | ||||
"{-3, -2, -1}", | ||||
], | ||||
), | ||||
) | ||||
def test_sets(obj, expected_output): | ||||
|
r10234 | """ | ||
Test that set and frozenset use Python 3 formatting. | ||||
""" | ||||
|
r26183 | got_output = pretty.pretty(obj) | ||
|
r26718 | assert got_output == expected_output | ||
|
r10234 | |||
|
r8888 | def test_pprint_heap_allocated_type(): | ||
""" | ||||
Test that pprint works for heap allocated types. | ||||
""" | ||||
|
r28529 | module_name = "xxlimited_35" | ||
|
r27698 | expected_output = ( | ||
|
r27706 | "xxlimited.Null" if sys.version_info < (3, 10, 6) else "xxlimited_35.Null" | ||
|
r27698 | ) | ||
|
r27061 | xxlimited = pytest.importorskip(module_name) | ||
|
r8888 | output = pretty.pretty(xxlimited.Null) | ||
|
r27698 | assert output == expected_output | ||
|
r26718 | |||
|
r10789 | |||
def test_pprint_nomod(): | ||||
""" | ||||
Test that pprint works for classes with no __module__. | ||||
""" | ||||
output = pretty.pretty(NoModule) | ||||
|
r26718 | assert output == "NoModule" | ||
|
r11875 | def test_pprint_break(): | ||
""" | ||||
Test that p.break_ produces expected output | ||||
""" | ||||
output = pretty.pretty(Breaking()) | ||||
expected = "TG: Breaking(\n ):" | ||||
|
r26718 | assert output == expected | ||
|
r11876 | |||
def test_pprint_break_repr(): | ||||
""" | ||||
Test that p.break_ is used in repr | ||||
""" | ||||
|
r25434 | output = pretty.pretty([[BreakingRepr()]]) | ||
expected = "[[Breaking(\n )]]" | ||||
|
r26718 | assert output == expected | ||
|
r25434 | |||
output = pretty.pretty([[BreakingRepr()]*2]) | ||||
expected = "[[Breaking(\n ),\n Breaking(\n )]]" | ||||
|
r26718 | assert output == expected | ||
|
r13425 | |||
def test_bad_repr(): | ||||
|
r18024 | """Don't catch bad repr errors""" | ||
|
r26915 | with pytest.raises(ZeroDivisionError): | ||
|
r22837 | pretty.pretty(BadRepr()) | ||
|
r13434 | |||
class BadException(Exception): | ||||
def __str__(self): | ||||
return -1 | ||||
class ReallyBadRepr(object): | ||||
__module__ = 1 | ||||
@property | ||||
def __class__(self): | ||||
raise ValueError("I am horrible") | ||||
|
r26915 | |||
|
r13434 | def __repr__(self): | ||
raise BadException() | ||||
def test_really_bad_repr(): | ||||
|
r26915 | with pytest.raises(BadException): | ||
|
r22837 | pretty.pretty(ReallyBadRepr()) | ||
|
r14996 | |||
class SA(object): | ||||
pass | ||||
class SB(SA): | ||||
pass | ||||
|
r23706 | class TestsPretty(unittest.TestCase): | ||
def test_super_repr(self): | ||||
# "<super: module_name.SA, None>" | ||||
output = pretty.pretty(super(SA)) | ||||
self.assertRegex(output, r"<super: \S+.SA, None>") | ||||
# "<super: module_name.SA, <module_name.SB at 0x...>>" | ||||
sb = SB() | ||||
output = pretty.pretty(super(SA, sb)) | ||||
self.assertRegex(output, r"<super: \S+.SA,\s+<\S+.SB at 0x\S+>>") | ||||
def test_long_list(self): | ||||
lis = list(range(10000)) | ||||
p = pretty.pretty(lis) | ||||
last2 = p.rsplit('\n', 2)[-2:] | ||||
self.assertEqual(last2, [' 999,', ' ...]']) | ||||
def test_long_set(self): | ||||
s = set(range(10000)) | ||||
p = pretty.pretty(s) | ||||
last2 = p.rsplit('\n', 2)[-2:] | ||||
self.assertEqual(last2, [' 999,', ' ...}']) | ||||
def test_long_tuple(self): | ||||
tup = tuple(range(10000)) | ||||
p = pretty.pretty(tup) | ||||
last2 = p.rsplit('\n', 2)[-2:] | ||||
self.assertEqual(last2, [' 999,', ' ...)']) | ||||
def test_long_dict(self): | ||||
d = { n:n for n in range(10000) } | ||||
p = pretty.pretty(d) | ||||
last2 = p.rsplit('\n', 2)[-2:] | ||||
self.assertEqual(last2, [' 999: 999,', ' ...}']) | ||||
def test_unbound_method(self): | ||||
output = pretty.pretty(MyObj.somemethod) | ||||
self.assertIn('MyObj.somemethod', output) | ||||
|
r18403 | |||
class MetaClass(type): | ||||
def __new__(cls, name): | ||||
return type.__new__(cls, name, (object,), {'name': name}) | ||||
def __repr__(self): | ||||
return "[CUSTOM REPR FOR CLASS %s]" % self.name | ||||
ClassWithMeta = MetaClass('ClassWithMeta') | ||||
def test_metaclass_repr(): | ||||
output = pretty.pretty(ClassWithMeta) | ||||
|
r26718 | assert output == "[CUSTOM REPR FOR CLASS ClassWithMeta]" | ||
|
r18420 | |||
|
r20662 | def test_unicode_repr(): | ||
|
r20727 | u = u"üniçodé" | ||
|
r22960 | ustr = u | ||
|
r26915 | |||
|
r20662 | class C(object): | ||
def __repr__(self): | ||||
return ustr | ||||
|
r26915 | |||
|
r20662 | c = C() | ||
p = pretty.pretty(c) | ||||
|
r26718 | assert p == u | ||
|
r20662 | p = pretty.pretty([c]) | ||
|
r27639 | assert p == "[%s]" % u | ||
|
r20662 | |||
|
r18420 | def test_basic_class(): | ||
def type_pprint_wrapper(obj, p, cycle): | ||||
if obj is MyObj: | ||||
type_pprint_wrapper.called = True | ||||
return pretty._type_pprint(obj, p, cycle) | ||||
type_pprint_wrapper.called = False | ||||
stream = StringIO() | ||||
printer = pretty.RepresentationPrinter(stream) | ||||
printer.type_pprinters[type] = type_pprint_wrapper | ||||
printer.pretty(MyObj) | ||||
printer.flush() | ||||
output = stream.getvalue() | ||||
|
r26718 | assert output == "%s.MyObj" % __name__ | ||
|
r26915 | assert type_pprint_wrapper.called is True | ||
|
r21375 | |||
|
r27204 | def test_collections_userlist(): | ||
# Create userlist with cycle | ||||
a = UserList() | ||||
a.append(a) | ||||
cases = [ | ||||
(UserList(), "UserList([])"), | ||||
( | ||||
UserList(i for i in range(1000, 1020)), | ||||
"UserList([1000,\n" | ||||
" 1001,\n" | ||||
" 1002,\n" | ||||
" 1003,\n" | ||||
" 1004,\n" | ||||
" 1005,\n" | ||||
" 1006,\n" | ||||
" 1007,\n" | ||||
" 1008,\n" | ||||
" 1009,\n" | ||||
" 1010,\n" | ||||
" 1011,\n" | ||||
" 1012,\n" | ||||
" 1013,\n" | ||||
" 1014,\n" | ||||
" 1015,\n" | ||||
" 1016,\n" | ||||
" 1017,\n" | ||||
" 1018,\n" | ||||
" 1019])", | ||||
), | ||||
(a, "UserList([UserList(...)])"), | ||||
] | ||||
for obj, expected in cases: | ||||
assert pretty.pretty(obj) == expected | ||||
|
r26718 | # TODO : pytest.mark.parametrise once nose is gone. | ||
|
r21375 | def test_collections_defaultdict(): | ||
# Create defaultdicts with cycles | ||||
a = defaultdict() | ||||
a.default_factory = a | ||||
b = defaultdict(list) | ||||
b['key'] = b | ||||
# Dictionary order cannot be relied on, test against single keys. | ||||
cases = [ | ||||
(defaultdict(list), 'defaultdict(list, {})'), | ||||
(defaultdict(list, {'key': '-' * 50}), | ||||
"defaultdict(list,\n" | ||||
" {'key': '--------------------------------------------------'})"), | ||||
(a, 'defaultdict(defaultdict(...), {})'), | ||||
(b, "defaultdict(list, {'key': defaultdict(...)})"), | ||||
] | ||||
for obj, expected in cases: | ||||
|
r26718 | assert pretty.pretty(obj) == expected | ||
|
r21375 | |||
|
r26718 | # TODO : pytest.mark.parametrise once nose is gone. | ||
|
r21375 | def test_collections_ordereddict(): | ||
# Create OrderedDict with cycle | ||||
a = OrderedDict() | ||||
a['key'] = a | ||||
cases = [ | ||||
(OrderedDict(), 'OrderedDict()'), | ||||
(OrderedDict((i, i) for i in range(1000, 1010)), | ||||
'OrderedDict([(1000, 1000),\n' | ||||
' (1001, 1001),\n' | ||||
' (1002, 1002),\n' | ||||
' (1003, 1003),\n' | ||||
' (1004, 1004),\n' | ||||
' (1005, 1005),\n' | ||||
' (1006, 1006),\n' | ||||
' (1007, 1007),\n' | ||||
' (1008, 1008),\n' | ||||
' (1009, 1009)])'), | ||||
(a, "OrderedDict([('key', OrderedDict(...))])"), | ||||
] | ||||
for obj, expected in cases: | ||||
|
r26718 | assert pretty.pretty(obj) == expected | ||
|
r21375 | |||
|
r26718 | # TODO : pytest.mark.parametrise once nose is gone. | ||
|
r21375 | def test_collections_deque(): | ||
# Create deque with cycle | ||||
a = deque() | ||||
a.append(a) | ||||
cases = [ | ||||
(deque(), 'deque([])'), | ||||
(deque(i for i in range(1000, 1020)), | ||||
'deque([1000,\n' | ||||
' 1001,\n' | ||||
' 1002,\n' | ||||
' 1003,\n' | ||||
' 1004,\n' | ||||
' 1005,\n' | ||||
' 1006,\n' | ||||
' 1007,\n' | ||||
' 1008,\n' | ||||
' 1009,\n' | ||||
' 1010,\n' | ||||
' 1011,\n' | ||||
' 1012,\n' | ||||
' 1013,\n' | ||||
' 1014,\n' | ||||
' 1015,\n' | ||||
' 1016,\n' | ||||
' 1017,\n' | ||||
' 1018,\n' | ||||
' 1019])'), | ||||
(a, 'deque([deque(...)])'), | ||||
] | ||||
for obj, expected in cases: | ||||
|
r26718 | assert pretty.pretty(obj) == expected | ||
|
r21375 | |||
|
r26718 | |||
# TODO : pytest.mark.parametrise once nose is gone. | ||||
|
r21375 | def test_collections_counter(): | ||
|
r21769 | class MyCounter(Counter): | ||
pass | ||||
|
r21375 | cases = [ | ||
(Counter(), 'Counter()'), | ||||
(Counter(a=1), "Counter({'a': 1})"), | ||||
|
r21769 | (MyCounter(a=1), "MyCounter({'a': 1})"), | ||
|
r28231 | (Counter(a=1, c=22), "Counter({'c': 22, 'a': 1})"), | ||
|
r21375 | ] | ||
for obj, expected in cases: | ||||
|
r26718 | assert pretty.pretty(obj) == expected | ||
|
r22748 | |||
|
r26718 | # TODO : pytest.mark.parametrise once nose is gone. | ||
|
r22748 | def test_mappingproxy(): | ||
MP = types.MappingProxyType | ||||
underlying_dict = {} | ||||
mp_recursive = MP(underlying_dict) | ||||
underlying_dict[2] = mp_recursive | ||||
underlying_dict[3] = underlying_dict | ||||
cases = [ | ||||
(MP({}), "mappingproxy({})"), | ||||
(MP({None: MP({})}), "mappingproxy({None: mappingproxy({})})"), | ||||
(MP({k: k.upper() for k in string.ascii_lowercase}), | ||||
"mappingproxy({'a': 'A',\n" | ||||
" 'b': 'B',\n" | ||||
" 'c': 'C',\n" | ||||
" 'd': 'D',\n" | ||||
" 'e': 'E',\n" | ||||
" 'f': 'F',\n" | ||||
" 'g': 'G',\n" | ||||
" 'h': 'H',\n" | ||||
" 'i': 'I',\n" | ||||
" 'j': 'J',\n" | ||||
" 'k': 'K',\n" | ||||
" 'l': 'L',\n" | ||||
" 'm': 'M',\n" | ||||
" 'n': 'N',\n" | ||||
" 'o': 'O',\n" | ||||
" 'p': 'P',\n" | ||||
" 'q': 'Q',\n" | ||||
" 'r': 'R',\n" | ||||
" 's': 'S',\n" | ||||
" 't': 'T',\n" | ||||
" 'u': 'U',\n" | ||||
" 'v': 'V',\n" | ||||
" 'w': 'W',\n" | ||||
" 'x': 'X',\n" | ||||
" 'y': 'Y',\n" | ||||
" 'z': 'Z'})"), | ||||
(mp_recursive, "mappingproxy({2: {...}, 3: {2: {...}, 3: {...}}})"), | ||||
(underlying_dict, | ||||
"{2: mappingproxy({2: {...}, 3: {...}}), 3: {...}}"), | ||||
] | ||||
for obj, expected in cases: | ||||
|
r26718 | assert pretty.pretty(obj) == expected | ||
|
r24026 | |||
|
r24372 | |||
|
r26718 | # TODO : pytest.mark.parametrise once nose is gone. | ||
|
r25623 | def test_simplenamespace(): | ||
SN = types.SimpleNamespace | ||||
sn_recursive = SN() | ||||
sn_recursive.first = sn_recursive | ||||
sn_recursive.second = sn_recursive | ||||
cases = [ | ||||
(SN(), "namespace()"), | ||||
(SN(x=SN()), "namespace(x=namespace())"), | ||||
(SN(a_long_name=[SN(s=string.ascii_lowercase)]*3, a_short_name=None), | ||||
"namespace(a_long_name=[namespace(s='abcdefghijklmnopqrstuvwxyz'),\n" | ||||
" namespace(s='abcdefghijklmnopqrstuvwxyz'),\n" | ||||
" namespace(s='abcdefghijklmnopqrstuvwxyz')],\n" | ||||
" a_short_name=None)"), | ||||
(sn_recursive, "namespace(first=namespace(...), second=namespace(...))"), | ||||
] | ||||
for obj, expected in cases: | ||||
|
r26718 | assert pretty.pretty(obj) == expected | ||
|
r25623 | |||
|
r24372 | def test_pretty_environ(): | ||
dict_repr = pretty.pretty(dict(os.environ)) | ||||
# reindent to align with 'environ' prefix | ||||
dict_indented = dict_repr.replace('\n', '\n' + (' ' * len('environ'))) | ||||
env_repr = pretty.pretty(os.environ) | ||||
|
r26718 | assert env_repr == "environ" + dict_indented | ||
|
r24372 | |||
|
r24026 | def test_function_pretty(): | ||
"Test pretty print of function" | ||||
|
r24031 | # posixpath is a pure python module, its interface is consistent | ||
|
r24026 | # across Python distributions | ||
|
r24031 | import posixpath | ||
|
r26718 | |||
assert pretty.pretty(posixpath.join) == "<function posixpath.join(a, *p)>" | ||||
|
r24026 | # custom function | ||
def meaning_of_life(question=None): | ||||
if question: | ||||
return 42 | ||||
return "Don't panic" | ||||
|
r26915 | assert "meaning_of_life(question=None)" in pretty.pretty(meaning_of_life) | ||
|
r24108 | |||
class OrderedCounter(Counter, OrderedDict): | ||||
'Counter that remembers the order elements are first encountered' | ||||
def __repr__(self): | ||||
return '%s(%r)' % (self.__class__.__name__, OrderedDict(self)) | ||||
def __reduce__(self): | ||||
return self.__class__, (OrderedDict(self),) | ||||
|
r24109 | class MySet(set): # Override repr of a basic type | ||
def __repr__(self): | ||||
return 'mine' | ||||
|
r24108 | def test_custom_repr(): | ||
"""A custom repr should override a pretty printer for a parent type""" | ||||
oc = OrderedCounter("abracadabra") | ||||
|
r26915 | assert "OrderedCounter(OrderedDict" in pretty.pretty(oc) | ||
|
r24109 | |||
|
r26718 | assert pretty.pretty(MySet()) == "mine" | ||