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