test_reprlib.py 15 KB

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