1# coding: utf-8
2"""Tests for IPython.lib.pretty."""
3
4# Copyright (c) IPython Development Team.
5# Distributed under the terms of the Modified BSD License.
6
7from __future__ import print_function
8
9from collections import Counter, defaultdict, deque, OrderedDict
10import types, string, ctypes
11
12import nose.tools as nt
13
14from IPython.lib import pretty
15from IPython.testing.decorators import (skip_without, py2_only, py3_only,
16                                        cpython2_only)
17from IPython.utils.py3compat import PY3, unicode_to_str
18
19if PY3:
20    from io import StringIO
21else:
22    from StringIO import StringIO
23
24
25class MyList(object):
26    def __init__(self, content):
27        self.content = content
28    def _repr_pretty_(self, p, cycle):
29        if cycle:
30            p.text("MyList(...)")
31        else:
32            with p.group(3, "MyList(", ")"):
33                for (i, child) in enumerate(self.content):
34                    if i:
35                        p.text(",")
36                        p.breakable()
37                    else:
38                        p.breakable("")
39                    p.pretty(child)
40
41
42class MyDict(dict):
43    def _repr_pretty_(self, p, cycle):
44        p.text("MyDict(...)")
45
46class MyObj(object):
47    def somemethod(self):
48        pass
49
50
51class Dummy1(object):
52    def _repr_pretty_(self, p, cycle):
53        p.text("Dummy1(...)")
54
55class Dummy2(Dummy1):
56    _repr_pretty_ = None
57
58class NoModule(object):
59    pass
60
61NoModule.__module__ = None
62
63class Breaking(object):
64    def _repr_pretty_(self, p, cycle):
65        with p.group(4,"TG: ",":"):
66            p.text("Breaking(")
67            p.break_()
68            p.text(")")
69
70class BreakingRepr(object):
71    def __repr__(self):
72        return "Breaking(\n)"
73
74class BreakingReprParent(object):
75    def _repr_pretty_(self, p, cycle):
76        with p.group(4,"TG: ",":"):
77            p.pretty(BreakingRepr())
78
79class BadRepr(object):
80
81    def __repr__(self):
82        return 1/0
83
84
85def test_indentation():
86    """Test correct indentation in groups"""
87    count = 40
88    gotoutput = pretty.pretty(MyList(range(count)))
89    expectedoutput = "MyList(\n" + ",\n".join("   %d" % i for i in range(count)) + ")"
90
91    nt.assert_equal(gotoutput, expectedoutput)
92
93
94def test_dispatch():
95    """
96    Test correct dispatching: The _repr_pretty_ method for MyDict
97    must be found before the registered printer for dict.
98    """
99    gotoutput = pretty.pretty(MyDict())
100    expectedoutput = "MyDict(...)"
101
102    nt.assert_equal(gotoutput, expectedoutput)
103
104
105def test_callability_checking():
106    """
107    Test that the _repr_pretty_ method is tested for callability and skipped if
108    not.
109    """
110    gotoutput = pretty.pretty(Dummy2())
111    expectedoutput = "Dummy1(...)"
112
113    nt.assert_equal(gotoutput, expectedoutput)
114
115
116def test_sets():
117    """
118    Test that set and frozenset use Python 3 formatting.
119    """
120    objects = [set(), frozenset(), set([1]), frozenset([1]), set([1, 2]),
121        frozenset([1, 2]), set([-1, -2, -3])]
122    expected = ['set()', 'frozenset()', '{1}', 'frozenset({1})', '{1, 2}',
123        'frozenset({1, 2})', '{-3, -2, -1}']
124    for obj, expected_output in zip(objects, expected):
125        got_output = pretty.pretty(obj)
126        yield nt.assert_equal, got_output, expected_output
127
128
129@skip_without('xxlimited')
130def test_pprint_heap_allocated_type():
131    """
132    Test that pprint works for heap allocated types.
133    """
134    import xxlimited
135    output = pretty.pretty(xxlimited.Null)
136    nt.assert_equal(output, 'xxlimited.Null')
137
138def test_pprint_nomod():
139    """
140    Test that pprint works for classes with no __module__.
141    """
142    output = pretty.pretty(NoModule)
143    nt.assert_equal(output, 'NoModule')
144
145def test_pprint_break():
146    """
147    Test that p.break_ produces expected output
148    """
149    output = pretty.pretty(Breaking())
150    expected = "TG: Breaking(\n    ):"
151    nt.assert_equal(output, expected)
152
153def test_pprint_break_repr():
154    """
155    Test that p.break_ is used in repr
156    """
157    output = pretty.pretty(BreakingReprParent())
158    expected = "TG: Breaking(\n    ):"
159    nt.assert_equal(output, expected)
160
161def test_bad_repr():
162    """Don't catch bad repr errors"""
163    with nt.assert_raises(ZeroDivisionError):
164        output = pretty.pretty(BadRepr())
165
166class BadException(Exception):
167    def __str__(self):
168        return -1
169
170class ReallyBadRepr(object):
171    __module__ = 1
172    @property
173    def __class__(self):
174        raise ValueError("I am horrible")
175
176    def __repr__(self):
177        raise BadException()
178
179def test_really_bad_repr():
180    with nt.assert_raises(BadException):
181        output = pretty.pretty(ReallyBadRepr())
182
183
184class SA(object):
185    pass
186
187class SB(SA):
188    pass
189
190def test_super_repr():
191    # "<super: module_name.SA, None>"
192    output = pretty.pretty(super(SA))
193    nt.assert_regexp_matches(output, r"<super: \S+.SA, None>")
194
195    # "<super: module_name.SA, <module_name.SB at 0x...>>"
196    sb = SB()
197    output = pretty.pretty(super(SA, sb))
198    nt.assert_regexp_matches(output, r"<super: \S+.SA,\s+<\S+.SB at 0x\S+>>")
199
200
201def test_long_list():
202    lis = list(range(10000))
203    p = pretty.pretty(lis)
204    last2 = p.rsplit('\n', 2)[-2:]
205    nt.assert_equal(last2, [' 999,', ' ...]'])
206
207def test_long_set():
208    s = set(range(10000))
209    p = pretty.pretty(s)
210    last2 = p.rsplit('\n', 2)[-2:]
211    nt.assert_equal(last2, [' 999,', ' ...}'])
212
213def test_long_tuple():
214    tup = tuple(range(10000))
215    p = pretty.pretty(tup)
216    last2 = p.rsplit('\n', 2)[-2:]
217    nt.assert_equal(last2, [' 999,', ' ...)'])
218
219def test_long_dict():
220    d = { n:n for n in range(10000) }
221    p = pretty.pretty(d)
222    last2 = p.rsplit('\n', 2)[-2:]
223    nt.assert_equal(last2, [' 999: 999,', ' ...}'])
224
225def test_unbound_method():
226    output = pretty.pretty(MyObj.somemethod)
227    nt.assert_in('MyObj.somemethod', output)
228
229
230class MetaClass(type):
231    def __new__(cls, name):
232        return type.__new__(cls, name, (object,), {'name': name})
233
234    def __repr__(self):
235        return "[CUSTOM REPR FOR CLASS %s]" % self.name
236
237
238ClassWithMeta = MetaClass('ClassWithMeta')
239
240
241def test_metaclass_repr():
242    output = pretty.pretty(ClassWithMeta)
243    nt.assert_equal(output, "[CUSTOM REPR FOR CLASS ClassWithMeta]")
244
245
246def test_unicode_repr():
247    u = u"üniçodé"
248    ustr = unicode_to_str(u)
249
250    class C(object):
251        def __repr__(self):
252            return ustr
253
254    c = C()
255    p = pretty.pretty(c)
256    nt.assert_equal(p, u)
257    p = pretty.pretty([c])
258    nt.assert_equal(p, u'[%s]' % u)
259
260
261def test_basic_class():
262    def type_pprint_wrapper(obj, p, cycle):
263        if obj is MyObj:
264            type_pprint_wrapper.called = True
265        return pretty._type_pprint(obj, p, cycle)
266    type_pprint_wrapper.called = False
267
268    stream = StringIO()
269    printer = pretty.RepresentationPrinter(stream)
270    printer.type_pprinters[type] = type_pprint_wrapper
271    printer.pretty(MyObj)
272    printer.flush()
273    output = stream.getvalue()
274
275    nt.assert_equal(output, '%s.MyObj' % __name__)
276    nt.assert_true(type_pprint_wrapper.called)
277
278
279# This is only run on Python 2 because in Python 3 the language prevents you
280# from setting a non-unicode value for __qualname__ on a metaclass, and it
281# doesn't respect the descriptor protocol if you subclass unicode and implement
282# __get__.
283@py2_only
284def test_fallback_to__name__on_type():
285    # Test that we correctly repr types that have non-string values for
286    # __qualname__ by falling back to __name__
287
288    class Type(object):
289        __qualname__ = 5
290
291    # Test repring of the type.
292    stream = StringIO()
293    printer = pretty.RepresentationPrinter(stream)
294
295    printer.pretty(Type)
296    printer.flush()
297    output = stream.getvalue()
298
299    # If __qualname__ is malformed, we should fall back to __name__.
300    expected = '.'.join([__name__, Type.__name__])
301    nt.assert_equal(output, expected)
302
303    # Clear stream buffer.
304    stream.buf = ''
305
306    # Test repring of an instance of the type.
307    instance = Type()
308    printer.pretty(instance)
309    printer.flush()
310    output = stream.getvalue()
311
312    # Should look like:
313    # <IPython.lib.tests.test_pretty.Type at 0x7f7658ae07d0>
314    prefix = '<' + '.'.join([__name__, Type.__name__]) + ' at 0x'
315    nt.assert_true(output.startswith(prefix))
316
317
318@py2_only
319def test_fail_gracefully_on_bogus__qualname__and__name__():
320    # Test that we correctly repr types that have non-string values for both
321    # __qualname__ and __name__
322
323    class Meta(type):
324        __name__ = 5
325
326    class Type(object):
327        __metaclass__ = Meta
328        __qualname__ = 5
329
330    stream = StringIO()
331    printer = pretty.RepresentationPrinter(stream)
332
333    printer.pretty(Type)
334    printer.flush()
335    output = stream.getvalue()
336
337    # If we can't find __name__ or __qualname__ just use a sentinel string.
338    expected = '.'.join([__name__, '<unknown type>'])
339    nt.assert_equal(output, expected)
340
341    # Clear stream buffer.
342    stream.buf = ''
343
344    # Test repring of an instance of the type.
345    instance = Type()
346    printer.pretty(instance)
347    printer.flush()
348    output = stream.getvalue()
349
350    # Should look like:
351    # <IPython.lib.tests.test_pretty.<unknown type> at 0x7f7658ae07d0>
352    prefix = '<' + '.'.join([__name__, '<unknown type>']) + ' at 0x'
353    nt.assert_true(output.startswith(prefix))
354
355
356def test_collections_defaultdict():
357    # Create defaultdicts with cycles
358    a = defaultdict()
359    a.default_factory = a
360    b = defaultdict(list)
361    b['key'] = b
362
363    # Dictionary order cannot be relied on, test against single keys.
364    cases = [
365        (defaultdict(list), 'defaultdict(list, {})'),
366        (defaultdict(list, {'key': '-' * 50}),
367         "defaultdict(list,\n"
368         "            {'key': '--------------------------------------------------'})"),
369        (a, 'defaultdict(defaultdict(...), {})'),
370        (b, "defaultdict(list, {'key': defaultdict(...)})"),
371    ]
372    for obj, expected in cases:
373        nt.assert_equal(pretty.pretty(obj), expected)
374
375
376def test_collections_ordereddict():
377    # Create OrderedDict with cycle
378    a = OrderedDict()
379    a['key'] = a
380
381    cases = [
382        (OrderedDict(), 'OrderedDict()'),
383        (OrderedDict((i, i) for i in range(1000, 1010)),
384         'OrderedDict([(1000, 1000),\n'
385         '             (1001, 1001),\n'
386         '             (1002, 1002),\n'
387         '             (1003, 1003),\n'
388         '             (1004, 1004),\n'
389         '             (1005, 1005),\n'
390         '             (1006, 1006),\n'
391         '             (1007, 1007),\n'
392         '             (1008, 1008),\n'
393         '             (1009, 1009)])'),
394        (a, "OrderedDict([('key', OrderedDict(...))])"),
395    ]
396    for obj, expected in cases:
397        nt.assert_equal(pretty.pretty(obj), expected)
398
399
400def test_collections_deque():
401    # Create deque with cycle
402    a = deque()
403    a.append(a)
404
405    cases = [
406        (deque(), 'deque([])'),
407        (deque(i for i in range(1000, 1020)),
408         'deque([1000,\n'
409         '       1001,\n'
410         '       1002,\n'
411         '       1003,\n'
412         '       1004,\n'
413         '       1005,\n'
414         '       1006,\n'
415         '       1007,\n'
416         '       1008,\n'
417         '       1009,\n'
418         '       1010,\n'
419         '       1011,\n'
420         '       1012,\n'
421         '       1013,\n'
422         '       1014,\n'
423         '       1015,\n'
424         '       1016,\n'
425         '       1017,\n'
426         '       1018,\n'
427         '       1019])'),
428        (a, 'deque([deque(...)])'),
429    ]
430    for obj, expected in cases:
431        nt.assert_equal(pretty.pretty(obj), expected)
432
433def test_collections_counter():
434    class MyCounter(Counter):
435        pass
436    cases = [
437        (Counter(), 'Counter()'),
438        (Counter(a=1), "Counter({'a': 1})"),
439        (MyCounter(a=1), "MyCounter({'a': 1})"),
440    ]
441    for obj, expected in cases:
442        nt.assert_equal(pretty.pretty(obj), expected)
443
444@py3_only
445def test_mappingproxy():
446    MP = types.MappingProxyType
447    underlying_dict = {}
448    mp_recursive = MP(underlying_dict)
449    underlying_dict[2] = mp_recursive
450    underlying_dict[3] = underlying_dict
451
452    cases = [
453        (MP({}), "mappingproxy({})"),
454        (MP({None: MP({})}), "mappingproxy({None: mappingproxy({})})"),
455        (MP({k: k.upper() for k in string.ascii_lowercase}),
456         "mappingproxy({'a': 'A',\n"
457         "              'b': 'B',\n"
458         "              'c': 'C',\n"
459         "              'd': 'D',\n"
460         "              'e': 'E',\n"
461         "              'f': 'F',\n"
462         "              'g': 'G',\n"
463         "              'h': 'H',\n"
464         "              'i': 'I',\n"
465         "              'j': 'J',\n"
466         "              'k': 'K',\n"
467         "              'l': 'L',\n"
468         "              'm': 'M',\n"
469         "              'n': 'N',\n"
470         "              'o': 'O',\n"
471         "              'p': 'P',\n"
472         "              'q': 'Q',\n"
473         "              'r': 'R',\n"
474         "              's': 'S',\n"
475         "              't': 'T',\n"
476         "              'u': 'U',\n"
477         "              'v': 'V',\n"
478         "              'w': 'W',\n"
479         "              'x': 'X',\n"
480         "              'y': 'Y',\n"
481         "              'z': 'Z'})"),
482        (mp_recursive, "mappingproxy({2: {...}, 3: {2: {...}, 3: {...}}})"),
483        (underlying_dict,
484         "{2: mappingproxy({2: {...}, 3: {...}}), 3: {...}}"),
485    ]
486    for obj, expected in cases:
487        nt.assert_equal(pretty.pretty(obj), expected)
488
489@cpython2_only # In PyPy, types.DictProxyType is dict
490def test_dictproxy():
491    # This is the dictproxy constructor itself from the Python API,
492    DP = ctypes.pythonapi.PyDictProxy_New
493    DP.argtypes, DP.restype = (ctypes.py_object,), ctypes.py_object
494
495    underlying_dict = {}
496    mp_recursive = DP(underlying_dict)
497    underlying_dict[0] = mp_recursive
498    underlying_dict[-3] = underlying_dict
499
500    cases = [
501        (DP({}), "dict_proxy({})"),
502        (DP({None: DP({})}), "dict_proxy({None: dict_proxy({})})"),
503        (DP({k: k.lower() for k in string.ascii_uppercase}),
504         "dict_proxy({'A': 'a',\n"
505         "            'B': 'b',\n"
506         "            'C': 'c',\n"
507         "            'D': 'd',\n"
508         "            'E': 'e',\n"
509         "            'F': 'f',\n"
510         "            'G': 'g',\n"
511         "            'H': 'h',\n"
512         "            'I': 'i',\n"
513         "            'J': 'j',\n"
514         "            'K': 'k',\n"
515         "            'L': 'l',\n"
516         "            'M': 'm',\n"
517         "            'N': 'n',\n"
518         "            'O': 'o',\n"
519         "            'P': 'p',\n"
520         "            'Q': 'q',\n"
521         "            'R': 'r',\n"
522         "            'S': 's',\n"
523         "            'T': 't',\n"
524         "            'U': 'u',\n"
525         "            'V': 'v',\n"
526         "            'W': 'w',\n"
527         "            'X': 'x',\n"
528         "            'Y': 'y',\n"
529         "            'Z': 'z'})"),
530        (mp_recursive, "dict_proxy({-3: {-3: {...}, 0: {...}}, 0: {...}})"),
531    ]
532    for obj, expected in cases:
533        nt.assert_is_instance(obj, types.DictProxyType) # Meta-test
534        nt.assert_equal(pretty.pretty(obj), expected)
535    nt.assert_equal(pretty.pretty(underlying_dict),
536                    "{-3: {...}, 0: dict_proxy({-3: {...}, 0: {...}})}")
537
538class OrderedCounter(Counter, OrderedDict):
539    'Counter that remembers the order elements are first encountered'
540
541    def __repr__(self):
542        return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))
543
544    def __reduce__(self):
545        return self.__class__, (OrderedDict(self),)
546
547class MySet(set):  # Override repr of a basic type
548    def __repr__(self):
549        return 'mine'
550
551def test_custom_repr():
552    """A custom repr should override a pretty printer for a parent type"""
553    oc = OrderedCounter("abracadabra")
554    nt.assert_in("OrderedCounter(OrderedDict", pretty.pretty(oc))
555
556    nt.assert_equal(pretty.pretty(MySet()), 'mine')
557