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