test_more.py 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794
  1. from __future__ import division, print_function, unicode_literals
  2. from decimal import Decimal
  3. from doctest import DocTestSuite
  4. from fractions import Fraction
  5. from functools import partial, reduce
  6. from heapq import merge
  7. from io import StringIO
  8. from itertools import (
  9. chain,
  10. count,
  11. groupby,
  12. islice,
  13. permutations,
  14. product,
  15. repeat,
  16. )
  17. from operator import add, itemgetter
  18. from unittest import TestCase
  19. from six.moves import filter, map, range, zip
  20. import more_itertools as mi
  21. def load_tests(loader, tests, ignore):
  22. # Add the doctests
  23. tests.addTests(DocTestSuite('more_itertools.more'))
  24. return tests
  25. class CollateTests(TestCase):
  26. """Unit tests for ``collate()``"""
  27. # Also accidentally tests peekable, though that could use its own tests
  28. def test_default(self):
  29. """Test with the default `key` function."""
  30. iterables = [range(4), range(7), range(3, 6)]
  31. self.assertEqual(
  32. sorted(reduce(list.__add__, [list(it) for it in iterables])),
  33. list(mi.collate(*iterables))
  34. )
  35. def test_key(self):
  36. """Test using a custom `key` function."""
  37. iterables = [range(5, 0, -1), range(4, 0, -1)]
  38. actual = sorted(
  39. reduce(list.__add__, [list(it) for it in iterables]), reverse=True
  40. )
  41. expected = list(mi.collate(*iterables, key=lambda x: -x))
  42. self.assertEqual(actual, expected)
  43. def test_empty(self):
  44. """Be nice if passed an empty list of iterables."""
  45. self.assertEqual([], list(mi.collate()))
  46. def test_one(self):
  47. """Work when only 1 iterable is passed."""
  48. self.assertEqual([0, 1], list(mi.collate(range(2))))
  49. def test_reverse(self):
  50. """Test the `reverse` kwarg."""
  51. iterables = [range(4, 0, -1), range(7, 0, -1), range(3, 6, -1)]
  52. actual = sorted(
  53. reduce(list.__add__, [list(it) for it in iterables]), reverse=True
  54. )
  55. expected = list(mi.collate(*iterables, reverse=True))
  56. self.assertEqual(actual, expected)
  57. def test_alias(self):
  58. self.assertNotEqual(merge.__doc__, mi.collate.__doc__)
  59. self.assertNotEqual(partial.__doc__, mi.collate.__doc__)
  60. class ChunkedTests(TestCase):
  61. """Tests for ``chunked()``"""
  62. def test_even(self):
  63. """Test when ``n`` divides evenly into the length of the iterable."""
  64. self.assertEqual(
  65. list(mi.chunked('ABCDEF', 3)), [['A', 'B', 'C'], ['D', 'E', 'F']]
  66. )
  67. def test_odd(self):
  68. """Test when ``n`` does not divide evenly into the length of the
  69. iterable.
  70. """
  71. self.assertEqual(
  72. list(mi.chunked('ABCDE', 3)), [['A', 'B', 'C'], ['D', 'E']]
  73. )
  74. class FirstTests(TestCase):
  75. """Tests for ``first()``"""
  76. def test_many(self):
  77. """Test that it works on many-item iterables."""
  78. # Also try it on a generator expression to make sure it works on
  79. # whatever those return, across Python versions.
  80. self.assertEqual(mi.first(x for x in range(4)), 0)
  81. def test_one(self):
  82. """Test that it doesn't raise StopIteration prematurely."""
  83. self.assertEqual(mi.first([3]), 3)
  84. def test_empty_stop_iteration(self):
  85. """It should raise StopIteration for empty iterables."""
  86. self.assertRaises(ValueError, lambda: mi.first([]))
  87. def test_default(self):
  88. """It should return the provided default arg for empty iterables."""
  89. self.assertEqual(mi.first([], 'boo'), 'boo')
  90. class PeekableTests(TestCase):
  91. """Tests for ``peekable()`` behavor not incidentally covered by testing
  92. ``collate()``
  93. """
  94. def test_peek_default(self):
  95. """Make sure passing a default into ``peek()`` works."""
  96. p = mi.peekable([])
  97. self.assertEqual(p.peek(7), 7)
  98. def test_truthiness(self):
  99. """Make sure a ``peekable`` tests true iff there are items remaining in
  100. the iterable.
  101. """
  102. p = mi.peekable([])
  103. self.assertFalse(p)
  104. p = mi.peekable(range(3))
  105. self.assertTrue(p)
  106. def test_simple_peeking(self):
  107. """Make sure ``next`` and ``peek`` advance and don't advance the
  108. iterator, respectively.
  109. """
  110. p = mi.peekable(range(10))
  111. self.assertEqual(next(p), 0)
  112. self.assertEqual(p.peek(), 1)
  113. self.assertEqual(next(p), 1)
  114. def test_indexing(self):
  115. """
  116. Indexing into the peekable shouldn't advance the iterator.
  117. """
  118. p = mi.peekable('abcdefghijkl')
  119. # The 0th index is what ``next()`` will return
  120. self.assertEqual(p[0], 'a')
  121. self.assertEqual(next(p), 'a')
  122. # Indexing further into the peekable shouldn't advance the itertor
  123. self.assertEqual(p[2], 'd')
  124. self.assertEqual(next(p), 'b')
  125. # The 0th index moves up with the iterator; the last index follows
  126. self.assertEqual(p[0], 'c')
  127. self.assertEqual(p[9], 'l')
  128. self.assertEqual(next(p), 'c')
  129. self.assertEqual(p[8], 'l')
  130. # Negative indexing should work too
  131. self.assertEqual(p[-2], 'k')
  132. self.assertEqual(p[-9], 'd')
  133. self.assertRaises(IndexError, lambda: p[-10])
  134. def test_slicing(self):
  135. """Slicing the peekable shouldn't advance the iterator."""
  136. seq = list('abcdefghijkl')
  137. p = mi.peekable(seq)
  138. # Slicing the peekable should just be like slicing a re-iterable
  139. self.assertEqual(p[1:4], seq[1:4])
  140. # Advancing the iterator moves the slices up also
  141. self.assertEqual(next(p), 'a')
  142. self.assertEqual(p[1:4], seq[1:][1:4])
  143. # Implicit starts and stop should work
  144. self.assertEqual(p[:5], seq[1:][:5])
  145. self.assertEqual(p[:], seq[1:][:])
  146. # Indexing past the end should work
  147. self.assertEqual(p[:100], seq[1:][:100])
  148. # Steps should work, including negative
  149. self.assertEqual(p[::2], seq[1:][::2])
  150. self.assertEqual(p[::-1], seq[1:][::-1])
  151. def test_slicing_reset(self):
  152. """Test slicing on a fresh iterable each time"""
  153. iterable = ['0', '1', '2', '3', '4', '5']
  154. indexes = list(range(-4, len(iterable) + 4)) + [None]
  155. steps = [1, 2, 3, 4, -1, -2, -3, 4]
  156. for slice_args in product(indexes, indexes, steps):
  157. it = iter(iterable)
  158. p = mi.peekable(it)
  159. next(p)
  160. index = slice(*slice_args)
  161. actual = p[index]
  162. expected = iterable[1:][index]
  163. self.assertEqual(actual, expected, slice_args)
  164. def test_slicing_error(self):
  165. iterable = '01234567'
  166. p = mi.peekable(iter(iterable))
  167. # Prime the cache
  168. p.peek()
  169. old_cache = list(p._cache)
  170. # Illegal slice
  171. with self.assertRaises(ValueError):
  172. p[1:-1:0]
  173. # Neither the cache nor the iteration should be affected
  174. self.assertEqual(old_cache, list(p._cache))
  175. self.assertEqual(list(p), list(iterable))
  176. def test_passthrough(self):
  177. """Iterating a peekable without using ``peek()`` or ``prepend()``
  178. should just give the underlying iterable's elements (a trivial test but
  179. useful to set a baseline in case something goes wrong)"""
  180. expected = [1, 2, 3, 4, 5]
  181. actual = list(mi.peekable(expected))
  182. self.assertEqual(actual, expected)
  183. # prepend() behavior tests
  184. def test_prepend(self):
  185. """Tests intersperesed ``prepend()`` and ``next()`` calls"""
  186. it = mi.peekable(range(2))
  187. actual = []
  188. # Test prepend() before next()
  189. it.prepend(10)
  190. actual += [next(it), next(it)]
  191. # Test prepend() between next()s
  192. it.prepend(11)
  193. actual += [next(it), next(it)]
  194. # Test prepend() after source iterable is consumed
  195. it.prepend(12)
  196. actual += [next(it)]
  197. expected = [10, 0, 11, 1, 12]
  198. self.assertEqual(actual, expected)
  199. def test_multi_prepend(self):
  200. """Tests prepending multiple items and getting them in proper order"""
  201. it = mi.peekable(range(5))
  202. actual = [next(it), next(it)]
  203. it.prepend(10, 11, 12)
  204. it.prepend(20, 21)
  205. actual += list(it)
  206. expected = [0, 1, 20, 21, 10, 11, 12, 2, 3, 4]
  207. self.assertEqual(actual, expected)
  208. def test_empty(self):
  209. """Tests prepending in front of an empty iterable"""
  210. it = mi.peekable([])
  211. it.prepend(10)
  212. actual = list(it)
  213. expected = [10]
  214. self.assertEqual(actual, expected)
  215. def test_prepend_truthiness(self):
  216. """Tests that ``__bool__()`` or ``__nonzero__()`` works properly
  217. with ``prepend()``"""
  218. it = mi.peekable(range(5))
  219. self.assertTrue(it)
  220. actual = list(it)
  221. self.assertFalse(it)
  222. it.prepend(10)
  223. self.assertTrue(it)
  224. actual += [next(it)]
  225. self.assertFalse(it)
  226. expected = [0, 1, 2, 3, 4, 10]
  227. self.assertEqual(actual, expected)
  228. def test_multi_prepend_peek(self):
  229. """Tests prepending multiple elements and getting them in reverse order
  230. while peeking"""
  231. it = mi.peekable(range(5))
  232. actual = [next(it), next(it)]
  233. self.assertEqual(it.peek(), 2)
  234. it.prepend(10, 11, 12)
  235. self.assertEqual(it.peek(), 10)
  236. it.prepend(20, 21)
  237. self.assertEqual(it.peek(), 20)
  238. actual += list(it)
  239. self.assertFalse(it)
  240. expected = [0, 1, 20, 21, 10, 11, 12, 2, 3, 4]
  241. self.assertEqual(actual, expected)
  242. def test_prepend_after_stop(self):
  243. """Test resuming iteration after a previous exhaustion"""
  244. it = mi.peekable(range(3))
  245. self.assertEqual(list(it), [0, 1, 2])
  246. self.assertRaises(StopIteration, lambda: next(it))
  247. it.prepend(10)
  248. self.assertEqual(next(it), 10)
  249. self.assertRaises(StopIteration, lambda: next(it))
  250. def test_prepend_slicing(self):
  251. """Tests interaction between prepending and slicing"""
  252. seq = list(range(20))
  253. p = mi.peekable(seq)
  254. p.prepend(30, 40, 50)
  255. pseq = [30, 40, 50] + seq # pseq for prepended_seq
  256. # adapt the specific tests from test_slicing
  257. self.assertEqual(p[0], 30)
  258. self.assertEqual(p[1:8], pseq[1:8])
  259. self.assertEqual(p[1:], pseq[1:])
  260. self.assertEqual(p[:5], pseq[:5])
  261. self.assertEqual(p[:], pseq[:])
  262. self.assertEqual(p[:100], pseq[:100])
  263. self.assertEqual(p[::2], pseq[::2])
  264. self.assertEqual(p[::-1], pseq[::-1])
  265. def test_prepend_indexing(self):
  266. """Tests interaction between prepending and indexing"""
  267. seq = list(range(20))
  268. p = mi.peekable(seq)
  269. p.prepend(30, 40, 50)
  270. self.assertEqual(p[0], 30)
  271. self.assertEqual(next(p), 30)
  272. self.assertEqual(p[2], 0)
  273. self.assertEqual(next(p), 40)
  274. self.assertEqual(p[0], 50)
  275. self.assertEqual(p[9], 8)
  276. self.assertEqual(next(p), 50)
  277. self.assertEqual(p[8], 8)
  278. self.assertEqual(p[-2], 18)
  279. self.assertEqual(p[-9], 11)
  280. self.assertRaises(IndexError, lambda: p[-21])
  281. def test_prepend_iterable(self):
  282. """Tests prepending from an iterable"""
  283. it = mi.peekable(range(5))
  284. # Don't directly use the range() object to avoid any range-specific
  285. # optimizations
  286. it.prepend(*(x for x in range(5)))
  287. actual = list(it)
  288. expected = list(chain(range(5), range(5)))
  289. self.assertEqual(actual, expected)
  290. def test_prepend_many(self):
  291. """Tests that prepending a huge number of elements works"""
  292. it = mi.peekable(range(5))
  293. # Don't directly use the range() object to avoid any range-specific
  294. # optimizations
  295. it.prepend(*(x for x in range(20000)))
  296. actual = list(it)
  297. expected = list(chain(range(20000), range(5)))
  298. self.assertEqual(actual, expected)
  299. def test_prepend_reversed(self):
  300. """Tests prepending from a reversed iterable"""
  301. it = mi.peekable(range(3))
  302. it.prepend(*reversed((10, 11, 12)))
  303. actual = list(it)
  304. expected = [12, 11, 10, 0, 1, 2]
  305. self.assertEqual(actual, expected)
  306. class ConsumerTests(TestCase):
  307. """Tests for ``consumer()``"""
  308. def test_consumer(self):
  309. @mi.consumer
  310. def eater():
  311. while True:
  312. x = yield # noqa
  313. e = eater()
  314. e.send('hi') # without @consumer, would raise TypeError
  315. class DistinctPermutationsTests(TestCase):
  316. def test_distinct_permutations(self):
  317. """Make sure the output for ``distinct_permutations()`` is the same as
  318. set(permutations(it)).
  319. """
  320. iterable = ['z', 'a', 'a', 'q', 'q', 'q', 'y']
  321. test_output = sorted(mi.distinct_permutations(iterable))
  322. ref_output = sorted(set(permutations(iterable)))
  323. self.assertEqual(test_output, ref_output)
  324. class IlenTests(TestCase):
  325. def test_ilen(self):
  326. """Sanity-checks for ``ilen()``."""
  327. # Non-empty
  328. self.assertEqual(
  329. mi.ilen(filter(lambda x: x % 10 == 0, range(101))), 11
  330. )
  331. # Empty
  332. self.assertEqual(mi.ilen((x for x in range(0))), 0)
  333. # Iterable with __len__
  334. self.assertEqual(mi.ilen(list(range(6))), 6)
  335. class WithIterTests(TestCase):
  336. def test_with_iter(self):
  337. s = StringIO('One fish\nTwo fish')
  338. initial_words = [line.split()[0] for line in mi.with_iter(s)]
  339. # Iterable's items should be faithfully represented
  340. self.assertEqual(initial_words, ['One', 'Two'])
  341. # The file object should be closed
  342. self.assertEqual(s.closed, True)
  343. class OneTests(TestCase):
  344. def test_basic(self):
  345. it = iter(['item'])
  346. self.assertEqual(mi.one(it), 'item')
  347. def test_too_short(self):
  348. it = iter([])
  349. self.assertRaises(ValueError, lambda: mi.one(it))
  350. self.assertRaises(IndexError, lambda: mi.one(it, too_short=IndexError))
  351. def test_too_long(self):
  352. it = count()
  353. self.assertRaises(ValueError, lambda: mi.one(it)) # burn 0 and 1
  354. self.assertEqual(next(it), 2)
  355. self.assertRaises(
  356. OverflowError, lambda: mi.one(it, too_long=OverflowError)
  357. )
  358. class IntersperseTest(TestCase):
  359. """ Tests for intersperse() """
  360. def test_even(self):
  361. iterable = (x for x in '01')
  362. self.assertEqual(
  363. list(mi.intersperse(None, iterable)), ['0', None, '1']
  364. )
  365. def test_odd(self):
  366. iterable = (x for x in '012')
  367. self.assertEqual(
  368. list(mi.intersperse(None, iterable)), ['0', None, '1', None, '2']
  369. )
  370. def test_nested(self):
  371. element = ('a', 'b')
  372. iterable = (x for x in '012')
  373. actual = list(mi.intersperse(element, iterable))
  374. expected = ['0', ('a', 'b'), '1', ('a', 'b'), '2']
  375. self.assertEqual(actual, expected)
  376. def test_not_iterable(self):
  377. self.assertRaises(TypeError, lambda: mi.intersperse('x', 1))
  378. def test_n(self):
  379. for n, element, expected in [
  380. (1, '_', ['0', '_', '1', '_', '2', '_', '3', '_', '4', '_', '5']),
  381. (2, '_', ['0', '1', '_', '2', '3', '_', '4', '5']),
  382. (3, '_', ['0', '1', '2', '_', '3', '4', '5']),
  383. (4, '_', ['0', '1', '2', '3', '_', '4', '5']),
  384. (5, '_', ['0', '1', '2', '3', '4', '_', '5']),
  385. (6, '_', ['0', '1', '2', '3', '4', '5']),
  386. (7, '_', ['0', '1', '2', '3', '4', '5']),
  387. (3, ['a', 'b'], ['0', '1', '2', ['a', 'b'], '3', '4', '5']),
  388. ]:
  389. iterable = (x for x in '012345')
  390. actual = list(mi.intersperse(element, iterable, n=n))
  391. self.assertEqual(actual, expected)
  392. def test_n_zero(self):
  393. self.assertRaises(
  394. ValueError, lambda: list(mi.intersperse('x', '012', n=0))
  395. )
  396. class UniqueToEachTests(TestCase):
  397. """Tests for ``unique_to_each()``"""
  398. def test_all_unique(self):
  399. """When all the input iterables are unique the output should match
  400. the input."""
  401. iterables = [[1, 2], [3, 4, 5], [6, 7, 8]]
  402. self.assertEqual(mi.unique_to_each(*iterables), iterables)
  403. def test_duplicates(self):
  404. """When there are duplicates in any of the input iterables that aren't
  405. in the rest, those duplicates should be emitted."""
  406. iterables = ["mississippi", "missouri"]
  407. self.assertEqual(
  408. mi.unique_to_each(*iterables), [['p', 'p'], ['o', 'u', 'r']]
  409. )
  410. def test_mixed(self):
  411. """When the input iterables contain different types the function should
  412. still behave properly"""
  413. iterables = ['x', (i for i in range(3)), [1, 2, 3], tuple()]
  414. self.assertEqual(mi.unique_to_each(*iterables), [['x'], [0], [3], []])
  415. class WindowedTests(TestCase):
  416. """Tests for ``windowed()``"""
  417. def test_basic(self):
  418. actual = list(mi.windowed([1, 2, 3, 4, 5], 3))
  419. expected = [(1, 2, 3), (2, 3, 4), (3, 4, 5)]
  420. self.assertEqual(actual, expected)
  421. def test_large_size(self):
  422. """
  423. When the window size is larger than the iterable, and no fill value is
  424. given,``None`` should be filled in.
  425. """
  426. actual = list(mi.windowed([1, 2, 3, 4, 5], 6))
  427. expected = [(1, 2, 3, 4, 5, None)]
  428. self.assertEqual(actual, expected)
  429. def test_fillvalue(self):
  430. """
  431. When sizes don't match evenly, the given fill value should be used.
  432. """
  433. iterable = [1, 2, 3, 4, 5]
  434. for n, kwargs, expected in [
  435. (6, {}, [(1, 2, 3, 4, 5, '!')]), # n > len(iterable)
  436. (3, {'step': 3}, [(1, 2, 3), (4, 5, '!')]), # using ``step``
  437. ]:
  438. actual = list(mi.windowed(iterable, n, fillvalue='!', **kwargs))
  439. self.assertEqual(actual, expected)
  440. def test_zero(self):
  441. """When the window size is zero, an empty tuple should be emitted."""
  442. actual = list(mi.windowed([1, 2, 3, 4, 5], 0))
  443. expected = [tuple()]
  444. self.assertEqual(actual, expected)
  445. def test_negative(self):
  446. """When the window size is negative, ValueError should be raised."""
  447. with self.assertRaises(ValueError):
  448. list(mi.windowed([1, 2, 3, 4, 5], -1))
  449. def test_step(self):
  450. """The window should advance by the number of steps provided"""
  451. iterable = [1, 2, 3, 4, 5, 6, 7]
  452. for n, step, expected in [
  453. (3, 2, [(1, 2, 3), (3, 4, 5), (5, 6, 7)]), # n > step
  454. (3, 3, [(1, 2, 3), (4, 5, 6), (7, None, None)]), # n == step
  455. (3, 4, [(1, 2, 3), (5, 6, 7)]), # line up nicely
  456. (3, 5, [(1, 2, 3), (6, 7, None)]), # off by one
  457. (3, 6, [(1, 2, 3), (7, None, None)]), # off by two
  458. (3, 7, [(1, 2, 3)]), # step past the end
  459. (7, 8, [(1, 2, 3, 4, 5, 6, 7)]), # step > len(iterable)
  460. ]:
  461. actual = list(mi.windowed(iterable, n, step=step))
  462. self.assertEqual(actual, expected)
  463. # Step must be greater than or equal to 1
  464. with self.assertRaises(ValueError):
  465. list(mi.windowed(iterable, 3, step=0))
  466. class BucketTests(TestCase):
  467. """Tests for ``bucket()``"""
  468. def test_basic(self):
  469. iterable = [10, 20, 30, 11, 21, 31, 12, 22, 23, 33]
  470. D = mi.bucket(iterable, key=lambda x: 10 * (x // 10))
  471. # In-order access
  472. self.assertEqual(list(D[10]), [10, 11, 12])
  473. # Out of order access
  474. self.assertEqual(list(D[30]), [30, 31, 33])
  475. self.assertEqual(list(D[20]), [20, 21, 22, 23])
  476. self.assertEqual(list(D[40]), []) # Nothing in here!
  477. def test_in(self):
  478. iterable = [10, 20, 30, 11, 21, 31, 12, 22, 23, 33]
  479. D = mi.bucket(iterable, key=lambda x: 10 * (x // 10))
  480. self.assertTrue(10 in D)
  481. self.assertFalse(40 in D)
  482. self.assertTrue(20 in D)
  483. self.assertFalse(21 in D)
  484. # Checking in-ness shouldn't advance the iterator
  485. self.assertEqual(next(D[10]), 10)
  486. def test_validator(self):
  487. iterable = count(0)
  488. key = lambda x: int(str(x)[0]) # First digit of each number
  489. validator = lambda x: 0 < x < 10 # No leading zeros
  490. D = mi.bucket(iterable, key, validator=validator)
  491. self.assertEqual(mi.take(3, D[1]), [1, 10, 11])
  492. self.assertNotIn(0, D) # Non-valid entries don't return True
  493. self.assertNotIn(0, D._cache) # Don't store non-valid entries
  494. self.assertEqual(list(D[0]), [])
  495. class SpyTests(TestCase):
  496. """Tests for ``spy()``"""
  497. def test_basic(self):
  498. original_iterable = iter('abcdefg')
  499. head, new_iterable = mi.spy(original_iterable)
  500. self.assertEqual(head, ['a'])
  501. self.assertEqual(
  502. list(new_iterable), ['a', 'b', 'c', 'd', 'e', 'f', 'g']
  503. )
  504. def test_unpacking(self):
  505. original_iterable = iter('abcdefg')
  506. (first, second, third), new_iterable = mi.spy(original_iterable, 3)
  507. self.assertEqual(first, 'a')
  508. self.assertEqual(second, 'b')
  509. self.assertEqual(third, 'c')
  510. self.assertEqual(
  511. list(new_iterable), ['a', 'b', 'c', 'd', 'e', 'f', 'g']
  512. )
  513. def test_too_many(self):
  514. original_iterable = iter('abc')
  515. head, new_iterable = mi.spy(original_iterable, 4)
  516. self.assertEqual(head, ['a', 'b', 'c'])
  517. self.assertEqual(list(new_iterable), ['a', 'b', 'c'])
  518. def test_zero(self):
  519. original_iterable = iter('abc')
  520. head, new_iterable = mi.spy(original_iterable, 0)
  521. self.assertEqual(head, [])
  522. self.assertEqual(list(new_iterable), ['a', 'b', 'c'])
  523. class InterleaveTests(TestCase):
  524. def test_even(self):
  525. actual = list(mi.interleave([1, 4, 7], [2, 5, 8], [3, 6, 9]))
  526. expected = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  527. self.assertEqual(actual, expected)
  528. def test_short(self):
  529. actual = list(mi.interleave([1, 4], [2, 5, 7], [3, 6, 8]))
  530. expected = [1, 2, 3, 4, 5, 6]
  531. self.assertEqual(actual, expected)
  532. def test_mixed_types(self):
  533. it_list = ['a', 'b', 'c', 'd']
  534. it_str = '12345'
  535. it_inf = count()
  536. actual = list(mi.interleave(it_list, it_str, it_inf))
  537. expected = ['a', '1', 0, 'b', '2', 1, 'c', '3', 2, 'd', '4', 3]
  538. self.assertEqual(actual, expected)
  539. class InterleaveLongestTests(TestCase):
  540. def test_even(self):
  541. actual = list(mi.interleave_longest([1, 4, 7], [2, 5, 8], [3, 6, 9]))
  542. expected = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  543. self.assertEqual(actual, expected)
  544. def test_short(self):
  545. actual = list(mi.interleave_longest([1, 4], [2, 5, 7], [3, 6, 8]))
  546. expected = [1, 2, 3, 4, 5, 6, 7, 8]
  547. self.assertEqual(actual, expected)
  548. def test_mixed_types(self):
  549. it_list = ['a', 'b', 'c', 'd']
  550. it_str = '12345'
  551. it_gen = (x for x in range(3))
  552. actual = list(mi.interleave_longest(it_list, it_str, it_gen))
  553. expected = ['a', '1', 0, 'b', '2', 1, 'c', '3', 2, 'd', '4', '5']
  554. self.assertEqual(actual, expected)
  555. class TestCollapse(TestCase):
  556. """Tests for ``collapse()``"""
  557. def test_collapse(self):
  558. l = [[1], 2, [[3], 4], [[[5]]]]
  559. self.assertEqual(list(mi.collapse(l)), [1, 2, 3, 4, 5])
  560. def test_collapse_to_string(self):
  561. l = [["s1"], "s2", [["s3"], "s4"], [[["s5"]]]]
  562. self.assertEqual(list(mi.collapse(l)), ["s1", "s2", "s3", "s4", "s5"])
  563. def test_collapse_flatten(self):
  564. l = [[1], [2], [[3], 4], [[[5]]]]
  565. self.assertEqual(list(mi.collapse(l, levels=1)), list(mi.flatten(l)))
  566. def test_collapse_to_level(self):
  567. l = [[1], 2, [[3], 4], [[[5]]]]
  568. self.assertEqual(list(mi.collapse(l, levels=2)), [1, 2, 3, 4, [5]])
  569. self.assertEqual(
  570. list(mi.collapse(mi.collapse(l, levels=1), levels=1)),
  571. list(mi.collapse(l, levels=2))
  572. )
  573. def test_collapse_to_list(self):
  574. l = (1, [2], (3, [4, (5,)], 'ab'))
  575. actual = list(mi.collapse(l, base_type=list))
  576. expected = [1, [2], 3, [4, (5,)], 'ab']
  577. self.assertEqual(actual, expected)
  578. class SideEffectTests(TestCase):
  579. """Tests for ``side_effect()``"""
  580. def test_individual(self):
  581. # The function increments the counter for each call
  582. counter = [0]
  583. def func(arg):
  584. counter[0] += 1
  585. result = list(mi.side_effect(func, range(10)))
  586. self.assertEqual(result, list(range(10)))
  587. self.assertEqual(counter[0], 10)
  588. def test_chunked(self):
  589. # The function increments the counter for each call
  590. counter = [0]
  591. def func(arg):
  592. counter[0] += 1
  593. result = list(mi.side_effect(func, range(10), 2))
  594. self.assertEqual(result, list(range(10)))
  595. self.assertEqual(counter[0], 5)
  596. def test_before_after(self):
  597. f = StringIO()
  598. collector = []
  599. def func(item):
  600. print(item, file=f)
  601. collector.append(f.getvalue())
  602. def it():
  603. yield u'a'
  604. yield u'b'
  605. raise RuntimeError('kaboom')
  606. before = lambda: print('HEADER', file=f)
  607. after = f.close
  608. try:
  609. mi.consume(mi.side_effect(func, it(), before=before, after=after))
  610. except RuntimeError:
  611. pass
  612. # The iterable should have been written to the file
  613. self.assertEqual(collector, [u'HEADER\na\n', u'HEADER\na\nb\n'])
  614. # The file should be closed even though something bad happened
  615. self.assertTrue(f.closed)
  616. def test_before_fails(self):
  617. f = StringIO()
  618. func = lambda x: print(x, file=f)
  619. def before():
  620. raise RuntimeError('ouch')
  621. try:
  622. mi.consume(
  623. mi.side_effect(func, u'abc', before=before, after=f.close)
  624. )
  625. except RuntimeError:
  626. pass
  627. # The file should be closed even though something bad happened in the
  628. # before function
  629. self.assertTrue(f.closed)
  630. class SlicedTests(TestCase):
  631. """Tests for ``sliced()``"""
  632. def test_even(self):
  633. """Test when the length of the sequence is divisible by *n*"""
  634. seq = 'ABCDEFGHI'
  635. self.assertEqual(list(mi.sliced(seq, 3)), ['ABC', 'DEF', 'GHI'])
  636. def test_odd(self):
  637. """Test when the length of the sequence is not divisible by *n*"""
  638. seq = 'ABCDEFGHI'
  639. self.assertEqual(list(mi.sliced(seq, 4)), ['ABCD', 'EFGH', 'I'])
  640. def test_not_sliceable(self):
  641. seq = (x for x in 'ABCDEFGHI')
  642. with self.assertRaises(TypeError):
  643. list(mi.sliced(seq, 3))
  644. class SplitAtTests(TestCase):
  645. """Tests for ``split()``"""
  646. def comp_with_str_split(self, str_to_split, delim):
  647. pred = lambda c: c == delim
  648. actual = list(map(''.join, mi.split_at(str_to_split, pred)))
  649. expected = str_to_split.split(delim)
  650. self.assertEqual(actual, expected)
  651. def test_seperators(self):
  652. test_strs = ['', 'abcba', 'aaabbbcccddd', 'e']
  653. for s, delim in product(test_strs, 'abcd'):
  654. self.comp_with_str_split(s, delim)
  655. class SplitBeforeTest(TestCase):
  656. """Tests for ``split_before()``"""
  657. def test_starts_with_sep(self):
  658. actual = list(mi.split_before('xooxoo', lambda c: c == 'x'))
  659. expected = [['x', 'o', 'o'], ['x', 'o', 'o']]
  660. self.assertEqual(actual, expected)
  661. def test_ends_with_sep(self):
  662. actual = list(mi.split_before('ooxoox', lambda c: c == 'x'))
  663. expected = [['o', 'o'], ['x', 'o', 'o'], ['x']]
  664. self.assertEqual(actual, expected)
  665. def test_no_sep(self):
  666. actual = list(mi.split_before('ooo', lambda c: c == 'x'))
  667. expected = [['o', 'o', 'o']]
  668. self.assertEqual(actual, expected)
  669. class SplitAfterTest(TestCase):
  670. """Tests for ``split_after()``"""
  671. def test_starts_with_sep(self):
  672. actual = list(mi.split_after('xooxoo', lambda c: c == 'x'))
  673. expected = [['x'], ['o', 'o', 'x'], ['o', 'o']]
  674. self.assertEqual(actual, expected)
  675. def test_ends_with_sep(self):
  676. actual = list(mi.split_after('ooxoox', lambda c: c == 'x'))
  677. expected = [['o', 'o', 'x'], ['o', 'o', 'x']]
  678. self.assertEqual(actual, expected)
  679. def test_no_sep(self):
  680. actual = list(mi.split_after('ooo', lambda c: c == 'x'))
  681. expected = [['o', 'o', 'o']]
  682. self.assertEqual(actual, expected)
  683. class PaddedTest(TestCase):
  684. """Tests for ``padded()``"""
  685. def test_no_n(self):
  686. seq = [1, 2, 3]
  687. # No fillvalue
  688. self.assertEqual(mi.take(5, mi.padded(seq)), [1, 2, 3, None, None])
  689. # With fillvalue
  690. self.assertEqual(
  691. mi.take(5, mi.padded(seq, fillvalue='')), [1, 2, 3, '', '']
  692. )
  693. def test_invalid_n(self):
  694. self.assertRaises(ValueError, lambda: list(mi.padded([1, 2, 3], n=-1)))
  695. self.assertRaises(ValueError, lambda: list(mi.padded([1, 2, 3], n=0)))
  696. def test_valid_n(self):
  697. seq = [1, 2, 3, 4, 5]
  698. # No need for padding: len(seq) <= n
  699. self.assertEqual(list(mi.padded(seq, n=4)), [1, 2, 3, 4, 5])
  700. self.assertEqual(list(mi.padded(seq, n=5)), [1, 2, 3, 4, 5])
  701. # No fillvalue
  702. self.assertEqual(
  703. list(mi.padded(seq, n=7)), [1, 2, 3, 4, 5, None, None]
  704. )
  705. # With fillvalue
  706. self.assertEqual(
  707. list(mi.padded(seq, fillvalue='', n=7)), [1, 2, 3, 4, 5, '', '']
  708. )
  709. def test_next_multiple(self):
  710. seq = [1, 2, 3, 4, 5, 6]
  711. # No need for padding: len(seq) % n == 0
  712. self.assertEqual(
  713. list(mi.padded(seq, n=3, next_multiple=True)), [1, 2, 3, 4, 5, 6]
  714. )
  715. # Padding needed: len(seq) < n
  716. self.assertEqual(
  717. list(mi.padded(seq, n=8, next_multiple=True)),
  718. [1, 2, 3, 4, 5, 6, None, None]
  719. )
  720. # No padding needed: len(seq) == n
  721. self.assertEqual(
  722. list(mi.padded(seq, n=6, next_multiple=True)), [1, 2, 3, 4, 5, 6]
  723. )
  724. # Padding needed: len(seq) > n
  725. self.assertEqual(
  726. list(mi.padded(seq, n=4, next_multiple=True)),
  727. [1, 2, 3, 4, 5, 6, None, None]
  728. )
  729. # With fillvalue
  730. self.assertEqual(
  731. list(mi.padded(seq, fillvalue='', n=4, next_multiple=True)),
  732. [1, 2, 3, 4, 5, 6, '', '']
  733. )
  734. class DistributeTest(TestCase):
  735. """Tests for distribute()"""
  736. def test_invalid_n(self):
  737. self.assertRaises(ValueError, lambda: mi.distribute(-1, [1, 2, 3]))
  738. self.assertRaises(ValueError, lambda: mi.distribute(0, [1, 2, 3]))
  739. def test_basic(self):
  740. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  741. for n, expected in [
  742. (1, [iterable]),
  743. (2, [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]),
  744. (3, [[1, 4, 7, 10], [2, 5, 8], [3, 6, 9]]),
  745. (10, [[n] for n in range(1, 10 + 1)]),
  746. ]:
  747. self.assertEqual(
  748. [list(x) for x in mi.distribute(n, iterable)], expected
  749. )
  750. def test_large_n(self):
  751. iterable = [1, 2, 3, 4]
  752. self.assertEqual(
  753. [list(x) for x in mi.distribute(6, iterable)],
  754. [[1], [2], [3], [4], [], []]
  755. )
  756. class StaggerTest(TestCase):
  757. """Tests for ``stagger()``"""
  758. def test_default(self):
  759. iterable = [0, 1, 2, 3]
  760. actual = list(mi.stagger(iterable))
  761. expected = [(None, 0, 1), (0, 1, 2), (1, 2, 3)]
  762. self.assertEqual(actual, expected)
  763. def test_offsets(self):
  764. iterable = [0, 1, 2, 3]
  765. for offsets, expected in [
  766. ((-2, 0, 2), [('', 0, 2), ('', 1, 3)]),
  767. ((-2, -1), [('', ''), ('', 0), (0, 1), (1, 2), (2, 3)]),
  768. ((1, 2), [(1, 2), (2, 3)]),
  769. ]:
  770. all_groups = mi.stagger(iterable, offsets=offsets, fillvalue='')
  771. self.assertEqual(list(all_groups), expected)
  772. def test_longest(self):
  773. iterable = [0, 1, 2, 3]
  774. for offsets, expected in [
  775. (
  776. (-1, 0, 1),
  777. [('', 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, ''), (3, '', '')]
  778. ),
  779. ((-2, -1), [('', ''), ('', 0), (0, 1), (1, 2), (2, 3), (3, '')]),
  780. ((1, 2), [(1, 2), (2, 3), (3, '')]),
  781. ]:
  782. all_groups = mi.stagger(
  783. iterable, offsets=offsets, fillvalue='', longest=True
  784. )
  785. self.assertEqual(list(all_groups), expected)
  786. class ZipOffsetTest(TestCase):
  787. """Tests for ``zip_offset()``"""
  788. def test_shortest(self):
  789. a_1 = [0, 1, 2, 3]
  790. a_2 = [0, 1, 2, 3, 4, 5]
  791. a_3 = [0, 1, 2, 3, 4, 5, 6, 7]
  792. actual = list(
  793. mi.zip_offset(a_1, a_2, a_3, offsets=(-1, 0, 1), fillvalue='')
  794. )
  795. expected = [('', 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5)]
  796. self.assertEqual(actual, expected)
  797. def test_longest(self):
  798. a_1 = [0, 1, 2, 3]
  799. a_2 = [0, 1, 2, 3, 4, 5]
  800. a_3 = [0, 1, 2, 3, 4, 5, 6, 7]
  801. actual = list(
  802. mi.zip_offset(a_1, a_2, a_3, offsets=(-1, 0, 1), longest=True)
  803. )
  804. expected = [
  805. (None, 0, 1),
  806. (0, 1, 2),
  807. (1, 2, 3),
  808. (2, 3, 4),
  809. (3, 4, 5),
  810. (None, 5, 6),
  811. (None, None, 7),
  812. ]
  813. self.assertEqual(actual, expected)
  814. def test_mismatch(self):
  815. iterables = [0, 1, 2], [2, 3, 4]
  816. offsets = (-1, 0, 1)
  817. self.assertRaises(
  818. ValueError,
  819. lambda: list(mi.zip_offset(*iterables, offsets=offsets))
  820. )
  821. class SortTogetherTest(TestCase):
  822. """Tests for sort_together()"""
  823. def test_key_list(self):
  824. """tests `key_list` including default, iterables include duplicates"""
  825. iterables = [
  826. ['GA', 'GA', 'GA', 'CT', 'CT', 'CT'],
  827. ['May', 'Aug.', 'May', 'June', 'July', 'July'],
  828. [97, 20, 100, 70, 100, 20]
  829. ]
  830. self.assertEqual(
  831. mi.sort_together(iterables),
  832. [
  833. ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'),
  834. ('June', 'July', 'July', 'May', 'Aug.', 'May'),
  835. (70, 100, 20, 97, 20, 100)
  836. ]
  837. )
  838. self.assertEqual(
  839. mi.sort_together(iterables, key_list=(0, 1)),
  840. [
  841. ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'),
  842. ('July', 'July', 'June', 'Aug.', 'May', 'May'),
  843. (100, 20, 70, 20, 97, 100)
  844. ]
  845. )
  846. self.assertEqual(
  847. mi.sort_together(iterables, key_list=(0, 1, 2)),
  848. [
  849. ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'),
  850. ('July', 'July', 'June', 'Aug.', 'May', 'May'),
  851. (20, 100, 70, 20, 97, 100)
  852. ]
  853. )
  854. self.assertEqual(
  855. mi.sort_together(iterables, key_list=(2,)),
  856. [
  857. ('GA', 'CT', 'CT', 'GA', 'GA', 'CT'),
  858. ('Aug.', 'July', 'June', 'May', 'May', 'July'),
  859. (20, 20, 70, 97, 100, 100)
  860. ]
  861. )
  862. def test_invalid_key_list(self):
  863. """tests `key_list` for indexes not available in `iterables`"""
  864. iterables = [
  865. ['GA', 'GA', 'GA', 'CT', 'CT', 'CT'],
  866. ['May', 'Aug.', 'May', 'June', 'July', 'July'],
  867. [97, 20, 100, 70, 100, 20]
  868. ]
  869. self.assertRaises(
  870. IndexError, lambda: mi.sort_together(iterables, key_list=(5,))
  871. )
  872. def test_reverse(self):
  873. """tests `reverse` to ensure a reverse sort for `key_list` iterables"""
  874. iterables = [
  875. ['GA', 'GA', 'GA', 'CT', 'CT', 'CT'],
  876. ['May', 'Aug.', 'May', 'June', 'July', 'July'],
  877. [97, 20, 100, 70, 100, 20]
  878. ]
  879. self.assertEqual(
  880. mi.sort_together(iterables, key_list=(0, 1, 2), reverse=True),
  881. [('GA', 'GA', 'GA', 'CT', 'CT', 'CT'),
  882. ('May', 'May', 'Aug.', 'June', 'July', 'July'),
  883. (100, 97, 20, 70, 100, 20)]
  884. )
  885. def test_uneven_iterables(self):
  886. """tests trimming of iterables to the shortest length before sorting"""
  887. iterables = [['GA', 'GA', 'GA', 'CT', 'CT', 'CT', 'MA'],
  888. ['May', 'Aug.', 'May', 'June', 'July', 'July'],
  889. [97, 20, 100, 70, 100, 20, 0]]
  890. self.assertEqual(
  891. mi.sort_together(iterables),
  892. [
  893. ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'),
  894. ('June', 'July', 'July', 'May', 'Aug.', 'May'),
  895. (70, 100, 20, 97, 20, 100)
  896. ]
  897. )
  898. class DivideTest(TestCase):
  899. """Tests for divide()"""
  900. def test_invalid_n(self):
  901. self.assertRaises(ValueError, lambda: mi.divide(-1, [1, 2, 3]))
  902. self.assertRaises(ValueError, lambda: mi.divide(0, [1, 2, 3]))
  903. def test_basic(self):
  904. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  905. for n, expected in [
  906. (1, [iterable]),
  907. (2, [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]),
  908. (3, [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]),
  909. (10, [[n] for n in range(1, 10 + 1)]),
  910. ]:
  911. self.assertEqual(
  912. [list(x) for x in mi.divide(n, iterable)], expected
  913. )
  914. def test_large_n(self):
  915. iterable = [1, 2, 3, 4]
  916. self.assertEqual(
  917. [list(x) for x in mi.divide(6, iterable)],
  918. [[1], [2], [3], [4], [], []]
  919. )
  920. class TestAlwaysIterable(TestCase):
  921. """Tests for always_iterable()"""
  922. def test_single(self):
  923. self.assertEqual(list(mi.always_iterable(1)), [1])
  924. def test_strings(self):
  925. for obj in ['foo', b'bar', u'baz']:
  926. actual = list(mi.always_iterable(obj))
  927. expected = [obj]
  928. self.assertEqual(actual, expected)
  929. def test_base_type(self):
  930. dict_obj = {'a': 1, 'b': 2}
  931. str_obj = '123'
  932. # Default: dicts are iterable like they normally are
  933. default_actual = list(mi.always_iterable(dict_obj))
  934. default_expected = list(dict_obj)
  935. self.assertEqual(default_actual, default_expected)
  936. # Unitary types set: dicts are not iterable
  937. custom_actual = list(mi.always_iterable(dict_obj, base_type=dict))
  938. custom_expected = [dict_obj]
  939. self.assertEqual(custom_actual, custom_expected)
  940. # With unitary types set, strings are iterable
  941. str_actual = list(mi.always_iterable(str_obj, base_type=None))
  942. str_expected = list(str_obj)
  943. self.assertEqual(str_actual, str_expected)
  944. def test_iterables(self):
  945. self.assertEqual(list(mi.always_iterable([0, 1])), [0, 1])
  946. self.assertEqual(
  947. list(mi.always_iterable([0, 1], base_type=list)), [[0, 1]]
  948. )
  949. self.assertEqual(
  950. list(mi.always_iterable(iter('foo'))), ['f', 'o', 'o']
  951. )
  952. self.assertEqual(list(mi.always_iterable([])), [])
  953. def test_none(self):
  954. self.assertEqual(list(mi.always_iterable(None)), [])
  955. def test_generator(self):
  956. def _gen():
  957. yield 0
  958. yield 1
  959. self.assertEqual(list(mi.always_iterable(_gen())), [0, 1])
  960. class AdjacentTests(TestCase):
  961. def test_typical(self):
  962. actual = list(mi.adjacent(lambda x: x % 5 == 0, range(10)))
  963. expected = [(True, 0), (True, 1), (False, 2), (False, 3), (True, 4),
  964. (True, 5), (True, 6), (False, 7), (False, 8), (False, 9)]
  965. self.assertEqual(actual, expected)
  966. def test_empty_iterable(self):
  967. actual = list(mi.adjacent(lambda x: x % 5 == 0, []))
  968. expected = []
  969. self.assertEqual(actual, expected)
  970. def test_length_one(self):
  971. actual = list(mi.adjacent(lambda x: x % 5 == 0, [0]))
  972. expected = [(True, 0)]
  973. self.assertEqual(actual, expected)
  974. actual = list(mi.adjacent(lambda x: x % 5 == 0, [1]))
  975. expected = [(False, 1)]
  976. self.assertEqual(actual, expected)
  977. def test_consecutive_true(self):
  978. """Test that when the predicate matches multiple consecutive elements
  979. it doesn't repeat elements in the output"""
  980. actual = list(mi.adjacent(lambda x: x % 5 < 2, range(10)))
  981. expected = [(True, 0), (True, 1), (True, 2), (False, 3), (True, 4),
  982. (True, 5), (True, 6), (True, 7), (False, 8), (False, 9)]
  983. self.assertEqual(actual, expected)
  984. def test_distance(self):
  985. actual = list(mi.adjacent(lambda x: x % 5 == 0, range(10), distance=2))
  986. expected = [(True, 0), (True, 1), (True, 2), (True, 3), (True, 4),
  987. (True, 5), (True, 6), (True, 7), (False, 8), (False, 9)]
  988. self.assertEqual(actual, expected)
  989. actual = list(mi.adjacent(lambda x: x % 5 == 0, range(10), distance=3))
  990. expected = [(True, 0), (True, 1), (True, 2), (True, 3), (True, 4),
  991. (True, 5), (True, 6), (True, 7), (True, 8), (False, 9)]
  992. self.assertEqual(actual, expected)
  993. def test_large_distance(self):
  994. """Test distance larger than the length of the iterable"""
  995. iterable = range(10)
  996. actual = list(mi.adjacent(lambda x: x % 5 == 4, iterable, distance=20))
  997. expected = list(zip(repeat(True), iterable))
  998. self.assertEqual(actual, expected)
  999. actual = list(mi.adjacent(lambda x: False, iterable, distance=20))
  1000. expected = list(zip(repeat(False), iterable))
  1001. self.assertEqual(actual, expected)
  1002. def test_zero_distance(self):
  1003. """Test that adjacent() reduces to zip+map when distance is 0"""
  1004. iterable = range(1000)
  1005. predicate = lambda x: x % 4 == 2
  1006. actual = mi.adjacent(predicate, iterable, 0)
  1007. expected = zip(map(predicate, iterable), iterable)
  1008. self.assertTrue(all(a == e for a, e in zip(actual, expected)))
  1009. def test_negative_distance(self):
  1010. """Test that adjacent() raises an error with negative distance"""
  1011. pred = lambda x: x
  1012. self.assertRaises(
  1013. ValueError, lambda: mi.adjacent(pred, range(1000), -1)
  1014. )
  1015. self.assertRaises(
  1016. ValueError, lambda: mi.adjacent(pred, range(10), -10)
  1017. )
  1018. def test_grouping(self):
  1019. """Test interaction of adjacent() with groupby_transform()"""
  1020. iterable = mi.adjacent(lambda x: x % 5 == 0, range(10))
  1021. grouper = mi.groupby_transform(iterable, itemgetter(0), itemgetter(1))
  1022. actual = [(k, list(g)) for k, g in grouper]
  1023. expected = [
  1024. (True, [0, 1]),
  1025. (False, [2, 3]),
  1026. (True, [4, 5, 6]),
  1027. (False, [7, 8, 9]),
  1028. ]
  1029. self.assertEqual(actual, expected)
  1030. def test_call_once(self):
  1031. """Test that the predicate is only called once per item."""
  1032. already_seen = set()
  1033. iterable = range(10)
  1034. def predicate(item):
  1035. self.assertNotIn(item, already_seen)
  1036. already_seen.add(item)
  1037. return True
  1038. actual = list(mi.adjacent(predicate, iterable))
  1039. expected = [(True, x) for x in iterable]
  1040. self.assertEqual(actual, expected)
  1041. class GroupByTransformTests(TestCase):
  1042. def assertAllGroupsEqual(self, groupby1, groupby2):
  1043. """Compare two groupby objects for equality, both keys and groups."""
  1044. for a, b in zip(groupby1, groupby2):
  1045. key1, group1 = a
  1046. key2, group2 = b
  1047. self.assertEqual(key1, key2)
  1048. self.assertListEqual(list(group1), list(group2))
  1049. self.assertRaises(StopIteration, lambda: next(groupby1))
  1050. self.assertRaises(StopIteration, lambda: next(groupby2))
  1051. def test_default_funcs(self):
  1052. """Test that groupby_transform() with default args mimics groupby()"""
  1053. iterable = [(x // 5, x) for x in range(1000)]
  1054. actual = mi.groupby_transform(iterable)
  1055. expected = groupby(iterable)
  1056. self.assertAllGroupsEqual(actual, expected)
  1057. def test_valuefunc(self):
  1058. iterable = [(int(x / 5), int(x / 3), x) for x in range(10)]
  1059. # Test the standard usage of grouping one iterable using another's keys
  1060. grouper = mi.groupby_transform(
  1061. iterable, keyfunc=itemgetter(0), valuefunc=itemgetter(-1)
  1062. )
  1063. actual = [(k, list(g)) for k, g in grouper]
  1064. expected = [(0, [0, 1, 2, 3, 4]), (1, [5, 6, 7, 8, 9])]
  1065. self.assertEqual(actual, expected)
  1066. grouper = mi.groupby_transform(
  1067. iterable, keyfunc=itemgetter(1), valuefunc=itemgetter(-1)
  1068. )
  1069. actual = [(k, list(g)) for k, g in grouper]
  1070. expected = [(0, [0, 1, 2]), (1, [3, 4, 5]), (2, [6, 7, 8]), (3, [9])]
  1071. self.assertEqual(actual, expected)
  1072. # and now for something a little different
  1073. d = dict(zip(range(10), 'abcdefghij'))
  1074. grouper = mi.groupby_transform(
  1075. range(10), keyfunc=lambda x: x // 5, valuefunc=d.get
  1076. )
  1077. actual = [(k, ''.join(g)) for k, g in grouper]
  1078. expected = [(0, 'abcde'), (1, 'fghij')]
  1079. self.assertEqual(actual, expected)
  1080. def test_no_valuefunc(self):
  1081. iterable = range(1000)
  1082. def key(x):
  1083. return x // 5
  1084. actual = mi.groupby_transform(iterable, key, valuefunc=None)
  1085. expected = groupby(iterable, key)
  1086. self.assertAllGroupsEqual(actual, expected)
  1087. actual = mi.groupby_transform(iterable, key) # default valuefunc
  1088. expected = groupby(iterable, key)
  1089. self.assertAllGroupsEqual(actual, expected)
  1090. class NumericRangeTests(TestCase):
  1091. def test_basic(self):
  1092. for args, expected in [
  1093. ((4,), [0, 1, 2, 3]),
  1094. ((4.0,), [0.0, 1.0, 2.0, 3.0]),
  1095. ((1.0, 4), [1.0, 2.0, 3.0]),
  1096. ((1, 4.0), [1, 2, 3]),
  1097. ((1.0, 5), [1.0, 2.0, 3.0, 4.0]),
  1098. ((0, 20, 5), [0, 5, 10, 15]),
  1099. ((0, 20, 5.0), [0.0, 5.0, 10.0, 15.0]),
  1100. ((0, 10, 3), [0, 3, 6, 9]),
  1101. ((0, 10, 3.0), [0.0, 3.0, 6.0, 9.0]),
  1102. ((0, -5, -1), [0, -1, -2, -3, -4]),
  1103. ((0.0, -5, -1), [0.0, -1.0, -2.0, -3.0, -4.0]),
  1104. ((1, 2, Fraction(1, 2)), [Fraction(1, 1), Fraction(3, 2)]),
  1105. ((0,), []),
  1106. ((0.0,), []),
  1107. ((1, 0), []),
  1108. ((1.0, 0.0), []),
  1109. ((Fraction(2, 1),), [Fraction(0, 1), Fraction(1, 1)]),
  1110. ((Decimal('2.0'),), [Decimal('0.0'), Decimal('1.0')]),
  1111. ]:
  1112. actual = list(mi.numeric_range(*args))
  1113. self.assertEqual(actual, expected)
  1114. self.assertTrue(
  1115. all(type(a) == type(e) for a, e in zip(actual, expected))
  1116. )
  1117. def test_arg_count(self):
  1118. self.assertRaises(TypeError, lambda: list(mi.numeric_range()))
  1119. self.assertRaises(
  1120. TypeError, lambda: list(mi.numeric_range(0, 1, 2, 3))
  1121. )
  1122. def test_zero_step(self):
  1123. self.assertRaises(
  1124. ValueError, lambda: list(mi.numeric_range(1, 2, 0))
  1125. )
  1126. class CountCycleTests(TestCase):
  1127. def test_basic(self):
  1128. expected = [
  1129. (0, 'a'), (0, 'b'), (0, 'c'),
  1130. (1, 'a'), (1, 'b'), (1, 'c'),
  1131. (2, 'a'), (2, 'b'), (2, 'c'),
  1132. ]
  1133. for actual in [
  1134. mi.take(9, mi.count_cycle('abc')), # n=None
  1135. list(mi.count_cycle('abc', 3)), # n=3
  1136. ]:
  1137. self.assertEqual(actual, expected)
  1138. def test_empty(self):
  1139. self.assertEqual(list(mi.count_cycle('')), [])
  1140. self.assertEqual(list(mi.count_cycle('', 2)), [])
  1141. def test_negative(self):
  1142. self.assertEqual(list(mi.count_cycle('abc', -3)), [])
  1143. class LocateTests(TestCase):
  1144. def test_default_pred(self):
  1145. iterable = [0, 1, 1, 0, 1, 0, 0]
  1146. actual = list(mi.locate(iterable))
  1147. expected = [1, 2, 4]
  1148. self.assertEqual(actual, expected)
  1149. def test_no_matches(self):
  1150. iterable = [0, 0, 0]
  1151. actual = list(mi.locate(iterable))
  1152. expected = []
  1153. self.assertEqual(actual, expected)
  1154. def test_custom_pred(self):
  1155. iterable = ['0', 1, 1, '0', 1, '0', '0']
  1156. pred = lambda x: x == '0'
  1157. actual = list(mi.locate(iterable, pred))
  1158. expected = [0, 3, 5, 6]
  1159. self.assertEqual(actual, expected)
  1160. class StripFunctionTests(TestCase):
  1161. def test_hashable(self):
  1162. iterable = list('www.example.com')
  1163. pred = lambda x: x in set('cmowz.')
  1164. self.assertEqual(list(mi.lstrip(iterable, pred)), list('example.com'))
  1165. self.assertEqual(list(mi.rstrip(iterable, pred)), list('www.example'))
  1166. self.assertEqual(list(mi.strip(iterable, pred)), list('example'))
  1167. def test_not_hashable(self):
  1168. iterable = [
  1169. list('http://'), list('www'), list('.example'), list('.com')
  1170. ]
  1171. pred = lambda x: x in [list('http://'), list('www'), list('.com')]
  1172. self.assertEqual(list(mi.lstrip(iterable, pred)), iterable[2:])
  1173. self.assertEqual(list(mi.rstrip(iterable, pred)), iterable[:3])
  1174. self.assertEqual(list(mi.strip(iterable, pred)), iterable[2: 3])
  1175. def test_math(self):
  1176. iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2]
  1177. pred = lambda x: x <= 2
  1178. self.assertEqual(list(mi.lstrip(iterable, pred)), iterable[3:])
  1179. self.assertEqual(list(mi.rstrip(iterable, pred)), iterable[:-3])
  1180. self.assertEqual(list(mi.strip(iterable, pred)), iterable[3:-3])
  1181. class IsliceExtendedTests(TestCase):
  1182. def test_all(self):
  1183. iterable = ['0', '1', '2', '3', '4', '5']
  1184. indexes = list(range(-4, len(iterable) + 4)) + [None]
  1185. steps = [1, 2, 3, 4, -1, -2, -3, 4]
  1186. for slice_args in product(indexes, indexes, steps):
  1187. try:
  1188. actual = list(mi.islice_extended(iterable, *slice_args))
  1189. except Exception as e:
  1190. self.fail((slice_args, e))
  1191. expected = iterable[slice(*slice_args)]
  1192. self.assertEqual(actual, expected, slice_args)
  1193. def test_zero_step(self):
  1194. with self.assertRaises(ValueError):
  1195. list(mi.islice_extended([1, 2, 3], 0, 1, 0))
  1196. class ConsecutiveGroupsTest(TestCase):
  1197. def test_numbers(self):
  1198. iterable = [-10, -8, -7, -6, 1, 2, 4, 5, -1, 7]
  1199. actual = [list(g) for g in mi.consecutive_groups(iterable)]
  1200. expected = [[-10], [-8, -7, -6], [1, 2], [4, 5], [-1], [7]]
  1201. self.assertEqual(actual, expected)
  1202. def test_custom_ordering(self):
  1203. iterable = ['1', '10', '11', '20', '21', '22', '30', '31']
  1204. ordering = lambda x: int(x)
  1205. actual = [list(g) for g in mi.consecutive_groups(iterable, ordering)]
  1206. expected = [['1'], ['10', '11'], ['20', '21', '22'], ['30', '31']]
  1207. self.assertEqual(actual, expected)
  1208. def test_exotic_ordering(self):
  1209. iterable = [
  1210. ('a', 'b', 'c', 'd'),
  1211. ('a', 'c', 'b', 'd'),
  1212. ('a', 'c', 'd', 'b'),
  1213. ('a', 'd', 'b', 'c'),
  1214. ('d', 'b', 'c', 'a'),
  1215. ('d', 'c', 'a', 'b'),
  1216. ]
  1217. ordering = list(permutations('abcd')).index
  1218. actual = [list(g) for g in mi.consecutive_groups(iterable, ordering)]
  1219. expected = [
  1220. [('a', 'b', 'c', 'd')],
  1221. [('a', 'c', 'b', 'd'), ('a', 'c', 'd', 'b'), ('a', 'd', 'b', 'c')],
  1222. [('d', 'b', 'c', 'a'), ('d', 'c', 'a', 'b')],
  1223. ]
  1224. self.assertEqual(actual, expected)
  1225. class DifferenceTest(TestCase):
  1226. def test_normal(self):
  1227. iterable = [10, 20, 30, 40, 50]
  1228. actual = list(mi.difference(iterable))
  1229. expected = [10, 10, 10, 10, 10]
  1230. self.assertEqual(actual, expected)
  1231. def test_custom(self):
  1232. iterable = [10, 20, 30, 40, 50]
  1233. actual = list(mi.difference(iterable, add))
  1234. expected = [10, 30, 50, 70, 90]
  1235. self.assertEqual(actual, expected)
  1236. def test_roundtrip(self):
  1237. original = list(range(100))
  1238. accumulated = mi.accumulate(original)
  1239. actual = list(mi.difference(accumulated))
  1240. self.assertEqual(actual, original)
  1241. def test_one(self):
  1242. self.assertEqual(list(mi.difference([0])), [0])
  1243. def test_empty(self):
  1244. self.assertEqual(list(mi.difference([])), [])
  1245. class SeekableTest(TestCase):
  1246. def test_exhaustion_reset(self):
  1247. iterable = [str(n) for n in range(10)]
  1248. s = mi.seekable(iterable)
  1249. self.assertEqual(list(s), iterable) # Normal iteration
  1250. self.assertEqual(list(s), []) # Iterable is exhausted
  1251. s.seek(0)
  1252. self.assertEqual(list(s), iterable) # Back in action
  1253. def test_partial_reset(self):
  1254. iterable = [str(n) for n in range(10)]
  1255. s = mi.seekable(iterable)
  1256. self.assertEqual(mi.take(5, s), iterable[:5]) # Normal iteration
  1257. s.seek(1)
  1258. self.assertEqual(list(s), iterable[1:]) # Get the rest of the iterable
  1259. def test_forward(self):
  1260. iterable = [str(n) for n in range(10)]
  1261. s = mi.seekable(iterable)
  1262. self.assertEqual(mi.take(1, s), iterable[:1]) # Normal iteration
  1263. s.seek(3) # Skip over index 2
  1264. self.assertEqual(list(s), iterable[3:]) # Result is similar to slicing
  1265. s.seek(0) # Back to 0
  1266. self.assertEqual(list(s), iterable) # No difference in result
  1267. def test_past_end(self):
  1268. iterable = [str(n) for n in range(10)]
  1269. s = mi.seekable(iterable)
  1270. self.assertEqual(mi.take(1, s), iterable[:1]) # Normal iteration
  1271. s.seek(20)
  1272. self.assertEqual(list(s), []) # Iterable is exhausted
  1273. s.seek(0) # Back to 0
  1274. self.assertEqual(list(s), iterable) # No difference in result
  1275. def test_elements(self):
  1276. iterable = map(str, count())
  1277. s = mi.seekable(iterable)
  1278. mi.take(10, s)
  1279. elements = s.elements()
  1280. self.assertEqual(
  1281. [elements[i] for i in range(10)], [str(n) for n in range(10)]
  1282. )
  1283. self.assertEqual(len(elements), 10)
  1284. mi.take(10, s)
  1285. self.assertEqual(list(elements), [str(n) for n in range(20)])
  1286. class SequenceViewTests(TestCase):
  1287. def test_init(self):
  1288. view = mi.SequenceView((1, 2, 3))
  1289. self.assertEqual(repr(view), "SequenceView((1, 2, 3))")
  1290. self.assertRaises(TypeError, lambda: mi.SequenceView({}))
  1291. def test_update(self):
  1292. seq = [1, 2, 3]
  1293. view = mi.SequenceView(seq)
  1294. self.assertEqual(len(view), 3)
  1295. self.assertEqual(repr(view), "SequenceView([1, 2, 3])")
  1296. seq.pop()
  1297. self.assertEqual(len(view), 2)
  1298. self.assertEqual(repr(view), "SequenceView([1, 2])")
  1299. def test_indexing(self):
  1300. seq = ('a', 'b', 'c', 'd', 'e', 'f')
  1301. view = mi.SequenceView(seq)
  1302. for i in range(-len(seq), len(seq)):
  1303. self.assertEqual(view[i], seq[i])
  1304. def test_slicing(self):
  1305. seq = ('a', 'b', 'c', 'd', 'e', 'f')
  1306. view = mi.SequenceView(seq)
  1307. n = len(seq)
  1308. indexes = list(range(-n - 1, n + 1)) + [None]
  1309. steps = list(range(-n, n + 1))
  1310. steps.remove(0)
  1311. for slice_args in product(indexes, indexes, steps):
  1312. i = slice(*slice_args)
  1313. self.assertEqual(view[i], seq[i])
  1314. def test_abc_methods(self):
  1315. # collections.Sequence should provide all of this functionality
  1316. seq = ('a', 'b', 'c', 'd', 'e', 'f', 'f')
  1317. view = mi.SequenceView(seq)
  1318. # __contains__
  1319. self.assertIn('b', view)
  1320. self.assertNotIn('g', view)
  1321. # __iter__
  1322. self.assertEqual(list(iter(view)), list(seq))
  1323. # __reversed__
  1324. self.assertEqual(list(reversed(view)), list(reversed(seq)))
  1325. # index
  1326. self.assertEqual(view.index('b'), 1)
  1327. # count
  1328. self.assertEqual(seq.count('f'), 2)
  1329. class RunLengthTest(TestCase):
  1330. def test_encode(self):
  1331. iterable = (int(str(n)[0]) for n in count(800))
  1332. actual = mi.take(4, mi.run_length.encode(iterable))
  1333. expected = [(8, 100), (9, 100), (1, 1000), (2, 1000)]
  1334. self.assertEqual(actual, expected)
  1335. def test_decode(self):
  1336. iterable = [('d', 4), ('c', 3), ('b', 2), ('a', 1)]
  1337. actual = ''.join(mi.run_length.decode(iterable))
  1338. expected = 'ddddcccbba'
  1339. self.assertEqual(actual, expected)
  1340. class ExactlyNTests(TestCase):
  1341. """Tests for ``exactly_n()``"""
  1342. def test_true(self):
  1343. """Iterable has ``n`` ``True`` elements"""
  1344. self.assertTrue(mi.exactly_n([True, False, True], 2))
  1345. self.assertTrue(mi.exactly_n([1, 1, 1, 0], 3))
  1346. self.assertTrue(mi.exactly_n([False, False], 0))
  1347. self.assertTrue(mi.exactly_n(range(100), 10, lambda x: x < 10))
  1348. def test_false(self):
  1349. """Iterable does not have ``n`` ``True`` elements"""
  1350. self.assertFalse(mi.exactly_n([True, False, False], 2))
  1351. self.assertFalse(mi.exactly_n([True, True, False], 1))
  1352. self.assertFalse(mi.exactly_n([False], 1))
  1353. self.assertFalse(mi.exactly_n([True], -1))
  1354. self.assertFalse(mi.exactly_n(repeat(True), 100))
  1355. def test_empty(self):
  1356. """Return ``True`` if the iterable is empty and ``n`` is 0"""
  1357. self.assertTrue(mi.exactly_n([], 0))
  1358. self.assertFalse(mi.exactly_n([], 1))
  1359. class AlwaysReversibleTests(TestCase):
  1360. """Tests for ``always_reversible()``"""
  1361. def test_regular_reversed(self):
  1362. self.assertEqual(list(reversed(range(10))),
  1363. list(mi.always_reversible(range(10))))
  1364. self.assertEqual(list(reversed([1, 2, 3])),
  1365. list(mi.always_reversible([1, 2, 3])))
  1366. self.assertEqual(reversed([1, 2, 3]).__class__,
  1367. mi.always_reversible([1, 2, 3]).__class__)
  1368. def test_nonseq_reversed(self):
  1369. # Create a non-reversible generator from a sequence
  1370. with self.assertRaises(TypeError):
  1371. reversed(x for x in range(10))
  1372. self.assertEqual(list(reversed(range(10))),
  1373. list(mi.always_reversible(x for x in range(10))))
  1374. self.assertEqual(list(reversed([1, 2, 3])),
  1375. list(mi.always_reversible(x for x in [1, 2, 3])))
  1376. self.assertNotEqual(reversed((1, 2)).__class__,
  1377. mi.always_reversible(x for x in (1, 2)).__class__)
  1378. class CircularShiftsTests(TestCase):
  1379. def test_empty(self):
  1380. # empty iterable -> empty list
  1381. self.assertEqual(list(mi.circular_shifts([])), [])
  1382. def test_simple_circular_shifts(self):
  1383. # test the a simple iterator case
  1384. self.assertEqual(
  1385. mi.circular_shifts(range(4)),
  1386. [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)]
  1387. )
  1388. def test_duplicates(self):
  1389. # test non-distinct entries
  1390. self.assertEqual(
  1391. mi.circular_shifts([0, 1, 0, 1]),
  1392. [(0, 1, 0, 1), (1, 0, 1, 0), (0, 1, 0, 1), (1, 0, 1, 0)]
  1393. )
  1394. class MakeDecoratorTests(TestCase):
  1395. def test_basic(self):
  1396. slicer = mi.make_decorator(islice)
  1397. @slicer(1, 10, 2)
  1398. def user_function(arg_1, arg_2, kwarg_1=None):
  1399. self.assertEqual(arg_1, 'arg_1')
  1400. self.assertEqual(arg_2, 'arg_2')
  1401. self.assertEqual(kwarg_1, 'kwarg_1')
  1402. return map(str, count())
  1403. it = user_function('arg_1', 'arg_2', kwarg_1='kwarg_1')
  1404. actual = list(it)
  1405. expected = ['1', '3', '5', '7', '9']
  1406. self.assertEqual(actual, expected)
  1407. def test_result_index(self):
  1408. def stringify(*args, **kwargs):
  1409. self.assertEqual(args[0], 'arg_0')
  1410. iterable = args[1]
  1411. self.assertEqual(args[2], 'arg_2')
  1412. self.assertEqual(kwargs['kwarg_1'], 'kwarg_1')
  1413. return map(str, iterable)
  1414. stringifier = mi.make_decorator(stringify, result_index=1)
  1415. @stringifier('arg_0', 'arg_2', kwarg_1='kwarg_1')
  1416. def user_function(n):
  1417. return count(n)
  1418. it = user_function(1)
  1419. actual = mi.take(5, it)
  1420. expected = ['1', '2', '3', '4', '5']
  1421. self.assertEqual(actual, expected)
  1422. def test_wrap_class(self):
  1423. seeker = mi.make_decorator(mi.seekable)
  1424. @seeker()
  1425. def user_function(n):
  1426. return map(str, range(n))
  1427. it = user_function(5)
  1428. self.assertEqual(list(it), ['0', '1', '2', '3', '4'])
  1429. it.seek(0)
  1430. self.assertEqual(list(it), ['0', '1', '2', '3', '4'])