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