test_more.py 194 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868
  1. import cmath
  2. import warnings
  3. from collections import Counter, abc
  4. from collections.abc import Set
  5. from datetime import datetime, timedelta
  6. from decimal import Decimal
  7. from doctest import DocTestSuite
  8. from fractions import Fraction
  9. from functools import partial, reduce
  10. from io import StringIO
  11. from itertools import (
  12. accumulate,
  13. chain,
  14. combinations,
  15. combinations_with_replacement,
  16. count,
  17. cycle,
  18. groupby,
  19. islice,
  20. permutations,
  21. product,
  22. repeat,
  23. )
  24. from operator import add, mul, itemgetter
  25. from pickle import loads, dumps
  26. from random import Random, random, randrange, seed
  27. from statistics import mean
  28. from string import ascii_letters
  29. from sys import version_info
  30. from time import sleep
  31. from traceback import format_exc
  32. from unittest import skipIf, TestCase
  33. import more_itertools as mi
  34. def load_tests(loader, tests, ignore):
  35. # Add the doctests
  36. tests.addTests(DocTestSuite('more_itertools.more'))
  37. return tests
  38. class ChunkedTests(TestCase):
  39. """Tests for ``chunked()``"""
  40. def test_even(self):
  41. """Test when ``n`` divides evenly into the length of the iterable."""
  42. self.assertEqual(
  43. list(mi.chunked('ABCDEF', 3)), [['A', 'B', 'C'], ['D', 'E', 'F']]
  44. )
  45. def test_odd(self):
  46. """Test when ``n`` does not divide evenly into the length of the
  47. iterable.
  48. """
  49. self.assertEqual(
  50. list(mi.chunked('ABCDE', 3)), [['A', 'B', 'C'], ['D', 'E']]
  51. )
  52. def test_none(self):
  53. """Test when ``n`` has the value ``None``."""
  54. self.assertEqual(
  55. list(mi.chunked('ABCDE', None)), [['A', 'B', 'C', 'D', 'E']]
  56. )
  57. def test_strict_false(self):
  58. """Test when ``n`` does not divide evenly into the length of the
  59. iterable and strict is false.
  60. """
  61. self.assertEqual(
  62. list(mi.chunked('ABCDE', 3, strict=False)),
  63. [['A', 'B', 'C'], ['D', 'E']],
  64. )
  65. def test_strict_being_true(self):
  66. """Test when ``n`` does not divide evenly into the length of the
  67. iterable and strict is True (raising an exception).
  68. """
  69. def f():
  70. return list(mi.chunked('ABCDE', 3, strict=True))
  71. self.assertRaisesRegex(ValueError, "iterable is not divisible by n", f)
  72. self.assertEqual(
  73. list(mi.chunked('ABCDEF', 3, strict=True)),
  74. [['A', 'B', 'C'], ['D', 'E', 'F']],
  75. )
  76. def test_strict_being_true_with_size_none(self):
  77. """Test when ``n`` has value ``None`` and the keyword strict is True
  78. (raising an exception).
  79. """
  80. def f():
  81. return list(mi.chunked('ABCDE', None, strict=True))
  82. self.assertRaisesRegex(
  83. ValueError, "n must not be None when using strict mode.", f
  84. )
  85. class FirstTests(TestCase):
  86. def test_many(self):
  87. # Also try it on a generator expression to make sure it works on
  88. # whatever those return, across Python versions.
  89. self.assertEqual(mi.first(x for x in range(4)), 0)
  90. def test_one(self):
  91. self.assertEqual(mi.first([3]), 3)
  92. def test_empty(self):
  93. with self.assertRaises(ValueError):
  94. mi.first([])
  95. def test_default(self):
  96. self.assertEqual(mi.first([], 'boo'), 'boo')
  97. class IterOnlyRange:
  98. """User-defined iterable class which only support __iter__.
  99. >>> r = IterOnlyRange(5)
  100. >>> r[0] # doctest: +SKIP
  101. AttributeError: IterOnlyRange instance has no attribute '__getitem__'
  102. Note: In Python 3, ``TypeError`` will be raised because ``object`` is
  103. inherited implicitly by default.
  104. >>> r[0] # doctest: +SKIP
  105. TypeError: 'IterOnlyRange' object does not support indexing
  106. """
  107. def __init__(self, n):
  108. """Set the length of the range."""
  109. self.n = n
  110. def __iter__(self):
  111. """Works same as range()."""
  112. return iter(range(self.n))
  113. class LastTests(TestCase):
  114. def test_basic(self):
  115. cases = [
  116. (range(4), 3),
  117. (iter(range(4)), 3),
  118. (range(1), 0),
  119. (iter(range(1)), 0),
  120. (IterOnlyRange(5), 4),
  121. ({n: str(n) for n in range(5)}, 4),
  122. ({0: '0', -1: '-1', 2: '-2'}, 2),
  123. ]
  124. for iterable, expected in cases:
  125. with self.subTest(iterable=iterable):
  126. self.assertEqual(mi.last(iterable), expected)
  127. def test_default(self):
  128. for iterable, default, expected in [
  129. (range(1), None, 0),
  130. ([], None, None),
  131. ({}, None, None),
  132. (iter([]), None, None),
  133. ]:
  134. with self.subTest(args=(iterable, default)):
  135. self.assertEqual(mi.last(iterable, default=default), expected)
  136. def test_empty(self):
  137. for iterable in ([], iter(range(0))):
  138. with self.subTest(iterable=iterable):
  139. with self.assertRaises(ValueError):
  140. mi.last(iterable)
  141. class NthOrLastTests(TestCase):
  142. """Tests for ``nth_or_last()``"""
  143. def test_basic(self):
  144. self.assertEqual(mi.nth_or_last(range(3), 1), 1)
  145. self.assertEqual(mi.nth_or_last(range(3), 3), 2)
  146. def test_default_value(self):
  147. default = 42
  148. self.assertEqual(mi.nth_or_last(range(0), 3, default), default)
  149. def test_empty_iterable_no_default(self):
  150. self.assertRaises(ValueError, lambda: mi.nth_or_last(range(0), 0))
  151. class PeekableMixinTests:
  152. """Common tests for ``peekable()`` and ``seekable()`` behavior"""
  153. cls = None
  154. def test_passthrough(self):
  155. """Iterating a peekable without using ``peek()`` or ``prepend()``
  156. should just give the underlying iterable's elements (a trivial test but
  157. useful to set a baseline in case something goes wrong)"""
  158. expected = [1, 2, 3, 4, 5]
  159. actual = list(self.cls(expected))
  160. self.assertEqual(actual, expected)
  161. def test_peek_default(self):
  162. """Make sure passing a default into ``peek()`` works."""
  163. p = self.cls([])
  164. self.assertEqual(p.peek(7), 7)
  165. def test_truthiness(self):
  166. """Make sure a ``peekable`` tests true iff there are items remaining in
  167. the iterable.
  168. """
  169. p = self.cls([])
  170. self.assertFalse(p)
  171. p = self.cls(range(3))
  172. self.assertTrue(p)
  173. def test_simple_peeking(self):
  174. """Make sure ``next`` and ``peek`` advance and don't advance the
  175. iterator, respectively.
  176. """
  177. p = self.cls(range(10))
  178. self.assertEqual(next(p), 0)
  179. self.assertEqual(p.peek(), 1)
  180. self.assertEqual(p.peek(), 1)
  181. self.assertEqual(next(p), 1)
  182. class PeekableTests(PeekableMixinTests, TestCase):
  183. cls = mi.peekable
  184. def test_indexing(self):
  185. """
  186. Indexing into the peekable shouldn't advance the iterator.
  187. """
  188. p = mi.peekable('abcdefghijkl')
  189. # The 0th index is what ``next()`` will return
  190. self.assertEqual(p[0], 'a')
  191. self.assertEqual(next(p), 'a')
  192. # Indexing further into the peekable shouldn't advance the iterator
  193. self.assertEqual(p[2], 'd')
  194. self.assertEqual(next(p), 'b')
  195. # The 0th index moves up with the iterator; the last index follows
  196. self.assertEqual(p[0], 'c')
  197. self.assertEqual(p[9], 'l')
  198. self.assertEqual(next(p), 'c')
  199. self.assertEqual(p[8], 'l')
  200. # Negative indexing should work too
  201. self.assertEqual(p[-2], 'k')
  202. self.assertEqual(p[-9], 'd')
  203. self.assertRaises(IndexError, lambda: p[-10])
  204. def test_slicing(self):
  205. """Slicing the peekable shouldn't advance the iterator."""
  206. seq = list('abcdefghijkl')
  207. p = mi.peekable(seq)
  208. # Slicing the peekable should just be like slicing a re-iterable
  209. self.assertEqual(p[1:4], seq[1:4])
  210. # Advancing the iterator moves the slices up also
  211. self.assertEqual(next(p), 'a')
  212. self.assertEqual(p[1:4], seq[1:][1:4])
  213. # Implicit starts and stop should work
  214. self.assertEqual(p[:5], seq[1:][:5])
  215. self.assertEqual(p[:], seq[1:][:])
  216. # Indexing past the end should work
  217. self.assertEqual(p[:100], seq[1:][:100])
  218. # Steps should work, including negative
  219. self.assertEqual(p[::2], seq[1:][::2])
  220. self.assertEqual(p[::-1], seq[1:][::-1])
  221. def test_slicing_reset(self):
  222. """Test slicing on a fresh iterable each time"""
  223. iterable = ['0', '1', '2', '3', '4', '5']
  224. indexes = list(range(-4, len(iterable) + 4)) + [None]
  225. steps = [1, 2, 3, 4, -1, -2, -3, 4]
  226. for slice_args in product(indexes, indexes, steps):
  227. it = iter(iterable)
  228. p = mi.peekable(it)
  229. next(p)
  230. index = slice(*slice_args)
  231. actual = p[index]
  232. expected = iterable[1:][index]
  233. self.assertEqual(actual, expected, slice_args)
  234. def test_slicing_error(self):
  235. iterable = '01234567'
  236. p = mi.peekable(iter(iterable))
  237. # Prime the cache
  238. p.peek()
  239. old_cache = list(p._cache)
  240. # Illegal slice
  241. with self.assertRaises(ValueError):
  242. p[1:-1:0]
  243. # Neither the cache nor the iteration should be affected
  244. self.assertEqual(old_cache, list(p._cache))
  245. self.assertEqual(list(p), list(iterable))
  246. # prepend() behavior tests
  247. def test_prepend(self):
  248. """Tests interspersed ``prepend()`` and ``next()`` calls"""
  249. it = mi.peekable(range(2))
  250. actual = []
  251. # Test prepend() before next()
  252. it.prepend(10)
  253. actual += [next(it), next(it)]
  254. # Test prepend() between next()s
  255. it.prepend(11)
  256. actual += [next(it), next(it)]
  257. # Test prepend() after source iterable is consumed
  258. it.prepend(12)
  259. actual += [next(it)]
  260. expected = [10, 0, 11, 1, 12]
  261. self.assertEqual(actual, expected)
  262. def test_multi_prepend(self):
  263. """Tests prepending multiple items and getting them in proper order"""
  264. it = mi.peekable(range(5))
  265. actual = [next(it), next(it)]
  266. it.prepend(10, 11, 12)
  267. it.prepend(20, 21)
  268. actual += list(it)
  269. expected = [0, 1, 20, 21, 10, 11, 12, 2, 3, 4]
  270. self.assertEqual(actual, expected)
  271. def test_empty(self):
  272. """Tests prepending in front of an empty iterable"""
  273. it = mi.peekable([])
  274. it.prepend(10)
  275. actual = list(it)
  276. expected = [10]
  277. self.assertEqual(actual, expected)
  278. def test_prepend_truthiness(self):
  279. """Tests that ``__bool__()`` or ``__nonzero__()`` works properly
  280. with ``prepend()``"""
  281. it = mi.peekable(range(5))
  282. self.assertTrue(it)
  283. actual = list(it)
  284. self.assertFalse(it)
  285. it.prepend(10)
  286. self.assertTrue(it)
  287. actual += [next(it)]
  288. self.assertFalse(it)
  289. expected = [0, 1, 2, 3, 4, 10]
  290. self.assertEqual(actual, expected)
  291. def test_multi_prepend_peek(self):
  292. """Tests prepending multiple elements and getting them in reverse order
  293. while peeking"""
  294. it = mi.peekable(range(5))
  295. actual = [next(it), next(it)]
  296. self.assertEqual(it.peek(), 2)
  297. it.prepend(10, 11, 12)
  298. self.assertEqual(it.peek(), 10)
  299. it.prepend(20, 21)
  300. self.assertEqual(it.peek(), 20)
  301. actual += list(it)
  302. self.assertFalse(it)
  303. expected = [0, 1, 20, 21, 10, 11, 12, 2, 3, 4]
  304. self.assertEqual(actual, expected)
  305. def test_prepend_after_stop(self):
  306. """Test resuming iteration after a previous exhaustion"""
  307. it = mi.peekable(range(3))
  308. self.assertEqual(list(it), [0, 1, 2])
  309. self.assertRaises(StopIteration, lambda: next(it))
  310. it.prepend(10)
  311. self.assertEqual(next(it), 10)
  312. self.assertRaises(StopIteration, lambda: next(it))
  313. def test_prepend_slicing(self):
  314. """Tests interaction between prepending and slicing"""
  315. seq = list(range(20))
  316. p = mi.peekable(seq)
  317. p.prepend(30, 40, 50)
  318. pseq = [30, 40, 50] + seq # pseq for prepended_seq
  319. # adapt the specific tests from test_slicing
  320. self.assertEqual(p[0], 30)
  321. self.assertEqual(p[1:8], pseq[1:8])
  322. self.assertEqual(p[1:], pseq[1:])
  323. self.assertEqual(p[:5], pseq[:5])
  324. self.assertEqual(p[:], pseq[:])
  325. self.assertEqual(p[:100], pseq[:100])
  326. self.assertEqual(p[::2], pseq[::2])
  327. self.assertEqual(p[::-1], pseq[::-1])
  328. def test_prepend_indexing(self):
  329. """Tests interaction between prepending and indexing"""
  330. seq = list(range(20))
  331. p = mi.peekable(seq)
  332. p.prepend(30, 40, 50)
  333. self.assertEqual(p[0], 30)
  334. self.assertEqual(next(p), 30)
  335. self.assertEqual(p[2], 0)
  336. self.assertEqual(next(p), 40)
  337. self.assertEqual(p[0], 50)
  338. self.assertEqual(p[9], 8)
  339. self.assertEqual(next(p), 50)
  340. self.assertEqual(p[8], 8)
  341. self.assertEqual(p[-2], 18)
  342. self.assertEqual(p[-9], 11)
  343. self.assertRaises(IndexError, lambda: p[-21])
  344. def test_prepend_iterable(self):
  345. """Tests prepending from an iterable"""
  346. it = mi.peekable(range(5))
  347. # Don't directly use the range() object to avoid any range-specific
  348. # optimizations
  349. it.prepend(*(x for x in range(5)))
  350. actual = list(it)
  351. expected = list(chain(range(5), range(5)))
  352. self.assertEqual(actual, expected)
  353. def test_prepend_many(self):
  354. """Tests that prepending a huge number of elements works"""
  355. it = mi.peekable(range(5))
  356. # Don't directly use the range() object to avoid any range-specific
  357. # optimizations
  358. it.prepend(*(x for x in range(20000)))
  359. actual = list(it)
  360. expected = list(chain(range(20000), range(5)))
  361. self.assertEqual(actual, expected)
  362. def test_prepend_reversed(self):
  363. """Tests prepending from a reversed iterable"""
  364. it = mi.peekable(range(3))
  365. it.prepend(*reversed((10, 11, 12)))
  366. actual = list(it)
  367. expected = [12, 11, 10, 0, 1, 2]
  368. self.assertEqual(actual, expected)
  369. class ConsumerTests(TestCase):
  370. """Tests for ``consumer()``"""
  371. def test_consumer(self):
  372. @mi.consumer
  373. def eater():
  374. while True:
  375. x = yield # noqa
  376. e = eater()
  377. e.send('hi') # without @consumer, would raise TypeError
  378. class DistinctPermutationsTests(TestCase):
  379. def test_distinct_permutations(self):
  380. """Make sure the output for ``distinct_permutations()`` is the same as
  381. set(permutations(it)).
  382. """
  383. iterable = ['z', 'a', 'a', 'q', 'q', 'q', 'y']
  384. test_output = sorted(mi.distinct_permutations(iterable))
  385. ref_output = sorted(set(permutations(iterable)))
  386. self.assertEqual(test_output, ref_output)
  387. def test_other_iterables(self):
  388. """Make sure ``distinct_permutations()`` accepts a different type of
  389. iterables.
  390. """
  391. # a generator
  392. iterable = (c for c in ['z', 'a', 'a', 'q', 'q', 'q', 'y'])
  393. test_output = sorted(mi.distinct_permutations(iterable))
  394. # "reload" it
  395. iterable = (c for c in ['z', 'a', 'a', 'q', 'q', 'q', 'y'])
  396. ref_output = sorted(set(permutations(iterable)))
  397. self.assertEqual(test_output, ref_output)
  398. # an iterator
  399. iterable = iter(['z', 'a', 'a', 'q', 'q', 'q', 'y'])
  400. test_output = sorted(mi.distinct_permutations(iterable))
  401. # "reload" it
  402. iterable = iter(['z', 'a', 'a', 'q', 'q', 'q', 'y'])
  403. ref_output = sorted(set(permutations(iterable)))
  404. self.assertEqual(test_output, ref_output)
  405. def test_r(self):
  406. for iterable, r in (
  407. ('mississippi', 0),
  408. ('mississippi', 1),
  409. ('mississippi', 6),
  410. ('mississippi', 7),
  411. ('mississippi', 12),
  412. ([0, 1, 1, 0], 0),
  413. ([0, 1, 1, 0], 1),
  414. ([0, 1, 1, 0], 2),
  415. ([0, 1, 1, 0], 3),
  416. ([0, 1, 1, 0], 4),
  417. (['a'], 0),
  418. (['a'], 1),
  419. (['a'], 5),
  420. ([], 0),
  421. ([], 1),
  422. ([], 4),
  423. ):
  424. with self.subTest(iterable=iterable, r=r):
  425. expected = sorted(set(permutations(iterable, r)))
  426. actual = sorted(mi.distinct_permutations(iter(iterable), r))
  427. self.assertEqual(actual, expected)
  428. class IlenTests(TestCase):
  429. def test_ilen(self):
  430. """Sanity-checks for ``ilen()``."""
  431. # Non-empty
  432. self.assertEqual(
  433. mi.ilen(filter(lambda x: x % 10 == 0, range(101))), 11
  434. )
  435. # Empty
  436. self.assertEqual(mi.ilen(x for x in range(0)), 0)
  437. # Iterable with __len__
  438. self.assertEqual(mi.ilen(list(range(6))), 6)
  439. class MinMaxTests(TestCase):
  440. def test_basic(self):
  441. for iterable, expected in (
  442. # easy case
  443. ([0, 1, 2, 3], (0, 3)),
  444. # min and max are not in the extremes + we have `int`s and `float`s
  445. ([3, 5.5, -1, 2], (-1, 5.5)),
  446. # unordered collection
  447. ({3, 5.5, -1, 2}, (-1, 5.5)),
  448. # with repetitions
  449. ([3, 5.5, float('-Inf'), 5.5], (float('-Inf'), 5.5)),
  450. # other collections
  451. ('banana', ('a', 'n')),
  452. ({0: 1, 2: 100, 1: 10}, (0, 2)),
  453. (range(3, 14), (3, 13)),
  454. ):
  455. with self.subTest(iterable=iterable, expected=expected):
  456. # check for expected results
  457. self.assertTupleEqual(mi.minmax(iterable), expected)
  458. # check for equality with built-in `min` and `max`
  459. self.assertTupleEqual(
  460. mi.minmax(iterable), (min(iterable), max(iterable))
  461. )
  462. def test_unpacked(self):
  463. self.assertTupleEqual(mi.minmax(2, 3, 1), (1, 3))
  464. self.assertTupleEqual(mi.minmax(12, 3, 4, key=str), (12, 4))
  465. def test_iterables(self):
  466. self.assertTupleEqual(mi.minmax(x for x in [0, 1, 2, 3]), (0, 3))
  467. self.assertTupleEqual(
  468. mi.minmax(map(str, [3, 5.5, 'a', 2])), ('2', 'a')
  469. )
  470. self.assertTupleEqual(
  471. mi.minmax(filter(None, [0, 3, '', None, 10])), (3, 10)
  472. )
  473. def test_key(self):
  474. self.assertTupleEqual(
  475. mi.minmax({(), (1, 4, 2), 'abcde', range(4)}, key=len),
  476. ((), 'abcde'),
  477. )
  478. self.assertTupleEqual(
  479. mi.minmax((x for x in [10, 3, 25]), key=str), (10, 3)
  480. )
  481. def test_default(self):
  482. with self.assertRaises(ValueError):
  483. mi.minmax([])
  484. self.assertIs(mi.minmax([], default=None), None)
  485. self.assertListEqual(mi.minmax([], default=[1, 'a']), [1, 'a'])
  486. class WithIterTests(TestCase):
  487. def test_with_iter(self):
  488. s = StringIO('One fish\nTwo fish')
  489. initial_words = [line.split()[0] for line in mi.with_iter(s)]
  490. # Iterable's items should be faithfully represented
  491. self.assertEqual(initial_words, ['One', 'Two'])
  492. # The file object should be closed
  493. self.assertTrue(s.closed)
  494. class OneTests(TestCase):
  495. def test_basic(self):
  496. it = iter(['item'])
  497. self.assertEqual(mi.one(it), 'item')
  498. def test_too_short(self):
  499. it = iter([])
  500. for too_short, exc_type in [
  501. (None, ValueError),
  502. (IndexError, IndexError),
  503. ]:
  504. with self.subTest(too_short=too_short):
  505. try:
  506. mi.one(it, too_short=too_short)
  507. except exc_type:
  508. formatted_exc = format_exc()
  509. self.assertIn('StopIteration', formatted_exc)
  510. self.assertIn(
  511. 'The above exception was the direct cause',
  512. formatted_exc,
  513. )
  514. else:
  515. self.fail()
  516. def test_too_long(self):
  517. it = count()
  518. self.assertRaises(ValueError, lambda: mi.one(it)) # burn 0 and 1
  519. self.assertEqual(next(it), 2)
  520. self.assertRaises(
  521. OverflowError, lambda: mi.one(it, too_long=OverflowError)
  522. )
  523. def test_too_long_default_message(self):
  524. it = count()
  525. self.assertRaisesRegex(
  526. ValueError,
  527. "Expected exactly one item in "
  528. "iterable, but got 0, 1, and "
  529. "perhaps more.",
  530. lambda: mi.one(it),
  531. )
  532. class IntersperseTest(TestCase):
  533. """Tests for intersperse()"""
  534. def test_even(self):
  535. iterable = (x for x in '01')
  536. self.assertEqual(
  537. list(mi.intersperse(None, iterable)), ['0', None, '1']
  538. )
  539. def test_odd(self):
  540. iterable = (x for x in '012')
  541. self.assertEqual(
  542. list(mi.intersperse(None, iterable)), ['0', None, '1', None, '2']
  543. )
  544. def test_nested(self):
  545. element = ('a', 'b')
  546. iterable = (x for x in '012')
  547. actual = list(mi.intersperse(element, iterable))
  548. expected = ['0', ('a', 'b'), '1', ('a', 'b'), '2']
  549. self.assertEqual(actual, expected)
  550. def test_not_iterable(self):
  551. self.assertRaises(TypeError, lambda: mi.intersperse('x', 1))
  552. def test_n(self):
  553. for n, element, expected in [
  554. (1, '_', ['0', '_', '1', '_', '2', '_', '3', '_', '4', '_', '5']),
  555. (2, '_', ['0', '1', '_', '2', '3', '_', '4', '5']),
  556. (3, '_', ['0', '1', '2', '_', '3', '4', '5']),
  557. (4, '_', ['0', '1', '2', '3', '_', '4', '5']),
  558. (5, '_', ['0', '1', '2', '3', '4', '_', '5']),
  559. (6, '_', ['0', '1', '2', '3', '4', '5']),
  560. (7, '_', ['0', '1', '2', '3', '4', '5']),
  561. (3, ['a', 'b'], ['0', '1', '2', ['a', 'b'], '3', '4', '5']),
  562. ]:
  563. iterable = (x for x in '012345')
  564. actual = list(mi.intersperse(element, iterable, n=n))
  565. self.assertEqual(actual, expected)
  566. def test_n_zero(self):
  567. self.assertRaises(
  568. ValueError, lambda: list(mi.intersperse('x', '012', n=0))
  569. )
  570. class UniqueToEachTests(TestCase):
  571. """Tests for ``unique_to_each()``"""
  572. def test_all_unique(self):
  573. """When all the input iterables are unique the output should match
  574. the input."""
  575. iterables = [[1, 2], [3, 4, 5], [6, 7, 8]]
  576. self.assertEqual(mi.unique_to_each(*iterables), iterables)
  577. def test_duplicates(self):
  578. """When there are duplicates in any of the input iterables that aren't
  579. in the rest, those duplicates should be emitted."""
  580. iterables = ["mississippi", "missouri"]
  581. self.assertEqual(
  582. mi.unique_to_each(*iterables), [['p', 'p'], ['o', 'u', 'r']]
  583. )
  584. def test_mixed(self):
  585. """When the input iterables contain different types the function should
  586. still behave properly"""
  587. iterables = ['x', (i for i in range(3)), [1, 2, 3], tuple()]
  588. self.assertEqual(mi.unique_to_each(*iterables), [['x'], [0], [3], []])
  589. class WindowedTests(TestCase):
  590. def test_basic(self):
  591. iterable = [1, 2, 3, 4, 5]
  592. for n, expected in (
  593. (6, [(1, 2, 3, 4, 5, None)]),
  594. (5, [(1, 2, 3, 4, 5)]),
  595. (4, [(1, 2, 3, 4), (2, 3, 4, 5)]),
  596. (3, [(1, 2, 3), (2, 3, 4), (3, 4, 5)]),
  597. (2, [(1, 2), (2, 3), (3, 4), (4, 5)]),
  598. (1, [(1,), (2,), (3,), (4,), (5,)]),
  599. (0, [()]),
  600. ):
  601. with self.subTest(n=n):
  602. actual = list(mi.windowed(iterable, n))
  603. self.assertEqual(actual, expected)
  604. def test_fillvalue(self):
  605. actual = list(mi.windowed([1, 2, 3, 4, 5], 6, fillvalue='!'))
  606. expected = [(1, 2, 3, 4, 5, '!')]
  607. self.assertEqual(actual, expected)
  608. def test_step(self):
  609. iterable = [1, 2, 3, 4, 5, 6, 7]
  610. for n, step, expected in [
  611. (3, 2, [(1, 2, 3), (3, 4, 5), (5, 6, 7)]), # n > step
  612. (3, 3, [(1, 2, 3), (4, 5, 6), (7, None, None)]), # n == step
  613. (3, 4, [(1, 2, 3), (5, 6, 7)]), # lines up nicely
  614. (3, 5, [(1, 2, 3), (6, 7, None)]), # off by one
  615. (3, 6, [(1, 2, 3), (7, None, None)]), # off by two
  616. (3, 7, [(1, 2, 3)]), # step past the end
  617. (7, 8, [(1, 2, 3, 4, 5, 6, 7)]), # step > len(iterable)
  618. ]:
  619. with self.subTest(n=n, step=step):
  620. actual = list(mi.windowed(iterable, n, step=step))
  621. self.assertEqual(actual, expected)
  622. def test_invalid_step(self):
  623. # Step must be greater than or equal to 1
  624. with self.assertRaises(ValueError):
  625. list(mi.windowed([1, 2, 3, 4, 5], 3, step=0))
  626. def test_fillvalue_step(self):
  627. actual = list(mi.windowed([1, 2, 3, 4, 5], 3, fillvalue='!', step=3))
  628. expected = [(1, 2, 3), (4, 5, '!')]
  629. self.assertEqual(actual, expected)
  630. def test_negative(self):
  631. with self.assertRaises(ValueError):
  632. list(mi.windowed([1, 2, 3, 4, 5], -1))
  633. def test_empty_seq(self):
  634. actual = list(mi.windowed([], 3))
  635. expected = []
  636. self.assertEqual(actual, expected)
  637. class SubstringsTests(TestCase):
  638. def test_basic(self):
  639. iterable = (x for x in range(4))
  640. actual = list(mi.substrings(iterable))
  641. expected = [
  642. (0,),
  643. (1,),
  644. (2,),
  645. (3,),
  646. (0, 1),
  647. (1, 2),
  648. (2, 3),
  649. (0, 1, 2),
  650. (1, 2, 3),
  651. (0, 1, 2, 3),
  652. ]
  653. self.assertEqual(actual, expected)
  654. def test_strings(self):
  655. iterable = 'abc'
  656. actual = list(mi.substrings(iterable))
  657. expected = [
  658. ('a',),
  659. ('b',),
  660. ('c',),
  661. ('a', 'b'),
  662. ('b', 'c'),
  663. ('a', 'b', 'c'),
  664. ]
  665. self.assertEqual(actual, expected)
  666. def test_empty(self):
  667. iterable = iter([])
  668. actual = list(mi.substrings(iterable))
  669. expected = []
  670. self.assertEqual(actual, expected)
  671. def test_order(self):
  672. iterable = [2, 0, 1]
  673. actual = list(mi.substrings(iterable))
  674. expected = [(2,), (0,), (1,), (2, 0), (0, 1), (2, 0, 1)]
  675. self.assertEqual(actual, expected)
  676. class SubstringsIndexesTests(TestCase):
  677. def test_basic(self):
  678. sequence = [x for x in range(4)]
  679. actual = list(mi.substrings_indexes(sequence))
  680. expected = [
  681. ([0], 0, 1),
  682. ([1], 1, 2),
  683. ([2], 2, 3),
  684. ([3], 3, 4),
  685. ([0, 1], 0, 2),
  686. ([1, 2], 1, 3),
  687. ([2, 3], 2, 4),
  688. ([0, 1, 2], 0, 3),
  689. ([1, 2, 3], 1, 4),
  690. ([0, 1, 2, 3], 0, 4),
  691. ]
  692. self.assertEqual(actual, expected)
  693. def test_strings(self):
  694. sequence = 'abc'
  695. actual = list(mi.substrings_indexes(sequence))
  696. expected = [
  697. ('a', 0, 1),
  698. ('b', 1, 2),
  699. ('c', 2, 3),
  700. ('ab', 0, 2),
  701. ('bc', 1, 3),
  702. ('abc', 0, 3),
  703. ]
  704. self.assertEqual(actual, expected)
  705. def test_empty(self):
  706. sequence = []
  707. actual = list(mi.substrings_indexes(sequence))
  708. expected = []
  709. self.assertEqual(actual, expected)
  710. def test_order(self):
  711. sequence = [2, 0, 1]
  712. actual = list(mi.substrings_indexes(sequence))
  713. expected = [
  714. ([2], 0, 1),
  715. ([0], 1, 2),
  716. ([1], 2, 3),
  717. ([2, 0], 0, 2),
  718. ([0, 1], 1, 3),
  719. ([2, 0, 1], 0, 3),
  720. ]
  721. self.assertEqual(actual, expected)
  722. def test_reverse(self):
  723. sequence = [2, 0, 1]
  724. actual = list(mi.substrings_indexes(sequence, reverse=True))
  725. expected = [
  726. ([2, 0, 1], 0, 3),
  727. ([2, 0], 0, 2),
  728. ([0, 1], 1, 3),
  729. ([2], 0, 1),
  730. ([0], 1, 2),
  731. ([1], 2, 3),
  732. ]
  733. self.assertEqual(actual, expected)
  734. class BucketTests(TestCase):
  735. def test_basic(self):
  736. iterable = [10, 20, 30, 11, 21, 31, 12, 22, 23, 33]
  737. D = mi.bucket(iterable, key=lambda x: 10 * (x // 10))
  738. # In-order access
  739. self.assertEqual(list(D[10]), [10, 11, 12])
  740. # Out of order access
  741. self.assertEqual(list(D[30]), [30, 31, 33])
  742. self.assertEqual(list(D[20]), [20, 21, 22, 23])
  743. self.assertEqual(list(D[40]), []) # Nothing in here!
  744. def test_in(self):
  745. iterable = [10, 20, 30, 11, 21, 31, 12, 22, 23, 33]
  746. D = mi.bucket(iterable, key=lambda x: 10 * (x // 10))
  747. self.assertIn(10, D)
  748. self.assertNotIn(40, D)
  749. self.assertIn(20, D)
  750. self.assertNotIn(21, D)
  751. # Checking in-ness shouldn't advance the iterator
  752. self.assertEqual(next(D[10]), 10)
  753. def test_validator(self):
  754. iterable = count(0)
  755. key = lambda x: int(str(x)[0]) # First digit of each number
  756. validator = lambda x: 0 < x < 10 # No leading zeros
  757. D = mi.bucket(iterable, key, validator=validator)
  758. self.assertEqual(mi.take(3, D[1]), [1, 10, 11])
  759. self.assertNotIn(0, D) # Non-valid entries don't return True
  760. self.assertNotIn(0, D._cache) # Don't store non-valid entries
  761. self.assertEqual(list(D[0]), [])
  762. def test_list(self):
  763. iterable = [10, 20, 30, 11, 21, 31, 12, 22, 23, 33]
  764. D = mi.bucket(iterable, key=lambda x: 10 * (x // 10))
  765. self.assertEqual(list(D[10]), [10, 11, 12])
  766. self.assertEqual(list(D[20]), [20, 21, 22, 23])
  767. self.assertEqual(list(D[30]), [30, 31, 33])
  768. self.assertEqual(set(D), {10, 20, 30})
  769. def test_list_validator(self):
  770. iterable = [10, 20, 30, 11, 21, 31, 12, 22, 23, 33]
  771. key = lambda x: 10 * (x // 10)
  772. validator = lambda x: x != 20
  773. D = mi.bucket(iterable, key, validator=validator)
  774. self.assertEqual(set(D), {10, 30})
  775. self.assertEqual(list(D[10]), [10, 11, 12])
  776. self.assertEqual(list(D[20]), [])
  777. self.assertEqual(list(D[30]), [30, 31, 33])
  778. class SpyTests(TestCase):
  779. """Tests for ``spy()``"""
  780. def test_basic(self):
  781. original_iterable = iter('abcdefg')
  782. head, new_iterable = mi.spy(original_iterable)
  783. self.assertEqual(head, ['a'])
  784. self.assertEqual(
  785. list(new_iterable), ['a', 'b', 'c', 'd', 'e', 'f', 'g']
  786. )
  787. def test_unpacking(self):
  788. original_iterable = iter('abcdefg')
  789. (first, second, third), new_iterable = mi.spy(original_iterable, 3)
  790. self.assertEqual(first, 'a')
  791. self.assertEqual(second, 'b')
  792. self.assertEqual(third, 'c')
  793. self.assertEqual(
  794. list(new_iterable), ['a', 'b', 'c', 'd', 'e', 'f', 'g']
  795. )
  796. def test_too_many(self):
  797. original_iterable = iter('abc')
  798. head, new_iterable = mi.spy(original_iterable, 4)
  799. self.assertEqual(head, ['a', 'b', 'c'])
  800. self.assertEqual(list(new_iterable), ['a', 'b', 'c'])
  801. def test_zero(self):
  802. original_iterable = iter('abc')
  803. head, new_iterable = mi.spy(original_iterable, 0)
  804. self.assertEqual(head, [])
  805. self.assertEqual(list(new_iterable), ['a', 'b', 'c'])
  806. def test_immutable(self):
  807. original_iterable = iter('abcdefg')
  808. head, new_iterable = mi.spy(original_iterable, 3)
  809. head[0] = 'A'
  810. self.assertEqual(head, ['A', 'b', 'c'])
  811. self.assertEqual(
  812. list(new_iterable), ['a', 'b', 'c', 'd', 'e', 'f', 'g']
  813. )
  814. class InterleaveTests(TestCase):
  815. def test_even(self):
  816. actual = list(mi.interleave([1, 4, 7], [2, 5, 8], [3, 6, 9]))
  817. expected = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  818. self.assertEqual(actual, expected)
  819. def test_short(self):
  820. actual = list(mi.interleave([1, 4], [2, 5, 7], [3, 6, 8]))
  821. expected = [1, 2, 3, 4, 5, 6]
  822. self.assertEqual(actual, expected)
  823. def test_mixed_types(self):
  824. it_list = ['a', 'b', 'c', 'd']
  825. it_str = '12345'
  826. it_inf = count()
  827. actual = list(mi.interleave(it_list, it_str, it_inf))
  828. expected = ['a', '1', 0, 'b', '2', 1, 'c', '3', 2, 'd', '4', 3]
  829. self.assertEqual(actual, expected)
  830. class InterleaveLongestTests(TestCase):
  831. def test_even(self):
  832. actual = list(mi.interleave_longest([1, 4, 7], [2, 5, 8], [3, 6, 9]))
  833. expected = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  834. self.assertEqual(actual, expected)
  835. def test_short(self):
  836. actual = list(mi.interleave_longest([1, 4], [2, 5, 7], [3, 6, 8]))
  837. expected = [1, 2, 3, 4, 5, 6, 7, 8]
  838. self.assertEqual(actual, expected)
  839. def test_mixed_types(self):
  840. it_list = ['a', 'b', 'c', 'd']
  841. it_str = '12345'
  842. it_gen = (x for x in range(3))
  843. actual = list(mi.interleave_longest(it_list, it_str, it_gen))
  844. expected = ['a', '1', 0, 'b', '2', 1, 'c', '3', 2, 'd', '4', '5']
  845. self.assertEqual(actual, expected)
  846. class InterleaveEvenlyTests(TestCase):
  847. def test_equal_lengths(self):
  848. # when lengths are equal, the relative order shouldn't change
  849. a = [1, 2, 3]
  850. b = [5, 6, 7]
  851. actual = list(mi.interleave_evenly([a, b]))
  852. expected = [1, 5, 2, 6, 3, 7]
  853. self.assertEqual(actual, expected)
  854. def test_proportional(self):
  855. # easy case where the iterables have proportional length
  856. a = [1, 2, 3, 4]
  857. b = [5, 6]
  858. actual = list(mi.interleave_evenly([a, b]))
  859. expected = [1, 2, 5, 3, 4, 6]
  860. self.assertEqual(actual, expected)
  861. # swapping a and b should yield the same result
  862. actual_swapped = list(mi.interleave_evenly([b, a]))
  863. self.assertEqual(actual_swapped, expected)
  864. def test_not_proportional(self):
  865. a = [1, 2, 3, 4, 5, 6, 7]
  866. b = [8, 9, 10]
  867. expected = [1, 2, 8, 3, 4, 9, 5, 6, 10, 7]
  868. actual = list(mi.interleave_evenly([a, b]))
  869. self.assertEqual(actual, expected)
  870. def test_degenerate_one(self):
  871. a = [0, 1, 2, 3, 4]
  872. b = [5]
  873. expected = [0, 1, 2, 5, 3, 4]
  874. actual = list(mi.interleave_evenly([a, b]))
  875. self.assertEqual(actual, expected)
  876. def test_degenerate_empty(self):
  877. a = [1, 2, 3]
  878. b = []
  879. expected = [1, 2, 3]
  880. actual = list(mi.interleave_evenly([a, b]))
  881. self.assertEqual(actual, expected)
  882. def test_three_iters(self):
  883. a = ["a1", "a2", "a3", "a4", "a5"]
  884. b = ["b1", "b2", "b3"]
  885. c = ["c1"]
  886. actual = list(mi.interleave_evenly([a, b, c]))
  887. expected = ["a1", "b1", "a2", "c1", "a3", "b2", "a4", "b3", "a5"]
  888. self.assertEqual(actual, expected)
  889. def test_many_iters(self):
  890. # smoke test with many iterables: create iterables with a random
  891. # number of elements starting with a character ("a0", "a1", ...)
  892. rng = Random(0)
  893. iterables = []
  894. for ch in ascii_letters:
  895. length = rng.randint(0, 100)
  896. iterable = [f"{ch}{i}" for i in range(length)]
  897. iterables.append(iterable)
  898. interleaved = list(mi.interleave_evenly(iterables))
  899. # for each iterable, check that the result contains all its items
  900. for iterable, ch_expect in zip(iterables, ascii_letters):
  901. interleaved_actual = [
  902. e for e in interleaved if e.startswith(ch_expect)
  903. ]
  904. assert len(set(interleaved_actual)) == len(iterable)
  905. def test_manual_lengths(self):
  906. a = combinations(range(4), 2)
  907. len_a = 4 * (4 - 1) // 2 # == 6
  908. b = combinations(range(4), 3)
  909. len_b = 4
  910. expected = [
  911. (0, 1),
  912. (0, 1, 2),
  913. (0, 2),
  914. (0, 3),
  915. (0, 1, 3),
  916. (1, 2),
  917. (0, 2, 3),
  918. (1, 3),
  919. (2, 3),
  920. (1, 2, 3),
  921. ]
  922. actual = list(mi.interleave_evenly([a, b], lengths=[len_a, len_b]))
  923. self.assertEqual(expected, actual)
  924. def test_no_length_raises(self):
  925. # combinations doesn't have __len__, should trigger ValueError
  926. iterables = [range(5), combinations(range(5), 2)]
  927. with self.assertRaises(ValueError):
  928. list(mi.interleave_evenly(iterables))
  929. def test_argument_mismatch_raises(self):
  930. # pass mismatching number of iterables and lengths
  931. iterables = [range(3)]
  932. lengths = [3, 4]
  933. with self.assertRaises(ValueError):
  934. list(mi.interleave_evenly(iterables, lengths=lengths))
  935. class TestCollapse(TestCase):
  936. """Tests for ``collapse()``"""
  937. def test_collapse(self):
  938. l = [[1], 2, [[3], 4], [[[5]]]]
  939. self.assertEqual(list(mi.collapse(l)), [1, 2, 3, 4, 5])
  940. def test_collapse_to_string(self):
  941. l = [["s1"], "s2", [["s3"], "s4"], [[["s5"]]]]
  942. self.assertEqual(list(mi.collapse(l)), ["s1", "s2", "s3", "s4", "s5"])
  943. def test_collapse_to_bytes(self):
  944. l = [[b"s1"], b"s2", [[b"s3"], b"s4"], [[[b"s5"]]]]
  945. self.assertEqual(
  946. list(mi.collapse(l)), [b"s1", b"s2", b"s3", b"s4", b"s5"]
  947. )
  948. def test_collapse_flatten(self):
  949. l = [[1], [2], [[3], 4], [[[5]]]]
  950. self.assertEqual(list(mi.collapse(l, levels=1)), list(mi.flatten(l)))
  951. def test_collapse_to_level(self):
  952. l = [[1], 2, [[3], 4], [[[5]]]]
  953. self.assertEqual(list(mi.collapse(l, levels=2)), [1, 2, 3, 4, [5]])
  954. self.assertEqual(
  955. list(mi.collapse(mi.collapse(l, levels=1), levels=1)),
  956. list(mi.collapse(l, levels=2)),
  957. )
  958. def test_collapse_to_list(self):
  959. l = (1, [2], (3, [4, (5,)], 'ab'))
  960. actual = list(mi.collapse(l, base_type=list))
  961. expected = [1, [2], 3, [4, (5,)], 'ab']
  962. self.assertEqual(actual, expected)
  963. class SideEffectTests(TestCase):
  964. """Tests for ``side_effect()``"""
  965. def test_individual(self):
  966. # The function increments the counter for each call
  967. counter = [0]
  968. def func(arg):
  969. counter[0] += 1
  970. result = list(mi.side_effect(func, range(10)))
  971. self.assertEqual(result, list(range(10)))
  972. self.assertEqual(counter[0], 10)
  973. def test_chunked(self):
  974. # The function increments the counter for each call
  975. counter = [0]
  976. def func(arg):
  977. counter[0] += 1
  978. result = list(mi.side_effect(func, range(10), 2))
  979. self.assertEqual(result, list(range(10)))
  980. self.assertEqual(counter[0], 5)
  981. def test_before_after(self):
  982. f = StringIO()
  983. collector = []
  984. def func(item):
  985. print(item, file=f)
  986. collector.append(f.getvalue())
  987. def it():
  988. yield 'a'
  989. yield 'b'
  990. raise RuntimeError('kaboom')
  991. before = lambda: print('HEADER', file=f)
  992. after = f.close
  993. try:
  994. mi.consume(mi.side_effect(func, it(), before=before, after=after))
  995. except RuntimeError:
  996. pass
  997. # The iterable should have been written to the file
  998. self.assertEqual(collector, ['HEADER\na\n', 'HEADER\na\nb\n'])
  999. # The file should be closed even though something bad happened
  1000. self.assertTrue(f.closed)
  1001. def test_before_fails(self):
  1002. f = StringIO()
  1003. func = lambda x: print(x, file=f)
  1004. def before():
  1005. raise RuntimeError('ouch')
  1006. try:
  1007. mi.consume(
  1008. mi.side_effect(func, 'abc', before=before, after=f.close)
  1009. )
  1010. except RuntimeError:
  1011. pass
  1012. # The file should be closed even though something bad happened in the
  1013. # before function
  1014. self.assertTrue(f.closed)
  1015. class SlicedTests(TestCase):
  1016. """Tests for ``sliced()``"""
  1017. def test_even(self):
  1018. """Test when the length of the sequence is divisible by *n*"""
  1019. seq = 'ABCDEFGHI'
  1020. self.assertEqual(list(mi.sliced(seq, 3)), ['ABC', 'DEF', 'GHI'])
  1021. def test_odd(self):
  1022. """Test when the length of the sequence is not divisible by *n*"""
  1023. seq = 'ABCDEFGHI'
  1024. self.assertEqual(list(mi.sliced(seq, 4)), ['ABCD', 'EFGH', 'I'])
  1025. def test_not_sliceable(self):
  1026. seq = (x for x in 'ABCDEFGHI')
  1027. with self.assertRaises(TypeError):
  1028. list(mi.sliced(seq, 3))
  1029. def test_odd_and_strict(self):
  1030. seq = [x for x in 'ABCDEFGHI']
  1031. with self.assertRaises(ValueError):
  1032. list(mi.sliced(seq, 4, strict=True))
  1033. def test_numpy_like_array(self):
  1034. # Numpy arrays don't behave like Python lists - calling bool()
  1035. # on them doesn't return False for empty lists and True for non-empty
  1036. # ones. Emulate that behavior.
  1037. class FalseList(list):
  1038. def __getitem__(self, key):
  1039. ret = super().__getitem__(key)
  1040. if isinstance(key, slice):
  1041. return FalseList(ret)
  1042. return ret
  1043. def __bool__(self):
  1044. return False
  1045. seq = FalseList(range(9))
  1046. actual = list(mi.sliced(seq, 3))
  1047. expected = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
  1048. self.assertEqual(actual, expected)
  1049. class SplitAtTests(TestCase):
  1050. def test_basic(self):
  1051. for iterable, separator in [
  1052. ('a,bb,ccc,dddd', ','),
  1053. (',a,bb,ccc,dddd', ','),
  1054. ('a,bb,ccc,dddd,', ','),
  1055. ('a,bb,ccc,,dddd', ','),
  1056. ('', ','),
  1057. (',', ','),
  1058. ('a,bb,ccc,dddd', ';'),
  1059. ]:
  1060. with self.subTest(iterable=iterable, separator=separator):
  1061. it = iter(iterable)
  1062. pred = lambda x: x == separator
  1063. actual = [''.join(x) for x in mi.split_at(it, pred)]
  1064. expected = iterable.split(separator)
  1065. self.assertEqual(actual, expected)
  1066. def test_maxsplit(self):
  1067. iterable = 'a,bb,ccc,dddd'
  1068. separator = ','
  1069. pred = lambda x: x == separator
  1070. for maxsplit in range(-1, 4):
  1071. with self.subTest(maxsplit=maxsplit):
  1072. it = iter(iterable)
  1073. result = mi.split_at(it, pred, maxsplit=maxsplit)
  1074. actual = [''.join(x) for x in result]
  1075. expected = iterable.split(separator, maxsplit)
  1076. self.assertEqual(actual, expected)
  1077. def test_keep_separator(self):
  1078. separator = ','
  1079. pred = lambda x: x == separator
  1080. for iterable, expected in [
  1081. ('a,bb,ccc', ['a', ',', 'bb', ',', 'ccc']),
  1082. (',a,bb,ccc', ['', ',', 'a', ',', 'bb', ',', 'ccc']),
  1083. ('a,bb,ccc,', ['a', ',', 'bb', ',', 'ccc', ',', '']),
  1084. ]:
  1085. with self.subTest(iterable=iterable):
  1086. it = iter(iterable)
  1087. result = mi.split_at(it, pred, keep_separator=True)
  1088. actual = [''.join(x) for x in result]
  1089. self.assertEqual(actual, expected)
  1090. def test_combination(self):
  1091. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  1092. pred = lambda x: x % 3 == 0
  1093. actual = list(
  1094. mi.split_at(iterable, pred, maxsplit=2, keep_separator=True)
  1095. )
  1096. expected = [[1, 2], [3], [4, 5], [6], [7, 8, 9, 10]]
  1097. self.assertEqual(actual, expected)
  1098. class SplitBeforeTest(TestCase):
  1099. """Tests for ``split_before()``"""
  1100. def test_starts_with_sep(self):
  1101. actual = list(mi.split_before('xooxoo', lambda c: c == 'x'))
  1102. expected = [['x', 'o', 'o'], ['x', 'o', 'o']]
  1103. self.assertEqual(actual, expected)
  1104. def test_ends_with_sep(self):
  1105. actual = list(mi.split_before('ooxoox', lambda c: c == 'x'))
  1106. expected = [['o', 'o'], ['x', 'o', 'o'], ['x']]
  1107. self.assertEqual(actual, expected)
  1108. def test_no_sep(self):
  1109. actual = list(mi.split_before('ooo', lambda c: c == 'x'))
  1110. expected = [['o', 'o', 'o']]
  1111. self.assertEqual(actual, expected)
  1112. def test_empty_collection(self):
  1113. actual = list(mi.split_before([], lambda c: bool(c)))
  1114. expected = []
  1115. self.assertEqual(actual, expected)
  1116. def test_max_split(self):
  1117. for args, expected in [
  1118. (
  1119. ('a,b,c,d', lambda c: c == ',', -1),
  1120. [['a'], [',', 'b'], [',', 'c'], [',', 'd']],
  1121. ),
  1122. (
  1123. ('a,b,c,d', lambda c: c == ',', 0),
  1124. [['a', ',', 'b', ',', 'c', ',', 'd']],
  1125. ),
  1126. (
  1127. ('a,b,c,d', lambda c: c == ',', 1),
  1128. [['a'], [',', 'b', ',', 'c', ',', 'd']],
  1129. ),
  1130. (
  1131. ('a,b,c,d', lambda c: c == ',', 2),
  1132. [['a'], [',', 'b'], [',', 'c', ',', 'd']],
  1133. ),
  1134. (
  1135. ('a,b,c,d', lambda c: c == ',', 10),
  1136. [['a'], [',', 'b'], [',', 'c'], [',', 'd']],
  1137. ),
  1138. (
  1139. ('a,b,c,d', lambda c: c == '@', 2),
  1140. [['a', ',', 'b', ',', 'c', ',', 'd']],
  1141. ),
  1142. (
  1143. ('a,b,c,d', lambda c: c != ',', 2),
  1144. [['a', ','], ['b', ','], ['c', ',', 'd']],
  1145. ),
  1146. ]:
  1147. actual = list(mi.split_before(*args))
  1148. self.assertEqual(actual, expected)
  1149. class SplitAfterTest(TestCase):
  1150. """Tests for ``split_after()``"""
  1151. def test_starts_with_sep(self):
  1152. actual = list(mi.split_after('xooxoo', lambda c: c == 'x'))
  1153. expected = [['x'], ['o', 'o', 'x'], ['o', 'o']]
  1154. self.assertEqual(actual, expected)
  1155. def test_ends_with_sep(self):
  1156. actual = list(mi.split_after('ooxoox', lambda c: c == 'x'))
  1157. expected = [['o', 'o', 'x'], ['o', 'o', 'x']]
  1158. self.assertEqual(actual, expected)
  1159. def test_no_sep(self):
  1160. actual = list(mi.split_after('ooo', lambda c: c == 'x'))
  1161. expected = [['o', 'o', 'o']]
  1162. self.assertEqual(actual, expected)
  1163. def test_max_split(self):
  1164. for args, expected in [
  1165. (
  1166. ('a,b,c,d', lambda c: c == ',', -1),
  1167. [['a', ','], ['b', ','], ['c', ','], ['d']],
  1168. ),
  1169. (
  1170. ('a,b,c,d', lambda c: c == ',', 0),
  1171. [['a', ',', 'b', ',', 'c', ',', 'd']],
  1172. ),
  1173. (
  1174. ('a,b,c,d', lambda c: c == ',', 1),
  1175. [['a', ','], ['b', ',', 'c', ',', 'd']],
  1176. ),
  1177. (
  1178. ('a,b,c,d', lambda c: c == ',', 2),
  1179. [['a', ','], ['b', ','], ['c', ',', 'd']],
  1180. ),
  1181. (
  1182. ('a,b,c,d', lambda c: c == ',', 10),
  1183. [['a', ','], ['b', ','], ['c', ','], ['d']],
  1184. ),
  1185. (
  1186. ('a,b,c,d', lambda c: c == '@', 2),
  1187. [['a', ',', 'b', ',', 'c', ',', 'd']],
  1188. ),
  1189. (
  1190. ('a,b,c,d', lambda c: c != ',', 2),
  1191. [['a'], [',', 'b'], [',', 'c', ',', 'd']],
  1192. ),
  1193. (
  1194. ([1], lambda x: x == 1, 1),
  1195. [[1]],
  1196. ),
  1197. ]:
  1198. actual = list(mi.split_after(*args))
  1199. self.assertEqual(actual, expected)
  1200. class SplitWhenTests(TestCase):
  1201. """Tests for ``split_when()``"""
  1202. @staticmethod
  1203. def _split_when_before(iterable, pred):
  1204. return mi.split_when(iterable, lambda _, c: pred(c))
  1205. @staticmethod
  1206. def _split_when_after(iterable, pred):
  1207. return mi.split_when(iterable, lambda c, _: pred(c))
  1208. # split_before emulation
  1209. def test_before_emulation_starts_with_sep(self):
  1210. actual = list(self._split_when_before('xooxoo', lambda c: c == 'x'))
  1211. expected = [['x', 'o', 'o'], ['x', 'o', 'o']]
  1212. self.assertEqual(actual, expected)
  1213. def test_before_emulation_ends_with_sep(self):
  1214. actual = list(self._split_when_before('ooxoox', lambda c: c == 'x'))
  1215. expected = [['o', 'o'], ['x', 'o', 'o'], ['x']]
  1216. self.assertEqual(actual, expected)
  1217. def test_before_emulation_no_sep(self):
  1218. actual = list(self._split_when_before('ooo', lambda c: c == 'x'))
  1219. expected = [['o', 'o', 'o']]
  1220. self.assertEqual(actual, expected)
  1221. # split_after emulation
  1222. def test_after_emulation_starts_with_sep(self):
  1223. actual = list(self._split_when_after('xooxoo', lambda c: c == 'x'))
  1224. expected = [['x'], ['o', 'o', 'x'], ['o', 'o']]
  1225. self.assertEqual(actual, expected)
  1226. def test_after_emulation_ends_with_sep(self):
  1227. actual = list(self._split_when_after('ooxoox', lambda c: c == 'x'))
  1228. expected = [['o', 'o', 'x'], ['o', 'o', 'x']]
  1229. self.assertEqual(actual, expected)
  1230. def test_after_emulation_no_sep(self):
  1231. actual = list(self._split_when_after('ooo', lambda c: c == 'x'))
  1232. expected = [['o', 'o', 'o']]
  1233. self.assertEqual(actual, expected)
  1234. # edge cases
  1235. def test_empty_iterable(self):
  1236. actual = list(mi.split_when('', lambda a, b: a != b))
  1237. expected = []
  1238. self.assertEqual(actual, expected)
  1239. def test_one_element(self):
  1240. actual = list(mi.split_when('o', lambda a, b: a == b))
  1241. expected = [['o']]
  1242. self.assertEqual(actual, expected)
  1243. def test_one_element_is_second_item(self):
  1244. actual = list(self._split_when_before('x', lambda c: c == 'x'))
  1245. expected = [['x']]
  1246. self.assertEqual(actual, expected)
  1247. def test_one_element_is_first_item(self):
  1248. actual = list(self._split_when_after('x', lambda c: c == 'x'))
  1249. expected = [['x']]
  1250. self.assertEqual(actual, expected)
  1251. def test_max_split(self):
  1252. for args, expected in [
  1253. (
  1254. ('a,b,c,d', lambda a, _: a == ',', -1),
  1255. [['a', ','], ['b', ','], ['c', ','], ['d']],
  1256. ),
  1257. (
  1258. ('a,b,c,d', lambda a, _: a == ',', 0),
  1259. [['a', ',', 'b', ',', 'c', ',', 'd']],
  1260. ),
  1261. (
  1262. ('a,b,c,d', lambda _, b: b == ',', 1),
  1263. [['a'], [',', 'b', ',', 'c', ',', 'd']],
  1264. ),
  1265. (
  1266. ('a,b,c,d', lambda a, _: a == ',', 2),
  1267. [['a', ','], ['b', ','], ['c', ',', 'd']],
  1268. ),
  1269. (
  1270. ('0124376', lambda a, b: a > b, -1),
  1271. [['0', '1', '2', '4'], ['3', '7'], ['6']],
  1272. ),
  1273. (
  1274. ('0124376', lambda a, b: a > b, 0),
  1275. [['0', '1', '2', '4', '3', '7', '6']],
  1276. ),
  1277. (
  1278. ('0124376', lambda a, b: a > b, 1),
  1279. [['0', '1', '2', '4'], ['3', '7', '6']],
  1280. ),
  1281. (
  1282. ('0124376', lambda a, b: a > b, 2),
  1283. [['0', '1', '2', '4'], ['3', '7'], ['6']],
  1284. ),
  1285. ]:
  1286. actual = list(mi.split_when(*args))
  1287. self.assertEqual(actual, expected, str(args))
  1288. class SplitIntoTests(TestCase):
  1289. """Tests for ``split_into()``"""
  1290. def test_iterable_just_right(self):
  1291. """Size of ``iterable`` equals the sum of ``sizes``."""
  1292. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  1293. sizes = [2, 3, 4]
  1294. expected = [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
  1295. actual = list(mi.split_into(iterable, sizes))
  1296. self.assertEqual(actual, expected)
  1297. def test_iterable_too_small(self):
  1298. """Size of ``iterable`` is smaller than sum of ``sizes``. Last return
  1299. list is shorter as a result."""
  1300. iterable = [1, 2, 3, 4, 5, 6, 7]
  1301. sizes = [2, 3, 4]
  1302. expected = [[1, 2], [3, 4, 5], [6, 7]]
  1303. actual = list(mi.split_into(iterable, sizes))
  1304. self.assertEqual(actual, expected)
  1305. def test_iterable_too_small_extra(self):
  1306. """Size of ``iterable`` is smaller than sum of ``sizes``. Second last
  1307. return list is shorter and last return list is empty as a result."""
  1308. iterable = [1, 2, 3, 4, 5, 6, 7]
  1309. sizes = [2, 3, 4, 5]
  1310. expected = [[1, 2], [3, 4, 5], [6, 7], []]
  1311. actual = list(mi.split_into(iterable, sizes))
  1312. self.assertEqual(actual, expected)
  1313. def test_iterable_too_large(self):
  1314. """Size of ``iterable`` is larger than sum of ``sizes``. Not all
  1315. items of iterable are returned."""
  1316. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  1317. sizes = [2, 3, 2]
  1318. expected = [[1, 2], [3, 4, 5], [6, 7]]
  1319. actual = list(mi.split_into(iterable, sizes))
  1320. self.assertEqual(actual, expected)
  1321. def test_using_none_with_leftover(self):
  1322. """Last item of ``sizes`` is None when items still remain in
  1323. ``iterable``. Last list returned stretches to fit all remaining items
  1324. of ``iterable``."""
  1325. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  1326. sizes = [2, 3, None]
  1327. expected = [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
  1328. actual = list(mi.split_into(iterable, sizes))
  1329. self.assertEqual(actual, expected)
  1330. def test_using_none_without_leftover(self):
  1331. """Last item of ``sizes`` is None when no items remain in
  1332. ``iterable``. Last list returned is empty."""
  1333. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  1334. sizes = [2, 3, 4, None]
  1335. expected = [[1, 2], [3, 4, 5], [6, 7, 8, 9], []]
  1336. actual = list(mi.split_into(iterable, sizes))
  1337. self.assertEqual(actual, expected)
  1338. def test_using_none_mid_sizes(self):
  1339. """None is present in ``sizes`` but is not the last item. Last list
  1340. returned stretches to fit all remaining items of ``iterable`` but
  1341. all items in ``sizes`` after None are ignored."""
  1342. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  1343. sizes = [2, 3, None, 4]
  1344. expected = [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
  1345. actual = list(mi.split_into(iterable, sizes))
  1346. self.assertEqual(actual, expected)
  1347. def test_iterable_empty(self):
  1348. """``iterable`` argument is empty but ``sizes`` is not. An empty
  1349. list is returned for each item in ``sizes``."""
  1350. iterable = []
  1351. sizes = [2, 4, 2]
  1352. expected = [[], [], []]
  1353. actual = list(mi.split_into(iterable, sizes))
  1354. self.assertEqual(actual, expected)
  1355. def test_iterable_empty_using_none(self):
  1356. """``iterable`` argument is empty but ``sizes`` is not. An empty
  1357. list is returned for each item in ``sizes`` that is not after a
  1358. None item."""
  1359. iterable = []
  1360. sizes = [2, 4, None, 2]
  1361. expected = [[], [], []]
  1362. actual = list(mi.split_into(iterable, sizes))
  1363. self.assertEqual(actual, expected)
  1364. def test_sizes_empty(self):
  1365. """``sizes`` argument is empty but ``iterable`` is not. An empty
  1366. generator is returned."""
  1367. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  1368. sizes = []
  1369. expected = []
  1370. actual = list(mi.split_into(iterable, sizes))
  1371. self.assertEqual(actual, expected)
  1372. def test_both_empty(self):
  1373. """Both ``sizes`` and ``iterable`` arguments are empty. An empty
  1374. generator is returned."""
  1375. iterable = []
  1376. sizes = []
  1377. expected = []
  1378. actual = list(mi.split_into(iterable, sizes))
  1379. self.assertEqual(actual, expected)
  1380. def test_bool_in_sizes(self):
  1381. """A bool object is present in ``sizes`` is treated as a 1 or 0 for
  1382. ``True`` or ``False`` due to bool being an instance of int."""
  1383. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  1384. sizes = [3, True, 2, False]
  1385. expected = [[1, 2, 3], [4], [5, 6], []]
  1386. actual = list(mi.split_into(iterable, sizes))
  1387. self.assertEqual(actual, expected)
  1388. def test_invalid_in_sizes(self):
  1389. """A ValueError is raised if an object in ``sizes`` is neither ``None``
  1390. or an integer."""
  1391. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  1392. sizes = [1, [], 3]
  1393. with self.assertRaises(ValueError):
  1394. list(mi.split_into(iterable, sizes))
  1395. def test_invalid_in_sizes_after_none(self):
  1396. """A item in ``sizes`` that is invalid will not raise a TypeError if it
  1397. comes after a ``None`` item."""
  1398. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  1399. sizes = [3, 4, None, []]
  1400. expected = [[1, 2, 3], [4, 5, 6, 7], [8, 9]]
  1401. actual = list(mi.split_into(iterable, sizes))
  1402. self.assertEqual(actual, expected)
  1403. def test_generator_iterable_integrity(self):
  1404. """Check that if ``iterable`` is an iterator, it is consumed only by as
  1405. many items as the sum of ``sizes``."""
  1406. iterable = (i for i in range(10))
  1407. sizes = [2, 3]
  1408. expected = [[0, 1], [2, 3, 4]]
  1409. actual = list(mi.split_into(iterable, sizes))
  1410. self.assertEqual(actual, expected)
  1411. iterable_expected = [5, 6, 7, 8, 9]
  1412. iterable_actual = list(iterable)
  1413. self.assertEqual(iterable_actual, iterable_expected)
  1414. def test_generator_sizes_integrity(self):
  1415. """Check that if ``sizes`` is an iterator, it is consumed only until a
  1416. ``None`` item is reached"""
  1417. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  1418. sizes = (i for i in [1, 2, None, 3, 4])
  1419. expected = [[1], [2, 3], [4, 5, 6, 7, 8, 9]]
  1420. actual = list(mi.split_into(iterable, sizes))
  1421. self.assertEqual(actual, expected)
  1422. sizes_expected = [3, 4]
  1423. sizes_actual = list(sizes)
  1424. self.assertEqual(sizes_actual, sizes_expected)
  1425. class PaddedTest(TestCase):
  1426. """Tests for ``padded()``"""
  1427. def test_no_n(self):
  1428. seq = [1, 2, 3]
  1429. # No fillvalue
  1430. self.assertEqual(mi.take(5, mi.padded(seq)), [1, 2, 3, None, None])
  1431. # With fillvalue
  1432. self.assertEqual(
  1433. mi.take(5, mi.padded(seq, fillvalue='')), [1, 2, 3, '', '']
  1434. )
  1435. def test_invalid_n(self):
  1436. self.assertRaises(ValueError, lambda: list(mi.padded([1, 2, 3], n=-1)))
  1437. self.assertRaises(ValueError, lambda: list(mi.padded([1, 2, 3], n=0)))
  1438. def test_valid_n(self):
  1439. seq = [1, 2, 3, 4, 5]
  1440. # No need for padding: len(seq) <= n
  1441. self.assertEqual(list(mi.padded(seq, n=4)), [1, 2, 3, 4, 5])
  1442. self.assertEqual(list(mi.padded(seq, n=5)), [1, 2, 3, 4, 5])
  1443. # No fillvalue
  1444. self.assertEqual(
  1445. list(mi.padded(seq, n=7)), [1, 2, 3, 4, 5, None, None]
  1446. )
  1447. # With fillvalue
  1448. self.assertEqual(
  1449. list(mi.padded(seq, fillvalue='', n=7)), [1, 2, 3, 4, 5, '', '']
  1450. )
  1451. def test_next_multiple(self):
  1452. seq = [1, 2, 3, 4, 5, 6]
  1453. # No need for padding: len(seq) % n == 0
  1454. self.assertEqual(
  1455. list(mi.padded(seq, n=3, next_multiple=True)), [1, 2, 3, 4, 5, 6]
  1456. )
  1457. # Padding needed: len(seq) < n
  1458. self.assertEqual(
  1459. list(mi.padded(seq, n=8, next_multiple=True)),
  1460. [1, 2, 3, 4, 5, 6, None, None],
  1461. )
  1462. # No padding needed: len(seq) == n
  1463. self.assertEqual(
  1464. list(mi.padded(seq, n=6, next_multiple=True)), [1, 2, 3, 4, 5, 6]
  1465. )
  1466. # Padding needed: len(seq) > n
  1467. self.assertEqual(
  1468. list(mi.padded(seq, n=4, next_multiple=True)),
  1469. [1, 2, 3, 4, 5, 6, None, None],
  1470. )
  1471. # With fillvalue
  1472. self.assertEqual(
  1473. list(mi.padded(seq, fillvalue='', n=4, next_multiple=True)),
  1474. [1, 2, 3, 4, 5, 6, '', ''],
  1475. )
  1476. class RepeatEachTests(TestCase):
  1477. """Tests for repeat_each()"""
  1478. def test_default(self):
  1479. actual = list(mi.repeat_each('ABC'))
  1480. expected = ['A', 'A', 'B', 'B', 'C', 'C']
  1481. self.assertEqual(actual, expected)
  1482. def test_basic(self):
  1483. actual = list(mi.repeat_each('ABC', 3))
  1484. expected = ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C']
  1485. self.assertEqual(actual, expected)
  1486. def test_empty(self):
  1487. actual = list(mi.repeat_each(''))
  1488. expected = []
  1489. self.assertEqual(actual, expected)
  1490. def test_no_repeat(self):
  1491. actual = list(mi.repeat_each('ABC', 0))
  1492. expected = []
  1493. self.assertEqual(actual, expected)
  1494. def test_negative_repeat(self):
  1495. actual = list(mi.repeat_each('ABC', -1))
  1496. expected = []
  1497. self.assertEqual(actual, expected)
  1498. def test_infinite_input(self):
  1499. repeater = mi.repeat_each(cycle('AB'))
  1500. actual = mi.take(6, repeater)
  1501. expected = ['A', 'A', 'B', 'B', 'A', 'A']
  1502. self.assertEqual(actual, expected)
  1503. class RepeatLastTests(TestCase):
  1504. def test_empty_iterable(self):
  1505. slice_length = 3
  1506. iterable = iter([])
  1507. actual = mi.take(slice_length, mi.repeat_last(iterable))
  1508. expected = [None] * slice_length
  1509. self.assertEqual(actual, expected)
  1510. def test_default_value(self):
  1511. slice_length = 3
  1512. iterable = iter([])
  1513. default = '3'
  1514. actual = mi.take(slice_length, mi.repeat_last(iterable, default))
  1515. expected = ['3'] * slice_length
  1516. self.assertEqual(actual, expected)
  1517. def test_basic(self):
  1518. slice_length = 10
  1519. iterable = (str(x) for x in range(5))
  1520. actual = mi.take(slice_length, mi.repeat_last(iterable))
  1521. expected = ['0', '1', '2', '3', '4', '4', '4', '4', '4', '4']
  1522. self.assertEqual(actual, expected)
  1523. class DistributeTest(TestCase):
  1524. """Tests for distribute()"""
  1525. def test_invalid_n(self):
  1526. self.assertRaises(ValueError, lambda: mi.distribute(-1, [1, 2, 3]))
  1527. self.assertRaises(ValueError, lambda: mi.distribute(0, [1, 2, 3]))
  1528. def test_basic(self):
  1529. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  1530. for n, expected in [
  1531. (1, [iterable]),
  1532. (2, [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]),
  1533. (3, [[1, 4, 7, 10], [2, 5, 8], [3, 6, 9]]),
  1534. (10, [[n] for n in range(1, 10 + 1)]),
  1535. ]:
  1536. self.assertEqual(
  1537. [list(x) for x in mi.distribute(n, iterable)], expected
  1538. )
  1539. def test_large_n(self):
  1540. iterable = [1, 2, 3, 4]
  1541. self.assertEqual(
  1542. [list(x) for x in mi.distribute(6, iterable)],
  1543. [[1], [2], [3], [4], [], []],
  1544. )
  1545. class StaggerTest(TestCase):
  1546. """Tests for ``stagger()``"""
  1547. def test_default(self):
  1548. iterable = [0, 1, 2, 3]
  1549. actual = list(mi.stagger(iterable))
  1550. expected = [(None, 0, 1), (0, 1, 2), (1, 2, 3)]
  1551. self.assertEqual(actual, expected)
  1552. def test_offsets(self):
  1553. iterable = [0, 1, 2, 3]
  1554. for offsets, expected in [
  1555. ((-2, 0, 2), [('', 0, 2), ('', 1, 3)]),
  1556. ((-2, -1), [('', ''), ('', 0), (0, 1), (1, 2), (2, 3)]),
  1557. ((1, 2), [(1, 2), (2, 3)]),
  1558. ]:
  1559. all_groups = mi.stagger(iterable, offsets=offsets, fillvalue='')
  1560. self.assertEqual(list(all_groups), expected)
  1561. def test_longest(self):
  1562. iterable = [0, 1, 2, 3]
  1563. for offsets, expected in [
  1564. (
  1565. (-1, 0, 1),
  1566. [('', 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, ''), (3, '', '')],
  1567. ),
  1568. ((-2, -1), [('', ''), ('', 0), (0, 1), (1, 2), (2, 3), (3, '')]),
  1569. ((1, 2), [(1, 2), (2, 3), (3, '')]),
  1570. ]:
  1571. all_groups = mi.stagger(
  1572. iterable, offsets=offsets, fillvalue='', longest=True
  1573. )
  1574. self.assertEqual(list(all_groups), expected)
  1575. class ZipEqualTest(TestCase):
  1576. @skipIf(version_info[:2] < (3, 10), 'zip_equal deprecated for 3.10+')
  1577. def test_deprecation(self):
  1578. with warnings.catch_warnings(record=True) as caught:
  1579. warnings.simplefilter('always')
  1580. self.assertEqual(
  1581. list(mi.zip_equal([1, 2], [3, 4])), [(1, 3), (2, 4)]
  1582. )
  1583. (warning,) = caught
  1584. assert warning.category == DeprecationWarning
  1585. def test_equal(self):
  1586. lists = [0, 1, 2], [2, 3, 4]
  1587. for iterables in [lists, map(iter, lists)]:
  1588. actual = list(mi.zip_equal(*iterables))
  1589. expected = [(0, 2), (1, 3), (2, 4)]
  1590. self.assertEqual(actual, expected)
  1591. def test_unequal_lists(self):
  1592. two_items = [0, 1]
  1593. three_items = [2, 3, 4]
  1594. four_items = [5, 6, 7, 8]
  1595. # the mismatch is at index 1
  1596. try:
  1597. list(mi.zip_equal(two_items, three_items, four_items))
  1598. except mi.UnequalIterablesError as e:
  1599. self.assertEqual(
  1600. e.args[0],
  1601. (
  1602. 'Iterables have different lengths: '
  1603. 'index 0 has length 2; index 1 has length 3'
  1604. ),
  1605. )
  1606. # the mismatch is at index 2
  1607. try:
  1608. list(mi.zip_equal(two_items, two_items, four_items, four_items))
  1609. except mi.UnequalIterablesError as e:
  1610. self.assertEqual(
  1611. e.args[0],
  1612. (
  1613. 'Iterables have different lengths: '
  1614. 'index 0 has length 2; index 2 has length 4'
  1615. ),
  1616. )
  1617. # One without length: delegate to _zip_equal_generator
  1618. try:
  1619. list(mi.zip_equal(two_items, iter(two_items), three_items))
  1620. except mi.UnequalIterablesError as e:
  1621. self.assertEqual(e.args[0], 'Iterables have different lengths')
  1622. class ZipOffsetTest(TestCase):
  1623. """Tests for ``zip_offset()``"""
  1624. def test_shortest(self):
  1625. a_1 = [0, 1, 2, 3]
  1626. a_2 = [0, 1, 2, 3, 4, 5]
  1627. a_3 = [0, 1, 2, 3, 4, 5, 6, 7]
  1628. actual = list(
  1629. mi.zip_offset(a_1, a_2, a_3, offsets=(-1, 0, 1), fillvalue='')
  1630. )
  1631. expected = [('', 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5)]
  1632. self.assertEqual(actual, expected)
  1633. def test_longest(self):
  1634. a_1 = [0, 1, 2, 3]
  1635. a_2 = [0, 1, 2, 3, 4, 5]
  1636. a_3 = [0, 1, 2, 3, 4, 5, 6, 7]
  1637. actual = list(
  1638. mi.zip_offset(a_1, a_2, a_3, offsets=(-1, 0, 1), longest=True)
  1639. )
  1640. expected = [
  1641. (None, 0, 1),
  1642. (0, 1, 2),
  1643. (1, 2, 3),
  1644. (2, 3, 4),
  1645. (3, 4, 5),
  1646. (None, 5, 6),
  1647. (None, None, 7),
  1648. ]
  1649. self.assertEqual(actual, expected)
  1650. def test_mismatch(self):
  1651. iterables = [0, 1, 2], [2, 3, 4]
  1652. offsets = (-1, 0, 1)
  1653. self.assertRaises(
  1654. ValueError,
  1655. lambda: list(mi.zip_offset(*iterables, offsets=offsets)),
  1656. )
  1657. class UnzipTests(TestCase):
  1658. """Tests for unzip()"""
  1659. def test_empty_iterable(self):
  1660. self.assertEqual(list(mi.unzip([])), [])
  1661. # in reality zip([], [], []) is equivalent to iter([])
  1662. # but it doesn't hurt to test both
  1663. self.assertEqual(list(mi.unzip(zip([], [], []))), [])
  1664. def test_length_one_iterable(self):
  1665. xs, ys, zs = mi.unzip(zip([1], [2], [3]))
  1666. self.assertEqual(list(xs), [1])
  1667. self.assertEqual(list(ys), [2])
  1668. self.assertEqual(list(zs), [3])
  1669. def test_normal_case(self):
  1670. xs, ys, zs = range(10), range(1, 11), range(2, 12)
  1671. zipped = zip(xs, ys, zs)
  1672. xs, ys, zs = mi.unzip(zipped)
  1673. self.assertEqual(list(xs), list(range(10)))
  1674. self.assertEqual(list(ys), list(range(1, 11)))
  1675. self.assertEqual(list(zs), list(range(2, 12)))
  1676. def test_improperly_zipped(self):
  1677. zipped = iter([(1, 2, 3), (4, 5), (6,)])
  1678. xs, ys, zs = mi.unzip(zipped)
  1679. self.assertEqual(list(xs), [1, 4, 6])
  1680. self.assertEqual(list(ys), [2, 5])
  1681. self.assertEqual(list(zs), [3])
  1682. def test_increasingly_zipped(self):
  1683. zipped = iter([(1, 2), (3, 4, 5), (6, 7, 8, 9)])
  1684. unzipped = mi.unzip(zipped)
  1685. # from the docstring:
  1686. # len(first tuple) is the number of iterables zipped
  1687. self.assertEqual(len(unzipped), 2)
  1688. xs, ys = unzipped
  1689. self.assertEqual(list(xs), [1, 3, 6])
  1690. self.assertEqual(list(ys), [2, 4, 7])
  1691. class SortTogetherTest(TestCase):
  1692. """Tests for sort_together()"""
  1693. def test_key_list(self):
  1694. """tests `key_list` including default, iterables include duplicates"""
  1695. iterables = [
  1696. ['GA', 'GA', 'GA', 'CT', 'CT', 'CT'],
  1697. ['May', 'Aug.', 'May', 'June', 'July', 'July'],
  1698. [97, 20, 100, 70, 100, 20],
  1699. ]
  1700. self.assertEqual(
  1701. mi.sort_together(iterables),
  1702. [
  1703. ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'),
  1704. ('June', 'July', 'July', 'May', 'Aug.', 'May'),
  1705. (70, 100, 20, 97, 20, 100),
  1706. ],
  1707. )
  1708. self.assertEqual(
  1709. mi.sort_together(iterables, key_list=(0, 1)),
  1710. [
  1711. ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'),
  1712. ('July', 'July', 'June', 'Aug.', 'May', 'May'),
  1713. (100, 20, 70, 20, 97, 100),
  1714. ],
  1715. )
  1716. self.assertEqual(
  1717. mi.sort_together(iterables, key_list=(0, 1, 2)),
  1718. [
  1719. ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'),
  1720. ('July', 'July', 'June', 'Aug.', 'May', 'May'),
  1721. (20, 100, 70, 20, 97, 100),
  1722. ],
  1723. )
  1724. self.assertEqual(
  1725. mi.sort_together(iterables, key_list=(2,)),
  1726. [
  1727. ('GA', 'CT', 'CT', 'GA', 'GA', 'CT'),
  1728. ('Aug.', 'July', 'June', 'May', 'May', 'July'),
  1729. (20, 20, 70, 97, 100, 100),
  1730. ],
  1731. )
  1732. def test_invalid_key_list(self):
  1733. """tests `key_list` for indexes not available in `iterables`"""
  1734. iterables = [
  1735. ['GA', 'GA', 'GA', 'CT', 'CT', 'CT'],
  1736. ['May', 'Aug.', 'May', 'June', 'July', 'July'],
  1737. [97, 20, 100, 70, 100, 20],
  1738. ]
  1739. self.assertRaises(
  1740. IndexError, lambda: mi.sort_together(iterables, key_list=(5,))
  1741. )
  1742. def test_key_function(self):
  1743. """tests `key` function, including interaction with `key_list`"""
  1744. iterables = [
  1745. ['GA', 'GA', 'GA', 'CT', 'CT', 'CT'],
  1746. ['May', 'Aug.', 'May', 'June', 'July', 'July'],
  1747. [97, 20, 100, 70, 100, 20],
  1748. ]
  1749. self.assertEqual(
  1750. mi.sort_together(iterables, key=lambda x: x),
  1751. [
  1752. ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'),
  1753. ('June', 'July', 'July', 'May', 'Aug.', 'May'),
  1754. (70, 100, 20, 97, 20, 100),
  1755. ],
  1756. )
  1757. self.assertEqual(
  1758. mi.sort_together(iterables, key=lambda x: x[::-1]),
  1759. [
  1760. ('GA', 'GA', 'GA', 'CT', 'CT', 'CT'),
  1761. ('May', 'Aug.', 'May', 'June', 'July', 'July'),
  1762. (97, 20, 100, 70, 100, 20),
  1763. ],
  1764. )
  1765. self.assertEqual(
  1766. mi.sort_together(
  1767. iterables,
  1768. key_list=(0, 2),
  1769. key=lambda state, number: (
  1770. number if state == 'CT' else 2 * number
  1771. ),
  1772. ),
  1773. [
  1774. ('CT', 'GA', 'CT', 'CT', 'GA', 'GA'),
  1775. ('July', 'Aug.', 'June', 'July', 'May', 'May'),
  1776. (20, 20, 70, 100, 97, 100),
  1777. ],
  1778. )
  1779. def test_reverse(self):
  1780. """tests `reverse` to ensure a reverse sort for `key_list` iterables"""
  1781. iterables = [
  1782. ['GA', 'GA', 'GA', 'CT', 'CT', 'CT'],
  1783. ['May', 'Aug.', 'May', 'June', 'July', 'July'],
  1784. [97, 20, 100, 70, 100, 20],
  1785. ]
  1786. self.assertEqual(
  1787. mi.sort_together(iterables, key_list=(0, 1, 2), reverse=True),
  1788. [
  1789. ('GA', 'GA', 'GA', 'CT', 'CT', 'CT'),
  1790. ('May', 'May', 'Aug.', 'June', 'July', 'July'),
  1791. (100, 97, 20, 70, 100, 20),
  1792. ],
  1793. )
  1794. def test_uneven_iterables(self):
  1795. """tests trimming of iterables to the shortest length before sorting"""
  1796. iterables = [
  1797. ['GA', 'GA', 'GA', 'CT', 'CT', 'CT', 'MA'],
  1798. ['May', 'Aug.', 'May', 'June', 'July', 'July'],
  1799. [97, 20, 100, 70, 100, 20, 0],
  1800. ]
  1801. self.assertEqual(
  1802. mi.sort_together(iterables),
  1803. [
  1804. ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'),
  1805. ('June', 'July', 'July', 'May', 'Aug.', 'May'),
  1806. (70, 100, 20, 97, 20, 100),
  1807. ],
  1808. )
  1809. class DivideTest(TestCase):
  1810. """Tests for divide()"""
  1811. def test_invalid_n(self):
  1812. self.assertRaises(ValueError, lambda: mi.divide(-1, [1, 2, 3]))
  1813. self.assertRaises(ValueError, lambda: mi.divide(0, [1, 2, 3]))
  1814. def test_basic(self):
  1815. iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  1816. for n, expected in [
  1817. (1, [iterable]),
  1818. (2, [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]),
  1819. (3, [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]),
  1820. (10, [[n] for n in range(1, 10 + 1)]),
  1821. ]:
  1822. self.assertEqual(
  1823. [list(x) for x in mi.divide(n, iterable)], expected
  1824. )
  1825. def test_large_n(self):
  1826. self.assertEqual(
  1827. [list(x) for x in mi.divide(6, iter(range(1, 4 + 1)))],
  1828. [[1], [2], [3], [4], [], []],
  1829. )
  1830. class TestAlwaysIterable(TestCase):
  1831. """Tests for always_iterable()"""
  1832. def test_single(self):
  1833. self.assertEqual(list(mi.always_iterable(1)), [1])
  1834. def test_strings(self):
  1835. for obj in ['foo', b'bar', 'baz']:
  1836. actual = list(mi.always_iterable(obj))
  1837. expected = [obj]
  1838. self.assertEqual(actual, expected)
  1839. def test_base_type(self):
  1840. dict_obj = {'a': 1, 'b': 2}
  1841. str_obj = '123'
  1842. # Default: dicts are iterable like they normally are
  1843. default_actual = list(mi.always_iterable(dict_obj))
  1844. default_expected = list(dict_obj)
  1845. self.assertEqual(default_actual, default_expected)
  1846. # Unitary types set: dicts are not iterable
  1847. custom_actual = list(mi.always_iterable(dict_obj, base_type=dict))
  1848. custom_expected = [dict_obj]
  1849. self.assertEqual(custom_actual, custom_expected)
  1850. # With unitary types set, strings are iterable
  1851. str_actual = list(mi.always_iterable(str_obj, base_type=None))
  1852. str_expected = list(str_obj)
  1853. self.assertEqual(str_actual, str_expected)
  1854. # base_type handles nested tuple (via isinstance).
  1855. base_type = ((dict,),)
  1856. custom_actual = list(mi.always_iterable(dict_obj, base_type=base_type))
  1857. custom_expected = [dict_obj]
  1858. self.assertEqual(custom_actual, custom_expected)
  1859. def test_iterables(self):
  1860. self.assertEqual(list(mi.always_iterable([0, 1])), [0, 1])
  1861. self.assertEqual(
  1862. list(mi.always_iterable([0, 1], base_type=list)), [[0, 1]]
  1863. )
  1864. self.assertEqual(
  1865. list(mi.always_iterable(iter('foo'))), ['f', 'o', 'o']
  1866. )
  1867. self.assertEqual(list(mi.always_iterable([])), [])
  1868. def test_none(self):
  1869. self.assertEqual(list(mi.always_iterable(None)), [])
  1870. def test_generator(self):
  1871. def _gen():
  1872. yield 0
  1873. yield 1
  1874. self.assertEqual(list(mi.always_iterable(_gen())), [0, 1])
  1875. class AdjacentTests(TestCase):
  1876. def test_typical(self):
  1877. actual = list(mi.adjacent(lambda x: x % 5 == 0, range(10)))
  1878. expected = [
  1879. (True, 0),
  1880. (True, 1),
  1881. (False, 2),
  1882. (False, 3),
  1883. (True, 4),
  1884. (True, 5),
  1885. (True, 6),
  1886. (False, 7),
  1887. (False, 8),
  1888. (False, 9),
  1889. ]
  1890. self.assertEqual(actual, expected)
  1891. def test_empty_iterable(self):
  1892. actual = list(mi.adjacent(lambda x: x % 5 == 0, []))
  1893. expected = []
  1894. self.assertEqual(actual, expected)
  1895. def test_length_one(self):
  1896. actual = list(mi.adjacent(lambda x: x % 5 == 0, [0]))
  1897. expected = [(True, 0)]
  1898. self.assertEqual(actual, expected)
  1899. actual = list(mi.adjacent(lambda x: x % 5 == 0, [1]))
  1900. expected = [(False, 1)]
  1901. self.assertEqual(actual, expected)
  1902. def test_consecutive_true(self):
  1903. """Test that when the predicate matches multiple consecutive elements
  1904. it doesn't repeat elements in the output"""
  1905. actual = list(mi.adjacent(lambda x: x % 5 < 2, range(10)))
  1906. expected = [
  1907. (True, 0),
  1908. (True, 1),
  1909. (True, 2),
  1910. (False, 3),
  1911. (True, 4),
  1912. (True, 5),
  1913. (True, 6),
  1914. (True, 7),
  1915. (False, 8),
  1916. (False, 9),
  1917. ]
  1918. self.assertEqual(actual, expected)
  1919. def test_distance(self):
  1920. actual = list(mi.adjacent(lambda x: x % 5 == 0, range(10), distance=2))
  1921. expected = [
  1922. (True, 0),
  1923. (True, 1),
  1924. (True, 2),
  1925. (True, 3),
  1926. (True, 4),
  1927. (True, 5),
  1928. (True, 6),
  1929. (True, 7),
  1930. (False, 8),
  1931. (False, 9),
  1932. ]
  1933. self.assertEqual(actual, expected)
  1934. actual = list(mi.adjacent(lambda x: x % 5 == 0, range(10), distance=3))
  1935. expected = [
  1936. (True, 0),
  1937. (True, 1),
  1938. (True, 2),
  1939. (True, 3),
  1940. (True, 4),
  1941. (True, 5),
  1942. (True, 6),
  1943. (True, 7),
  1944. (True, 8),
  1945. (False, 9),
  1946. ]
  1947. self.assertEqual(actual, expected)
  1948. def test_large_distance(self):
  1949. """Test distance larger than the length of the iterable"""
  1950. iterable = range(10)
  1951. actual = list(mi.adjacent(lambda x: x % 5 == 4, iterable, distance=20))
  1952. expected = list(zip(repeat(True), iterable))
  1953. self.assertEqual(actual, expected)
  1954. actual = list(mi.adjacent(lambda x: False, iterable, distance=20))
  1955. expected = list(zip(repeat(False), iterable))
  1956. self.assertEqual(actual, expected)
  1957. def test_zero_distance(self):
  1958. """Test that adjacent() reduces to zip+map when distance is 0"""
  1959. iterable = range(1000)
  1960. predicate = lambda x: x % 4 == 2
  1961. actual = mi.adjacent(predicate, iterable, 0)
  1962. expected = zip(map(predicate, iterable), iterable)
  1963. self.assertTrue(all(a == e for a, e in zip(actual, expected)))
  1964. def test_negative_distance(self):
  1965. """Test that adjacent() raises an error with negative distance"""
  1966. pred = lambda x: x
  1967. self.assertRaises(
  1968. ValueError, lambda: mi.adjacent(pred, range(1000), -1)
  1969. )
  1970. self.assertRaises(
  1971. ValueError, lambda: mi.adjacent(pred, range(10), -10)
  1972. )
  1973. def test_grouping(self):
  1974. """Test interaction of adjacent() with groupby_transform()"""
  1975. iterable = mi.adjacent(lambda x: x % 5 == 0, range(10))
  1976. grouper = mi.groupby_transform(iterable, itemgetter(0), itemgetter(1))
  1977. actual = [(k, list(g)) for k, g in grouper]
  1978. expected = [
  1979. (True, [0, 1]),
  1980. (False, [2, 3]),
  1981. (True, [4, 5, 6]),
  1982. (False, [7, 8, 9]),
  1983. ]
  1984. self.assertEqual(actual, expected)
  1985. def test_call_once(self):
  1986. """Test that the predicate is only called once per item."""
  1987. already_seen = set()
  1988. iterable = range(10)
  1989. def predicate(item):
  1990. self.assertNotIn(item, already_seen)
  1991. already_seen.add(item)
  1992. return True
  1993. actual = list(mi.adjacent(predicate, iterable))
  1994. expected = [(True, x) for x in iterable]
  1995. self.assertEqual(actual, expected)
  1996. class GroupByTransformTests(TestCase):
  1997. def assertAllGroupsEqual(self, groupby1, groupby2):
  1998. for a, b in zip(groupby1, groupby2):
  1999. key1, group1 = a
  2000. key2, group2 = b
  2001. self.assertEqual(key1, key2)
  2002. self.assertListEqual(list(group1), list(group2))
  2003. self.assertRaises(StopIteration, lambda: next(groupby1))
  2004. self.assertRaises(StopIteration, lambda: next(groupby2))
  2005. def test_default_funcs(self):
  2006. iterable = [(x // 5, x) for x in range(1000)]
  2007. actual = mi.groupby_transform(iterable)
  2008. expected = groupby(iterable)
  2009. self.assertAllGroupsEqual(actual, expected)
  2010. def test_valuefunc(self):
  2011. iterable = [(int(x / 5), int(x / 3), x) for x in range(10)]
  2012. # Test the standard usage of grouping one iterable using another's keys
  2013. grouper = mi.groupby_transform(
  2014. iterable, keyfunc=itemgetter(0), valuefunc=itemgetter(-1)
  2015. )
  2016. actual = [(k, list(g)) for k, g in grouper]
  2017. expected = [(0, [0, 1, 2, 3, 4]), (1, [5, 6, 7, 8, 9])]
  2018. self.assertEqual(actual, expected)
  2019. grouper = mi.groupby_transform(
  2020. iterable, keyfunc=itemgetter(1), valuefunc=itemgetter(-1)
  2021. )
  2022. actual = [(k, list(g)) for k, g in grouper]
  2023. expected = [(0, [0, 1, 2]), (1, [3, 4, 5]), (2, [6, 7, 8]), (3, [9])]
  2024. self.assertEqual(actual, expected)
  2025. # and now for something a little different
  2026. d = dict(zip(range(10), 'abcdefghij'))
  2027. grouper = mi.groupby_transform(
  2028. range(10), keyfunc=lambda x: x // 5, valuefunc=d.get
  2029. )
  2030. actual = [(k, ''.join(g)) for k, g in grouper]
  2031. expected = [(0, 'abcde'), (1, 'fghij')]
  2032. self.assertEqual(actual, expected)
  2033. def test_no_valuefunc(self):
  2034. iterable = range(1000)
  2035. def key(x):
  2036. return x // 5
  2037. actual = mi.groupby_transform(iterable, key, valuefunc=None)
  2038. expected = groupby(iterable, key)
  2039. self.assertAllGroupsEqual(actual, expected)
  2040. actual = mi.groupby_transform(iterable, key) # default valuefunc
  2041. expected = groupby(iterable, key)
  2042. self.assertAllGroupsEqual(actual, expected)
  2043. def test_reducefunc(self):
  2044. iterable = range(50)
  2045. keyfunc = lambda k: 10 * (k // 10)
  2046. valuefunc = lambda v: v + 1
  2047. reducefunc = sum
  2048. actual = list(
  2049. mi.groupby_transform(
  2050. iterable,
  2051. keyfunc=keyfunc,
  2052. valuefunc=valuefunc,
  2053. reducefunc=reducefunc,
  2054. )
  2055. )
  2056. expected = [(0, 55), (10, 155), (20, 255), (30, 355), (40, 455)]
  2057. self.assertEqual(actual, expected)
  2058. class NumericRangeTests(TestCase):
  2059. def test_basic(self):
  2060. for args, expected in [
  2061. ((4,), [0, 1, 2, 3]),
  2062. ((4.0,), [0.0, 1.0, 2.0, 3.0]),
  2063. ((1.0, 4), [1.0, 2.0, 3.0]),
  2064. ((1, 4.0), [1.0, 2.0, 3.0]),
  2065. ((1.0, 5), [1.0, 2.0, 3.0, 4.0]),
  2066. ((0, 20, 5), [0, 5, 10, 15]),
  2067. ((0, 20, 5.0), [0.0, 5.0, 10.0, 15.0]),
  2068. ((0, 10, 3), [0, 3, 6, 9]),
  2069. ((0, 10, 3.0), [0.0, 3.0, 6.0, 9.0]),
  2070. ((0, -5, -1), [0, -1, -2, -3, -4]),
  2071. ((0.0, -5, -1), [0.0, -1.0, -2.0, -3.0, -4.0]),
  2072. ((1, 2, Fraction(1, 2)), [Fraction(1, 1), Fraction(3, 2)]),
  2073. ((0,), []),
  2074. ((0.0,), []),
  2075. ((1, 0), []),
  2076. ((1.0, 0.0), []),
  2077. ((0.1, 0.30000000000000001, 0.2), [0.1]), # IEE 754 !
  2078. (
  2079. (
  2080. Decimal("0.1"),
  2081. Decimal("0.30000000000000001"),
  2082. Decimal("0.2"),
  2083. ),
  2084. [Decimal("0.1"), Decimal("0.3")],
  2085. ), # okay with Decimal
  2086. (
  2087. (
  2088. Fraction(1, 10),
  2089. Fraction(30000000000000001, 100000000000000000),
  2090. Fraction(2, 10),
  2091. ),
  2092. [Fraction(1, 10), Fraction(3, 10)],
  2093. ), # okay with Fraction
  2094. ((Fraction(2, 1),), [Fraction(0, 1), Fraction(1, 1)]),
  2095. ((Decimal('2.0'),), [Decimal('0.0'), Decimal('1.0')]),
  2096. (
  2097. (
  2098. datetime(2019, 3, 29, 12, 34, 56),
  2099. datetime(2019, 3, 29, 12, 37, 55),
  2100. timedelta(minutes=1),
  2101. ),
  2102. [
  2103. datetime(2019, 3, 29, 12, 34, 56),
  2104. datetime(2019, 3, 29, 12, 35, 56),
  2105. datetime(2019, 3, 29, 12, 36, 56),
  2106. ],
  2107. ),
  2108. ]:
  2109. actual = list(mi.numeric_range(*args))
  2110. self.assertEqual(expected, actual)
  2111. self.assertTrue(
  2112. all(type(a) is type(e) for a, e in zip(actual, expected))
  2113. )
  2114. def test_arg_count(self):
  2115. for args, message in [
  2116. ((), 'numeric_range expected at least 1 argument, got 0'),
  2117. (
  2118. (0, 1, 2, 3),
  2119. 'numeric_range expected at most 3 arguments, got 4',
  2120. ),
  2121. ]:
  2122. with self.assertRaisesRegex(TypeError, message):
  2123. mi.numeric_range(*args)
  2124. def test_zero_step(self):
  2125. for args in [
  2126. (1, 2, 0),
  2127. (
  2128. datetime(2019, 3, 29, 12, 34, 56),
  2129. datetime(2019, 3, 29, 12, 37, 55),
  2130. timedelta(minutes=0),
  2131. ),
  2132. (1.0, 2.0, 0.0),
  2133. (Decimal("1.0"), Decimal("2.0"), Decimal("0.0")),
  2134. (Fraction(2, 2), Fraction(4, 2), Fraction(0, 2)),
  2135. ]:
  2136. with self.assertRaises(ValueError):
  2137. list(mi.numeric_range(*args))
  2138. def test_bool(self):
  2139. for args, expected in [
  2140. ((1.0, 3.0, 1.5), True),
  2141. ((1.0, 2.0, 1.5), True),
  2142. ((1.0, 1.0, 1.5), False),
  2143. ((1.0, 0.0, 1.5), False),
  2144. ((3.0, 1.0, -1.5), True),
  2145. ((2.0, 1.0, -1.5), True),
  2146. ((1.0, 1.0, -1.5), False),
  2147. ((0.0, 1.0, -1.5), False),
  2148. ((Decimal("1.0"), Decimal("2.0"), Decimal("1.5")), True),
  2149. ((Decimal("1.0"), Decimal("0.0"), Decimal("1.5")), False),
  2150. ((Fraction(2, 2), Fraction(4, 2), Fraction(3, 2)), True),
  2151. ((Fraction(2, 2), Fraction(0, 2), Fraction(3, 2)), False),
  2152. (
  2153. (
  2154. datetime(2019, 3, 29),
  2155. datetime(2019, 3, 30),
  2156. timedelta(hours=1),
  2157. ),
  2158. True,
  2159. ),
  2160. (
  2161. (
  2162. datetime(2019, 3, 29),
  2163. datetime(2019, 3, 28),
  2164. timedelta(hours=1),
  2165. ),
  2166. False,
  2167. ),
  2168. ]:
  2169. self.assertEqual(expected, bool(mi.numeric_range(*args)))
  2170. def test_contains(self):
  2171. for args, expected_in, expected_not_in in [
  2172. ((10,), range(10), (0.5,)),
  2173. ((1.0, 9.9, 1.5), (1.0, 2.5, 4.0, 5.5, 7.0, 8.5), (0.9,)),
  2174. ((9.0, 1.0, -1.5), (1.5, 3.0, 4.5, 6.0, 7.5, 9.0), (0.0, 0.9)),
  2175. (
  2176. (Decimal("1.0"), Decimal("9.9"), Decimal("1.5")),
  2177. (
  2178. Decimal("1.0"),
  2179. Decimal("2.5"),
  2180. Decimal("4.0"),
  2181. Decimal("5.5"),
  2182. Decimal("7.0"),
  2183. Decimal("8.5"),
  2184. ),
  2185. (Decimal("0.9"),),
  2186. ),
  2187. (
  2188. (Fraction(0, 1), Fraction(5, 1), Fraction(1, 2)),
  2189. (Fraction(0, 1), Fraction(1, 2), Fraction(9, 2)),
  2190. (Fraction(10, 2),),
  2191. ),
  2192. (
  2193. (
  2194. datetime(2019, 3, 29),
  2195. datetime(2019, 3, 30),
  2196. timedelta(hours=1),
  2197. ),
  2198. (datetime(2019, 3, 29, 15),),
  2199. (datetime(2019, 3, 29, 15, 30),),
  2200. ),
  2201. ]:
  2202. r = mi.numeric_range(*args)
  2203. for v in expected_in:
  2204. self.assertTrue(v in r)
  2205. self.assertFalse(v not in r)
  2206. for v in expected_not_in:
  2207. self.assertFalse(v in r)
  2208. self.assertTrue(v not in r)
  2209. def test_eq(self):
  2210. for args1, args2 in [
  2211. ((0, 5, 2), (0, 6, 2)),
  2212. ((1.0, 9.9, 1.5), (1.0, 8.6, 1.5)),
  2213. ((8.5, 0.0, -1.5), (8.5, 0.7, -1.5)),
  2214. ((7.0, 0.0, 1.0), (17.0, 7.0, 0.5)),
  2215. (
  2216. (Decimal("1.0"), Decimal("9.9"), Decimal("1.5")),
  2217. (Decimal("1.0"), Decimal("8.6"), Decimal("1.5")),
  2218. ),
  2219. (
  2220. (Fraction(1, 1), Fraction(10, 1), Fraction(3, 2)),
  2221. (Fraction(1, 1), Fraction(9, 1), Fraction(3, 2)),
  2222. ),
  2223. (
  2224. (
  2225. datetime(2019, 3, 29),
  2226. datetime(2019, 3, 30),
  2227. timedelta(hours=10),
  2228. ),
  2229. (
  2230. datetime(2019, 3, 29),
  2231. datetime(2019, 3, 30, 1),
  2232. timedelta(hours=10),
  2233. ),
  2234. ),
  2235. ]:
  2236. self.assertEqual(
  2237. mi.numeric_range(*args1), mi.numeric_range(*args2)
  2238. )
  2239. for args1, args2 in [
  2240. ((0, 5, 2), (0, 7, 2)),
  2241. ((1.0, 9.9, 1.5), (1.2, 9.9, 1.5)),
  2242. ((1.0, 9.9, 1.5), (1.0, 10.3, 1.5)),
  2243. ((1.0, 9.9, 1.5), (1.0, 9.9, 1.4)),
  2244. ((8.5, 0.0, -1.5), (8.4, 0.0, -1.5)),
  2245. ((8.5, 0.0, -1.5), (8.5, -0.7, -1.5)),
  2246. ((8.5, 0.0, -1.5), (8.5, 0.0, -1.4)),
  2247. ((0.0, 7.0, 1.0), (7.0, 0.0, 1.0)),
  2248. (
  2249. (Decimal("1.0"), Decimal("10.0"), Decimal("1.5")),
  2250. (Decimal("1.0"), Decimal("10.5"), Decimal("1.5")),
  2251. ),
  2252. (
  2253. (Fraction(1, 1), Fraction(10, 1), Fraction(3, 2)),
  2254. (Fraction(1, 1), Fraction(21, 2), Fraction(3, 2)),
  2255. ),
  2256. (
  2257. (
  2258. datetime(2019, 3, 29),
  2259. datetime(2019, 3, 30),
  2260. timedelta(hours=10),
  2261. ),
  2262. (
  2263. datetime(2019, 3, 29),
  2264. datetime(2019, 3, 30, 15),
  2265. timedelta(hours=10),
  2266. ),
  2267. ),
  2268. ]:
  2269. self.assertNotEqual(
  2270. mi.numeric_range(*args1), mi.numeric_range(*args2)
  2271. )
  2272. self.assertNotEqual(mi.numeric_range(7.0), 1)
  2273. self.assertNotEqual(mi.numeric_range(7.0), "abc")
  2274. def test_get_item_by_index(self):
  2275. for args, index, expected in [
  2276. ((1, 6), 2, 3),
  2277. ((1.0, 6.0, 1.5), 0, 1.0),
  2278. ((1.0, 6.0, 1.5), 1, 2.5),
  2279. ((1.0, 6.0, 1.5), 2, 4.0),
  2280. ((1.0, 6.0, 1.5), 3, 5.5),
  2281. ((1.0, 6.0, 1.5), -1, 5.5),
  2282. ((1.0, 6.0, 1.5), -2, 4.0),
  2283. (
  2284. (Decimal("1.0"), Decimal("9.0"), Decimal("1.5")),
  2285. -1,
  2286. Decimal("8.5"),
  2287. ),
  2288. (
  2289. (Fraction(1, 1), Fraction(10, 1), Fraction(3, 2)),
  2290. 2,
  2291. Fraction(4, 1),
  2292. ),
  2293. (
  2294. (
  2295. datetime(2019, 3, 29),
  2296. datetime(2019, 3, 30),
  2297. timedelta(hours=10),
  2298. ),
  2299. 1,
  2300. datetime(2019, 3, 29, 10),
  2301. ),
  2302. ]:
  2303. self.assertEqual(expected, mi.numeric_range(*args)[index])
  2304. for args, index in [
  2305. ((1.0, 6.0, 1.5), 4),
  2306. ((1.0, 6.0, 1.5), -5),
  2307. ((6.0, 1.0, 1.5), 0),
  2308. ((6.0, 1.0, 1.5), -1),
  2309. ((Decimal("1.0"), Decimal("9.0"), Decimal("-1.5")), -1),
  2310. ((Fraction(1, 1), Fraction(2, 1), Fraction(3, 2)), 2),
  2311. (
  2312. (
  2313. datetime(2019, 3, 29),
  2314. datetime(2019, 3, 30),
  2315. timedelta(hours=10),
  2316. ),
  2317. 8,
  2318. ),
  2319. ]:
  2320. with self.assertRaises(IndexError):
  2321. mi.numeric_range(*args)[index]
  2322. def test_get_item_by_slice(self):
  2323. for args, sl, expected_args in [
  2324. ((1.0, 9.0, 1.5), slice(None, None, None), (1.0, 9.0, 1.5)),
  2325. ((1.0, 9.0, 1.5), slice(None, 1, None), (1.0, 2.5, 1.5)),
  2326. ((1.0, 9.0, 1.5), slice(None, None, 2), (1.0, 9.0, 3.0)),
  2327. ((1.0, 9.0, 1.5), slice(None, 2, None), (1.0, 4.0, 1.5)),
  2328. ((1.0, 9.0, 1.5), slice(1, 2, None), (2.5, 4.0, 1.5)),
  2329. ((1.0, 9.0, 1.5), slice(1, -1, None), (2.5, 8.5, 1.5)),
  2330. ((1.0, 9.0, 1.5), slice(10, None, 3), (9.0, 9.0, 4.5)),
  2331. ((1.0, 9.0, 1.5), slice(-10, None, 3), (1.0, 9.0, 4.5)),
  2332. ((1.0, 9.0, 1.5), slice(None, -10, 3), (1.0, 1.0, 4.5)),
  2333. ((1.0, 9.0, 1.5), slice(None, 10, 3), (1.0, 9.0, 4.5)),
  2334. (
  2335. (Decimal("1.0"), Decimal("9.0"), Decimal("1.5")),
  2336. slice(1, -1, None),
  2337. (Decimal("2.5"), Decimal("8.5"), Decimal("1.5")),
  2338. ),
  2339. (
  2340. (Fraction(1, 1), Fraction(5, 1), Fraction(3, 2)),
  2341. slice(1, -1, None),
  2342. (Fraction(5, 2), Fraction(4, 1), Fraction(3, 2)),
  2343. ),
  2344. (
  2345. (
  2346. datetime(2019, 3, 29),
  2347. datetime(2019, 3, 30),
  2348. timedelta(hours=10),
  2349. ),
  2350. slice(1, -1, None),
  2351. (
  2352. datetime(2019, 3, 29, 10),
  2353. datetime(2019, 3, 29, 20),
  2354. timedelta(hours=10),
  2355. ),
  2356. ),
  2357. ]:
  2358. self.assertEqual(
  2359. mi.numeric_range(*expected_args), mi.numeric_range(*args)[sl]
  2360. )
  2361. def test_hash(self):
  2362. for args, expected in [
  2363. ((1.0, 6.0, 1.5), hash((1.0, 5.5, 1.5))),
  2364. ((1.0, 7.0, 1.5), hash((1.0, 5.5, 1.5))),
  2365. ((1.0, 7.5, 1.5), hash((1.0, 7.0, 1.5))),
  2366. ((1.0, 1.5, 1.5), hash((1.0, 1.0, 1.5))),
  2367. ((1.5, 1.0, 1.5), hash(range(0, 0))),
  2368. ((1.5, 1.5, 1.5), hash(range(0, 0))),
  2369. (
  2370. (Decimal("1.0"), Decimal("9.0"), Decimal("1.5")),
  2371. hash((Decimal("1.0"), Decimal("8.5"), Decimal("1.5"))),
  2372. ),
  2373. (
  2374. (Fraction(1, 1), Fraction(5, 1), Fraction(3, 2)),
  2375. hash((Fraction(1, 1), Fraction(4, 1), Fraction(3, 2))),
  2376. ),
  2377. (
  2378. (
  2379. datetime(2019, 3, 29),
  2380. datetime(2019, 3, 30),
  2381. timedelta(hours=10),
  2382. ),
  2383. hash(
  2384. (
  2385. datetime(2019, 3, 29),
  2386. datetime(2019, 3, 29, 20),
  2387. timedelta(hours=10),
  2388. )
  2389. ),
  2390. ),
  2391. ]:
  2392. self.assertEqual(expected, hash(mi.numeric_range(*args)))
  2393. def test_iter_twice(self):
  2394. r1 = mi.numeric_range(1.0, 9.9, 1.5)
  2395. r2 = mi.numeric_range(8.5, 0.0, -1.5)
  2396. self.assertEqual([1.0, 2.5, 4.0, 5.5, 7.0, 8.5], list(r1))
  2397. self.assertEqual([1.0, 2.5, 4.0, 5.5, 7.0, 8.5], list(r1))
  2398. self.assertEqual([8.5, 7.0, 5.5, 4.0, 2.5, 1.0], list(r2))
  2399. self.assertEqual([8.5, 7.0, 5.5, 4.0, 2.5, 1.0], list(r2))
  2400. def test_len(self):
  2401. for args, expected in [
  2402. ((1.0, 7.0, 1.5), 4),
  2403. ((1.0, 7.01, 1.5), 5),
  2404. ((7.0, 1.0, -1.5), 4),
  2405. ((7.01, 1.0, -1.5), 5),
  2406. ((0.1, 0.30000000000000001, 0.2), 1), # IEE 754 !
  2407. (
  2408. (
  2409. Decimal("0.1"),
  2410. Decimal("0.30000000000000001"),
  2411. Decimal("0.2"),
  2412. ),
  2413. 2,
  2414. ), # works with Decimal
  2415. ((Decimal("1.0"), Decimal("9.0"), Decimal("1.5")), 6),
  2416. ((Fraction(1, 1), Fraction(5, 1), Fraction(3, 2)), 3),
  2417. (
  2418. (
  2419. datetime(2019, 3, 29),
  2420. datetime(2019, 3, 30),
  2421. timedelta(hours=10),
  2422. ),
  2423. 3,
  2424. ),
  2425. ]:
  2426. self.assertEqual(expected, len(mi.numeric_range(*args)))
  2427. def test_repr(self):
  2428. for args, *expected in [
  2429. ((7.0,), "numeric_range(0.0, 7.0)"),
  2430. ((1.0, 7.0), "numeric_range(1.0, 7.0)"),
  2431. ((7.0, 1.0, -1.5), "numeric_range(7.0, 1.0, -1.5)"),
  2432. (
  2433. (Decimal("1.0"), Decimal("9.0"), Decimal("1.5")),
  2434. (
  2435. "numeric_range(Decimal('1.0'), Decimal('9.0'), "
  2436. "Decimal('1.5'))"
  2437. ),
  2438. ),
  2439. (
  2440. (Fraction(7, 7), Fraction(10, 2), Fraction(3, 2)),
  2441. (
  2442. "numeric_range(Fraction(1, 1), Fraction(5, 1), "
  2443. "Fraction(3, 2))"
  2444. ),
  2445. ),
  2446. (
  2447. (
  2448. datetime(2019, 3, 29),
  2449. datetime(2019, 3, 30),
  2450. timedelta(hours=10),
  2451. ),
  2452. "numeric_range(datetime.datetime(2019, 3, 29, 0, 0), "
  2453. "datetime.datetime(2019, 3, 30, 0, 0), "
  2454. "datetime.timedelta(seconds=36000))",
  2455. "numeric_range(datetime.datetime(2019, 3, 29, 0, 0), "
  2456. "datetime.datetime(2019, 3, 30, 0, 0), "
  2457. "datetime.timedelta(0, 36000))",
  2458. ),
  2459. ]:
  2460. with self.subTest(args=args):
  2461. self.assertIn(repr(mi.numeric_range(*args)), expected)
  2462. def test_reversed(self):
  2463. for args, expected in [
  2464. ((7.0,), [6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0]),
  2465. ((1.0, 7.0), [6.0, 5.0, 4.0, 3.0, 2.0, 1.0]),
  2466. ((7.0, 1.0, -1.5), [2.5, 4.0, 5.5, 7.0]),
  2467. ((7.0, 0.9, -1.5), [1.0, 2.5, 4.0, 5.5, 7.0]),
  2468. (
  2469. (Decimal("1.0"), Decimal("5.0"), Decimal("1.5")),
  2470. [Decimal('4.0'), Decimal('2.5'), Decimal('1.0')],
  2471. ),
  2472. (
  2473. (Fraction(1, 1), Fraction(5, 1), Fraction(3, 2)),
  2474. [Fraction(4, 1), Fraction(5, 2), Fraction(1, 1)],
  2475. ),
  2476. (
  2477. (
  2478. datetime(2019, 3, 29),
  2479. datetime(2019, 3, 30),
  2480. timedelta(hours=10),
  2481. ),
  2482. [
  2483. datetime(2019, 3, 29, 20),
  2484. datetime(2019, 3, 29, 10),
  2485. datetime(2019, 3, 29),
  2486. ],
  2487. ),
  2488. ]:
  2489. self.assertEqual(expected, list(reversed(mi.numeric_range(*args))))
  2490. def test_count(self):
  2491. for args, v, c in [
  2492. ((7.0,), 0.0, 1),
  2493. ((7.0,), 0.5, 0),
  2494. ((7.0,), 6.0, 1),
  2495. ((7.0,), 7.0, 0),
  2496. ((7.0,), 10.0, 0),
  2497. (
  2498. (Decimal("1.0"), Decimal("5.0"), Decimal("1.5")),
  2499. Decimal('4.0'),
  2500. 1,
  2501. ),
  2502. (
  2503. (Fraction(1, 1), Fraction(5, 1), Fraction(3, 2)),
  2504. Fraction(5, 2),
  2505. 1,
  2506. ),
  2507. (
  2508. (
  2509. datetime(2019, 3, 29),
  2510. datetime(2019, 3, 30),
  2511. timedelta(hours=10),
  2512. ),
  2513. datetime(2019, 3, 29, 20),
  2514. 1,
  2515. ),
  2516. ]:
  2517. self.assertEqual(c, mi.numeric_range(*args).count(v))
  2518. def test_index(self):
  2519. for args, v, i in [
  2520. ((7.0,), 0.0, 0),
  2521. ((7.0,), 6.0, 6),
  2522. ((7.0, 0.0, -1.0), 7.0, 0),
  2523. ((7.0, 0.0, -1.0), 1.0, 6),
  2524. (
  2525. (Decimal("1.0"), Decimal("5.0"), Decimal("1.5")),
  2526. Decimal('4.0'),
  2527. 2,
  2528. ),
  2529. (
  2530. (Fraction(1, 1), Fraction(5, 1), Fraction(3, 2)),
  2531. Fraction(5, 2),
  2532. 1,
  2533. ),
  2534. (
  2535. (
  2536. datetime(2019, 3, 29),
  2537. datetime(2019, 3, 30),
  2538. timedelta(hours=10),
  2539. ),
  2540. datetime(2019, 3, 29, 20),
  2541. 2,
  2542. ),
  2543. ]:
  2544. self.assertEqual(i, mi.numeric_range(*args).index(v))
  2545. for args, v in [
  2546. ((0.7,), 0.5),
  2547. ((0.7,), 7.0),
  2548. ((0.7,), 10.0),
  2549. ((7.0, 0.0, -1.0), 0.5),
  2550. ((7.0, 0.0, -1.0), 0.0),
  2551. ((7.0, 0.0, -1.0), 10.0),
  2552. ((7.0, 0.0), 5.0),
  2553. ((Decimal("1.0"), Decimal("5.0"), Decimal("1.5")), Decimal('4.5')),
  2554. ((Fraction(1, 1), Fraction(5, 1), Fraction(3, 2)), Fraction(5, 3)),
  2555. (
  2556. (
  2557. datetime(2019, 3, 29),
  2558. datetime(2019, 3, 30),
  2559. timedelta(hours=10),
  2560. ),
  2561. datetime(2019, 3, 30),
  2562. ),
  2563. ]:
  2564. with self.assertRaises(ValueError):
  2565. mi.numeric_range(*args).index(v)
  2566. def test_parent_classes(self):
  2567. r = mi.numeric_range(7.0)
  2568. self.assertTrue(isinstance(r, abc.Iterable))
  2569. self.assertFalse(isinstance(r, abc.Iterator))
  2570. self.assertTrue(isinstance(r, abc.Sequence))
  2571. self.assertTrue(isinstance(r, abc.Hashable))
  2572. def test_bad_key(self):
  2573. r = mi.numeric_range(7.0)
  2574. for arg, message in [
  2575. ('a', 'numeric range indices must be integers or slices, not str'),
  2576. (
  2577. (),
  2578. 'numeric range indices must be integers or slices, not tuple',
  2579. ),
  2580. ]:
  2581. with self.assertRaisesRegex(TypeError, message):
  2582. r[arg]
  2583. def test_pickle(self):
  2584. for args in [
  2585. (7.0,),
  2586. (5.0, 7.0),
  2587. (5.0, 7.0, 3.0),
  2588. (7.0, 5.0),
  2589. (7.0, 5.0, 4.0),
  2590. (7.0, 5.0, -1.0),
  2591. (Decimal("1.0"), Decimal("5.0"), Decimal("1.5")),
  2592. (Fraction(1, 1), Fraction(5, 1), Fraction(3, 2)),
  2593. (datetime(2019, 3, 29), datetime(2019, 3, 30)),
  2594. ]:
  2595. r = mi.numeric_range(*args)
  2596. self.assertTrue(dumps(r)) # assert not empty
  2597. self.assertEqual(r, loads(dumps(r)))
  2598. class CountCycleTests(TestCase):
  2599. def test_basic(self):
  2600. expected = [
  2601. (0, 'a'),
  2602. (0, 'b'),
  2603. (0, 'c'),
  2604. (1, 'a'),
  2605. (1, 'b'),
  2606. (1, 'c'),
  2607. (2, 'a'),
  2608. (2, 'b'),
  2609. (2, 'c'),
  2610. ]
  2611. for actual in [
  2612. mi.take(9, mi.count_cycle('abc')), # n=None
  2613. list(mi.count_cycle('abc', 3)), # n=3
  2614. ]:
  2615. self.assertEqual(actual, expected)
  2616. def test_empty(self):
  2617. self.assertEqual(list(mi.count_cycle('')), [])
  2618. self.assertEqual(list(mi.count_cycle('', 2)), [])
  2619. def test_negative(self):
  2620. self.assertEqual(list(mi.count_cycle('abc', -3)), [])
  2621. class MarkEndsTests(TestCase):
  2622. def test_basic(self):
  2623. for size, expected in [
  2624. (0, []),
  2625. (1, [(True, True, '0')]),
  2626. (2, [(True, False, '0'), (False, True, '1')]),
  2627. (3, [(True, False, '0'), (False, False, '1'), (False, True, '2')]),
  2628. (
  2629. 4,
  2630. [
  2631. (True, False, '0'),
  2632. (False, False, '1'),
  2633. (False, False, '2'),
  2634. (False, True, '3'),
  2635. ],
  2636. ),
  2637. ]:
  2638. with self.subTest(size=size):
  2639. iterable = map(str, range(size))
  2640. actual = list(mi.mark_ends(iterable))
  2641. self.assertEqual(actual, expected)
  2642. class LocateTests(TestCase):
  2643. def test_default_pred(self):
  2644. iterable = [0, 1, 1, 0, 1, 0, 0]
  2645. actual = list(mi.locate(iterable))
  2646. expected = [1, 2, 4]
  2647. self.assertEqual(actual, expected)
  2648. def test_no_matches(self):
  2649. iterable = [0, 0, 0]
  2650. actual = list(mi.locate(iterable))
  2651. expected = []
  2652. self.assertEqual(actual, expected)
  2653. def test_custom_pred(self):
  2654. iterable = ['0', 1, 1, '0', 1, '0', '0']
  2655. pred = lambda x: x == '0'
  2656. actual = list(mi.locate(iterable, pred))
  2657. expected = [0, 3, 5, 6]
  2658. self.assertEqual(actual, expected)
  2659. def test_window_size(self):
  2660. iterable = ['0', 1, 1, '0', 1, '0', '0']
  2661. pred = lambda *args: args == ('0', 1)
  2662. actual = list(mi.locate(iterable, pred, window_size=2))
  2663. expected = [0, 3]
  2664. self.assertEqual(actual, expected)
  2665. def test_window_size_large(self):
  2666. iterable = [1, 2, 3, 4]
  2667. pred = lambda a, b, c, d, e: True
  2668. actual = list(mi.locate(iterable, pred, window_size=5))
  2669. expected = [0]
  2670. self.assertEqual(actual, expected)
  2671. def test_window_size_zero(self):
  2672. iterable = [1, 2, 3, 4]
  2673. pred = lambda: True
  2674. with self.assertRaises(ValueError):
  2675. list(mi.locate(iterable, pred, window_size=0))
  2676. class StripFunctionTests(TestCase):
  2677. def test_hashable(self):
  2678. iterable = list('www.example.com')
  2679. pred = lambda x: x in set('cmowz.')
  2680. self.assertEqual(list(mi.lstrip(iterable, pred)), list('example.com'))
  2681. self.assertEqual(list(mi.rstrip(iterable, pred)), list('www.example'))
  2682. self.assertEqual(list(mi.strip(iterable, pred)), list('example'))
  2683. def test_not_hashable(self):
  2684. iterable = [
  2685. list('http://'),
  2686. list('www'),
  2687. list('.example'),
  2688. list('.com'),
  2689. ]
  2690. pred = lambda x: x in [list('http://'), list('www'), list('.com')]
  2691. self.assertEqual(list(mi.lstrip(iterable, pred)), iterable[2:])
  2692. self.assertEqual(list(mi.rstrip(iterable, pred)), iterable[:3])
  2693. self.assertEqual(list(mi.strip(iterable, pred)), iterable[2:3])
  2694. def test_math(self):
  2695. iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2]
  2696. pred = lambda x: x <= 2
  2697. self.assertEqual(list(mi.lstrip(iterable, pred)), iterable[3:])
  2698. self.assertEqual(list(mi.rstrip(iterable, pred)), iterable[:-3])
  2699. self.assertEqual(list(mi.strip(iterable, pred)), iterable[3:-3])
  2700. class IsliceExtendedTests(TestCase):
  2701. def test_all(self):
  2702. iterable = ['0', '1', '2', '3', '4', '5']
  2703. indexes = [*range(-4, 10), None]
  2704. steps = [1, 2, 3, 4, -1, -2, -3, -4]
  2705. for slice_args in product(indexes, indexes, steps):
  2706. with self.subTest(slice_args=slice_args):
  2707. actual = list(mi.islice_extended(iterable, *slice_args))
  2708. expected = iterable[slice(*slice_args)]
  2709. self.assertEqual(actual, expected, slice_args)
  2710. def test_zero_step(self):
  2711. with self.assertRaises(ValueError):
  2712. list(mi.islice_extended([1, 2, 3], 0, 1, 0))
  2713. def test_slicing(self):
  2714. iterable = map(str, count())
  2715. first_slice = mi.islice_extended(iterable)[10:]
  2716. second_slice = mi.islice_extended(first_slice)[:10]
  2717. third_slice = mi.islice_extended(second_slice)[::2]
  2718. self.assertEqual(list(third_slice), ['10', '12', '14', '16', '18'])
  2719. def test_slicing_extensive(self):
  2720. iterable = range(10)
  2721. options = (None, 1, 2, 7, -1)
  2722. for start, stop, step in product(options, options, options):
  2723. with self.subTest(slice_args=(start, stop, step)):
  2724. sliced_tuple_0 = tuple(
  2725. mi.islice_extended(iterable)[start:stop:step]
  2726. )
  2727. sliced_tuple_1 = tuple(
  2728. mi.islice_extended(iterable, start, stop, step)
  2729. )
  2730. sliced_range = tuple(iterable[start:stop:step])
  2731. self.assertEqual(sliced_tuple_0, sliced_range)
  2732. self.assertEqual(sliced_tuple_1, sliced_range)
  2733. def test_invalid_slice(self):
  2734. with self.assertRaises(TypeError):
  2735. mi.islice_extended(count())[13]
  2736. class ConsecutiveGroupsTest(TestCase):
  2737. def test_numbers(self):
  2738. iterable = [-10, -8, -7, -6, 1, 2, 4, 5, -1, 7]
  2739. actual = [list(g) for g in mi.consecutive_groups(iterable)]
  2740. expected = [[-10], [-8, -7, -6], [1, 2], [4, 5], [-1], [7]]
  2741. self.assertEqual(actual, expected)
  2742. def test_custom_ordering(self):
  2743. iterable = ['1', '10', '11', '20', '21', '22', '30', '31']
  2744. ordering = lambda x: int(x)
  2745. actual = [list(g) for g in mi.consecutive_groups(iterable, ordering)]
  2746. expected = [['1'], ['10', '11'], ['20', '21', '22'], ['30', '31']]
  2747. self.assertEqual(actual, expected)
  2748. def test_exotic_ordering(self):
  2749. iterable = [
  2750. ('a', 'b', 'c', 'd'),
  2751. ('a', 'c', 'b', 'd'),
  2752. ('a', 'c', 'd', 'b'),
  2753. ('a', 'd', 'b', 'c'),
  2754. ('d', 'b', 'c', 'a'),
  2755. ('d', 'c', 'a', 'b'),
  2756. ]
  2757. ordering = list(permutations('abcd')).index
  2758. actual = [list(g) for g in mi.consecutive_groups(iterable, ordering)]
  2759. expected = [
  2760. [('a', 'b', 'c', 'd')],
  2761. [('a', 'c', 'b', 'd'), ('a', 'c', 'd', 'b'), ('a', 'd', 'b', 'c')],
  2762. [('d', 'b', 'c', 'a'), ('d', 'c', 'a', 'b')],
  2763. ]
  2764. self.assertEqual(actual, expected)
  2765. class DifferenceTest(TestCase):
  2766. def test_normal(self):
  2767. iterable = [10, 20, 30, 40, 50]
  2768. actual = list(mi.difference(iterable))
  2769. expected = [10, 10, 10, 10, 10]
  2770. self.assertEqual(actual, expected)
  2771. def test_custom(self):
  2772. iterable = [10, 20, 30, 40, 50]
  2773. actual = list(mi.difference(iterable, add))
  2774. expected = [10, 30, 50, 70, 90]
  2775. self.assertEqual(actual, expected)
  2776. def test_roundtrip(self):
  2777. original = list(range(100))
  2778. accumulated = accumulate(original)
  2779. actual = list(mi.difference(accumulated))
  2780. self.assertEqual(actual, original)
  2781. def test_one(self):
  2782. self.assertEqual(list(mi.difference([0])), [0])
  2783. def test_empty(self):
  2784. self.assertEqual(list(mi.difference([])), [])
  2785. def test_initial(self):
  2786. original = list(range(100))
  2787. accumulated = accumulate(original, initial=100)
  2788. actual = list(mi.difference(accumulated, initial=100))
  2789. self.assertEqual(actual, original)
  2790. class SeekableTest(PeekableMixinTests, TestCase):
  2791. cls = mi.seekable
  2792. def test_exhaustion_reset(self):
  2793. iterable = [str(n) for n in range(10)]
  2794. s = mi.seekable(iterable)
  2795. self.assertEqual(list(s), iterable) # Normal iteration
  2796. self.assertEqual(list(s), []) # Iterable is exhausted
  2797. s.seek(0)
  2798. self.assertEqual(list(s), iterable) # Back in action
  2799. def test_partial_reset(self):
  2800. iterable = [str(n) for n in range(10)]
  2801. s = mi.seekable(iterable)
  2802. self.assertEqual(mi.take(5, s), iterable[:5]) # Normal iteration
  2803. s.seek(1)
  2804. self.assertEqual(list(s), iterable[1:]) # Get the rest of the iterable
  2805. def test_forward(self):
  2806. iterable = [str(n) for n in range(10)]
  2807. s = mi.seekable(iterable)
  2808. self.assertEqual(mi.take(1, s), iterable[:1]) # Normal iteration
  2809. s.seek(3) # Skip over index 2
  2810. self.assertEqual(list(s), iterable[3:]) # Result is similar to slicing
  2811. s.seek(0) # Back to 0
  2812. self.assertEqual(list(s), iterable) # No difference in result
  2813. def test_past_end(self):
  2814. iterable = [str(n) for n in range(10)]
  2815. s = mi.seekable(iterable)
  2816. self.assertEqual(mi.take(1, s), iterable[:1]) # Normal iteration
  2817. s.seek(20)
  2818. self.assertEqual(list(s), []) # Iterable is exhausted
  2819. s.seek(0) # Back to 0
  2820. self.assertEqual(list(s), iterable) # No difference in result
  2821. def test_elements(self):
  2822. iterable = map(str, count())
  2823. s = mi.seekable(iterable)
  2824. mi.take(10, s)
  2825. elements = s.elements()
  2826. self.assertEqual(
  2827. [elements[i] for i in range(10)], [str(n) for n in range(10)]
  2828. )
  2829. self.assertEqual(len(elements), 10)
  2830. mi.take(10, s)
  2831. self.assertEqual(list(elements), [str(n) for n in range(20)])
  2832. def test_maxlen(self):
  2833. iterable = map(str, count())
  2834. s = mi.seekable(iterable, maxlen=4)
  2835. self.assertEqual(mi.take(10, s), [str(n) for n in range(10)])
  2836. self.assertEqual(list(s.elements()), ['6', '7', '8', '9'])
  2837. s.seek(0)
  2838. self.assertEqual(mi.take(14, s), [str(n) for n in range(6, 20)])
  2839. self.assertEqual(list(s.elements()), ['16', '17', '18', '19'])
  2840. def test_maxlen_zero(self):
  2841. iterable = [str(x) for x in range(5)]
  2842. s = mi.seekable(iterable, maxlen=0)
  2843. self.assertEqual(list(s), iterable)
  2844. self.assertEqual(list(s.elements()), [])
  2845. def test_relative_seek(self):
  2846. iterable = [str(x) for x in range(5)]
  2847. s = mi.seekable(iterable)
  2848. s.relative_seek(2)
  2849. self.assertEqual(next(s), '2')
  2850. s.relative_seek(-2)
  2851. self.assertEqual(next(s), '1')
  2852. s.relative_seek(-10) # Lower bound
  2853. self.assertEqual(next(s), '0')
  2854. s.relative_seek(10) # Lower bound
  2855. self.assertEqual(list(s.elements()), [str(x) for x in range(5)])
  2856. class SequenceViewTests(TestCase):
  2857. def test_init(self):
  2858. view = mi.SequenceView((1, 2, 3))
  2859. self.assertEqual(repr(view), "SequenceView((1, 2, 3))")
  2860. self.assertRaises(TypeError, lambda: mi.SequenceView({}))
  2861. def test_update(self):
  2862. seq = [1, 2, 3]
  2863. view = mi.SequenceView(seq)
  2864. self.assertEqual(len(view), 3)
  2865. self.assertEqual(repr(view), "SequenceView([1, 2, 3])")
  2866. seq.pop()
  2867. self.assertEqual(len(view), 2)
  2868. self.assertEqual(repr(view), "SequenceView([1, 2])")
  2869. def test_indexing(self):
  2870. seq = ('a', 'b', 'c', 'd', 'e', 'f')
  2871. view = mi.SequenceView(seq)
  2872. for i in range(-len(seq), len(seq)):
  2873. self.assertEqual(view[i], seq[i])
  2874. def test_slicing(self):
  2875. seq = ('a', 'b', 'c', 'd', 'e', 'f')
  2876. view = mi.SequenceView(seq)
  2877. n = len(seq)
  2878. indexes = list(range(-n - 1, n + 1)) + [None]
  2879. steps = list(range(-n, n + 1))
  2880. steps.remove(0)
  2881. for slice_args in product(indexes, indexes, steps):
  2882. i = slice(*slice_args)
  2883. self.assertEqual(view[i], seq[i])
  2884. def test_abc_methods(self):
  2885. # collections.Sequence should provide all of this functionality
  2886. seq = ('a', 'b', 'c', 'd', 'e', 'f', 'f')
  2887. view = mi.SequenceView(seq)
  2888. # __contains__
  2889. self.assertIn('b', view)
  2890. self.assertNotIn('g', view)
  2891. # __iter__
  2892. self.assertEqual(list(iter(view)), list(seq))
  2893. # __reversed__
  2894. self.assertEqual(list(reversed(view)), list(reversed(seq)))
  2895. # index
  2896. self.assertEqual(view.index('b'), 1)
  2897. # count
  2898. self.assertEqual(seq.count('f'), 2)
  2899. class RunLengthTest(TestCase):
  2900. def test_encode(self):
  2901. iterable = (int(str(n)[0]) for n in count(800))
  2902. actual = mi.take(4, mi.run_length.encode(iterable))
  2903. expected = [(8, 100), (9, 100), (1, 1000), (2, 1000)]
  2904. self.assertEqual(actual, expected)
  2905. def test_decode(self):
  2906. iterable = [('d', 4), ('c', 3), ('b', 2), ('a', 1)]
  2907. actual = ''.join(mi.run_length.decode(iterable))
  2908. expected = 'ddddcccbba'
  2909. self.assertEqual(actual, expected)
  2910. class ExactlyNTests(TestCase):
  2911. """Tests for ``exactly_n()``"""
  2912. def test_true(self):
  2913. """Iterable has ``n`` ``True`` elements"""
  2914. self.assertTrue(mi.exactly_n([True, False, True], 2))
  2915. self.assertTrue(mi.exactly_n([1, 1, 1, 0], 3))
  2916. self.assertTrue(mi.exactly_n([False, False], 0))
  2917. self.assertTrue(mi.exactly_n(range(100), 10, lambda x: x < 10))
  2918. def test_false(self):
  2919. """Iterable does not have ``n`` ``True`` elements"""
  2920. self.assertFalse(mi.exactly_n([True, False, False], 2))
  2921. self.assertFalse(mi.exactly_n([True, True, False], 1))
  2922. self.assertFalse(mi.exactly_n([False], 1))
  2923. self.assertFalse(mi.exactly_n([True], -1))
  2924. self.assertFalse(mi.exactly_n(repeat(True), 100))
  2925. def test_empty(self):
  2926. """Return ``True`` if the iterable is empty and ``n`` is 0"""
  2927. self.assertTrue(mi.exactly_n([], 0))
  2928. self.assertFalse(mi.exactly_n([], 1))
  2929. class AlwaysReversibleTests(TestCase):
  2930. """Tests for ``always_reversible()``"""
  2931. def test_regular_reversed(self):
  2932. self.assertEqual(
  2933. list(reversed(range(10))), list(mi.always_reversible(range(10)))
  2934. )
  2935. self.assertEqual(
  2936. list(reversed([1, 2, 3])), list(mi.always_reversible([1, 2, 3]))
  2937. )
  2938. self.assertEqual(
  2939. reversed([1, 2, 3]).__class__,
  2940. mi.always_reversible([1, 2, 3]).__class__,
  2941. )
  2942. def test_nonseq_reversed(self):
  2943. # Create a non-reversible generator from a sequence
  2944. with self.assertRaises(TypeError):
  2945. reversed(x for x in range(10))
  2946. self.assertEqual(
  2947. list(reversed(range(10))),
  2948. list(mi.always_reversible(x for x in range(10))),
  2949. )
  2950. self.assertEqual(
  2951. list(reversed([1, 2, 3])),
  2952. list(mi.always_reversible(x for x in [1, 2, 3])),
  2953. )
  2954. self.assertNotEqual(
  2955. reversed((1, 2)).__class__,
  2956. mi.always_reversible(x for x in (1, 2)).__class__,
  2957. )
  2958. class CircularShiftsTests(TestCase):
  2959. def test_empty(self):
  2960. # empty iterable -> empty list
  2961. self.assertEqual(list(mi.circular_shifts([])), [])
  2962. def test_simple_circular_shifts(self):
  2963. # test the a simple iterator case
  2964. self.assertEqual(
  2965. mi.circular_shifts(range(4)),
  2966. [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)],
  2967. )
  2968. def test_duplicates(self):
  2969. # test non-distinct entries
  2970. self.assertEqual(
  2971. mi.circular_shifts([0, 1, 0, 1]),
  2972. [(0, 1, 0, 1), (1, 0, 1, 0), (0, 1, 0, 1), (1, 0, 1, 0)],
  2973. )
  2974. class MakeDecoratorTests(TestCase):
  2975. def test_basic(self):
  2976. slicer = mi.make_decorator(islice)
  2977. @slicer(1, 10, 2)
  2978. def user_function(arg_1, arg_2, kwarg_1=None):
  2979. self.assertEqual(arg_1, 'arg_1')
  2980. self.assertEqual(arg_2, 'arg_2')
  2981. self.assertEqual(kwarg_1, 'kwarg_1')
  2982. return map(str, count())
  2983. it = user_function('arg_1', 'arg_2', kwarg_1='kwarg_1')
  2984. actual = list(it)
  2985. expected = ['1', '3', '5', '7', '9']
  2986. self.assertEqual(actual, expected)
  2987. def test_result_index(self):
  2988. def stringify(*args, **kwargs):
  2989. self.assertEqual(args[0], 'arg_0')
  2990. iterable = args[1]
  2991. self.assertEqual(args[2], 'arg_2')
  2992. self.assertEqual(kwargs['kwarg_1'], 'kwarg_1')
  2993. return map(str, iterable)
  2994. stringifier = mi.make_decorator(stringify, result_index=1)
  2995. @stringifier('arg_0', 'arg_2', kwarg_1='kwarg_1')
  2996. def user_function(n):
  2997. return count(n)
  2998. it = user_function(1)
  2999. actual = mi.take(5, it)
  3000. expected = ['1', '2', '3', '4', '5']
  3001. self.assertEqual(actual, expected)
  3002. def test_wrap_class(self):
  3003. seeker = mi.make_decorator(mi.seekable)
  3004. @seeker()
  3005. def user_function(n):
  3006. return map(str, range(n))
  3007. it = user_function(5)
  3008. self.assertEqual(list(it), ['0', '1', '2', '3', '4'])
  3009. it.seek(0)
  3010. self.assertEqual(list(it), ['0', '1', '2', '3', '4'])
  3011. class MapReduceTests(TestCase):
  3012. def test_default(self):
  3013. iterable = (str(x) for x in range(5))
  3014. keyfunc = lambda x: int(x) // 2
  3015. actual = sorted(mi.map_reduce(iterable, keyfunc).items())
  3016. expected = [(0, ['0', '1']), (1, ['2', '3']), (2, ['4'])]
  3017. self.assertEqual(actual, expected)
  3018. def test_valuefunc(self):
  3019. iterable = (str(x) for x in range(5))
  3020. keyfunc = lambda x: int(x) // 2
  3021. valuefunc = int
  3022. actual = sorted(mi.map_reduce(iterable, keyfunc, valuefunc).items())
  3023. expected = [(0, [0, 1]), (1, [2, 3]), (2, [4])]
  3024. self.assertEqual(actual, expected)
  3025. def test_reducefunc(self):
  3026. iterable = (str(x) for x in range(5))
  3027. keyfunc = lambda x: int(x) // 2
  3028. valuefunc = int
  3029. reducefunc = lambda value_list: reduce(mul, value_list, 1)
  3030. actual = sorted(
  3031. mi.map_reduce(iterable, keyfunc, valuefunc, reducefunc).items()
  3032. )
  3033. expected = [(0, 0), (1, 6), (2, 4)]
  3034. self.assertEqual(actual, expected)
  3035. def test_ret(self):
  3036. d = mi.map_reduce([1, 0, 2, 0, 1, 0], bool)
  3037. self.assertEqual(d, {False: [0, 0, 0], True: [1, 2, 1]})
  3038. self.assertRaises(KeyError, lambda: d[None].append(1))
  3039. class RlocateTests(TestCase):
  3040. def test_default_pred(self):
  3041. iterable = [0, 1, 1, 0, 1, 0, 0]
  3042. for it in (iterable[:], iter(iterable)):
  3043. actual = list(mi.rlocate(it))
  3044. expected = [4, 2, 1]
  3045. self.assertEqual(actual, expected)
  3046. def test_no_matches(self):
  3047. iterable = [0, 0, 0]
  3048. for it in (iterable[:], iter(iterable)):
  3049. actual = list(mi.rlocate(it))
  3050. expected = []
  3051. self.assertEqual(actual, expected)
  3052. def test_custom_pred(self):
  3053. iterable = ['0', 1, 1, '0', 1, '0', '0']
  3054. pred = lambda x: x == '0'
  3055. for it in (iterable[:], iter(iterable)):
  3056. actual = list(mi.rlocate(it, pred))
  3057. expected = [6, 5, 3, 0]
  3058. self.assertEqual(actual, expected)
  3059. def test_efficient_reversal(self):
  3060. iterable = range(9**9) # Is efficiently reversible
  3061. target = 9**9 - 2
  3062. pred = lambda x: x == target # Find-able from the right
  3063. actual = next(mi.rlocate(iterable, pred))
  3064. self.assertEqual(actual, target)
  3065. def test_window_size(self):
  3066. iterable = ['0', 1, 1, '0', 1, '0', '0']
  3067. pred = lambda *args: args == ('0', 1)
  3068. for it in (iterable, iter(iterable)):
  3069. actual = list(mi.rlocate(it, pred, window_size=2))
  3070. expected = [3, 0]
  3071. self.assertEqual(actual, expected)
  3072. def test_window_size_large(self):
  3073. iterable = [1, 2, 3, 4]
  3074. pred = lambda a, b, c, d, e: True
  3075. for it in (iterable, iter(iterable)):
  3076. actual = list(mi.rlocate(iterable, pred, window_size=5))
  3077. expected = [0]
  3078. self.assertEqual(actual, expected)
  3079. def test_window_size_zero(self):
  3080. iterable = [1, 2, 3, 4]
  3081. pred = lambda: True
  3082. for it in (iterable, iter(iterable)):
  3083. with self.assertRaises(ValueError):
  3084. list(mi.locate(iterable, pred, window_size=0))
  3085. class ReplaceTests(TestCase):
  3086. def test_basic(self):
  3087. iterable = range(10)
  3088. pred = lambda x: x % 2 == 0
  3089. substitutes = []
  3090. actual = list(mi.replace(iterable, pred, substitutes))
  3091. expected = [1, 3, 5, 7, 9]
  3092. self.assertEqual(actual, expected)
  3093. def test_count(self):
  3094. iterable = range(10)
  3095. pred = lambda x: x % 2 == 0
  3096. substitutes = []
  3097. actual = list(mi.replace(iterable, pred, substitutes, count=4))
  3098. expected = [1, 3, 5, 7, 8, 9]
  3099. self.assertEqual(actual, expected)
  3100. def test_window_size(self):
  3101. iterable = range(10)
  3102. pred = lambda *args: args == (0, 1, 2)
  3103. substitutes = []
  3104. actual = list(mi.replace(iterable, pred, substitutes, window_size=3))
  3105. expected = [3, 4, 5, 6, 7, 8, 9]
  3106. self.assertEqual(actual, expected)
  3107. def test_window_size_end(self):
  3108. iterable = range(10)
  3109. pred = lambda *args: args == (7, 8, 9)
  3110. substitutes = []
  3111. actual = list(mi.replace(iterable, pred, substitutes, window_size=3))
  3112. expected = [0, 1, 2, 3, 4, 5, 6]
  3113. self.assertEqual(actual, expected)
  3114. def test_window_size_count(self):
  3115. iterable = range(10)
  3116. pred = lambda *args: (args == (0, 1, 2)) or (args == (7, 8, 9))
  3117. substitutes = []
  3118. actual = list(
  3119. mi.replace(iterable, pred, substitutes, count=1, window_size=3)
  3120. )
  3121. expected = [3, 4, 5, 6, 7, 8, 9]
  3122. self.assertEqual(actual, expected)
  3123. def test_window_size_large(self):
  3124. iterable = range(4)
  3125. pred = lambda a, b, c, d, e: True
  3126. substitutes = [5, 6, 7]
  3127. actual = list(mi.replace(iterable, pred, substitutes, window_size=5))
  3128. expected = [5, 6, 7]
  3129. self.assertEqual(actual, expected)
  3130. def test_window_size_zero(self):
  3131. iterable = range(10)
  3132. pred = lambda *args: True
  3133. substitutes = []
  3134. with self.assertRaises(ValueError):
  3135. list(mi.replace(iterable, pred, substitutes, window_size=0))
  3136. def test_iterable_substitutes(self):
  3137. iterable = range(5)
  3138. pred = lambda x: x % 2 == 0
  3139. substitutes = iter('__')
  3140. actual = list(mi.replace(iterable, pred, substitutes))
  3141. expected = ['_', '_', 1, '_', '_', 3, '_', '_']
  3142. self.assertEqual(actual, expected)
  3143. class PartitionsTest(TestCase):
  3144. def test_types(self):
  3145. for iterable in ['abcd', ['a', 'b', 'c', 'd'], ('a', 'b', 'c', 'd')]:
  3146. with self.subTest(iterable=iterable):
  3147. actual = list(mi.partitions(iterable))
  3148. expected = [
  3149. [['a', 'b', 'c', 'd']],
  3150. [['a'], ['b', 'c', 'd']],
  3151. [['a', 'b'], ['c', 'd']],
  3152. [['a', 'b', 'c'], ['d']],
  3153. [['a'], ['b'], ['c', 'd']],
  3154. [['a'], ['b', 'c'], ['d']],
  3155. [['a', 'b'], ['c'], ['d']],
  3156. [['a'], ['b'], ['c'], ['d']],
  3157. ]
  3158. self.assertEqual(actual, expected)
  3159. def test_empty(self):
  3160. iterable = []
  3161. actual = list(mi.partitions(iterable))
  3162. expected = [[[]]]
  3163. self.assertEqual(actual, expected)
  3164. def test_order(self):
  3165. iterable = iter([3, 2, 1])
  3166. actual = list(mi.partitions(iterable))
  3167. expected = [[[3, 2, 1]], [[3], [2, 1]], [[3, 2], [1]], [[3], [2], [1]]]
  3168. self.assertEqual(actual, expected)
  3169. def test_duplicates(self):
  3170. iterable = [1, 1, 1]
  3171. actual = list(mi.partitions(iterable))
  3172. expected = [[[1, 1, 1]], [[1], [1, 1]], [[1, 1], [1]], [[1], [1], [1]]]
  3173. self.assertEqual(actual, expected)
  3174. class _FrozenMultiset(Set):
  3175. """
  3176. A helper class, useful to compare two lists without reference to the order
  3177. of elements.
  3178. FrozenMultiset represents a hashable set that allows duplicate elements.
  3179. """
  3180. def __init__(self, iterable):
  3181. self._collection = frozenset(Counter(iterable).items())
  3182. def __contains__(self, y):
  3183. """
  3184. >>> (0, 1) in _FrozenMultiset([(0, 1), (2,), (0, 1)])
  3185. True
  3186. """
  3187. return any(y == x for x, _ in self._collection)
  3188. def __iter__(self):
  3189. """
  3190. >>> sorted(_FrozenMultiset([(0, 1), (2,), (0, 1)]))
  3191. [(0, 1), (0, 1), (2,)]
  3192. """
  3193. return (x for x, c in self._collection for _ in range(c))
  3194. def __len__(self):
  3195. """
  3196. >>> len(_FrozenMultiset([(0, 1), (2,), (0, 1)]))
  3197. 3
  3198. """
  3199. return sum(c for x, c in self._collection)
  3200. def has_duplicates(self):
  3201. """
  3202. >>> _FrozenMultiset([(0, 1), (2,), (0, 1)]).has_duplicates()
  3203. True
  3204. """
  3205. return any(c != 1 for _, c in self._collection)
  3206. def __hash__(self):
  3207. return hash(self._collection)
  3208. def __repr__(self):
  3209. return "FrozenSet([{}]".format(", ".join(repr(x) for x in iter(self)))
  3210. class SetPartitionsTests(TestCase):
  3211. @staticmethod
  3212. def _normalize_partition(p):
  3213. """
  3214. Return a normalized, hashable, version of a partition using
  3215. _FrozenMultiset
  3216. """
  3217. return _FrozenMultiset(_FrozenMultiset(g) for g in p)
  3218. @staticmethod
  3219. def _normalize_partitions(ps):
  3220. """
  3221. Return a normalized set of all normalized partitions using
  3222. _FrozenMultiset
  3223. """
  3224. return _FrozenMultiset(
  3225. SetPartitionsTests._normalize_partition(p) for p in ps
  3226. )
  3227. def test_repeated(self):
  3228. it = 'aaa'
  3229. actual = mi.set_partitions(it, 2)
  3230. expected = [['a', 'aa'], ['a', 'aa'], ['a', 'aa']]
  3231. self.assertEqual(
  3232. self._normalize_partitions(expected),
  3233. self._normalize_partitions(actual),
  3234. )
  3235. def test_each_correct(self):
  3236. a = set(range(6))
  3237. for p in mi.set_partitions(a):
  3238. total = {e for g in p for e in g}
  3239. self.assertEqual(a, total)
  3240. def test_duplicates(self):
  3241. a = set(range(6))
  3242. for p in mi.set_partitions(a):
  3243. self.assertFalse(self._normalize_partition(p).has_duplicates())
  3244. def test_found_all(self):
  3245. """small example, hand-checked"""
  3246. expected = [
  3247. [[0], [1], [2, 3, 4]],
  3248. [[0], [1, 2], [3, 4]],
  3249. [[0], [2], [1, 3, 4]],
  3250. [[0], [3], [1, 2, 4]],
  3251. [[0], [4], [1, 2, 3]],
  3252. [[0], [1, 3], [2, 4]],
  3253. [[0], [1, 4], [2, 3]],
  3254. [[1], [2], [0, 3, 4]],
  3255. [[1], [3], [0, 2, 4]],
  3256. [[1], [4], [0, 2, 3]],
  3257. [[1], [0, 2], [3, 4]],
  3258. [[1], [0, 3], [2, 4]],
  3259. [[1], [0, 4], [2, 3]],
  3260. [[2], [3], [0, 1, 4]],
  3261. [[2], [4], [0, 1, 3]],
  3262. [[2], [0, 1], [3, 4]],
  3263. [[2], [0, 3], [1, 4]],
  3264. [[2], [0, 4], [1, 3]],
  3265. [[3], [4], [0, 1, 2]],
  3266. [[3], [0, 1], [2, 4]],
  3267. [[3], [0, 2], [1, 4]],
  3268. [[3], [0, 4], [1, 2]],
  3269. [[4], [0, 1], [2, 3]],
  3270. [[4], [0, 2], [1, 3]],
  3271. [[4], [0, 3], [1, 2]],
  3272. ]
  3273. actual = mi.set_partitions(range(5), 3)
  3274. self.assertEqual(
  3275. self._normalize_partitions(expected),
  3276. self._normalize_partitions(actual),
  3277. )
  3278. def test_stirling_numbers(self):
  3279. """Check against https://en.wikipedia.org/wiki/
  3280. Stirling_numbers_of_the_second_kind#Table_of_values"""
  3281. cardinality_by_k_by_n = [
  3282. [1],
  3283. [1, 1],
  3284. [1, 3, 1],
  3285. [1, 7, 6, 1],
  3286. [1, 15, 25, 10, 1],
  3287. [1, 31, 90, 65, 15, 1],
  3288. ]
  3289. for n, cardinality_by_k in enumerate(cardinality_by_k_by_n, 1):
  3290. for k, cardinality in enumerate(cardinality_by_k, 1):
  3291. self.assertEqual(
  3292. cardinality, len(list(mi.set_partitions(range(n), k)))
  3293. )
  3294. def test_no_group(self):
  3295. def helper():
  3296. list(mi.set_partitions(range(4), -1))
  3297. self.assertRaises(ValueError, helper)
  3298. def test_to_many_groups(self):
  3299. self.assertEqual([], list(mi.set_partitions(range(4), 5)))
  3300. class TimeLimitedTests(TestCase):
  3301. def test_basic(self):
  3302. def generator():
  3303. yield 1
  3304. yield 2
  3305. sleep(0.2)
  3306. yield 3
  3307. iterable = mi.time_limited(0.1, generator())
  3308. actual = list(iterable)
  3309. expected = [1, 2]
  3310. self.assertEqual(actual, expected)
  3311. self.assertTrue(iterable.timed_out)
  3312. def test_complete(self):
  3313. iterable = mi.time_limited(2, iter(range(10)))
  3314. actual = list(iterable)
  3315. expected = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  3316. self.assertEqual(actual, expected)
  3317. self.assertFalse(iterable.timed_out)
  3318. def test_zero_limit(self):
  3319. iterable = mi.time_limited(0, count())
  3320. actual = list(iterable)
  3321. expected = []
  3322. self.assertEqual(actual, expected)
  3323. self.assertTrue(iterable.timed_out)
  3324. def test_invalid_limit(self):
  3325. with self.assertRaises(ValueError):
  3326. list(mi.time_limited(-0.1, count()))
  3327. class OnlyTests(TestCase):
  3328. def test_defaults(self):
  3329. self.assertEqual(mi.only([]), None)
  3330. self.assertEqual(mi.only([1]), 1)
  3331. self.assertRaises(ValueError, lambda: mi.only([1, 2]))
  3332. def test_custom_value(self):
  3333. self.assertEqual(mi.only([], default='!'), '!')
  3334. self.assertEqual(mi.only([1], default='!'), 1)
  3335. self.assertRaises(ValueError, lambda: mi.only([1, 2], default='!'))
  3336. def test_custom_exception(self):
  3337. self.assertEqual(mi.only([], too_long=RuntimeError), None)
  3338. self.assertEqual(mi.only([1], too_long=RuntimeError), 1)
  3339. self.assertRaises(
  3340. RuntimeError, lambda: mi.only([1, 2], too_long=RuntimeError)
  3341. )
  3342. def test_default_exception_message(self):
  3343. self.assertRaisesRegex(
  3344. ValueError,
  3345. "Expected exactly one item in iterable, "
  3346. "but got 'foo', 'bar', and perhaps more",
  3347. lambda: mi.only(['foo', 'bar', 'baz']),
  3348. )
  3349. class IchunkedTests(TestCase):
  3350. def test_even(self):
  3351. iterable = (str(x) for x in range(10))
  3352. actual = [''.join(c) for c in mi.ichunked(iterable, 5)]
  3353. expected = ['01234', '56789']
  3354. self.assertEqual(actual, expected)
  3355. def test_odd(self):
  3356. iterable = (str(x) for x in range(10))
  3357. actual = [''.join(c) for c in mi.ichunked(iterable, 4)]
  3358. expected = ['0123', '4567', '89']
  3359. self.assertEqual(actual, expected)
  3360. def test_zero(self):
  3361. iterable = []
  3362. actual = [list(c) for c in mi.ichunked(iterable, 0)]
  3363. expected = []
  3364. self.assertEqual(actual, expected)
  3365. def test_negative(self):
  3366. iterable = count()
  3367. with self.assertRaises(ValueError):
  3368. [list(c) for c in mi.ichunked(iterable, -1)]
  3369. def test_out_of_order(self):
  3370. iterable = map(str, count())
  3371. it = mi.ichunked(iterable, 4)
  3372. chunk_1 = next(it)
  3373. chunk_2 = next(it)
  3374. self.assertEqual(''.join(chunk_2), '4567')
  3375. self.assertEqual(''.join(chunk_1), '0123')
  3376. def test_laziness(self):
  3377. def gen():
  3378. yield 0
  3379. raise RuntimeError
  3380. yield from count(1)
  3381. it = mi.ichunked(gen(), 4)
  3382. chunk = next(it)
  3383. self.assertEqual(next(chunk), 0)
  3384. self.assertRaises(RuntimeError, next, it)
  3385. def test_memory_in_order(self):
  3386. gen_numbers = []
  3387. def gen():
  3388. for gen_number in count():
  3389. gen_numbers.append(gen_number)
  3390. yield gen_number
  3391. # No items should be kept in memory when a ichunked is first called
  3392. all_chunks = mi.ichunked(gen(), 4)
  3393. self.assertEqual(gen_numbers, [])
  3394. # The first item of each chunk should be generated on chunk generation
  3395. first_chunk = next(all_chunks)
  3396. self.assertEqual(gen_numbers, [0])
  3397. # If we don't read a chunk before getting its successor, its contents
  3398. # will be cached
  3399. second_chunk = next(all_chunks)
  3400. self.assertEqual(gen_numbers, [0, 1, 2, 3, 4])
  3401. # Check if we can read in cached values
  3402. self.assertEqual(list(first_chunk), [0, 1, 2, 3])
  3403. self.assertEqual(list(second_chunk), [4, 5, 6, 7])
  3404. # Again only the most recent chunk should have an item cached
  3405. third_chunk = next(all_chunks)
  3406. self.assertEqual(len(gen_numbers), 9)
  3407. # No new item should be cached when reading past the first number
  3408. next(third_chunk)
  3409. self.assertEqual(len(gen_numbers), 9)
  3410. # we should not be able to read spent chunks
  3411. self.assertEqual(list(first_chunk), [])
  3412. self.assertEqual(list(second_chunk), [])
  3413. class DistinctCombinationsTests(TestCase):
  3414. def test_basic(self):
  3415. for iterable in [
  3416. (1, 2, 2, 3, 3, 3), # In order
  3417. range(6), # All distinct
  3418. 'abbccc', # Not numbers
  3419. 'cccbba', # Backward
  3420. 'mississippi', # No particular order
  3421. ]:
  3422. for r in range(len(iterable)):
  3423. with self.subTest(iterable=iterable, r=r):
  3424. actual = list(mi.distinct_combinations(iterable, r))
  3425. expected = list(
  3426. mi.unique_everseen(combinations(iterable, r))
  3427. )
  3428. self.assertEqual(actual, expected)
  3429. def test_negative(self):
  3430. with self.assertRaises(ValueError):
  3431. list(mi.distinct_combinations([], -1))
  3432. def test_empty(self):
  3433. self.assertEqual(list(mi.distinct_combinations([], 2)), [])
  3434. class FilterExceptTests(TestCase):
  3435. def test_no_exceptions_pass(self):
  3436. iterable = '0123'
  3437. actual = list(mi.filter_except(int, iterable))
  3438. expected = ['0', '1', '2', '3']
  3439. self.assertEqual(actual, expected)
  3440. def test_no_exceptions_raise(self):
  3441. iterable = ['0', '1', 'two', '3']
  3442. with self.assertRaises(ValueError):
  3443. list(mi.filter_except(int, iterable))
  3444. def test_raise(self):
  3445. iterable = ['0', '1' '2', 'three', None]
  3446. with self.assertRaises(TypeError):
  3447. list(mi.filter_except(int, iterable, ValueError))
  3448. def test_false(self):
  3449. # Even if the validator returns false, we pass through
  3450. validator = lambda x: False
  3451. iterable = ['0', '1', '2', 'three', None]
  3452. actual = list(mi.filter_except(validator, iterable, Exception))
  3453. expected = ['0', '1', '2', 'three', None]
  3454. self.assertEqual(actual, expected)
  3455. def test_multiple(self):
  3456. iterable = ['0', '1', '2', 'three', None, '4']
  3457. actual = list(mi.filter_except(int, iterable, ValueError, TypeError))
  3458. expected = ['0', '1', '2', '4']
  3459. self.assertEqual(actual, expected)
  3460. class MapExceptTests(TestCase):
  3461. def test_no_exceptions_pass(self):
  3462. iterable = '0123'
  3463. actual = list(mi.map_except(int, iterable))
  3464. expected = [0, 1, 2, 3]
  3465. self.assertEqual(actual, expected)
  3466. def test_no_exceptions_raise(self):
  3467. iterable = ['0', '1', 'two', '3']
  3468. with self.assertRaises(ValueError):
  3469. list(mi.map_except(int, iterable))
  3470. def test_raise(self):
  3471. iterable = ['0', '1' '2', 'three', None]
  3472. with self.assertRaises(TypeError):
  3473. list(mi.map_except(int, iterable, ValueError))
  3474. def test_multiple(self):
  3475. iterable = ['0', '1', '2', 'three', None, '4']
  3476. actual = list(mi.map_except(int, iterable, ValueError, TypeError))
  3477. expected = [0, 1, 2, 4]
  3478. self.assertEqual(actual, expected)
  3479. class MapIfTests(TestCase):
  3480. def test_without_func_else(self):
  3481. iterable = list(range(-5, 5))
  3482. actual = list(mi.map_if(iterable, lambda x: x > 3, lambda x: 'toobig'))
  3483. expected = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 'toobig']
  3484. self.assertEqual(actual, expected)
  3485. def test_with_func_else(self):
  3486. iterable = list(range(-5, 5))
  3487. actual = list(
  3488. mi.map_if(
  3489. iterable, lambda x: x >= 0, lambda x: 'notneg', lambda x: 'neg'
  3490. )
  3491. )
  3492. expected = ['neg'] * 5 + ['notneg'] * 5
  3493. self.assertEqual(actual, expected)
  3494. def test_empty(self):
  3495. actual = list(mi.map_if([], lambda x: len(x) > 5, lambda x: None))
  3496. expected = []
  3497. self.assertEqual(actual, expected)
  3498. class SampleTests(TestCase):
  3499. def test_unit_case(self):
  3500. """Test against a fixed case by seeding the random module."""
  3501. # Beware that this test really just verifies random.random() behavior.
  3502. # If the algorithm is changed (e.g. to a more naive implementation)
  3503. # this test will fail, but the algorithm might be correct.
  3504. # Also, this test can pass and the algorithm can be completely wrong.
  3505. data = "abcdef"
  3506. weights = list(range(1, len(data) + 1))
  3507. seed(123)
  3508. actual = mi.sample(data, k=2, weights=weights)
  3509. expected = ['f', 'e']
  3510. self.assertEqual(actual, expected)
  3511. def test_length(self):
  3512. """Check that *k* elements are sampled."""
  3513. data = [1, 2, 3, 4, 5]
  3514. for k in [0, 3, 5, 7]:
  3515. sampled = mi.sample(data, k=k)
  3516. actual = len(sampled)
  3517. expected = min(k, len(data))
  3518. self.assertEqual(actual, expected)
  3519. def test_sampling_entire_iterable(self):
  3520. """If k=len(iterable), the sample contains the original elements."""
  3521. data = ["a", 2, "a", 4, (1, 2, 3)]
  3522. actual = set(mi.sample(data, k=len(data)))
  3523. expected = set(data)
  3524. self.assertEqual(actual, expected)
  3525. def test_scale_invariance_of_weights(self):
  3526. """The probability of choosing element a_i is w_i / sum(weights).
  3527. Scaling weights should not change the probability or outcome."""
  3528. data = "abcdef"
  3529. weights = list(range(1, len(data) + 1))
  3530. seed(123)
  3531. first_sample = mi.sample(data, k=2, weights=weights)
  3532. # Scale the weights and sample again
  3533. weights_scaled = [w / 1e10 for w in weights]
  3534. seed(123)
  3535. second_sample = mi.sample(data, k=2, weights=weights_scaled)
  3536. self.assertEqual(first_sample, second_sample)
  3537. def test_invariance_under_permutations_unweighted(self):
  3538. """The order of the data should not matter. This is a stochastic test,
  3539. but it will fail in less than 1 / 10_000 cases."""
  3540. # Create a data set and a reversed data set
  3541. data = list(range(100))
  3542. data_rev = list(reversed(data))
  3543. # Sample each data set 10 times
  3544. data_means = [mean(mi.sample(data, k=50)) for _ in range(10)]
  3545. data_rev_means = [mean(mi.sample(data_rev, k=50)) for _ in range(10)]
  3546. # The difference in the means should be low, i.e. little bias
  3547. difference_in_means = abs(mean(data_means) - mean(data_rev_means))
  3548. # The observed largest difference in 10,000 simulations was 5.09599
  3549. self.assertTrue(difference_in_means < 5.1)
  3550. def test_invariance_under_permutations_weighted(self):
  3551. """The order of the data should not matter. This is a stochastic test,
  3552. but it will fail in less than 1 / 10_000 cases."""
  3553. # Create a data set and a reversed data set
  3554. data = list(range(1, 101))
  3555. data_rev = list(reversed(data))
  3556. # Sample each data set 10 times
  3557. data_means = [
  3558. mean(mi.sample(data, k=50, weights=data)) for _ in range(10)
  3559. ]
  3560. data_rev_means = [
  3561. mean(mi.sample(data_rev, k=50, weights=data_rev))
  3562. for _ in range(10)
  3563. ]
  3564. # The difference in the means should be low, i.e. little bias
  3565. difference_in_means = abs(mean(data_means) - mean(data_rev_means))
  3566. # The observed largest difference in 10,000 simulations was 4.337999
  3567. self.assertTrue(difference_in_means < 4.4)
  3568. class IsSortedTests(TestCase):
  3569. def test_basic(self):
  3570. for iterable, kwargs, expected in [
  3571. ([], {}, True),
  3572. ([1], {}, True),
  3573. ([1, 2, 3], {}, True),
  3574. ([1, 1, 2, 3], {}, True),
  3575. ([1, 10, 2, 3], {}, False),
  3576. (['1', '10', '2', '3'], {}, True),
  3577. (['1', '10', '2', '3'], {'key': int}, False),
  3578. ([1, 2, 3], {'reverse': True}, False),
  3579. ([1, 1, 2, 3], {'reverse': True}, False),
  3580. ([1, 10, 2, 3], {'reverse': True}, False),
  3581. (['3', '2', '10', '1'], {'reverse': True}, True),
  3582. (['3', '2', '10', '1'], {'key': int, 'reverse': True}, False),
  3583. # strict
  3584. ([], {'strict': True}, True),
  3585. ([1], {'strict': True}, True),
  3586. ([1, 1], {'strict': True}, False),
  3587. ([1, 2, 3], {'strict': True}, True),
  3588. ([1, 1, 2, 3], {'strict': True}, False),
  3589. ([1, 10, 2, 3], {'strict': True}, False),
  3590. (['1', '10', '2', '3'], {'strict': True}, True),
  3591. (['1', '10', '2', '3', '3'], {'strict': True}, False),
  3592. (['1', '10', '2', '3'], {'strict': True, 'key': int}, False),
  3593. ([1, 2, 3], {'strict': True, 'reverse': True}, False),
  3594. ([1, 1, 2, 3], {'strict': True, 'reverse': True}, False),
  3595. ([1, 10, 2, 3], {'strict': True, 'reverse': True}, False),
  3596. (['3', '2', '10', '1'], {'strict': True, 'reverse': True}, True),
  3597. (
  3598. ['3', '2', '10', '10', '1'],
  3599. {'strict': True, 'reverse': True},
  3600. False,
  3601. ),
  3602. (
  3603. ['3', '2', '10', '1'],
  3604. {'strict': True, 'key': int, 'reverse': True},
  3605. False,
  3606. ),
  3607. # We'll do the same weird thing as Python here
  3608. (['nan', 0, 'nan', 0], {'key': float}, True),
  3609. ([0, 'nan', 0, 'nan'], {'key': float}, True),
  3610. (['nan', 0, 'nan', 0], {'key': float, 'reverse': True}, True),
  3611. ([0, 'nan', 0, 'nan'], {'key': float, 'reverse': True}, True),
  3612. ([0, 'nan', 0, 'nan'], {'strict': True, 'key': float}, True),
  3613. (
  3614. ['nan', 0, 'nan', 0],
  3615. {'strict': True, 'key': float, 'reverse': True},
  3616. True,
  3617. ),
  3618. ]:
  3619. key = kwargs.get('key', None)
  3620. reverse = kwargs.get('reverse', False)
  3621. strict = kwargs.get('strict', False)
  3622. with self.subTest(
  3623. iterable=iterable, key=key, reverse=reverse, strict=strict
  3624. ):
  3625. mi_result = mi.is_sorted(
  3626. iter(iterable), key=key, reverse=reverse, strict=strict
  3627. )
  3628. sorted_iterable = sorted(iterable, key=key, reverse=reverse)
  3629. if strict:
  3630. sorted_iterable = list(mi.unique_justseen(sorted_iterable))
  3631. py_result = iterable == sorted_iterable
  3632. self.assertEqual(mi_result, expected)
  3633. self.assertEqual(mi_result, py_result)
  3634. class CallbackIterTests(TestCase):
  3635. def _target(self, cb=None, exc=None, wait=0):
  3636. total = 0
  3637. for i, c in enumerate('abc', 1):
  3638. total += i
  3639. if wait:
  3640. sleep(wait)
  3641. if cb:
  3642. cb(i, c, intermediate_total=total)
  3643. if exc:
  3644. raise exc('error in target')
  3645. return total
  3646. def test_basic(self):
  3647. func = lambda callback=None: self._target(cb=callback, wait=0.02)
  3648. with mi.callback_iter(func, wait_seconds=0.01) as it:
  3649. # Execution doesn't start until we begin iterating
  3650. self.assertFalse(it.done)
  3651. # Consume everything
  3652. self.assertEqual(
  3653. list(it),
  3654. [
  3655. ((1, 'a'), {'intermediate_total': 1}),
  3656. ((2, 'b'), {'intermediate_total': 3}),
  3657. ((3, 'c'), {'intermediate_total': 6}),
  3658. ],
  3659. )
  3660. # After consuming everything the future is done and the
  3661. # result is available.
  3662. self.assertTrue(it.done)
  3663. self.assertEqual(it.result, 6)
  3664. # This examines the internal state of the ThreadPoolExecutor. This
  3665. # isn't documented, so may break in future Python versions.
  3666. self.assertTrue(it._executor._shutdown)
  3667. def test_callback_kwd(self):
  3668. with mi.callback_iter(self._target, callback_kwd='cb') as it:
  3669. self.assertEqual(
  3670. list(it),
  3671. [
  3672. ((1, 'a'), {'intermediate_total': 1}),
  3673. ((2, 'b'), {'intermediate_total': 3}),
  3674. ((3, 'c'), {'intermediate_total': 6}),
  3675. ],
  3676. )
  3677. def test_partial_consumption(self):
  3678. func = lambda callback=None: self._target(cb=callback)
  3679. with mi.callback_iter(func) as it:
  3680. self.assertEqual(next(it), ((1, 'a'), {'intermediate_total': 1}))
  3681. self.assertTrue(it._executor._shutdown)
  3682. def test_abort(self):
  3683. func = lambda callback=None: self._target(cb=callback, wait=0.1)
  3684. with mi.callback_iter(func) as it:
  3685. self.assertEqual(next(it), ((1, 'a'), {'intermediate_total': 1}))
  3686. with self.assertRaises(mi.AbortThread):
  3687. it.result
  3688. def test_no_result(self):
  3689. func = lambda callback=None: self._target(cb=callback)
  3690. with mi.callback_iter(func) as it:
  3691. with self.assertRaises(RuntimeError):
  3692. it.result
  3693. def test_exception(self):
  3694. func = lambda callback=None: self._target(cb=callback, exc=ValueError)
  3695. with mi.callback_iter(func) as it:
  3696. self.assertEqual(
  3697. next(it),
  3698. ((1, 'a'), {'intermediate_total': 1}),
  3699. )
  3700. with self.assertRaises(ValueError):
  3701. it.result
  3702. class WindowedCompleteTests(TestCase):
  3703. """Tests for ``windowed_complete()``"""
  3704. def test_basic(self):
  3705. actual = list(mi.windowed_complete([1, 2, 3, 4, 5], 3))
  3706. expected = [
  3707. ((), (1, 2, 3), (4, 5)),
  3708. ((1,), (2, 3, 4), (5,)),
  3709. ((1, 2), (3, 4, 5), ()),
  3710. ]
  3711. self.assertEqual(actual, expected)
  3712. def test_zero_length(self):
  3713. actual = list(mi.windowed_complete([1, 2, 3], 0))
  3714. expected = [
  3715. ((), (), (1, 2, 3)),
  3716. ((1,), (), (2, 3)),
  3717. ((1, 2), (), (3,)),
  3718. ((1, 2, 3), (), ()),
  3719. ]
  3720. self.assertEqual(actual, expected)
  3721. def test_wrong_length(self):
  3722. seq = [1, 2, 3, 4, 5]
  3723. for n in (-10, -1, len(seq) + 1, len(seq) + 10):
  3724. with self.subTest(n=n):
  3725. with self.assertRaises(ValueError):
  3726. list(mi.windowed_complete(seq, n))
  3727. def test_every_partition(self):
  3728. every_partition = lambda seq: chain(
  3729. *map(partial(mi.windowed_complete, seq), range(len(seq)))
  3730. )
  3731. seq = 'ABC'
  3732. actual = list(every_partition(seq))
  3733. expected = [
  3734. ((), (), ('A', 'B', 'C')),
  3735. (('A',), (), ('B', 'C')),
  3736. (('A', 'B'), (), ('C',)),
  3737. (('A', 'B', 'C'), (), ()),
  3738. ((), ('A',), ('B', 'C')),
  3739. (('A',), ('B',), ('C',)),
  3740. (('A', 'B'), ('C',), ()),
  3741. ((), ('A', 'B'), ('C',)),
  3742. (('A',), ('B', 'C'), ()),
  3743. ]
  3744. self.assertEqual(actual, expected)
  3745. class AllUniqueTests(TestCase):
  3746. def test_basic(self):
  3747. for iterable, expected in [
  3748. ([], True),
  3749. ([1, 2, 3], True),
  3750. ([1, 1], False),
  3751. ([1, 2, 3, 1], False),
  3752. ([1, 2, 3, '1'], True),
  3753. ]:
  3754. with self.subTest(args=(iterable,)):
  3755. self.assertEqual(mi.all_unique(iterable), expected)
  3756. def test_non_hashable(self):
  3757. self.assertEqual(mi.all_unique([[1, 2], [3, 4]]), True)
  3758. self.assertEqual(mi.all_unique([[1, 2], [3, 4], [1, 2]]), False)
  3759. def test_partially_hashable(self):
  3760. self.assertEqual(mi.all_unique([[1, 2], [3, 4], (5, 6)]), True)
  3761. self.assertEqual(
  3762. mi.all_unique([[1, 2], [3, 4], (5, 6), [1, 2]]), False
  3763. )
  3764. self.assertEqual(
  3765. mi.all_unique([[1, 2], [3, 4], (5, 6), (5, 6)]), False
  3766. )
  3767. def test_key(self):
  3768. iterable = ['A', 'B', 'C', 'b']
  3769. self.assertEqual(mi.all_unique(iterable, lambda x: x), True)
  3770. self.assertEqual(mi.all_unique(iterable, str.lower), False)
  3771. def test_infinite(self):
  3772. self.assertEqual(mi.all_unique(mi.prepend(3, count())), False)
  3773. class NthProductTests(TestCase):
  3774. def test_basic(self):
  3775. iterables = ['ab', 'cdef', 'ghi']
  3776. for index, expected in enumerate(product(*iterables)):
  3777. actual = mi.nth_product(index, *iterables)
  3778. self.assertEqual(actual, expected)
  3779. def test_long(self):
  3780. actual = mi.nth_product(1337, range(101), range(22), range(53))
  3781. expected = (1, 3, 12)
  3782. self.assertEqual(actual, expected)
  3783. def test_negative(self):
  3784. iterables = ['abc', 'de', 'fghi']
  3785. for index, expected in enumerate(product(*iterables)):
  3786. actual = mi.nth_product(index - 24, *iterables)
  3787. self.assertEqual(actual, expected)
  3788. def test_invalid_index(self):
  3789. with self.assertRaises(IndexError):
  3790. mi.nth_product(24, 'ab', 'cde', 'fghi')
  3791. class NthCombinationWithReplacementTests(TestCase):
  3792. def test_basic(self):
  3793. iterable = 'abcdefg'
  3794. r = 4
  3795. for index, expected in enumerate(
  3796. combinations_with_replacement(iterable, r)
  3797. ):
  3798. actual = mi.nth_combination_with_replacement(iterable, r, index)
  3799. self.assertEqual(actual, expected)
  3800. def test_long(self):
  3801. actual = mi.nth_combination_with_replacement(range(90), 4, 2000000)
  3802. expected = (22, 65, 68, 81)
  3803. self.assertEqual(actual, expected)
  3804. def test_invalid_r(self):
  3805. for r in (-1, 3):
  3806. with self.assertRaises(ValueError):
  3807. mi.nth_combination_with_replacement([], r, 0)
  3808. def test_invalid_index(self):
  3809. with self.assertRaises(IndexError):
  3810. mi.nth_combination_with_replacement('abcdefg', 3, -85)
  3811. class ValueChainTests(TestCase):
  3812. def test_empty(self):
  3813. actual = list(mi.value_chain())
  3814. expected = []
  3815. self.assertEqual(actual, expected)
  3816. def test_simple(self):
  3817. actual = list(mi.value_chain(1, 2.71828, False, 'foo'))
  3818. expected = [1, 2.71828, False, 'foo']
  3819. self.assertEqual(actual, expected)
  3820. def test_more(self):
  3821. actual = list(mi.value_chain(b'bar', [1, 2, 3], 4, {'key': 1}))
  3822. expected = [b'bar', 1, 2, 3, 4, 'key']
  3823. self.assertEqual(actual, expected)
  3824. def test_empty_lists(self):
  3825. actual = list(mi.value_chain(1, 2, [], [3, 4]))
  3826. expected = [1, 2, 3, 4]
  3827. self.assertEqual(actual, expected)
  3828. def test_complex(self):
  3829. obj = object()
  3830. actual = list(
  3831. mi.value_chain(
  3832. (1, (2, (3,))),
  3833. ['foo', ['bar', ['baz']], 'tic'],
  3834. {'key': {'foo': 1}},
  3835. obj,
  3836. )
  3837. )
  3838. expected = [1, (2, (3,)), 'foo', ['bar', ['baz']], 'tic', 'key', obj]
  3839. self.assertEqual(actual, expected)
  3840. class ProductIndexTests(TestCase):
  3841. def test_basic(self):
  3842. iterables = ['ab', 'cdef', 'ghi']
  3843. first_index = {}
  3844. for index, element in enumerate(product(*iterables)):
  3845. actual = mi.product_index(element, *iterables)
  3846. expected = first_index.setdefault(element, index)
  3847. self.assertEqual(actual, expected)
  3848. def test_multiplicity(self):
  3849. iterables = ['ab', 'bab', 'cab']
  3850. first_index = {}
  3851. for index, element in enumerate(product(*iterables)):
  3852. actual = mi.product_index(element, *iterables)
  3853. expected = first_index.setdefault(element, index)
  3854. self.assertEqual(actual, expected)
  3855. def test_long(self):
  3856. actual = mi.product_index((1, 3, 12), range(101), range(22), range(53))
  3857. expected = 1337
  3858. self.assertEqual(actual, expected)
  3859. def test_invalid_empty(self):
  3860. with self.assertRaises(ValueError):
  3861. mi.product_index('', 'ab', 'cde', 'fghi')
  3862. def test_invalid_small(self):
  3863. with self.assertRaises(ValueError):
  3864. mi.product_index('ac', 'ab', 'cde', 'fghi')
  3865. def test_invalid_large(self):
  3866. with self.assertRaises(ValueError):
  3867. mi.product_index('achi', 'ab', 'cde', 'fghi')
  3868. def test_invalid_match(self):
  3869. with self.assertRaises(ValueError):
  3870. mi.product_index('axf', 'ab', 'cde', 'fghi')
  3871. class CombinationIndexTests(TestCase):
  3872. def test_r_less_than_n(self):
  3873. iterable = 'abcdefg'
  3874. r = 4
  3875. first_index = {}
  3876. for index, element in enumerate(combinations(iterable, r)):
  3877. actual = mi.combination_index(element, iterable)
  3878. expected = first_index.setdefault(element, index)
  3879. self.assertEqual(actual, expected)
  3880. def test_r_equal_to_n(self):
  3881. iterable = 'abcd'
  3882. r = len(iterable)
  3883. first_index = {}
  3884. for index, element in enumerate(combinations(iterable, r=r)):
  3885. actual = mi.combination_index(element, iterable)
  3886. expected = first_index.setdefault(element, index)
  3887. self.assertEqual(actual, expected)
  3888. def test_multiplicity(self):
  3889. iterable = 'abacba'
  3890. r = 3
  3891. first_index = {}
  3892. for index, element in enumerate(combinations(iterable, r)):
  3893. actual = mi.combination_index(element, iterable)
  3894. expected = first_index.setdefault(element, index)
  3895. self.assertEqual(actual, expected)
  3896. def test_null(self):
  3897. actual = mi.combination_index(tuple(), [])
  3898. expected = 0
  3899. self.assertEqual(actual, expected)
  3900. def test_long(self):
  3901. actual = mi.combination_index((2, 12, 35, 126), range(180))
  3902. expected = 2000000
  3903. self.assertEqual(actual, expected)
  3904. def test_invalid_order(self):
  3905. with self.assertRaises(ValueError):
  3906. mi.combination_index(tuple('acb'), 'abcde')
  3907. def test_invalid_large(self):
  3908. with self.assertRaises(ValueError):
  3909. mi.combination_index(tuple('abcdefg'), 'abcdef')
  3910. def test_invalid_match(self):
  3911. with self.assertRaises(ValueError):
  3912. mi.combination_index(tuple('axe'), 'abcde')
  3913. class CombinationWithReplacementIndexTests(TestCase):
  3914. def test_r_less_than_n(self):
  3915. iterable = 'abcdefg'
  3916. r = 4
  3917. first_index = {}
  3918. for index, element in enumerate(
  3919. combinations_with_replacement(iterable, r)
  3920. ):
  3921. actual = mi.combination_with_replacement_index(element, iterable)
  3922. expected = first_index.setdefault(element, index)
  3923. self.assertEqual(actual, expected)
  3924. def test_r_equal_to_n(self):
  3925. iterable = 'abcd'
  3926. r = len(iterable)
  3927. first_index = {}
  3928. for index, element in enumerate(
  3929. combinations_with_replacement(iterable, r=r)
  3930. ):
  3931. actual = mi.combination_with_replacement_index(element, iterable)
  3932. expected = first_index.setdefault(element, index)
  3933. self.assertEqual(actual, expected)
  3934. def test_multiplicity(self):
  3935. iterable = 'abacba'
  3936. r = 3
  3937. first_index = {}
  3938. for index, element in enumerate(
  3939. combinations_with_replacement(iterable, r)
  3940. ):
  3941. actual = mi.combination_with_replacement_index(element, iterable)
  3942. expected = first_index.setdefault(element, index)
  3943. self.assertEqual(actual, expected)
  3944. def test_null(self):
  3945. actual = mi.combination_with_replacement_index(tuple(), [])
  3946. expected = 0
  3947. self.assertEqual(actual, expected)
  3948. def test_long(self):
  3949. actual = mi.combination_with_replacement_index(
  3950. (22, 65, 68, 81), range(90)
  3951. )
  3952. expected = 2000000
  3953. self.assertEqual(actual, expected)
  3954. def test_invalid_order(self):
  3955. with self.assertRaises(ValueError):
  3956. mi.combination_with_replacement_index(tuple('acb'), 'abcde')
  3957. def test_invalid_large(self):
  3958. with self.assertRaises(ValueError):
  3959. mi.combination_with_replacement_index(tuple('abcdefg'), 'abcdef')
  3960. def test_invalid_match(self):
  3961. with self.assertRaises(ValueError):
  3962. mi.combination_with_replacement_index(tuple('axe'), 'abcde')
  3963. class PermutationIndexTests(TestCase):
  3964. def test_r_less_than_n(self):
  3965. iterable = 'abcdefg'
  3966. r = 4
  3967. first_index = {}
  3968. for index, element in enumerate(permutations(iterable, r)):
  3969. actual = mi.permutation_index(element, iterable)
  3970. expected = first_index.setdefault(element, index)
  3971. self.assertEqual(actual, expected)
  3972. def test_r_equal_to_n(self):
  3973. iterable = 'abcd'
  3974. first_index = {}
  3975. for index, element in enumerate(permutations(iterable)):
  3976. actual = mi.permutation_index(element, iterable)
  3977. expected = first_index.setdefault(element, index)
  3978. self.assertEqual(actual, expected)
  3979. def test_multiplicity(self):
  3980. iterable = 'abacba'
  3981. r = 3
  3982. first_index = {}
  3983. for index, element in enumerate(permutations(iterable, r)):
  3984. actual = mi.permutation_index(element, iterable)
  3985. expected = first_index.setdefault(element, index)
  3986. self.assertEqual(actual, expected)
  3987. def test_null(self):
  3988. actual = mi.permutation_index(tuple(), [])
  3989. expected = 0
  3990. self.assertEqual(actual, expected)
  3991. def test_long(self):
  3992. actual = mi.permutation_index((2, 12, 35, 126), range(180))
  3993. expected = 11631678
  3994. self.assertEqual(actual, expected)
  3995. def test_invalid_large(self):
  3996. with self.assertRaises(ValueError):
  3997. mi.permutation_index(tuple('abcdefg'), 'abcdef')
  3998. def test_invalid_match(self):
  3999. with self.assertRaises(ValueError):
  4000. mi.permutation_index(tuple('axe'), 'abcde')
  4001. class CountableTests(TestCase):
  4002. def test_empty(self):
  4003. iterable = []
  4004. it = mi.countable(iterable)
  4005. self.assertEqual(it.items_seen, 0)
  4006. self.assertEqual(list(it), [])
  4007. def test_basic(self):
  4008. iterable = '0123456789'
  4009. it = mi.countable(iterable)
  4010. self.assertEqual(it.items_seen, 0)
  4011. self.assertEqual(next(it), '0')
  4012. self.assertEqual(it.items_seen, 1)
  4013. self.assertEqual(''.join(it), '123456789')
  4014. self.assertEqual(it.items_seen, 10)
  4015. class ChunkedEvenTests(TestCase):
  4016. """Tests for ``chunked_even()``"""
  4017. def test_0(self):
  4018. self._test_finite('', 3, [])
  4019. def test_1(self):
  4020. self._test_finite('A', 1, [['A']])
  4021. def test_4(self):
  4022. self._test_finite('ABCD', 3, [['A', 'B'], ['C', 'D']])
  4023. def test_5(self):
  4024. self._test_finite('ABCDE', 3, [['A', 'B', 'C'], ['D', 'E']])
  4025. def test_6(self):
  4026. self._test_finite('ABCDEF', 3, [['A', 'B', 'C'], ['D', 'E', 'F']])
  4027. def test_7(self):
  4028. self._test_finite(
  4029. 'ABCDEFG', 3, [['A', 'B', 'C'], ['D', 'E'], ['F', 'G']]
  4030. )
  4031. def _test_finite(self, seq, n, expected):
  4032. # Check with and without `len()`
  4033. self.assertEqual(list(mi.chunked_even(seq, n)), expected)
  4034. self.assertEqual(list(mi.chunked_even(iter(seq), n)), expected)
  4035. def test_infinite(self):
  4036. for n in range(1, 5):
  4037. k = 0
  4038. def count_with_assert():
  4039. for i in count():
  4040. # Look-ahead should be less than n^2
  4041. self.assertLessEqual(i, n * k + n * n)
  4042. yield i
  4043. ls = mi.chunked_even(count_with_assert(), n)
  4044. while k < 2:
  4045. self.assertEqual(next(ls), list(range(k * n, (k + 1) * n)))
  4046. k += 1
  4047. def test_evenness(self):
  4048. for N in range(1, 50):
  4049. for n in range(1, N + 2):
  4050. lengths = []
  4051. items = []
  4052. for l in mi.chunked_even(range(N), n):
  4053. L = len(l)
  4054. self.assertLessEqual(L, n)
  4055. self.assertGreaterEqual(L, 1)
  4056. lengths.append(L)
  4057. items.extend(l)
  4058. self.assertEqual(items, list(range(N)))
  4059. self.assertLessEqual(max(lengths) - min(lengths), 1)
  4060. class ZipBroadcastTests(TestCase):
  4061. def test_zip(self):
  4062. for objects, zipped, strict_ok in [
  4063. # Empty
  4064. ([], [], True),
  4065. # One argument
  4066. ([1], [(1,)], True),
  4067. ([[1]], [(1,)], True),
  4068. ([[1, 2]], [(1,), (2,)], True),
  4069. # All scalars
  4070. ([1, 2], [(1, 2)], True),
  4071. ([1, 2, 3], [(1, 2, 3)], True),
  4072. # Iterables with length = 0
  4073. ([[], 1], [], True),
  4074. ([1, []], [], True),
  4075. ([[], []], [], True),
  4076. ([[], 1, 2], [], True),
  4077. ([[], 1, []], [], True),
  4078. ([1, [], 2], [], True),
  4079. ([1, [], []], [], True),
  4080. ([[], [], 1], [], True),
  4081. ([[], [], []], [], True),
  4082. # Iterables with length = 1
  4083. ([1, [2]], [(1, 2)], True),
  4084. ([[1], 2], [(1, 2)], True),
  4085. ([[1], [2]], [(1, 2)], True),
  4086. ([1, [2], 3], [(1, 2, 3)], True),
  4087. ([1, [2], [3]], [(1, 2, 3)], True),
  4088. ([[1], 2, 3], [(1, 2, 3)], True),
  4089. ([[1], 2, [3]], [(1, 2, 3)], True),
  4090. ([[1], [2], 3], [(1, 2, 3)], True),
  4091. ([[1], [2], [3]], [(1, 2, 3)], True),
  4092. # Iterables with length > 1
  4093. ([1, [2, 3]], [(1, 2), (1, 3)], True),
  4094. ([[1, 2], 3], [(1, 3), (2, 3)], True),
  4095. ([[1, 2], [3, 4]], [(1, 3), (2, 4)], True),
  4096. ([1, [2, 3], 4], [(1, 2, 4), (1, 3, 4)], True),
  4097. ([1, [2, 3], [4, 5]], [(1, 2, 4), (1, 3, 5)], True),
  4098. ([[1, 2], 3, 4], [(1, 3, 4), (2, 3, 4)], True),
  4099. ([[1, 2], 3, [4, 5]], [(1, 3, 4), (2, 3, 5)], True),
  4100. ([[1, 2], [3, 4], 5], [(1, 3, 5), (2, 4, 5)], True),
  4101. ([[1, 2], [3, 4], [5, 6]], [(1, 3, 5), (2, 4, 6)], True),
  4102. # Iterables with different lengths
  4103. ([[], [1]], [], False),
  4104. ([[1], []], [], False),
  4105. ([[1], [2, 3]], [(1, 2)], False),
  4106. ([[1, 2], [3]], [(1, 3)], False),
  4107. ([[1, 2], [3], [4]], [(1, 3, 4)], False),
  4108. ([[1], [2, 3], [4]], [(1, 2, 4)], False),
  4109. ([[1], [2], [3, 4]], [(1, 2, 3)], False),
  4110. ([[1], [2, 3], [4, 5]], [(1, 2, 4)], False),
  4111. ([[1, 2], [3], [4, 5]], [(1, 3, 4)], False),
  4112. ([[1, 2], [3, 4], [5]], [(1, 3, 5)], False),
  4113. ([1, [2, 3], [4, 5, 6]], [(1, 2, 4), (1, 3, 5)], False),
  4114. ([[1, 2], 3, [4, 5, 6]], [(1, 3, 4), (2, 3, 5)], False),
  4115. ([1, [2, 3, 4], [5, 6]], [(1, 2, 5), (1, 3, 6)], False),
  4116. ([[1, 2, 3], 4, [5, 6]], [(1, 4, 5), (2, 4, 6)], False),
  4117. ([[1, 2], [3, 4, 5], 6], [(1, 3, 6), (2, 4, 6)], False),
  4118. ([[1, 2, 3], [4, 5], 6], [(1, 4, 6), (2, 5, 6)], False),
  4119. # Infinite
  4120. ([count(), 1, [2]], [(0, 1, 2)], False),
  4121. ([count(), 1, [2, 3]], [(0, 1, 2), (1, 1, 3)], False),
  4122. # Miscellaneous
  4123. (['a', [1, 2], [3, 4, 5]], [('a', 1, 3), ('a', 2, 4)], False),
  4124. ]:
  4125. # Truncate by default
  4126. with self.subTest(objects=objects, strict=False, zipped=zipped):
  4127. self.assertEqual(list(mi.zip_broadcast(*objects)), zipped)
  4128. # Raise an exception for strict=True
  4129. with self.subTest(objects=objects, strict=True, zipped=zipped):
  4130. if strict_ok:
  4131. self.assertEqual(
  4132. list(mi.zip_broadcast(*objects, strict=True)),
  4133. zipped,
  4134. )
  4135. else:
  4136. with self.assertRaises(ValueError):
  4137. list(mi.zip_broadcast(*objects, strict=True))
  4138. def test_scalar_types(self):
  4139. # Default: str and bytes are treated as scalar
  4140. self.assertEqual(
  4141. list(mi.zip_broadcast('ab', [1, 2, 3])),
  4142. [('ab', 1), ('ab', 2), ('ab', 3)],
  4143. )
  4144. self.assertEqual(
  4145. list(mi.zip_broadcast(b'ab', [1, 2, 3])),
  4146. [(b'ab', 1), (b'ab', 2), (b'ab', 3)],
  4147. )
  4148. # scalar_types=None allows str and bytes to be treated as iterable
  4149. self.assertEqual(
  4150. list(mi.zip_broadcast('abc', [1, 2, 3], scalar_types=None)),
  4151. [('a', 1), ('b', 2), ('c', 3)],
  4152. )
  4153. # Use a custom type
  4154. self.assertEqual(
  4155. list(mi.zip_broadcast({'a': 'b'}, [1, 2, 3], scalar_types=dict)),
  4156. [({'a': 'b'}, 1), ({'a': 'b'}, 2), ({'a': 'b'}, 3)],
  4157. )
  4158. class UniqueInWindowTests(TestCase):
  4159. def test_invalid_n(self):
  4160. with self.assertRaises(ValueError):
  4161. list(mi.unique_in_window([], 0))
  4162. def test_basic(self):
  4163. for iterable, n, expected in [
  4164. (range(9), 10, list(range(9))),
  4165. (range(20), 10, list(range(20))),
  4166. ([1, 2, 3, 4, 4, 4], 1, [1, 2, 3, 4, 4, 4]),
  4167. ([1, 2, 3, 4, 4, 4], 2, [1, 2, 3, 4]),
  4168. ([1, 2, 3, 4, 4, 4], 3, [1, 2, 3, 4]),
  4169. ([1, 2, 3, 4, 4, 4], 4, [1, 2, 3, 4]),
  4170. ([1, 2, 3, 4, 4, 4], 5, [1, 2, 3, 4]),
  4171. (
  4172. [0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 3, 4, 2],
  4173. 2,
  4174. [0, 1, 0, 2, 3, 4, 2],
  4175. ),
  4176. ]:
  4177. with self.subTest(expected=expected):
  4178. actual = list(mi.unique_in_window(iterable, n))
  4179. self.assertEqual(actual, expected)
  4180. def test_key(self):
  4181. iterable = [0, 1, 3, 4, 5, 6, 7, 8, 9]
  4182. n = 3
  4183. key = lambda x: x // 3
  4184. actual = list(mi.unique_in_window(iterable, n, key=key))
  4185. expected = [0, 3, 6, 9]
  4186. self.assertEqual(actual, expected)
  4187. class StrictlyNTests(TestCase):
  4188. def test_basic(self):
  4189. iterable = ['a', 'b', 'c', 'd']
  4190. n = 4
  4191. actual = list(mi.strictly_n(iter(iterable), n))
  4192. expected = iterable
  4193. self.assertEqual(actual, expected)
  4194. def test_too_short_default(self):
  4195. iterable = ['a', 'b', 'c', 'd']
  4196. n = 5
  4197. with self.assertRaises(ValueError) as exc:
  4198. list(mi.strictly_n(iter(iterable), n))
  4199. self.assertEqual(
  4200. 'Too few items in iterable (got 4)', exc.exception.args[0]
  4201. )
  4202. def test_too_long_default(self):
  4203. iterable = ['a', 'b', 'c', 'd']
  4204. n = 3
  4205. with self.assertRaises(ValueError) as cm:
  4206. list(mi.strictly_n(iter(iterable), n))
  4207. self.assertEqual(
  4208. 'Too many items in iterable (got at least 4)',
  4209. cm.exception.args[0],
  4210. )
  4211. def test_too_short_custom(self):
  4212. call_count = 0
  4213. def too_short(item_count):
  4214. nonlocal call_count
  4215. call_count += 1
  4216. iterable = ['a', 'b', 'c', 'd']
  4217. n = 6
  4218. actual = []
  4219. for item in mi.strictly_n(iter(iterable), n, too_short=too_short):
  4220. actual.append(item)
  4221. expected = ['a', 'b', 'c', 'd']
  4222. self.assertEqual(actual, expected)
  4223. self.assertEqual(call_count, 1)
  4224. def test_too_long_custom(self):
  4225. import logging
  4226. iterable = ['a', 'b', 'c', 'd']
  4227. n = 2
  4228. too_long = lambda item_count: logging.warning(
  4229. 'Picked the first %s items', n
  4230. )
  4231. with self.assertLogs(level='WARNING') as cm:
  4232. actual = list(mi.strictly_n(iter(iterable), n, too_long=too_long))
  4233. self.assertEqual(actual, ['a', 'b'])
  4234. self.assertIn('Picked the first 2 items', cm.output[0])
  4235. class DuplicatesEverSeenTests(TestCase):
  4236. def test_basic(self):
  4237. for iterable, expected in [
  4238. ([], []),
  4239. ([1, 2, 3], []),
  4240. ([1, 1], [1]),
  4241. ([1, 2, 1, 2], [1, 2]),
  4242. ([1, 2, 3, '1'], []),
  4243. ]:
  4244. with self.subTest(args=(iterable,)):
  4245. self.assertEqual(
  4246. list(mi.duplicates_everseen(iterable)), expected
  4247. )
  4248. def test_non_hashable(self):
  4249. self.assertEqual(list(mi.duplicates_everseen([[1, 2], [3, 4]])), [])
  4250. self.assertEqual(
  4251. list(mi.duplicates_everseen([[1, 2], [3, 4], [1, 2]])), [[1, 2]]
  4252. )
  4253. def test_partially_hashable(self):
  4254. self.assertEqual(
  4255. list(mi.duplicates_everseen([[1, 2], [3, 4], (5, 6)])), []
  4256. )
  4257. self.assertEqual(
  4258. list(mi.duplicates_everseen([[1, 2], [3, 4], (5, 6), [1, 2]])),
  4259. [[1, 2]],
  4260. )
  4261. self.assertEqual(
  4262. list(mi.duplicates_everseen([[1, 2], [3, 4], (5, 6), (5, 6)])),
  4263. [(5, 6)],
  4264. )
  4265. def test_key_hashable(self):
  4266. iterable = 'HEheHEhe'
  4267. self.assertEqual(list(mi.duplicates_everseen(iterable)), list('HEhe'))
  4268. self.assertEqual(
  4269. list(mi.duplicates_everseen(iterable, str.lower)),
  4270. list('heHEhe'),
  4271. )
  4272. def test_key_non_hashable(self):
  4273. iterable = [[1, 2], [3, 0], [5, -2], [5, 6]]
  4274. self.assertEqual(
  4275. list(mi.duplicates_everseen(iterable, lambda x: x)), []
  4276. )
  4277. self.assertEqual(
  4278. list(mi.duplicates_everseen(iterable, sum)), [[3, 0], [5, -2]]
  4279. )
  4280. def test_key_partially_hashable(self):
  4281. iterable = [[1, 2], (1, 2), [1, 2], [5, 6]]
  4282. self.assertEqual(
  4283. list(mi.duplicates_everseen(iterable, lambda x: x)), [[1, 2]]
  4284. )
  4285. self.assertEqual(
  4286. list(mi.duplicates_everseen(iterable, list)), [(1, 2), [1, 2]]
  4287. )
  4288. class DuplicatesJustSeenTests(TestCase):
  4289. def test_basic(self):
  4290. for iterable, expected in [
  4291. ([], []),
  4292. ([1, 2, 3, 3, 2, 2], [3, 2]),
  4293. ([1, 1], [1]),
  4294. ([1, 2, 1, 2], []),
  4295. ([1, 2, 3, '1'], []),
  4296. ]:
  4297. with self.subTest(args=(iterable,)):
  4298. self.assertEqual(
  4299. list(mi.duplicates_justseen(iterable)), expected
  4300. )
  4301. def test_non_hashable(self):
  4302. self.assertEqual(list(mi.duplicates_justseen([[1, 2], [3, 4]])), [])
  4303. self.assertEqual(
  4304. list(
  4305. mi.duplicates_justseen(
  4306. [[1, 2], [3, 4], [3, 4], [3, 4], [1, 2]]
  4307. )
  4308. ),
  4309. [[3, 4], [3, 4]],
  4310. )
  4311. def test_partially_hashable(self):
  4312. self.assertEqual(
  4313. list(mi.duplicates_justseen([[1, 2], [3, 4], (5, 6)])), []
  4314. )
  4315. self.assertEqual(
  4316. list(
  4317. mi.duplicates_justseen(
  4318. [[1, 2], [3, 4], (5, 6), [1, 2], [1, 2]]
  4319. )
  4320. ),
  4321. [[1, 2]],
  4322. )
  4323. self.assertEqual(
  4324. list(
  4325. mi.duplicates_justseen(
  4326. [[1, 2], [3, 4], (5, 6), (5, 6), (5, 6)]
  4327. )
  4328. ),
  4329. [(5, 6), (5, 6)],
  4330. )
  4331. def test_key_hashable(self):
  4332. iterable = 'HEheHHHhEheeEe'
  4333. self.assertEqual(list(mi.duplicates_justseen(iterable)), list('HHe'))
  4334. self.assertEqual(
  4335. list(mi.duplicates_justseen(iterable, str.lower)),
  4336. list('HHheEe'),
  4337. )
  4338. def test_key_non_hashable(self):
  4339. iterable = [[1, 2], [3, 0], [5, -2], [5, 6], [1, 2]]
  4340. self.assertEqual(
  4341. list(mi.duplicates_justseen(iterable, lambda x: x)), []
  4342. )
  4343. self.assertEqual(
  4344. list(mi.duplicates_justseen(iterable, sum)), [[3, 0], [5, -2]]
  4345. )
  4346. def test_key_partially_hashable(self):
  4347. iterable = [[1, 2], (1, 2), [1, 2], [5, 6], [1, 2]]
  4348. self.assertEqual(
  4349. list(mi.duplicates_justseen(iterable, lambda x: x)), []
  4350. )
  4351. self.assertEqual(
  4352. list(mi.duplicates_justseen(iterable, list)), [(1, 2), [1, 2]]
  4353. )
  4354. def test_nested(self):
  4355. iterable = [[[1, 2], [1, 2]], [5, 6], [5, 6]]
  4356. self.assertEqual(list(mi.duplicates_justseen(iterable)), [[5, 6]])
  4357. class ClassifyUniqueTests(TestCase):
  4358. def test_basic(self):
  4359. self.assertEqual(
  4360. list(mi.classify_unique('mississippi')),
  4361. [
  4362. ('m', True, True),
  4363. ('i', True, True),
  4364. ('s', True, True),
  4365. ('s', False, False),
  4366. ('i', True, False),
  4367. ('s', True, False),
  4368. ('s', False, False),
  4369. ('i', True, False),
  4370. ('p', True, True),
  4371. ('p', False, False),
  4372. ('i', True, False),
  4373. ],
  4374. )
  4375. def test_non_hashable(self):
  4376. self.assertEqual(
  4377. list(mi.classify_unique([[1, 2], [3, 4], [3, 4], [1, 2]])),
  4378. [
  4379. ([1, 2], True, True),
  4380. ([3, 4], True, True),
  4381. ([3, 4], False, False),
  4382. ([1, 2], True, False),
  4383. ],
  4384. )
  4385. def test_partially_hashable(self):
  4386. self.assertEqual(
  4387. list(
  4388. mi.classify_unique(
  4389. [[1, 2], [3, 4], (5, 6), (5, 6), (3, 4), [1, 2]]
  4390. )
  4391. ),
  4392. [
  4393. ([1, 2], True, True),
  4394. ([3, 4], True, True),
  4395. ((5, 6), True, True),
  4396. ((5, 6), False, False),
  4397. ((3, 4), True, True),
  4398. ([1, 2], True, False),
  4399. ],
  4400. )
  4401. def test_key_hashable(self):
  4402. iterable = 'HEheHHHhEheeEe'
  4403. self.assertEqual(
  4404. list(mi.classify_unique(iterable)),
  4405. [
  4406. ('H', True, True),
  4407. ('E', True, True),
  4408. ('h', True, True),
  4409. ('e', True, True),
  4410. ('H', True, False),
  4411. ('H', False, False),
  4412. ('H', False, False),
  4413. ('h', True, False),
  4414. ('E', True, False),
  4415. ('h', True, False),
  4416. ('e', True, False),
  4417. ('e', False, False),
  4418. ('E', True, False),
  4419. ('e', True, False),
  4420. ],
  4421. )
  4422. self.assertEqual(
  4423. list(mi.classify_unique(iterable, str.lower)),
  4424. [
  4425. ('H', True, True),
  4426. ('E', True, True),
  4427. ('h', True, False),
  4428. ('e', True, False),
  4429. ('H', True, False),
  4430. ('H', False, False),
  4431. ('H', False, False),
  4432. ('h', False, False),
  4433. ('E', True, False),
  4434. ('h', True, False),
  4435. ('e', True, False),
  4436. ('e', False, False),
  4437. ('E', False, False),
  4438. ('e', False, False),
  4439. ],
  4440. )
  4441. def test_key_non_hashable(self):
  4442. iterable = [[1, 2], [3, 0], [5, -2], [5, 6], [1, 2]]
  4443. self.assertEqual(
  4444. list(mi.classify_unique(iterable, lambda x: x)),
  4445. [
  4446. ([1, 2], True, True),
  4447. ([3, 0], True, True),
  4448. ([5, -2], True, True),
  4449. ([5, 6], True, True),
  4450. ([1, 2], True, False),
  4451. ],
  4452. )
  4453. self.assertEqual(
  4454. list(mi.classify_unique(iterable, sum)),
  4455. [
  4456. ([1, 2], True, True),
  4457. ([3, 0], False, False),
  4458. ([5, -2], False, False),
  4459. ([5, 6], True, True),
  4460. ([1, 2], True, False),
  4461. ],
  4462. )
  4463. def test_key_partially_hashable(self):
  4464. iterable = [[1, 2], (1, 2), [1, 2], [5, 6], [1, 2]]
  4465. self.assertEqual(
  4466. list(mi.classify_unique(iterable, lambda x: x)),
  4467. [
  4468. ([1, 2], True, True),
  4469. ((1, 2), True, True),
  4470. ([1, 2], True, False),
  4471. ([5, 6], True, True),
  4472. ([1, 2], True, False),
  4473. ],
  4474. )
  4475. self.assertEqual(
  4476. list(mi.classify_unique(iterable, list)),
  4477. [
  4478. ([1, 2], True, True),
  4479. ((1, 2), False, False),
  4480. ([1, 2], False, False),
  4481. ([5, 6], True, True),
  4482. ([1, 2], True, False),
  4483. ],
  4484. )
  4485. def test_vs_unique_everseen(self):
  4486. input = 'AAAABBBBCCDAABBB'
  4487. output = [e for e, j, u in mi.classify_unique(input) if u]
  4488. self.assertEqual(output, ['A', 'B', 'C', 'D'])
  4489. self.assertEqual(list(mi.unique_everseen(input)), output)
  4490. def test_vs_unique_everseen_key(self):
  4491. input = 'aAbACCc'
  4492. output = [e for e, j, u in mi.classify_unique(input, str.lower) if u]
  4493. self.assertEqual(output, list('abC'))
  4494. self.assertEqual(list(mi.unique_everseen(input, str.lower)), output)
  4495. def test_vs_unique_justseen(self):
  4496. input = 'AAAABBBCCDABB'
  4497. output = [e for e, j, u in mi.classify_unique(input) if j]
  4498. self.assertEqual(output, list('ABCDAB'))
  4499. self.assertEqual(list(mi.unique_justseen(input)), output)
  4500. def test_vs_unique_justseen_key(self):
  4501. input = 'AABCcAD'
  4502. output = [e for e, j, u in mi.classify_unique(input, str.lower) if j]
  4503. self.assertEqual(output, list('ABCAD'))
  4504. self.assertEqual(list(mi.unique_justseen(input, str.lower)), output)
  4505. def test_vs_duplicates_everseen(self):
  4506. input = [1, 2, 1, 2]
  4507. output = [e for e, j, u in mi.classify_unique(input) if not u]
  4508. self.assertEqual(output, [1, 2])
  4509. self.assertEqual(list(mi.duplicates_everseen(input)), output)
  4510. def test_vs_duplicates_everseen_key(self):
  4511. input = 'HEheHEhe'
  4512. output = [
  4513. e for e, j, u in mi.classify_unique(input, str.lower) if not u
  4514. ]
  4515. self.assertEqual(output, list('heHEhe'))
  4516. self.assertEqual(
  4517. list(mi.duplicates_everseen(input, str.lower)), output
  4518. )
  4519. def test_vs_duplicates_justseen(self):
  4520. input = [1, 2, 3, 3, 2, 2]
  4521. output = [e for e, j, u in mi.classify_unique(input) if not j]
  4522. self.assertEqual(output, [3, 2])
  4523. self.assertEqual(list(mi.duplicates_justseen(input)), output)
  4524. def test_vs_duplicates_justseen_key(self):
  4525. input = 'HEheHHHhEheeEe'
  4526. output = [
  4527. e for e, j, u in mi.classify_unique(input, str.lower) if not j
  4528. ]
  4529. self.assertEqual(output, list('HHheEe'))
  4530. self.assertEqual(
  4531. list(mi.duplicates_justseen(input, str.lower)), output
  4532. )
  4533. class LongestCommonPrefixTests(TestCase):
  4534. def test_basic(self):
  4535. iterables = [[1, 2], [1, 2, 3], [1, 2, 4]]
  4536. self.assertEqual(list(mi.longest_common_prefix(iterables)), [1, 2])
  4537. def test_iterators(self):
  4538. iterables = iter([iter([1, 2]), iter([1, 2, 3]), iter([1, 2, 4])])
  4539. self.assertEqual(list(mi.longest_common_prefix(iterables)), [1, 2])
  4540. def test_no_iterables(self):
  4541. iterables = []
  4542. self.assertEqual(list(mi.longest_common_prefix(iterables)), [])
  4543. def test_empty_iterables_only(self):
  4544. iterables = [[], [], []]
  4545. self.assertEqual(list(mi.longest_common_prefix(iterables)), [])
  4546. def test_includes_empty_iterables(self):
  4547. iterables = [[1, 2], [1, 2, 3], [1, 2, 4], []]
  4548. self.assertEqual(list(mi.longest_common_prefix(iterables)), [])
  4549. def test_non_hashable(self):
  4550. # See https://github.com/more-itertools/more-itertools/issues/603
  4551. iterables = [[[1], [2]], [[1], [2], [3]], [[1], [2], [4]]]
  4552. self.assertEqual(list(mi.longest_common_prefix(iterables)), [[1], [2]])
  4553. def test_prefix_contains_elements_of_the_first_iterable(self):
  4554. iterables = [[[1], [2]], [[1], [2], [3]], [[1], [2], [4]]]
  4555. prefix = list(mi.longest_common_prefix(iterables))
  4556. self.assertIs(prefix[0], iterables[0][0])
  4557. self.assertIs(prefix[1], iterables[0][1])
  4558. self.assertIsNot(prefix[0], iterables[1][0])
  4559. self.assertIsNot(prefix[1], iterables[1][1])
  4560. self.assertIsNot(prefix[0], iterables[2][0])
  4561. self.assertIsNot(prefix[1], iterables[2][1])
  4562. def test_infinite_iterables(self):
  4563. prefix = mi.longest_common_prefix([count(), count()])
  4564. self.assertEqual(next(prefix), 0)
  4565. self.assertEqual(next(prefix), 1)
  4566. self.assertEqual(next(prefix), 2)
  4567. def test_contains_infinite_iterables(self):
  4568. iterables = [[0, 1, 2], count()]
  4569. self.assertEqual(list(mi.longest_common_prefix(iterables)), [0, 1, 2])
  4570. class IequalsTests(TestCase):
  4571. def test_basic(self):
  4572. self.assertTrue(mi.iequals("abc", iter("abc")))
  4573. self.assertTrue(mi.iequals(range(3), [0, 1, 2]))
  4574. self.assertFalse(mi.iequals("abc", [0, 1, 2]))
  4575. def test_no_iterables(self):
  4576. self.assertTrue(mi.iequals())
  4577. def test_one_iterable(self):
  4578. self.assertTrue(mi.iequals("abc"))
  4579. def test_more_than_two_iterable(self):
  4580. self.assertTrue(mi.iequals("abc", iter("abc"), ['a', 'b', 'c']))
  4581. self.assertFalse(mi.iequals("abc", iter("abc"), ['a', 'b', 'd']))
  4582. def test_order_matters(self):
  4583. self.assertFalse(mi.iequals("abc", "acb"))
  4584. def test_not_equal_lengths(self):
  4585. self.assertFalse(mi.iequals("abc", "ab"))
  4586. self.assertFalse(mi.iequals("abc", "bc"))
  4587. self.assertFalse(mi.iequals("aaa", "aaaa"))
  4588. def test_empty_iterables(self):
  4589. self.assertTrue(mi.iequals([], ""))
  4590. def test_none_is_not_a_sentinel(self):
  4591. # See https://stackoverflow.com/a/900444
  4592. self.assertFalse(mi.iequals([1, 2], [1, 2, None]))
  4593. self.assertFalse(mi.iequals([1, 2], [None, 1, 2]))
  4594. def test_not_identical_but_equal(self):
  4595. self.assertTrue([1, True], [1.0, complex(1, 0)])
  4596. class ConstrainedBatchesTests(TestCase):
  4597. def test_basic(self):
  4598. zen = [
  4599. 'Beautiful is better than ugly',
  4600. 'Explicit is better than implicit',
  4601. 'Simple is better than complex',
  4602. 'Complex is better than complicated',
  4603. 'Flat is better than nested',
  4604. 'Sparse is better than dense',
  4605. 'Readability counts',
  4606. ]
  4607. for size, expected in (
  4608. (
  4609. 34,
  4610. [
  4611. (zen[0],),
  4612. (zen[1],),
  4613. (zen[2],),
  4614. (zen[3],),
  4615. (zen[4],),
  4616. (zen[5],),
  4617. (zen[6],),
  4618. ],
  4619. ),
  4620. (
  4621. 61,
  4622. [
  4623. (zen[0], zen[1]),
  4624. (zen[2],),
  4625. (zen[3], zen[4]),
  4626. (zen[5], zen[6]),
  4627. ],
  4628. ),
  4629. (
  4630. 90,
  4631. [
  4632. (zen[0], zen[1], zen[2]),
  4633. (zen[3], zen[4], zen[5]),
  4634. (zen[6],),
  4635. ],
  4636. ),
  4637. (
  4638. 124,
  4639. [(zen[0], zen[1], zen[2], zen[3]), (zen[4], zen[5], zen[6])],
  4640. ),
  4641. (
  4642. 150,
  4643. [(zen[0], zen[1], zen[2], zen[3], zen[4]), (zen[5], zen[6])],
  4644. ),
  4645. (
  4646. 177,
  4647. [(zen[0], zen[1], zen[2], zen[3], zen[4], zen[5]), (zen[6],)],
  4648. ),
  4649. ):
  4650. with self.subTest(size=size):
  4651. actual = list(mi.constrained_batches(iter(zen), size))
  4652. self.assertEqual(actual, expected)
  4653. def test_max_count(self):
  4654. iterable = ['1', '1', '12345678', '12345', '12345']
  4655. max_size = 10
  4656. max_count = 2
  4657. actual = list(mi.constrained_batches(iterable, max_size, max_count))
  4658. expected = [('1', '1'), ('12345678',), ('12345', '12345')]
  4659. self.assertEqual(actual, expected)
  4660. def test_strict(self):
  4661. iterable = ['1', '123456789', '1']
  4662. size = 8
  4663. with self.assertRaises(ValueError):
  4664. list(mi.constrained_batches(iterable, size))
  4665. actual = list(mi.constrained_batches(iterable, size, strict=False))
  4666. expected = [('1',), ('123456789',), ('1',)]
  4667. self.assertEqual(actual, expected)
  4668. def test_get_len(self):
  4669. class Record(tuple):
  4670. def total_size(self):
  4671. return sum(len(x) for x in self)
  4672. record_3 = Record(('1', '23'))
  4673. record_5 = Record(('1234', '1'))
  4674. record_10 = Record(('1', '12345678', '1'))
  4675. record_2 = Record(('1', '1'))
  4676. iterable = [record_3, record_5, record_10, record_2]
  4677. self.assertEqual(
  4678. list(
  4679. mi.constrained_batches(
  4680. iterable, 10, get_len=lambda x: x.total_size()
  4681. )
  4682. ),
  4683. [(record_3, record_5), (record_10,), (record_2,)],
  4684. )
  4685. def test_bad_max(self):
  4686. with self.assertRaises(ValueError):
  4687. list(mi.constrained_batches([], 0))
  4688. class GrayProductTests(TestCase):
  4689. def test_basic(self):
  4690. self.assertEqual(
  4691. tuple(mi.gray_product(('a', 'b', 'c'), range(1, 3))),
  4692. (("a", 1), ("b", 1), ("c", 1), ("c", 2), ("b", 2), ("a", 2)),
  4693. )
  4694. out = mi.gray_product(('foo', 'bar'), (3, 4, 5, 6), ['quz', 'baz'])
  4695. self.assertEqual(next(out), ('foo', 3, 'quz'))
  4696. self.assertEqual(
  4697. list(out),
  4698. [
  4699. ('bar', 3, 'quz'),
  4700. ('bar', 4, 'quz'),
  4701. ('foo', 4, 'quz'),
  4702. ('foo', 5, 'quz'),
  4703. ('bar', 5, 'quz'),
  4704. ('bar', 6, 'quz'),
  4705. ('foo', 6, 'quz'),
  4706. ('foo', 6, 'baz'),
  4707. ('bar', 6, 'baz'),
  4708. ('bar', 5, 'baz'),
  4709. ('foo', 5, 'baz'),
  4710. ('foo', 4, 'baz'),
  4711. ('bar', 4, 'baz'),
  4712. ('bar', 3, 'baz'),
  4713. ('foo', 3, 'baz'),
  4714. ],
  4715. )
  4716. self.assertEqual(tuple(mi.gray_product()), ((),))
  4717. self.assertEqual(tuple(mi.gray_product((1, 2))), ((1,), (2,)))
  4718. def test_errors(self):
  4719. with self.assertRaises(ValueError):
  4720. list(mi.gray_product((1, 2), ()))
  4721. with self.assertRaises(ValueError):
  4722. list(mi.gray_product((1, 2), (2,)))
  4723. def test_vs_product(self):
  4724. iters = (
  4725. ("a", "b"),
  4726. range(3, 6),
  4727. [None, None],
  4728. {"i", "j", "k", "l"},
  4729. "XYZ",
  4730. )
  4731. self.assertEqual(
  4732. sorted(product(*iters)), sorted(mi.gray_product(*iters))
  4733. )
  4734. class PartialProductTests(TestCase):
  4735. def test_no_iterables(self):
  4736. self.assertEqual(tuple(mi.partial_product()), ((),))
  4737. def test_empty_iterable(self):
  4738. self.assertEqual(tuple(mi.partial_product('AB', '', 'CD')), ())
  4739. def test_one_iterable(self):
  4740. # a single iterable should pass through
  4741. self.assertEqual(
  4742. tuple(mi.partial_product('ABCD')),
  4743. (
  4744. ('A',),
  4745. ('B',),
  4746. ('C',),
  4747. ('D',),
  4748. ),
  4749. )
  4750. def test_two_iterables(self):
  4751. self.assertEqual(
  4752. list(mi.partial_product('ABCD', [1])),
  4753. [('A', 1), ('B', 1), ('C', 1), ('D', 1)],
  4754. )
  4755. expected = [
  4756. ('A', 1),
  4757. ('B', 1),
  4758. ('C', 1),
  4759. ('D', 1),
  4760. ('D', 2),
  4761. ('D', 3),
  4762. ('D', 4),
  4763. ]
  4764. self.assertEqual(
  4765. list(mi.partial_product('ABCD', [1, 2, 3, 4])), expected
  4766. )
  4767. def test_basic(self):
  4768. ones = [1, 2, 3]
  4769. tens = [10, 20, 30, 40, 50]
  4770. hundreds = [100, 200]
  4771. expected = [
  4772. (1, 10, 100),
  4773. (2, 10, 100),
  4774. (3, 10, 100),
  4775. (3, 20, 100),
  4776. (3, 30, 100),
  4777. (3, 40, 100),
  4778. (3, 50, 100),
  4779. (3, 50, 200),
  4780. ]
  4781. actual = list(mi.partial_product(ones, tens, hundreds))
  4782. self.assertEqual(actual, expected)
  4783. def test_uneven_length_iterables(self):
  4784. # this is also the docstring example
  4785. expected = [
  4786. ('A', 'C', 'D'),
  4787. ('B', 'C', 'D'),
  4788. ('B', 'C', 'E'),
  4789. ('B', 'C', 'F'),
  4790. ]
  4791. self.assertEqual(list(mi.partial_product('AB', 'C', 'DEF')), expected)
  4792. class IterateTests(TestCase):
  4793. def test_basic(self) -> None:
  4794. result = list(islice(mi.iterate(lambda x: 2 * x, start=1), 10))
  4795. expected = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
  4796. self.assertEqual(result, expected)
  4797. def test_func_controls_iteration_stop(self) -> None:
  4798. def func(num):
  4799. if num > 100:
  4800. raise StopIteration
  4801. return num * 2
  4802. result = list(islice(mi.iterate(func, start=1), 10))
  4803. expected = [1, 2, 4, 8, 16, 32, 64, 128]
  4804. self.assertEqual(result, expected)
  4805. class TakewhileInclusiveTests(TestCase):
  4806. def test_basic(self) -> None:
  4807. result = list(mi.takewhile_inclusive(lambda x: x < 5, [1, 4, 6, 4, 1]))
  4808. expected = [1, 4, 6]
  4809. self.assertEqual(result, expected)
  4810. def test_empty_iterator(self) -> None:
  4811. result = list(mi.takewhile_inclusive(lambda x: True, []))
  4812. expected = []
  4813. self.assertEqual(result, expected)
  4814. def test_collatz_sequence(self) -> None:
  4815. is_even = lambda n: n % 2 == 0
  4816. start = 11
  4817. result = list(
  4818. mi.takewhile_inclusive(
  4819. lambda n: n != 1,
  4820. mi.iterate(
  4821. lambda n: n // 2 if is_even(n) else 3 * n + 1, start
  4822. ),
  4823. )
  4824. )
  4825. expected = [11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
  4826. self.assertEqual(result, expected)
  4827. class OuterProductTests(TestCase):
  4828. def test_basic(self) -> None:
  4829. greetings = ['Hello', 'Goodbye']
  4830. names = ['Alice', 'Bob', 'Carol']
  4831. greet = lambda greeting, name: f'{greeting}, {name}!'
  4832. result = list(mi.outer_product(greet, greetings, names))
  4833. expected = [
  4834. ('Hello, Alice!', 'Hello, Bob!', 'Hello, Carol!'),
  4835. ('Goodbye, Alice!', 'Goodbye, Bob!', 'Goodbye, Carol!'),
  4836. ]
  4837. self.assertEqual(result, expected)
  4838. class IterSuppressTests(TestCase):
  4839. class Producer:
  4840. def __init__(self, exc, die_early=False):
  4841. self.exc = exc
  4842. self.pos = 0
  4843. self.die_early = die_early
  4844. def __iter__(self):
  4845. if self.die_early:
  4846. raise self.exc
  4847. return self
  4848. def __next__(self):
  4849. ret = self.pos
  4850. if self.pos >= 5:
  4851. raise self.exc
  4852. self.pos += 1
  4853. return ret
  4854. def test_no_error(self):
  4855. iterator = range(5)
  4856. actual = list(mi.iter_suppress(iterator, RuntimeError))
  4857. expected = [0, 1, 2, 3, 4]
  4858. self.assertEqual(actual, expected)
  4859. def test_raises_error(self):
  4860. iterator = self.Producer(ValueError)
  4861. with self.assertRaises(ValueError):
  4862. list(mi.iter_suppress(iterator, RuntimeError))
  4863. def test_suppression(self):
  4864. iterator = self.Producer(ValueError)
  4865. actual = list(mi.iter_suppress(iterator, RuntimeError, ValueError))
  4866. expected = [0, 1, 2, 3, 4]
  4867. self.assertEqual(actual, expected)
  4868. def test_early_suppression(self):
  4869. iterator = self.Producer(ValueError, die_early=True)
  4870. actual = list(mi.iter_suppress(iterator, RuntimeError, ValueError))
  4871. expected = []
  4872. self.assertEqual(actual, expected)
  4873. class FilterMapTests(TestCase):
  4874. def test_no_iterables(self):
  4875. actual = list(mi.filter_map(lambda _: None, []))
  4876. expected = []
  4877. self.assertEqual(actual, expected)
  4878. def test_filter(self):
  4879. actual = list(mi.filter_map(lambda _: None, [1, 2, 3]))
  4880. expected = []
  4881. self.assertEqual(actual, expected)
  4882. def test_map(self):
  4883. actual = list(mi.filter_map(lambda x: x + 1, [1, 2, 3]))
  4884. expected = [2, 3, 4]
  4885. self.assertEqual(actual, expected)
  4886. def test_filter_map(self):
  4887. actual = list(
  4888. mi.filter_map(
  4889. lambda x: int(x) if x.isnumeric() else None,
  4890. ['1', 'a', '2', 'b', '3'],
  4891. )
  4892. )
  4893. expected = [1, 2, 3]
  4894. self.assertEqual(actual, expected)
  4895. class PowersetOfSetsTests(TestCase):
  4896. def test_simple(self):
  4897. iterable = [0, 1, 2]
  4898. actual = list(mi.powerset_of_sets(iterable))
  4899. expected = [set(), {0}, {1}, {2}, {0, 1}, {0, 2}, {1, 2}, {0, 1, 2}]
  4900. self.assertEqual(actual, expected)
  4901. def test_hash_count(self):
  4902. hash_count = 0
  4903. class Str(str):
  4904. def __hash__(true_self):
  4905. nonlocal hash_count
  4906. hash_count += 1
  4907. return super.__hash__(true_self)
  4908. iterable = map(Str, 'ABBBCDD')
  4909. self.assertEqual(len(list(mi.powerset_of_sets(iterable))), 128)
  4910. self.assertLessEqual(hash_count, 14)
  4911. class JoinMappingTests(TestCase):
  4912. def test_basic(self):
  4913. salary_map = {'e1': 12, 'e2': 23, 'e3': 34}
  4914. dept_map = {'e1': 'eng', 'e2': 'sales', 'e3': 'eng'}
  4915. service_map = {'e1': 5, 'e2': 9, 'e3': 2}
  4916. field_to_map = {
  4917. 'salary': salary_map,
  4918. 'dept': dept_map,
  4919. 'service': service_map,
  4920. }
  4921. expected = {
  4922. 'e1': {'salary': 12, 'dept': 'eng', 'service': 5},
  4923. 'e2': {'salary': 23, 'dept': 'sales', 'service': 9},
  4924. 'e3': {'salary': 34, 'dept': 'eng', 'service': 2},
  4925. }
  4926. self.assertEqual(dict(mi.join_mappings(**field_to_map)), expected)
  4927. def test_empty(self):
  4928. self.assertEqual(dict(mi.join_mappings()), {})
  4929. class DiscreteFourierTransformTests(TestCase):
  4930. def test_basic(self):
  4931. # Example calculation from:
  4932. # https://en.wikipedia.org/wiki/Discrete_Fourier_transform#Example
  4933. xarr = [1, 2 - 1j, -1j, -1 + 2j]
  4934. Xarr = [2, -2 - 2j, -2j, 4 + 4j]
  4935. self.assertTrue(all(map(cmath.isclose, mi.dft(xarr), Xarr)))
  4936. self.assertTrue(all(map(cmath.isclose, mi.idft(Xarr), xarr)))
  4937. def test_roundtrip(self):
  4938. for _ in range(1_000):
  4939. N = randrange(35)
  4940. xarr = [complex(random(), random()) for i in range(N)]
  4941. Xarr = list(mi.dft(xarr))
  4942. assert all(map(cmath.isclose, mi.idft(Xarr), xarr))
  4943. class DoubleStarMapTests(TestCase):
  4944. def test_construction(self):
  4945. iterable = [{'price': 1.23}, {'price': 42}, {'price': 0.1}]
  4946. actual = list(mi.doublestarmap('{price:.2f}'.format, iterable))
  4947. expected = ['1.23', '42.00', '0.10']
  4948. self.assertEqual(actual, expected)
  4949. def test_identity(self):
  4950. iterable = [{'x': 1}, {'x': 2}, {'x': 3}]
  4951. actual = list(mi.doublestarmap(lambda x: x, iterable))
  4952. expected = [1, 2, 3]
  4953. self.assertEqual(actual, expected)
  4954. def test_adding(self):
  4955. iterable = [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}]
  4956. actual = list(mi.doublestarmap(lambda a, b: a + b, iterable))
  4957. expected = [3, 7]
  4958. self.assertEqual(actual, expected)
  4959. def test_mismatch_function_smaller(self):
  4960. iterable = [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}]
  4961. with self.assertRaises(TypeError):
  4962. list(mi.doublestarmap(lambda a: a, iterable))
  4963. def test_mismatch_function_different(self):
  4964. iterable = [{'a': 1}, {'a': 2}]
  4965. with self.assertRaises(TypeError):
  4966. list(mi.doublestarmap(lambda x: x, iterable))
  4967. def test_mismatch_function_larger(self):
  4968. iterable = [{'a': 1}, {'a': 2}]
  4969. with self.assertRaises(TypeError):
  4970. list(mi.doublestarmap(lambda a, b: a + b, iterable))
  4971. def test_no_mapping(self):
  4972. iterable = [1, 2, 3, 4]
  4973. with self.assertRaises(TypeError):
  4974. list(mi.doublestarmap(lambda x: x, iterable))
  4975. def test_empty(self):
  4976. actual = list(mi.doublestarmap(lambda x: x, []))
  4977. expected = []
  4978. self.assertEqual(actual, expected)