/Lib/test/test_collections.py

http://unladen-swallow.googlecode.com/ · Python · 493 lines · 409 code · 57 blank · 27 comment · 41 complexity · bdf7a1560460581f4257f298e42e0ddb MD5 · raw file

  1. import unittest, doctest, sys
  2. from test import test_support
  3. from collections import namedtuple
  4. import pickle, cPickle, copy
  5. import keyword
  6. import re
  7. from collections import Hashable, Iterable, Iterator
  8. from collections import Sized, Container, Callable
  9. from collections import Set, MutableSet
  10. from collections import Mapping, MutableMapping
  11. from collections import Sequence, MutableSequence
  12. TestNT = namedtuple('TestNT', 'x y z') # type used for pickle tests
  13. class TestNamedTuple(unittest.TestCase):
  14. def test_factory(self):
  15. Point = namedtuple('Point', 'x y')
  16. self.assertEqual(Point.__name__, 'Point')
  17. # Docstrings are omitted with -O2 and above, so this test doesn't make
  18. # any sense.
  19. if sys.flags.optimize <= 1:
  20. self.assertEqual(Point.__doc__, 'Point(x, y)')
  21. self.assertEqual(Point.__slots__, ())
  22. self.assertEqual(Point.__module__, __name__)
  23. self.assertEqual(Point.__getitem__, tuple.__getitem__)
  24. self.assertEqual(Point._fields, ('x', 'y'))
  25. self.assertRaises(ValueError, namedtuple, 'abc%', 'efg ghi') # type has non-alpha char
  26. self.assertRaises(ValueError, namedtuple, 'class', 'efg ghi') # type has keyword
  27. self.assertRaises(ValueError, namedtuple, '9abc', 'efg ghi') # type starts with digit
  28. self.assertRaises(ValueError, namedtuple, 'abc', 'efg g%hi') # field with non-alpha char
  29. self.assertRaises(ValueError, namedtuple, 'abc', 'abc class') # field has keyword
  30. self.assertRaises(ValueError, namedtuple, 'abc', '8efg 9ghi') # field starts with digit
  31. self.assertRaises(ValueError, namedtuple, 'abc', '_efg ghi') # field with leading underscore
  32. self.assertRaises(ValueError, namedtuple, 'abc', 'efg efg ghi') # duplicate field
  33. namedtuple('Point0', 'x1 y2') # Verify that numbers are allowed in names
  34. namedtuple('_', 'a b c') # Test leading underscores in a typename
  35. nt = namedtuple('nt', u'the quick brown fox') # check unicode input
  36. self.assert_("u'" not in repr(nt._fields))
  37. nt = namedtuple('nt', (u'the', u'quick')) # check unicode input
  38. self.assert_("u'" not in repr(nt._fields))
  39. self.assertRaises(TypeError, Point._make, [11]) # catch too few args
  40. self.assertRaises(TypeError, Point._make, [11, 22, 33]) # catch too many args
  41. def test_instance(self):
  42. Point = namedtuple('Point', 'x y')
  43. p = Point(11, 22)
  44. self.assertEqual(p, Point(x=11, y=22))
  45. self.assertEqual(p, Point(11, y=22))
  46. self.assertEqual(p, Point(y=22, x=11))
  47. self.assertEqual(p, Point(*(11, 22)))
  48. self.assertEqual(p, Point(**dict(x=11, y=22)))
  49. self.assertRaises(TypeError, Point, 1) # too few args
  50. self.assertRaises(TypeError, Point, 1, 2, 3) # too many args
  51. self.assertRaises(TypeError, eval, 'Point(XXX=1, y=2)', locals()) # wrong keyword argument
  52. self.assertRaises(TypeError, eval, 'Point(x=1)', locals()) # missing keyword argument
  53. self.assertEqual(repr(p), 'Point(x=11, y=22)')
  54. self.assert_('__dict__' not in dir(p)) # verify instance has no dict
  55. self.assert_('__weakref__' not in dir(p))
  56. self.assertEqual(p, Point._make([11, 22])) # test _make classmethod
  57. self.assertEqual(p._fields, ('x', 'y')) # test _fields attribute
  58. self.assertEqual(p._replace(x=1), (1, 22)) # test _replace method
  59. self.assertEqual(p._asdict(), dict(x=11, y=22)) # test _asdict method
  60. try:
  61. p._replace(x=1, error=2)
  62. except ValueError:
  63. pass
  64. else:
  65. self._fail('Did not detect an incorrect fieldname')
  66. # verify that field string can have commas
  67. Point = namedtuple('Point', 'x, y')
  68. p = Point(x=11, y=22)
  69. self.assertEqual(repr(p), 'Point(x=11, y=22)')
  70. # verify that fieldspec can be a non-string sequence
  71. Point = namedtuple('Point', ('x', 'y'))
  72. p = Point(x=11, y=22)
  73. self.assertEqual(repr(p), 'Point(x=11, y=22)')
  74. def test_tupleness(self):
  75. Point = namedtuple('Point', 'x y')
  76. p = Point(11, 22)
  77. self.assert_(isinstance(p, tuple))
  78. self.assertEqual(p, (11, 22)) # matches a real tuple
  79. self.assertEqual(tuple(p), (11, 22)) # coercable to a real tuple
  80. self.assertEqual(list(p), [11, 22]) # coercable to a list
  81. self.assertEqual(max(p), 22) # iterable
  82. self.assertEqual(max(*p), 22) # star-able
  83. x, y = p
  84. self.assertEqual(p, (x, y)) # unpacks like a tuple
  85. self.assertEqual((p[0], p[1]), (11, 22)) # indexable like a tuple
  86. self.assertRaises(IndexError, p.__getitem__, 3)
  87. self.assertEqual(p.x, x)
  88. self.assertEqual(p.y, y)
  89. self.assertRaises(AttributeError, eval, 'p.z', locals())
  90. def test_odd_sizes(self):
  91. Zero = namedtuple('Zero', '')
  92. self.assertEqual(Zero(), ())
  93. self.assertEqual(Zero._make([]), ())
  94. self.assertEqual(repr(Zero()), 'Zero()')
  95. self.assertEqual(Zero()._asdict(), {})
  96. self.assertEqual(Zero()._fields, ())
  97. Dot = namedtuple('Dot', 'd')
  98. self.assertEqual(Dot(1), (1,))
  99. self.assertEqual(Dot._make([1]), (1,))
  100. self.assertEqual(Dot(1).d, 1)
  101. self.assertEqual(repr(Dot(1)), 'Dot(d=1)')
  102. self.assertEqual(Dot(1)._asdict(), {'d':1})
  103. self.assertEqual(Dot(1)._replace(d=999), (999,))
  104. self.assertEqual(Dot(1)._fields, ('d',))
  105. n = 5000
  106. import string, random
  107. names = list(set(''.join([random.choice(string.ascii_letters)
  108. for j in range(10)]) for i in range(n)))
  109. n = len(names)
  110. Big = namedtuple('Big', names)
  111. b = Big(*range(n))
  112. self.assertEqual(b, tuple(range(n)))
  113. self.assertEqual(Big._make(range(n)), tuple(range(n)))
  114. for pos, name in enumerate(names):
  115. self.assertEqual(getattr(b, name), pos)
  116. repr(b) # make sure repr() doesn't blow-up
  117. d = b._asdict()
  118. d_expected = dict(zip(names, range(n)))
  119. self.assertEqual(d, d_expected)
  120. b2 = b._replace(**dict([(names[1], 999),(names[-5], 42)]))
  121. b2_expected = range(n)
  122. b2_expected[1] = 999
  123. b2_expected[-5] = 42
  124. self.assertEqual(b2, tuple(b2_expected))
  125. self.assertEqual(b._fields, tuple(names))
  126. def test_pickle(self):
  127. p = TestNT(x=10, y=20, z=30)
  128. for module in pickle, cPickle:
  129. loads = getattr(module, 'loads')
  130. dumps = getattr(module, 'dumps')
  131. for protocol in -1, 0, 1, 2:
  132. q = loads(dumps(p, protocol))
  133. self.assertEqual(p, q)
  134. self.assertEqual(p._fields, q._fields)
  135. def test_copy(self):
  136. p = TestNT(x=10, y=20, z=30)
  137. for copier in copy.copy, copy.deepcopy:
  138. q = copier(p)
  139. self.assertEqual(p, q)
  140. self.assertEqual(p._fields, q._fields)
  141. def test_name_conflicts(self):
  142. # Some names like "self", "cls", "tuple", "itemgetter", and "property"
  143. # failed when used as field names. Test to make sure these now work.
  144. T = namedtuple('T', 'itemgetter property self cls tuple')
  145. t = T(1, 2, 3, 4, 5)
  146. self.assertEqual(t, (1,2,3,4,5))
  147. newt = t._replace(itemgetter=10, property=20, self=30, cls=40, tuple=50)
  148. self.assertEqual(newt, (10,20,30,40,50))
  149. # Broader test of all interesting names in a template
  150. with test_support.captured_stdout() as template:
  151. T = namedtuple('T', 'x', verbose=True)
  152. words = set(re.findall('[A-Za-z]+', template.getvalue()))
  153. words -= set(keyword.kwlist)
  154. T = namedtuple('T', words)
  155. # test __new__
  156. values = tuple(range(len(words)))
  157. t = T(*values)
  158. self.assertEqual(t, values)
  159. t = T(**dict(zip(T._fields, values)))
  160. self.assertEqual(t, values)
  161. # test _make
  162. t = T._make(values)
  163. self.assertEqual(t, values)
  164. # exercise __repr__
  165. repr(t)
  166. # test _asdict
  167. self.assertEqual(t._asdict(), dict(zip(T._fields, values)))
  168. # test _replace
  169. t = T._make(values)
  170. newvalues = tuple(v*10 for v in values)
  171. newt = t._replace(**dict(zip(T._fields, newvalues)))
  172. self.assertEqual(newt, newvalues)
  173. # test _fields
  174. self.assertEqual(T._fields, tuple(words))
  175. # test __getnewargs__
  176. self.assertEqual(t.__getnewargs__(), values)
  177. class ABCTestCase(unittest.TestCase):
  178. def validate_abstract_methods(self, abc, *names):
  179. methodstubs = dict.fromkeys(names, lambda s, *args: 0)
  180. # everything should work will all required methods are present
  181. C = type('C', (abc,), methodstubs)
  182. C()
  183. # instantiation should fail if a required method is missing
  184. for name in names:
  185. stubs = methodstubs.copy()
  186. del stubs[name]
  187. C = type('C', (abc,), stubs)
  188. self.assertRaises(TypeError, C, name)
  189. class TestOneTrickPonyABCs(ABCTestCase):
  190. def test_Hashable(self):
  191. # Check some non-hashables
  192. non_samples = [list(), set(), dict()]
  193. for x in non_samples:
  194. self.failIf(isinstance(x, Hashable), repr(x))
  195. self.failIf(issubclass(type(x), Hashable), repr(type(x)))
  196. # Check some hashables
  197. samples = [None,
  198. int(), float(), complex(),
  199. str(),
  200. tuple(), frozenset(),
  201. int, list, object, type,
  202. ]
  203. for x in samples:
  204. self.failUnless(isinstance(x, Hashable), repr(x))
  205. self.failUnless(issubclass(type(x), Hashable), repr(type(x)))
  206. self.assertRaises(TypeError, Hashable)
  207. # Check direct subclassing
  208. class H(Hashable):
  209. def __hash__(self):
  210. return super(H, self).__hash__()
  211. __eq__ = Hashable.__eq__ # Silence Py3k warning
  212. self.assertEqual(hash(H()), 0)
  213. self.failIf(issubclass(int, H))
  214. self.validate_abstract_methods(Hashable, '__hash__')
  215. def test_Iterable(self):
  216. # Check some non-iterables
  217. non_samples = [None, 42, 3.14, 1j]
  218. for x in non_samples:
  219. self.failIf(isinstance(x, Iterable), repr(x))
  220. self.failIf(issubclass(type(x), Iterable), repr(type(x)))
  221. # Check some iterables
  222. samples = [str(),
  223. tuple(), list(), set(), frozenset(), dict(),
  224. dict().keys(), dict().items(), dict().values(),
  225. (lambda: (yield))(),
  226. (x for x in []),
  227. ]
  228. for x in samples:
  229. self.failUnless(isinstance(x, Iterable), repr(x))
  230. self.failUnless(issubclass(type(x), Iterable), repr(type(x)))
  231. # Check direct subclassing
  232. class I(Iterable):
  233. def __iter__(self):
  234. return super(I, self).__iter__()
  235. self.assertEqual(list(I()), [])
  236. self.failIf(issubclass(str, I))
  237. self.validate_abstract_methods(Iterable, '__iter__')
  238. def test_Iterator(self):
  239. non_samples = [None, 42, 3.14, 1j, "".encode('ascii'), "", (), [],
  240. {}, set()]
  241. for x in non_samples:
  242. self.failIf(isinstance(x, Iterator), repr(x))
  243. self.failIf(issubclass(type(x), Iterator), repr(type(x)))
  244. samples = [iter(str()),
  245. iter(tuple()), iter(list()), iter(dict()),
  246. iter(set()), iter(frozenset()),
  247. iter(dict().keys()), iter(dict().items()),
  248. iter(dict().values()),
  249. (lambda: (yield))(),
  250. (x for x in []),
  251. ]
  252. for x in samples:
  253. self.failUnless(isinstance(x, Iterator), repr(x))
  254. self.failUnless(issubclass(type(x), Iterator), repr(type(x)))
  255. self.validate_abstract_methods(Iterator, 'next')
  256. def test_Sized(self):
  257. non_samples = [None, 42, 3.14, 1j,
  258. (lambda: (yield))(),
  259. (x for x in []),
  260. ]
  261. for x in non_samples:
  262. self.failIf(isinstance(x, Sized), repr(x))
  263. self.failIf(issubclass(type(x), Sized), repr(type(x)))
  264. samples = [str(),
  265. tuple(), list(), set(), frozenset(), dict(),
  266. dict().keys(), dict().items(), dict().values(),
  267. ]
  268. for x in samples:
  269. self.failUnless(isinstance(x, Sized), repr(x))
  270. self.failUnless(issubclass(type(x), Sized), repr(type(x)))
  271. self.validate_abstract_methods(Sized, '__len__')
  272. def test_Container(self):
  273. non_samples = [None, 42, 3.14, 1j,
  274. (lambda: (yield))(),
  275. (x for x in []),
  276. ]
  277. for x in non_samples:
  278. self.failIf(isinstance(x, Container), repr(x))
  279. self.failIf(issubclass(type(x), Container), repr(type(x)))
  280. samples = [str(),
  281. tuple(), list(), set(), frozenset(), dict(),
  282. dict().keys(), dict().items(),
  283. ]
  284. for x in samples:
  285. self.failUnless(isinstance(x, Container), repr(x))
  286. self.failUnless(issubclass(type(x), Container), repr(type(x)))
  287. self.validate_abstract_methods(Container, '__contains__')
  288. def test_Callable(self):
  289. non_samples = [None, 42, 3.14, 1j,
  290. "", "".encode('ascii'), (), [], {}, set(),
  291. (lambda: (yield))(),
  292. (x for x in []),
  293. ]
  294. for x in non_samples:
  295. self.failIf(isinstance(x, Callable), repr(x))
  296. self.failIf(issubclass(type(x), Callable), repr(type(x)))
  297. samples = [lambda: None,
  298. type, int, object,
  299. len,
  300. list.append, [].append,
  301. ]
  302. for x in samples:
  303. self.failUnless(isinstance(x, Callable), repr(x))
  304. self.failUnless(issubclass(type(x), Callable), repr(type(x)))
  305. self.validate_abstract_methods(Callable, '__call__')
  306. def test_direct_subclassing(self):
  307. for B in Hashable, Iterable, Iterator, Sized, Container, Callable:
  308. class C(B):
  309. pass
  310. self.failUnless(issubclass(C, B))
  311. self.failIf(issubclass(int, C))
  312. def test_registration(self):
  313. for B in Hashable, Iterable, Iterator, Sized, Container, Callable:
  314. class C:
  315. __metaclass__ = type
  316. __hash__ = None # Make sure it isn't hashable by default
  317. self.failIf(issubclass(C, B), B.__name__)
  318. B.register(C)
  319. self.failUnless(issubclass(C, B))
  320. class WithSet(MutableSet):
  321. def __init__(self, it=()):
  322. self.data = set(it)
  323. def __len__(self):
  324. return len(self.data)
  325. def __iter__(self):
  326. return iter(self.data)
  327. def __contains__(self, item):
  328. return item in self.data
  329. def add(self, item):
  330. self.data.add(item)
  331. def discard(self, item):
  332. self.data.discard(item)
  333. class TestCollectionABCs(ABCTestCase):
  334. # XXX For now, we only test some virtual inheritance properties.
  335. # We should also test the proper behavior of the collection ABCs
  336. # as real base classes or mix-in classes.
  337. def test_Set(self):
  338. for sample in [set, frozenset]:
  339. self.failUnless(isinstance(sample(), Set))
  340. self.failUnless(issubclass(sample, Set))
  341. self.validate_abstract_methods(Set, '__contains__', '__iter__', '__len__')
  342. def test_hash_Set(self):
  343. class OneTwoThreeSet(Set):
  344. def __init__(self):
  345. self.contents = [1, 2, 3]
  346. def __contains__(self, x):
  347. return x in self.contents
  348. def __len__(self):
  349. return len(self.contents)
  350. def __iter__(self):
  351. return iter(self.contents)
  352. def __hash__(self):
  353. return self._hash()
  354. a, b = OneTwoThreeSet(), OneTwoThreeSet()
  355. self.failUnless(hash(a) == hash(b))
  356. def test_MutableSet(self):
  357. self.failUnless(isinstance(set(), MutableSet))
  358. self.failUnless(issubclass(set, MutableSet))
  359. self.failIf(isinstance(frozenset(), MutableSet))
  360. self.failIf(issubclass(frozenset, MutableSet))
  361. self.validate_abstract_methods(MutableSet, '__contains__', '__iter__', '__len__',
  362. 'add', 'discard')
  363. def test_issue_5647(self):
  364. # MutableSet.__iand__ mutated the set during iteration
  365. s = WithSet('abcd')
  366. s &= WithSet('cdef') # This used to fail
  367. self.assertEqual(set(s), set('cd'))
  368. def test_issue_4920(self):
  369. # MutableSet.pop() method did not work
  370. class MySet(collections.MutableSet):
  371. __slots__=['__s']
  372. def __init__(self,items=None):
  373. if items is None:
  374. items=[]
  375. self.__s=set(items)
  376. def __contains__(self,v):
  377. return v in self.__s
  378. def __iter__(self):
  379. return iter(self.__s)
  380. def __len__(self):
  381. return len(self.__s)
  382. def add(self,v):
  383. result=v not in self.__s
  384. self.__s.add(v)
  385. return result
  386. def discard(self,v):
  387. result=v in self.__s
  388. self.__s.discard(v)
  389. return result
  390. def __repr__(self):
  391. return "MySet(%s)" % repr(list(self))
  392. s = MySet([5,43,2,1])
  393. self.assertEqual(s.pop(), 1)
  394. def test_Mapping(self):
  395. for sample in [dict]:
  396. self.failUnless(isinstance(sample(), Mapping))
  397. self.failUnless(issubclass(sample, Mapping))
  398. self.validate_abstract_methods(Mapping, '__contains__', '__iter__', '__len__',
  399. '__getitem__')
  400. def test_MutableMapping(self):
  401. for sample in [dict]:
  402. self.failUnless(isinstance(sample(), MutableMapping))
  403. self.failUnless(issubclass(sample, MutableMapping))
  404. self.validate_abstract_methods(MutableMapping, '__contains__', '__iter__', '__len__',
  405. '__getitem__', '__setitem__', '__delitem__')
  406. def test_Sequence(self):
  407. for sample in [tuple, list, str]:
  408. self.failUnless(isinstance(sample(), Sequence))
  409. self.failUnless(issubclass(sample, Sequence))
  410. self.failUnless(issubclass(basestring, Sequence))
  411. self.failUnless(isinstance(range(10), Sequence))
  412. self.failUnless(issubclass(xrange, Sequence))
  413. self.failUnless(issubclass(str, Sequence))
  414. self.validate_abstract_methods(Sequence, '__contains__', '__iter__', '__len__',
  415. '__getitem__')
  416. def test_MutableSequence(self):
  417. for sample in [tuple, str]:
  418. self.failIf(isinstance(sample(), MutableSequence))
  419. self.failIf(issubclass(sample, MutableSequence))
  420. for sample in [list]:
  421. self.failUnless(isinstance(sample(), MutableSequence))
  422. self.failUnless(issubclass(sample, MutableSequence))
  423. self.failIf(issubclass(basestring, MutableSequence))
  424. self.validate_abstract_methods(MutableSequence, '__contains__', '__iter__',
  425. '__len__', '__getitem__', '__setitem__', '__delitem__', 'insert')
  426. import doctest, collections
  427. def test_main(verbose=None):
  428. if sys.flags.optimize >= 2:
  429. print >>sys.stderr, "test_collections -- skipping some tests due to -O flag."
  430. sys.stderr.flush()
  431. NamedTupleDocs = doctest.DocTestSuite(module=collections)
  432. test_classes = [TestNamedTuple, NamedTupleDocs, TestOneTrickPonyABCs, TestCollectionABCs]
  433. test_support.run_unittest(*test_classes)
  434. test_support.run_doctest(collections, verbose)
  435. if __name__ == "__main__":
  436. test_main(verbose=True)