Home | History | Annotate | Download | only in test
      1 """
      2   Test cases for the repr module
      3   Nick Mathewson
      4 """
      5 
      6 import sys
      7 import os
      8 import shutil
      9 import importlib
     10 import importlib.util
     11 import unittest
     12 
     13 from test.support import create_empty_file, verbose
     14 from reprlib import repr as r # Don't shadow builtin repr
     15 from reprlib import Repr
     16 from reprlib import recursive_repr
     17 
     18 
     19 def nestedTuple(nesting):
     20     t = ()
     21     for i in range(nesting):
     22         t = (t,)
     23     return t
     24 
     25 class ReprTests(unittest.TestCase):
     26 
     27     def test_string(self):
     28         eq = self.assertEqual
     29         eq(r("abc"), "'abc'")
     30         eq(r("abcdefghijklmnop"),"'abcdefghijklmnop'")
     31 
     32         s = "a"*30+"b"*30
     33         expected = repr(s)[:13] + "..." + repr(s)[-14:]
     34         eq(r(s), expected)
     35 
     36         eq(r("\"'"), repr("\"'"))
     37         s = "\""*30+"'"*100
     38         expected = repr(s)[:13] + "..." + repr(s)[-14:]
     39         eq(r(s), expected)
     40 
     41     def test_tuple(self):
     42         eq = self.assertEqual
     43         eq(r((1,)), "(1,)")
     44 
     45         t3 = (1, 2, 3)
     46         eq(r(t3), "(1, 2, 3)")
     47 
     48         r2 = Repr()
     49         r2.maxtuple = 2
     50         expected = repr(t3)[:-2] + "...)"
     51         eq(r2.repr(t3), expected)
     52 
     53     def test_container(self):
     54         from array import array
     55         from collections import deque
     56 
     57         eq = self.assertEqual
     58         # Tuples give up after 6 elements
     59         eq(r(()), "()")
     60         eq(r((1,)), "(1,)")
     61         eq(r((1, 2, 3)), "(1, 2, 3)")
     62         eq(r((1, 2, 3, 4, 5, 6)), "(1, 2, 3, 4, 5, 6)")
     63         eq(r((1, 2, 3, 4, 5, 6, 7)), "(1, 2, 3, 4, 5, 6, ...)")
     64 
     65         # Lists give up after 6 as well
     66         eq(r([]), "[]")
     67         eq(r([1]), "[1]")
     68         eq(r([1, 2, 3]), "[1, 2, 3]")
     69         eq(r([1, 2, 3, 4, 5, 6]), "[1, 2, 3, 4, 5, 6]")
     70         eq(r([1, 2, 3, 4, 5, 6, 7]), "[1, 2, 3, 4, 5, 6, ...]")
     71 
     72         # Sets give up after 6 as well
     73         eq(r(set([])), "set()")
     74         eq(r(set([1])), "{1}")
     75         eq(r(set([1, 2, 3])), "{1, 2, 3}")
     76         eq(r(set([1, 2, 3, 4, 5, 6])), "{1, 2, 3, 4, 5, 6}")
     77         eq(r(set([1, 2, 3, 4, 5, 6, 7])), "{1, 2, 3, 4, 5, 6, ...}")
     78 
     79         # Frozensets give up after 6 as well
     80         eq(r(frozenset([])), "frozenset()")
     81         eq(r(frozenset([1])), "frozenset({1})")
     82         eq(r(frozenset([1, 2, 3])), "frozenset({1, 2, 3})")
     83         eq(r(frozenset([1, 2, 3, 4, 5, 6])), "frozenset({1, 2, 3, 4, 5, 6})")
     84         eq(r(frozenset([1, 2, 3, 4, 5, 6, 7])), "frozenset({1, 2, 3, 4, 5, 6, ...})")
     85 
     86         # collections.deque after 6
     87         eq(r(deque([1, 2, 3, 4, 5, 6, 7])), "deque([1, 2, 3, 4, 5, 6, ...])")
     88 
     89         # Dictionaries give up after 4.
     90         eq(r({}), "{}")
     91         d = {'alice': 1, 'bob': 2, 'charles': 3, 'dave': 4}
     92         eq(r(d), "{'alice': 1, 'bob': 2, 'charles': 3, 'dave': 4}")
     93         d['arthur'] = 1
     94         eq(r(d), "{'alice': 1, 'arthur': 1, 'bob': 2, 'charles': 3, ...}")
     95 
     96         # array.array after 5.
     97         eq(r(array('i')), "array('i')")
     98         eq(r(array('i', [1])), "array('i', [1])")
     99         eq(r(array('i', [1, 2])), "array('i', [1, 2])")
    100         eq(r(array('i', [1, 2, 3])), "array('i', [1, 2, 3])")
    101         eq(r(array('i', [1, 2, 3, 4])), "array('i', [1, 2, 3, 4])")
    102         eq(r(array('i', [1, 2, 3, 4, 5])), "array('i', [1, 2, 3, 4, 5])")
    103         eq(r(array('i', [1, 2, 3, 4, 5, 6])),
    104                    "array('i', [1, 2, 3, 4, 5, ...])")
    105 
    106     def test_set_literal(self):
    107         eq = self.assertEqual
    108         eq(r({1}), "{1}")
    109         eq(r({1, 2, 3}), "{1, 2, 3}")
    110         eq(r({1, 2, 3, 4, 5, 6}), "{1, 2, 3, 4, 5, 6}")
    111         eq(r({1, 2, 3, 4, 5, 6, 7}), "{1, 2, 3, 4, 5, 6, ...}")
    112 
    113     def test_frozenset(self):
    114         eq = self.assertEqual
    115         eq(r(frozenset({1})), "frozenset({1})")
    116         eq(r(frozenset({1, 2, 3})), "frozenset({1, 2, 3})")
    117         eq(r(frozenset({1, 2, 3, 4, 5, 6})), "frozenset({1, 2, 3, 4, 5, 6})")
    118         eq(r(frozenset({1, 2, 3, 4, 5, 6, 7})), "frozenset({1, 2, 3, 4, 5, 6, ...})")
    119 
    120     def test_numbers(self):
    121         eq = self.assertEqual
    122         eq(r(123), repr(123))
    123         eq(r(123), repr(123))
    124         eq(r(1.0/3), repr(1.0/3))
    125 
    126         n = 10**100
    127         expected = repr(n)[:18] + "..." + repr(n)[-19:]
    128         eq(r(n), expected)
    129 
    130     def test_instance(self):
    131         eq = self.assertEqual
    132         i1 = ClassWithRepr("a")
    133         eq(r(i1), repr(i1))
    134 
    135         i2 = ClassWithRepr("x"*1000)
    136         expected = repr(i2)[:13] + "..." + repr(i2)[-14:]
    137         eq(r(i2), expected)
    138 
    139         i3 = ClassWithFailingRepr()
    140         eq(r(i3), ("<ClassWithFailingRepr instance at %#x>"%id(i3)))
    141 
    142         s = r(ClassWithFailingRepr)
    143         self.assertTrue(s.startswith("<class "))
    144         self.assertTrue(s.endswith(">"))
    145         self.assertIn(s.find("..."), [12, 13])
    146 
    147     def test_lambda(self):
    148         r = repr(lambda x: x)
    149         self.assertTrue(r.startswith("<function ReprTests.test_lambda.<locals>.<lambda"), r)
    150         # XXX anonymous functions?  see func_repr
    151 
    152     def test_builtin_function(self):
    153         eq = self.assertEqual
    154         # Functions
    155         eq(repr(hash), '<built-in function hash>')
    156         # Methods
    157         self.assertTrue(repr(''.split).startswith(
    158             '<built-in method split of str object at 0x'))
    159 
    160     def test_range(self):
    161         eq = self.assertEqual
    162         eq(repr(range(1)), 'range(0, 1)')
    163         eq(repr(range(1, 2)), 'range(1, 2)')
    164         eq(repr(range(1, 4, 3)), 'range(1, 4, 3)')
    165 
    166     def test_nesting(self):
    167         eq = self.assertEqual
    168         # everything is meant to give up after 6 levels.
    169         eq(r([[[[[[[]]]]]]]), "[[[[[[[]]]]]]]")
    170         eq(r([[[[[[[[]]]]]]]]), "[[[[[[[...]]]]]]]")
    171 
    172         eq(r(nestedTuple(6)), "(((((((),),),),),),)")
    173         eq(r(nestedTuple(7)), "(((((((...),),),),),),)")
    174 
    175         eq(r({ nestedTuple(5) : nestedTuple(5) }),
    176            "{((((((),),),),),): ((((((),),),),),)}")
    177         eq(r({ nestedTuple(6) : nestedTuple(6) }),
    178            "{((((((...),),),),),): ((((((...),),),),),)}")
    179 
    180         eq(r([[[[[[{}]]]]]]), "[[[[[[{}]]]]]]")
    181         eq(r([[[[[[[{}]]]]]]]), "[[[[[[[...]]]]]]]")
    182 
    183     def test_cell(self):
    184         def get_cell():
    185             x = 42
    186             def inner():
    187                 return x
    188             return inner
    189         x = get_cell().__closure__[0]
    190         self.assertRegex(repr(x), r'<cell at 0x[0-9A-Fa-f]+: '
    191                                   r'int object at 0x[0-9A-Fa-f]+>')
    192         self.assertRegex(r(x), r'<cell at 0x.*\.\.\..*>')
    193 
    194     def test_descriptors(self):
    195         eq = self.assertEqual
    196         # method descriptors
    197         eq(repr(dict.items), "<method 'items' of 'dict' objects>")
    198         # XXX member descriptors
    199         # XXX attribute descriptors
    200         # XXX slot descriptors
    201         # static and class methods
    202         class C:
    203             def foo(cls): pass
    204         x = staticmethod(C.foo)
    205         self.assertTrue(repr(x).startswith('<staticmethod object at 0x'))
    206         x = classmethod(C.foo)
    207         self.assertTrue(repr(x).startswith('<classmethod object at 0x'))
    208 
    209     def test_unsortable(self):
    210         # Repr.repr() used to call sorted() on sets, frozensets and dicts
    211         # without taking into account that not all objects are comparable
    212         x = set([1j, 2j, 3j])
    213         y = frozenset(x)
    214         z = {1j: 1, 2j: 2}
    215         r(x)
    216         r(y)
    217         r(z)
    218 
    219 def write_file(path, text):
    220     with open(path, 'w', encoding='ASCII') as fp:
    221         fp.write(text)
    222 
    223 class LongReprTest(unittest.TestCase):
    224     longname = 'areallylongpackageandmodulenametotestreprtruncation'
    225 
    226     def setUp(self):
    227         self.pkgname = os.path.join(self.longname)
    228         self.subpkgname = os.path.join(self.longname, self.longname)
    229         # Make the package and subpackage
    230         shutil.rmtree(self.pkgname, ignore_errors=True)
    231         os.mkdir(self.pkgname)
    232         create_empty_file(os.path.join(self.pkgname, '__init__.py'))
    233         shutil.rmtree(self.subpkgname, ignore_errors=True)
    234         os.mkdir(self.subpkgname)
    235         create_empty_file(os.path.join(self.subpkgname, '__init__.py'))
    236         # Remember where we are
    237         self.here = os.getcwd()
    238         sys.path.insert(0, self.here)
    239         # When regrtest is run with its -j option, this command alone is not
    240         # enough.
    241         importlib.invalidate_caches()
    242 
    243     def tearDown(self):
    244         actions = []
    245         for dirpath, dirnames, filenames in os.walk(self.pkgname):
    246             for name in dirnames + filenames:
    247                 actions.append(os.path.join(dirpath, name))
    248         actions.append(self.pkgname)
    249         actions.sort()
    250         actions.reverse()
    251         for p in actions:
    252             if os.path.isdir(p):
    253                 os.rmdir(p)
    254             else:
    255                 os.remove(p)
    256         del sys.path[0]
    257 
    258     def _check_path_limitations(self, module_name):
    259         # base directory
    260         source_path_len = len(self.here)
    261         # a path separator + `longname` (twice)
    262         source_path_len += 2 * (len(self.longname) + 1)
    263         # a path separator + `module_name` + ".py"
    264         source_path_len += len(module_name) + 1 + len(".py")
    265         cached_path_len = (source_path_len +
    266             len(importlib.util.cache_from_source("x.py")) - len("x.py"))
    267         if os.name == 'nt' and cached_path_len >= 258:
    268             # Under Windows, the max path len is 260 including C's terminating
    269             # NUL character.
    270             # (see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#maxpath)
    271             self.skipTest("test paths too long (%d characters) for Windows' 260 character limit"
    272                           % cached_path_len)
    273         elif os.name == 'nt' and verbose:
    274             print("cached_path_len =", cached_path_len)
    275 
    276     def test_module(self):
    277         self.maxDiff = None
    278         self._check_path_limitations(self.pkgname)
    279         create_empty_file(os.path.join(self.subpkgname, self.pkgname + '.py'))
    280         importlib.invalidate_caches()
    281         from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import areallylongpackageandmodulenametotestreprtruncation
    282         module = areallylongpackageandmodulenametotestreprtruncation
    283         self.assertEqual(repr(module), "<module %r from %r>" % (module.__name__, module.__file__))
    284         self.assertEqual(repr(sys), "<module 'sys' (built-in)>")
    285 
    286     def test_type(self):
    287         self._check_path_limitations('foo')
    288         eq = self.assertEqual
    289         write_file(os.path.join(self.subpkgname, 'foo.py'), '''\
    290 class foo(object):
    291     pass
    292 ''')
    293         importlib.invalidate_caches()
    294         from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import foo
    295         eq(repr(foo.foo),
    296                "<class '%s.foo'>" % foo.__name__)
    297 
    298     @unittest.skip('need a suitable object')
    299     def test_object(self):
    300         # XXX Test the repr of a type with a really long tp_name but with no
    301         # tp_repr.  WIBNI we had ::Inline? :)
    302         pass
    303 
    304     def test_class(self):
    305         self._check_path_limitations('bar')
    306         write_file(os.path.join(self.subpkgname, 'bar.py'), '''\
    307 class bar:
    308     pass
    309 ''')
    310         importlib.invalidate_caches()
    311         from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import bar
    312         # Module name may be prefixed with "test.", depending on how run.
    313         self.assertEqual(repr(bar.bar), "<class '%s.bar'>" % bar.__name__)
    314 
    315     def test_instance(self):
    316         self._check_path_limitations('baz')
    317         write_file(os.path.join(self.subpkgname, 'baz.py'), '''\
    318 class baz:
    319     pass
    320 ''')
    321         importlib.invalidate_caches()
    322         from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import baz
    323         ibaz = baz.baz()
    324         self.assertTrue(repr(ibaz).startswith(
    325             "<%s.baz object at 0x" % baz.__name__))
    326 
    327     def test_method(self):
    328         self._check_path_limitations('qux')
    329         eq = self.assertEqual
    330         write_file(os.path.join(self.subpkgname, 'qux.py'), '''\
    331 class aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:
    332     def amethod(self): pass
    333 ''')
    334         importlib.invalidate_caches()
    335         from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import qux
    336         # Unbound methods first
    337         r = repr(qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod)
    338         self.assertTrue(r.startswith('<function aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod'), r)
    339         # Bound method next
    340         iqux = qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()
    341         r = repr(iqux.amethod)
    342         self.assertTrue(r.startswith(
    343             '<bound method aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod of <%s.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa object at 0x' \
    344             % (qux.__name__,) ), r)
    345 
    346     @unittest.skip('needs a built-in function with a really long name')
    347     def test_builtin_function(self):
    348         # XXX test built-in functions and methods with really long names
    349         pass
    350 
    351 class ClassWithRepr:
    352     def __init__(self, s):
    353         self.s = s
    354     def __repr__(self):
    355         return "ClassWithRepr(%r)" % self.s
    356 
    357 
    358 class ClassWithFailingRepr:
    359     def __repr__(self):
    360         raise Exception("This should be caught by Repr.repr_instance")
    361 
    362 class MyContainer:
    363     'Helper class for TestRecursiveRepr'
    364     def __init__(self, values):
    365         self.values = list(values)
    366     def append(self, value):
    367         self.values.append(value)
    368     @recursive_repr()
    369     def __repr__(self):
    370         return '<' + ', '.join(map(str, self.values)) + '>'
    371 
    372 class MyContainer2(MyContainer):
    373     @recursive_repr('+++')
    374     def __repr__(self):
    375         return '<' + ', '.join(map(str, self.values)) + '>'
    376 
    377 class MyContainer3:
    378     def __repr__(self):
    379         'Test document content'
    380         pass
    381     wrapped = __repr__
    382     wrapper = recursive_repr()(wrapped)
    383 
    384 class TestRecursiveRepr(unittest.TestCase):
    385     def test_recursive_repr(self):
    386         m = MyContainer(list('abcde'))
    387         m.append(m)
    388         m.append('x')
    389         m.append(m)
    390         self.assertEqual(repr(m), '<a, b, c, d, e, ..., x, ...>')
    391         m = MyContainer2(list('abcde'))
    392         m.append(m)
    393         m.append('x')
    394         m.append(m)
    395         self.assertEqual(repr(m), '<a, b, c, d, e, +++, x, +++>')
    396 
    397     def test_assigned_attributes(self):
    398         from functools import WRAPPER_ASSIGNMENTS as assigned
    399         wrapped = MyContainer3.wrapped
    400         wrapper = MyContainer3.wrapper
    401         for name in assigned:
    402             self.assertIs(getattr(wrapper, name), getattr(wrapped, name))
    403 
    404 if __name__ == "__main__":
    405     unittest.main()
    406