From 5da4cfb4ff9a5c8117a1bba55b83cc06c218631d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 5 Sep 2021 14:17:18 -0400 Subject: [PATCH 1/9] Attempt to unvendor dependencies, but still fall back to a vendored version when needed. Doesn't work because pkg_resources can't load entry points from a distribution present only as a wheel. --- MANIFEST.in | 2 +- _setuptools_vendored/__init__.py | 7 + .../appdirs-1.4.3-py2.py3-none-any.whl | Bin 0 -> 12154 bytes .../more_itertools-8.8.0-py3-none-any.whl | Bin 0 -> 48787 bytes .../ordered_set-3.1.1-py2.py3-none-any.whl | Bin 0 -> 7813 bytes .../packaging-20.4-py2.py3-none-any.whl | Bin 0 -> 37620 bytes .../pyparsing-2.2.1-py2.py3-none-any.whl | Bin 0 -> 57557 bytes .../six-1.16.0-py2.py3-none-any.whl | Bin 0 -> 11053 bytes conftest.py | 5 +- pavement.py | 70 - pkg_resources/__init__.py | 13 +- pkg_resources/_vendor/__init__.py | 0 pkg_resources/_vendor/appdirs.py | 608 -- pkg_resources/_vendor/packaging/__about__.py | 27 - pkg_resources/_vendor/packaging/__init__.py | 26 - pkg_resources/_vendor/packaging/_compat.py | 38 - .../_vendor/packaging/_structures.py | 86 - pkg_resources/_vendor/packaging/_typing.py | 48 - pkg_resources/_vendor/packaging/markers.py | 328 - pkg_resources/_vendor/packaging/py.typed | 0 .../_vendor/packaging/requirements.py | 145 - pkg_resources/_vendor/packaging/specifiers.py | 863 --- pkg_resources/_vendor/packaging/tags.py | 751 --- pkg_resources/_vendor/packaging/utils.py | 65 - pkg_resources/_vendor/packaging/version.py | 535 -- pkg_resources/_vendor/pyparsing.py | 5742 ----------------- pkg_resources/_vendor/vendored.txt | 3 - pkg_resources/extern/__init__.py | 73 - pkg_resources/tests/test_resources.py | 2 +- setup.cfg | 7 + setuptools/__init__.py | 1 + setuptools/_vendor/__init__.py | 0 setuptools/_vendor/more_itertools/__init__.py | 4 - .../_vendor/more_itertools/__init__.pyi | 2 - setuptools/_vendor/more_itertools/more.py | 3825 ----------- setuptools/_vendor/more_itertools/more.pyi | 480 -- setuptools/_vendor/more_itertools/py.typed | 0 setuptools/_vendor/more_itertools/recipes.py | 620 -- setuptools/_vendor/more_itertools/recipes.pyi | 103 - setuptools/_vendor/ordered_set.py | 488 -- setuptools/_vendor/packaging/__about__.py | 27 - setuptools/_vendor/packaging/__init__.py | 26 - setuptools/_vendor/packaging/_compat.py | 38 - setuptools/_vendor/packaging/_structures.py | 86 - setuptools/_vendor/packaging/_typing.py | 48 - setuptools/_vendor/packaging/markers.py | 328 - setuptools/_vendor/packaging/py.typed | 0 setuptools/_vendor/packaging/requirements.py | 145 - setuptools/_vendor/packaging/specifiers.py | 863 --- setuptools/_vendor/packaging/tags.py | 751 --- setuptools/_vendor/packaging/utils.py | 65 - setuptools/_vendor/packaging/version.py | 535 -- setuptools/_vendor/pyparsing.py | 5742 ----------------- setuptools/_vendor/vendored.txt | 4 - setuptools/command/build_py.py | 2 +- setuptools/command/egg_info.py | 2 +- setuptools/command/test.py | 2 +- setuptools/config.py | 4 +- setuptools/dist.py | 10 +- setuptools/extern/__init__.py | 73 - setuptools/msvc.py | 4 +- setuptools/package_index.py | 2 +- setuptools/tests/test_extern.py | 2 +- setuptools/tests/test_wheel.py | 4 +- setuptools/wheel.py | 4 +- 65 files changed, 42 insertions(+), 23692 deletions(-) create mode 100644 _setuptools_vendored/__init__.py create mode 100644 _setuptools_vendored/appdirs-1.4.3-py2.py3-none-any.whl create mode 100644 _setuptools_vendored/more_itertools-8.8.0-py3-none-any.whl create mode 100644 _setuptools_vendored/ordered_set-3.1.1-py2.py3-none-any.whl create mode 100644 _setuptools_vendored/packaging-20.4-py2.py3-none-any.whl create mode 100644 _setuptools_vendored/pyparsing-2.2.1-py2.py3-none-any.whl create mode 100644 _setuptools_vendored/six-1.16.0-py2.py3-none-any.whl delete mode 100644 pavement.py delete mode 100644 pkg_resources/_vendor/__init__.py delete mode 100644 pkg_resources/_vendor/appdirs.py delete mode 100644 pkg_resources/_vendor/packaging/__about__.py delete mode 100644 pkg_resources/_vendor/packaging/__init__.py delete mode 100644 pkg_resources/_vendor/packaging/_compat.py delete mode 100644 pkg_resources/_vendor/packaging/_structures.py delete mode 100644 pkg_resources/_vendor/packaging/_typing.py delete mode 100644 pkg_resources/_vendor/packaging/markers.py delete mode 100644 pkg_resources/_vendor/packaging/py.typed delete mode 100644 pkg_resources/_vendor/packaging/requirements.py delete mode 100644 pkg_resources/_vendor/packaging/specifiers.py delete mode 100644 pkg_resources/_vendor/packaging/tags.py delete mode 100644 pkg_resources/_vendor/packaging/utils.py delete mode 100644 pkg_resources/_vendor/packaging/version.py delete mode 100644 pkg_resources/_vendor/pyparsing.py delete mode 100644 pkg_resources/_vendor/vendored.txt delete mode 100644 pkg_resources/extern/__init__.py delete mode 100644 setuptools/_vendor/__init__.py delete mode 100644 setuptools/_vendor/more_itertools/__init__.py delete mode 100644 setuptools/_vendor/more_itertools/__init__.pyi delete mode 100644 setuptools/_vendor/more_itertools/more.py delete mode 100644 setuptools/_vendor/more_itertools/more.pyi delete mode 100644 setuptools/_vendor/more_itertools/py.typed delete mode 100644 setuptools/_vendor/more_itertools/recipes.py delete mode 100644 setuptools/_vendor/more_itertools/recipes.pyi delete mode 100644 setuptools/_vendor/ordered_set.py delete mode 100644 setuptools/_vendor/packaging/__about__.py delete mode 100644 setuptools/_vendor/packaging/__init__.py delete mode 100644 setuptools/_vendor/packaging/_compat.py delete mode 100644 setuptools/_vendor/packaging/_structures.py delete mode 100644 setuptools/_vendor/packaging/_typing.py delete mode 100644 setuptools/_vendor/packaging/markers.py delete mode 100644 setuptools/_vendor/packaging/py.typed delete mode 100644 setuptools/_vendor/packaging/requirements.py delete mode 100644 setuptools/_vendor/packaging/specifiers.py delete mode 100644 setuptools/_vendor/packaging/tags.py delete mode 100644 setuptools/_vendor/packaging/utils.py delete mode 100644 setuptools/_vendor/packaging/version.py delete mode 100644 setuptools/_vendor/pyparsing.py delete mode 100644 setuptools/_vendor/vendored.txt delete mode 100644 setuptools/extern/__init__.py diff --git a/MANIFEST.in b/MANIFEST.in index 3e8f09de37..91bb3dbd88 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,7 +2,7 @@ recursive-include setuptools *.py *.exe *.xml *.tmpl recursive-include tests *.py recursive-include setuptools/tests *.html recursive-include docs *.py *.txt *.rst *.conf *.css *.css_t Makefile indexsidebar.html -recursive-include setuptools/_vendor *.py *.txt +recursive-include _setuptools_vendored * recursive-include pkg_resources *.py *.txt recursive-include pkg_resources/tests/data * recursive-include tools * diff --git a/_setuptools_vendored/__init__.py b/_setuptools_vendored/__init__.py new file mode 100644 index 0000000000..8299cd32be --- /dev/null +++ b/_setuptools_vendored/__init__.py @@ -0,0 +1,7 @@ +""" +Ensure Setuptools dependencies are present. +""" +import sys +import pathlib + +sys.path.extend(map(str, pathlib.Path(__file__).parent.glob('*.whl'))) diff --git a/_setuptools_vendored/appdirs-1.4.3-py2.py3-none-any.whl b/_setuptools_vendored/appdirs-1.4.3-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..fda65956cad15586feb3f6bf2a5df8d63cd38324 GIT binary patch literal 12154 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 uC;!E1? literal 0 HcmV?d00001 diff --git a/_setuptools_vendored/more_itertools-8.8.0-py3-none-any.whl b/_setuptools_vendored/more_itertools-8.8.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..6bc13f23b27d2e6edde73f955b3669a8d5306123 GIT binary patch literal 48787 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 z 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�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 literal 0 HcmV?d00001 diff --git a/_setuptools_vendored/ordered_set-3.1.1-py2.py3-none-any.whl b/_setuptools_vendored/ordered_set-3.1.1-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..e2da8296dde68f342e0e0cbe10148a56ac475806 GIT binary patch literal 7813 zcmai(Wl)@3m$re%p>cQjMuNLF!QBZKg1b8;1h?Q$aOpq@?(Xhx9Y{lPX)F*3nY=SK z-#h2jH*=m{t9Cs<)>W%^-D}srYiX$hkckiw5YQ36XXj{u2RQ)QI0y(7;s^+THUu|M zYa34+YYQ(MZ*KR1=~pLil1nT`U**t;*-O&|=G0__D_z zgdApeW=5Jm|Cqt1RXvwa{a7YKUsuB{N;UKJeSF$qf~I?Xy>|kf&d`oWoEGOnvRcLQ z;!f8LZo_fu(fpm5Q-P(SoQ*gw!wQ8vE}P?~sEv5vmFcQ#t6mU|Lo_Xb;rzxP+al|I zxq=5Hfq|QC>HOFB_tNoHcbp1b!bofrPC3^C+x6!xdgd+f$kRTGwslXBA41ASU*K8@>9Wtv!9&Z}EZjXJCv0lNpTa>|8U7{4G zD=}-YKM)IJP)Bh#+PhSnRUL9`CQIBgs(pzq#d?}$){NRRd*~Fjp1|{_vnj=5h&x%0 zCO)Mtez?NQ&0)gN#yB0+F>7aWs{>W_@rJ`T-FjYiJ-`)xXNU^?uDnv>kL>Fay4j3+ zjrzv!oqfe<5?{s|g;kKU0m=l~GkY}zDcR_sepL`TbIRDg=tafYwznyImI*3q(5s3g z=EUPu)51TbAQ~YI9?E%d_i66UTSm{{#9Zd__fa*NHJQ~YXFW!yeCGP)rYl7j@ytdL z^cNl;@s3-?3my^n%nHwKND14R))-noX{4J}va%-)44-rn`#!@2kM9n|9&JYbjjui; zt0Z5kwU{GxF+&J=q4T@QB47P`Q?3^lTi6rxOvHcsWLm%ctt0a#~ou>xU5WM3sf%5lLC(d9mrr1E9iSbcqvh>+>c zHudZdUhS5#MQ`XC?e3L;)#XLN(9F(3SBkO|js|b3zd+(!UUR$v?&@Ky5^zTx75eP> zVV;?XH)?ne60A2rdX~Q@tddr$qtbI9y?9JZP^p&QF00||?6z8ArQTi?a}ruL!BZC_ z9lM;1dFk5xYXnExSnOawR4Y`4J%Qr6l6TbADW!AxpNY9oCE=06g&N_avp8QwnAIby z3x3cHt^o+_nIvwRJwP16Yi#KF{B-HG=yCSt3;=m#h`Ia_KUWl!@UL`Hcd}raYeUO1l^P<%{AjPixL6-(DDP+dsA2X9SNGvnhA@emMld=B1&81bdI8l<0 zw&L1NgUJFDPcZ*L(J_U*6`#OY$Quk^0S0AzTP}6HB-_B-jRAh1nh6xh!VMPKkd4}%cw)ng6rZObizY< z-Inf^+5^6>V7^W+OJt8SPT;sFjUoo`%z11)5^&Gt^m?=z8%hLfgUMXyohGg7i|mno zYe*x-2tE;npWMEmzuZbyl{pKF*-9tT82PF*HF}ClNGyO;(?YW4cd9LFo0O2`(1mw& zR>c2u(OiB8NV$WQwkXSF`ivQSz=v0Hq$p3;NS&P+zcGf1QE<}UfB4uy@@p{#Hgj_! ziXKpi=5~}GvFr)M|@EcarHu5a~$^WEYHm&#l@eQ zZ*pzIahsu?eYl3paae7;1TQ7@^Ht^M5Yb|~eaCEz+7N2LU-M@jTT>t0(sekUSUC=cD?gnpW&QksOa&+O?D(P}eSTSm@8 zRLI?rtF~seMEsf9bZ6I%u?W_2S?w-m0$-Ft!>O`EHXId^G7E7qTlLmC8~ggHtuYww zy09rC*Vm3vr!b*dCY7}kTQD={Twi&7Q0LwAm^3UeaV%|oy?DqMuntFY)g5_r^C4pMvu)& zS8lo0A*4!VA;1v6_2wFHt_(a1jrtF!#b?-Z3N~Jnr-3mI1h{(FVQOx+I6(m~o4=|4 zd?*oo7!Gf`P?^o5A4b41>dlr&IqqY*Xzu2HEbjX7qGkf;kvj?PGd!B#JmTX;V-!VZ@_v3?M6!UhIN-VU?g) z=o>_NzsLg(wvJhf4>FN(!Dd#lZxX+{}Oh}c>fSdC{ z?`yuYLeG(7KqF$St31HfKD%eVt*~`nZ@{Mz@^m8ysf!P^(zH(T1d zxpxH~QnFE!CuSR|ebugWbxGyBzPtv3Z9R+Mm{D3cx?C+SLgbcEe|9rN z3D{DCF%P!G#~5th8VHT81yrj|xJeElqdvB`RAIKvMqDb=`(P^HOu zBn=o2=qtOQ)*o2f74>dLV6FoCRiuwhQiqt?^Bj81$~Du>FD)08`rv0G>#4x7JA4Ua zUt4qDE>nJAT(pRAd`F57gxk(v-eVIr}PDLcl2GaL2$> z0cs5#=qcV@`EI-6)U#o$4gduK@p8V%#*<9*wI-PIEP}K*-EJ%WItx=xMHA)-yG%zGPaaXZXb z-(^vy7A0DpQJezIR%a_ygf0WXB=?_3@u`*<>cJMN%y%jCE+|$9S3LNSfuR!3F}Gaq z2fsdsh=kn1X@)ckf!4+zRnFY|@4=Jq&3BYPb+<{~Xat6G5!`3!#WS8TZ{(t>*qce`R3tQfF|}b-o+O`fR$s56^JUed+2Wz zUWWzZUSr(<(7v7OmVd2oU-Z3AQPsIkb(b&9AkMNtxYJkMOA21RMw8l&>t!)zefb5T zN69gAScs%Nc1_sCfe8RnGMJ2f7-KRq8}PP)3XfB~JL?pxZ*t$~GsqfS#++H9dM2A< ztfM|>-&s5{`L2SJ65DD@c4E3CRmHhdJ()9Vg~j!QJLIBMcBuz@tr1qgDJ$5HdtC@4 zDqNJiV}wF=g%Pj4=(bI(n{?|nQ9Jdj%r{Ptv_68}Qe0QxYm9k?=n%6sQk>yNx4bz7 zNIkaIvb5pSpV`0K3!E%sgmljd(*@-?-M-D`eEaKXHom}1_(eVEW>=Om*DXk>WmUgU z)=kB^_G!`EDy;N&AxcN}i1KC#SF7{dPUCt+sa0o*fwmK|QWkgMqs2X4Ho_bB|ixDlRBkPxlc%+P6MR zxA8X7W2@}RgLL2)Udr!8iUoP?f%Yo{j+Ma-S1iy4&)$TE)m^OZN8bxcyFt-ymi?bK zL3i0KQg^>s<>p4{nH71diZ4$b)BGa9*(;}3E>&+;Kei8p_5U&T{O5O}D+$upror7@Q zf0xp0Ka#k%<*`&DTuS*CIs9=AJXXwOSqptIHL5e7e3IF?64jdXu|`C{q!`uSRVIL1 z1Quwpa=a>1(-LyqdP>o5&@3r@ml^I5$?rjQAbP?ua9=dXr`3D=j#C`F_uYvmT=i75 zUzBS5=W$obQ4RUa`b>mT2(31tr z>dBTHxwygF@SkTa`k~EvWOe(SdkygvV=c1haX$4tBg`(^$g*n6Z{ib~s+jhNq95TQ zQRyzCGpJ)eVfxM`^Z12Q;kU;bt~Y5K%^#T3BV1BqiBOj>nJw|9&kQM;q?oBzNftzU>MZN5ib}G4 zEB$1@1Yw>5b-8U>OPD)aG&!W^I6i%<@pj42S<&FZl0MJ7Gd|jYNw3E~4VRsmmlk{6U`ap7ft1Nx)QoZU%2N0Tf2R2xtn;1tID+13DygXcGykx z)p$}Z)Z^}$`og>$)*Px?UBNW+!7BeUZ}MwKvAksCturXJ_;<@jDy71B?34RU`$Md$ ztwB=O#=Yco#e@2 zToTn3@~GaoP|4501EuR>VJ|q_XVsf?1u{ zuwvUF`>XfwV-3TVZHQxyUVKzN0F6PKi2{VtiB6V|O>N$`Br4)g(L@RUC>D4RO;1Q zKUy;mtxAaI{8%g=!EdSHdpgn;y}Uc`1An_^vXh_=-v;J@S&ffB43d(FJTr^0cW38! zWJH25V!u*-DlL{!d3gPh0%w%&ICB|B`c_xDyV1fV(&U~%g7wkCl@dCJf<#h61sEm& z)EOfed5mU*E?wGHr9h;-O3z;JG2z6B#Xsf(kf@e+}M$fLn5NA{BX- zk=H>!u89})%3#A+3aW~O+2|5X9eHR)hM5;q(O^cYj5yVffE@{-8G$zqBSmVk(1pAsx{{YCFTC&e=etxP~30a-uBc!8VG>yFqc>wv(=xQ>3 zKi&u<6(i_nYQqM-QBZ^~NhC-7rp)X^25~-Zp)gti16Z#H({i2`3>t6Qn2tJ84oTzg z?UmmCP-`*QeG(Ry#@-*-DuBMs_a#3`^?3vhN#n;GuU8#2`k?8pqRbbkEN+{T4vJSa zopC12Kn6Z&QhwadO2n zbio{Hj9nnndRZH_jDrzvXySJM5CF~vHgIctie6W8f-gNnUXPt~Ss;HQ{RvQtbmU5; zqWf8`VxeWIhQYv=EbK9HnE7ge&$#BBSwt;^{0DX9w9RbW8WZvUkJY_7i2+3od%f5! zS-f%WuxyTHAF8rChp^}HmLtnU$pXn)ONW?y?(o+r|_nqG}@(F0mNO0ZuD|cCfyU( z_~Qcl>tR%z)U)6raxLF?uOQH8HZdxmJASp8*Qa>4)nQz+yazYD5|-0Oi7Tg)KIZcs z0A%NCZvkr_uc}awRCXhRMzF^upI@wHaA_KKFr_eJ_cLVv41DsfdtDA3RUp|&U>}3% zizZH4;m5P6y~%gqa}a*rpzH$Z$q!_0;qI6-Y1eS+kyUU{g~!3U&9QO9-{BLIU_9f? z9S}`#QQ?~cU8`b*0-cdO_+8gviQ$7ZzcCH3h^tH|KkN;l_vu`Qnz%U+MoQGF$C_@6 zOjguz|3V+n^9jx{i>G|Ccz17LA^kRmiP$vCW6 zjUv8Sg9lf}soIg*3nY8q(DE@-Qg61m-7=;Ib*U3uB6Dk4GNsT?1JZWtykz!Jqpdz< znt6wRq`DDV-!adDRSW`W+Nsmdh;0~t1BGeTUVZmilapjP5Q(IxiKsopL>syB)@w40+tIx zIFHzSN@H1a`MPg6N4e2G5Y8>TeuWWeC&ZIKd1sKF@`vj?<_3@$ni(kRU?eFdh+?Rz333N$T{NQ@l8Q?! zkh#Q^I>j*d7grRTTwQLQZ?A6?k@X+gAbU!osL4nXiaf4RA2}TuWgX3R&N0u4l}M@K z$A`rwQsWKtj@n7f)rDwcxAneS@$(zcC)Vd~n)A1q{~UDolw-U8iokTcXHO^Yic#YQb&-i(b(#xeVtEulc$2drfrmY$ZI&{?;}O$339;xSP0xqMPN}q~tf`NH`NYtQaw`W2%e* z@C01Qio~pP930fKx`(Tyf8HEyLTOYjL1<)(={=Iw-lqH-B5>fDCNm=Zq&&qI$(1|5 zHcfrH@?NEE6rM%#2{SIGYQR32C0i#?_)x=u0r87Xq`|rhXIZ5WgWNPz4Chc{qyg}^ zDYD{Wcg;{|mf9s%I|tHRs!;2tdjclJWv(H zF*7yWsKdL&zvHPntqM}%8s}-yQBoP_;*RA(`=mX`JI61y%0IVY}j$r!b%|D zEu3w9ZJfEi{kajc;pk1WDL0`r)hk+0g5MY0T;UAzn3i6sd;E=ga&-XG2 zk-Jd4bwMsla2!$Jmy%r;QsE8Wh&&`DB>bDBv?%4#{AY3gx7#Dic|)>UHfiF@d0~X$Xmi76xnL&C~p%7L<{CVqCz2SQ+3fLg#EZ6hrYpQ5ObTU{#@PTYd zrOsk`+Jz()2Q*~Wuq3;BQiJLR|1gi-Q2}4Qe@udiW~(4kB+^i<{fZq$`A??C)c|VX zgN>PIz{QDeXa{4B1HJA=RoXfz0=n7>I>oC-l)Kjd*?}>!my$jidRhrs6o<D+~MA z_SY%p|IGdW`N99nL_jDD8~P)Q|B?H@y2HN{|9)Bjn;1a)55#|8-G7Jvy@~z}6{7gR zq5s)Zf5-h@_5a2#{R8eVJ@9wz-*x}r*d|1F%Ky8{|LOuQRTR{J<)Hpq>wkJDlj>i8 F{{cwQH>Lmp literal 0 HcmV?d00001 diff --git a/_setuptools_vendored/packaging-20.4-py2.py3-none-any.whl b/_setuptools_vendored/packaging-20.4-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..7ae35b72b693f86e66c298d86e4e79260107c5ce GIT binary patch literal 37620 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⪚r4#7D{4-ZZS%m)RuFj1ANUVpvE`J~`q8qV$9 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 literal 0 HcmV?d00001 diff --git a/conftest.py b/conftest.py index d5e851fe50..9ad9c836d5 100644 --- a/conftest.py +++ b/conftest.py @@ -16,11 +16,8 @@ def pytest_addoption(parser): 'setuptools/tests/mod_with_constant.py', 'setuptools/_distutils', '_distutils_hack', - 'setuptools/extern', - 'pkg_resources/extern', 'pkg_resources/tests/data', - 'setuptools/_vendor', - 'pkg_resources/_vendor', + '_setuptools_vendored', ] diff --git a/pavement.py b/pavement.py deleted file mode 100644 index 81ff6f1201..0000000000 --- a/pavement.py +++ /dev/null @@ -1,70 +0,0 @@ -import re -import sys -import subprocess - -from paver.easy import task, path as Path - - -def remove_all(paths): - for path in paths: - path.rmtree() if path.isdir() else path.remove() - - -@task -def update_vendored(): - update_pkg_resources() - update_setuptools() - - -def rewrite_packaging(pkg_files, new_root): - """ - Rewrite imports in packaging to redirect to vendored copies. - """ - for file in pkg_files.glob('*.py'): - text = file.text() - text = re.sub(r' (pyparsing)', rf' {new_root}.\1', text) - text = text.replace( - 'from six.moves.urllib import parse', - 'from urllib import parse', - ) - file.write_text(text) - - -def clean(vendor): - """ - Remove all files out of the vendor directory except the meta - data (as pip uninstall doesn't support -t). - """ - remove_all( - path - for path in vendor.glob('*') - if path.basename() != 'vendored.txt' - ) - - -def install(vendor): - clean(vendor) - install_args = [ - sys.executable, - '-m', 'pip', - 'install', - '-r', str(vendor / 'vendored.txt'), - '-t', str(vendor), - ] - subprocess.check_call(install_args) - remove_all(vendor.glob('*.dist-info')) - remove_all(vendor.glob('*.egg-info')) - remove_all(vendor.glob('six.py')) - (vendor / '__init__.py').write_text('') - - -def update_pkg_resources(): - vendor = Path('pkg_resources/_vendor') - install(vendor) - rewrite_packaging(vendor / 'packaging', 'pkg_resources.extern') - - -def update_setuptools(): - vendor = Path('setuptools/_vendor') - install(vendor) - rewrite_packaging(vendor / 'packaging', 'setuptools.extern') diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index c84f1dd9e8..d89465e909 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -71,12 +71,13 @@ except ImportError: importlib_machinery = None -from pkg_resources.extern import appdirs -from pkg_resources.extern import packaging -__import__('pkg_resources.extern.packaging.version') -__import__('pkg_resources.extern.packaging.specifiers') -__import__('pkg_resources.extern.packaging.requirements') -__import__('pkg_resources.extern.packaging.markers') +import _setuptools_vendored # noqa: F401 + +import appdirs +import packaging.version +import packaging.specifiers +import packaging.requirements +import packaging.markers if sys.version_info < (3, 5): raise RuntimeError("Python 3.5 or later is required") diff --git a/pkg_resources/_vendor/__init__.py b/pkg_resources/_vendor/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/pkg_resources/_vendor/appdirs.py b/pkg_resources/_vendor/appdirs.py deleted file mode 100644 index ae67001af8..0000000000 --- a/pkg_resources/_vendor/appdirs.py +++ /dev/null @@ -1,608 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (c) 2005-2010 ActiveState Software Inc. -# Copyright (c) 2013 Eddy PetriÈ™or - -"""Utilities for determining application-specific dirs. - -See for details and usage. -""" -# Dev Notes: -# - MSDN on where to store app data files: -# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 -# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html -# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html - -__version_info__ = (1, 4, 3) -__version__ = '.'.join(map(str, __version_info__)) - - -import sys -import os - -PY3 = sys.version_info[0] == 3 - -if PY3: - unicode = str - -if sys.platform.startswith('java'): - import platform - os_name = platform.java_ver()[3][0] - if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. - system = 'win32' - elif os_name.startswith('Mac'): # "Mac OS X", etc. - system = 'darwin' - else: # "Linux", "SunOS", "FreeBSD", etc. - # Setting this to "linux2" is not ideal, but only Windows or Mac - # are actually checked for and the rest of the module expects - # *sys.platform* style strings. - system = 'linux2' -else: - system = sys.platform - - - -def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): - r"""Return full path to the user-specific data dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "roaming" (boolean, default False) can be set True to use the Windows - roaming appdata directory. That means that for users on a Windows - network setup for roaming profiles, this user data will be - sync'd on login. See - - for a discussion of issues. - - Typical user data directories are: - Mac OS X: ~/Library/Application Support/ - Unix: ~/.local/share/ # or in $XDG_DATA_HOME, if defined - Win XP (not roaming): C:\Documents and Settings\\Application Data\\ - Win XP (roaming): C:\Documents and Settings\\Local Settings\Application Data\\ - Win 7 (not roaming): C:\Users\\AppData\Local\\ - Win 7 (roaming): C:\Users\\AppData\Roaming\\ - - For Unix, we follow the XDG spec and support $XDG_DATA_HOME. - That means, by default "~/.local/share/". - """ - if system == "win32": - if appauthor is None: - appauthor = appname - const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" - path = os.path.normpath(_get_win_folder(const)) - if appname: - if appauthor is not False: - path = os.path.join(path, appauthor, appname) - else: - path = os.path.join(path, appname) - elif system == 'darwin': - path = os.path.expanduser('~/Library/Application Support/') - if appname: - path = os.path.join(path, appname) - else: - path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): - r"""Return full path to the user-shared data dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "multipath" is an optional parameter only applicable to *nix - which indicates that the entire list of data dirs should be - returned. By default, the first item from XDG_DATA_DIRS is - returned, or '/usr/local/share/', - if XDG_DATA_DIRS is not set - - Typical site data directories are: - Mac OS X: /Library/Application Support/ - Unix: /usr/local/share/ or /usr/share/ - Win XP: C:\Documents and Settings\All Users\Application Data\\ - Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) - Win 7: C:\ProgramData\\ # Hidden, but writeable on Win 7. - - For Unix, this is using the $XDG_DATA_DIRS[0] default. - - WARNING: Do not use this on Windows. See the Vista-Fail note above for why. - """ - if system == "win32": - if appauthor is None: - appauthor = appname - path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) - if appname: - if appauthor is not False: - path = os.path.join(path, appauthor, appname) - else: - path = os.path.join(path, appname) - elif system == 'darwin': - path = os.path.expanduser('/Library/Application Support') - if appname: - path = os.path.join(path, appname) - else: - # XDG default for $XDG_DATA_DIRS - # only first, if multipath is False - path = os.getenv('XDG_DATA_DIRS', - os.pathsep.join(['/usr/local/share', '/usr/share'])) - pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] - if appname: - if version: - appname = os.path.join(appname, version) - pathlist = [os.sep.join([x, appname]) for x in pathlist] - - if multipath: - path = os.pathsep.join(pathlist) - else: - path = pathlist[0] - return path - - if appname and version: - path = os.path.join(path, version) - return path - - -def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): - r"""Return full path to the user-specific config dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "roaming" (boolean, default False) can be set True to use the Windows - roaming appdata directory. That means that for users on a Windows - network setup for roaming profiles, this user data will be - sync'd on login. See - - for a discussion of issues. - - Typical user config directories are: - Mac OS X: same as user_data_dir - Unix: ~/.config/ # or in $XDG_CONFIG_HOME, if defined - Win *: same as user_data_dir - - For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. - That means, by default "~/.config/". - """ - if system in ["win32", "darwin"]: - path = user_data_dir(appname, appauthor, None, roaming) - else: - path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): - r"""Return full path to the user-shared data dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "multipath" is an optional parameter only applicable to *nix - which indicates that the entire list of config dirs should be - returned. By default, the first item from XDG_CONFIG_DIRS is - returned, or '/etc/xdg/', if XDG_CONFIG_DIRS is not set - - Typical site config directories are: - Mac OS X: same as site_data_dir - Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ for each value in - $XDG_CONFIG_DIRS - Win *: same as site_data_dir - Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) - - For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False - - WARNING: Do not use this on Windows. See the Vista-Fail note above for why. - """ - if system in ["win32", "darwin"]: - path = site_data_dir(appname, appauthor) - if appname and version: - path = os.path.join(path, version) - else: - # XDG default for $XDG_CONFIG_DIRS - # only first, if multipath is False - path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') - pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] - if appname: - if version: - appname = os.path.join(appname, version) - pathlist = [os.sep.join([x, appname]) for x in pathlist] - - if multipath: - path = os.pathsep.join(pathlist) - else: - path = pathlist[0] - return path - - -def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): - r"""Return full path to the user-specific cache dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "opinion" (boolean) can be False to disable the appending of - "Cache" to the base app data dir for Windows. See - discussion below. - - Typical user cache directories are: - Mac OS X: ~/Library/Caches/ - Unix: ~/.cache/ (XDG default) - Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache - Vista: C:\Users\\AppData\Local\\\Cache - - On Windows the only suggestion in the MSDN docs is that local settings go in - the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming - app data dir (the default returned by `user_data_dir` above). Apps typically - put cache data somewhere *under* the given dir here. Some examples: - ...\Mozilla\Firefox\Profiles\\Cache - ...\Acme\SuperApp\Cache\1.0 - OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. - This can be disabled with the `opinion=False` option. - """ - if system == "win32": - if appauthor is None: - appauthor = appname - path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) - if appname: - if appauthor is not False: - path = os.path.join(path, appauthor, appname) - else: - path = os.path.join(path, appname) - if opinion: - path = os.path.join(path, "Cache") - elif system == 'darwin': - path = os.path.expanduser('~/Library/Caches') - if appname: - path = os.path.join(path, appname) - else: - path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): - r"""Return full path to the user-specific state dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "roaming" (boolean, default False) can be set True to use the Windows - roaming appdata directory. That means that for users on a Windows - network setup for roaming profiles, this user data will be - sync'd on login. See - - for a discussion of issues. - - Typical user state directories are: - Mac OS X: same as user_data_dir - Unix: ~/.local/state/ # or in $XDG_STATE_HOME, if defined - Win *: same as user_data_dir - - For Unix, we follow this Debian proposal - to extend the XDG spec and support $XDG_STATE_HOME. - - That means, by default "~/.local/state/". - """ - if system in ["win32", "darwin"]: - path = user_data_dir(appname, appauthor, None, roaming) - else: - path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state")) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): - r"""Return full path to the user-specific log dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "opinion" (boolean) can be False to disable the appending of - "Logs" to the base app data dir for Windows, and "log" to the - base cache dir for Unix. See discussion below. - - Typical user log directories are: - Mac OS X: ~/Library/Logs/ - Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined - Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs - Vista: C:\Users\\AppData\Local\\\Logs - - On Windows the only suggestion in the MSDN docs is that local settings - go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in - examples of what some windows apps use for a logs dir.) - - OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` - value for Windows and appends "log" to the user cache dir for Unix. - This can be disabled with the `opinion=False` option. - """ - if system == "darwin": - path = os.path.join( - os.path.expanduser('~/Library/Logs'), - appname) - elif system == "win32": - path = user_data_dir(appname, appauthor, version) - version = False - if opinion: - path = os.path.join(path, "Logs") - else: - path = user_cache_dir(appname, appauthor, version) - version = False - if opinion: - path = os.path.join(path, "log") - if appname and version: - path = os.path.join(path, version) - return path - - -class AppDirs(object): - """Convenience wrapper for getting application dirs.""" - def __init__(self, appname=None, appauthor=None, version=None, - roaming=False, multipath=False): - self.appname = appname - self.appauthor = appauthor - self.version = version - self.roaming = roaming - self.multipath = multipath - - @property - def user_data_dir(self): - return user_data_dir(self.appname, self.appauthor, - version=self.version, roaming=self.roaming) - - @property - def site_data_dir(self): - return site_data_dir(self.appname, self.appauthor, - version=self.version, multipath=self.multipath) - - @property - def user_config_dir(self): - return user_config_dir(self.appname, self.appauthor, - version=self.version, roaming=self.roaming) - - @property - def site_config_dir(self): - return site_config_dir(self.appname, self.appauthor, - version=self.version, multipath=self.multipath) - - @property - def user_cache_dir(self): - return user_cache_dir(self.appname, self.appauthor, - version=self.version) - - @property - def user_state_dir(self): - return user_state_dir(self.appname, self.appauthor, - version=self.version) - - @property - def user_log_dir(self): - return user_log_dir(self.appname, self.appauthor, - version=self.version) - - -#---- internal support stuff - -def _get_win_folder_from_registry(csidl_name): - """This is a fallback technique at best. I'm not sure if using the - registry for this guarantees us the correct answer for all CSIDL_* - names. - """ - if PY3: - import winreg as _winreg - else: - import _winreg - - shell_folder_name = { - "CSIDL_APPDATA": "AppData", - "CSIDL_COMMON_APPDATA": "Common AppData", - "CSIDL_LOCAL_APPDATA": "Local AppData", - }[csidl_name] - - key = _winreg.OpenKey( - _winreg.HKEY_CURRENT_USER, - r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" - ) - dir, type = _winreg.QueryValueEx(key, shell_folder_name) - return dir - - -def _get_win_folder_with_pywin32(csidl_name): - from win32com.shell import shellcon, shell - dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) - # Try to make this a unicode path because SHGetFolderPath does - # not return unicode strings when there is unicode data in the - # path. - try: - dir = unicode(dir) - - # Downgrade to short path name if have highbit chars. See - # . - has_high_char = False - for c in dir: - if ord(c) > 255: - has_high_char = True - break - if has_high_char: - try: - import win32api - dir = win32api.GetShortPathName(dir) - except ImportError: - pass - except UnicodeError: - pass - return dir - - -def _get_win_folder_with_ctypes(csidl_name): - import ctypes - - csidl_const = { - "CSIDL_APPDATA": 26, - "CSIDL_COMMON_APPDATA": 35, - "CSIDL_LOCAL_APPDATA": 28, - }[csidl_name] - - buf = ctypes.create_unicode_buffer(1024) - ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) - - # Downgrade to short path name if have highbit chars. See - # . - has_high_char = False - for c in buf: - if ord(c) > 255: - has_high_char = True - break - if has_high_char: - buf2 = ctypes.create_unicode_buffer(1024) - if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): - buf = buf2 - - return buf.value - -def _get_win_folder_with_jna(csidl_name): - import array - from com.sun import jna - from com.sun.jna.platform import win32 - - buf_size = win32.WinDef.MAX_PATH * 2 - buf = array.zeros('c', buf_size) - shell = win32.Shell32.INSTANCE - shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) - dir = jna.Native.toString(buf.tostring()).rstrip("\0") - - # Downgrade to short path name if have highbit chars. See - # . - has_high_char = False - for c in dir: - if ord(c) > 255: - has_high_char = True - break - if has_high_char: - buf = array.zeros('c', buf_size) - kernel = win32.Kernel32.INSTANCE - if kernel.GetShortPathName(dir, buf, buf_size): - dir = jna.Native.toString(buf.tostring()).rstrip("\0") - - return dir - -if system == "win32": - try: - import win32com.shell - _get_win_folder = _get_win_folder_with_pywin32 - except ImportError: - try: - from ctypes import windll - _get_win_folder = _get_win_folder_with_ctypes - except ImportError: - try: - import com.sun.jna - _get_win_folder = _get_win_folder_with_jna - except ImportError: - _get_win_folder = _get_win_folder_from_registry - - -#---- self test code - -if __name__ == "__main__": - appname = "MyApp" - appauthor = "MyCompany" - - props = ("user_data_dir", - "user_config_dir", - "user_cache_dir", - "user_state_dir", - "user_log_dir", - "site_data_dir", - "site_config_dir") - - print("-- app dirs %s --" % __version__) - - print("-- app dirs (with optional 'version')") - dirs = AppDirs(appname, appauthor, version="1.0") - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) - - print("\n-- app dirs (without optional 'version')") - dirs = AppDirs(appname, appauthor) - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) - - print("\n-- app dirs (without optional 'appauthor')") - dirs = AppDirs(appname) - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) - - print("\n-- app dirs (with disabled 'appauthor')") - dirs = AppDirs(appname, appauthor=False) - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/pkg_resources/_vendor/packaging/__about__.py b/pkg_resources/_vendor/packaging/__about__.py deleted file mode 100644 index 4d998578d7..0000000000 --- a/pkg_resources/_vendor/packaging/__about__.py +++ /dev/null @@ -1,27 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] - -__title__ = "packaging" -__summary__ = "Core utilities for Python packages" -__uri__ = "https://github.com/pypa/packaging" - -__version__ = "20.4" - -__author__ = "Donald Stufft and individual contributors" -__email__ = "donald@stufft.io" - -__license__ = "BSD-2-Clause or Apache-2.0" -__copyright__ = "Copyright 2014-2019 %s" % __author__ diff --git a/pkg_resources/_vendor/packaging/__init__.py b/pkg_resources/_vendor/packaging/__init__.py deleted file mode 100644 index a0cf67df52..0000000000 --- a/pkg_resources/_vendor/packaging/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -from .__about__ import ( - __author__, - __copyright__, - __email__, - __license__, - __summary__, - __title__, - __uri__, - __version__, -) - -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] diff --git a/pkg_resources/_vendor/packaging/_compat.py b/pkg_resources/_vendor/packaging/_compat.py deleted file mode 100644 index e54bd4ede8..0000000000 --- a/pkg_resources/_vendor/packaging/_compat.py +++ /dev/null @@ -1,38 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import sys - -from ._typing import TYPE_CHECKING - -if TYPE_CHECKING: # pragma: no cover - from typing import Any, Dict, Tuple, Type - - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -# flake8: noqa - -if PY3: - string_types = (str,) -else: - string_types = (basestring,) - - -def with_metaclass(meta, *bases): - # type: (Type[Any], Tuple[Type[Any], ...]) -> Any - """ - Create a base class with a metaclass. - """ - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): # type: ignore - def __new__(cls, name, this_bases, d): - # type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any - return meta(name, bases, d) - - return type.__new__(metaclass, "temporary_class", (), {}) diff --git a/pkg_resources/_vendor/packaging/_structures.py b/pkg_resources/_vendor/packaging/_structures.py deleted file mode 100644 index 800d5c5588..0000000000 --- a/pkg_resources/_vendor/packaging/_structures.py +++ /dev/null @@ -1,86 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - - -class InfinityType(object): - def __repr__(self): - # type: () -> str - return "Infinity" - - def __hash__(self): - # type: () -> int - return hash(repr(self)) - - def __lt__(self, other): - # type: (object) -> bool - return False - - def __le__(self, other): - # type: (object) -> bool - return False - - def __eq__(self, other): - # type: (object) -> bool - return isinstance(other, self.__class__) - - def __ne__(self, other): - # type: (object) -> bool - return not isinstance(other, self.__class__) - - def __gt__(self, other): - # type: (object) -> bool - return True - - def __ge__(self, other): - # type: (object) -> bool - return True - - def __neg__(self): - # type: (object) -> NegativeInfinityType - return NegativeInfinity - - -Infinity = InfinityType() - - -class NegativeInfinityType(object): - def __repr__(self): - # type: () -> str - return "-Infinity" - - def __hash__(self): - # type: () -> int - return hash(repr(self)) - - def __lt__(self, other): - # type: (object) -> bool - return True - - def __le__(self, other): - # type: (object) -> bool - return True - - def __eq__(self, other): - # type: (object) -> bool - return isinstance(other, self.__class__) - - def __ne__(self, other): - # type: (object) -> bool - return not isinstance(other, self.__class__) - - def __gt__(self, other): - # type: (object) -> bool - return False - - def __ge__(self, other): - # type: (object) -> bool - return False - - def __neg__(self): - # type: (object) -> InfinityType - return Infinity - - -NegativeInfinity = NegativeInfinityType() diff --git a/pkg_resources/_vendor/packaging/_typing.py b/pkg_resources/_vendor/packaging/_typing.py deleted file mode 100644 index 77a8b9185a..0000000000 --- a/pkg_resources/_vendor/packaging/_typing.py +++ /dev/null @@ -1,48 +0,0 @@ -"""For neatly implementing static typing in packaging. - -`mypy` - the static type analysis tool we use - uses the `typing` module, which -provides core functionality fundamental to mypy's functioning. - -Generally, `typing` would be imported at runtime and used in that fashion - -it acts as a no-op at runtime and does not have any run-time overhead by -design. - -As it turns out, `typing` is not vendorable - it uses separate sources for -Python 2/Python 3. Thus, this codebase can not expect it to be present. -To work around this, mypy allows the typing import to be behind a False-y -optional to prevent it from running at runtime and type-comments can be used -to remove the need for the types to be accessible directly during runtime. - -This module provides the False-y guard in a nicely named fashion so that a -curious maintainer can reach here to read this. - -In packaging, all static-typing related imports should be guarded as follows: - - from packaging._typing import TYPE_CHECKING - - if TYPE_CHECKING: - from typing import ... - -Ref: https://github.com/python/mypy/issues/3216 -""" - -__all__ = ["TYPE_CHECKING", "cast"] - -# The TYPE_CHECKING constant defined by the typing module is False at runtime -# but True while type checking. -if False: # pragma: no cover - from typing import TYPE_CHECKING -else: - TYPE_CHECKING = False - -# typing's cast syntax requires calling typing.cast at runtime, but we don't -# want to import typing at runtime. Here, we inform the type checkers that -# we're importing `typing.cast` as `cast` and re-implement typing.cast's -# runtime behavior in a block that is ignored by type checkers. -if TYPE_CHECKING: # pragma: no cover - # not executed at runtime - from typing import cast -else: - # executed at runtime - def cast(type_, value): # noqa - return value diff --git a/pkg_resources/_vendor/packaging/markers.py b/pkg_resources/_vendor/packaging/markers.py deleted file mode 100644 index fd1559c10e..0000000000 --- a/pkg_resources/_vendor/packaging/markers.py +++ /dev/null @@ -1,328 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import operator -import os -import platform -import sys - -from pkg_resources.extern.pyparsing import ParseException, ParseResults, stringStart, stringEnd -from pkg_resources.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString -from pkg_resources.extern.pyparsing import Literal as L # noqa - -from ._compat import string_types -from ._typing import TYPE_CHECKING -from .specifiers import Specifier, InvalidSpecifier - -if TYPE_CHECKING: # pragma: no cover - from typing import Any, Callable, Dict, List, Optional, Tuple, Union - - Operator = Callable[[str, str], bool] - - -__all__ = [ - "InvalidMarker", - "UndefinedComparison", - "UndefinedEnvironmentName", - "Marker", - "default_environment", -] - - -class InvalidMarker(ValueError): - """ - An invalid marker was found, users should refer to PEP 508. - """ - - -class UndefinedComparison(ValueError): - """ - An invalid operation was attempted on a value that doesn't support it. - """ - - -class UndefinedEnvironmentName(ValueError): - """ - A name was attempted to be used that does not exist inside of the - environment. - """ - - -class Node(object): - def __init__(self, value): - # type: (Any) -> None - self.value = value - - def __str__(self): - # type: () -> str - return str(self.value) - - def __repr__(self): - # type: () -> str - return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) - - def serialize(self): - # type: () -> str - raise NotImplementedError - - -class Variable(Node): - def serialize(self): - # type: () -> str - return str(self) - - -class Value(Node): - def serialize(self): - # type: () -> str - return '"{0}"'.format(self) - - -class Op(Node): - def serialize(self): - # type: () -> str - return str(self) - - -VARIABLE = ( - L("implementation_version") - | L("platform_python_implementation") - | L("implementation_name") - | L("python_full_version") - | L("platform_release") - | L("platform_version") - | L("platform_machine") - | L("platform_system") - | L("python_version") - | L("sys_platform") - | L("os_name") - | L("os.name") # PEP-345 - | L("sys.platform") # PEP-345 - | L("platform.version") # PEP-345 - | L("platform.machine") # PEP-345 - | L("platform.python_implementation") # PEP-345 - | L("python_implementation") # undocumented setuptools legacy - | L("extra") # PEP-508 -) -ALIASES = { - "os.name": "os_name", - "sys.platform": "sys_platform", - "platform.version": "platform_version", - "platform.machine": "platform_machine", - "platform.python_implementation": "platform_python_implementation", - "python_implementation": "platform_python_implementation", -} -VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) - -VERSION_CMP = ( - L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") -) - -MARKER_OP = VERSION_CMP | L("not in") | L("in") -MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) - -MARKER_VALUE = QuotedString("'") | QuotedString('"') -MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) - -BOOLOP = L("and") | L("or") - -MARKER_VAR = VARIABLE | MARKER_VALUE - -MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) -MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) - -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() - -MARKER_EXPR = Forward() -MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) -MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) - -MARKER = stringStart + MARKER_EXPR + stringEnd - - -def _coerce_parse_result(results): - # type: (Union[ParseResults, List[Any]]) -> List[Any] - if isinstance(results, ParseResults): - return [_coerce_parse_result(i) for i in results] - else: - return results - - -def _format_marker(marker, first=True): - # type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str - - assert isinstance(marker, (list, tuple, string_types)) - - # Sometimes we have a structure like [[...]] which is a single item list - # where the single item is itself it's own list. In that case we want skip - # the rest of this function so that we don't get extraneous () on the - # outside. - if ( - isinstance(marker, list) - and len(marker) == 1 - and isinstance(marker[0], (list, tuple)) - ): - return _format_marker(marker[0]) - - if isinstance(marker, list): - inner = (_format_marker(m, first=False) for m in marker) - if first: - return " ".join(inner) - else: - return "(" + " ".join(inner) + ")" - elif isinstance(marker, tuple): - return " ".join([m.serialize() for m in marker]) - else: - return marker - - -_operators = { - "in": lambda lhs, rhs: lhs in rhs, - "not in": lambda lhs, rhs: lhs not in rhs, - "<": operator.lt, - "<=": operator.le, - "==": operator.eq, - "!=": operator.ne, - ">=": operator.ge, - ">": operator.gt, -} # type: Dict[str, Operator] - - -def _eval_op(lhs, op, rhs): - # type: (str, Op, str) -> bool - try: - spec = Specifier("".join([op.serialize(), rhs])) - except InvalidSpecifier: - pass - else: - return spec.contains(lhs) - - oper = _operators.get(op.serialize()) # type: Optional[Operator] - if oper is None: - raise UndefinedComparison( - "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) - ) - - return oper(lhs, rhs) - - -class Undefined(object): - pass - - -_undefined = Undefined() - - -def _get_env(environment, name): - # type: (Dict[str, str], str) -> str - value = environment.get(name, _undefined) # type: Union[str, Undefined] - - if isinstance(value, Undefined): - raise UndefinedEnvironmentName( - "{0!r} does not exist in evaluation environment.".format(name) - ) - - return value - - -def _evaluate_markers(markers, environment): - # type: (List[Any], Dict[str, str]) -> bool - groups = [[]] # type: List[List[bool]] - - for marker in markers: - assert isinstance(marker, (list, tuple, string_types)) - - if isinstance(marker, list): - groups[-1].append(_evaluate_markers(marker, environment)) - elif isinstance(marker, tuple): - lhs, op, rhs = marker - - if isinstance(lhs, Variable): - lhs_value = _get_env(environment, lhs.value) - rhs_value = rhs.value - else: - lhs_value = lhs.value - rhs_value = _get_env(environment, rhs.value) - - groups[-1].append(_eval_op(lhs_value, op, rhs_value)) - else: - assert marker in ["and", "or"] - if marker == "or": - groups.append([]) - - return any(all(item) for item in groups) - - -def format_full_version(info): - # type: (sys._version_info) -> str - version = "{0.major}.{0.minor}.{0.micro}".format(info) - kind = info.releaselevel - if kind != "final": - version += kind[0] + str(info.serial) - return version - - -def default_environment(): - # type: () -> Dict[str, str] - if hasattr(sys, "implementation"): - # Ignoring the `sys.implementation` reference for type checking due to - # mypy not liking that the attribute doesn't exist in Python 2.7 when - # run with the `--py27` flag. - iver = format_full_version(sys.implementation.version) # type: ignore - implementation_name = sys.implementation.name # type: ignore - else: - iver = "0" - implementation_name = "" - - return { - "implementation_name": implementation_name, - "implementation_version": iver, - "os_name": os.name, - "platform_machine": platform.machine(), - "platform_release": platform.release(), - "platform_system": platform.system(), - "platform_version": platform.version(), - "python_full_version": platform.python_version(), - "platform_python_implementation": platform.python_implementation(), - "python_version": ".".join(platform.python_version_tuple()[:2]), - "sys_platform": sys.platform, - } - - -class Marker(object): - def __init__(self, marker): - # type: (str) -> None - try: - self._markers = _coerce_parse_result(MARKER.parseString(marker)) - except ParseException as e: - err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( - marker, marker[e.loc : e.loc + 8] - ) - raise InvalidMarker(err_str) - - def __str__(self): - # type: () -> str - return _format_marker(self._markers) - - def __repr__(self): - # type: () -> str - return "".format(str(self)) - - def evaluate(self, environment=None): - # type: (Optional[Dict[str, str]]) -> bool - """Evaluate a marker. - - Return the boolean from evaluating the given marker against the - environment. environment is an optional argument to override all or - part of the determined environment. - - The environment is determined from the current Python process. - """ - current_environment = default_environment() - if environment is not None: - current_environment.update(environment) - - return _evaluate_markers(self._markers, current_environment) diff --git a/pkg_resources/_vendor/packaging/py.typed b/pkg_resources/_vendor/packaging/py.typed deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/pkg_resources/_vendor/packaging/requirements.py b/pkg_resources/_vendor/packaging/requirements.py deleted file mode 100644 index 9495a1df1e..0000000000 --- a/pkg_resources/_vendor/packaging/requirements.py +++ /dev/null @@ -1,145 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import string -import re - -from pkg_resources.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException -from pkg_resources.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine -from pkg_resources.extern.pyparsing import Literal as L # noqa -from urllib import parse as urlparse - -from ._typing import TYPE_CHECKING -from .markers import MARKER_EXPR, Marker -from .specifiers import LegacySpecifier, Specifier, SpecifierSet - -if TYPE_CHECKING: # pragma: no cover - from typing import List - - -class InvalidRequirement(ValueError): - """ - An invalid requirement was found, users should refer to PEP 508. - """ - - -ALPHANUM = Word(string.ascii_letters + string.digits) - -LBRACKET = L("[").suppress() -RBRACKET = L("]").suppress() -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() -COMMA = L(",").suppress() -SEMICOLON = L(";").suppress() -AT = L("@").suppress() - -PUNCTUATION = Word("-_.") -IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) -IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) - -NAME = IDENTIFIER("name") -EXTRA = IDENTIFIER - -URI = Regex(r"[^ ]+")("url") -URL = AT + URI - -EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) -EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") - -VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) -VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) - -VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY -VERSION_MANY = Combine( - VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False -)("_raw_spec") -_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) -_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") - -VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") -VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) - -MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") -MARKER_EXPR.setParseAction( - lambda s, l, t: Marker(s[t._original_start : t._original_end]) -) -MARKER_SEPARATOR = SEMICOLON -MARKER = MARKER_SEPARATOR + MARKER_EXPR - -VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) -URL_AND_MARKER = URL + Optional(MARKER) - -NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) - -REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd -# pkg_resources.extern.pyparsing isn't thread safe during initialization, so we do it eagerly, see -# issue #104 -REQUIREMENT.parseString("x[]") - - -class Requirement(object): - """Parse a requirement. - - Parse a given requirement string into its parts, such as name, specifier, - URL, and extras. Raises InvalidRequirement on a badly-formed requirement - string. - """ - - # TODO: Can we test whether something is contained within a requirement? - # If so how do we do that? Do we need to test against the _name_ of - # the thing as well as the version? What about the markers? - # TODO: Can we normalize the name and extra name? - - def __init__(self, requirement_string): - # type: (str) -> None - try: - req = REQUIREMENT.parseString(requirement_string) - except ParseException as e: - raise InvalidRequirement( - 'Parse error at "{0!r}": {1}'.format( - requirement_string[e.loc : e.loc + 8], e.msg - ) - ) - - self.name = req.name - if req.url: - parsed_url = urlparse.urlparse(req.url) - if parsed_url.scheme == "file": - if urlparse.urlunparse(parsed_url) != req.url: - raise InvalidRequirement("Invalid URL given") - elif not (parsed_url.scheme and parsed_url.netloc) or ( - not parsed_url.scheme and not parsed_url.netloc - ): - raise InvalidRequirement("Invalid URL: {0}".format(req.url)) - self.url = req.url - else: - self.url = None - self.extras = set(req.extras.asList() if req.extras else []) - self.specifier = SpecifierSet(req.specifier) - self.marker = req.marker if req.marker else None - - def __str__(self): - # type: () -> str - parts = [self.name] # type: List[str] - - if self.extras: - parts.append("[{0}]".format(",".join(sorted(self.extras)))) - - if self.specifier: - parts.append(str(self.specifier)) - - if self.url: - parts.append("@ {0}".format(self.url)) - if self.marker: - parts.append(" ") - - if self.marker: - parts.append("; {0}".format(self.marker)) - - return "".join(parts) - - def __repr__(self): - # type: () -> str - return "".format(str(self)) diff --git a/pkg_resources/_vendor/packaging/specifiers.py b/pkg_resources/_vendor/packaging/specifiers.py deleted file mode 100644 index fe09bb1dbb..0000000000 --- a/pkg_resources/_vendor/packaging/specifiers.py +++ /dev/null @@ -1,863 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import abc -import functools -import itertools -import re - -from ._compat import string_types, with_metaclass -from ._typing import TYPE_CHECKING -from .utils import canonicalize_version -from .version import Version, LegacyVersion, parse - -if TYPE_CHECKING: # pragma: no cover - from typing import ( - List, - Dict, - Union, - Iterable, - Iterator, - Optional, - Callable, - Tuple, - FrozenSet, - ) - - ParsedVersion = Union[Version, LegacyVersion] - UnparsedVersion = Union[Version, LegacyVersion, str] - CallableOperator = Callable[[ParsedVersion, str], bool] - - -class InvalidSpecifier(ValueError): - """ - An invalid specifier was found, users should refer to PEP 440. - """ - - -class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore - @abc.abstractmethod - def __str__(self): - # type: () -> str - """ - Returns the str representation of this Specifier like object. This - should be representative of the Specifier itself. - """ - - @abc.abstractmethod - def __hash__(self): - # type: () -> int - """ - Returns a hash value for this Specifier like object. - """ - - @abc.abstractmethod - def __eq__(self, other): - # type: (object) -> bool - """ - Returns a boolean representing whether or not the two Specifier like - objects are equal. - """ - - @abc.abstractmethod - def __ne__(self, other): - # type: (object) -> bool - """ - Returns a boolean representing whether or not the two Specifier like - objects are not equal. - """ - - @abc.abstractproperty - def prereleases(self): - # type: () -> Optional[bool] - """ - Returns whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None - """ - Sets whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @abc.abstractmethod - def contains(self, item, prereleases=None): - # type: (str, Optional[bool]) -> bool - """ - Determines if the given item is contained within this specifier. - """ - - @abc.abstractmethod - def filter(self, iterable, prereleases=None): - # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] - """ - Takes an iterable of items and filters them so that only items which - are contained within this specifier are allowed in it. - """ - - -class _IndividualSpecifier(BaseSpecifier): - - _operators = {} # type: Dict[str, str] - - def __init__(self, spec="", prereleases=None): - # type: (str, Optional[bool]) -> None - match = self._regex.search(spec) - if not match: - raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) - - self._spec = ( - match.group("operator").strip(), - match.group("version").strip(), - ) # type: Tuple[str, str] - - # Store whether or not this Specifier should accept prereleases - self._prereleases = prereleases - - def __repr__(self): - # type: () -> str - pre = ( - ", prereleases={0!r}".format(self.prereleases) - if self._prereleases is not None - else "" - ) - - return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre) - - def __str__(self): - # type: () -> str - return "{0}{1}".format(*self._spec) - - @property - def _canonical_spec(self): - # type: () -> Tuple[str, Union[Version, str]] - return self._spec[0], canonicalize_version(self._spec[1]) - - def __hash__(self): - # type: () -> int - return hash(self._canonical_spec) - - def __eq__(self, other): - # type: (object) -> bool - if isinstance(other, string_types): - try: - other = self.__class__(str(other)) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._canonical_spec == other._canonical_spec - - def __ne__(self, other): - # type: (object) -> bool - if isinstance(other, string_types): - try: - other = self.__class__(str(other)) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._spec != other._spec - - def _get_operator(self, op): - # type: (str) -> CallableOperator - operator_callable = getattr( - self, "_compare_{0}".format(self._operators[op]) - ) # type: CallableOperator - return operator_callable - - def _coerce_version(self, version): - # type: (UnparsedVersion) -> ParsedVersion - if not isinstance(version, (LegacyVersion, Version)): - version = parse(version) - return version - - @property - def operator(self): - # type: () -> str - return self._spec[0] - - @property - def version(self): - # type: () -> str - return self._spec[1] - - @property - def prereleases(self): - # type: () -> Optional[bool] - return self._prereleases - - @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None - self._prereleases = value - - def __contains__(self, item): - # type: (str) -> bool - return self.contains(item) - - def contains(self, item, prereleases=None): - # type: (UnparsedVersion, Optional[bool]) -> bool - - # Determine if prereleases are to be allowed or not. - if prereleases is None: - prereleases = self.prereleases - - # Normalize item to a Version or LegacyVersion, this allows us to have - # a shortcut for ``"2.0" in Specifier(">=2") - normalized_item = self._coerce_version(item) - - # Determine if we should be supporting prereleases in this specifier - # or not, if we do not support prereleases than we can short circuit - # logic if this version is a prereleases. - if normalized_item.is_prerelease and not prereleases: - return False - - # Actually do the comparison to determine if this item is contained - # within this Specifier or not. - operator_callable = self._get_operator(self.operator) # type: CallableOperator - return operator_callable(normalized_item, self.version) - - def filter(self, iterable, prereleases=None): - # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] - - yielded = False - found_prereleases = [] - - kw = {"prereleases": prereleases if prereleases is not None else True} - - # Attempt to iterate over all the values in the iterable and if any of - # them match, yield them. - for version in iterable: - parsed_version = self._coerce_version(version) - - if self.contains(parsed_version, **kw): - # If our version is a prerelease, and we were not set to allow - # prereleases, then we'll store it for later incase nothing - # else matches this specifier. - if parsed_version.is_prerelease and not ( - prereleases or self.prereleases - ): - found_prereleases.append(version) - # Either this is not a prerelease, or we should have been - # accepting prereleases from the beginning. - else: - yielded = True - yield version - - # Now that we've iterated over everything, determine if we've yielded - # any values, and if we have not and we have any prereleases stored up - # then we will go ahead and yield the prereleases. - if not yielded and found_prereleases: - for version in found_prereleases: - yield version - - -class LegacySpecifier(_IndividualSpecifier): - - _regex_str = r""" - (?P(==|!=|<=|>=|<|>)) - \s* - (?P - [^,;\s)]* # Since this is a "legacy" specifier, and the version - # string can be just about anything, we match everything - # except for whitespace, a semi-colon for marker support, - # a closing paren since versions can be enclosed in - # them, and a comma since it's a version separator. - ) - """ - - _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) - - _operators = { - "==": "equal", - "!=": "not_equal", - "<=": "less_than_equal", - ">=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - } - - def _coerce_version(self, version): - # type: (Union[ParsedVersion, str]) -> LegacyVersion - if not isinstance(version, LegacyVersion): - version = LegacyVersion(str(version)) - return version - - def _compare_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective == self._coerce_version(spec) - - def _compare_not_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective != self._coerce_version(spec) - - def _compare_less_than_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective <= self._coerce_version(spec) - - def _compare_greater_than_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective >= self._coerce_version(spec) - - def _compare_less_than(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective < self._coerce_version(spec) - - def _compare_greater_than(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective > self._coerce_version(spec) - - -def _require_version_compare( - fn # type: (Callable[[Specifier, ParsedVersion, str], bool]) -): - # type: (...) -> Callable[[Specifier, ParsedVersion, str], bool] - @functools.wraps(fn) - def wrapped(self, prospective, spec): - # type: (Specifier, ParsedVersion, str) -> bool - if not isinstance(prospective, Version): - return False - return fn(self, prospective, spec) - - return wrapped - - -class Specifier(_IndividualSpecifier): - - _regex_str = r""" - (?P(~=|==|!=|<=|>=|<|>|===)) - (?P - (?: - # The identity operators allow for an escape hatch that will - # do an exact string match of the version you wish to install. - # This will not be parsed by PEP 440 and we cannot determine - # any semantic meaning from it. This operator is discouraged - # but included entirely as an escape hatch. - (?<====) # Only match for the identity operator - \s* - [^\s]* # We just match everything, except for whitespace - # since we are only testing for strict identity. - ) - | - (?: - # The (non)equality operators allow for wild card and local - # versions to be specified so we have to define these two - # operators separately to enable that. - (?<===|!=) # Only match for equals and not equals - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)* # release - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - - # You cannot use a wild card and a dev or local version - # together so group them with a | and make them optional. - (?: - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local - | - \.\* # Wild card syntax of .* - )? - ) - | - (?: - # The compatible operator requires at least two digits in the - # release segment. - (?<=~=) # Only match for the compatible operator - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - ) - | - (?: - # All other operators only allow a sub set of what the - # (non)equality operators do. Specifically they do not allow - # local versions to be specified nor do they allow the prefix - # matching wild cards. - (?=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - "===": "arbitrary", - } - - @_require_version_compare - def _compare_compatible(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - - # Compatible releases have an equivalent combination of >= and ==. That - # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to - # implement this in terms of the other specifiers instead of - # implementing it ourselves. The only thing we need to do is construct - # the other specifiers. - - # We want everything but the last item in the version, but we want to - # ignore post and dev releases and we want to treat the pre-release as - # it's own separate segment. - prefix = ".".join( - list( - itertools.takewhile( - lambda x: (not x.startswith("post") and not x.startswith("dev")), - _version_split(spec), - ) - )[:-1] - ) - - # Add the prefix notation to the end of our string - prefix += ".*" - - return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( - prospective, prefix - ) - - @_require_version_compare - def _compare_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - - # We need special logic to handle prefix matching - if spec.endswith(".*"): - # In the case of prefix matching we want to ignore local segment. - prospective = Version(prospective.public) - # Split the spec out by dots, and pretend that there is an implicit - # dot in between a release segment and a pre-release segment. - split_spec = _version_split(spec[:-2]) # Remove the trailing .* - - # Split the prospective version out by dots, and pretend that there - # is an implicit dot in between a release segment and a pre-release - # segment. - split_prospective = _version_split(str(prospective)) - - # Shorten the prospective version to be the same length as the spec - # so that we can determine if the specifier is a prefix of the - # prospective version or not. - shortened_prospective = split_prospective[: len(split_spec)] - - # Pad out our two sides with zeros so that they both equal the same - # length. - padded_spec, padded_prospective = _pad_version( - split_spec, shortened_prospective - ) - - return padded_prospective == padded_spec - else: - # Convert our spec string into a Version - spec_version = Version(spec) - - # If the specifier does not have a local segment, then we want to - # act as if the prospective version also does not have a local - # segment. - if not spec_version.local: - prospective = Version(prospective.public) - - return prospective == spec_version - - @_require_version_compare - def _compare_not_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - return not self._compare_equal(prospective, spec) - - @_require_version_compare - def _compare_less_than_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return Version(prospective.public) <= Version(spec) - - @_require_version_compare - def _compare_greater_than_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return Version(prospective.public) >= Version(spec) - - @_require_version_compare - def _compare_less_than(self, prospective, spec_str): - # type: (ParsedVersion, str) -> bool - - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec_str) - - # Check to see if the prospective version is less than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective < spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a pre-release version, that we do not accept pre-release - # versions for the version mentioned in the specifier (e.g. <3.1 should - # not match 3.1.dev0, but should match 3.0.dev0). - if not spec.is_prerelease and prospective.is_prerelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # less than the spec version *and* it's not a pre-release of the same - # version in the spec. - return True - - @_require_version_compare - def _compare_greater_than(self, prospective, spec_str): - # type: (ParsedVersion, str) -> bool - - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec_str) - - # Check to see if the prospective version is greater than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective > spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a post-release version, that we do not accept - # post-release versions for the version mentioned in the specifier - # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). - if not spec.is_postrelease and prospective.is_postrelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # Ensure that we do not allow a local version of the version mentioned - # in the specifier, which is technically greater than, to match. - if prospective.local is not None: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # greater than the spec version *and* it's not a pre-release of the - # same version in the spec. - return True - - def _compare_arbitrary(self, prospective, spec): - # type: (Version, str) -> bool - return str(prospective).lower() == str(spec).lower() - - @property - def prereleases(self): - # type: () -> bool - - # If there is an explicit prereleases set for this, then we'll just - # blindly use that. - if self._prereleases is not None: - return self._prereleases - - # Look at all of our specifiers and determine if they are inclusive - # operators, and if they are if they are including an explicit - # prerelease. - operator, version = self._spec - if operator in ["==", ">=", "<=", "~=", "==="]: - # The == specifier can include a trailing .*, if it does we - # want to remove before parsing. - if operator == "==" and version.endswith(".*"): - version = version[:-2] - - # Parse the version, and if it is a pre-release than this - # specifier allows pre-releases. - if parse(version).is_prerelease: - return True - - return False - - @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None - self._prereleases = value - - -_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") - - -def _version_split(version): - # type: (str) -> List[str] - result = [] # type: List[str] - for item in version.split("."): - match = _prefix_regex.search(item) - if match: - result.extend(match.groups()) - else: - result.append(item) - return result - - -def _pad_version(left, right): - # type: (List[str], List[str]) -> Tuple[List[str], List[str]] - left_split, right_split = [], [] - - # Get the release segment of our versions - left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) - right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) - - # Get the rest of our versions - left_split.append(left[len(left_split[0]) :]) - right_split.append(right[len(right_split[0]) :]) - - # Insert our padding - left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) - right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) - - return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) - - -class SpecifierSet(BaseSpecifier): - def __init__(self, specifiers="", prereleases=None): - # type: (str, Optional[bool]) -> None - - # Split on , to break each individual specifier into it's own item, and - # strip each item to remove leading/trailing whitespace. - split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] - - # Parsed each individual specifier, attempting first to make it a - # Specifier and falling back to a LegacySpecifier. - parsed = set() - for specifier in split_specifiers: - try: - parsed.add(Specifier(specifier)) - except InvalidSpecifier: - parsed.add(LegacySpecifier(specifier)) - - # Turn our parsed specifiers into a frozen set and save them for later. - self._specs = frozenset(parsed) - - # Store our prereleases value so we can use it later to determine if - # we accept prereleases or not. - self._prereleases = prereleases - - def __repr__(self): - # type: () -> str - pre = ( - ", prereleases={0!r}".format(self.prereleases) - if self._prereleases is not None - else "" - ) - - return "".format(str(self), pre) - - def __str__(self): - # type: () -> str - return ",".join(sorted(str(s) for s in self._specs)) - - def __hash__(self): - # type: () -> int - return hash(self._specs) - - def __and__(self, other): - # type: (Union[SpecifierSet, str]) -> SpecifierSet - if isinstance(other, string_types): - other = SpecifierSet(other) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - specifier = SpecifierSet() - specifier._specs = frozenset(self._specs | other._specs) - - if self._prereleases is None and other._prereleases is not None: - specifier._prereleases = other._prereleases - elif self._prereleases is not None and other._prereleases is None: - specifier._prereleases = self._prereleases - elif self._prereleases == other._prereleases: - specifier._prereleases = self._prereleases - else: - raise ValueError( - "Cannot combine SpecifierSets with True and False prerelease " - "overrides." - ) - - return specifier - - def __eq__(self, other): - # type: (object) -> bool - if isinstance(other, (string_types, _IndividualSpecifier)): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs == other._specs - - def __ne__(self, other): - # type: (object) -> bool - if isinstance(other, (string_types, _IndividualSpecifier)): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs != other._specs - - def __len__(self): - # type: () -> int - return len(self._specs) - - def __iter__(self): - # type: () -> Iterator[FrozenSet[_IndividualSpecifier]] - return iter(self._specs) - - @property - def prereleases(self): - # type: () -> Optional[bool] - - # If we have been given an explicit prerelease modifier, then we'll - # pass that through here. - if self._prereleases is not None: - return self._prereleases - - # If we don't have any specifiers, and we don't have a forced value, - # then we'll just return None since we don't know if this should have - # pre-releases or not. - if not self._specs: - return None - - # Otherwise we'll see if any of the given specifiers accept - # prereleases, if any of them do we'll return True, otherwise False. - return any(s.prereleases for s in self._specs) - - @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None - self._prereleases = value - - def __contains__(self, item): - # type: (Union[ParsedVersion, str]) -> bool - return self.contains(item) - - def contains(self, item, prereleases=None): - # type: (Union[ParsedVersion, str], Optional[bool]) -> bool - - # Ensure that our item is a Version or LegacyVersion instance. - if not isinstance(item, (LegacyVersion, Version)): - item = parse(item) - - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # We can determine if we're going to allow pre-releases by looking to - # see if any of the underlying items supports them. If none of them do - # and this item is a pre-release then we do not allow it and we can - # short circuit that here. - # Note: This means that 1.0.dev1 would not be contained in something - # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 - if not prereleases and item.is_prerelease: - return False - - # We simply dispatch to the underlying specs here to make sure that the - # given version is contained within all of them. - # Note: This use of all() here means that an empty set of specifiers - # will always return True, this is an explicit design decision. - return all(s.contains(item, prereleases=prereleases) for s in self._specs) - - def filter( - self, - iterable, # type: Iterable[Union[ParsedVersion, str]] - prereleases=None, # type: Optional[bool] - ): - # type: (...) -> Iterable[Union[ParsedVersion, str]] - - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # If we have any specifiers, then we want to wrap our iterable in the - # filter method for each one, this will act as a logical AND amongst - # each specifier. - if self._specs: - for spec in self._specs: - iterable = spec.filter(iterable, prereleases=bool(prereleases)) - return iterable - # If we do not have any specifiers, then we need to have a rough filter - # which will filter out any pre-releases, unless there are no final - # releases, and which will filter out LegacyVersion in general. - else: - filtered = [] # type: List[Union[ParsedVersion, str]] - found_prereleases = [] # type: List[Union[ParsedVersion, str]] - - for item in iterable: - # Ensure that we some kind of Version class for this item. - if not isinstance(item, (LegacyVersion, Version)): - parsed_version = parse(item) - else: - parsed_version = item - - # Filter out any item which is parsed as a LegacyVersion - if isinstance(parsed_version, LegacyVersion): - continue - - # Store any item which is a pre-release for later unless we've - # already found a final version or we are accepting prereleases - if parsed_version.is_prerelease and not prereleases: - if not filtered: - found_prereleases.append(item) - else: - filtered.append(item) - - # If we've found no items except for pre-releases, then we'll go - # ahead and use the pre-releases - if not filtered and found_prereleases and prereleases is None: - return found_prereleases - - return filtered diff --git a/pkg_resources/_vendor/packaging/tags.py b/pkg_resources/_vendor/packaging/tags.py deleted file mode 100644 index 9064910b8b..0000000000 --- a/pkg_resources/_vendor/packaging/tags.py +++ /dev/null @@ -1,751 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import absolute_import - -import distutils.util - -try: - from importlib.machinery import EXTENSION_SUFFIXES -except ImportError: # pragma: no cover - import imp - - EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] - del imp -import logging -import os -import platform -import re -import struct -import sys -import sysconfig -import warnings - -from ._typing import TYPE_CHECKING, cast - -if TYPE_CHECKING: # pragma: no cover - from typing import ( - Dict, - FrozenSet, - IO, - Iterable, - Iterator, - List, - Optional, - Sequence, - Tuple, - Union, - ) - - PythonVersion = Sequence[int] - MacVersion = Tuple[int, int] - GlibcVersion = Tuple[int, int] - - -logger = logging.getLogger(__name__) - -INTERPRETER_SHORT_NAMES = { - "python": "py", # Generic. - "cpython": "cp", - "pypy": "pp", - "ironpython": "ip", - "jython": "jy", -} # type: Dict[str, str] - - -_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 - - -class Tag(object): - """ - A representation of the tag triple for a wheel. - - Instances are considered immutable and thus are hashable. Equality checking - is also supported. - """ - - __slots__ = ["_interpreter", "_abi", "_platform"] - - def __init__(self, interpreter, abi, platform): - # type: (str, str, str) -> None - self._interpreter = interpreter.lower() - self._abi = abi.lower() - self._platform = platform.lower() - - @property - def interpreter(self): - # type: () -> str - return self._interpreter - - @property - def abi(self): - # type: () -> str - return self._abi - - @property - def platform(self): - # type: () -> str - return self._platform - - def __eq__(self, other): - # type: (object) -> bool - if not isinstance(other, Tag): - return NotImplemented - - return ( - (self.platform == other.platform) - and (self.abi == other.abi) - and (self.interpreter == other.interpreter) - ) - - def __hash__(self): - # type: () -> int - return hash((self._interpreter, self._abi, self._platform)) - - def __str__(self): - # type: () -> str - return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) - - def __repr__(self): - # type: () -> str - return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) - - -def parse_tag(tag): - # type: (str) -> FrozenSet[Tag] - """ - Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. - - Returning a set is required due to the possibility that the tag is a - compressed tag set. - """ - tags = set() - interpreters, abis, platforms = tag.split("-") - for interpreter in interpreters.split("."): - for abi in abis.split("."): - for platform_ in platforms.split("."): - tags.add(Tag(interpreter, abi, platform_)) - return frozenset(tags) - - -def _warn_keyword_parameter(func_name, kwargs): - # type: (str, Dict[str, bool]) -> bool - """ - Backwards-compatibility with Python 2.7 to allow treating 'warn' as keyword-only. - """ - if not kwargs: - return False - elif len(kwargs) > 1 or "warn" not in kwargs: - kwargs.pop("warn", None) - arg = next(iter(kwargs.keys())) - raise TypeError( - "{}() got an unexpected keyword argument {!r}".format(func_name, arg) - ) - return kwargs["warn"] - - -def _get_config_var(name, warn=False): - # type: (str, bool) -> Union[int, str, None] - value = sysconfig.get_config_var(name) - if value is None and warn: - logger.debug( - "Config variable '%s' is unset, Python ABI tag may be incorrect", name - ) - return value - - -def _normalize_string(string): - # type: (str) -> str - return string.replace(".", "_").replace("-", "_") - - -def _abi3_applies(python_version): - # type: (PythonVersion) -> bool - """ - Determine if the Python version supports abi3. - - PEP 384 was first implemented in Python 3.2. - """ - return len(python_version) > 1 and tuple(python_version) >= (3, 2) - - -def _cpython_abis(py_version, warn=False): - # type: (PythonVersion, bool) -> List[str] - py_version = tuple(py_version) # To allow for version comparison. - abis = [] - version = _version_nodot(py_version[:2]) - debug = pymalloc = ucs4 = "" - with_debug = _get_config_var("Py_DEBUG", warn) - has_refcount = hasattr(sys, "gettotalrefcount") - # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled - # extension modules is the best option. - # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 - has_ext = "_d.pyd" in EXTENSION_SUFFIXES - if with_debug or (with_debug is None and (has_refcount or has_ext)): - debug = "d" - if py_version < (3, 8): - with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) - if with_pymalloc or with_pymalloc is None: - pymalloc = "m" - if py_version < (3, 3): - unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) - if unicode_size == 4 or ( - unicode_size is None and sys.maxunicode == 0x10FFFF - ): - ucs4 = "u" - elif debug: - # Debug builds can also load "normal" extension modules. - # We can also assume no UCS-4 or pymalloc requirement. - abis.append("cp{version}".format(version=version)) - abis.insert( - 0, - "cp{version}{debug}{pymalloc}{ucs4}".format( - version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4 - ), - ) - return abis - - -def cpython_tags( - python_version=None, # type: Optional[PythonVersion] - abis=None, # type: Optional[Iterable[str]] - platforms=None, # type: Optional[Iterable[str]] - **kwargs # type: bool -): - # type: (...) -> Iterator[Tag] - """ - Yields the tags for a CPython interpreter. - - The tags consist of: - - cp-- - - cp-abi3- - - cp-none- - - cp-abi3- # Older Python versions down to 3.2. - - If python_version only specifies a major version then user-provided ABIs and - the 'none' ABItag will be used. - - If 'abi3' or 'none' are specified in 'abis' then they will be yielded at - their normal position and not at the beginning. - """ - warn = _warn_keyword_parameter("cpython_tags", kwargs) - if not python_version: - python_version = sys.version_info[:2] - - interpreter = "cp{}".format(_version_nodot(python_version[:2])) - - if abis is None: - if len(python_version) > 1: - abis = _cpython_abis(python_version, warn) - else: - abis = [] - abis = list(abis) - # 'abi3' and 'none' are explicitly handled later. - for explicit_abi in ("abi3", "none"): - try: - abis.remove(explicit_abi) - except ValueError: - pass - - platforms = list(platforms or _platform_tags()) - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - if _abi3_applies(python_version): - for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): - yield tag - for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): - yield tag - - if _abi3_applies(python_version): - for minor_version in range(python_version[1] - 1, 1, -1): - for platform_ in platforms: - interpreter = "cp{version}".format( - version=_version_nodot((python_version[0], minor_version)) - ) - yield Tag(interpreter, "abi3", platform_) - - -def _generic_abi(): - # type: () -> Iterator[str] - abi = sysconfig.get_config_var("SOABI") - if abi: - yield _normalize_string(abi) - - -def generic_tags( - interpreter=None, # type: Optional[str] - abis=None, # type: Optional[Iterable[str]] - platforms=None, # type: Optional[Iterable[str]] - **kwargs # type: bool -): - # type: (...) -> Iterator[Tag] - """ - Yields the tags for a generic interpreter. - - The tags consist of: - - -- - - The "none" ABI will be added if it was not explicitly provided. - """ - warn = _warn_keyword_parameter("generic_tags", kwargs) - if not interpreter: - interp_name = interpreter_name() - interp_version = interpreter_version(warn=warn) - interpreter = "".join([interp_name, interp_version]) - if abis is None: - abis = _generic_abi() - platforms = list(platforms or _platform_tags()) - abis = list(abis) - if "none" not in abis: - abis.append("none") - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - - -def _py_interpreter_range(py_version): - # type: (PythonVersion) -> Iterator[str] - """ - Yields Python versions in descending order. - - After the latest version, the major-only version will be yielded, and then - all previous versions of that major version. - """ - if len(py_version) > 1: - yield "py{version}".format(version=_version_nodot(py_version[:2])) - yield "py{major}".format(major=py_version[0]) - if len(py_version) > 1: - for minor in range(py_version[1] - 1, -1, -1): - yield "py{version}".format(version=_version_nodot((py_version[0], minor))) - - -def compatible_tags( - python_version=None, # type: Optional[PythonVersion] - interpreter=None, # type: Optional[str] - platforms=None, # type: Optional[Iterable[str]] -): - # type: (...) -> Iterator[Tag] - """ - Yields the sequence of tags that are compatible with a specific version of Python. - - The tags consist of: - - py*-none- - - -none-any # ... if `interpreter` is provided. - - py*-none-any - """ - if not python_version: - python_version = sys.version_info[:2] - platforms = list(platforms or _platform_tags()) - for version in _py_interpreter_range(python_version): - for platform_ in platforms: - yield Tag(version, "none", platform_) - if interpreter: - yield Tag(interpreter, "none", "any") - for version in _py_interpreter_range(python_version): - yield Tag(version, "none", "any") - - -def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): - # type: (str, bool) -> str - if not is_32bit: - return arch - - if arch.startswith("ppc"): - return "ppc" - - return "i386" - - -def _mac_binary_formats(version, cpu_arch): - # type: (MacVersion, str) -> List[str] - formats = [cpu_arch] - if cpu_arch == "x86_64": - if version < (10, 4): - return [] - formats.extend(["intel", "fat64", "fat32"]) - - elif cpu_arch == "i386": - if version < (10, 4): - return [] - formats.extend(["intel", "fat32", "fat"]) - - elif cpu_arch == "ppc64": - # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? - if version > (10, 5) or version < (10, 4): - return [] - formats.append("fat64") - - elif cpu_arch == "ppc": - if version > (10, 6): - return [] - formats.extend(["fat32", "fat"]) - - formats.append("universal") - return formats - - -def mac_platforms(version=None, arch=None): - # type: (Optional[MacVersion], Optional[str]) -> Iterator[str] - """ - Yields the platform tags for a macOS system. - - The `version` parameter is a two-item tuple specifying the macOS version to - generate platform tags for. The `arch` parameter is the CPU architecture to - generate platform tags for. Both parameters default to the appropriate value - for the current system. - """ - version_str, _, cpu_arch = platform.mac_ver() # type: ignore - if version is None: - version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) - else: - version = version - if arch is None: - arch = _mac_arch(cpu_arch) - else: - arch = arch - for minor_version in range(version[1], -1, -1): - compat_version = version[0], minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) - - -# From PEP 513. -def _is_manylinux_compatible(name, glibc_version): - # type: (str, GlibcVersion) -> bool - # Check for presence of _manylinux module. - try: - import _manylinux # noqa - - return bool(getattr(_manylinux, name + "_compatible")) - except (ImportError, AttributeError): - # Fall through to heuristic check below. - pass - - return _have_compatible_glibc(*glibc_version) - - -def _glibc_version_string(): - # type: () -> Optional[str] - # Returns glibc version string, or None if not using glibc. - return _glibc_version_string_confstr() or _glibc_version_string_ctypes() - - -def _glibc_version_string_confstr(): - # type: () -> Optional[str] - """ - Primary implementation of glibc_version_string using os.confstr. - """ - # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely - # to be broken or missing. This strategy is used in the standard library - # platform module. - # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 - try: - # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". - version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821 - "CS_GNU_LIBC_VERSION" - ) - assert version_string is not None - _, version = version_string.split() # type: Tuple[str, str] - except (AssertionError, AttributeError, OSError, ValueError): - # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... - return None - return version - - -def _glibc_version_string_ctypes(): - # type: () -> Optional[str] - """ - Fallback implementation of glibc_version_string using ctypes. - """ - try: - import ctypes - except ImportError: - return None - - # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen - # manpage says, "If filename is NULL, then the returned handle is for the - # main program". This way we can let the linker do the work to figure out - # which libc our process is actually using. - # - # Note: typeshed is wrong here so we are ignoring this line. - process_namespace = ctypes.CDLL(None) # type: ignore - try: - gnu_get_libc_version = process_namespace.gnu_get_libc_version - except AttributeError: - # Symbol doesn't exist -> therefore, we are not linked to - # glibc. - return None - - # Call gnu_get_libc_version, which returns a string like "2.5" - gnu_get_libc_version.restype = ctypes.c_char_p - version_str = gnu_get_libc_version() # type: str - # py2 / py3 compatibility: - if not isinstance(version_str, str): - version_str = version_str.decode("ascii") - - return version_str - - -# Separated out from have_compatible_glibc for easier unit testing. -def _check_glibc_version(version_str, required_major, minimum_minor): - # type: (str, int, int) -> bool - # Parse string and check against requested version. - # - # We use a regexp instead of str.split because we want to discard any - # random junk that might come after the minor version -- this might happen - # in patched/forked versions of glibc (e.g. Linaro's version of glibc - # uses version strings like "2.20-2014.11"). See gh-3588. - m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) - if not m: - warnings.warn( - "Expected glibc version with 2 components major.minor," - " got: %s" % version_str, - RuntimeWarning, - ) - return False - return ( - int(m.group("major")) == required_major - and int(m.group("minor")) >= minimum_minor - ) - - -def _have_compatible_glibc(required_major, minimum_minor): - # type: (int, int) -> bool - version_str = _glibc_version_string() - if version_str is None: - return False - return _check_glibc_version(version_str, required_major, minimum_minor) - - -# Python does not provide platform information at sufficient granularity to -# identify the architecture of the running executable in some cases, so we -# determine it dynamically by reading the information from the running -# process. This only applies on Linux, which uses the ELF format. -class _ELFFileHeader(object): - # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header - class _InvalidELFFileHeader(ValueError): - """ - An invalid ELF file header was found. - """ - - ELF_MAGIC_NUMBER = 0x7F454C46 - ELFCLASS32 = 1 - ELFCLASS64 = 2 - ELFDATA2LSB = 1 - ELFDATA2MSB = 2 - EM_386 = 3 - EM_S390 = 22 - EM_ARM = 40 - EM_X86_64 = 62 - EF_ARM_ABIMASK = 0xFF000000 - EF_ARM_ABI_VER5 = 0x05000000 - EF_ARM_ABI_FLOAT_HARD = 0x00000400 - - def __init__(self, file): - # type: (IO[bytes]) -> None - def unpack(fmt): - # type: (str) -> int - try: - (result,) = struct.unpack( - fmt, file.read(struct.calcsize(fmt)) - ) # type: (int, ) - except struct.error: - raise _ELFFileHeader._InvalidELFFileHeader() - return result - - self.e_ident_magic = unpack(">I") - if self.e_ident_magic != self.ELF_MAGIC_NUMBER: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_class = unpack("B") - if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_data = unpack("B") - if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_version = unpack("B") - self.e_ident_osabi = unpack("B") - self.e_ident_abiversion = unpack("B") - self.e_ident_pad = file.read(7) - format_h = "H" - format_i = "I" - format_q = "Q" - format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q - self.e_type = unpack(format_h) - self.e_machine = unpack(format_h) - self.e_version = unpack(format_i) - self.e_entry = unpack(format_p) - self.e_phoff = unpack(format_p) - self.e_shoff = unpack(format_p) - self.e_flags = unpack(format_i) - self.e_ehsize = unpack(format_h) - self.e_phentsize = unpack(format_h) - self.e_phnum = unpack(format_h) - self.e_shentsize = unpack(format_h) - self.e_shnum = unpack(format_h) - self.e_shstrndx = unpack(format_h) - - -def _get_elf_header(): - # type: () -> Optional[_ELFFileHeader] - try: - with open(sys.executable, "rb") as f: - elf_header = _ELFFileHeader(f) - except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): - return None - return elf_header - - -def _is_linux_armhf(): - # type: () -> bool - # hard-float ABI can be detected from the ELF header of the running - # process - # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_ARM - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABIMASK - ) == elf_header.EF_ARM_ABI_VER5 - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD - ) == elf_header.EF_ARM_ABI_FLOAT_HARD - return result - - -def _is_linux_i686(): - # type: () -> bool - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_386 - return result - - -def _have_compatible_manylinux_abi(arch): - # type: (str) -> bool - if arch == "armv7l": - return _is_linux_armhf() - if arch == "i686": - return _is_linux_i686() - return True - - -def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): - # type: (bool) -> Iterator[str] - linux = _normalize_string(distutils.util.get_platform()) - if is_32bit: - if linux == "linux_x86_64": - linux = "linux_i686" - elif linux == "linux_aarch64": - linux = "linux_armv7l" - manylinux_support = [] - _, arch = linux.split("_", 1) - if _have_compatible_manylinux_abi(arch): - if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}: - manylinux_support.append( - ("manylinux2014", (2, 17)) - ) # CentOS 7 w/ glibc 2.17 (PEP 599) - if arch in {"x86_64", "i686"}: - manylinux_support.append( - ("manylinux2010", (2, 12)) - ) # CentOS 6 w/ glibc 2.12 (PEP 571) - manylinux_support.append( - ("manylinux1", (2, 5)) - ) # CentOS 5 w/ glibc 2.5 (PEP 513) - manylinux_support_iter = iter(manylinux_support) - for name, glibc_version in manylinux_support_iter: - if _is_manylinux_compatible(name, glibc_version): - yield linux.replace("linux", name) - break - # Support for a later manylinux implies support for an earlier version. - for name, _ in manylinux_support_iter: - yield linux.replace("linux", name) - yield linux - - -def _generic_platforms(): - # type: () -> Iterator[str] - yield _normalize_string(distutils.util.get_platform()) - - -def _platform_tags(): - # type: () -> Iterator[str] - """ - Provides the platform tags for this installation. - """ - if platform.system() == "Darwin": - return mac_platforms() - elif platform.system() == "Linux": - return _linux_platforms() - else: - return _generic_platforms() - - -def interpreter_name(): - # type: () -> str - """ - Returns the name of the running interpreter. - """ - try: - name = sys.implementation.name # type: ignore - except AttributeError: # pragma: no cover - # Python 2.7 compatibility. - name = platform.python_implementation().lower() - return INTERPRETER_SHORT_NAMES.get(name) or name - - -def interpreter_version(**kwargs): - # type: (bool) -> str - """ - Returns the version of the running interpreter. - """ - warn = _warn_keyword_parameter("interpreter_version", kwargs) - version = _get_config_var("py_version_nodot", warn=warn) - if version: - version = str(version) - else: - version = _version_nodot(sys.version_info[:2]) - return version - - -def _version_nodot(version): - # type: (PythonVersion) -> str - if any(v >= 10 for v in version): - sep = "_" - else: - sep = "" - return sep.join(map(str, version)) - - -def sys_tags(**kwargs): - # type: (bool) -> Iterator[Tag] - """ - Returns the sequence of tag triples for the running interpreter. - - The order of the sequence corresponds to priority order for the - interpreter, from most to least important. - """ - warn = _warn_keyword_parameter("sys_tags", kwargs) - - interp_name = interpreter_name() - if interp_name == "cp": - for tag in cpython_tags(warn=warn): - yield tag - else: - for tag in generic_tags(): - yield tag - - for tag in compatible_tags(): - yield tag diff --git a/pkg_resources/_vendor/packaging/utils.py b/pkg_resources/_vendor/packaging/utils.py deleted file mode 100644 index 19579c1a0f..0000000000 --- a/pkg_resources/_vendor/packaging/utils.py +++ /dev/null @@ -1,65 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import re - -from ._typing import TYPE_CHECKING, cast -from .version import InvalidVersion, Version - -if TYPE_CHECKING: # pragma: no cover - from typing import NewType, Union - - NormalizedName = NewType("NormalizedName", str) - -_canonicalize_regex = re.compile(r"[-_.]+") - - -def canonicalize_name(name): - # type: (str) -> NormalizedName - # This is taken from PEP 503. - value = _canonicalize_regex.sub("-", name).lower() - return cast("NormalizedName", value) - - -def canonicalize_version(_version): - # type: (str) -> Union[Version, str] - """ - This is very similar to Version.__str__, but has one subtle difference - with the way it handles the release segment. - """ - - try: - version = Version(_version) - except InvalidVersion: - # Legacy versions cannot be normalized - return _version - - parts = [] - - # Epoch - if version.epoch != 0: - parts.append("{0}!".format(version.epoch)) - - # Release segment - # NB: This strips trailing '.0's to normalize - parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release))) - - # Pre-release - if version.pre is not None: - parts.append("".join(str(x) for x in version.pre)) - - # Post-release - if version.post is not None: - parts.append(".post{0}".format(version.post)) - - # Development release - if version.dev is not None: - parts.append(".dev{0}".format(version.dev)) - - # Local version segment - if version.local is not None: - parts.append("+{0}".format(version.local)) - - return "".join(parts) diff --git a/pkg_resources/_vendor/packaging/version.py b/pkg_resources/_vendor/packaging/version.py deleted file mode 100644 index 00371e86a8..0000000000 --- a/pkg_resources/_vendor/packaging/version.py +++ /dev/null @@ -1,535 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import collections -import itertools -import re - -from ._structures import Infinity, NegativeInfinity -from ._typing import TYPE_CHECKING - -if TYPE_CHECKING: # pragma: no cover - from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union - - from ._structures import InfinityType, NegativeInfinityType - - InfiniteTypes = Union[InfinityType, NegativeInfinityType] - PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] - SubLocalType = Union[InfiniteTypes, int, str] - LocalType = Union[ - NegativeInfinityType, - Tuple[ - Union[ - SubLocalType, - Tuple[SubLocalType, str], - Tuple[NegativeInfinityType, SubLocalType], - ], - ..., - ], - ] - CmpKey = Tuple[ - int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType - ] - LegacyCmpKey = Tuple[int, Tuple[str, ...]] - VersionComparisonMethod = Callable[ - [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool - ] - -__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] - - -_Version = collections.namedtuple( - "_Version", ["epoch", "release", "dev", "pre", "post", "local"] -) - - -def parse(version): - # type: (str) -> Union[LegacyVersion, Version] - """ - Parse the given version string and return either a :class:`Version` object - or a :class:`LegacyVersion` object depending on if the given version is - a valid PEP 440 version or a legacy version. - """ - try: - return Version(version) - except InvalidVersion: - return LegacyVersion(version) - - -class InvalidVersion(ValueError): - """ - An invalid version was found, users should refer to PEP 440. - """ - - -class _BaseVersion(object): - _key = None # type: Union[CmpKey, LegacyCmpKey] - - def __hash__(self): - # type: () -> int - return hash(self._key) - - def __lt__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s < o) - - def __le__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s <= o) - - def __eq__(self, other): - # type: (object) -> bool - return self._compare(other, lambda s, o: s == o) - - def __ge__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s >= o) - - def __gt__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s > o) - - def __ne__(self, other): - # type: (object) -> bool - return self._compare(other, lambda s, o: s != o) - - def _compare(self, other, method): - # type: (object, VersionComparisonMethod) -> Union[bool, NotImplemented] - if not isinstance(other, _BaseVersion): - return NotImplemented - - return method(self._key, other._key) - - -class LegacyVersion(_BaseVersion): - def __init__(self, version): - # type: (str) -> None - self._version = str(version) - self._key = _legacy_cmpkey(self._version) - - def __str__(self): - # type: () -> str - return self._version - - def __repr__(self): - # type: () -> str - return "".format(repr(str(self))) - - @property - def public(self): - # type: () -> str - return self._version - - @property - def base_version(self): - # type: () -> str - return self._version - - @property - def epoch(self): - # type: () -> int - return -1 - - @property - def release(self): - # type: () -> None - return None - - @property - def pre(self): - # type: () -> None - return None - - @property - def post(self): - # type: () -> None - return None - - @property - def dev(self): - # type: () -> None - return None - - @property - def local(self): - # type: () -> None - return None - - @property - def is_prerelease(self): - # type: () -> bool - return False - - @property - def is_postrelease(self): - # type: () -> bool - return False - - @property - def is_devrelease(self): - # type: () -> bool - return False - - -_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) - -_legacy_version_replacement_map = { - "pre": "c", - "preview": "c", - "-": "final-", - "rc": "c", - "dev": "@", -} - - -def _parse_version_parts(s): - # type: (str) -> Iterator[str] - for part in _legacy_version_component_re.split(s): - part = _legacy_version_replacement_map.get(part, part) - - if not part or part == ".": - continue - - if part[:1] in "0123456789": - # pad for numeric comparison - yield part.zfill(8) - else: - yield "*" + part - - # ensure that alpha/beta/candidate are before final - yield "*final" - - -def _legacy_cmpkey(version): - # type: (str) -> LegacyCmpKey - - # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch - # greater than or equal to 0. This will effectively put the LegacyVersion, - # which uses the defacto standard originally implemented by setuptools, - # as before all PEP 440 versions. - epoch = -1 - - # This scheme is taken from pkg_resources.parse_version setuptools prior to - # it's adoption of the packaging library. - parts = [] # type: List[str] - for part in _parse_version_parts(version.lower()): - if part.startswith("*"): - # remove "-" before a prerelease tag - if part < "*final": - while parts and parts[-1] == "*final-": - parts.pop() - - # remove trailing zeros from each series of numeric parts - while parts and parts[-1] == "00000000": - parts.pop() - - parts.append(part) - - return epoch, tuple(parts) - - -# Deliberately not anchored to the start and end of the string, to make it -# easier for 3rd party code to reuse -VERSION_PATTERN = r""" - v? - (?: - (?:(?P[0-9]+)!)? # epoch - (?P[0-9]+(?:\.[0-9]+)*) # release segment - (?P
                                          # pre-release
-            [-_\.]?
-            (?P(a|b|c|rc|alpha|beta|pre|preview))
-            [-_\.]?
-            (?P[0-9]+)?
-        )?
-        (?P                                         # post release
-            (?:-(?P[0-9]+))
-            |
-            (?:
-                [-_\.]?
-                (?Ppost|rev|r)
-                [-_\.]?
-                (?P[0-9]+)?
-            )
-        )?
-        (?P                                          # dev release
-            [-_\.]?
-            (?Pdev)
-            [-_\.]?
-            (?P[0-9]+)?
-        )?
-    )
-    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
-"""
-
-
-class Version(_BaseVersion):
-
-    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
-
-    def __init__(self, version):
-        # type: (str) -> None
-
-        # Validate the version and parse it into pieces
-        match = self._regex.search(version)
-        if not match:
-            raise InvalidVersion("Invalid version: '{0}'".format(version))
-
-        # Store the parsed out pieces of the version
-        self._version = _Version(
-            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
-            release=tuple(int(i) for i in match.group("release").split(".")),
-            pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
-            post=_parse_letter_version(
-                match.group("post_l"), match.group("post_n1") or match.group("post_n2")
-            ),
-            dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
-            local=_parse_local_version(match.group("local")),
-        )
-
-        # Generate a key which will be used for sorting
-        self._key = _cmpkey(
-            self._version.epoch,
-            self._version.release,
-            self._version.pre,
-            self._version.post,
-            self._version.dev,
-            self._version.local,
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return "".format(repr(str(self)))
-
-    def __str__(self):
-        # type: () -> str
-        parts = []
-
-        # Epoch
-        if self.epoch != 0:
-            parts.append("{0}!".format(self.epoch))
-
-        # Release segment
-        parts.append(".".join(str(x) for x in self.release))
-
-        # Pre-release
-        if self.pre is not None:
-            parts.append("".join(str(x) for x in self.pre))
-
-        # Post-release
-        if self.post is not None:
-            parts.append(".post{0}".format(self.post))
-
-        # Development release
-        if self.dev is not None:
-            parts.append(".dev{0}".format(self.dev))
-
-        # Local version segment
-        if self.local is not None:
-            parts.append("+{0}".format(self.local))
-
-        return "".join(parts)
-
-    @property
-    def epoch(self):
-        # type: () -> int
-        _epoch = self._version.epoch  # type: int
-        return _epoch
-
-    @property
-    def release(self):
-        # type: () -> Tuple[int, ...]
-        _release = self._version.release  # type: Tuple[int, ...]
-        return _release
-
-    @property
-    def pre(self):
-        # type: () -> Optional[Tuple[str, int]]
-        _pre = self._version.pre  # type: Optional[Tuple[str, int]]
-        return _pre
-
-    @property
-    def post(self):
-        # type: () -> Optional[Tuple[str, int]]
-        return self._version.post[1] if self._version.post else None
-
-    @property
-    def dev(self):
-        # type: () -> Optional[Tuple[str, int]]
-        return self._version.dev[1] if self._version.dev else None
-
-    @property
-    def local(self):
-        # type: () -> Optional[str]
-        if self._version.local:
-            return ".".join(str(x) for x in self._version.local)
-        else:
-            return None
-
-    @property
-    def public(self):
-        # type: () -> str
-        return str(self).split("+", 1)[0]
-
-    @property
-    def base_version(self):
-        # type: () -> str
-        parts = []
-
-        # Epoch
-        if self.epoch != 0:
-            parts.append("{0}!".format(self.epoch))
-
-        # Release segment
-        parts.append(".".join(str(x) for x in self.release))
-
-        return "".join(parts)
-
-    @property
-    def is_prerelease(self):
-        # type: () -> bool
-        return self.dev is not None or self.pre is not None
-
-    @property
-    def is_postrelease(self):
-        # type: () -> bool
-        return self.post is not None
-
-    @property
-    def is_devrelease(self):
-        # type: () -> bool
-        return self.dev is not None
-
-    @property
-    def major(self):
-        # type: () -> int
-        return self.release[0] if len(self.release) >= 1 else 0
-
-    @property
-    def minor(self):
-        # type: () -> int
-        return self.release[1] if len(self.release) >= 2 else 0
-
-    @property
-    def micro(self):
-        # type: () -> int
-        return self.release[2] if len(self.release) >= 3 else 0
-
-
-def _parse_letter_version(
-    letter,  # type: str
-    number,  # type: Union[str, bytes, SupportsInt]
-):
-    # type: (...) -> Optional[Tuple[str, int]]
-
-    if letter:
-        # We consider there to be an implicit 0 in a pre-release if there is
-        # not a numeral associated with it.
-        if number is None:
-            number = 0
-
-        # We normalize any letters to their lower case form
-        letter = letter.lower()
-
-        # We consider some words to be alternate spellings of other words and
-        # in those cases we want to normalize the spellings to our preferred
-        # spelling.
-        if letter == "alpha":
-            letter = "a"
-        elif letter == "beta":
-            letter = "b"
-        elif letter in ["c", "pre", "preview"]:
-            letter = "rc"
-        elif letter in ["rev", "r"]:
-            letter = "post"
-
-        return letter, int(number)
-    if not letter and number:
-        # We assume if we are given a number, but we are not given a letter
-        # then this is using the implicit post release syntax (e.g. 1.0-1)
-        letter = "post"
-
-        return letter, int(number)
-
-    return None
-
-
-_local_version_separators = re.compile(r"[\._-]")
-
-
-def _parse_local_version(local):
-    # type: (str) -> Optional[LocalType]
-    """
-    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
-    """
-    if local is not None:
-        return tuple(
-            part.lower() if not part.isdigit() else int(part)
-            for part in _local_version_separators.split(local)
-        )
-    return None
-
-
-def _cmpkey(
-    epoch,  # type: int
-    release,  # type: Tuple[int, ...]
-    pre,  # type: Optional[Tuple[str, int]]
-    post,  # type: Optional[Tuple[str, int]]
-    dev,  # type: Optional[Tuple[str, int]]
-    local,  # type: Optional[Tuple[SubLocalType]]
-):
-    # type: (...) -> CmpKey
-
-    # When we compare a release version, we want to compare it with all of the
-    # trailing zeros removed. So we'll use a reverse the list, drop all the now
-    # leading zeros until we come to something non zero, then take the rest
-    # re-reverse it back into the correct order and make it a tuple and use
-    # that for our sorting key.
-    _release = tuple(
-        reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
-    )
-
-    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
-    # We'll do this by abusing the pre segment, but we _only_ want to do this
-    # if there is not a pre or a post segment. If we have one of those then
-    # the normal sorting rules will handle this case correctly.
-    if pre is None and post is None and dev is not None:
-        _pre = NegativeInfinity  # type: PrePostDevType
-    # Versions without a pre-release (except as noted above) should sort after
-    # those with one.
-    elif pre is None:
-        _pre = Infinity
-    else:
-        _pre = pre
-
-    # Versions without a post segment should sort before those with one.
-    if post is None:
-        _post = NegativeInfinity  # type: PrePostDevType
-
-    else:
-        _post = post
-
-    # Versions without a development segment should sort after those with one.
-    if dev is None:
-        _dev = Infinity  # type: PrePostDevType
-
-    else:
-        _dev = dev
-
-    if local is None:
-        # Versions without a local segment should sort before those with one.
-        _local = NegativeInfinity  # type: LocalType
-    else:
-        # Versions with a local segment need that segment parsed to implement
-        # the sorting rules in PEP440.
-        # - Alpha numeric segments sort before numeric segments
-        # - Alpha numeric segments sort lexicographically
-        # - Numeric segments sort numerically
-        # - Shorter versions sort before longer versions when the prefixes
-        #   match exactly
-        _local = tuple(
-            (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local
-        )
-
-    return epoch, _release, _pre, _post, _dev, _local
diff --git a/pkg_resources/_vendor/pyparsing.py b/pkg_resources/_vendor/pyparsing.py
deleted file mode 100644
index 4cae788387..0000000000
--- a/pkg_resources/_vendor/pyparsing.py
+++ /dev/null
@@ -1,5742 +0,0 @@
-# module pyparsing.py
-#
-# Copyright (c) 2003-2018  Paul T. McGuire
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__doc__ = \
-"""
-pyparsing module - Classes and methods to define and execute parsing grammars
-=============================================================================
-
-The pyparsing module is an alternative approach to creating and executing simple grammars,
-vs. the traditional lex/yacc approach, or the use of regular expressions.  With pyparsing, you
-don't need to learn a new syntax for defining grammars or matching expressions - the parsing module
-provides a library of classes that you use to construct the grammar directly in Python.
-
-Here is a program to parse "Hello, World!" (or any greeting of the form 
-C{", !"}), built up using L{Word}, L{Literal}, and L{And} elements 
-(L{'+'} operator gives L{And} expressions, strings are auto-converted to
-L{Literal} expressions)::
-
-    from pyparsing import Word, alphas
-
-    # define grammar of a greeting
-    greet = Word(alphas) + "," + Word(alphas) + "!"
-
-    hello = "Hello, World!"
-    print (hello, "->", greet.parseString(hello))
-
-The program outputs the following::
-
-    Hello, World! -> ['Hello', ',', 'World', '!']
-
-The Python representation of the grammar is quite readable, owing to the self-explanatory
-class names, and the use of '+', '|' and '^' operators.
-
-The L{ParseResults} object returned from L{ParserElement.parseString} can be accessed as a nested list, a dictionary, or an
-object with named attributes.
-
-The pyparsing module handles some of the problems that are typically vexing when writing text parsers:
- - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello  ,  World  !", etc.)
- - quoted strings
- - embedded comments
-
-
-Getting Started -
------------------
-Visit the classes L{ParserElement} and L{ParseResults} to see the base classes that most other pyparsing
-classes inherit from. Use the docstrings for examples of how to:
- - construct literal match expressions from L{Literal} and L{CaselessLiteral} classes
- - construct character word-group expressions using the L{Word} class
- - see how to create repetitive expressions using L{ZeroOrMore} and L{OneOrMore} classes
- - use L{'+'}, L{'|'}, L{'^'}, and L{'&'} operators to combine simple expressions into more complex ones
- - associate names with your parsed results using L{ParserElement.setResultsName}
- - find some helpful expression short-cuts like L{delimitedList} and L{oneOf}
- - find more useful common expressions in the L{pyparsing_common} namespace class
-"""
-
-__version__ = "2.2.1"
-__versionTime__ = "18 Sep 2018 00:49 UTC"
-__author__ = "Paul McGuire "
-
-import string
-from weakref import ref as wkref
-import copy
-import sys
-import warnings
-import re
-import sre_constants
-import collections
-import pprint
-import traceback
-import types
-from datetime import datetime
-
-try:
-    from _thread import RLock
-except ImportError:
-    from threading import RLock
-
-try:
-    # Python 3
-    from collections.abc import Iterable
-    from collections.abc import MutableMapping
-except ImportError:
-    # Python 2.7
-    from collections import Iterable
-    from collections import MutableMapping
-
-try:
-    from collections import OrderedDict as _OrderedDict
-except ImportError:
-    try:
-        from ordereddict import OrderedDict as _OrderedDict
-    except ImportError:
-        _OrderedDict = None
-
-#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) )
-
-__all__ = [
-'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty',
-'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal',
-'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or',
-'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException',
-'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException',
-'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 
-'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore',
-'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col',
-'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString',
-'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums',
-'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno',
-'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral',
-'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables',
-'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', 
-'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd',
-'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute',
-'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass',
-'CloseMatch', 'tokenMap', 'pyparsing_common',
-]
-
-system_version = tuple(sys.version_info)[:3]
-PY_3 = system_version[0] == 3
-if PY_3:
-    _MAX_INT = sys.maxsize
-    basestring = str
-    unichr = chr
-    _ustr = str
-
-    # build list of single arg builtins, that can be used as parse actions
-    singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max]
-
-else:
-    _MAX_INT = sys.maxint
-    range = xrange
-
-    def _ustr(obj):
-        """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries
-           str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It
-           then < returns the unicode object | encodes it with the default encoding | ... >.
-        """
-        if isinstance(obj,unicode):
-            return obj
-
-        try:
-            # If this works, then _ustr(obj) has the same behaviour as str(obj), so
-            # it won't break any existing code.
-            return str(obj)
-
-        except UnicodeEncodeError:
-            # Else encode it
-            ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace')
-            xmlcharref = Regex(r'&#\d+;')
-            xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:])
-            return xmlcharref.transformString(ret)
-
-    # build list of single arg builtins, tolerant of Python version, that can be used as parse actions
-    singleArgBuiltins = []
-    import __builtin__
-    for fname in "sum len sorted reversed list tuple set any all min max".split():
-        try:
-            singleArgBuiltins.append(getattr(__builtin__,fname))
-        except AttributeError:
-            continue
-            
-_generatorType = type((y for y in range(1)))
- 
-def _xml_escape(data):
-    """Escape &, <, >, ", ', etc. in a string of data."""
-
-    # ampersand must be replaced first
-    from_symbols = '&><"\''
-    to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split())
-    for from_,to_ in zip(from_symbols, to_symbols):
-        data = data.replace(from_, to_)
-    return data
-
-class _Constants(object):
-    pass
-
-alphas     = string.ascii_uppercase + string.ascii_lowercase
-nums       = "0123456789"
-hexnums    = nums + "ABCDEFabcdef"
-alphanums  = alphas + nums
-_bslash    = chr(92)
-printables = "".join(c for c in string.printable if c not in string.whitespace)
-
-class ParseBaseException(Exception):
-    """base exception class for all parsing runtime exceptions"""
-    # Performance tuning: we construct a *lot* of these, so keep this
-    # constructor as small and fast as possible
-    def __init__( self, pstr, loc=0, msg=None, elem=None ):
-        self.loc = loc
-        if msg is None:
-            self.msg = pstr
-            self.pstr = ""
-        else:
-            self.msg = msg
-            self.pstr = pstr
-        self.parserElement = elem
-        self.args = (pstr, loc, msg)
-
-    @classmethod
-    def _from_exception(cls, pe):
-        """
-        internal factory method to simplify creating one type of ParseException 
-        from another - avoids having __init__ signature conflicts among subclasses
-        """
-        return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement)
-
-    def __getattr__( self, aname ):
-        """supported attributes by name are:
-            - lineno - returns the line number of the exception text
-            - col - returns the column number of the exception text
-            - line - returns the line containing the exception text
-        """
-        if( aname == "lineno" ):
-            return lineno( self.loc, self.pstr )
-        elif( aname in ("col", "column") ):
-            return col( self.loc, self.pstr )
-        elif( aname == "line" ):
-            return line( self.loc, self.pstr )
-        else:
-            raise AttributeError(aname)
-
-    def __str__( self ):
-        return "%s (at char %d), (line:%d, col:%d)" % \
-                ( self.msg, self.loc, self.lineno, self.column )
-    def __repr__( self ):
-        return _ustr(self)
-    def markInputline( self, markerString = ">!<" ):
-        """Extracts the exception line from the input string, and marks
-           the location of the exception with a special symbol.
-        """
-        line_str = self.line
-        line_column = self.column - 1
-        if markerString:
-            line_str = "".join((line_str[:line_column],
-                                markerString, line_str[line_column:]))
-        return line_str.strip()
-    def __dir__(self):
-        return "lineno col line".split() + dir(type(self))
-
-class ParseException(ParseBaseException):
-    """
-    Exception thrown when parse expressions don't match class;
-    supported attributes by name are:
-     - lineno - returns the line number of the exception text
-     - col - returns the column number of the exception text
-     - line - returns the line containing the exception text
-        
-    Example::
-        try:
-            Word(nums).setName("integer").parseString("ABC")
-        except ParseException as pe:
-            print(pe)
-            print("column: {}".format(pe.col))
-            
-    prints::
-       Expected integer (at char 0), (line:1, col:1)
-        column: 1
-    """
-    pass
-
-class ParseFatalException(ParseBaseException):
-    """user-throwable exception thrown when inconsistent parse content
-       is found; stops all parsing immediately"""
-    pass
-
-class ParseSyntaxException(ParseFatalException):
-    """just like L{ParseFatalException}, but thrown internally when an
-       L{ErrorStop} ('-' operator) indicates that parsing is to stop 
-       immediately because an unbacktrackable syntax error has been found"""
-    pass
-
-#~ class ReparseException(ParseBaseException):
-    #~ """Experimental class - parse actions can raise this exception to cause
-       #~ pyparsing to reparse the input string:
-        #~ - with a modified input string, and/or
-        #~ - with a modified start location
-       #~ Set the values of the ReparseException in the constructor, and raise the
-       #~ exception in a parse action to cause pyparsing to use the new string/location.
-       #~ Setting the values as None causes no change to be made.
-       #~ """
-    #~ def __init_( self, newstring, restartLoc ):
-        #~ self.newParseText = newstring
-        #~ self.reparseLoc = restartLoc
-
-class RecursiveGrammarException(Exception):
-    """exception thrown by L{ParserElement.validate} if the grammar could be improperly recursive"""
-    def __init__( self, parseElementList ):
-        self.parseElementTrace = parseElementList
-
-    def __str__( self ):
-        return "RecursiveGrammarException: %s" % self.parseElementTrace
-
-class _ParseResultsWithOffset(object):
-    def __init__(self,p1,p2):
-        self.tup = (p1,p2)
-    def __getitem__(self,i):
-        return self.tup[i]
-    def __repr__(self):
-        return repr(self.tup[0])
-    def setOffset(self,i):
-        self.tup = (self.tup[0],i)
-
-class ParseResults(object):
-    """
-    Structured parse results, to provide multiple means of access to the parsed data:
-       - as a list (C{len(results)})
-       - by list index (C{results[0], results[1]}, etc.)
-       - by attribute (C{results.} - see L{ParserElement.setResultsName})
-
-    Example::
-        integer = Word(nums)
-        date_str = (integer.setResultsName("year") + '/' 
-                        + integer.setResultsName("month") + '/' 
-                        + integer.setResultsName("day"))
-        # equivalent form:
-        # date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-
-        # parseString returns a ParseResults object
-        result = date_str.parseString("1999/12/31")
-
-        def test(s, fn=repr):
-            print("%s -> %s" % (s, fn(eval(s))))
-        test("list(result)")
-        test("result[0]")
-        test("result['month']")
-        test("result.day")
-        test("'month' in result")
-        test("'minutes' in result")
-        test("result.dump()", str)
-    prints::
-        list(result) -> ['1999', '/', '12', '/', '31']
-        result[0] -> '1999'
-        result['month'] -> '12'
-        result.day -> '31'
-        'month' in result -> True
-        'minutes' in result -> False
-        result.dump() -> ['1999', '/', '12', '/', '31']
-        - day: 31
-        - month: 12
-        - year: 1999
-    """
-    def __new__(cls, toklist=None, name=None, asList=True, modal=True ):
-        if isinstance(toklist, cls):
-            return toklist
-        retobj = object.__new__(cls)
-        retobj.__doinit = True
-        return retobj
-
-    # Performance tuning: we construct a *lot* of these, so keep this
-    # constructor as small and fast as possible
-    def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ):
-        if self.__doinit:
-            self.__doinit = False
-            self.__name = None
-            self.__parent = None
-            self.__accumNames = {}
-            self.__asList = asList
-            self.__modal = modal
-            if toklist is None:
-                toklist = []
-            if isinstance(toklist, list):
-                self.__toklist = toklist[:]
-            elif isinstance(toklist, _generatorType):
-                self.__toklist = list(toklist)
-            else:
-                self.__toklist = [toklist]
-            self.__tokdict = dict()
-
-        if name is not None and name:
-            if not modal:
-                self.__accumNames[name] = 0
-            if isinstance(name,int):
-                name = _ustr(name) # will always return a str, but use _ustr for consistency
-            self.__name = name
-            if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])):
-                if isinstance(toklist,basestring):
-                    toklist = [ toklist ]
-                if asList:
-                    if isinstance(toklist,ParseResults):
-                        self[name] = _ParseResultsWithOffset(toklist.copy(),0)
-                    else:
-                        self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0)
-                    self[name].__name = name
-                else:
-                    try:
-                        self[name] = toklist[0]
-                    except (KeyError,TypeError,IndexError):
-                        self[name] = toklist
-
-    def __getitem__( self, i ):
-        if isinstance( i, (int,slice) ):
-            return self.__toklist[i]
-        else:
-            if i not in self.__accumNames:
-                return self.__tokdict[i][-1][0]
-            else:
-                return ParseResults([ v[0] for v in self.__tokdict[i] ])
-
-    def __setitem__( self, k, v, isinstance=isinstance ):
-        if isinstance(v,_ParseResultsWithOffset):
-            self.__tokdict[k] = self.__tokdict.get(k,list()) + [v]
-            sub = v[0]
-        elif isinstance(k,(int,slice)):
-            self.__toklist[k] = v
-            sub = v
-        else:
-            self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)]
-            sub = v
-        if isinstance(sub,ParseResults):
-            sub.__parent = wkref(self)
-
-    def __delitem__( self, i ):
-        if isinstance(i,(int,slice)):
-            mylen = len( self.__toklist )
-            del self.__toklist[i]
-
-            # convert int to slice
-            if isinstance(i, int):
-                if i < 0:
-                    i += mylen
-                i = slice(i, i+1)
-            # get removed indices
-            removed = list(range(*i.indices(mylen)))
-            removed.reverse()
-            # fixup indices in token dictionary
-            for name,occurrences in self.__tokdict.items():
-                for j in removed:
-                    for k, (value, position) in enumerate(occurrences):
-                        occurrences[k] = _ParseResultsWithOffset(value, position - (position > j))
-        else:
-            del self.__tokdict[i]
-
-    def __contains__( self, k ):
-        return k in self.__tokdict
-
-    def __len__( self ): return len( self.__toklist )
-    def __bool__(self): return ( not not self.__toklist )
-    __nonzero__ = __bool__
-    def __iter__( self ): return iter( self.__toklist )
-    def __reversed__( self ): return iter( self.__toklist[::-1] )
-    def _iterkeys( self ):
-        if hasattr(self.__tokdict, "iterkeys"):
-            return self.__tokdict.iterkeys()
-        else:
-            return iter(self.__tokdict)
-
-    def _itervalues( self ):
-        return (self[k] for k in self._iterkeys())
-            
-    def _iteritems( self ):
-        return ((k, self[k]) for k in self._iterkeys())
-
-    if PY_3:
-        keys = _iterkeys       
-        """Returns an iterator of all named result keys (Python 3.x only)."""
-
-        values = _itervalues
-        """Returns an iterator of all named result values (Python 3.x only)."""
-
-        items = _iteritems
-        """Returns an iterator of all named result key-value tuples (Python 3.x only)."""
-
-    else:
-        iterkeys = _iterkeys
-        """Returns an iterator of all named result keys (Python 2.x only)."""
-
-        itervalues = _itervalues
-        """Returns an iterator of all named result values (Python 2.x only)."""
-
-        iteritems = _iteritems
-        """Returns an iterator of all named result key-value tuples (Python 2.x only)."""
-
-        def keys( self ):
-            """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.iterkeys())
-
-        def values( self ):
-            """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.itervalues())
-                
-        def items( self ):
-            """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.iteritems())
-
-    def haskeys( self ):
-        """Since keys() returns an iterator, this method is helpful in bypassing
-           code that looks for the existence of any defined results names."""
-        return bool(self.__tokdict)
-        
-    def pop( self, *args, **kwargs):
-        """
-        Removes and returns item at specified index (default=C{last}).
-        Supports both C{list} and C{dict} semantics for C{pop()}. If passed no
-        argument or an integer argument, it will use C{list} semantics
-        and pop tokens from the list of parsed tokens. If passed a 
-        non-integer argument (most likely a string), it will use C{dict}
-        semantics and pop the corresponding value from any defined 
-        results names. A second default return value argument is 
-        supported, just as in C{dict.pop()}.
-
-        Example::
-            def remove_first(tokens):
-                tokens.pop(0)
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-            print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321']
-
-            label = Word(alphas)
-            patt = label("LABEL") + OneOrMore(Word(nums))
-            print(patt.parseString("AAB 123 321").dump())
-
-            # Use pop() in a parse action to remove named result (note that corresponding value is not
-            # removed from list form of results)
-            def remove_LABEL(tokens):
-                tokens.pop("LABEL")
-                return tokens
-            patt.addParseAction(remove_LABEL)
-            print(patt.parseString("AAB 123 321").dump())
-        prints::
-            ['AAB', '123', '321']
-            - LABEL: AAB
-
-            ['AAB', '123', '321']
-        """
-        if not args:
-            args = [-1]
-        for k,v in kwargs.items():
-            if k == 'default':
-                args = (args[0], v)
-            else:
-                raise TypeError("pop() got an unexpected keyword argument '%s'" % k)
-        if (isinstance(args[0], int) or 
-                        len(args) == 1 or 
-                        args[0] in self):
-            index = args[0]
-            ret = self[index]
-            del self[index]
-            return ret
-        else:
-            defaultvalue = args[1]
-            return defaultvalue
-
-    def get(self, key, defaultValue=None):
-        """
-        Returns named result matching the given key, or if there is no
-        such name, then returns the given C{defaultValue} or C{None} if no
-        C{defaultValue} is specified.
-
-        Similar to C{dict.get()}.
-        
-        Example::
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
-
-            result = date_str.parseString("1999/12/31")
-            print(result.get("year")) # -> '1999'
-            print(result.get("hour", "not specified")) # -> 'not specified'
-            print(result.get("hour")) # -> None
-        """
-        if key in self:
-            return self[key]
-        else:
-            return defaultValue
-
-    def insert( self, index, insStr ):
-        """
-        Inserts new element at location index in the list of parsed tokens.
-        
-        Similar to C{list.insert()}.
-
-        Example::
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-
-            # use a parse action to insert the parse location in the front of the parsed results
-            def insert_locn(locn, tokens):
-                tokens.insert(0, locn)
-            print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321']
-        """
-        self.__toklist.insert(index, insStr)
-        # fixup indices in token dictionary
-        for name,occurrences in self.__tokdict.items():
-            for k, (value, position) in enumerate(occurrences):
-                occurrences[k] = _ParseResultsWithOffset(value, position + (position > index))
-
-    def append( self, item ):
-        """
-        Add single element to end of ParseResults list of elements.
-
-        Example::
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-            
-            # use a parse action to compute the sum of the parsed integers, and add it to the end
-            def append_sum(tokens):
-                tokens.append(sum(map(int, tokens)))
-            print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444]
-        """
-        self.__toklist.append(item)
-
-    def extend( self, itemseq ):
-        """
-        Add sequence of elements to end of ParseResults list of elements.
-
-        Example::
-            patt = OneOrMore(Word(alphas))
-            
-            # use a parse action to append the reverse of the matched strings, to make a palindrome
-            def make_palindrome(tokens):
-                tokens.extend(reversed([t[::-1] for t in tokens]))
-                return ''.join(tokens)
-            print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl'
-        """
-        if isinstance(itemseq, ParseResults):
-            self += itemseq
-        else:
-            self.__toklist.extend(itemseq)
-
-    def clear( self ):
-        """
-        Clear all elements and results names.
-        """
-        del self.__toklist[:]
-        self.__tokdict.clear()
-
-    def __getattr__( self, name ):
-        try:
-            return self[name]
-        except KeyError:
-            return ""
-            
-        if name in self.__tokdict:
-            if name not in self.__accumNames:
-                return self.__tokdict[name][-1][0]
-            else:
-                return ParseResults([ v[0] for v in self.__tokdict[name] ])
-        else:
-            return ""
-
-    def __add__( self, other ):
-        ret = self.copy()
-        ret += other
-        return ret
-
-    def __iadd__( self, other ):
-        if other.__tokdict:
-            offset = len(self.__toklist)
-            addoffset = lambda a: offset if a<0 else a+offset
-            otheritems = other.__tokdict.items()
-            otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) )
-                                for (k,vlist) in otheritems for v in vlist]
-            for k,v in otherdictitems:
-                self[k] = v
-                if isinstance(v[0],ParseResults):
-                    v[0].__parent = wkref(self)
-            
-        self.__toklist += other.__toklist
-        self.__accumNames.update( other.__accumNames )
-        return self
-
-    def __radd__(self, other):
-        if isinstance(other,int) and other == 0:
-            # useful for merging many ParseResults using sum() builtin
-            return self.copy()
-        else:
-            # this may raise a TypeError - so be it
-            return other + self
-        
-    def __repr__( self ):
-        return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) )
-
-    def __str__( self ):
-        return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']'
-
-    def _asStringList( self, sep='' ):
-        out = []
-        for item in self.__toklist:
-            if out and sep:
-                out.append(sep)
-            if isinstance( item, ParseResults ):
-                out += item._asStringList()
-            else:
-                out.append( _ustr(item) )
-        return out
-
-    def asList( self ):
-        """
-        Returns the parse results as a nested list of matching tokens, all converted to strings.
-
-        Example::
-            patt = OneOrMore(Word(alphas))
-            result = patt.parseString("sldkj lsdkj sldkj")
-            # even though the result prints in string-like form, it is actually a pyparsing ParseResults
-            print(type(result), result) # ->  ['sldkj', 'lsdkj', 'sldkj']
-            
-            # Use asList() to create an actual list
-            result_list = result.asList()
-            print(type(result_list), result_list) # ->  ['sldkj', 'lsdkj', 'sldkj']
-        """
-        return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist]
-
-    def asDict( self ):
-        """
-        Returns the named parse results as a nested dictionary.
-
-        Example::
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-            
-            result = date_str.parseString('12/31/1999')
-            print(type(result), repr(result)) # ->  (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
-            
-            result_dict = result.asDict()
-            print(type(result_dict), repr(result_dict)) # ->  {'day': '1999', 'year': '12', 'month': '31'}
-
-            # even though a ParseResults supports dict-like access, sometime you just need to have a dict
-            import json
-            print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable
-            print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"}
-        """
-        if PY_3:
-            item_fn = self.items
-        else:
-            item_fn = self.iteritems
-            
-        def toItem(obj):
-            if isinstance(obj, ParseResults):
-                if obj.haskeys():
-                    return obj.asDict()
-                else:
-                    return [toItem(v) for v in obj]
-            else:
-                return obj
-                
-        return dict((k,toItem(v)) for k,v in item_fn())
-
-    def copy( self ):
-        """
-        Returns a new copy of a C{ParseResults} object.
-        """
-        ret = ParseResults( self.__toklist )
-        ret.__tokdict = self.__tokdict.copy()
-        ret.__parent = self.__parent
-        ret.__accumNames.update( self.__accumNames )
-        ret.__name = self.__name
-        return ret
-
-    def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ):
-        """
-        (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.
-        """
-        nl = "\n"
-        out = []
-        namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items()
-                                                            for v in vlist)
-        nextLevelIndent = indent + "  "
-
-        # collapse out indents if formatting is not desired
-        if not formatted:
-            indent = ""
-            nextLevelIndent = ""
-            nl = ""
-
-        selfTag = None
-        if doctag is not None:
-            selfTag = doctag
-        else:
-            if self.__name:
-                selfTag = self.__name
-
-        if not selfTag:
-            if namedItemsOnly:
-                return ""
-            else:
-                selfTag = "ITEM"
-
-        out += [ nl, indent, "<", selfTag, ">" ]
-
-        for i,res in enumerate(self.__toklist):
-            if isinstance(res,ParseResults):
-                if i in namedItems:
-                    out += [ res.asXML(namedItems[i],
-                                        namedItemsOnly and doctag is None,
-                                        nextLevelIndent,
-                                        formatted)]
-                else:
-                    out += [ res.asXML(None,
-                                        namedItemsOnly and doctag is None,
-                                        nextLevelIndent,
-                                        formatted)]
-            else:
-                # individual token, see if there is a name for it
-                resTag = None
-                if i in namedItems:
-                    resTag = namedItems[i]
-                if not resTag:
-                    if namedItemsOnly:
-                        continue
-                    else:
-                        resTag = "ITEM"
-                xmlBodyText = _xml_escape(_ustr(res))
-                out += [ nl, nextLevelIndent, "<", resTag, ">",
-                                                xmlBodyText,
-                                                "" ]
-
-        out += [ nl, indent, "" ]
-        return "".join(out)
-
-    def __lookup(self,sub):
-        for k,vlist in self.__tokdict.items():
-            for v,loc in vlist:
-                if sub is v:
-                    return k
-        return None
-
-    def getName(self):
-        r"""
-        Returns the results name for this token expression. Useful when several 
-        different expressions might match at a particular location.
-
-        Example::
-            integer = Word(nums)
-            ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d")
-            house_number_expr = Suppress('#') + Word(nums, alphanums)
-            user_data = (Group(house_number_expr)("house_number") 
-                        | Group(ssn_expr)("ssn")
-                        | Group(integer)("age"))
-            user_info = OneOrMore(user_data)
-            
-            result = user_info.parseString("22 111-22-3333 #221B")
-            for item in result:
-                print(item.getName(), ':', item[0])
-        prints::
-            age : 22
-            ssn : 111-22-3333
-            house_number : 221B
-        """
-        if self.__name:
-            return self.__name
-        elif self.__parent:
-            par = self.__parent()
-            if par:
-                return par.__lookup(self)
-            else:
-                return None
-        elif (len(self) == 1 and
-               len(self.__tokdict) == 1 and
-               next(iter(self.__tokdict.values()))[0][1] in (0,-1)):
-            return next(iter(self.__tokdict.keys()))
-        else:
-            return None
-
-    def dump(self, indent='', depth=0, full=True):
-        """
-        Diagnostic method for listing out the contents of a C{ParseResults}.
-        Accepts an optional C{indent} argument so that this string can be embedded
-        in a nested display of other data.
-
-        Example::
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-            
-            result = date_str.parseString('12/31/1999')
-            print(result.dump())
-        prints::
-            ['12', '/', '31', '/', '1999']
-            - day: 1999
-            - month: 31
-            - year: 12
-        """
-        out = []
-        NL = '\n'
-        out.append( indent+_ustr(self.asList()) )
-        if full:
-            if self.haskeys():
-                items = sorted((str(k), v) for k,v in self.items())
-                for k,v in items:
-                    if out:
-                        out.append(NL)
-                    out.append( "%s%s- %s: " % (indent,('  '*depth), k) )
-                    if isinstance(v,ParseResults):
-                        if v:
-                            out.append( v.dump(indent,depth+1) )
-                        else:
-                            out.append(_ustr(v))
-                    else:
-                        out.append(repr(v))
-            elif any(isinstance(vv,ParseResults) for vv in self):
-                v = self
-                for i,vv in enumerate(v):
-                    if isinstance(vv,ParseResults):
-                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),vv.dump(indent,depth+1) ))
-                    else:
-                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),_ustr(vv)))
-            
-        return "".join(out)
-
-    def pprint(self, *args, **kwargs):
-        """
-        Pretty-printer for parsed results as a list, using the C{pprint} module.
-        Accepts additional positional or keyword args as defined for the 
-        C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint})
-
-        Example::
-            ident = Word(alphas, alphanums)
-            num = Word(nums)
-            func = Forward()
-            term = ident | num | Group('(' + func + ')')
-            func <<= ident + Group(Optional(delimitedList(term)))
-            result = func.parseString("fna a,b,(fnb c,d,200),100")
-            result.pprint(width=40)
-        prints::
-            ['fna',
-             ['a',
-              'b',
-              ['(', 'fnb', ['c', 'd', '200'], ')'],
-              '100']]
-        """
-        pprint.pprint(self.asList(), *args, **kwargs)
-
-    # add support for pickle protocol
-    def __getstate__(self):
-        return ( self.__toklist,
-                 ( self.__tokdict.copy(),
-                   self.__parent is not None and self.__parent() or None,
-                   self.__accumNames,
-                   self.__name ) )
-
-    def __setstate__(self,state):
-        self.__toklist = state[0]
-        (self.__tokdict,
-         par,
-         inAccumNames,
-         self.__name) = state[1]
-        self.__accumNames = {}
-        self.__accumNames.update(inAccumNames)
-        if par is not None:
-            self.__parent = wkref(par)
-        else:
-            self.__parent = None
-
-    def __getnewargs__(self):
-        return self.__toklist, self.__name, self.__asList, self.__modal
-
-    def __dir__(self):
-        return (dir(type(self)) + list(self.keys()))
-
-MutableMapping.register(ParseResults)
-
-def col (loc,strg):
-    """Returns current column within a string, counting newlines as line separators.
-   The first column is number 1.
-
-   Note: the default parsing behavior is to expand tabs in the input string
-   before starting the parsing process.  See L{I{ParserElement.parseString}} for more information
-   on parsing strings containing C{}s, and suggested methods to maintain a
-   consistent view of the parsed string, the parse location, and line and column
-   positions within the parsed string.
-   """
-    s = strg
-    return 1 if 0} for more information
-   on parsing strings containing C{}s, and suggested methods to maintain a
-   consistent view of the parsed string, the parse location, and line and column
-   positions within the parsed string.
-   """
-    return strg.count("\n",0,loc) + 1
-
-def line( loc, strg ):
-    """Returns the line of text containing loc within a string, counting newlines as line separators.
-       """
-    lastCR = strg.rfind("\n", 0, loc)
-    nextCR = strg.find("\n", loc)
-    if nextCR >= 0:
-        return strg[lastCR+1:nextCR]
-    else:
-        return strg[lastCR+1:]
-
-def _defaultStartDebugAction( instring, loc, expr ):
-    print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )))
-
-def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ):
-    print ("Matched " + _ustr(expr) + " -> " + str(toks.asList()))
-
-def _defaultExceptionDebugAction( instring, loc, expr, exc ):
-    print ("Exception raised:" + _ustr(exc))
-
-def nullDebugAction(*args):
-    """'Do-nothing' debug action, to suppress debugging output during parsing."""
-    pass
-
-# Only works on Python 3.x - nonlocal is toxic to Python 2 installs
-#~ 'decorator to trim function calls to match the arity of the target'
-#~ def _trim_arity(func, maxargs=3):
-    #~ if func in singleArgBuiltins:
-        #~ return lambda s,l,t: func(t)
-    #~ limit = 0
-    #~ foundArity = False
-    #~ def wrapper(*args):
-        #~ nonlocal limit,foundArity
-        #~ while 1:
-            #~ try:
-                #~ ret = func(*args[limit:])
-                #~ foundArity = True
-                #~ return ret
-            #~ except TypeError:
-                #~ if limit == maxargs or foundArity:
-                    #~ raise
-                #~ limit += 1
-                #~ continue
-    #~ return wrapper
-
-# this version is Python 2.x-3.x cross-compatible
-'decorator to trim function calls to match the arity of the target'
-def _trim_arity(func, maxargs=2):
-    if func in singleArgBuiltins:
-        return lambda s,l,t: func(t)
-    limit = [0]
-    foundArity = [False]
-    
-    # traceback return data structure changed in Py3.5 - normalize back to plain tuples
-    if system_version[:2] >= (3,5):
-        def extract_stack(limit=0):
-            # special handling for Python 3.5.0 - extra deep call stack by 1
-            offset = -3 if system_version == (3,5,0) else -2
-            frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset]
-            return [frame_summary[:2]]
-        def extract_tb(tb, limit=0):
-            frames = traceback.extract_tb(tb, limit=limit)
-            frame_summary = frames[-1]
-            return [frame_summary[:2]]
-    else:
-        extract_stack = traceback.extract_stack
-        extract_tb = traceback.extract_tb
-    
-    # synthesize what would be returned by traceback.extract_stack at the call to 
-    # user's parse action 'func', so that we don't incur call penalty at parse time
-    
-    LINE_DIFF = 6
-    # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND 
-    # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!!
-    this_line = extract_stack(limit=2)[-1]
-    pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF)
-
-    def wrapper(*args):
-        while 1:
-            try:
-                ret = func(*args[limit[0]:])
-                foundArity[0] = True
-                return ret
-            except TypeError:
-                # re-raise TypeErrors if they did not come from our arity testing
-                if foundArity[0]:
-                    raise
-                else:
-                    try:
-                        tb = sys.exc_info()[-1]
-                        if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth:
-                            raise
-                    finally:
-                        del tb
-
-                if limit[0] <= maxargs:
-                    limit[0] += 1
-                    continue
-                raise
-
-    # copy func name to wrapper for sensible debug output
-    func_name = ""
-    try:
-        func_name = getattr(func, '__name__', 
-                            getattr(func, '__class__').__name__)
-    except Exception:
-        func_name = str(func)
-    wrapper.__name__ = func_name
-
-    return wrapper
-
-class ParserElement(object):
-    """Abstract base level parser element class."""
-    DEFAULT_WHITE_CHARS = " \n\t\r"
-    verbose_stacktrace = False
-
-    @staticmethod
-    def setDefaultWhitespaceChars( chars ):
-        r"""
-        Overrides the default whitespace chars
-
-        Example::
-            # default whitespace chars are space,  and newline
-            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def', 'ghi', 'jkl']
-            
-            # change to just treat newline as significant
-            ParserElement.setDefaultWhitespaceChars(" \t")
-            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def']
-        """
-        ParserElement.DEFAULT_WHITE_CHARS = chars
-
-    @staticmethod
-    def inlineLiteralsUsing(cls):
-        """
-        Set class to be used for inclusion of string literals into a parser.
-        
-        Example::
-            # default literal class used is Literal
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
-
-
-            # change to Suppress
-            ParserElement.inlineLiteralsUsing(Suppress)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '12', '31']
-        """
-        ParserElement._literalStringClass = cls
-
-    def __init__( self, savelist=False ):
-        self.parseAction = list()
-        self.failAction = None
-        #~ self.name = ""  # don't define self.name, let subclasses try/except upcall
-        self.strRepr = None
-        self.resultsName = None
-        self.saveAsList = savelist
-        self.skipWhitespace = True
-        self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
-        self.copyDefaultWhiteChars = True
-        self.mayReturnEmpty = False # used when checking for left-recursion
-        self.keepTabs = False
-        self.ignoreExprs = list()
-        self.debug = False
-        self.streamlined = False
-        self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index
-        self.errmsg = ""
-        self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all)
-        self.debugActions = ( None, None, None ) #custom debug actions
-        self.re = None
-        self.callPreparse = True # used to avoid redundant calls to preParse
-        self.callDuringTry = False
-
-    def copy( self ):
-        """
-        Make a copy of this C{ParserElement}.  Useful for defining different parse actions
-        for the same parsing pattern, using copies of the original parse element.
-        
-        Example::
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K")
-            integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
-            
-            print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M"))
-        prints::
-            [5120, 100, 655360, 268435456]
-        Equivalent form of C{expr.copy()} is just C{expr()}::
-            integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
-        """
-        cpy = copy.copy( self )
-        cpy.parseAction = self.parseAction[:]
-        cpy.ignoreExprs = self.ignoreExprs[:]
-        if self.copyDefaultWhiteChars:
-            cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
-        return cpy
-
-    def setName( self, name ):
-        """
-        Define name for this expression, makes debugging and exception messages clearer.
-        
-        Example::
-            Word(nums).parseString("ABC")  # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1)
-            Word(nums).setName("integer").parseString("ABC")  # -> Exception: Expected integer (at char 0), (line:1, col:1)
-        """
-        self.name = name
-        self.errmsg = "Expected " + self.name
-        if hasattr(self,"exception"):
-            self.exception.msg = self.errmsg
-        return self
-
-    def setResultsName( self, name, listAllMatches=False ):
-        """
-        Define name for referencing matching tokens as a nested attribute
-        of the returned parse results.
-        NOTE: this returns a *copy* of the original C{ParserElement} object;
-        this is so that the client can define a basic element, such as an
-        integer, and reference it in multiple places with different names.
-
-        You can also set results names using the abbreviated syntax,
-        C{expr("name")} in place of C{expr.setResultsName("name")} - 
-        see L{I{__call__}<__call__>}.
-
-        Example::
-            date_str = (integer.setResultsName("year") + '/' 
-                        + integer.setResultsName("month") + '/' 
-                        + integer.setResultsName("day"))
-
-            # equivalent form:
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-        """
-        newself = self.copy()
-        if name.endswith("*"):
-            name = name[:-1]
-            listAllMatches=True
-        newself.resultsName = name
-        newself.modalResults = not listAllMatches
-        return newself
-
-    def setBreak(self,breakFlag = True):
-        """Method to invoke the Python pdb debugger when this element is
-           about to be parsed. Set C{breakFlag} to True to enable, False to
-           disable.
-        """
-        if breakFlag:
-            _parseMethod = self._parse
-            def breaker(instring, loc, doActions=True, callPreParse=True):
-                import pdb
-                pdb.set_trace()
-                return _parseMethod( instring, loc, doActions, callPreParse )
-            breaker._originalParseMethod = _parseMethod
-            self._parse = breaker
-        else:
-            if hasattr(self._parse,"_originalParseMethod"):
-                self._parse = self._parse._originalParseMethod
-        return self
-
-    def setParseAction( self, *fns, **kwargs ):
-        """
-        Define one or more actions to perform when successfully matching parse element definition.
-        Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)},
-        C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where:
-         - s   = the original string being parsed (see note below)
-         - loc = the location of the matching substring
-         - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object
-        If the functions in fns modify the tokens, they can return them as the return
-        value from fn, and the modified list of tokens will replace the original.
-        Otherwise, fn does not need to return any value.
-
-        Optional keyword arguments:
-         - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing
-
-        Note: the default parsing behavior is to expand tabs in the input string
-        before starting the parsing process.  See L{I{parseString}} for more information
-        on parsing strings containing C{}s, and suggested methods to maintain a
-        consistent view of the parsed string, the parse location, and line and column
-        positions within the parsed string.
-        
-        Example::
-            integer = Word(nums)
-            date_str = integer + '/' + integer + '/' + integer
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
-
-            # use parse action to convert to ints at parse time
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            date_str = integer + '/' + integer + '/' + integer
-
-            # note that integer fields are now ints, not strings
-            date_str.parseString("1999/12/31")  # -> [1999, '/', 12, '/', 31]
-        """
-        self.parseAction = list(map(_trim_arity, list(fns)))
-        self.callDuringTry = kwargs.get("callDuringTry", False)
-        return self
-
-    def addParseAction( self, *fns, **kwargs ):
-        """
-        Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}}.
-        
-        See examples in L{I{copy}}.
-        """
-        self.parseAction += list(map(_trim_arity, list(fns)))
-        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
-        return self
-
-    def addCondition(self, *fns, **kwargs):
-        """Add a boolean predicate function to expression's list of parse actions. See 
-        L{I{setParseAction}} for function call signatures. Unlike C{setParseAction}, 
-        functions passed to C{addCondition} need to return boolean success/fail of the condition.
-
-        Optional keyword arguments:
-         - message = define a custom message to be used in the raised exception
-         - fatal   = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException
-         
-        Example::
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            year_int = integer.copy()
-            year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later")
-            date_str = year_int + '/' + integer + '/' + integer
-
-            result = date_str.parseString("1999/12/31")  # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1)
-        """
-        msg = kwargs.get("message", "failed user-defined condition")
-        exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException
-        for fn in fns:
-            def pa(s,l,t):
-                if not bool(_trim_arity(fn)(s,l,t)):
-                    raise exc_type(s,l,msg)
-            self.parseAction.append(pa)
-        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
-        return self
-
-    def setFailAction( self, fn ):
-        """Define action to perform if parsing fails at this expression.
-           Fail acton fn is a callable function that takes the arguments
-           C{fn(s,loc,expr,err)} where:
-            - s = string being parsed
-            - loc = location where expression match was attempted and failed
-            - expr = the parse expression that failed
-            - err = the exception thrown
-           The function returns no value.  It may throw C{L{ParseFatalException}}
-           if it is desired to stop parsing immediately."""
-        self.failAction = fn
-        return self
-
-    def _skipIgnorables( self, instring, loc ):
-        exprsFound = True
-        while exprsFound:
-            exprsFound = False
-            for e in self.ignoreExprs:
-                try:
-                    while 1:
-                        loc,dummy = e._parse( instring, loc )
-                        exprsFound = True
-                except ParseException:
-                    pass
-        return loc
-
-    def preParse( self, instring, loc ):
-        if self.ignoreExprs:
-            loc = self._skipIgnorables( instring, loc )
-
-        if self.skipWhitespace:
-            wt = self.whiteChars
-            instrlen = len(instring)
-            while loc < instrlen and instring[loc] in wt:
-                loc += 1
-
-        return loc
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        return loc, []
-
-    def postParse( self, instring, loc, tokenlist ):
-        return tokenlist
-
-    #~ @profile
-    def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ):
-        debugging = ( self.debug ) #and doActions )
-
-        if debugging or self.failAction:
-            #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))
-            if (self.debugActions[0] ):
-                self.debugActions[0]( instring, loc, self )
-            if callPreParse and self.callPreparse:
-                preloc = self.preParse( instring, loc )
-            else:
-                preloc = loc
-            tokensStart = preloc
-            try:
-                try:
-                    loc,tokens = self.parseImpl( instring, preloc, doActions )
-                except IndexError:
-                    raise ParseException( instring, len(instring), self.errmsg, self )
-            except ParseBaseException as err:
-                #~ print ("Exception raised:", err)
-                if self.debugActions[2]:
-                    self.debugActions[2]( instring, tokensStart, self, err )
-                if self.failAction:
-                    self.failAction( instring, tokensStart, self, err )
-                raise
-        else:
-            if callPreParse and self.callPreparse:
-                preloc = self.preParse( instring, loc )
-            else:
-                preloc = loc
-            tokensStart = preloc
-            if self.mayIndexError or preloc >= len(instring):
-                try:
-                    loc,tokens = self.parseImpl( instring, preloc, doActions )
-                except IndexError:
-                    raise ParseException( instring, len(instring), self.errmsg, self )
-            else:
-                loc,tokens = self.parseImpl( instring, preloc, doActions )
-
-        tokens = self.postParse( instring, loc, tokens )
-
-        retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults )
-        if self.parseAction and (doActions or self.callDuringTry):
-            if debugging:
-                try:
-                    for fn in self.parseAction:
-                        tokens = fn( instring, tokensStart, retTokens )
-                        if tokens is not None:
-                            retTokens = ParseResults( tokens,
-                                                      self.resultsName,
-                                                      asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
-                                                      modal=self.modalResults )
-                except ParseBaseException as err:
-                    #~ print "Exception raised in user parse action:", err
-                    if (self.debugActions[2] ):
-                        self.debugActions[2]( instring, tokensStart, self, err )
-                    raise
-            else:
-                for fn in self.parseAction:
-                    tokens = fn( instring, tokensStart, retTokens )
-                    if tokens is not None:
-                        retTokens = ParseResults( tokens,
-                                                  self.resultsName,
-                                                  asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
-                                                  modal=self.modalResults )
-        if debugging:
-            #~ print ("Matched",self,"->",retTokens.asList())
-            if (self.debugActions[1] ):
-                self.debugActions[1]( instring, tokensStart, loc, self, retTokens )
-
-        return loc, retTokens
-
-    def tryParse( self, instring, loc ):
-        try:
-            return self._parse( instring, loc, doActions=False )[0]
-        except ParseFatalException:
-            raise ParseException( instring, loc, self.errmsg, self)
-    
-    def canParseNext(self, instring, loc):
-        try:
-            self.tryParse(instring, loc)
-        except (ParseException, IndexError):
-            return False
-        else:
-            return True
-
-    class _UnboundedCache(object):
-        def __init__(self):
-            cache = {}
-            self.not_in_cache = not_in_cache = object()
-
-            def get(self, key):
-                return cache.get(key, not_in_cache)
-
-            def set(self, key, value):
-                cache[key] = value
-
-            def clear(self):
-                cache.clear()
-                
-            def cache_len(self):
-                return len(cache)
-
-            self.get = types.MethodType(get, self)
-            self.set = types.MethodType(set, self)
-            self.clear = types.MethodType(clear, self)
-            self.__len__ = types.MethodType(cache_len, self)
-
-    if _OrderedDict is not None:
-        class _FifoCache(object):
-            def __init__(self, size):
-                self.not_in_cache = not_in_cache = object()
-
-                cache = _OrderedDict()
-
-                def get(self, key):
-                    return cache.get(key, not_in_cache)
-
-                def set(self, key, value):
-                    cache[key] = value
-                    while len(cache) > size:
-                        try:
-                            cache.popitem(False)
-                        except KeyError:
-                            pass
-
-                def clear(self):
-                    cache.clear()
-
-                def cache_len(self):
-                    return len(cache)
-
-                self.get = types.MethodType(get, self)
-                self.set = types.MethodType(set, self)
-                self.clear = types.MethodType(clear, self)
-                self.__len__ = types.MethodType(cache_len, self)
-
-    else:
-        class _FifoCache(object):
-            def __init__(self, size):
-                self.not_in_cache = not_in_cache = object()
-
-                cache = {}
-                key_fifo = collections.deque([], size)
-
-                def get(self, key):
-                    return cache.get(key, not_in_cache)
-
-                def set(self, key, value):
-                    cache[key] = value
-                    while len(key_fifo) > size:
-                        cache.pop(key_fifo.popleft(), None)
-                    key_fifo.append(key)
-
-                def clear(self):
-                    cache.clear()
-                    key_fifo.clear()
-
-                def cache_len(self):
-                    return len(cache)
-
-                self.get = types.MethodType(get, self)
-                self.set = types.MethodType(set, self)
-                self.clear = types.MethodType(clear, self)
-                self.__len__ = types.MethodType(cache_len, self)
-
-    # argument cache for optimizing repeated calls when backtracking through recursive expressions
-    packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail
-    packrat_cache_lock = RLock()
-    packrat_cache_stats = [0, 0]
-
-    # this method gets repeatedly called during backtracking with the same arguments -
-    # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression
-    def _parseCache( self, instring, loc, doActions=True, callPreParse=True ):
-        HIT, MISS = 0, 1
-        lookup = (self, instring, loc, callPreParse, doActions)
-        with ParserElement.packrat_cache_lock:
-            cache = ParserElement.packrat_cache
-            value = cache.get(lookup)
-            if value is cache.not_in_cache:
-                ParserElement.packrat_cache_stats[MISS] += 1
-                try:
-                    value = self._parseNoCache(instring, loc, doActions, callPreParse)
-                except ParseBaseException as pe:
-                    # cache a copy of the exception, without the traceback
-                    cache.set(lookup, pe.__class__(*pe.args))
-                    raise
-                else:
-                    cache.set(lookup, (value[0], value[1].copy()))
-                    return value
-            else:
-                ParserElement.packrat_cache_stats[HIT] += 1
-                if isinstance(value, Exception):
-                    raise value
-                return (value[0], value[1].copy())
-
-    _parse = _parseNoCache
-
-    @staticmethod
-    def resetCache():
-        ParserElement.packrat_cache.clear()
-        ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats)
-
-    _packratEnabled = False
-    @staticmethod
-    def enablePackrat(cache_size_limit=128):
-        """Enables "packrat" parsing, which adds memoizing to the parsing logic.
-           Repeated parse attempts at the same string location (which happens
-           often in many complex grammars) can immediately return a cached value,
-           instead of re-executing parsing/validating code.  Memoizing is done of
-           both valid results and parsing exceptions.
-           
-           Parameters:
-            - cache_size_limit - (default=C{128}) - if an integer value is provided
-              will limit the size of the packrat cache; if None is passed, then
-              the cache size will be unbounded; if 0 is passed, the cache will
-              be effectively disabled.
-            
-           This speedup may break existing programs that use parse actions that
-           have side-effects.  For this reason, packrat parsing is disabled when
-           you first import pyparsing.  To activate the packrat feature, your
-           program must call the class method C{ParserElement.enablePackrat()}.  If
-           your program uses C{psyco} to "compile as you go", you must call
-           C{enablePackrat} before calling C{psyco.full()}.  If you do not do this,
-           Python will crash.  For best results, call C{enablePackrat()} immediately
-           after importing pyparsing.
-           
-           Example::
-               import pyparsing
-               pyparsing.ParserElement.enablePackrat()
-        """
-        if not ParserElement._packratEnabled:
-            ParserElement._packratEnabled = True
-            if cache_size_limit is None:
-                ParserElement.packrat_cache = ParserElement._UnboundedCache()
-            else:
-                ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit)
-            ParserElement._parse = ParserElement._parseCache
-
-    def parseString( self, instring, parseAll=False ):
-        """
-        Execute the parse expression with the given string.
-        This is the main interface to the client code, once the complete
-        expression has been built.
-
-        If you want the grammar to require that the entire input string be
-        successfully parsed, then set C{parseAll} to True (equivalent to ending
-        the grammar with C{L{StringEnd()}}).
-
-        Note: C{parseString} implicitly calls C{expandtabs()} on the input string,
-        in order to report proper column numbers in parse actions.
-        If the input string contains tabs and
-        the grammar uses parse actions that use the C{loc} argument to index into the
-        string being parsed, you can ensure you have a consistent view of the input
-        string by:
-         - calling C{parseWithTabs} on your grammar before calling C{parseString}
-           (see L{I{parseWithTabs}})
-         - define your parse action using the full C{(s,loc,toks)} signature, and
-           reference the input string using the parse action's C{s} argument
-         - explicitly expand the tabs in your input string before calling
-           C{parseString}
-        
-        Example::
-            Word('a').parseString('aaaaabaaa')  # -> ['aaaaa']
-            Word('a').parseString('aaaaabaaa', parseAll=True)  # -> Exception: Expected end of text
-        """
-        ParserElement.resetCache()
-        if not self.streamlined:
-            self.streamline()
-            #~ self.saveAsList = True
-        for e in self.ignoreExprs:
-            e.streamline()
-        if not self.keepTabs:
-            instring = instring.expandtabs()
-        try:
-            loc, tokens = self._parse( instring, 0 )
-            if parseAll:
-                loc = self.preParse( instring, loc )
-                se = Empty() + StringEnd()
-                se._parse( instring, loc )
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-        else:
-            return tokens
-
-    def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ):
-        """
-        Scan the input string for expression matches.  Each match will return the
-        matching tokens, start location, and end location.  May be called with optional
-        C{maxMatches} argument, to clip scanning after 'n' matches are found.  If
-        C{overlap} is specified, then overlapping matches will be reported.
-
-        Note that the start and end locations are reported relative to the string
-        being parsed.  See L{I{parseString}} for more information on parsing
-        strings with embedded tabs.
-
-        Example::
-            source = "sldjf123lsdjjkf345sldkjf879lkjsfd987"
-            print(source)
-            for tokens,start,end in Word(alphas).scanString(source):
-                print(' '*start + '^'*(end-start))
-                print(' '*start + tokens[0])
-        
-        prints::
-        
-            sldjf123lsdjjkf345sldkjf879lkjsfd987
-            ^^^^^
-            sldjf
-                    ^^^^^^^
-                    lsdjjkf
-                              ^^^^^^
-                              sldkjf
-                                       ^^^^^^
-                                       lkjsfd
-        """
-        if not self.streamlined:
-            self.streamline()
-        for e in self.ignoreExprs:
-            e.streamline()
-
-        if not self.keepTabs:
-            instring = _ustr(instring).expandtabs()
-        instrlen = len(instring)
-        loc = 0
-        preparseFn = self.preParse
-        parseFn = self._parse
-        ParserElement.resetCache()
-        matches = 0
-        try:
-            while loc <= instrlen and matches < maxMatches:
-                try:
-                    preloc = preparseFn( instring, loc )
-                    nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
-                except ParseException:
-                    loc = preloc+1
-                else:
-                    if nextLoc > loc:
-                        matches += 1
-                        yield tokens, preloc, nextLoc
-                        if overlap:
-                            nextloc = preparseFn( instring, loc )
-                            if nextloc > loc:
-                                loc = nextLoc
-                            else:
-                                loc += 1
-                        else:
-                            loc = nextLoc
-                    else:
-                        loc = preloc+1
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def transformString( self, instring ):
-        """
-        Extension to C{L{scanString}}, to modify matching text with modified tokens that may
-        be returned from a parse action.  To use C{transformString}, define a grammar and
-        attach a parse action to it that modifies the returned token list.
-        Invoking C{transformString()} on a target string will then scan for matches,
-        and replace the matched text patterns according to the logic in the parse
-        action.  C{transformString()} returns the resulting transformed string.
-        
-        Example::
-            wd = Word(alphas)
-            wd.setParseAction(lambda toks: toks[0].title())
-            
-            print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york."))
-        Prints::
-            Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York.
-        """
-        out = []
-        lastE = 0
-        # force preservation of s, to minimize unwanted transformation of string, and to
-        # keep string locs straight between transformString and scanString
-        self.keepTabs = True
-        try:
-            for t,s,e in self.scanString( instring ):
-                out.append( instring[lastE:s] )
-                if t:
-                    if isinstance(t,ParseResults):
-                        out += t.asList()
-                    elif isinstance(t,list):
-                        out += t
-                    else:
-                        out.append(t)
-                lastE = e
-            out.append(instring[lastE:])
-            out = [o for o in out if o]
-            return "".join(map(_ustr,_flatten(out)))
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def searchString( self, instring, maxMatches=_MAX_INT ):
-        """
-        Another extension to C{L{scanString}}, simplifying the access to the tokens found
-        to match the given parse expression.  May be called with optional
-        C{maxMatches} argument, to clip searching after 'n' matches are found.
-        
-        Example::
-            # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters
-            cap_word = Word(alphas.upper(), alphas.lower())
-            
-            print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))
-
-            # the sum() builtin can be used to merge results into a single ParseResults object
-            print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")))
-        prints::
-            [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']]
-            ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity']
-        """
-        try:
-            return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ])
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False):
-        """
-        Generator method to split a string using the given expression as a separator.
-        May be called with optional C{maxsplit} argument, to limit the number of splits;
-        and the optional C{includeSeparators} argument (default=C{False}), if the separating
-        matching text should be included in the split results.
-        
-        Example::        
-            punc = oneOf(list(".,;:/-!?"))
-            print(list(punc.split("This, this?, this sentence, is badly punctuated!")))
-        prints::
-            ['This', ' this', '', ' this sentence', ' is badly punctuated', '']
-        """
-        splits = 0
-        last = 0
-        for t,s,e in self.scanString(instring, maxMatches=maxsplit):
-            yield instring[last:s]
-            if includeSeparators:
-                yield t[0]
-            last = e
-        yield instring[last:]
-
-    def __add__(self, other ):
-        """
-        Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement
-        converts them to L{Literal}s by default.
-        
-        Example::
-            greet = Word(alphas) + "," + Word(alphas) + "!"
-            hello = "Hello, World!"
-            print (hello, "->", greet.parseString(hello))
-        Prints::
-            Hello, World! -> ['Hello', ',', 'World', '!']
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return And( [ self, other ] )
-
-    def __radd__(self, other ):
-        """
-        Implementation of + operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other + self
-
-    def __sub__(self, other):
-        """
-        Implementation of - operator, returns C{L{And}} with error stop
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return self + And._ErrorStop() + other
-
-    def __rsub__(self, other ):
-        """
-        Implementation of - operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other - self
-
-    def __mul__(self,other):
-        """
-        Implementation of * operator, allows use of C{expr * 3} in place of
-        C{expr + expr + expr}.  Expressions may also me multiplied by a 2-integer
-        tuple, similar to C{{min,max}} multipliers in regular expressions.  Tuples
-        may also include C{None} as in:
-         - C{expr*(n,None)} or C{expr*(n,)} is equivalent
-              to C{expr*n + L{ZeroOrMore}(expr)}
-              (read as "at least n instances of C{expr}")
-         - C{expr*(None,n)} is equivalent to C{expr*(0,n)}
-              (read as "0 to n instances of C{expr}")
-         - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)}
-         - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)}
-
-        Note that C{expr*(None,n)} does not raise an exception if
-        more than n exprs exist in the input stream; that is,
-        C{expr*(None,n)} does not enforce a maximum number of expr
-        occurrences.  If this behavior is desired, then write
-        C{expr*(None,n) + ~expr}
-        """
-        if isinstance(other,int):
-            minElements, optElements = other,0
-        elif isinstance(other,tuple):
-            other = (other + (None, None))[:2]
-            if other[0] is None:
-                other = (0, other[1])
-            if isinstance(other[0],int) and other[1] is None:
-                if other[0] == 0:
-                    return ZeroOrMore(self)
-                if other[0] == 1:
-                    return OneOrMore(self)
-                else:
-                    return self*other[0] + ZeroOrMore(self)
-            elif isinstance(other[0],int) and isinstance(other[1],int):
-                minElements, optElements = other
-                optElements -= minElements
-            else:
-                raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1]))
-        else:
-            raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other))
-
-        if minElements < 0:
-            raise ValueError("cannot multiply ParserElement by negative value")
-        if optElements < 0:
-            raise ValueError("second tuple value must be greater or equal to first tuple value")
-        if minElements == optElements == 0:
-            raise ValueError("cannot multiply ParserElement by 0 or (0,0)")
-
-        if (optElements):
-            def makeOptionalList(n):
-                if n>1:
-                    return Optional(self + makeOptionalList(n-1))
-                else:
-                    return Optional(self)
-            if minElements:
-                if minElements == 1:
-                    ret = self + makeOptionalList(optElements)
-                else:
-                    ret = And([self]*minElements) + makeOptionalList(optElements)
-            else:
-                ret = makeOptionalList(optElements)
-        else:
-            if minElements == 1:
-                ret = self
-            else:
-                ret = And([self]*minElements)
-        return ret
-
-    def __rmul__(self, other):
-        return self.__mul__(other)
-
-    def __or__(self, other ):
-        """
-        Implementation of | operator - returns C{L{MatchFirst}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return MatchFirst( [ self, other ] )
-
-    def __ror__(self, other ):
-        """
-        Implementation of | operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other | self
-
-    def __xor__(self, other ):
-        """
-        Implementation of ^ operator - returns C{L{Or}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return Or( [ self, other ] )
-
-    def __rxor__(self, other ):
-        """
-        Implementation of ^ operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other ^ self
-
-    def __and__(self, other ):
-        """
-        Implementation of & operator - returns C{L{Each}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return Each( [ self, other ] )
-
-    def __rand__(self, other ):
-        """
-        Implementation of & operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other & self
-
-    def __invert__( self ):
-        """
-        Implementation of ~ operator - returns C{L{NotAny}}
-        """
-        return NotAny( self )
-
-    def __call__(self, name=None):
-        """
-        Shortcut for C{L{setResultsName}}, with C{listAllMatches=False}.
-        
-        If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be
-        passed as C{True}.
-           
-        If C{name} is omitted, same as calling C{L{copy}}.
-
-        Example::
-            # these are equivalent
-            userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno")
-            userdata = Word(alphas)("name") + Word(nums+"-")("socsecno")             
-        """
-        if name is not None:
-            return self.setResultsName(name)
-        else:
-            return self.copy()
-
-    def suppress( self ):
-        """
-        Suppresses the output of this C{ParserElement}; useful to keep punctuation from
-        cluttering up returned output.
-        """
-        return Suppress( self )
-
-    def leaveWhitespace( self ):
-        """
-        Disables the skipping of whitespace before matching the characters in the
-        C{ParserElement}'s defined pattern.  This is normally only used internally by
-        the pyparsing module, but may be needed in some whitespace-sensitive grammars.
-        """
-        self.skipWhitespace = False
-        return self
-
-    def setWhitespaceChars( self, chars ):
-        """
-        Overrides the default whitespace chars
-        """
-        self.skipWhitespace = True
-        self.whiteChars = chars
-        self.copyDefaultWhiteChars = False
-        return self
-
-    def parseWithTabs( self ):
-        """
-        Overrides default behavior to expand C{}s to spaces before parsing the input string.
-        Must be called before C{parseString} when the input grammar contains elements that
-        match C{} characters.
-        """
-        self.keepTabs = True
-        return self
-
-    def ignore( self, other ):
-        """
-        Define expression to be ignored (e.g., comments) while doing pattern
-        matching; may be called repeatedly, to define multiple comment or other
-        ignorable patterns.
-        
-        Example::
-            patt = OneOrMore(Word(alphas))
-            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj']
-            
-            patt.ignore(cStyleComment)
-            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd']
-        """
-        if isinstance(other, basestring):
-            other = Suppress(other)
-
-        if isinstance( other, Suppress ):
-            if other not in self.ignoreExprs:
-                self.ignoreExprs.append(other)
-        else:
-            self.ignoreExprs.append( Suppress( other.copy() ) )
-        return self
-
-    def setDebugActions( self, startAction, successAction, exceptionAction ):
-        """
-        Enable display of debugging messages while doing pattern matching.
-        """
-        self.debugActions = (startAction or _defaultStartDebugAction,
-                             successAction or _defaultSuccessDebugAction,
-                             exceptionAction or _defaultExceptionDebugAction)
-        self.debug = True
-        return self
-
-    def setDebug( self, flag=True ):
-        """
-        Enable display of debugging messages while doing pattern matching.
-        Set C{flag} to True to enable, False to disable.
-
-        Example::
-            wd = Word(alphas).setName("alphaword")
-            integer = Word(nums).setName("numword")
-            term = wd | integer
-            
-            # turn on debugging for wd
-            wd.setDebug()
-
-            OneOrMore(term).parseString("abc 123 xyz 890")
-        
-        prints::
-            Match alphaword at loc 0(1,1)
-            Matched alphaword -> ['abc']
-            Match alphaword at loc 3(1,4)
-            Exception raised:Expected alphaword (at char 4), (line:1, col:5)
-            Match alphaword at loc 7(1,8)
-            Matched alphaword -> ['xyz']
-            Match alphaword at loc 11(1,12)
-            Exception raised:Expected alphaword (at char 12), (line:1, col:13)
-            Match alphaword at loc 15(1,16)
-            Exception raised:Expected alphaword (at char 15), (line:1, col:16)
-
-        The output shown is that produced by the default debug actions - custom debug actions can be
-        specified using L{setDebugActions}. Prior to attempting
-        to match the C{wd} expression, the debugging message C{"Match  at loc (,)"}
-        is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"}
-        message is shown. Also note the use of L{setName} to assign a human-readable name to the expression,
-        which makes debugging and exception messages easier to understand - for instance, the default
-        name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}.
-        """
-        if flag:
-            self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction )
-        else:
-            self.debug = False
-        return self
-
-    def __str__( self ):
-        return self.name
-
-    def __repr__( self ):
-        return _ustr(self)
-
-    def streamline( self ):
-        self.streamlined = True
-        self.strRepr = None
-        return self
-
-    def checkRecursion( self, parseElementList ):
-        pass
-
-    def validate( self, validateTrace=[] ):
-        """
-        Check defined expressions for valid structure, check for infinite recursive definitions.
-        """
-        self.checkRecursion( [] )
-
-    def parseFile( self, file_or_filename, parseAll=False ):
-        """
-        Execute the parse expression on the given file or filename.
-        If a filename is specified (instead of a file object),
-        the entire file is opened, read, and closed before parsing.
-        """
-        try:
-            file_contents = file_or_filename.read()
-        except AttributeError:
-            with open(file_or_filename, "r") as f:
-                file_contents = f.read()
-        try:
-            return self.parseString(file_contents, parseAll)
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def __eq__(self,other):
-        if isinstance(other, ParserElement):
-            return self is other or vars(self) == vars(other)
-        elif isinstance(other, basestring):
-            return self.matches(other)
-        else:
-            return super(ParserElement,self)==other
-
-    def __ne__(self,other):
-        return not (self == other)
-
-    def __hash__(self):
-        return hash(id(self))
-
-    def __req__(self,other):
-        return self == other
-
-    def __rne__(self,other):
-        return not (self == other)
-
-    def matches(self, testString, parseAll=True):
-        """
-        Method for quick testing of a parser against a test string. Good for simple 
-        inline microtests of sub expressions while building up larger parser.
-           
-        Parameters:
-         - testString - to test against this expression for a match
-         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests
-            
-        Example::
-            expr = Word(nums)
-            assert expr.matches("100")
-        """
-        try:
-            self.parseString(_ustr(testString), parseAll=parseAll)
-            return True
-        except ParseBaseException:
-            return False
-                
-    def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False):
-        """
-        Execute the parse expression on a series of test strings, showing each
-        test, the parsed results or where the parse failed. Quick and easy way to
-        run a parse expression against a list of sample strings.
-           
-        Parameters:
-         - tests - a list of separate test strings, or a multiline string of test strings
-         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests           
-         - comment - (default=C{'#'}) - expression for indicating embedded comments in the test 
-              string; pass None to disable comment filtering
-         - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline;
-              if False, only dump nested list
-         - printResults - (default=C{True}) prints test output to stdout
-         - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing
-
-        Returns: a (success, results) tuple, where success indicates that all tests succeeded
-        (or failed if C{failureTests} is True), and the results contain a list of lines of each 
-        test's output
-        
-        Example::
-            number_expr = pyparsing_common.number.copy()
-
-            result = number_expr.runTests('''
-                # unsigned integer
-                100
-                # negative integer
-                -100
-                # float with scientific notation
-                6.02e23
-                # integer with scientific notation
-                1e-12
-                ''')
-            print("Success" if result[0] else "Failed!")
-
-            result = number_expr.runTests('''
-                # stray character
-                100Z
-                # missing leading digit before '.'
-                -.100
-                # too many '.'
-                3.14.159
-                ''', failureTests=True)
-            print("Success" if result[0] else "Failed!")
-        prints::
-            # unsigned integer
-            100
-            [100]
-
-            # negative integer
-            -100
-            [-100]
-
-            # float with scientific notation
-            6.02e23
-            [6.02e+23]
-
-            # integer with scientific notation
-            1e-12
-            [1e-12]
-
-            Success
-            
-            # stray character
-            100Z
-               ^
-            FAIL: Expected end of text (at char 3), (line:1, col:4)
-
-            # missing leading digit before '.'
-            -.100
-            ^
-            FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1)
-
-            # too many '.'
-            3.14.159
-                ^
-            FAIL: Expected end of text (at char 4), (line:1, col:5)
-
-            Success
-
-        Each test string must be on a single line. If you want to test a string that spans multiple
-        lines, create a test like this::
-
-            expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines")
-        
-        (Note that this is a raw string literal, you must include the leading 'r'.)
-        """
-        if isinstance(tests, basestring):
-            tests = list(map(str.strip, tests.rstrip().splitlines()))
-        if isinstance(comment, basestring):
-            comment = Literal(comment)
-        allResults = []
-        comments = []
-        success = True
-        for t in tests:
-            if comment is not None and comment.matches(t, False) or comments and not t:
-                comments.append(t)
-                continue
-            if not t:
-                continue
-            out = ['\n'.join(comments), t]
-            comments = []
-            try:
-                t = t.replace(r'\n','\n')
-                result = self.parseString(t, parseAll=parseAll)
-                out.append(result.dump(full=fullDump))
-                success = success and not failureTests
-            except ParseBaseException as pe:
-                fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else ""
-                if '\n' in t:
-                    out.append(line(pe.loc, t))
-                    out.append(' '*(col(pe.loc,t)-1) + '^' + fatal)
-                else:
-                    out.append(' '*pe.loc + '^' + fatal)
-                out.append("FAIL: " + str(pe))
-                success = success and failureTests
-                result = pe
-            except Exception as exc:
-                out.append("FAIL-EXCEPTION: " + str(exc))
-                success = success and failureTests
-                result = exc
-
-            if printResults:
-                if fullDump:
-                    out.append('')
-                print('\n'.join(out))
-
-            allResults.append((t, result))
-        
-        return success, allResults
-
-        
-class Token(ParserElement):
-    """
-    Abstract C{ParserElement} subclass, for defining atomic matching patterns.
-    """
-    def __init__( self ):
-        super(Token,self).__init__( savelist=False )
-
-
-class Empty(Token):
-    """
-    An empty token, will always match.
-    """
-    def __init__( self ):
-        super(Empty,self).__init__()
-        self.name = "Empty"
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-
-
-class NoMatch(Token):
-    """
-    A token that will never match.
-    """
-    def __init__( self ):
-        super(NoMatch,self).__init__()
-        self.name = "NoMatch"
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-        self.errmsg = "Unmatchable token"
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        raise ParseException(instring, loc, self.errmsg, self)
-
-
-class Literal(Token):
-    """
-    Token to exactly match a specified string.
-    
-    Example::
-        Literal('blah').parseString('blah')  # -> ['blah']
-        Literal('blah').parseString('blahfooblah')  # -> ['blah']
-        Literal('blah').parseString('bla')  # -> Exception: Expected "blah"
-    
-    For case-insensitive matching, use L{CaselessLiteral}.
-    
-    For keyword matching (force word break before and after the matched string),
-    use L{Keyword} or L{CaselessKeyword}.
-    """
-    def __init__( self, matchString ):
-        super(Literal,self).__init__()
-        self.match = matchString
-        self.matchLen = len(matchString)
-        try:
-            self.firstMatchChar = matchString[0]
-        except IndexError:
-            warnings.warn("null string passed to Literal; use Empty() instead",
-                            SyntaxWarning, stacklevel=2)
-            self.__class__ = Empty
-        self.name = '"%s"' % _ustr(self.match)
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = False
-        self.mayIndexError = False
-
-    # Performance tuning: this routine gets called a *lot*
-    # if this is a single character match string  and the first character matches,
-    # short-circuit as quickly as possible, and avoid calling startswith
-    #~ @profile
-    def parseImpl( self, instring, loc, doActions=True ):
-        if (instring[loc] == self.firstMatchChar and
-            (self.matchLen==1 or instring.startswith(self.match,loc)) ):
-            return loc+self.matchLen, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-_L = Literal
-ParserElement._literalStringClass = Literal
-
-class Keyword(Token):
-    """
-    Token to exactly match a specified string as a keyword, that is, it must be
-    immediately followed by a non-keyword character.  Compare with C{L{Literal}}:
-     - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}.
-     - C{Keyword("if")} will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'}
-    Accepts two optional constructor arguments in addition to the keyword string:
-     - C{identChars} is a string of characters that would be valid identifier characters,
-          defaulting to all alphanumerics + "_" and "$"
-     - C{caseless} allows case-insensitive matching, default is C{False}.
-       
-    Example::
-        Keyword("start").parseString("start")  # -> ['start']
-        Keyword("start").parseString("starting")  # -> Exception
-
-    For case-insensitive matching, use L{CaselessKeyword}.
-    """
-    DEFAULT_KEYWORD_CHARS = alphanums+"_$"
-
-    def __init__( self, matchString, identChars=None, caseless=False ):
-        super(Keyword,self).__init__()
-        if identChars is None:
-            identChars = Keyword.DEFAULT_KEYWORD_CHARS
-        self.match = matchString
-        self.matchLen = len(matchString)
-        try:
-            self.firstMatchChar = matchString[0]
-        except IndexError:
-            warnings.warn("null string passed to Keyword; use Empty() instead",
-                            SyntaxWarning, stacklevel=2)
-        self.name = '"%s"' % self.match
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = False
-        self.mayIndexError = False
-        self.caseless = caseless
-        if caseless:
-            self.caselessmatch = matchString.upper()
-            identChars = identChars.upper()
-        self.identChars = set(identChars)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.caseless:
-            if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
-                 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and
-                 (loc == 0 or instring[loc-1].upper() not in self.identChars) ):
-                return loc+self.matchLen, self.match
-        else:
-            if (instring[loc] == self.firstMatchChar and
-                (self.matchLen==1 or instring.startswith(self.match,loc)) and
-                (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and
-                (loc == 0 or instring[loc-1] not in self.identChars) ):
-                return loc+self.matchLen, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-
-    def copy(self):
-        c = super(Keyword,self).copy()
-        c.identChars = Keyword.DEFAULT_KEYWORD_CHARS
-        return c
-
-    @staticmethod
-    def setDefaultKeywordChars( chars ):
-        """Overrides the default Keyword chars
-        """
-        Keyword.DEFAULT_KEYWORD_CHARS = chars
-
-class CaselessLiteral(Literal):
-    """
-    Token to match a specified string, ignoring case of letters.
-    Note: the matched results will always be in the case of the given
-    match string, NOT the case of the input text.
-
-    Example::
-        OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD']
-        
-    (Contrast with example for L{CaselessKeyword}.)
-    """
-    def __init__( self, matchString ):
-        super(CaselessLiteral,self).__init__( matchString.upper() )
-        # Preserve the defining literal.
-        self.returnString = matchString
-        self.name = "'%s'" % self.returnString
-        self.errmsg = "Expected " + self.name
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if instring[ loc:loc+self.matchLen ].upper() == self.match:
-            return loc+self.matchLen, self.returnString
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class CaselessKeyword(Keyword):
-    """
-    Caseless version of L{Keyword}.
-
-    Example::
-        OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD']
-        
-    (Contrast with example for L{CaselessLiteral}.)
-    """
-    def __init__( self, matchString, identChars=None ):
-        super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True )
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
-             (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ):
-            return loc+self.matchLen, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class CloseMatch(Token):
-    """
-    A variation on L{Literal} which matches "close" matches, that is, 
-    strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters:
-     - C{match_string} - string to be matched
-     - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match
-    
-    The results from a successful parse will contain the matched text from the input string and the following named results:
-     - C{mismatches} - a list of the positions within the match_string where mismatches were found
-     - C{original} - the original match_string used to compare against the input string
-    
-    If C{mismatches} is an empty list, then the match was an exact match.
-    
-    Example::
-        patt = CloseMatch("ATCATCGAATGGA")
-        patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']})
-        patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1)
-
-        # exact match
-        patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']})
-
-        # close match allowing up to 2 mismatches
-        patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2)
-        patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']})
-    """
-    def __init__(self, match_string, maxMismatches=1):
-        super(CloseMatch,self).__init__()
-        self.name = match_string
-        self.match_string = match_string
-        self.maxMismatches = maxMismatches
-        self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches)
-        self.mayIndexError = False
-        self.mayReturnEmpty = False
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        start = loc
-        instrlen = len(instring)
-        maxloc = start + len(self.match_string)
-
-        if maxloc <= instrlen:
-            match_string = self.match_string
-            match_stringloc = 0
-            mismatches = []
-            maxMismatches = self.maxMismatches
-
-            for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)):
-                src,mat = s_m
-                if src != mat:
-                    mismatches.append(match_stringloc)
-                    if len(mismatches) > maxMismatches:
-                        break
-            else:
-                loc = match_stringloc + 1
-                results = ParseResults([instring[start:loc]])
-                results['original'] = self.match_string
-                results['mismatches'] = mismatches
-                return loc, results
-
-        raise ParseException(instring, loc, self.errmsg, self)
-
-
-class Word(Token):
-    """
-    Token for matching words composed of allowed character sets.
-    Defined with string containing all allowed initial characters,
-    an optional string containing allowed body characters (if omitted,
-    defaults to the initial character set), and an optional minimum,
-    maximum, and/or exact length.  The default value for C{min} is 1 (a
-    minimum value < 1 is not valid); the default values for C{max} and C{exact}
-    are 0, meaning no maximum or exact length restriction. An optional
-    C{excludeChars} parameter can list characters that might be found in 
-    the input C{bodyChars} string; useful to define a word of all printables
-    except for one or two characters, for instance.
-    
-    L{srange} is useful for defining custom character set strings for defining 
-    C{Word} expressions, using range notation from regular expression character sets.
-    
-    A common mistake is to use C{Word} to match a specific literal string, as in 
-    C{Word("Address")}. Remember that C{Word} uses the string argument to define
-    I{sets} of matchable characters. This expression would match "Add", "AAA",
-    "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'.
-    To match an exact literal string, use L{Literal} or L{Keyword}.
-
-    pyparsing includes helper strings for building Words:
-     - L{alphas}
-     - L{nums}
-     - L{alphanums}
-     - L{hexnums}
-     - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.)
-     - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.)
-     - L{printables} (any non-whitespace character)
-
-    Example::
-        # a word composed of digits
-        integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9"))
-        
-        # a word with a leading capital, and zero or more lowercase
-        capital_word = Word(alphas.upper(), alphas.lower())
-
-        # hostnames are alphanumeric, with leading alpha, and '-'
-        hostname = Word(alphas, alphanums+'-')
-        
-        # roman numeral (not a strict parser, accepts invalid mix of characters)
-        roman = Word("IVXLCDM")
-        
-        # any string of non-whitespace characters, except for ','
-        csv_value = Word(printables, excludeChars=",")
-    """
-    def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ):
-        super(Word,self).__init__()
-        if excludeChars:
-            initChars = ''.join(c for c in initChars if c not in excludeChars)
-            if bodyChars:
-                bodyChars = ''.join(c for c in bodyChars if c not in excludeChars)
-        self.initCharsOrig = initChars
-        self.initChars = set(initChars)
-        if bodyChars :
-            self.bodyCharsOrig = bodyChars
-            self.bodyChars = set(bodyChars)
-        else:
-            self.bodyCharsOrig = initChars
-            self.bodyChars = set(initChars)
-
-        self.maxSpecified = max > 0
-
-        if min < 1:
-            raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted")
-
-        self.minLen = min
-
-        if max > 0:
-            self.maxLen = max
-        else:
-            self.maxLen = _MAX_INT
-
-        if exact > 0:
-            self.maxLen = exact
-            self.minLen = exact
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.asKeyword = asKeyword
-
-        if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0):
-            if self.bodyCharsOrig == self.initCharsOrig:
-                self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig)
-            elif len(self.initCharsOrig) == 1:
-                self.reString = "%s[%s]*" % \
-                                      (re.escape(self.initCharsOrig),
-                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
-            else:
-                self.reString = "[%s][%s]*" % \
-                                      (_escapeRegexRangeChars(self.initCharsOrig),
-                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
-            if self.asKeyword:
-                self.reString = r"\b"+self.reString+r"\b"
-            try:
-                self.re = re.compile( self.reString )
-            except Exception:
-                self.re = None
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.re:
-            result = self.re.match(instring,loc)
-            if not result:
-                raise ParseException(instring, loc, self.errmsg, self)
-
-            loc = result.end()
-            return loc, result.group()
-
-        if not(instring[ loc ] in self.initChars):
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        start = loc
-        loc += 1
-        instrlen = len(instring)
-        bodychars = self.bodyChars
-        maxloc = start + self.maxLen
-        maxloc = min( maxloc, instrlen )
-        while loc < maxloc and instring[loc] in bodychars:
-            loc += 1
-
-        throwException = False
-        if loc - start < self.minLen:
-            throwException = True
-        if self.maxSpecified and loc < instrlen and instring[loc] in bodychars:
-            throwException = True
-        if self.asKeyword:
-            if (start>0 and instring[start-1] in bodychars) or (loc4:
-                    return s[:4]+"..."
-                else:
-                    return s
-
-            if ( self.initCharsOrig != self.bodyCharsOrig ):
-                self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) )
-            else:
-                self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig)
-
-        return self.strRepr
-
-
-class Regex(Token):
-    r"""
-    Token for matching strings that match a given regular expression.
-    Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module.
-    If the given regex contains named groups (defined using C{(?P...)}), these will be preserved as 
-    named parse results.
-
-    Example::
-        realnum = Regex(r"[+-]?\d+\.\d*")
-        date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\d\d?)')
-        # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression
-        roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})")
-    """
-    compiledREtype = type(re.compile("[A-Z]"))
-    def __init__( self, pattern, flags=0):
-        """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags."""
-        super(Regex,self).__init__()
-
-        if isinstance(pattern, basestring):
-            if not pattern:
-                warnings.warn("null string passed to Regex; use Empty() instead",
-                        SyntaxWarning, stacklevel=2)
-
-            self.pattern = pattern
-            self.flags = flags
-
-            try:
-                self.re = re.compile(self.pattern, self.flags)
-                self.reString = self.pattern
-            except sre_constants.error:
-                warnings.warn("invalid pattern (%s) passed to Regex" % pattern,
-                    SyntaxWarning, stacklevel=2)
-                raise
-
-        elif isinstance(pattern, Regex.compiledREtype):
-            self.re = pattern
-            self.pattern = \
-            self.reString = str(pattern)
-            self.flags = flags
-            
-        else:
-            raise ValueError("Regex may only be constructed with a string or a compiled RE object")
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        result = self.re.match(instring,loc)
-        if not result:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        loc = result.end()
-        d = result.groupdict()
-        ret = ParseResults(result.group())
-        if d:
-            for k in d:
-                ret[k] = d[k]
-        return loc,ret
-
-    def __str__( self ):
-        try:
-            return super(Regex,self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-            self.strRepr = "Re:(%s)" % repr(self.pattern)
-
-        return self.strRepr
-
-
-class QuotedString(Token):
-    r"""
-    Token for matching strings that are delimited by quoting characters.
-    
-    Defined with the following parameters:
-        - quoteChar - string of one or more characters defining the quote delimiting string
-        - escChar - character to escape quotes, typically backslash (default=C{None})
-        - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{None})
-        - multiline - boolean indicating whether quotes can span multiple lines (default=C{False})
-        - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True})
-        - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar)
-        - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True})
-
-    Example::
-        qs = QuotedString('"')
-        print(qs.searchString('lsjdf "This is the quote" sldjf'))
-        complex_qs = QuotedString('{{', endQuoteChar='}}')
-        print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf'))
-        sql_qs = QuotedString('"', escQuote='""')
-        print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf'))
-    prints::
-        [['This is the quote']]
-        [['This is the "quote"']]
-        [['This is the quote with "embedded" quotes']]
-    """
-    def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True):
-        super(QuotedString,self).__init__()
-
-        # remove white space from quote chars - wont work anyway
-        quoteChar = quoteChar.strip()
-        if not quoteChar:
-            warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
-            raise SyntaxError()
-
-        if endQuoteChar is None:
-            endQuoteChar = quoteChar
-        else:
-            endQuoteChar = endQuoteChar.strip()
-            if not endQuoteChar:
-                warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
-                raise SyntaxError()
-
-        self.quoteChar = quoteChar
-        self.quoteCharLen = len(quoteChar)
-        self.firstQuoteChar = quoteChar[0]
-        self.endQuoteChar = endQuoteChar
-        self.endQuoteCharLen = len(endQuoteChar)
-        self.escChar = escChar
-        self.escQuote = escQuote
-        self.unquoteResults = unquoteResults
-        self.convertWhitespaceEscapes = convertWhitespaceEscapes
-
-        if multiline:
-            self.flags = re.MULTILINE | re.DOTALL
-            self.pattern = r'%s(?:[^%s%s]' % \
-                ( re.escape(self.quoteChar),
-                  _escapeRegexRangeChars(self.endQuoteChar[0]),
-                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
-        else:
-            self.flags = 0
-            self.pattern = r'%s(?:[^%s\n\r%s]' % \
-                ( re.escape(self.quoteChar),
-                  _escapeRegexRangeChars(self.endQuoteChar[0]),
-                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
-        if len(self.endQuoteChar) > 1:
-            self.pattern += (
-                '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]),
-                                               _escapeRegexRangeChars(self.endQuoteChar[i]))
-                                    for i in range(len(self.endQuoteChar)-1,0,-1)) + ')'
-                )
-        if escQuote:
-            self.pattern += (r'|(?:%s)' % re.escape(escQuote))
-        if escChar:
-            self.pattern += (r'|(?:%s.)' % re.escape(escChar))
-            self.escCharReplacePattern = re.escape(self.escChar)+"(.)"
-        self.pattern += (r')*%s' % re.escape(self.endQuoteChar))
-
-        try:
-            self.re = re.compile(self.pattern, self.flags)
-            self.reString = self.pattern
-        except sre_constants.error:
-            warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern,
-                SyntaxWarning, stacklevel=2)
-            raise
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None
-        if not result:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        loc = result.end()
-        ret = result.group()
-
-        if self.unquoteResults:
-
-            # strip off quotes
-            ret = ret[self.quoteCharLen:-self.endQuoteCharLen]
-
-            if isinstance(ret,basestring):
-                # replace escaped whitespace
-                if '\\' in ret and self.convertWhitespaceEscapes:
-                    ws_map = {
-                        r'\t' : '\t',
-                        r'\n' : '\n',
-                        r'\f' : '\f',
-                        r'\r' : '\r',
-                    }
-                    for wslit,wschar in ws_map.items():
-                        ret = ret.replace(wslit, wschar)
-
-                # replace escaped characters
-                if self.escChar:
-                    ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret)
-
-                # replace escaped quotes
-                if self.escQuote:
-                    ret = ret.replace(self.escQuote, self.endQuoteChar)
-
-        return loc, ret
-
-    def __str__( self ):
-        try:
-            return super(QuotedString,self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-            self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar)
-
-        return self.strRepr
-
-
-class CharsNotIn(Token):
-    """
-    Token for matching words composed of characters I{not} in a given set (will
-    include whitespace in matched characters if not listed in the provided exclusion set - see example).
-    Defined with string containing all disallowed characters, and an optional
-    minimum, maximum, and/or exact length.  The default value for C{min} is 1 (a
-    minimum value < 1 is not valid); the default values for C{max} and C{exact}
-    are 0, meaning no maximum or exact length restriction.
-
-    Example::
-        # define a comma-separated-value as anything that is not a ','
-        csv_value = CharsNotIn(',')
-        print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213"))
-    prints::
-        ['dkls', 'lsdkjf', 's12 34', '@!#', '213']
-    """
-    def __init__( self, notChars, min=1, max=0, exact=0 ):
-        super(CharsNotIn,self).__init__()
-        self.skipWhitespace = False
-        self.notChars = notChars
-
-        if min < 1:
-            raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted")
-
-        self.minLen = min
-
-        if max > 0:
-            self.maxLen = max
-        else:
-            self.maxLen = _MAX_INT
-
-        if exact > 0:
-            self.maxLen = exact
-            self.minLen = exact
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = ( self.minLen == 0 )
-        self.mayIndexError = False
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if instring[loc] in self.notChars:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        start = loc
-        loc += 1
-        notchars = self.notChars
-        maxlen = min( start+self.maxLen, len(instring) )
-        while loc < maxlen and \
-              (instring[loc] not in notchars):
-            loc += 1
-
-        if loc - start < self.minLen:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        return loc, instring[start:loc]
-
-    def __str__( self ):
-        try:
-            return super(CharsNotIn, self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-            if len(self.notChars) > 4:
-                self.strRepr = "!W:(%s...)" % self.notChars[:4]
-            else:
-                self.strRepr = "!W:(%s)" % self.notChars
-
-        return self.strRepr
-
-class White(Token):
-    """
-    Special matching class for matching whitespace.  Normally, whitespace is ignored
-    by pyparsing grammars.  This class is included when some whitespace structures
-    are significant.  Define with a string containing the whitespace characters to be
-    matched; default is C{" \\t\\r\\n"}.  Also takes optional C{min}, C{max}, and C{exact} arguments,
-    as defined for the C{L{Word}} class.
-    """
-    whiteStrs = {
-        " " : "",
-        "\t": "",
-        "\n": "",
-        "\r": "",
-        "\f": "",
-        }
-    def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0):
-        super(White,self).__init__()
-        self.matchWhite = ws
-        self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) )
-        #~ self.leaveWhitespace()
-        self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite))
-        self.mayReturnEmpty = True
-        self.errmsg = "Expected " + self.name
-
-        self.minLen = min
-
-        if max > 0:
-            self.maxLen = max
-        else:
-            self.maxLen = _MAX_INT
-
-        if exact > 0:
-            self.maxLen = exact
-            self.minLen = exact
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if not(instring[ loc ] in self.matchWhite):
-            raise ParseException(instring, loc, self.errmsg, self)
-        start = loc
-        loc += 1
-        maxloc = start + self.maxLen
-        maxloc = min( maxloc, len(instring) )
-        while loc < maxloc and instring[loc] in self.matchWhite:
-            loc += 1
-
-        if loc - start < self.minLen:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        return loc, instring[start:loc]
-
-
-class _PositionToken(Token):
-    def __init__( self ):
-        super(_PositionToken,self).__init__()
-        self.name=self.__class__.__name__
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-
-class GoToColumn(_PositionToken):
-    """
-    Token to advance to a specific column of input text; useful for tabular report scraping.
-    """
-    def __init__( self, colno ):
-        super(GoToColumn,self).__init__()
-        self.col = colno
-
-    def preParse( self, instring, loc ):
-        if col(loc,instring) != self.col:
-            instrlen = len(instring)
-            if self.ignoreExprs:
-                loc = self._skipIgnorables( instring, loc )
-            while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col :
-                loc += 1
-        return loc
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        thiscol = col( loc, instring )
-        if thiscol > self.col:
-            raise ParseException( instring, loc, "Text not in expected column", self )
-        newloc = loc + self.col - thiscol
-        ret = instring[ loc: newloc ]
-        return newloc, ret
-
-
-class LineStart(_PositionToken):
-    """
-    Matches if current position is at the beginning of a line within the parse string
-    
-    Example::
-    
-        test = '''\
-        AAA this line
-        AAA and this line
-          AAA but not this one
-        B AAA and definitely not this one
-        '''
-
-        for t in (LineStart() + 'AAA' + restOfLine).searchString(test):
-            print(t)
-    
-    Prints::
-        ['AAA', ' this line']
-        ['AAA', ' and this line']    
-
-    """
-    def __init__( self ):
-        super(LineStart,self).__init__()
-        self.errmsg = "Expected start of line"
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if col(loc, instring) == 1:
-            return loc, []
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class LineEnd(_PositionToken):
-    """
-    Matches if current position is at the end of a line within the parse string
-    """
-    def __init__( self ):
-        super(LineEnd,self).__init__()
-        self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") )
-        self.errmsg = "Expected end of line"
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if loc len(instring):
-            return loc, []
-        else:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-class WordStart(_PositionToken):
-    """
-    Matches if the current position is at the beginning of a Word, and
-    is not preceded by any character in a given set of C{wordChars}
-    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
-    use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of
-    the string being parsed, or at the beginning of a line.
-    """
-    def __init__(self, wordChars = printables):
-        super(WordStart,self).__init__()
-        self.wordChars = set(wordChars)
-        self.errmsg = "Not at the start of a word"
-
-    def parseImpl(self, instring, loc, doActions=True ):
-        if loc != 0:
-            if (instring[loc-1] in self.wordChars or
-                instring[loc] not in self.wordChars):
-                raise ParseException(instring, loc, self.errmsg, self)
-        return loc, []
-
-class WordEnd(_PositionToken):
-    """
-    Matches if the current position is at the end of a Word, and
-    is not followed by any character in a given set of C{wordChars}
-    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
-    use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of
-    the string being parsed, or at the end of a line.
-    """
-    def __init__(self, wordChars = printables):
-        super(WordEnd,self).__init__()
-        self.wordChars = set(wordChars)
-        self.skipWhitespace = False
-        self.errmsg = "Not at the end of a word"
-
-    def parseImpl(self, instring, loc, doActions=True ):
-        instrlen = len(instring)
-        if instrlen>0 and loc maxExcLoc:
-                    maxException = err
-                    maxExcLoc = err.loc
-            except IndexError:
-                if len(instring) > maxExcLoc:
-                    maxException = ParseException(instring,len(instring),e.errmsg,self)
-                    maxExcLoc = len(instring)
-            else:
-                # save match among all matches, to retry longest to shortest
-                matches.append((loc2, e))
-
-        if matches:
-            matches.sort(key=lambda x: -x[0])
-            for _,e in matches:
-                try:
-                    return e._parse( instring, loc, doActions )
-                except ParseException as err:
-                    err.__traceback__ = None
-                    if err.loc > maxExcLoc:
-                        maxException = err
-                        maxExcLoc = err.loc
-
-        if maxException is not None:
-            maxException.msg = self.errmsg
-            raise maxException
-        else:
-            raise ParseException(instring, loc, "no defined alternatives to match", self)
-
-
-    def __ixor__(self, other ):
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        return self.append( other ) #Or( [ self, other ] )
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}"
-
-        return self.strRepr
-
-    def checkRecursion( self, parseElementList ):
-        subRecCheckList = parseElementList[:] + [ self ]
-        for e in self.exprs:
-            e.checkRecursion( subRecCheckList )
-
-
-class MatchFirst(ParseExpression):
-    """
-    Requires that at least one C{ParseExpression} is found.
-    If two expressions match, the first one listed is the one that will match.
-    May be constructed using the C{'|'} operator.
-
-    Example::
-        # construct MatchFirst using '|' operator
-        
-        # watch the order of expressions to match
-        number = Word(nums) | Combine(Word(nums) + '.' + Word(nums))
-        print(number.searchString("123 3.1416 789")) #  Fail! -> [['123'], ['3'], ['1416'], ['789']]
-
-        # put more selective expression first
-        number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums)
-        print(number.searchString("123 3.1416 789")) #  Better -> [['123'], ['3.1416'], ['789']]
-    """
-    def __init__( self, exprs, savelist = False ):
-        super(MatchFirst,self).__init__(exprs, savelist)
-        if self.exprs:
-            self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
-        else:
-            self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        maxExcLoc = -1
-        maxException = None
-        for e in self.exprs:
-            try:
-                ret = e._parse( instring, loc, doActions )
-                return ret
-            except ParseException as err:
-                if err.loc > maxExcLoc:
-                    maxException = err
-                    maxExcLoc = err.loc
-            except IndexError:
-                if len(instring) > maxExcLoc:
-                    maxException = ParseException(instring,len(instring),e.errmsg,self)
-                    maxExcLoc = len(instring)
-
-        # only got here if no expression matched, raise exception for match that made it the furthest
-        else:
-            if maxException is not None:
-                maxException.msg = self.errmsg
-                raise maxException
-            else:
-                raise ParseException(instring, loc, "no defined alternatives to match", self)
-
-    def __ior__(self, other ):
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        return self.append( other ) #MatchFirst( [ self, other ] )
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}"
-
-        return self.strRepr
-
-    def checkRecursion( self, parseElementList ):
-        subRecCheckList = parseElementList[:] + [ self ]
-        for e in self.exprs:
-            e.checkRecursion( subRecCheckList )
-
-
-class Each(ParseExpression):
-    """
-    Requires all given C{ParseExpression}s to be found, but in any order.
-    Expressions may be separated by whitespace.
-    May be constructed using the C{'&'} operator.
-
-    Example::
-        color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN")
-        shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON")
-        integer = Word(nums)
-        shape_attr = "shape:" + shape_type("shape")
-        posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn")
-        color_attr = "color:" + color("color")
-        size_attr = "size:" + integer("size")
-
-        # use Each (using operator '&') to accept attributes in any order 
-        # (shape and posn are required, color and size are optional)
-        shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr)
-
-        shape_spec.runTests('''
-            shape: SQUARE color: BLACK posn: 100, 120
-            shape: CIRCLE size: 50 color: BLUE posn: 50,80
-            color:GREEN size:20 shape:TRIANGLE posn:20,40
-            '''
-            )
-    prints::
-        shape: SQUARE color: BLACK posn: 100, 120
-        ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']]
-        - color: BLACK
-        - posn: ['100', ',', '120']
-          - x: 100
-          - y: 120
-        - shape: SQUARE
-
-
-        shape: CIRCLE size: 50 color: BLUE posn: 50,80
-        ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']]
-        - color: BLUE
-        - posn: ['50', ',', '80']
-          - x: 50
-          - y: 80
-        - shape: CIRCLE
-        - size: 50
-
-
-        color: GREEN size: 20 shape: TRIANGLE posn: 20,40
-        ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']]
-        - color: GREEN
-        - posn: ['20', ',', '40']
-          - x: 20
-          - y: 40
-        - shape: TRIANGLE
-        - size: 20
-    """
-    def __init__( self, exprs, savelist = True ):
-        super(Each,self).__init__(exprs, savelist)
-        self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
-        self.skipWhitespace = True
-        self.initExprGroups = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.initExprGroups:
-            self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional))
-            opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ]
-            opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)]
-            self.optionals = opt1 + opt2
-            self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ]
-            self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ]
-            self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ]
-            self.required += self.multirequired
-            self.initExprGroups = False
-        tmpLoc = loc
-        tmpReqd = self.required[:]
-        tmpOpt  = self.optionals[:]
-        matchOrder = []
-
-        keepMatching = True
-        while keepMatching:
-            tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired
-            failed = []
-            for e in tmpExprs:
-                try:
-                    tmpLoc = e.tryParse( instring, tmpLoc )
-                except ParseException:
-                    failed.append(e)
-                else:
-                    matchOrder.append(self.opt1map.get(id(e),e))
-                    if e in tmpReqd:
-                        tmpReqd.remove(e)
-                    elif e in tmpOpt:
-                        tmpOpt.remove(e)
-            if len(failed) == len(tmpExprs):
-                keepMatching = False
-
-        if tmpReqd:
-            missing = ", ".join(_ustr(e) for e in tmpReqd)
-            raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing )
-
-        # add any unmatched Optionals, in case they have default values defined
-        matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt]
-
-        resultlist = []
-        for e in matchOrder:
-            loc,results = e._parse(instring,loc,doActions)
-            resultlist.append(results)
-
-        finalResults = sum(resultlist, ParseResults([]))
-        return loc, finalResults
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}"
-
-        return self.strRepr
-
-    def checkRecursion( self, parseElementList ):
-        subRecCheckList = parseElementList[:] + [ self ]
-        for e in self.exprs:
-            e.checkRecursion( subRecCheckList )
-
-
-class ParseElementEnhance(ParserElement):
-    """
-    Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens.
-    """
-    def __init__( self, expr, savelist=False ):
-        super(ParseElementEnhance,self).__init__(savelist)
-        if isinstance( expr, basestring ):
-            if issubclass(ParserElement._literalStringClass, Token):
-                expr = ParserElement._literalStringClass(expr)
-            else:
-                expr = ParserElement._literalStringClass(Literal(expr))
-        self.expr = expr
-        self.strRepr = None
-        if expr is not None:
-            self.mayIndexError = expr.mayIndexError
-            self.mayReturnEmpty = expr.mayReturnEmpty
-            self.setWhitespaceChars( expr.whiteChars )
-            self.skipWhitespace = expr.skipWhitespace
-            self.saveAsList = expr.saveAsList
-            self.callPreparse = expr.callPreparse
-            self.ignoreExprs.extend(expr.ignoreExprs)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.expr is not None:
-            return self.expr._parse( instring, loc, doActions, callPreParse=False )
-        else:
-            raise ParseException("",loc,self.errmsg,self)
-
-    def leaveWhitespace( self ):
-        self.skipWhitespace = False
-        self.expr = self.expr.copy()
-        if self.expr is not None:
-            self.expr.leaveWhitespace()
-        return self
-
-    def ignore( self, other ):
-        if isinstance( other, Suppress ):
-            if other not in self.ignoreExprs:
-                super( ParseElementEnhance, self).ignore( other )
-                if self.expr is not None:
-                    self.expr.ignore( self.ignoreExprs[-1] )
-        else:
-            super( ParseElementEnhance, self).ignore( other )
-            if self.expr is not None:
-                self.expr.ignore( self.ignoreExprs[-1] )
-        return self
-
-    def streamline( self ):
-        super(ParseElementEnhance,self).streamline()
-        if self.expr is not None:
-            self.expr.streamline()
-        return self
-
-    def checkRecursion( self, parseElementList ):
-        if self in parseElementList:
-            raise RecursiveGrammarException( parseElementList+[self] )
-        subRecCheckList = parseElementList[:] + [ self ]
-        if self.expr is not None:
-            self.expr.checkRecursion( subRecCheckList )
-
-    def validate( self, validateTrace=[] ):
-        tmp = validateTrace[:]+[self]
-        if self.expr is not None:
-            self.expr.validate(tmp)
-        self.checkRecursion( [] )
-
-    def __str__( self ):
-        try:
-            return super(ParseElementEnhance,self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None and self.expr is not None:
-            self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) )
-        return self.strRepr
-
-
-class FollowedBy(ParseElementEnhance):
-    """
-    Lookahead matching of the given parse expression.  C{FollowedBy}
-    does I{not} advance the parsing position within the input string, it only
-    verifies that the specified parse expression matches at the current
-    position.  C{FollowedBy} always returns a null token list.
-
-    Example::
-        # use FollowedBy to match a label only if it is followed by a ':'
-        data_word = Word(alphas)
-        label = data_word + FollowedBy(':')
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        
-        OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint()
-    prints::
-        [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']]
-    """
-    def __init__( self, expr ):
-        super(FollowedBy,self).__init__(expr)
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        self.expr.tryParse( instring, loc )
-        return loc, []
-
-
-class NotAny(ParseElementEnhance):
-    """
-    Lookahead to disallow matching with the given parse expression.  C{NotAny}
-    does I{not} advance the parsing position within the input string, it only
-    verifies that the specified parse expression does I{not} match at the current
-    position.  Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny}
-    always returns a null token list.  May be constructed using the '~' operator.
-
-    Example::
-        
-    """
-    def __init__( self, expr ):
-        super(NotAny,self).__init__(expr)
-        #~ self.leaveWhitespace()
-        self.skipWhitespace = False  # do NOT use self.leaveWhitespace(), don't want to propagate to exprs
-        self.mayReturnEmpty = True
-        self.errmsg = "Found unwanted token, "+_ustr(self.expr)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.expr.canParseNext(instring, loc):
-            raise ParseException(instring, loc, self.errmsg, self)
-        return loc, []
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "~{" + _ustr(self.expr) + "}"
-
-        return self.strRepr
-
-class _MultipleMatch(ParseElementEnhance):
-    def __init__( self, expr, stopOn=None):
-        super(_MultipleMatch, self).__init__(expr)
-        self.saveAsList = True
-        ender = stopOn
-        if isinstance(ender, basestring):
-            ender = ParserElement._literalStringClass(ender)
-        self.not_ender = ~ender if ender is not None else None
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        self_expr_parse = self.expr._parse
-        self_skip_ignorables = self._skipIgnorables
-        check_ender = self.not_ender is not None
-        if check_ender:
-            try_not_ender = self.not_ender.tryParse
-        
-        # must be at least one (but first see if we are the stopOn sentinel;
-        # if so, fail)
-        if check_ender:
-            try_not_ender(instring, loc)
-        loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False )
-        try:
-            hasIgnoreExprs = (not not self.ignoreExprs)
-            while 1:
-                if check_ender:
-                    try_not_ender(instring, loc)
-                if hasIgnoreExprs:
-                    preloc = self_skip_ignorables( instring, loc )
-                else:
-                    preloc = loc
-                loc, tmptokens = self_expr_parse( instring, preloc, doActions )
-                if tmptokens or tmptokens.haskeys():
-                    tokens += tmptokens
-        except (ParseException,IndexError):
-            pass
-
-        return loc, tokens
-        
-class OneOrMore(_MultipleMatch):
-    """
-    Repetition of one or more of the given expression.
-    
-    Parameters:
-     - expr - expression that must match one or more times
-     - stopOn - (default=C{None}) - expression for a terminating sentinel
-          (only required if the sentinel would ordinarily match the repetition 
-          expression)          
-
-    Example::
-        data_word = Word(alphas)
-        label = data_word + FollowedBy(':')
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
-
-        text = "shape: SQUARE posn: upper left color: BLACK"
-        OneOrMore(attr_expr).parseString(text).pprint()  # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']]
-
-        # use stopOn attribute for OneOrMore to avoid reading label string as part of the data
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']]
-        
-        # could also be written as
-        (attr_expr * (1,)).parseString(text).pprint()
-    """
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + _ustr(self.expr) + "}..."
-
-        return self.strRepr
-
-class ZeroOrMore(_MultipleMatch):
-    """
-    Optional repetition of zero or more of the given expression.
-    
-    Parameters:
-     - expr - expression that must match zero or more times
-     - stopOn - (default=C{None}) - expression for a terminating sentinel
-          (only required if the sentinel would ordinarily match the repetition 
-          expression)          
-
-    Example: similar to L{OneOrMore}
-    """
-    def __init__( self, expr, stopOn=None):
-        super(ZeroOrMore,self).__init__(expr, stopOn=stopOn)
-        self.mayReturnEmpty = True
-        
-    def parseImpl( self, instring, loc, doActions=True ):
-        try:
-            return super(ZeroOrMore, self).parseImpl(instring, loc, doActions)
-        except (ParseException,IndexError):
-            return loc, []
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "[" + _ustr(self.expr) + "]..."
-
-        return self.strRepr
-
-class _NullToken(object):
-    def __bool__(self):
-        return False
-    __nonzero__ = __bool__
-    def __str__(self):
-        return ""
-
-_optionalNotMatched = _NullToken()
-class Optional(ParseElementEnhance):
-    """
-    Optional matching of the given expression.
-
-    Parameters:
-     - expr - expression that must match zero or more times
-     - default (optional) - value to be returned if the optional expression is not found.
-
-    Example::
-        # US postal code can be a 5-digit zip, plus optional 4-digit qualifier
-        zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4)))
-        zip.runTests('''
-            # traditional ZIP code
-            12345
-            
-            # ZIP+4 form
-            12101-0001
-            
-            # invalid ZIP
-            98765-
-            ''')
-    prints::
-        # traditional ZIP code
-        12345
-        ['12345']
-
-        # ZIP+4 form
-        12101-0001
-        ['12101-0001']
-
-        # invalid ZIP
-        98765-
-             ^
-        FAIL: Expected end of text (at char 5), (line:1, col:6)
-    """
-    def __init__( self, expr, default=_optionalNotMatched ):
-        super(Optional,self).__init__( expr, savelist=False )
-        self.saveAsList = self.expr.saveAsList
-        self.defaultValue = default
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        try:
-            loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False )
-        except (ParseException,IndexError):
-            if self.defaultValue is not _optionalNotMatched:
-                if self.expr.resultsName:
-                    tokens = ParseResults([ self.defaultValue ])
-                    tokens[self.expr.resultsName] = self.defaultValue
-                else:
-                    tokens = [ self.defaultValue ]
-            else:
-                tokens = []
-        return loc, tokens
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "[" + _ustr(self.expr) + "]"
-
-        return self.strRepr
-
-class SkipTo(ParseElementEnhance):
-    """
-    Token for skipping over all undefined text until the matched expression is found.
-
-    Parameters:
-     - expr - target expression marking the end of the data to be skipped
-     - include - (default=C{False}) if True, the target expression is also parsed 
-          (the skipped text and target expression are returned as a 2-element list).
-     - ignore - (default=C{None}) used to define grammars (typically quoted strings and 
-          comments) that might contain false matches to the target expression
-     - failOn - (default=C{None}) define expressions that are not allowed to be 
-          included in the skipped test; if found before the target expression is found, 
-          the SkipTo is not a match
-
-    Example::
-        report = '''
-            Outstanding Issues Report - 1 Jan 2000
-
-               # | Severity | Description                               |  Days Open
-            -----+----------+-------------------------------------------+-----------
-             101 | Critical | Intermittent system crash                 |          6
-              94 | Cosmetic | Spelling error on Login ('log|n')         |         14
-              79 | Minor    | System slow when running too many reports |         47
-            '''
-        integer = Word(nums)
-        SEP = Suppress('|')
-        # use SkipTo to simply match everything up until the next SEP
-        # - ignore quoted strings, so that a '|' character inside a quoted string does not match
-        # - parse action will call token.strip() for each matched token, i.e., the description body
-        string_data = SkipTo(SEP, ignore=quotedString)
-        string_data.setParseAction(tokenMap(str.strip))
-        ticket_expr = (integer("issue_num") + SEP 
-                      + string_data("sev") + SEP 
-                      + string_data("desc") + SEP 
-                      + integer("days_open"))
-        
-        for tkt in ticket_expr.searchString(report):
-            print tkt.dump()
-    prints::
-        ['101', 'Critical', 'Intermittent system crash', '6']
-        - days_open: 6
-        - desc: Intermittent system crash
-        - issue_num: 101
-        - sev: Critical
-        ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14']
-        - days_open: 14
-        - desc: Spelling error on Login ('log|n')
-        - issue_num: 94
-        - sev: Cosmetic
-        ['79', 'Minor', 'System slow when running too many reports', '47']
-        - days_open: 47
-        - desc: System slow when running too many reports
-        - issue_num: 79
-        - sev: Minor
-    """
-    def __init__( self, other, include=False, ignore=None, failOn=None ):
-        super( SkipTo, self ).__init__( other )
-        self.ignoreExpr = ignore
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-        self.includeMatch = include
-        self.asList = False
-        if isinstance(failOn, basestring):
-            self.failOn = ParserElement._literalStringClass(failOn)
-        else:
-            self.failOn = failOn
-        self.errmsg = "No match found for "+_ustr(self.expr)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        startloc = loc
-        instrlen = len(instring)
-        expr = self.expr
-        expr_parse = self.expr._parse
-        self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None
-        self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None
-        
-        tmploc = loc
-        while tmploc <= instrlen:
-            if self_failOn_canParseNext is not None:
-                # break if failOn expression matches
-                if self_failOn_canParseNext(instring, tmploc):
-                    break
-                    
-            if self_ignoreExpr_tryParse is not None:
-                # advance past ignore expressions
-                while 1:
-                    try:
-                        tmploc = self_ignoreExpr_tryParse(instring, tmploc)
-                    except ParseBaseException:
-                        break
-            
-            try:
-                expr_parse(instring, tmploc, doActions=False, callPreParse=False)
-            except (ParseException, IndexError):
-                # no match, advance loc in string
-                tmploc += 1
-            else:
-                # matched skipto expr, done
-                break
-
-        else:
-            # ran off the end of the input string without matching skipto expr, fail
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        # build up return values
-        loc = tmploc
-        skiptext = instring[startloc:loc]
-        skipresult = ParseResults(skiptext)
-        
-        if self.includeMatch:
-            loc, mat = expr_parse(instring,loc,doActions,callPreParse=False)
-            skipresult += mat
-
-        return loc, skipresult
-
-class Forward(ParseElementEnhance):
-    """
-    Forward declaration of an expression to be defined later -
-    used for recursive grammars, such as algebraic infix notation.
-    When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator.
-
-    Note: take care when assigning to C{Forward} not to overlook precedence of operators.
-    Specifically, '|' has a lower precedence than '<<', so that::
-        fwdExpr << a | b | c
-    will actually be evaluated as::
-        (fwdExpr << a) | b | c
-    thereby leaving b and c out as parseable alternatives.  It is recommended that you
-    explicitly group the values inserted into the C{Forward}::
-        fwdExpr << (a | b | c)
-    Converting to use the '<<=' operator instead will avoid this problem.
-
-    See L{ParseResults.pprint} for an example of a recursive parser created using
-    C{Forward}.
-    """
-    def __init__( self, other=None ):
-        super(Forward,self).__init__( other, savelist=False )
-
-    def __lshift__( self, other ):
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass(other)
-        self.expr = other
-        self.strRepr = None
-        self.mayIndexError = self.expr.mayIndexError
-        self.mayReturnEmpty = self.expr.mayReturnEmpty
-        self.setWhitespaceChars( self.expr.whiteChars )
-        self.skipWhitespace = self.expr.skipWhitespace
-        self.saveAsList = self.expr.saveAsList
-        self.ignoreExprs.extend(self.expr.ignoreExprs)
-        return self
-        
-    def __ilshift__(self, other):
-        return self << other
-    
-    def leaveWhitespace( self ):
-        self.skipWhitespace = False
-        return self
-
-    def streamline( self ):
-        if not self.streamlined:
-            self.streamlined = True
-            if self.expr is not None:
-                self.expr.streamline()
-        return self
-
-    def validate( self, validateTrace=[] ):
-        if self not in validateTrace:
-            tmp = validateTrace[:]+[self]
-            if self.expr is not None:
-                self.expr.validate(tmp)
-        self.checkRecursion([])
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-        return self.__class__.__name__ + ": ..."
-
-        # stubbed out for now - creates awful memory and perf issues
-        self._revertClass = self.__class__
-        self.__class__ = _ForwardNoRecurse
-        try:
-            if self.expr is not None:
-                retString = _ustr(self.expr)
-            else:
-                retString = "None"
-        finally:
-            self.__class__ = self._revertClass
-        return self.__class__.__name__ + ": " + retString
-
-    def copy(self):
-        if self.expr is not None:
-            return super(Forward,self).copy()
-        else:
-            ret = Forward()
-            ret <<= self
-            return ret
-
-class _ForwardNoRecurse(Forward):
-    def __str__( self ):
-        return "..."
-
-class TokenConverter(ParseElementEnhance):
-    """
-    Abstract subclass of C{ParseExpression}, for converting parsed results.
-    """
-    def __init__( self, expr, savelist=False ):
-        super(TokenConverter,self).__init__( expr )#, savelist )
-        self.saveAsList = False
-
-class Combine(TokenConverter):
-    """
-    Converter to concatenate all matching tokens to a single string.
-    By default, the matching patterns must also be contiguous in the input string;
-    this can be disabled by specifying C{'adjacent=False'} in the constructor.
-
-    Example::
-        real = Word(nums) + '.' + Word(nums)
-        print(real.parseString('3.1416')) # -> ['3', '.', '1416']
-        # will also erroneously match the following
-        print(real.parseString('3. 1416')) # -> ['3', '.', '1416']
-
-        real = Combine(Word(nums) + '.' + Word(nums))
-        print(real.parseString('3.1416')) # -> ['3.1416']
-        # no match when there are internal spaces
-        print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...)
-    """
-    def __init__( self, expr, joinString="", adjacent=True ):
-        super(Combine,self).__init__( expr )
-        # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself
-        if adjacent:
-            self.leaveWhitespace()
-        self.adjacent = adjacent
-        self.skipWhitespace = True
-        self.joinString = joinString
-        self.callPreparse = True
-
-    def ignore( self, other ):
-        if self.adjacent:
-            ParserElement.ignore(self, other)
-        else:
-            super( Combine, self).ignore( other )
-        return self
-
-    def postParse( self, instring, loc, tokenlist ):
-        retToks = tokenlist.copy()
-        del retToks[:]
-        retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults)
-
-        if self.resultsName and retToks.haskeys():
-            return [ retToks ]
-        else:
-            return retToks
-
-class Group(TokenConverter):
-    """
-    Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions.
-
-    Example::
-        ident = Word(alphas)
-        num = Word(nums)
-        term = ident | num
-        func = ident + Optional(delimitedList(term))
-        print(func.parseString("fn a,b,100"))  # -> ['fn', 'a', 'b', '100']
-
-        func = ident + Group(Optional(delimitedList(term)))
-        print(func.parseString("fn a,b,100"))  # -> ['fn', ['a', 'b', '100']]
-    """
-    def __init__( self, expr ):
-        super(Group,self).__init__( expr )
-        self.saveAsList = True
-
-    def postParse( self, instring, loc, tokenlist ):
-        return [ tokenlist ]
-
-class Dict(TokenConverter):
-    """
-    Converter to return a repetitive expression as a list, but also as a dictionary.
-    Each element can also be referenced using the first token in the expression as its key.
-    Useful for tabular report scraping when the first column can be used as a item key.
-
-    Example::
-        data_word = Word(alphas)
-        label = data_word + FollowedBy(':')
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
-
-        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
-        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        
-        # print attributes as plain groups
-        print(OneOrMore(attr_expr).parseString(text).dump())
-        
-        # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names
-        result = Dict(OneOrMore(Group(attr_expr))).parseString(text)
-        print(result.dump())
-        
-        # access named fields as dict entries, or output as dict
-        print(result['shape'])        
-        print(result.asDict())
-    prints::
-        ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap']
-
-        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
-        - color: light blue
-        - posn: upper left
-        - shape: SQUARE
-        - texture: burlap
-        SQUARE
-        {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'}
-    See more examples at L{ParseResults} of accessing fields by results name.
-    """
-    def __init__( self, expr ):
-        super(Dict,self).__init__( expr )
-        self.saveAsList = True
-
-    def postParse( self, instring, loc, tokenlist ):
-        for i,tok in enumerate(tokenlist):
-            if len(tok) == 0:
-                continue
-            ikey = tok[0]
-            if isinstance(ikey,int):
-                ikey = _ustr(tok[0]).strip()
-            if len(tok)==1:
-                tokenlist[ikey] = _ParseResultsWithOffset("",i)
-            elif len(tok)==2 and not isinstance(tok[1],ParseResults):
-                tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i)
-            else:
-                dictvalue = tok.copy() #ParseResults(i)
-                del dictvalue[0]
-                if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()):
-                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i)
-                else:
-                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i)
-
-        if self.resultsName:
-            return [ tokenlist ]
-        else:
-            return tokenlist
-
-
-class Suppress(TokenConverter):
-    """
-    Converter for ignoring the results of a parsed expression.
-
-    Example::
-        source = "a, b, c,d"
-        wd = Word(alphas)
-        wd_list1 = wd + ZeroOrMore(',' + wd)
-        print(wd_list1.parseString(source))
-
-        # often, delimiters that are useful during parsing are just in the
-        # way afterward - use Suppress to keep them out of the parsed output
-        wd_list2 = wd + ZeroOrMore(Suppress(',') + wd)
-        print(wd_list2.parseString(source))
-    prints::
-        ['a', ',', 'b', ',', 'c', ',', 'd']
-        ['a', 'b', 'c', 'd']
-    (See also L{delimitedList}.)
-    """
-    def postParse( self, instring, loc, tokenlist ):
-        return []
-
-    def suppress( self ):
-        return self
-
-
-class OnlyOnce(object):
-    """
-    Wrapper for parse actions, to ensure they are only called once.
-    """
-    def __init__(self, methodCall):
-        self.callable = _trim_arity(methodCall)
-        self.called = False
-    def __call__(self,s,l,t):
-        if not self.called:
-            results = self.callable(s,l,t)
-            self.called = True
-            return results
-        raise ParseException(s,l,"")
-    def reset(self):
-        self.called = False
-
-def traceParseAction(f):
-    """
-    Decorator for debugging parse actions. 
-    
-    When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".}
-    When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised.
-
-    Example::
-        wd = Word(alphas)
-
-        @traceParseAction
-        def remove_duplicate_chars(tokens):
-            return ''.join(sorted(set(''.join(tokens))))
-
-        wds = OneOrMore(wd).setParseAction(remove_duplicate_chars)
-        print(wds.parseString("slkdjs sld sldd sdlf sdljf"))
-    prints::
-        >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {}))
-        <3:
-            thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc
-        sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\n" % (thisFunc,line(l,s),l,t) )
-        try:
-            ret = f(*paArgs)
-        except Exception as exc:
-            sys.stderr.write( "< ['aa', 'bb', 'cc']
-        delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE']
-    """
-    dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..."
-    if combine:
-        return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName)
-    else:
-        return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName)
-
-def countedArray( expr, intExpr=None ):
-    """
-    Helper to define a counted list of expressions.
-    This helper defines a pattern of the form::
-        integer expr expr expr...
-    where the leading integer tells how many expr expressions follow.
-    The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed.
-    
-    If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value.
-
-    Example::
-        countedArray(Word(alphas)).parseString('2 ab cd ef')  # -> ['ab', 'cd']
-
-        # in this parser, the leading integer value is given in binary,
-        # '10' indicating that 2 values are in the array
-        binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2))
-        countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef')  # -> ['ab', 'cd']
-    """
-    arrayExpr = Forward()
-    def countFieldParseAction(s,l,t):
-        n = t[0]
-        arrayExpr << (n and Group(And([expr]*n)) or Group(empty))
-        return []
-    if intExpr is None:
-        intExpr = Word(nums).setParseAction(lambda t:int(t[0]))
-    else:
-        intExpr = intExpr.copy()
-    intExpr.setName("arrayLen")
-    intExpr.addParseAction(countFieldParseAction, callDuringTry=True)
-    return ( intExpr + arrayExpr ).setName('(len) ' + _ustr(expr) + '...')
-
-def _flatten(L):
-    ret = []
-    for i in L:
-        if isinstance(i,list):
-            ret.extend(_flatten(i))
-        else:
-            ret.append(i)
-    return ret
-
-def matchPreviousLiteral(expr):
-    """
-    Helper to define an expression that is indirectly defined from
-    the tokens matched in a previous expression, that is, it looks
-    for a 'repeat' of a previous expression.  For example::
-        first = Word(nums)
-        second = matchPreviousLiteral(first)
-        matchExpr = first + ":" + second
-    will match C{"1:1"}, but not C{"1:2"}.  Because this matches a
-    previous literal, will also match the leading C{"1:1"} in C{"1:10"}.
-    If this is not desired, use C{matchPreviousExpr}.
-    Do I{not} use with packrat parsing enabled.
-    """
-    rep = Forward()
-    def copyTokenToRepeater(s,l,t):
-        if t:
-            if len(t) == 1:
-                rep << t[0]
-            else:
-                # flatten t tokens
-                tflat = _flatten(t.asList())
-                rep << And(Literal(tt) for tt in tflat)
-        else:
-            rep << Empty()
-    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
-    rep.setName('(prev) ' + _ustr(expr))
-    return rep
-
-def matchPreviousExpr(expr):
-    """
-    Helper to define an expression that is indirectly defined from
-    the tokens matched in a previous expression, that is, it looks
-    for a 'repeat' of a previous expression.  For example::
-        first = Word(nums)
-        second = matchPreviousExpr(first)
-        matchExpr = first + ":" + second
-    will match C{"1:1"}, but not C{"1:2"}.  Because this matches by
-    expressions, will I{not} match the leading C{"1:1"} in C{"1:10"};
-    the expressions are evaluated first, and then compared, so
-    C{"1"} is compared with C{"10"}.
-    Do I{not} use with packrat parsing enabled.
-    """
-    rep = Forward()
-    e2 = expr.copy()
-    rep <<= e2
-    def copyTokenToRepeater(s,l,t):
-        matchTokens = _flatten(t.asList())
-        def mustMatchTheseTokens(s,l,t):
-            theseTokens = _flatten(t.asList())
-            if  theseTokens != matchTokens:
-                raise ParseException("",0,"")
-        rep.setParseAction( mustMatchTheseTokens, callDuringTry=True )
-    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
-    rep.setName('(prev) ' + _ustr(expr))
-    return rep
-
-def _escapeRegexRangeChars(s):
-    #~  escape these chars: ^-]
-    for c in r"\^-]":
-        s = s.replace(c,_bslash+c)
-    s = s.replace("\n",r"\n")
-    s = s.replace("\t",r"\t")
-    return _ustr(s)
-
-def oneOf( strs, caseless=False, useRegex=True ):
-    """
-    Helper to quickly define a set of alternative Literals, and makes sure to do
-    longest-first testing when there is a conflict, regardless of the input order,
-    but returns a C{L{MatchFirst}} for best performance.
-
-    Parameters:
-     - strs - a string of space-delimited literals, or a collection of string literals
-     - caseless - (default=C{False}) - treat all literals as caseless
-     - useRegex - (default=C{True}) - as an optimization, will generate a Regex
-          object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or
-          if creating a C{Regex} raises an exception)
-
-    Example::
-        comp_oper = oneOf("< = > <= >= !=")
-        var = Word(alphas)
-        number = Word(nums)
-        term = var | number
-        comparison_expr = term + comp_oper + term
-        print(comparison_expr.searchString("B = 12  AA=23 B<=AA AA>12"))
-    prints::
-        [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']]
-    """
-    if caseless:
-        isequal = ( lambda a,b: a.upper() == b.upper() )
-        masks = ( lambda a,b: b.upper().startswith(a.upper()) )
-        parseElementClass = CaselessLiteral
-    else:
-        isequal = ( lambda a,b: a == b )
-        masks = ( lambda a,b: b.startswith(a) )
-        parseElementClass = Literal
-
-    symbols = []
-    if isinstance(strs,basestring):
-        symbols = strs.split()
-    elif isinstance(strs, Iterable):
-        symbols = list(strs)
-    else:
-        warnings.warn("Invalid argument to oneOf, expected string or iterable",
-                SyntaxWarning, stacklevel=2)
-    if not symbols:
-        return NoMatch()
-
-    i = 0
-    while i < len(symbols)-1:
-        cur = symbols[i]
-        for j,other in enumerate(symbols[i+1:]):
-            if ( isequal(other, cur) ):
-                del symbols[i+j+1]
-                break
-            elif ( masks(cur, other) ):
-                del symbols[i+j+1]
-                symbols.insert(i,other)
-                cur = other
-                break
-        else:
-            i += 1
-
-    if not caseless and useRegex:
-        #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] ))
-        try:
-            if len(symbols)==len("".join(symbols)):
-                return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols))
-            else:
-                return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols))
-        except Exception:
-            warnings.warn("Exception creating Regex for oneOf, building MatchFirst",
-                    SyntaxWarning, stacklevel=2)
-
-
-    # last resort, just use MatchFirst
-    return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols))
-
-def dictOf( key, value ):
-    """
-    Helper to easily and clearly define a dictionary by specifying the respective patterns
-    for the key and value.  Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens
-    in the proper order.  The key pattern can include delimiting markers or punctuation,
-    as long as they are suppressed, thereby leaving the significant key text.  The value
-    pattern can include named results, so that the C{Dict} results can include named token
-    fields.
-
-    Example::
-        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
-        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        print(OneOrMore(attr_expr).parseString(text).dump())
-        
-        attr_label = label
-        attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)
-
-        # similar to Dict, but simpler call format
-        result = dictOf(attr_label, attr_value).parseString(text)
-        print(result.dump())
-        print(result['shape'])
-        print(result.shape)  # object attribute access works too
-        print(result.asDict())
-    prints::
-        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
-        - color: light blue
-        - posn: upper left
-        - shape: SQUARE
-        - texture: burlap
-        SQUARE
-        SQUARE
-        {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'}
-    """
-    return Dict( ZeroOrMore( Group ( key + value ) ) )
-
-def originalTextFor(expr, asString=True):
-    """
-    Helper to return the original, untokenized text for a given expression.  Useful to
-    restore the parsed fields of an HTML start tag into the raw tag text itself, or to
-    revert separate tokens with intervening whitespace back to the original matching
-    input text. By default, returns astring containing the original parsed text.  
-       
-    If the optional C{asString} argument is passed as C{False}, then the return value is a 
-    C{L{ParseResults}} containing any results names that were originally matched, and a 
-    single token containing the original matched text from the input string.  So if 
-    the expression passed to C{L{originalTextFor}} contains expressions with defined
-    results names, you must set C{asString} to C{False} if you want to preserve those
-    results name values.
-
-    Example::
-        src = "this is test  bold text  normal text "
-        for tag in ("b","i"):
-            opener,closer = makeHTMLTags(tag)
-            patt = originalTextFor(opener + SkipTo(closer) + closer)
-            print(patt.searchString(src)[0])
-    prints::
-        [' bold text ']
-        ['text']
-    """
-    locMarker = Empty().setParseAction(lambda s,loc,t: loc)
-    endlocMarker = locMarker.copy()
-    endlocMarker.callPreparse = False
-    matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end")
-    if asString:
-        extractText = lambda s,l,t: s[t._original_start:t._original_end]
-    else:
-        def extractText(s,l,t):
-            t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]]
-    matchExpr.setParseAction(extractText)
-    matchExpr.ignoreExprs = expr.ignoreExprs
-    return matchExpr
-
-def ungroup(expr): 
-    """
-    Helper to undo pyparsing's default grouping of And expressions, even
-    if all but one are non-empty.
-    """
-    return TokenConverter(expr).setParseAction(lambda t:t[0])
-
-def locatedExpr(expr):
-    """
-    Helper to decorate a returned token with its starting and ending locations in the input string.
-    This helper adds the following results names:
-     - locn_start = location where matched expression begins
-     - locn_end = location where matched expression ends
-     - value = the actual parsed results
-
-    Be careful if the input text contains C{} characters, you may want to call
-    C{L{ParserElement.parseWithTabs}}
-
-    Example::
-        wd = Word(alphas)
-        for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"):
-            print(match)
-    prints::
-        [[0, 'ljsdf', 5]]
-        [[8, 'lksdjjf', 15]]
-        [[18, 'lkkjj', 23]]
-    """
-    locator = Empty().setParseAction(lambda s,l,t: l)
-    return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end"))
-
-
-# convenience constants for positional expressions
-empty       = Empty().setName("empty")
-lineStart   = LineStart().setName("lineStart")
-lineEnd     = LineEnd().setName("lineEnd")
-stringStart = StringStart().setName("stringStart")
-stringEnd   = StringEnd().setName("stringEnd")
-
-_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1])
-_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16)))
-_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8)))
-_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | CharsNotIn(r'\]', exact=1)
-_charRange = Group(_singleChar + Suppress("-") + _singleChar)
-_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]"
-
-def srange(s):
-    r"""
-    Helper to easily define string ranges for use in Word construction.  Borrows
-    syntax from regexp '[]' string range definitions::
-        srange("[0-9]")   -> "0123456789"
-        srange("[a-z]")   -> "abcdefghijklmnopqrstuvwxyz"
-        srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_"
-    The input string must be enclosed in []'s, and the returned string is the expanded
-    character set joined into a single string.
-    The values enclosed in the []'s may be:
-     - a single character
-     - an escaped character with a leading backslash (such as C{\-} or C{\]})
-     - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character) 
-         (C{\0x##} is also supported for backwards compatibility) 
-     - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character)
-     - a range of any of the above, separated by a dash (C{'a-z'}, etc.)
-     - any combination of the above (C{'aeiouy'}, C{'a-zA-Z0-9_$'}, etc.)
-    """
-    _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1))
-    try:
-        return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body)
-    except Exception:
-        return ""
-
-def matchOnlyAtCol(n):
-    """
-    Helper method for defining parse actions that require matching at a specific
-    column in the input text.
-    """
-    def verifyCol(strg,locn,toks):
-        if col(locn,strg) != n:
-            raise ParseException(strg,locn,"matched token not at column %d" % n)
-    return verifyCol
-
-def replaceWith(replStr):
-    """
-    Helper method for common parse actions that simply return a literal value.  Especially
-    useful when used with C{L{transformString}()}.
-
-    Example::
-        num = Word(nums).setParseAction(lambda toks: int(toks[0]))
-        na = oneOf("N/A NA").setParseAction(replaceWith(math.nan))
-        term = na | num
-        
-        OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234]
-    """
-    return lambda s,l,t: [replStr]
-
-def removeQuotes(s,l,t):
-    """
-    Helper parse action for removing quotation marks from parsed quoted strings.
-
-    Example::
-        # by default, quotation marks are included in parsed results
-        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"]
-
-        # use removeQuotes to strip quotation marks from parsed results
-        quotedString.setParseAction(removeQuotes)
-        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"]
-    """
-    return t[0][1:-1]
-
-def tokenMap(func, *args):
-    """
-    Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional 
-    args are passed, they are forwarded to the given function as additional arguments after
-    the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the
-    parsed data to an integer using base 16.
-
-    Example (compare the last to example in L{ParserElement.transformString}::
-        hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16))
-        hex_ints.runTests('''
-            00 11 22 aa FF 0a 0d 1a
-            ''')
-        
-        upperword = Word(alphas).setParseAction(tokenMap(str.upper))
-        OneOrMore(upperword).runTests('''
-            my kingdom for a horse
-            ''')
-
-        wd = Word(alphas).setParseAction(tokenMap(str.title))
-        OneOrMore(wd).setParseAction(' '.join).runTests('''
-            now is the winter of our discontent made glorious summer by this sun of york
-            ''')
-    prints::
-        00 11 22 aa FF 0a 0d 1a
-        [0, 17, 34, 170, 255, 10, 13, 26]
-
-        my kingdom for a horse
-        ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE']
-
-        now is the winter of our discontent made glorious summer by this sun of york
-        ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York']
-    """
-    def pa(s,l,t):
-        return [func(tokn, *args) for tokn in t]
-
-    try:
-        func_name = getattr(func, '__name__', 
-                            getattr(func, '__class__').__name__)
-    except Exception:
-        func_name = str(func)
-    pa.__name__ = func_name
-
-    return pa
-
-upcaseTokens = tokenMap(lambda t: _ustr(t).upper())
-"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}"""
-
-downcaseTokens = tokenMap(lambda t: _ustr(t).lower())
-"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}"""
-    
-def _makeTags(tagStr, xml):
-    """Internal helper to construct opening and closing tag expressions, given a tag name"""
-    if isinstance(tagStr,basestring):
-        resname = tagStr
-        tagStr = Keyword(tagStr, caseless=not xml)
-    else:
-        resname = tagStr.name
-
-    tagAttrName = Word(alphas,alphanums+"_-:")
-    if (xml):
-        tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes )
-        openTag = Suppress("<") + tagStr("tag") + \
-                Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \
-                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
-    else:
-        printablesLessRAbrack = "".join(c for c in printables if c not in ">")
-        tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack)
-        openTag = Suppress("<") + tagStr("tag") + \
-                Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \
-                Optional( Suppress("=") + tagAttrValue ) ))) + \
-                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
-    closeTag = Combine(_L("")
-
-    openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname)
-    closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % resname)
-    openTag.tag = resname
-    closeTag.tag = resname
-    return openTag, closeTag
-
-def makeHTMLTags(tagStr):
-    """
-    Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches
-    tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values.
-
-    Example::
-        text = 'More info at the pyparsing wiki page'
-        # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple
-        a,a_end = makeHTMLTags("A")
-        link_expr = a + SkipTo(a_end)("link_text") + a_end
-        
-        for link in link_expr.searchString(text):
-            # attributes in the  tag (like "href" shown here) are also accessible as named results
-            print(link.link_text, '->', link.href)
-    prints::
-        pyparsing -> http://pyparsing.wikispaces.com
-    """
-    return _makeTags( tagStr, False )
-
-def makeXMLTags(tagStr):
-    """
-    Helper to construct opening and closing tag expressions for XML, given a tag name. Matches
-    tags only in the given upper/lower case.
-
-    Example: similar to L{makeHTMLTags}
-    """
-    return _makeTags( tagStr, True )
-
-def withAttribute(*args,**attrDict):
-    """
-    Helper to create a validating parse action to be used with start tags created
-    with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag
-    with a required attribute value, to avoid false matches on common tags such as
-    C{} or C{
}. - - Call C{withAttribute} with a series of attribute names and values. Specify the list - of filter attributes names and values as: - - keyword arguments, as in C{(align="right")}, or - - as an explicit dict with C{**} operator, when an attribute name is also a Python - reserved word, as in C{**{"class":"Customer", "align":"right"}} - - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) - For attribute names with a namespace prefix, you must use the second form. Attribute - names are matched insensitive to upper/lower case. - - If just testing for C{class} (with or without a namespace), use C{L{withClass}}. - - To verify that the attribute exists, but without specifying a value, pass - C{withAttribute.ANY_VALUE} as the value. - - Example:: - html = ''' -
- Some text -
1 4 0 1 0
-
1,3 2,3 1,1
-
this has no type
-
- - ''' - div,div_end = makeHTMLTags("div") - - # only match div tag having a type attribute with value "grid" - div_grid = div().setParseAction(withAttribute(type="grid")) - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.searchString(html): - print(grid_header.body) - - # construct a match with any div tag having a type attribute, regardless of the value - div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.searchString(html): - print(div_header.body) - prints:: - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - if args: - attrs = args[:] - else: - attrs = attrDict.items() - attrs = [(k,v) for k,v in attrs] - def pa(s,l,tokens): - for attrName,attrValue in attrs: - if attrName not in tokens: - raise ParseException(s,l,"no matching attribute " + attrName) - if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: - raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % - (attrName, tokens[attrName], attrValue)) - return pa -withAttribute.ANY_VALUE = object() - -def withClass(classname, namespace=''): - """ - Simplified version of C{L{withAttribute}} when matching on a div class - made - difficult because C{class} is a reserved word in Python. - - Example:: - html = ''' -
- Some text -
1 4 0 1 0
-
1,3 2,3 1,1
-
this <div> has no class
-
- - ''' - div,div_end = makeHTMLTags("div") - div_grid = div().setParseAction(withClass("grid")) - - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.searchString(html): - print(grid_header.body) - - div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.searchString(html): - print(div_header.body) - prints:: - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - classattr = "%s:class" % namespace if namespace else "class" - return withAttribute(**{classattr : classname}) - -opAssoc = _Constants() -opAssoc.LEFT = object() -opAssoc.RIGHT = object() - -def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): - """ - Helper method for constructing grammars of expressions made up of - operators working in a precedence hierarchy. Operators may be unary or - binary, left- or right-associative. Parse actions can also be attached - to operator expressions. The generated parser will also recognize the use - of parentheses to override operator precedences (see example below). - - Note: if you define a deep operator list, you may see performance issues - when using infixNotation. See L{ParserElement.enablePackrat} for a - mechanism to potentially improve your parser performance. - - Parameters: - - baseExpr - expression representing the most basic element for the nested - - opList - list of tuples, one for each operator precedence level in the - expression grammar; each tuple is of the form - (opExpr, numTerms, rightLeftAssoc, parseAction), where: - - opExpr is the pyparsing expression for the operator; - may also be a string, which will be converted to a Literal; - if numTerms is 3, opExpr is a tuple of two expressions, for the - two operators separating the 3 terms - - numTerms is the number of terms for this operator (must - be 1, 2, or 3) - - rightLeftAssoc is the indicator whether the operator is - right or left associative, using the pyparsing-defined - constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. - - parseAction is the parse action to be associated with - expressions matching this operator expression (the - parse action tuple member may be omitted); if the parse action - is passed a tuple or list of functions, this is equivalent to - calling C{setParseAction(*fn)} (L{ParserElement.setParseAction}) - - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) - - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) - - Example:: - # simple example of four-function arithmetic with ints and variable names - integer = pyparsing_common.signed_integer - varname = pyparsing_common.identifier - - arith_expr = infixNotation(integer | varname, - [ - ('-', 1, opAssoc.RIGHT), - (oneOf('* /'), 2, opAssoc.LEFT), - (oneOf('+ -'), 2, opAssoc.LEFT), - ]) - - arith_expr.runTests(''' - 5+3*6 - (5+3)*6 - -2--11 - ''', fullDump=False) - prints:: - 5+3*6 - [[5, '+', [3, '*', 6]]] - - (5+3)*6 - [[[5, '+', 3], '*', 6]] - - -2--11 - [[['-', 2], '-', ['-', 11]]] - """ - ret = Forward() - lastExpr = baseExpr | ( lpar + ret + rpar ) - for i,operDef in enumerate(opList): - opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] - termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr - if arity == 3: - if opExpr is None or len(opExpr) != 2: - raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") - opExpr1, opExpr2 = opExpr - thisExpr = Forward().setName(termName) - if rightLeftAssoc == opAssoc.LEFT: - if arity == 1: - matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) - elif arity == 2: - if opExpr is not None: - matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) - else: - matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) - elif arity == 3: - matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ - Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) - else: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - elif rightLeftAssoc == opAssoc.RIGHT: - if arity == 1: - # try to avoid LR with this extra test - if not isinstance(opExpr, Optional): - opExpr = Optional(opExpr) - matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) - elif arity == 2: - if opExpr is not None: - matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) - else: - matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) - elif arity == 3: - matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ - Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) - else: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - else: - raise ValueError("operator must indicate right or left associativity") - if pa: - if isinstance(pa, (tuple, list)): - matchExpr.setParseAction(*pa) - else: - matchExpr.setParseAction(pa) - thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) - lastExpr = thisExpr - ret <<= lastExpr - return ret - -operatorPrecedence = infixNotation -"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" - -dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") -sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") -quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| - Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") -unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") - -def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): - """ - Helper method for defining nested lists enclosed in opening and closing - delimiters ("(" and ")" are the default). - - Parameters: - - opener - opening character for a nested list (default=C{"("}); can also be a pyparsing expression - - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression - - content - expression for items within the nested lists (default=C{None}) - - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{quotedString}) - - If an expression is not provided for the content argument, the nested - expression will capture all whitespace-delimited content between delimiters - as a list of separate values. - - Use the C{ignoreExpr} argument to define expressions that may contain - opening or closing characters that should not be treated as opening - or closing characters for nesting, such as quotedString or a comment - expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. - The default is L{quotedString}, but if no expressions are to be ignored, - then pass C{None} for this argument. - - Example:: - data_type = oneOf("void int short long char float double") - decl_data_type = Combine(data_type + Optional(Word('*'))) - ident = Word(alphas+'_', alphanums+'_') - number = pyparsing_common.number - arg = Group(decl_data_type + ident) - LPAR,RPAR = map(Suppress, "()") - - code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) - - c_function = (decl_data_type("type") - + ident("name") - + LPAR + Optional(delimitedList(arg), [])("args") + RPAR - + code_body("body")) - c_function.ignore(cStyleComment) - - source_code = ''' - int is_odd(int x) { - return (x%2); - } - - int dec_to_hex(char hchar) { - if (hchar >= '0' && hchar <= '9') { - return (ord(hchar)-ord('0')); - } else { - return (10+ord(hchar)-ord('A')); - } - } - ''' - for func in c_function.searchString(source_code): - print("%(name)s (%(type)s) args: %(args)s" % func) - - prints:: - is_odd (int) args: [['int', 'x']] - dec_to_hex (int) args: [['char', 'hchar']] - """ - if opener == closer: - raise ValueError("opening and closing strings cannot be the same") - if content is None: - if isinstance(opener,basestring) and isinstance(closer,basestring): - if len(opener) == 1 and len(closer)==1: - if ignoreExpr is not None: - content = (Combine(OneOrMore(~ignoreExpr + - CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS - ).setParseAction(lambda t:t[0].strip())) - else: - if ignoreExpr is not None: - content = (Combine(OneOrMore(~ignoreExpr + - ~Literal(opener) + ~Literal(closer) + - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - raise ValueError("opening and closing arguments must be strings if no content expression is given") - ret = Forward() - if ignoreExpr is not None: - ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) - else: - ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) - ret.setName('nested %s%s expression' % (opener,closer)) - return ret - -def indentedBlock(blockStatementExpr, indentStack, indent=True): - """ - Helper method for defining space-delimited indentation blocks, such as - those used to define block statements in Python source code. - - Parameters: - - blockStatementExpr - expression defining syntax of statement that - is repeated within the indented block - - indentStack - list created by caller to manage indentation stack - (multiple statementWithIndentedBlock expressions within a single grammar - should share a common indentStack) - - indent - boolean indicating whether block must be indented beyond the - the current level; set to False for block of left-most statements - (default=C{True}) - - A valid block must contain at least one C{blockStatement}. - - Example:: - data = ''' - def A(z): - A1 - B = 100 - G = A2 - A2 - A3 - B - def BB(a,b,c): - BB1 - def BBA(): - bba1 - bba2 - bba3 - C - D - def spam(x,y): - def eggs(z): - pass - ''' - - - indentStack = [1] - stmt = Forward() - - identifier = Word(alphas, alphanums) - funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") - func_body = indentedBlock(stmt, indentStack) - funcDef = Group( funcDecl + func_body ) - - rvalue = Forward() - funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") - rvalue << (funcCall | identifier | Word(nums)) - assignment = Group(identifier + "=" + rvalue) - stmt << ( funcDef | assignment | identifier ) - - module_body = OneOrMore(stmt) - - parseTree = module_body.parseString(data) - parseTree.pprint() - prints:: - [['def', - 'A', - ['(', 'z', ')'], - ':', - [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], - 'B', - ['def', - 'BB', - ['(', 'a', 'b', 'c', ')'], - ':', - [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], - 'C', - 'D', - ['def', - 'spam', - ['(', 'x', 'y', ')'], - ':', - [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] - """ - def checkPeerIndent(s,l,t): - if l >= len(s): return - curCol = col(l,s) - if curCol != indentStack[-1]: - if curCol > indentStack[-1]: - raise ParseFatalException(s,l,"illegal nesting") - raise ParseException(s,l,"not a peer entry") - - def checkSubIndent(s,l,t): - curCol = col(l,s) - if curCol > indentStack[-1]: - indentStack.append( curCol ) - else: - raise ParseException(s,l,"not a subentry") - - def checkUnindent(s,l,t): - if l >= len(s): return - curCol = col(l,s) - if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): - raise ParseException(s,l,"not an unindent") - indentStack.pop() - - NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) - INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') - PEER = Empty().setParseAction(checkPeerIndent).setName('') - UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') - if indent: - smExpr = Group( Optional(NL) + - #~ FollowedBy(blockStatementExpr) + - INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) - else: - smExpr = Group( Optional(NL) + - (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) - blockStatementExpr.ignore(_bslash + LineEnd()) - return smExpr.setName('indented block') - -alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") -punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") - -anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) -_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) -commonHTMLEntity = Regex('&(?P' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") -def replaceHTMLEntity(t): - """Helper parser action to replace common HTML entities with their special characters""" - return _htmlEntityMap.get(t.entity) - -# it's easy to get these comment structures wrong - they're very common, so may as well make them available -cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") -"Comment of the form C{/* ... */}" - -htmlComment = Regex(r"").setName("HTML comment") -"Comment of the form C{}" - -restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") -dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") -"Comment of the form C{// ... (to end of line)}" - -cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") -"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" - -javaStyleComment = cppStyleComment -"Same as C{L{cppStyleComment}}" - -pythonStyleComment = Regex(r"#.*").setName("Python style comment") -"Comment of the form C{# ... (to end of line)}" - -_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + - Optional( Word(" \t") + - ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") -commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") -"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. - This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" - -# some other useful expressions - using lower-case class name since we are really using this as a namespace -class pyparsing_common: - """ - Here are some common low-level expressions that may be useful in jump-starting parser development: - - numeric forms (L{integers}, L{reals}, L{scientific notation}) - - common L{programming identifiers} - - network addresses (L{MAC}, L{IPv4}, L{IPv6}) - - ISO8601 L{dates} and L{datetime} - - L{UUID} - - L{comma-separated list} - Parse actions: - - C{L{convertToInteger}} - - C{L{convertToFloat}} - - C{L{convertToDate}} - - C{L{convertToDatetime}} - - C{L{stripHTMLTags}} - - C{L{upcaseTokens}} - - C{L{downcaseTokens}} - - Example:: - pyparsing_common.number.runTests(''' - # any int or real number, returned as the appropriate type - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.fnumber.runTests(''' - # any int or real number, returned as float - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.hex_integer.runTests(''' - # hex numbers - 100 - FF - ''') - - pyparsing_common.fraction.runTests(''' - # fractions - 1/2 - -3/4 - ''') - - pyparsing_common.mixed_integer.runTests(''' - # mixed fractions - 1 - 1/2 - -3/4 - 1-3/4 - ''') - - import uuid - pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) - pyparsing_common.uuid.runTests(''' - # uuid - 12345678-1234-5678-1234-567812345678 - ''') - prints:: - # any int or real number, returned as the appropriate type - 100 - [100] - - -100 - [-100] - - +100 - [100] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # any int or real number, returned as float - 100 - [100.0] - - -100 - [-100.0] - - +100 - [100.0] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # hex numbers - 100 - [256] - - FF - [255] - - # fractions - 1/2 - [0.5] - - -3/4 - [-0.75] - - # mixed fractions - 1 - [1] - - 1/2 - [0.5] - - -3/4 - [-0.75] - - 1-3/4 - [1.75] - - # uuid - 12345678-1234-5678-1234-567812345678 - [UUID('12345678-1234-5678-1234-567812345678')] - """ - - convertToInteger = tokenMap(int) - """ - Parse action for converting parsed integers to Python int - """ - - convertToFloat = tokenMap(float) - """ - Parse action for converting parsed numbers to Python float - """ - - integer = Word(nums).setName("integer").setParseAction(convertToInteger) - """expression that parses an unsigned integer, returns an int""" - - hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) - """expression that parses a hexadecimal integer, returns an int""" - - signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) - """expression that parses an integer with optional leading sign, returns an int""" - - fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") - """fractional expression of an integer divided by an integer, returns a float""" - fraction.addParseAction(lambda t: t[0]/t[-1]) - - mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") - """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" - mixed_integer.addParseAction(sum) - - real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) - """expression that parses a floating point number and returns a float""" - - sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) - """expression that parses a floating point number with optional scientific notation and returns a float""" - - # streamlining this expression makes the docs nicer-looking - number = (sci_real | real | signed_integer).streamline() - """any numeric expression, returns the corresponding Python type""" - - fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) - """any int or real number, returned as float""" - - identifier = Word(alphas+'_', alphanums+'_').setName("identifier") - """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" - - ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") - "IPv4 address (C{0.0.0.0 - 255.255.255.255})" - - _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") - _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") - _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") - _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) - _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") - ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") - "IPv6 address (long, short, or mixed form)" - - mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") - "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" - - @staticmethod - def convertToDate(fmt="%Y-%m-%d"): - """ - Helper to create a parse action for converting parsed date string to Python datetime.date - - Params - - - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) - - Example:: - date_expr = pyparsing_common.iso8601_date.copy() - date_expr.setParseAction(pyparsing_common.convertToDate()) - print(date_expr.parseString("1999-12-31")) - prints:: - [datetime.date(1999, 12, 31)] - """ - def cvt_fn(s,l,t): - try: - return datetime.strptime(t[0], fmt).date() - except ValueError as ve: - raise ParseException(s, l, str(ve)) - return cvt_fn - - @staticmethod - def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): - """ - Helper to create a parse action for converting parsed datetime string to Python datetime.datetime - - Params - - - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) - - Example:: - dt_expr = pyparsing_common.iso8601_datetime.copy() - dt_expr.setParseAction(pyparsing_common.convertToDatetime()) - print(dt_expr.parseString("1999-12-31T23:59:59.999")) - prints:: - [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] - """ - def cvt_fn(s,l,t): - try: - return datetime.strptime(t[0], fmt) - except ValueError as ve: - raise ParseException(s, l, str(ve)) - return cvt_fn - - iso8601_date = Regex(r'(?P\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?').setName("ISO8601 date") - "ISO8601 date (C{yyyy-mm-dd})" - - iso8601_datetime = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") - "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" - - uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") - "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" - - _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() - @staticmethod - def stripHTMLTags(s, l, tokens): - """ - Parse action to remove HTML tags from web page HTML source - - Example:: - # strip HTML links from normal text - text = 'More info at the
pyparsing wiki page' - td,td_end = makeHTMLTags("TD") - table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end - - print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' - """ - return pyparsing_common._html_stripper.transformString(tokens[0]) - - _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') - + Optional( White(" \t") ) ) ).streamline().setName("commaItem") - comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") - """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" - - upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) - """Parse action to convert tokens to upper case.""" - - downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) - """Parse action to convert tokens to lower case.""" - - -if __name__ == "__main__": - - selectToken = CaselessLiteral("select") - fromToken = CaselessLiteral("from") - - ident = Word(alphas, alphanums + "_$") - - columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) - columnNameList = Group(delimitedList(columnName)).setName("columns") - columnSpec = ('*' | columnNameList) - - tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) - tableNameList = Group(delimitedList(tableName)).setName("tables") - - simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") - - # demo runTests method, including embedded comments in test string - simpleSQL.runTests(""" - # '*' as column list and dotted table name - select * from SYS.XYZZY - - # caseless match on "SELECT", and casts back to "select" - SELECT * from XYZZY, ABC - - # list of column names, and mixed case SELECT keyword - Select AA,BB,CC from Sys.dual - - # multiple tables - Select A, B, C from Sys.dual, Table2 - - # invalid SELECT keyword - should fail - Xelect A, B, C from Sys.dual - - # incomplete command - should fail - Select - - # invalid column name - should fail - Select ^^^ frox Sys.dual - - """) - - pyparsing_common.number.runTests(""" - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - """) - - # any int or real number, returned as float - pyparsing_common.fnumber.runTests(""" - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - """) - - pyparsing_common.hex_integer.runTests(""" - 100 - FF - """) - - import uuid - pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) - pyparsing_common.uuid.runTests(""" - 12345678-1234-5678-1234-567812345678 - """) diff --git a/pkg_resources/_vendor/vendored.txt b/pkg_resources/_vendor/vendored.txt deleted file mode 100644 index da7a19813e..0000000000 --- a/pkg_resources/_vendor/vendored.txt +++ /dev/null @@ -1,3 +0,0 @@ -packaging==20.4 -pyparsing==2.2.1 -appdirs==1.4.3 diff --git a/pkg_resources/extern/__init__.py b/pkg_resources/extern/__init__.py deleted file mode 100644 index fed5929540..0000000000 --- a/pkg_resources/extern/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -import importlib.util -import sys - - -class VendorImporter: - """ - A PEP 302 meta path importer for finding optionally-vendored - or otherwise naturally-installed packages from root_name. - """ - - def __init__(self, root_name, vendored_names=(), vendor_pkg=None): - self.root_name = root_name - self.vendored_names = set(vendored_names) - self.vendor_pkg = vendor_pkg or root_name.replace('extern', '_vendor') - - @property - def search_path(self): - """ - Search first the vendor package then as a natural package. - """ - yield self.vendor_pkg + '.' - yield '' - - def _module_matches_namespace(self, fullname): - """Figure out if the target module is vendored.""" - root, base, target = fullname.partition(self.root_name + '.') - return not root and any(map(target.startswith, self.vendored_names)) - - def load_module(self, fullname): - """ - Iterate over the search path to locate and load fullname. - """ - root, base, target = fullname.partition(self.root_name + '.') - for prefix in self.search_path: - try: - extant = prefix + target - __import__(extant) - mod = sys.modules[extant] - sys.modules[fullname] = mod - return mod - except ImportError: - pass - else: - raise ImportError( - "The '{target}' package is required; " - "normally this is bundled with this package so if you get " - "this warning, consult the packager of your " - "distribution.".format(**locals()) - ) - - def create_module(self, spec): - return self.load_module(spec.name) - - def exec_module(self, module): - pass - - def find_spec(self, fullname, path=None, target=None): - """Return a module spec for vendored names.""" - return ( - importlib.util.spec_from_loader(fullname, self) - if self._module_matches_namespace(fullname) else None - ) - - def install(self): - """ - Install this importer into sys.meta_path if not already present. - """ - if self not in sys.meta_path: - sys.meta_path.append(self) - - -names = 'packaging', 'pyparsing', 'appdirs' -VendorImporter(__name__, names).install() diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py index 965a7c0089..20b4f2e6bf 100644 --- a/pkg_resources/tests/test_resources.py +++ b/pkg_resources/tests/test_resources.py @@ -5,7 +5,7 @@ import itertools import pytest -from pkg_resources.extern import packaging +import packaging import pkg_resources from pkg_resources import ( diff --git a/setup.cfg b/setup.cfg index 7de45d8be6..67dd0e3f93 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,6 +26,13 @@ packages = find_namespace: # include_package_data = true python_requires = >=3.6 install_requires = + packaging==20.4 + pyparsing==2.2.1 + ordered-set==3.1.1 + more_itertools==8.8.0 + + # for pkg_resources + appdirs==1.4.3 [options.packages.find] exclude = diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 9d6f0bc0dd..974a5d8e80 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -13,6 +13,7 @@ from ._deprecation_warning import SetuptoolsDeprecationWarning +import _setuptools_vendored # noqa: F401 import setuptools.version from setuptools.extension import Extension from setuptools.dist import Distribution diff --git a/setuptools/_vendor/__init__.py b/setuptools/_vendor/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/setuptools/_vendor/more_itertools/__init__.py b/setuptools/_vendor/more_itertools/__init__.py deleted file mode 100644 index 19a169fc30..0000000000 --- a/setuptools/_vendor/more_itertools/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .more import * # noqa -from .recipes import * # noqa - -__version__ = '8.8.0' diff --git a/setuptools/_vendor/more_itertools/__init__.pyi b/setuptools/_vendor/more_itertools/__init__.pyi deleted file mode 100644 index 96f6e36c7f..0000000000 --- a/setuptools/_vendor/more_itertools/__init__.pyi +++ /dev/null @@ -1,2 +0,0 @@ -from .more import * -from .recipes import * diff --git a/setuptools/_vendor/more_itertools/more.py b/setuptools/_vendor/more_itertools/more.py deleted file mode 100644 index 0f7d282aa5..0000000000 --- a/setuptools/_vendor/more_itertools/more.py +++ /dev/null @@ -1,3825 +0,0 @@ -import warnings - -from collections import Counter, defaultdict, deque, abc -from collections.abc import Sequence -from concurrent.futures import ThreadPoolExecutor -from functools import partial, reduce, wraps -from heapq import merge, heapify, heapreplace, heappop -from itertools import ( - chain, - compress, - count, - cycle, - dropwhile, - groupby, - islice, - repeat, - starmap, - takewhile, - tee, - zip_longest, -) -from math import exp, factorial, floor, log -from queue import Empty, Queue -from random import random, randrange, uniform -from operator import itemgetter, mul, sub, gt, lt -from sys import hexversion, maxsize -from time import monotonic - -from .recipes import ( - consume, - flatten, - pairwise, - powerset, - take, - unique_everseen, -) - -__all__ = [ - 'AbortThread', - 'adjacent', - 'always_iterable', - 'always_reversible', - 'bucket', - 'callback_iter', - 'chunked', - 'circular_shifts', - 'collapse', - 'collate', - 'consecutive_groups', - 'consumer', - 'countable', - 'count_cycle', - 'mark_ends', - 'difference', - 'distinct_combinations', - 'distinct_permutations', - 'distribute', - 'divide', - 'exactly_n', - 'filter_except', - 'first', - 'groupby_transform', - 'ilen', - 'interleave_longest', - 'interleave', - 'intersperse', - 'islice_extended', - 'iterate', - 'ichunked', - 'is_sorted', - 'last', - 'locate', - 'lstrip', - 'make_decorator', - 'map_except', - 'map_reduce', - 'nth_or_last', - 'nth_permutation', - 'nth_product', - 'numeric_range', - 'one', - 'only', - 'padded', - 'partitions', - 'set_partitions', - 'peekable', - 'repeat_last', - 'replace', - 'rlocate', - 'rstrip', - 'run_length', - 'sample', - 'seekable', - 'SequenceView', - 'side_effect', - 'sliced', - 'sort_together', - 'split_at', - 'split_after', - 'split_before', - 'split_when', - 'split_into', - 'spy', - 'stagger', - 'strip', - 'substrings', - 'substrings_indexes', - 'time_limited', - 'unique_to_each', - 'unzip', - 'windowed', - 'with_iter', - 'UnequalIterablesError', - 'zip_equal', - 'zip_offset', - 'windowed_complete', - 'all_unique', - 'value_chain', - 'product_index', - 'combination_index', - 'permutation_index', -] - -_marker = object() - - -def chunked(iterable, n, strict=False): - """Break *iterable* into lists of length *n*: - - >>> list(chunked([1, 2, 3, 4, 5, 6], 3)) - [[1, 2, 3], [4, 5, 6]] - - By the default, the last yielded list will have fewer than *n* elements - if the length of *iterable* is not divisible by *n*: - - >>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3)) - [[1, 2, 3], [4, 5, 6], [7, 8]] - - To use a fill-in value instead, see the :func:`grouper` recipe. - - If the length of *iterable* is not divisible by *n* and *strict* is - ``True``, then ``ValueError`` will be raised before the last - list is yielded. - - """ - iterator = iter(partial(take, n, iter(iterable)), []) - if strict: - - def ret(): - for chunk in iterator: - if len(chunk) != n: - raise ValueError('iterable is not divisible by n.') - yield chunk - - return iter(ret()) - else: - return iterator - - -def first(iterable, default=_marker): - """Return the first item of *iterable*, or *default* if *iterable* is - empty. - - >>> first([0, 1, 2, 3]) - 0 - >>> first([], 'some default') - 'some default' - - If *default* is not provided and there are no items in the iterable, - raise ``ValueError``. - - :func:`first` is useful when you have a generator of expensive-to-retrieve - values and want any arbitrary one. It is marginally shorter than - ``next(iter(iterable), default)``. - - """ - try: - return next(iter(iterable)) - except StopIteration as e: - if default is _marker: - raise ValueError( - 'first() was called on an empty iterable, and no ' - 'default value was provided.' - ) from e - return default - - -def last(iterable, default=_marker): - """Return the last item of *iterable*, or *default* if *iterable* is - empty. - - >>> last([0, 1, 2, 3]) - 3 - >>> last([], 'some default') - 'some default' - - If *default* is not provided and there are no items in the iterable, - raise ``ValueError``. - """ - try: - if isinstance(iterable, Sequence): - return iterable[-1] - # Work around https://bugs.python.org/issue38525 - elif hasattr(iterable, '__reversed__') and (hexversion != 0x030800F0): - return next(reversed(iterable)) - else: - return deque(iterable, maxlen=1)[-1] - except (IndexError, TypeError, StopIteration): - if default is _marker: - raise ValueError( - 'last() was called on an empty iterable, and no default was ' - 'provided.' - ) - return default - - -def nth_or_last(iterable, n, default=_marker): - """Return the nth or the last item of *iterable*, - or *default* if *iterable* is empty. - - >>> nth_or_last([0, 1, 2, 3], 2) - 2 - >>> nth_or_last([0, 1], 2) - 1 - >>> nth_or_last([], 0, 'some default') - 'some default' - - If *default* is not provided and there are no items in the iterable, - raise ``ValueError``. - """ - return last(islice(iterable, n + 1), default=default) - - -class peekable: - """Wrap an iterator to allow lookahead and prepending elements. - - Call :meth:`peek` on the result to get the value that will be returned - by :func:`next`. This won't advance the iterator: - - >>> p = peekable(['a', 'b']) - >>> p.peek() - 'a' - >>> next(p) - 'a' - - Pass :meth:`peek` a default value to return that instead of raising - ``StopIteration`` when the iterator is exhausted. - - >>> p = peekable([]) - >>> p.peek('hi') - 'hi' - - peekables also offer a :meth:`prepend` method, which "inserts" items - at the head of the iterable: - - >>> p = peekable([1, 2, 3]) - >>> p.prepend(10, 11, 12) - >>> next(p) - 10 - >>> p.peek() - 11 - >>> list(p) - [11, 12, 1, 2, 3] - - peekables can be indexed. Index 0 is the item that will be returned by - :func:`next`, index 1 is the item after that, and so on: - The values up to the given index will be cached. - - >>> p = peekable(['a', 'b', 'c', 'd']) - >>> p[0] - 'a' - >>> p[1] - 'b' - >>> next(p) - 'a' - - Negative indexes are supported, but be aware that they will cache the - remaining items in the source iterator, which may require significant - storage. - - To check whether a peekable is exhausted, check its truth value: - - >>> p = peekable(['a', 'b']) - >>> if p: # peekable has items - ... list(p) - ['a', 'b'] - >>> if not p: # peekable is exhausted - ... list(p) - [] - - """ - - def __init__(self, iterable): - self._it = iter(iterable) - self._cache = deque() - - def __iter__(self): - return self - - def __bool__(self): - try: - self.peek() - except StopIteration: - return False - return True - - def peek(self, default=_marker): - """Return the item that will be next returned from ``next()``. - - Return ``default`` if there are no items left. If ``default`` is not - provided, raise ``StopIteration``. - - """ - if not self._cache: - try: - self._cache.append(next(self._it)) - except StopIteration: - if default is _marker: - raise - return default - return self._cache[0] - - def prepend(self, *items): - """Stack up items to be the next ones returned from ``next()`` or - ``self.peek()``. The items will be returned in - first in, first out order:: - - >>> p = peekable([1, 2, 3]) - >>> p.prepend(10, 11, 12) - >>> next(p) - 10 - >>> list(p) - [11, 12, 1, 2, 3] - - It is possible, by prepending items, to "resurrect" a peekable that - previously raised ``StopIteration``. - - >>> p = peekable([]) - >>> next(p) - Traceback (most recent call last): - ... - StopIteration - >>> p.prepend(1) - >>> next(p) - 1 - >>> next(p) - Traceback (most recent call last): - ... - StopIteration - - """ - self._cache.extendleft(reversed(items)) - - def __next__(self): - if self._cache: - return self._cache.popleft() - - return next(self._it) - - def _get_slice(self, index): - # Normalize the slice's arguments - step = 1 if (index.step is None) else index.step - if step > 0: - start = 0 if (index.start is None) else index.start - stop = maxsize if (index.stop is None) else index.stop - elif step < 0: - start = -1 if (index.start is None) else index.start - stop = (-maxsize - 1) if (index.stop is None) else index.stop - else: - raise ValueError('slice step cannot be zero') - - # If either the start or stop index is negative, we'll need to cache - # the rest of the iterable in order to slice from the right side. - if (start < 0) or (stop < 0): - self._cache.extend(self._it) - # Otherwise we'll need to find the rightmost index and cache to that - # point. - else: - n = min(max(start, stop) + 1, maxsize) - cache_len = len(self._cache) - if n >= cache_len: - self._cache.extend(islice(self._it, n - cache_len)) - - return list(self._cache)[index] - - def __getitem__(self, index): - if isinstance(index, slice): - return self._get_slice(index) - - cache_len = len(self._cache) - if index < 0: - self._cache.extend(self._it) - elif index >= cache_len: - self._cache.extend(islice(self._it, index + 1 - cache_len)) - - return self._cache[index] - - -def collate(*iterables, **kwargs): - """Return a sorted merge of the items from each of several already-sorted - *iterables*. - - >>> list(collate('ACDZ', 'AZ', 'JKL')) - ['A', 'A', 'C', 'D', 'J', 'K', 'L', 'Z', 'Z'] - - Works lazily, keeping only the next value from each iterable in memory. Use - :func:`collate` to, for example, perform a n-way mergesort of items that - don't fit in memory. - - If a *key* function is specified, the iterables will be sorted according - to its result: - - >>> key = lambda s: int(s) # Sort by numeric value, not by string - >>> list(collate(['1', '10'], ['2', '11'], key=key)) - ['1', '2', '10', '11'] - - - If the *iterables* are sorted in descending order, set *reverse* to - ``True``: - - >>> list(collate([5, 3, 1], [4, 2, 0], reverse=True)) - [5, 4, 3, 2, 1, 0] - - If the elements of the passed-in iterables are out of order, you might get - unexpected results. - - On Python 3.5+, this function is an alias for :func:`heapq.merge`. - - """ - warnings.warn( - "collate is no longer part of more_itertools, use heapq.merge", - DeprecationWarning, - ) - return merge(*iterables, **kwargs) - - -def consumer(func): - """Decorator that automatically advances a PEP-342-style "reverse iterator" - to its first yield point so you don't have to call ``next()`` on it - manually. - - >>> @consumer - ... def tally(): - ... i = 0 - ... while True: - ... print('Thing number %s is %s.' % (i, (yield))) - ... i += 1 - ... - >>> t = tally() - >>> t.send('red') - Thing number 0 is red. - >>> t.send('fish') - Thing number 1 is fish. - - Without the decorator, you would have to call ``next(t)`` before - ``t.send()`` could be used. - - """ - - @wraps(func) - def wrapper(*args, **kwargs): - gen = func(*args, **kwargs) - next(gen) - return gen - - return wrapper - - -def ilen(iterable): - """Return the number of items in *iterable*. - - >>> ilen(x for x in range(1000000) if x % 3 == 0) - 333334 - - This consumes the iterable, so handle with care. - - """ - # This approach was selected because benchmarks showed it's likely the - # fastest of the known implementations at the time of writing. - # See GitHub tracker: #236, #230. - counter = count() - deque(zip(iterable, counter), maxlen=0) - return next(counter) - - -def iterate(func, start): - """Return ``start``, ``func(start)``, ``func(func(start))``, ... - - >>> from itertools import islice - >>> list(islice(iterate(lambda x: 2*x, 1), 10)) - [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] - - """ - while True: - yield start - start = func(start) - - -def with_iter(context_manager): - """Wrap an iterable in a ``with`` statement, so it closes once exhausted. - - For example, this will close the file when the iterator is exhausted:: - - upper_lines = (line.upper() for line in with_iter(open('foo'))) - - Any context manager which returns an iterable is a candidate for - ``with_iter``. - - """ - with context_manager as iterable: - yield from iterable - - -def one(iterable, too_short=None, too_long=None): - """Return the first item from *iterable*, which is expected to contain only - that item. Raise an exception if *iterable* is empty or has more than one - item. - - :func:`one` is useful for ensuring that an iterable contains only one item. - For example, it can be used to retrieve the result of a database query - that is expected to return a single row. - - If *iterable* is empty, ``ValueError`` will be raised. You may specify a - different exception with the *too_short* keyword: - - >>> it = [] - >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - ValueError: too many items in iterable (expected 1)' - >>> too_short = IndexError('too few items') - >>> one(it, too_short=too_short) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - IndexError: too few items - - Similarly, if *iterable* contains more than one item, ``ValueError`` will - be raised. You may specify a different exception with the *too_long* - keyword: - - >>> it = ['too', 'many'] - >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - ValueError: Expected exactly one item in iterable, but got 'too', - 'many', and perhaps more. - >>> too_long = RuntimeError - >>> one(it, too_long=too_long) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - RuntimeError - - Note that :func:`one` attempts to advance *iterable* twice to ensure there - is only one item. See :func:`spy` or :func:`peekable` to check iterable - contents less destructively. - - """ - it = iter(iterable) - - try: - first_value = next(it) - except StopIteration as e: - raise ( - too_short or ValueError('too few items in iterable (expected 1)') - ) from e - - try: - second_value = next(it) - except StopIteration: - pass - else: - msg = ( - 'Expected exactly one item in iterable, but got {!r}, {!r}, ' - 'and perhaps more.'.format(first_value, second_value) - ) - raise too_long or ValueError(msg) - - return first_value - - -def distinct_permutations(iterable, r=None): - """Yield successive distinct permutations of the elements in *iterable*. - - >>> sorted(distinct_permutations([1, 0, 1])) - [(0, 1, 1), (1, 0, 1), (1, 1, 0)] - - Equivalent to ``set(permutations(iterable))``, except duplicates are not - generated and thrown away. For larger input sequences this is much more - efficient. - - Duplicate permutations arise when there are duplicated elements in the - input iterable. The number of items returned is - `n! / (x_1! * x_2! * ... * x_n!)`, where `n` is the total number of - items input, and each `x_i` is the count of a distinct item in the input - sequence. - - If *r* is given, only the *r*-length permutations are yielded. - - >>> sorted(distinct_permutations([1, 0, 1], r=2)) - [(0, 1), (1, 0), (1, 1)] - >>> sorted(distinct_permutations(range(3), r=2)) - [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] - - """ - # Algorithm: https://w.wiki/Qai - def _full(A): - while True: - # Yield the permutation we have - yield tuple(A) - - # Find the largest index i such that A[i] < A[i + 1] - for i in range(size - 2, -1, -1): - if A[i] < A[i + 1]: - break - # If no such index exists, this permutation is the last one - else: - return - - # Find the largest index j greater than j such that A[i] < A[j] - for j in range(size - 1, i, -1): - if A[i] < A[j]: - break - - # Swap the value of A[i] with that of A[j], then reverse the - # sequence from A[i + 1] to form the new permutation - A[i], A[j] = A[j], A[i] - A[i + 1 :] = A[: i - size : -1] # A[i + 1:][::-1] - - # Algorithm: modified from the above - def _partial(A, r): - # Split A into the first r items and the last r items - head, tail = A[:r], A[r:] - right_head_indexes = range(r - 1, -1, -1) - left_tail_indexes = range(len(tail)) - - while True: - # Yield the permutation we have - yield tuple(head) - - # Starting from the right, find the first index of the head with - # value smaller than the maximum value of the tail - call it i. - pivot = tail[-1] - for i in right_head_indexes: - if head[i] < pivot: - break - pivot = head[i] - else: - return - - # Starting from the left, find the first value of the tail - # with a value greater than head[i] and swap. - for j in left_tail_indexes: - if tail[j] > head[i]: - head[i], tail[j] = tail[j], head[i] - break - # If we didn't find one, start from the right and find the first - # index of the head with a value greater than head[i] and swap. - else: - for j in right_head_indexes: - if head[j] > head[i]: - head[i], head[j] = head[j], head[i] - break - - # Reverse head[i + 1:] and swap it with tail[:r - (i + 1)] - tail += head[: i - r : -1] # head[i + 1:][::-1] - i += 1 - head[i:], tail[:] = tail[: r - i], tail[r - i :] - - items = sorted(iterable) - - size = len(items) - if r is None: - r = size - - if 0 < r <= size: - return _full(items) if (r == size) else _partial(items, r) - - return iter(() if r else ((),)) - - -def intersperse(e, iterable, n=1): - """Intersperse filler element *e* among the items in *iterable*, leaving - *n* items between each filler element. - - >>> list(intersperse('!', [1, 2, 3, 4, 5])) - [1, '!', 2, '!', 3, '!', 4, '!', 5] - - >>> list(intersperse(None, [1, 2, 3, 4, 5], n=2)) - [1, 2, None, 3, 4, None, 5] - - """ - if n == 0: - raise ValueError('n must be > 0') - elif n == 1: - # interleave(repeat(e), iterable) -> e, x_0, e, e, x_1, e, x_2... - # islice(..., 1, None) -> x_0, e, e, x_1, e, x_2... - return islice(interleave(repeat(e), iterable), 1, None) - else: - # interleave(filler, chunks) -> [e], [x_0, x_1], [e], [x_2, x_3]... - # islice(..., 1, None) -> [x_0, x_1], [e], [x_2, x_3]... - # flatten(...) -> x_0, x_1, e, x_2, x_3... - filler = repeat([e]) - chunks = chunked(iterable, n) - return flatten(islice(interleave(filler, chunks), 1, None)) - - -def unique_to_each(*iterables): - """Return the elements from each of the input iterables that aren't in the - other input iterables. - - For example, suppose you have a set of packages, each with a set of - dependencies:: - - {'pkg_1': {'A', 'B'}, 'pkg_2': {'B', 'C'}, 'pkg_3': {'B', 'D'}} - - If you remove one package, which dependencies can also be removed? - - If ``pkg_1`` is removed, then ``A`` is no longer necessary - it is not - associated with ``pkg_2`` or ``pkg_3``. Similarly, ``C`` is only needed for - ``pkg_2``, and ``D`` is only needed for ``pkg_3``:: - - >>> unique_to_each({'A', 'B'}, {'B', 'C'}, {'B', 'D'}) - [['A'], ['C'], ['D']] - - If there are duplicates in one input iterable that aren't in the others - they will be duplicated in the output. Input order is preserved:: - - >>> unique_to_each("mississippi", "missouri") - [['p', 'p'], ['o', 'u', 'r']] - - It is assumed that the elements of each iterable are hashable. - - """ - pool = [list(it) for it in iterables] - counts = Counter(chain.from_iterable(map(set, pool))) - uniques = {element for element in counts if counts[element] == 1} - return [list(filter(uniques.__contains__, it)) for it in pool] - - -def windowed(seq, n, fillvalue=None, step=1): - """Return a sliding window of width *n* over the given iterable. - - >>> all_windows = windowed([1, 2, 3, 4, 5], 3) - >>> list(all_windows) - [(1, 2, 3), (2, 3, 4), (3, 4, 5)] - - When the window is larger than the iterable, *fillvalue* is used in place - of missing values: - - >>> list(windowed([1, 2, 3], 4)) - [(1, 2, 3, None)] - - Each window will advance in increments of *step*: - - >>> list(windowed([1, 2, 3, 4, 5, 6], 3, fillvalue='!', step=2)) - [(1, 2, 3), (3, 4, 5), (5, 6, '!')] - - To slide into the iterable's items, use :func:`chain` to add filler items - to the left: - - >>> iterable = [1, 2, 3, 4] - >>> n = 3 - >>> padding = [None] * (n - 1) - >>> list(windowed(chain(padding, iterable), 3)) - [(None, None, 1), (None, 1, 2), (1, 2, 3), (2, 3, 4)] - """ - if n < 0: - raise ValueError('n must be >= 0') - if n == 0: - yield tuple() - return - if step < 1: - raise ValueError('step must be >= 1') - - window = deque(maxlen=n) - i = n - for _ in map(window.append, seq): - i -= 1 - if not i: - i = step - yield tuple(window) - - size = len(window) - if size < n: - yield tuple(chain(window, repeat(fillvalue, n - size))) - elif 0 < i < min(step, n): - window += (fillvalue,) * i - yield tuple(window) - - -def substrings(iterable): - """Yield all of the substrings of *iterable*. - - >>> [''.join(s) for s in substrings('more')] - ['m', 'o', 'r', 'e', 'mo', 'or', 're', 'mor', 'ore', 'more'] - - Note that non-string iterables can also be subdivided. - - >>> list(substrings([0, 1, 2])) - [(0,), (1,), (2,), (0, 1), (1, 2), (0, 1, 2)] - - """ - # The length-1 substrings - seq = [] - for item in iter(iterable): - seq.append(item) - yield (item,) - seq = tuple(seq) - item_count = len(seq) - - # And the rest - for n in range(2, item_count + 1): - for i in range(item_count - n + 1): - yield seq[i : i + n] - - -def substrings_indexes(seq, reverse=False): - """Yield all substrings and their positions in *seq* - - The items yielded will be a tuple of the form ``(substr, i, j)``, where - ``substr == seq[i:j]``. - - This function only works for iterables that support slicing, such as - ``str`` objects. - - >>> for item in substrings_indexes('more'): - ... print(item) - ('m', 0, 1) - ('o', 1, 2) - ('r', 2, 3) - ('e', 3, 4) - ('mo', 0, 2) - ('or', 1, 3) - ('re', 2, 4) - ('mor', 0, 3) - ('ore', 1, 4) - ('more', 0, 4) - - Set *reverse* to ``True`` to yield the same items in the opposite order. - - - """ - r = range(1, len(seq) + 1) - if reverse: - r = reversed(r) - return ( - (seq[i : i + L], i, i + L) for L in r for i in range(len(seq) - L + 1) - ) - - -class bucket: - """Wrap *iterable* and return an object that buckets it iterable into - child iterables based on a *key* function. - - >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3'] - >>> s = bucket(iterable, key=lambda x: x[0]) # Bucket by 1st character - >>> sorted(list(s)) # Get the keys - ['a', 'b', 'c'] - >>> a_iterable = s['a'] - >>> next(a_iterable) - 'a1' - >>> next(a_iterable) - 'a2' - >>> list(s['b']) - ['b1', 'b2', 'b3'] - - The original iterable will be advanced and its items will be cached until - they are used by the child iterables. This may require significant storage. - - By default, attempting to select a bucket to which no items belong will - exhaust the iterable and cache all values. - If you specify a *validator* function, selected buckets will instead be - checked against it. - - >>> from itertools import count - >>> it = count(1, 2) # Infinite sequence of odd numbers - >>> key = lambda x: x % 10 # Bucket by last digit - >>> validator = lambda x: x in {1, 3, 5, 7, 9} # Odd digits only - >>> s = bucket(it, key=key, validator=validator) - >>> 2 in s - False - >>> list(s[2]) - [] - - """ - - def __init__(self, iterable, key, validator=None): - self._it = iter(iterable) - self._key = key - self._cache = defaultdict(deque) - self._validator = validator or (lambda x: True) - - def __contains__(self, value): - if not self._validator(value): - return False - - try: - item = next(self[value]) - except StopIteration: - return False - else: - self._cache[value].appendleft(item) - - return True - - def _get_values(self, value): - """ - Helper to yield items from the parent iterator that match *value*. - Items that don't match are stored in the local cache as they - are encountered. - """ - while True: - # If we've cached some items that match the target value, emit - # the first one and evict it from the cache. - if self._cache[value]: - yield self._cache[value].popleft() - # Otherwise we need to advance the parent iterator to search for - # a matching item, caching the rest. - else: - while True: - try: - item = next(self._it) - except StopIteration: - return - item_value = self._key(item) - if item_value == value: - yield item - break - elif self._validator(item_value): - self._cache[item_value].append(item) - - def __iter__(self): - for item in self._it: - item_value = self._key(item) - if self._validator(item_value): - self._cache[item_value].append(item) - - yield from self._cache.keys() - - def __getitem__(self, value): - if not self._validator(value): - return iter(()) - - return self._get_values(value) - - -def spy(iterable, n=1): - """Return a 2-tuple with a list containing the first *n* elements of - *iterable*, and an iterator with the same items as *iterable*. - This allows you to "look ahead" at the items in the iterable without - advancing it. - - There is one item in the list by default: - - >>> iterable = 'abcdefg' - >>> head, iterable = spy(iterable) - >>> head - ['a'] - >>> list(iterable) - ['a', 'b', 'c', 'd', 'e', 'f', 'g'] - - You may use unpacking to retrieve items instead of lists: - - >>> (head,), iterable = spy('abcdefg') - >>> head - 'a' - >>> (first, second), iterable = spy('abcdefg', 2) - >>> first - 'a' - >>> second - 'b' - - The number of items requested can be larger than the number of items in - the iterable: - - >>> iterable = [1, 2, 3, 4, 5] - >>> head, iterable = spy(iterable, 10) - >>> head - [1, 2, 3, 4, 5] - >>> list(iterable) - [1, 2, 3, 4, 5] - - """ - it = iter(iterable) - head = take(n, it) - - return head.copy(), chain(head, it) - - -def interleave(*iterables): - """Return a new iterable yielding from each iterable in turn, - until the shortest is exhausted. - - >>> list(interleave([1, 2, 3], [4, 5], [6, 7, 8])) - [1, 4, 6, 2, 5, 7] - - For a version that doesn't terminate after the shortest iterable is - exhausted, see :func:`interleave_longest`. - - """ - return chain.from_iterable(zip(*iterables)) - - -def interleave_longest(*iterables): - """Return a new iterable yielding from each iterable in turn, - skipping any that are exhausted. - - >>> list(interleave_longest([1, 2, 3], [4, 5], [6, 7, 8])) - [1, 4, 6, 2, 5, 7, 3, 8] - - This function produces the same output as :func:`roundrobin`, but may - perform better for some inputs (in particular when the number of iterables - is large). - - """ - i = chain.from_iterable(zip_longest(*iterables, fillvalue=_marker)) - return (x for x in i if x is not _marker) - - -def collapse(iterable, base_type=None, levels=None): - """Flatten an iterable with multiple levels of nesting (e.g., a list of - lists of tuples) into non-iterable types. - - >>> iterable = [(1, 2), ([3, 4], [[5], [6]])] - >>> list(collapse(iterable)) - [1, 2, 3, 4, 5, 6] - - Binary and text strings are not considered iterable and - will not be collapsed. - - To avoid collapsing other types, specify *base_type*: - - >>> iterable = ['ab', ('cd', 'ef'), ['gh', 'ij']] - >>> list(collapse(iterable, base_type=tuple)) - ['ab', ('cd', 'ef'), 'gh', 'ij'] - - Specify *levels* to stop flattening after a certain level: - - >>> iterable = [('a', ['b']), ('c', ['d'])] - >>> list(collapse(iterable)) # Fully flattened - ['a', 'b', 'c', 'd'] - >>> list(collapse(iterable, levels=1)) # Only one level flattened - ['a', ['b'], 'c', ['d']] - - """ - - def walk(node, level): - if ( - ((levels is not None) and (level > levels)) - or isinstance(node, (str, bytes)) - or ((base_type is not None) and isinstance(node, base_type)) - ): - yield node - return - - try: - tree = iter(node) - except TypeError: - yield node - return - else: - for child in tree: - yield from walk(child, level + 1) - - yield from walk(iterable, 0) - - -def side_effect(func, iterable, chunk_size=None, before=None, after=None): - """Invoke *func* on each item in *iterable* (or on each *chunk_size* group - of items) before yielding the item. - - `func` must be a function that takes a single argument. Its return value - will be discarded. - - *before* and *after* are optional functions that take no arguments. They - will be executed before iteration starts and after it ends, respectively. - - `side_effect` can be used for logging, updating progress bars, or anything - that is not functionally "pure." - - Emitting a status message: - - >>> from more_itertools import consume - >>> func = lambda item: print('Received {}'.format(item)) - >>> consume(side_effect(func, range(2))) - Received 0 - Received 1 - - Operating on chunks of items: - - >>> pair_sums = [] - >>> func = lambda chunk: pair_sums.append(sum(chunk)) - >>> list(side_effect(func, [0, 1, 2, 3, 4, 5], 2)) - [0, 1, 2, 3, 4, 5] - >>> list(pair_sums) - [1, 5, 9] - - Writing to a file-like object: - - >>> from io import StringIO - >>> from more_itertools import consume - >>> f = StringIO() - >>> func = lambda x: print(x, file=f) - >>> before = lambda: print(u'HEADER', file=f) - >>> after = f.close - >>> it = [u'a', u'b', u'c'] - >>> consume(side_effect(func, it, before=before, after=after)) - >>> f.closed - True - - """ - try: - if before is not None: - before() - - if chunk_size is None: - for item in iterable: - func(item) - yield item - else: - for chunk in chunked(iterable, chunk_size): - func(chunk) - yield from chunk - finally: - if after is not None: - after() - - -def sliced(seq, n, strict=False): - """Yield slices of length *n* from the sequence *seq*. - - >>> list(sliced((1, 2, 3, 4, 5, 6), 3)) - [(1, 2, 3), (4, 5, 6)] - - By the default, the last yielded slice will have fewer than *n* elements - if the length of *seq* is not divisible by *n*: - - >>> list(sliced((1, 2, 3, 4, 5, 6, 7, 8), 3)) - [(1, 2, 3), (4, 5, 6), (7, 8)] - - If the length of *seq* is not divisible by *n* and *strict* is - ``True``, then ``ValueError`` will be raised before the last - slice is yielded. - - This function will only work for iterables that support slicing. - For non-sliceable iterables, see :func:`chunked`. - - """ - iterator = takewhile(len, (seq[i : i + n] for i in count(0, n))) - if strict: - - def ret(): - for _slice in iterator: - if len(_slice) != n: - raise ValueError("seq is not divisible by n.") - yield _slice - - return iter(ret()) - else: - return iterator - - -def split_at(iterable, pred, maxsplit=-1, keep_separator=False): - """Yield lists of items from *iterable*, where each list is delimited by - an item where callable *pred* returns ``True``. - - >>> list(split_at('abcdcba', lambda x: x == 'b')) - [['a'], ['c', 'd', 'c'], ['a']] - - >>> list(split_at(range(10), lambda n: n % 2 == 1)) - [[0], [2], [4], [6], [8], []] - - At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, - then there is no limit on the number of splits: - - >>> list(split_at(range(10), lambda n: n % 2 == 1, maxsplit=2)) - [[0], [2], [4, 5, 6, 7, 8, 9]] - - By default, the delimiting items are not included in the output. - The include them, set *keep_separator* to ``True``. - - >>> list(split_at('abcdcba', lambda x: x == 'b', keep_separator=True)) - [['a'], ['b'], ['c', 'd', 'c'], ['b'], ['a']] - - """ - if maxsplit == 0: - yield list(iterable) - return - - buf = [] - it = iter(iterable) - for item in it: - if pred(item): - yield buf - if keep_separator: - yield [item] - if maxsplit == 1: - yield list(it) - return - buf = [] - maxsplit -= 1 - else: - buf.append(item) - yield buf - - -def split_before(iterable, pred, maxsplit=-1): - """Yield lists of items from *iterable*, where each list ends just before - an item for which callable *pred* returns ``True``: - - >>> list(split_before('OneTwo', lambda s: s.isupper())) - [['O', 'n', 'e'], ['T', 'w', 'o']] - - >>> list(split_before(range(10), lambda n: n % 3 == 0)) - [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] - - At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, - then there is no limit on the number of splits: - - >>> list(split_before(range(10), lambda n: n % 3 == 0, maxsplit=2)) - [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]] - """ - if maxsplit == 0: - yield list(iterable) - return - - buf = [] - it = iter(iterable) - for item in it: - if pred(item) and buf: - yield buf - if maxsplit == 1: - yield [item] + list(it) - return - buf = [] - maxsplit -= 1 - buf.append(item) - if buf: - yield buf - - -def split_after(iterable, pred, maxsplit=-1): - """Yield lists of items from *iterable*, where each list ends with an - item where callable *pred* returns ``True``: - - >>> list(split_after('one1two2', lambda s: s.isdigit())) - [['o', 'n', 'e', '1'], ['t', 'w', 'o', '2']] - - >>> list(split_after(range(10), lambda n: n % 3 == 0)) - [[0], [1, 2, 3], [4, 5, 6], [7, 8, 9]] - - At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, - then there is no limit on the number of splits: - - >>> list(split_after(range(10), lambda n: n % 3 == 0, maxsplit=2)) - [[0], [1, 2, 3], [4, 5, 6, 7, 8, 9]] - - """ - if maxsplit == 0: - yield list(iterable) - return - - buf = [] - it = iter(iterable) - for item in it: - buf.append(item) - if pred(item) and buf: - yield buf - if maxsplit == 1: - yield list(it) - return - buf = [] - maxsplit -= 1 - if buf: - yield buf - - -def split_when(iterable, pred, maxsplit=-1): - """Split *iterable* into pieces based on the output of *pred*. - *pred* should be a function that takes successive pairs of items and - returns ``True`` if the iterable should be split in between them. - - For example, to find runs of increasing numbers, split the iterable when - element ``i`` is larger than element ``i + 1``: - - >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], lambda x, y: x > y)) - [[1, 2, 3, 3], [2, 5], [2, 4], [2]] - - At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, - then there is no limit on the number of splits: - - >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], - ... lambda x, y: x > y, maxsplit=2)) - [[1, 2, 3, 3], [2, 5], [2, 4, 2]] - - """ - if maxsplit == 0: - yield list(iterable) - return - - it = iter(iterable) - try: - cur_item = next(it) - except StopIteration: - return - - buf = [cur_item] - for next_item in it: - if pred(cur_item, next_item): - yield buf - if maxsplit == 1: - yield [next_item] + list(it) - return - buf = [] - maxsplit -= 1 - - buf.append(next_item) - cur_item = next_item - - yield buf - - -def split_into(iterable, sizes): - """Yield a list of sequential items from *iterable* of length 'n' for each - integer 'n' in *sizes*. - - >>> list(split_into([1,2,3,4,5,6], [1,2,3])) - [[1], [2, 3], [4, 5, 6]] - - If the sum of *sizes* is smaller than the length of *iterable*, then the - remaining items of *iterable* will not be returned. - - >>> list(split_into([1,2,3,4,5,6], [2,3])) - [[1, 2], [3, 4, 5]] - - If the sum of *sizes* is larger than the length of *iterable*, fewer items - will be returned in the iteration that overruns *iterable* and further - lists will be empty: - - >>> list(split_into([1,2,3,4], [1,2,3,4])) - [[1], [2, 3], [4], []] - - When a ``None`` object is encountered in *sizes*, the returned list will - contain items up to the end of *iterable* the same way that itertools.slice - does: - - >>> list(split_into([1,2,3,4,5,6,7,8,9,0], [2,3,None])) - [[1, 2], [3, 4, 5], [6, 7, 8, 9, 0]] - - :func:`split_into` can be useful for grouping a series of items where the - sizes of the groups are not uniform. An example would be where in a row - from a table, multiple columns represent elements of the same feature - (e.g. a point represented by x,y,z) but, the format is not the same for - all columns. - """ - # convert the iterable argument into an iterator so its contents can - # be consumed by islice in case it is a generator - it = iter(iterable) - - for size in sizes: - if size is None: - yield list(it) - return - else: - yield list(islice(it, size)) - - -def padded(iterable, fillvalue=None, n=None, next_multiple=False): - """Yield the elements from *iterable*, followed by *fillvalue*, such that - at least *n* items are emitted. - - >>> list(padded([1, 2, 3], '?', 5)) - [1, 2, 3, '?', '?'] - - If *next_multiple* is ``True``, *fillvalue* will be emitted until the - number of items emitted is a multiple of *n*:: - - >>> list(padded([1, 2, 3, 4], n=3, next_multiple=True)) - [1, 2, 3, 4, None, None] - - If *n* is ``None``, *fillvalue* will be emitted indefinitely. - - """ - it = iter(iterable) - if n is None: - yield from chain(it, repeat(fillvalue)) - elif n < 1: - raise ValueError('n must be at least 1') - else: - item_count = 0 - for item in it: - yield item - item_count += 1 - - remaining = (n - item_count) % n if next_multiple else n - item_count - for _ in range(remaining): - yield fillvalue - - -def repeat_last(iterable, default=None): - """After the *iterable* is exhausted, keep yielding its last element. - - >>> list(islice(repeat_last(range(3)), 5)) - [0, 1, 2, 2, 2] - - If the iterable is empty, yield *default* forever:: - - >>> list(islice(repeat_last(range(0), 42), 5)) - [42, 42, 42, 42, 42] - - """ - item = _marker - for item in iterable: - yield item - final = default if item is _marker else item - yield from repeat(final) - - -def distribute(n, iterable): - """Distribute the items from *iterable* among *n* smaller iterables. - - >>> group_1, group_2 = distribute(2, [1, 2, 3, 4, 5, 6]) - >>> list(group_1) - [1, 3, 5] - >>> list(group_2) - [2, 4, 6] - - If the length of *iterable* is not evenly divisible by *n*, then the - length of the returned iterables will not be identical: - - >>> children = distribute(3, [1, 2, 3, 4, 5, 6, 7]) - >>> [list(c) for c in children] - [[1, 4, 7], [2, 5], [3, 6]] - - If the length of *iterable* is smaller than *n*, then the last returned - iterables will be empty: - - >>> children = distribute(5, [1, 2, 3]) - >>> [list(c) for c in children] - [[1], [2], [3], [], []] - - This function uses :func:`itertools.tee` and may require significant - storage. If you need the order items in the smaller iterables to match the - original iterable, see :func:`divide`. - - """ - if n < 1: - raise ValueError('n must be at least 1') - - children = tee(iterable, n) - return [islice(it, index, None, n) for index, it in enumerate(children)] - - -def stagger(iterable, offsets=(-1, 0, 1), longest=False, fillvalue=None): - """Yield tuples whose elements are offset from *iterable*. - The amount by which the `i`-th item in each tuple is offset is given by - the `i`-th item in *offsets*. - - >>> list(stagger([0, 1, 2, 3])) - [(None, 0, 1), (0, 1, 2), (1, 2, 3)] - >>> list(stagger(range(8), offsets=(0, 2, 4))) - [(0, 2, 4), (1, 3, 5), (2, 4, 6), (3, 5, 7)] - - By default, the sequence will end when the final element of a tuple is the - last item in the iterable. To continue until the first element of a tuple - is the last item in the iterable, set *longest* to ``True``:: - - >>> list(stagger([0, 1, 2, 3], longest=True)) - [(None, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, None), (3, None, None)] - - By default, ``None`` will be used to replace offsets beyond the end of the - sequence. Specify *fillvalue* to use some other value. - - """ - children = tee(iterable, len(offsets)) - - return zip_offset( - *children, offsets=offsets, longest=longest, fillvalue=fillvalue - ) - - -class UnequalIterablesError(ValueError): - def __init__(self, details=None): - msg = 'Iterables have different lengths' - if details is not None: - msg += (': index 0 has length {}; index {} has length {}').format( - *details - ) - - super().__init__(msg) - - -def _zip_equal_generator(iterables): - for combo in zip_longest(*iterables, fillvalue=_marker): - for val in combo: - if val is _marker: - raise UnequalIterablesError() - yield combo - - -def zip_equal(*iterables): - """``zip`` the input *iterables* together, but raise - ``UnequalIterablesError`` if they aren't all the same length. - - >>> it_1 = range(3) - >>> it_2 = iter('abc') - >>> list(zip_equal(it_1, it_2)) - [(0, 'a'), (1, 'b'), (2, 'c')] - - >>> it_1 = range(3) - >>> it_2 = iter('abcd') - >>> list(zip_equal(it_1, it_2)) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - more_itertools.more.UnequalIterablesError: Iterables have different - lengths - - """ - if hexversion >= 0x30A00A6: - warnings.warn( - ( - 'zip_equal will be removed in a future version of ' - 'more-itertools. Use the builtin zip function with ' - 'strict=True instead.' - ), - DeprecationWarning, - ) - # Check whether the iterables are all the same size. - try: - first_size = len(iterables[0]) - for i, it in enumerate(iterables[1:], 1): - size = len(it) - if size != first_size: - break - else: - # If we didn't break out, we can use the built-in zip. - return zip(*iterables) - - # If we did break out, there was a mismatch. - raise UnequalIterablesError(details=(first_size, i, size)) - # If any one of the iterables didn't have a length, start reading - # them until one runs out. - except TypeError: - return _zip_equal_generator(iterables) - - -def zip_offset(*iterables, offsets, longest=False, fillvalue=None): - """``zip`` the input *iterables* together, but offset the `i`-th iterable - by the `i`-th item in *offsets*. - - >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1))) - [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e')] - - This can be used as a lightweight alternative to SciPy or pandas to analyze - data sets in which some series have a lead or lag relationship. - - By default, the sequence will end when the shortest iterable is exhausted. - To continue until the longest iterable is exhausted, set *longest* to - ``True``. - - >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1), longest=True)) - [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e'), (None, 'f')] - - By default, ``None`` will be used to replace offsets beyond the end of the - sequence. Specify *fillvalue* to use some other value. - - """ - if len(iterables) != len(offsets): - raise ValueError("Number of iterables and offsets didn't match") - - staggered = [] - for it, n in zip(iterables, offsets): - if n < 0: - staggered.append(chain(repeat(fillvalue, -n), it)) - elif n > 0: - staggered.append(islice(it, n, None)) - else: - staggered.append(it) - - if longest: - return zip_longest(*staggered, fillvalue=fillvalue) - - return zip(*staggered) - - -def sort_together(iterables, key_list=(0,), key=None, reverse=False): - """Return the input iterables sorted together, with *key_list* as the - priority for sorting. All iterables are trimmed to the length of the - shortest one. - - This can be used like the sorting function in a spreadsheet. If each - iterable represents a column of data, the key list determines which - columns are used for sorting. - - By default, all iterables are sorted using the ``0``-th iterable:: - - >>> iterables = [(4, 3, 2, 1), ('a', 'b', 'c', 'd')] - >>> sort_together(iterables) - [(1, 2, 3, 4), ('d', 'c', 'b', 'a')] - - Set a different key list to sort according to another iterable. - Specifying multiple keys dictates how ties are broken:: - - >>> iterables = [(3, 1, 2), (0, 1, 0), ('c', 'b', 'a')] - >>> sort_together(iterables, key_list=(1, 2)) - [(2, 3, 1), (0, 0, 1), ('a', 'c', 'b')] - - To sort by a function of the elements of the iterable, pass a *key* - function. Its arguments are the elements of the iterables corresponding to - the key list:: - - >>> names = ('a', 'b', 'c') - >>> lengths = (1, 2, 3) - >>> widths = (5, 2, 1) - >>> def area(length, width): - ... return length * width - >>> sort_together([names, lengths, widths], key_list=(1, 2), key=area) - [('c', 'b', 'a'), (3, 2, 1), (1, 2, 5)] - - Set *reverse* to ``True`` to sort in descending order. - - >>> sort_together([(1, 2, 3), ('c', 'b', 'a')], reverse=True) - [(3, 2, 1), ('a', 'b', 'c')] - - """ - if key is None: - # if there is no key function, the key argument to sorted is an - # itemgetter - key_argument = itemgetter(*key_list) - else: - # if there is a key function, call it with the items at the offsets - # specified by the key function as arguments - key_list = list(key_list) - if len(key_list) == 1: - # if key_list contains a single item, pass the item at that offset - # as the only argument to the key function - key_offset = key_list[0] - key_argument = lambda zipped_items: key(zipped_items[key_offset]) - else: - # if key_list contains multiple items, use itemgetter to return a - # tuple of items, which we pass as *args to the key function - get_key_items = itemgetter(*key_list) - key_argument = lambda zipped_items: key( - *get_key_items(zipped_items) - ) - - return list( - zip(*sorted(zip(*iterables), key=key_argument, reverse=reverse)) - ) - - -def unzip(iterable): - """The inverse of :func:`zip`, this function disaggregates the elements - of the zipped *iterable*. - - The ``i``-th iterable contains the ``i``-th element from each element - of the zipped iterable. The first element is used to to determine the - length of the remaining elements. - - >>> iterable = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] - >>> letters, numbers = unzip(iterable) - >>> list(letters) - ['a', 'b', 'c', 'd'] - >>> list(numbers) - [1, 2, 3, 4] - - This is similar to using ``zip(*iterable)``, but it avoids reading - *iterable* into memory. Note, however, that this function uses - :func:`itertools.tee` and thus may require significant storage. - - """ - head, iterable = spy(iter(iterable)) - if not head: - # empty iterable, e.g. zip([], [], []) - return () - # spy returns a one-length iterable as head - head = head[0] - iterables = tee(iterable, len(head)) - - def itemgetter(i): - def getter(obj): - try: - return obj[i] - except IndexError: - # basically if we have an iterable like - # iter([(1, 2, 3), (4, 5), (6,)]) - # the second unzipped iterable would fail at the third tuple - # since it would try to access tup[1] - # same with the third unzipped iterable and the second tuple - # to support these "improperly zipped" iterables, - # we create a custom itemgetter - # which just stops the unzipped iterables - # at first length mismatch - raise StopIteration - - return getter - - return tuple(map(itemgetter(i), it) for i, it in enumerate(iterables)) - - -def divide(n, iterable): - """Divide the elements from *iterable* into *n* parts, maintaining - order. - - >>> group_1, group_2 = divide(2, [1, 2, 3, 4, 5, 6]) - >>> list(group_1) - [1, 2, 3] - >>> list(group_2) - [4, 5, 6] - - If the length of *iterable* is not evenly divisible by *n*, then the - length of the returned iterables will not be identical: - - >>> children = divide(3, [1, 2, 3, 4, 5, 6, 7]) - >>> [list(c) for c in children] - [[1, 2, 3], [4, 5], [6, 7]] - - If the length of the iterable is smaller than n, then the last returned - iterables will be empty: - - >>> children = divide(5, [1, 2, 3]) - >>> [list(c) for c in children] - [[1], [2], [3], [], []] - - This function will exhaust the iterable before returning and may require - significant storage. If order is not important, see :func:`distribute`, - which does not first pull the iterable into memory. - - """ - if n < 1: - raise ValueError('n must be at least 1') - - try: - iterable[:0] - except TypeError: - seq = tuple(iterable) - else: - seq = iterable - - q, r = divmod(len(seq), n) - - ret = [] - stop = 0 - for i in range(1, n + 1): - start = stop - stop += q + 1 if i <= r else q - ret.append(iter(seq[start:stop])) - - return ret - - -def always_iterable(obj, base_type=(str, bytes)): - """If *obj* is iterable, return an iterator over its items:: - - >>> obj = (1, 2, 3) - >>> list(always_iterable(obj)) - [1, 2, 3] - - If *obj* is not iterable, return a one-item iterable containing *obj*:: - - >>> obj = 1 - >>> list(always_iterable(obj)) - [1] - - If *obj* is ``None``, return an empty iterable: - - >>> obj = None - >>> list(always_iterable(None)) - [] - - By default, binary and text strings are not considered iterable:: - - >>> obj = 'foo' - >>> list(always_iterable(obj)) - ['foo'] - - If *base_type* is set, objects for which ``isinstance(obj, base_type)`` - returns ``True`` won't be considered iterable. - - >>> obj = {'a': 1} - >>> list(always_iterable(obj)) # Iterate over the dict's keys - ['a'] - >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit - [{'a': 1}] - - Set *base_type* to ``None`` to avoid any special handling and treat objects - Python considers iterable as iterable: - - >>> obj = 'foo' - >>> list(always_iterable(obj, base_type=None)) - ['f', 'o', 'o'] - """ - if obj is None: - return iter(()) - - if (base_type is not None) and isinstance(obj, base_type): - return iter((obj,)) - - try: - return iter(obj) - except TypeError: - return iter((obj,)) - - -def adjacent(predicate, iterable, distance=1): - """Return an iterable over `(bool, item)` tuples where the `item` is - drawn from *iterable* and the `bool` indicates whether - that item satisfies the *predicate* or is adjacent to an item that does. - - For example, to find whether items are adjacent to a ``3``:: - - >>> list(adjacent(lambda x: x == 3, range(6))) - [(False, 0), (False, 1), (True, 2), (True, 3), (True, 4), (False, 5)] - - Set *distance* to change what counts as adjacent. For example, to find - whether items are two places away from a ``3``: - - >>> list(adjacent(lambda x: x == 3, range(6), distance=2)) - [(False, 0), (True, 1), (True, 2), (True, 3), (True, 4), (True, 5)] - - This is useful for contextualizing the results of a search function. - For example, a code comparison tool might want to identify lines that - have changed, but also surrounding lines to give the viewer of the diff - context. - - The predicate function will only be called once for each item in the - iterable. - - See also :func:`groupby_transform`, which can be used with this function - to group ranges of items with the same `bool` value. - - """ - # Allow distance=0 mainly for testing that it reproduces results with map() - if distance < 0: - raise ValueError('distance must be at least 0') - - i1, i2 = tee(iterable) - padding = [False] * distance - selected = chain(padding, map(predicate, i1), padding) - adjacent_to_selected = map(any, windowed(selected, 2 * distance + 1)) - return zip(adjacent_to_selected, i2) - - -def groupby_transform(iterable, keyfunc=None, valuefunc=None, reducefunc=None): - """An extension of :func:`itertools.groupby` that can apply transformations - to the grouped data. - - * *keyfunc* is a function computing a key value for each item in *iterable* - * *valuefunc* is a function that transforms the individual items from - *iterable* after grouping - * *reducefunc* is a function that transforms each group of items - - >>> iterable = 'aAAbBBcCC' - >>> keyfunc = lambda k: k.upper() - >>> valuefunc = lambda v: v.lower() - >>> reducefunc = lambda g: ''.join(g) - >>> list(groupby_transform(iterable, keyfunc, valuefunc, reducefunc)) - [('A', 'aaa'), ('B', 'bbb'), ('C', 'ccc')] - - Each optional argument defaults to an identity function if not specified. - - :func:`groupby_transform` is useful when grouping elements of an iterable - using a separate iterable as the key. To do this, :func:`zip` the iterables - and pass a *keyfunc* that extracts the first element and a *valuefunc* - that extracts the second element:: - - >>> from operator import itemgetter - >>> keys = [0, 0, 1, 1, 1, 2, 2, 2, 3] - >>> values = 'abcdefghi' - >>> iterable = zip(keys, values) - >>> grouper = groupby_transform(iterable, itemgetter(0), itemgetter(1)) - >>> [(k, ''.join(g)) for k, g in grouper] - [(0, 'ab'), (1, 'cde'), (2, 'fgh'), (3, 'i')] - - Note that the order of items in the iterable is significant. - Only adjacent items are grouped together, so if you don't want any - duplicate groups, you should sort the iterable by the key function. - - """ - ret = groupby(iterable, keyfunc) - if valuefunc: - ret = ((k, map(valuefunc, g)) for k, g in ret) - if reducefunc: - ret = ((k, reducefunc(g)) for k, g in ret) - - return ret - - -class numeric_range(abc.Sequence, abc.Hashable): - """An extension of the built-in ``range()`` function whose arguments can - be any orderable numeric type. - - With only *stop* specified, *start* defaults to ``0`` and *step* - defaults to ``1``. The output items will match the type of *stop*: - - >>> list(numeric_range(3.5)) - [0.0, 1.0, 2.0, 3.0] - - With only *start* and *stop* specified, *step* defaults to ``1``. The - output items will match the type of *start*: - - >>> from decimal import Decimal - >>> start = Decimal('2.1') - >>> stop = Decimal('5.1') - >>> list(numeric_range(start, stop)) - [Decimal('2.1'), Decimal('3.1'), Decimal('4.1')] - - With *start*, *stop*, and *step* specified the output items will match - the type of ``start + step``: - - >>> from fractions import Fraction - >>> start = Fraction(1, 2) # Start at 1/2 - >>> stop = Fraction(5, 2) # End at 5/2 - >>> step = Fraction(1, 2) # Count by 1/2 - >>> list(numeric_range(start, stop, step)) - [Fraction(1, 2), Fraction(1, 1), Fraction(3, 2), Fraction(2, 1)] - - If *step* is zero, ``ValueError`` is raised. Negative steps are supported: - - >>> list(numeric_range(3, -1, -1.0)) - [3.0, 2.0, 1.0, 0.0] - - Be aware of the limitations of floating point numbers; the representation - of the yielded numbers may be surprising. - - ``datetime.datetime`` objects can be used for *start* and *stop*, if *step* - is a ``datetime.timedelta`` object: - - >>> import datetime - >>> start = datetime.datetime(2019, 1, 1) - >>> stop = datetime.datetime(2019, 1, 3) - >>> step = datetime.timedelta(days=1) - >>> items = iter(numeric_range(start, stop, step)) - >>> next(items) - datetime.datetime(2019, 1, 1, 0, 0) - >>> next(items) - datetime.datetime(2019, 1, 2, 0, 0) - - """ - - _EMPTY_HASH = hash(range(0, 0)) - - def __init__(self, *args): - argc = len(args) - if argc == 1: - (self._stop,) = args - self._start = type(self._stop)(0) - self._step = type(self._stop - self._start)(1) - elif argc == 2: - self._start, self._stop = args - self._step = type(self._stop - self._start)(1) - elif argc == 3: - self._start, self._stop, self._step = args - elif argc == 0: - raise TypeError( - 'numeric_range expected at least ' - '1 argument, got {}'.format(argc) - ) - else: - raise TypeError( - 'numeric_range expected at most ' - '3 arguments, got {}'.format(argc) - ) - - self._zero = type(self._step)(0) - if self._step == self._zero: - raise ValueError('numeric_range() arg 3 must not be zero') - self._growing = self._step > self._zero - self._init_len() - - def __bool__(self): - if self._growing: - return self._start < self._stop - else: - return self._start > self._stop - - def __contains__(self, elem): - if self._growing: - if self._start <= elem < self._stop: - return (elem - self._start) % self._step == self._zero - else: - if self._start >= elem > self._stop: - return (self._start - elem) % (-self._step) == self._zero - - return False - - def __eq__(self, other): - if isinstance(other, numeric_range): - empty_self = not bool(self) - empty_other = not bool(other) - if empty_self or empty_other: - return empty_self and empty_other # True if both empty - else: - return ( - self._start == other._start - and self._step == other._step - and self._get_by_index(-1) == other._get_by_index(-1) - ) - else: - return False - - def __getitem__(self, key): - if isinstance(key, int): - return self._get_by_index(key) - elif isinstance(key, slice): - step = self._step if key.step is None else key.step * self._step - - if key.start is None or key.start <= -self._len: - start = self._start - elif key.start >= self._len: - start = self._stop - else: # -self._len < key.start < self._len - start = self._get_by_index(key.start) - - if key.stop is None or key.stop >= self._len: - stop = self._stop - elif key.stop <= -self._len: - stop = self._start - else: # -self._len < key.stop < self._len - stop = self._get_by_index(key.stop) - - return numeric_range(start, stop, step) - else: - raise TypeError( - 'numeric range indices must be ' - 'integers or slices, not {}'.format(type(key).__name__) - ) - - def __hash__(self): - if self: - return hash((self._start, self._get_by_index(-1), self._step)) - else: - return self._EMPTY_HASH - - def __iter__(self): - values = (self._start + (n * self._step) for n in count()) - if self._growing: - return takewhile(partial(gt, self._stop), values) - else: - return takewhile(partial(lt, self._stop), values) - - def __len__(self): - return self._len - - def _init_len(self): - if self._growing: - start = self._start - stop = self._stop - step = self._step - else: - start = self._stop - stop = self._start - step = -self._step - distance = stop - start - if distance <= self._zero: - self._len = 0 - else: # distance > 0 and step > 0: regular euclidean division - q, r = divmod(distance, step) - self._len = int(q) + int(r != self._zero) - - def __reduce__(self): - return numeric_range, (self._start, self._stop, self._step) - - def __repr__(self): - if self._step == 1: - return "numeric_range({}, {})".format( - repr(self._start), repr(self._stop) - ) - else: - return "numeric_range({}, {}, {})".format( - repr(self._start), repr(self._stop), repr(self._step) - ) - - def __reversed__(self): - return iter( - numeric_range( - self._get_by_index(-1), self._start - self._step, -self._step - ) - ) - - def count(self, value): - return int(value in self) - - def index(self, value): - if self._growing: - if self._start <= value < self._stop: - q, r = divmod(value - self._start, self._step) - if r == self._zero: - return int(q) - else: - if self._start >= value > self._stop: - q, r = divmod(self._start - value, -self._step) - if r == self._zero: - return int(q) - - raise ValueError("{} is not in numeric range".format(value)) - - def _get_by_index(self, i): - if i < 0: - i += self._len - if i < 0 or i >= self._len: - raise IndexError("numeric range object index out of range") - return self._start + i * self._step - - -def count_cycle(iterable, n=None): - """Cycle through the items from *iterable* up to *n* times, yielding - the number of completed cycles along with each item. If *n* is omitted the - process repeats indefinitely. - - >>> list(count_cycle('AB', 3)) - [(0, 'A'), (0, 'B'), (1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')] - - """ - iterable = tuple(iterable) - if not iterable: - return iter(()) - counter = count() if n is None else range(n) - return ((i, item) for i in counter for item in iterable) - - -def mark_ends(iterable): - """Yield 3-tuples of the form ``(is_first, is_last, item)``. - - >>> list(mark_ends('ABC')) - [(True, False, 'A'), (False, False, 'B'), (False, True, 'C')] - - Use this when looping over an iterable to take special action on its first - and/or last items: - - >>> iterable = ['Header', 100, 200, 'Footer'] - >>> total = 0 - >>> for is_first, is_last, item in mark_ends(iterable): - ... if is_first: - ... continue # Skip the header - ... if is_last: - ... continue # Skip the footer - ... total += item - >>> print(total) - 300 - """ - it = iter(iterable) - - try: - b = next(it) - except StopIteration: - return - - try: - for i in count(): - a = b - b = next(it) - yield i == 0, False, a - - except StopIteration: - yield i == 0, True, a - - -def locate(iterable, pred=bool, window_size=None): - """Yield the index of each item in *iterable* for which *pred* returns - ``True``. - - *pred* defaults to :func:`bool`, which will select truthy items: - - >>> list(locate([0, 1, 1, 0, 1, 0, 0])) - [1, 2, 4] - - Set *pred* to a custom function to, e.g., find the indexes for a particular - item. - - >>> list(locate(['a', 'b', 'c', 'b'], lambda x: x == 'b')) - [1, 3] - - If *window_size* is given, then the *pred* function will be called with - that many items. This enables searching for sub-sequences: - - >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] - >>> pred = lambda *args: args == (1, 2, 3) - >>> list(locate(iterable, pred=pred, window_size=3)) - [1, 5, 9] - - Use with :func:`seekable` to find indexes and then retrieve the associated - items: - - >>> from itertools import count - >>> from more_itertools import seekable - >>> source = (3 * n + 1 if (n % 2) else n // 2 for n in count()) - >>> it = seekable(source) - >>> pred = lambda x: x > 100 - >>> indexes = locate(it, pred=pred) - >>> i = next(indexes) - >>> it.seek(i) - >>> next(it) - 106 - - """ - if window_size is None: - return compress(count(), map(pred, iterable)) - - if window_size < 1: - raise ValueError('window size must be at least 1') - - it = windowed(iterable, window_size, fillvalue=_marker) - return compress(count(), starmap(pred, it)) - - -def lstrip(iterable, pred): - """Yield the items from *iterable*, but strip any from the beginning - for which *pred* returns ``True``. - - For example, to remove a set of items from the start of an iterable: - - >>> iterable = (None, False, None, 1, 2, None, 3, False, None) - >>> pred = lambda x: x in {None, False, ''} - >>> list(lstrip(iterable, pred)) - [1, 2, None, 3, False, None] - - This function is analogous to to :func:`str.lstrip`, and is essentially - an wrapper for :func:`itertools.dropwhile`. - - """ - return dropwhile(pred, iterable) - - -def rstrip(iterable, pred): - """Yield the items from *iterable*, but strip any from the end - for which *pred* returns ``True``. - - For example, to remove a set of items from the end of an iterable: - - >>> iterable = (None, False, None, 1, 2, None, 3, False, None) - >>> pred = lambda x: x in {None, False, ''} - >>> list(rstrip(iterable, pred)) - [None, False, None, 1, 2, None, 3] - - This function is analogous to :func:`str.rstrip`. - - """ - cache = [] - cache_append = cache.append - cache_clear = cache.clear - for x in iterable: - if pred(x): - cache_append(x) - else: - yield from cache - cache_clear() - yield x - - -def strip(iterable, pred): - """Yield the items from *iterable*, but strip any from the - beginning and end for which *pred* returns ``True``. - - For example, to remove a set of items from both ends of an iterable: - - >>> iterable = (None, False, None, 1, 2, None, 3, False, None) - >>> pred = lambda x: x in {None, False, ''} - >>> list(strip(iterable, pred)) - [1, 2, None, 3] - - This function is analogous to :func:`str.strip`. - - """ - return rstrip(lstrip(iterable, pred), pred) - - -class islice_extended: - """An extension of :func:`itertools.islice` that supports negative values - for *stop*, *start*, and *step*. - - >>> iterable = iter('abcdefgh') - >>> list(islice_extended(iterable, -4, -1)) - ['e', 'f', 'g'] - - Slices with negative values require some caching of *iterable*, but this - function takes care to minimize the amount of memory required. - - For example, you can use a negative step with an infinite iterator: - - >>> from itertools import count - >>> list(islice_extended(count(), 110, 99, -2)) - [110, 108, 106, 104, 102, 100] - - You can also use slice notation directly: - - >>> iterable = map(str, count()) - >>> it = islice_extended(iterable)[10:20:2] - >>> list(it) - ['10', '12', '14', '16', '18'] - - """ - - def __init__(self, iterable, *args): - it = iter(iterable) - if args: - self._iterable = _islice_helper(it, slice(*args)) - else: - self._iterable = it - - def __iter__(self): - return self - - def __next__(self): - return next(self._iterable) - - def __getitem__(self, key): - if isinstance(key, slice): - return islice_extended(_islice_helper(self._iterable, key)) - - raise TypeError('islice_extended.__getitem__ argument must be a slice') - - -def _islice_helper(it, s): - start = s.start - stop = s.stop - if s.step == 0: - raise ValueError('step argument must be a non-zero integer or None.') - step = s.step or 1 - - if step > 0: - start = 0 if (start is None) else start - - if start < 0: - # Consume all but the last -start items - cache = deque(enumerate(it, 1), maxlen=-start) - len_iter = cache[-1][0] if cache else 0 - - # Adjust start to be positive - i = max(len_iter + start, 0) - - # Adjust stop to be positive - if stop is None: - j = len_iter - elif stop >= 0: - j = min(stop, len_iter) - else: - j = max(len_iter + stop, 0) - - # Slice the cache - n = j - i - if n <= 0: - return - - for index, item in islice(cache, 0, n, step): - yield item - elif (stop is not None) and (stop < 0): - # Advance to the start position - next(islice(it, start, start), None) - - # When stop is negative, we have to carry -stop items while - # iterating - cache = deque(islice(it, -stop), maxlen=-stop) - - for index, item in enumerate(it): - cached_item = cache.popleft() - if index % step == 0: - yield cached_item - cache.append(item) - else: - # When both start and stop are positive we have the normal case - yield from islice(it, start, stop, step) - else: - start = -1 if (start is None) else start - - if (stop is not None) and (stop < 0): - # Consume all but the last items - n = -stop - 1 - cache = deque(enumerate(it, 1), maxlen=n) - len_iter = cache[-1][0] if cache else 0 - - # If start and stop are both negative they are comparable and - # we can just slice. Otherwise we can adjust start to be negative - # and then slice. - if start < 0: - i, j = start, stop - else: - i, j = min(start - len_iter, -1), None - - for index, item in list(cache)[i:j:step]: - yield item - else: - # Advance to the stop position - if stop is not None: - m = stop + 1 - next(islice(it, m, m), None) - - # stop is positive, so if start is negative they are not comparable - # and we need the rest of the items. - if start < 0: - i = start - n = None - # stop is None and start is positive, so we just need items up to - # the start index. - elif stop is None: - i = None - n = start + 1 - # Both stop and start are positive, so they are comparable. - else: - i = None - n = start - stop - if n <= 0: - return - - cache = list(islice(it, n)) - - yield from cache[i::step] - - -def always_reversible(iterable): - """An extension of :func:`reversed` that supports all iterables, not - just those which implement the ``Reversible`` or ``Sequence`` protocols. - - >>> print(*always_reversible(x for x in range(3))) - 2 1 0 - - If the iterable is already reversible, this function returns the - result of :func:`reversed()`. If the iterable is not reversible, - this function will cache the remaining items in the iterable and - yield them in reverse order, which may require significant storage. - """ - try: - return reversed(iterable) - except TypeError: - return reversed(list(iterable)) - - -def consecutive_groups(iterable, ordering=lambda x: x): - """Yield groups of consecutive items using :func:`itertools.groupby`. - The *ordering* function determines whether two items are adjacent by - returning their position. - - By default, the ordering function is the identity function. This is - suitable for finding runs of numbers: - - >>> iterable = [1, 10, 11, 12, 20, 30, 31, 32, 33, 40] - >>> for group in consecutive_groups(iterable): - ... print(list(group)) - [1] - [10, 11, 12] - [20] - [30, 31, 32, 33] - [40] - - For finding runs of adjacent letters, try using the :meth:`index` method - of a string of letters: - - >>> from string import ascii_lowercase - >>> iterable = 'abcdfgilmnop' - >>> ordering = ascii_lowercase.index - >>> for group in consecutive_groups(iterable, ordering): - ... print(list(group)) - ['a', 'b', 'c', 'd'] - ['f', 'g'] - ['i'] - ['l', 'm', 'n', 'o', 'p'] - - Each group of consecutive items is an iterator that shares it source with - *iterable*. When an an output group is advanced, the previous group is - no longer available unless its elements are copied (e.g., into a ``list``). - - >>> iterable = [1, 2, 11, 12, 21, 22] - >>> saved_groups = [] - >>> for group in consecutive_groups(iterable): - ... saved_groups.append(list(group)) # Copy group elements - >>> saved_groups - [[1, 2], [11, 12], [21, 22]] - - """ - for k, g in groupby( - enumerate(iterable), key=lambda x: x[0] - ordering(x[1]) - ): - yield map(itemgetter(1), g) - - -def difference(iterable, func=sub, *, initial=None): - """This function is the inverse of :func:`itertools.accumulate`. By default - it will compute the first difference of *iterable* using - :func:`operator.sub`: - - >>> from itertools import accumulate - >>> iterable = accumulate([0, 1, 2, 3, 4]) # produces 0, 1, 3, 6, 10 - >>> list(difference(iterable)) - [0, 1, 2, 3, 4] - - *func* defaults to :func:`operator.sub`, but other functions can be - specified. They will be applied as follows:: - - A, B, C, D, ... --> A, func(B, A), func(C, B), func(D, C), ... - - For example, to do progressive division: - - >>> iterable = [1, 2, 6, 24, 120] - >>> func = lambda x, y: x // y - >>> list(difference(iterable, func)) - [1, 2, 3, 4, 5] - - If the *initial* keyword is set, the first element will be skipped when - computing successive differences. - - >>> it = [10, 11, 13, 16] # from accumulate([1, 2, 3], initial=10) - >>> list(difference(it, initial=10)) - [1, 2, 3] - - """ - a, b = tee(iterable) - try: - first = [next(b)] - except StopIteration: - return iter([]) - - if initial is not None: - first = [] - - return chain(first, starmap(func, zip(b, a))) - - -class SequenceView(Sequence): - """Return a read-only view of the sequence object *target*. - - :class:`SequenceView` objects are analogous to Python's built-in - "dictionary view" types. They provide a dynamic view of a sequence's items, - meaning that when the sequence updates, so does the view. - - >>> seq = ['0', '1', '2'] - >>> view = SequenceView(seq) - >>> view - SequenceView(['0', '1', '2']) - >>> seq.append('3') - >>> view - SequenceView(['0', '1', '2', '3']) - - Sequence views support indexing, slicing, and length queries. They act - like the underlying sequence, except they don't allow assignment: - - >>> view[1] - '1' - >>> view[1:-1] - ['1', '2'] - >>> len(view) - 4 - - Sequence views are useful as an alternative to copying, as they don't - require (much) extra storage. - - """ - - def __init__(self, target): - if not isinstance(target, Sequence): - raise TypeError - self._target = target - - def __getitem__(self, index): - return self._target[index] - - def __len__(self): - return len(self._target) - - def __repr__(self): - return '{}({})'.format(self.__class__.__name__, repr(self._target)) - - -class seekable: - """Wrap an iterator to allow for seeking backward and forward. This - progressively caches the items in the source iterable so they can be - re-visited. - - Call :meth:`seek` with an index to seek to that position in the source - iterable. - - To "reset" an iterator, seek to ``0``: - - >>> from itertools import count - >>> it = seekable((str(n) for n in count())) - >>> next(it), next(it), next(it) - ('0', '1', '2') - >>> it.seek(0) - >>> next(it), next(it), next(it) - ('0', '1', '2') - >>> next(it) - '3' - - You can also seek forward: - - >>> it = seekable((str(n) for n in range(20))) - >>> it.seek(10) - >>> next(it) - '10' - >>> it.seek(20) # Seeking past the end of the source isn't a problem - >>> list(it) - [] - >>> it.seek(0) # Resetting works even after hitting the end - >>> next(it), next(it), next(it) - ('0', '1', '2') - - Call :meth:`peek` to look ahead one item without advancing the iterator: - - >>> it = seekable('1234') - >>> it.peek() - '1' - >>> list(it) - ['1', '2', '3', '4'] - >>> it.peek(default='empty') - 'empty' - - Before the iterator is at its end, calling :func:`bool` on it will return - ``True``. After it will return ``False``: - - >>> it = seekable('5678') - >>> bool(it) - True - >>> list(it) - ['5', '6', '7', '8'] - >>> bool(it) - False - - You may view the contents of the cache with the :meth:`elements` method. - That returns a :class:`SequenceView`, a view that updates automatically: - - >>> it = seekable((str(n) for n in range(10))) - >>> next(it), next(it), next(it) - ('0', '1', '2') - >>> elements = it.elements() - >>> elements - SequenceView(['0', '1', '2']) - >>> next(it) - '3' - >>> elements - SequenceView(['0', '1', '2', '3']) - - By default, the cache grows as the source iterable progresses, so beware of - wrapping very large or infinite iterables. Supply *maxlen* to limit the - size of the cache (this of course limits how far back you can seek). - - >>> from itertools import count - >>> it = seekable((str(n) for n in count()), maxlen=2) - >>> next(it), next(it), next(it), next(it) - ('0', '1', '2', '3') - >>> list(it.elements()) - ['2', '3'] - >>> it.seek(0) - >>> next(it), next(it), next(it), next(it) - ('2', '3', '4', '5') - >>> next(it) - '6' - - """ - - def __init__(self, iterable, maxlen=None): - self._source = iter(iterable) - if maxlen is None: - self._cache = [] - else: - self._cache = deque([], maxlen) - self._index = None - - def __iter__(self): - return self - - def __next__(self): - if self._index is not None: - try: - item = self._cache[self._index] - except IndexError: - self._index = None - else: - self._index += 1 - return item - - item = next(self._source) - self._cache.append(item) - return item - - def __bool__(self): - try: - self.peek() - except StopIteration: - return False - return True - - def peek(self, default=_marker): - try: - peeked = next(self) - except StopIteration: - if default is _marker: - raise - return default - if self._index is None: - self._index = len(self._cache) - self._index -= 1 - return peeked - - def elements(self): - return SequenceView(self._cache) - - def seek(self, index): - self._index = index - remainder = index - len(self._cache) - if remainder > 0: - consume(self, remainder) - - -class run_length: - """ - :func:`run_length.encode` compresses an iterable with run-length encoding. - It yields groups of repeated items with the count of how many times they - were repeated: - - >>> uncompressed = 'abbcccdddd' - >>> list(run_length.encode(uncompressed)) - [('a', 1), ('b', 2), ('c', 3), ('d', 4)] - - :func:`run_length.decode` decompresses an iterable that was previously - compressed with run-length encoding. It yields the items of the - decompressed iterable: - - >>> compressed = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] - >>> list(run_length.decode(compressed)) - ['a', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd', 'd'] - - """ - - @staticmethod - def encode(iterable): - return ((k, ilen(g)) for k, g in groupby(iterable)) - - @staticmethod - def decode(iterable): - return chain.from_iterable(repeat(k, n) for k, n in iterable) - - -def exactly_n(iterable, n, predicate=bool): - """Return ``True`` if exactly ``n`` items in the iterable are ``True`` - according to the *predicate* function. - - >>> exactly_n([True, True, False], 2) - True - >>> exactly_n([True, True, False], 1) - False - >>> exactly_n([0, 1, 2, 3, 4, 5], 3, lambda x: x < 3) - True - - The iterable will be advanced until ``n + 1`` truthy items are encountered, - so avoid calling it on infinite iterables. - - """ - return len(take(n + 1, filter(predicate, iterable))) == n - - -def circular_shifts(iterable): - """Return a list of circular shifts of *iterable*. - - >>> circular_shifts(range(4)) - [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)] - """ - lst = list(iterable) - return take(len(lst), windowed(cycle(lst), len(lst))) - - -def make_decorator(wrapping_func, result_index=0): - """Return a decorator version of *wrapping_func*, which is a function that - modifies an iterable. *result_index* is the position in that function's - signature where the iterable goes. - - This lets you use itertools on the "production end," i.e. at function - definition. This can augment what the function returns without changing the - function's code. - - For example, to produce a decorator version of :func:`chunked`: - - >>> from more_itertools import chunked - >>> chunker = make_decorator(chunked, result_index=0) - >>> @chunker(3) - ... def iter_range(n): - ... return iter(range(n)) - ... - >>> list(iter_range(9)) - [[0, 1, 2], [3, 4, 5], [6, 7, 8]] - - To only allow truthy items to be returned: - - >>> truth_serum = make_decorator(filter, result_index=1) - >>> @truth_serum(bool) - ... def boolean_test(): - ... return [0, 1, '', ' ', False, True] - ... - >>> list(boolean_test()) - [1, ' ', True] - - The :func:`peekable` and :func:`seekable` wrappers make for practical - decorators: - - >>> from more_itertools import peekable - >>> peekable_function = make_decorator(peekable) - >>> @peekable_function() - ... def str_range(*args): - ... return (str(x) for x in range(*args)) - ... - >>> it = str_range(1, 20, 2) - >>> next(it), next(it), next(it) - ('1', '3', '5') - >>> it.peek() - '7' - >>> next(it) - '7' - - """ - # See https://sites.google.com/site/bbayles/index/decorator_factory for - # notes on how this works. - def decorator(*wrapping_args, **wrapping_kwargs): - def outer_wrapper(f): - def inner_wrapper(*args, **kwargs): - result = f(*args, **kwargs) - wrapping_args_ = list(wrapping_args) - wrapping_args_.insert(result_index, result) - return wrapping_func(*wrapping_args_, **wrapping_kwargs) - - return inner_wrapper - - return outer_wrapper - - return decorator - - -def map_reduce(iterable, keyfunc, valuefunc=None, reducefunc=None): - """Return a dictionary that maps the items in *iterable* to categories - defined by *keyfunc*, transforms them with *valuefunc*, and - then summarizes them by category with *reducefunc*. - - *valuefunc* defaults to the identity function if it is unspecified. - If *reducefunc* is unspecified, no summarization takes place: - - >>> keyfunc = lambda x: x.upper() - >>> result = map_reduce('abbccc', keyfunc) - >>> sorted(result.items()) - [('A', ['a']), ('B', ['b', 'b']), ('C', ['c', 'c', 'c'])] - - Specifying *valuefunc* transforms the categorized items: - - >>> keyfunc = lambda x: x.upper() - >>> valuefunc = lambda x: 1 - >>> result = map_reduce('abbccc', keyfunc, valuefunc) - >>> sorted(result.items()) - [('A', [1]), ('B', [1, 1]), ('C', [1, 1, 1])] - - Specifying *reducefunc* summarizes the categorized items: - - >>> keyfunc = lambda x: x.upper() - >>> valuefunc = lambda x: 1 - >>> reducefunc = sum - >>> result = map_reduce('abbccc', keyfunc, valuefunc, reducefunc) - >>> sorted(result.items()) - [('A', 1), ('B', 2), ('C', 3)] - - You may want to filter the input iterable before applying the map/reduce - procedure: - - >>> all_items = range(30) - >>> items = [x for x in all_items if 10 <= x <= 20] # Filter - >>> keyfunc = lambda x: x % 2 # Evens map to 0; odds to 1 - >>> categories = map_reduce(items, keyfunc=keyfunc) - >>> sorted(categories.items()) - [(0, [10, 12, 14, 16, 18, 20]), (1, [11, 13, 15, 17, 19])] - >>> summaries = map_reduce(items, keyfunc=keyfunc, reducefunc=sum) - >>> sorted(summaries.items()) - [(0, 90), (1, 75)] - - Note that all items in the iterable are gathered into a list before the - summarization step, which may require significant storage. - - The returned object is a :obj:`collections.defaultdict` with the - ``default_factory`` set to ``None``, such that it behaves like a normal - dictionary. - - """ - valuefunc = (lambda x: x) if (valuefunc is None) else valuefunc - - ret = defaultdict(list) - for item in iterable: - key = keyfunc(item) - value = valuefunc(item) - ret[key].append(value) - - if reducefunc is not None: - for key, value_list in ret.items(): - ret[key] = reducefunc(value_list) - - ret.default_factory = None - return ret - - -def rlocate(iterable, pred=bool, window_size=None): - """Yield the index of each item in *iterable* for which *pred* returns - ``True``, starting from the right and moving left. - - *pred* defaults to :func:`bool`, which will select truthy items: - - >>> list(rlocate([0, 1, 1, 0, 1, 0, 0])) # Truthy at 1, 2, and 4 - [4, 2, 1] - - Set *pred* to a custom function to, e.g., find the indexes for a particular - item: - - >>> iterable = iter('abcb') - >>> pred = lambda x: x == 'b' - >>> list(rlocate(iterable, pred)) - [3, 1] - - If *window_size* is given, then the *pred* function will be called with - that many items. This enables searching for sub-sequences: - - >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] - >>> pred = lambda *args: args == (1, 2, 3) - >>> list(rlocate(iterable, pred=pred, window_size=3)) - [9, 5, 1] - - Beware, this function won't return anything for infinite iterables. - If *iterable* is reversible, ``rlocate`` will reverse it and search from - the right. Otherwise, it will search from the left and return the results - in reverse order. - - See :func:`locate` to for other example applications. - - """ - if window_size is None: - try: - len_iter = len(iterable) - return (len_iter - i - 1 for i in locate(reversed(iterable), pred)) - except TypeError: - pass - - return reversed(list(locate(iterable, pred, window_size))) - - -def replace(iterable, pred, substitutes, count=None, window_size=1): - """Yield the items from *iterable*, replacing the items for which *pred* - returns ``True`` with the items from the iterable *substitutes*. - - >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1] - >>> pred = lambda x: x == 0 - >>> substitutes = (2, 3) - >>> list(replace(iterable, pred, substitutes)) - [1, 1, 2, 3, 1, 1, 2, 3, 1, 1] - - If *count* is given, the number of replacements will be limited: - - >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1, 0] - >>> pred = lambda x: x == 0 - >>> substitutes = [None] - >>> list(replace(iterable, pred, substitutes, count=2)) - [1, 1, None, 1, 1, None, 1, 1, 0] - - Use *window_size* to control the number of items passed as arguments to - *pred*. This allows for locating and replacing subsequences. - - >>> iterable = [0, 1, 2, 5, 0, 1, 2, 5] - >>> window_size = 3 - >>> pred = lambda *args: args == (0, 1, 2) # 3 items passed to pred - >>> substitutes = [3, 4] # Splice in these items - >>> list(replace(iterable, pred, substitutes, window_size=window_size)) - [3, 4, 5, 3, 4, 5] - - """ - if window_size < 1: - raise ValueError('window_size must be at least 1') - - # Save the substitutes iterable, since it's used more than once - substitutes = tuple(substitutes) - - # Add padding such that the number of windows matches the length of the - # iterable - it = chain(iterable, [_marker] * (window_size - 1)) - windows = windowed(it, window_size) - - n = 0 - for w in windows: - # If the current window matches our predicate (and we haven't hit - # our maximum number of replacements), splice in the substitutes - # and then consume the following windows that overlap with this one. - # For example, if the iterable is (0, 1, 2, 3, 4...) - # and the window size is 2, we have (0, 1), (1, 2), (2, 3)... - # If the predicate matches on (0, 1), we need to zap (0, 1) and (1, 2) - if pred(*w): - if (count is None) or (n < count): - n += 1 - yield from substitutes - consume(windows, window_size - 1) - continue - - # If there was no match (or we've reached the replacement limit), - # yield the first item from the window. - if w and (w[0] is not _marker): - yield w[0] - - -def partitions(iterable): - """Yield all possible order-preserving partitions of *iterable*. - - >>> iterable = 'abc' - >>> for part in partitions(iterable): - ... print([''.join(p) for p in part]) - ['abc'] - ['a', 'bc'] - ['ab', 'c'] - ['a', 'b', 'c'] - - This is unrelated to :func:`partition`. - - """ - sequence = list(iterable) - n = len(sequence) - for i in powerset(range(1, n)): - yield [sequence[i:j] for i, j in zip((0,) + i, i + (n,))] - - -def set_partitions(iterable, k=None): - """ - Yield the set partitions of *iterable* into *k* parts. Set partitions are - not order-preserving. - - >>> iterable = 'abc' - >>> for part in set_partitions(iterable, 2): - ... print([''.join(p) for p in part]) - ['a', 'bc'] - ['ab', 'c'] - ['b', 'ac'] - - - If *k* is not given, every set partition is generated. - - >>> iterable = 'abc' - >>> for part in set_partitions(iterable): - ... print([''.join(p) for p in part]) - ['abc'] - ['a', 'bc'] - ['ab', 'c'] - ['b', 'ac'] - ['a', 'b', 'c'] - - """ - L = list(iterable) - n = len(L) - if k is not None: - if k < 1: - raise ValueError( - "Can't partition in a negative or zero number of groups" - ) - elif k > n: - return - - def set_partitions_helper(L, k): - n = len(L) - if k == 1: - yield [L] - elif n == k: - yield [[s] for s in L] - else: - e, *M = L - for p in set_partitions_helper(M, k - 1): - yield [[e], *p] - for p in set_partitions_helper(M, k): - for i in range(len(p)): - yield p[:i] + [[e] + p[i]] + p[i + 1 :] - - if k is None: - for k in range(1, n + 1): - yield from set_partitions_helper(L, k) - else: - yield from set_partitions_helper(L, k) - - -class time_limited: - """ - Yield items from *iterable* until *limit_seconds* have passed. - If the time limit expires before all items have been yielded, the - ``timed_out`` parameter will be set to ``True``. - - >>> from time import sleep - >>> def generator(): - ... yield 1 - ... yield 2 - ... sleep(0.2) - ... yield 3 - >>> iterable = time_limited(0.1, generator()) - >>> list(iterable) - [1, 2] - >>> iterable.timed_out - True - - Note that the time is checked before each item is yielded, and iteration - stops if the time elapsed is greater than *limit_seconds*. If your time - limit is 1 second, but it takes 2 seconds to generate the first item from - the iterable, the function will run for 2 seconds and not yield anything. - - """ - - def __init__(self, limit_seconds, iterable): - if limit_seconds < 0: - raise ValueError('limit_seconds must be positive') - self.limit_seconds = limit_seconds - self._iterable = iter(iterable) - self._start_time = monotonic() - self.timed_out = False - - def __iter__(self): - return self - - def __next__(self): - item = next(self._iterable) - if monotonic() - self._start_time > self.limit_seconds: - self.timed_out = True - raise StopIteration - - return item - - -def only(iterable, default=None, too_long=None): - """If *iterable* has only one item, return it. - If it has zero items, return *default*. - If it has more than one item, raise the exception given by *too_long*, - which is ``ValueError`` by default. - - >>> only([], default='missing') - 'missing' - >>> only([1]) - 1 - >>> only([1, 2]) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - ValueError: Expected exactly one item in iterable, but got 1, 2, - and perhaps more.' - >>> only([1, 2], too_long=TypeError) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - TypeError - - Note that :func:`only` attempts to advance *iterable* twice to ensure there - is only one item. See :func:`spy` or :func:`peekable` to check - iterable contents less destructively. - """ - it = iter(iterable) - first_value = next(it, default) - - try: - second_value = next(it) - except StopIteration: - pass - else: - msg = ( - 'Expected exactly one item in iterable, but got {!r}, {!r}, ' - 'and perhaps more.'.format(first_value, second_value) - ) - raise too_long or ValueError(msg) - - return first_value - - -def ichunked(iterable, n): - """Break *iterable* into sub-iterables with *n* elements each. - :func:`ichunked` is like :func:`chunked`, but it yields iterables - instead of lists. - - If the sub-iterables are read in order, the elements of *iterable* - won't be stored in memory. - If they are read out of order, :func:`itertools.tee` is used to cache - elements as necessary. - - >>> from itertools import count - >>> all_chunks = ichunked(count(), 4) - >>> c_1, c_2, c_3 = next(all_chunks), next(all_chunks), next(all_chunks) - >>> list(c_2) # c_1's elements have been cached; c_3's haven't been - [4, 5, 6, 7] - >>> list(c_1) - [0, 1, 2, 3] - >>> list(c_3) - [8, 9, 10, 11] - - """ - source = iter(iterable) - - while True: - # Check to see whether we're at the end of the source iterable - item = next(source, _marker) - if item is _marker: - return - - # Clone the source and yield an n-length slice - source, it = tee(chain([item], source)) - yield islice(it, n) - - # Advance the source iterable - consume(source, n) - - -def distinct_combinations(iterable, r): - """Yield the distinct combinations of *r* items taken from *iterable*. - - >>> list(distinct_combinations([0, 0, 1], 2)) - [(0, 0), (0, 1)] - - Equivalent to ``set(combinations(iterable))``, except duplicates are not - generated and thrown away. For larger input sequences this is much more - efficient. - - """ - if r < 0: - raise ValueError('r must be non-negative') - elif r == 0: - yield () - return - pool = tuple(iterable) - generators = [unique_everseen(enumerate(pool), key=itemgetter(1))] - current_combo = [None] * r - level = 0 - while generators: - try: - cur_idx, p = next(generators[-1]) - except StopIteration: - generators.pop() - level -= 1 - continue - current_combo[level] = p - if level + 1 == r: - yield tuple(current_combo) - else: - generators.append( - unique_everseen( - enumerate(pool[cur_idx + 1 :], cur_idx + 1), - key=itemgetter(1), - ) - ) - level += 1 - - -def filter_except(validator, iterable, *exceptions): - """Yield the items from *iterable* for which the *validator* function does - not raise one of the specified *exceptions*. - - *validator* is called for each item in *iterable*. - It should be a function that accepts one argument and raises an exception - if that item is not valid. - - >>> iterable = ['1', '2', 'three', '4', None] - >>> list(filter_except(int, iterable, ValueError, TypeError)) - ['1', '2', '4'] - - If an exception other than one given by *exceptions* is raised by - *validator*, it is raised like normal. - """ - for item in iterable: - try: - validator(item) - except exceptions: - pass - else: - yield item - - -def map_except(function, iterable, *exceptions): - """Transform each item from *iterable* with *function* and yield the - result, unless *function* raises one of the specified *exceptions*. - - *function* is called to transform each item in *iterable*. - It should be a accept one argument. - - >>> iterable = ['1', '2', 'three', '4', None] - >>> list(map_except(int, iterable, ValueError, TypeError)) - [1, 2, 4] - - If an exception other than one given by *exceptions* is raised by - *function*, it is raised like normal. - """ - for item in iterable: - try: - yield function(item) - except exceptions: - pass - - -def _sample_unweighted(iterable, k): - # Implementation of "Algorithm L" from the 1994 paper by Kim-Hung Li: - # "Reservoir-Sampling Algorithms of Time Complexity O(n(1+log(N/n)))". - - # Fill up the reservoir (collection of samples) with the first `k` samples - reservoir = take(k, iterable) - - # Generate random number that's the largest in a sample of k U(0,1) numbers - # Largest order statistic: https://en.wikipedia.org/wiki/Order_statistic - W = exp(log(random()) / k) - - # The number of elements to skip before changing the reservoir is a random - # number with a geometric distribution. Sample it using random() and logs. - next_index = k + floor(log(random()) / log(1 - W)) - - for index, element in enumerate(iterable, k): - - if index == next_index: - reservoir[randrange(k)] = element - # The new W is the largest in a sample of k U(0, `old_W`) numbers - W *= exp(log(random()) / k) - next_index += floor(log(random()) / log(1 - W)) + 1 - - return reservoir - - -def _sample_weighted(iterable, k, weights): - # Implementation of "A-ExpJ" from the 2006 paper by Efraimidis et al. : - # "Weighted random sampling with a reservoir". - - # Log-transform for numerical stability for weights that are small/large - weight_keys = (log(random()) / weight for weight in weights) - - # Fill up the reservoir (collection of samples) with the first `k` - # weight-keys and elements, then heapify the list. - reservoir = take(k, zip(weight_keys, iterable)) - heapify(reservoir) - - # The number of jumps before changing the reservoir is a random variable - # with an exponential distribution. Sample it using random() and logs. - smallest_weight_key, _ = reservoir[0] - weights_to_skip = log(random()) / smallest_weight_key - - for weight, element in zip(weights, iterable): - if weight >= weights_to_skip: - # The notation here is consistent with the paper, but we store - # the weight-keys in log-space for better numerical stability. - smallest_weight_key, _ = reservoir[0] - t_w = exp(weight * smallest_weight_key) - r_2 = uniform(t_w, 1) # generate U(t_w, 1) - weight_key = log(r_2) / weight - heapreplace(reservoir, (weight_key, element)) - smallest_weight_key, _ = reservoir[0] - weights_to_skip = log(random()) / smallest_weight_key - else: - weights_to_skip -= weight - - # Equivalent to [element for weight_key, element in sorted(reservoir)] - return [heappop(reservoir)[1] for _ in range(k)] - - -def sample(iterable, k, weights=None): - """Return a *k*-length list of elements chosen (without replacement) - from the *iterable*. Like :func:`random.sample`, but works on iterables - of unknown length. - - >>> iterable = range(100) - >>> sample(iterable, 5) # doctest: +SKIP - [81, 60, 96, 16, 4] - - An iterable with *weights* may also be given: - - >>> iterable = range(100) - >>> weights = (i * i + 1 for i in range(100)) - >>> sampled = sample(iterable, 5, weights=weights) # doctest: +SKIP - [79, 67, 74, 66, 78] - - The algorithm can also be used to generate weighted random permutations. - The relative weight of each item determines the probability that it - appears late in the permutation. - - >>> data = "abcdefgh" - >>> weights = range(1, len(data) + 1) - >>> sample(data, k=len(data), weights=weights) # doctest: +SKIP - ['c', 'a', 'b', 'e', 'g', 'd', 'h', 'f'] - """ - if k == 0: - return [] - - iterable = iter(iterable) - if weights is None: - return _sample_unweighted(iterable, k) - else: - weights = iter(weights) - return _sample_weighted(iterable, k, weights) - - -def is_sorted(iterable, key=None, reverse=False): - """Returns ``True`` if the items of iterable are in sorted order, and - ``False`` otherwise. *key* and *reverse* have the same meaning that they do - in the built-in :func:`sorted` function. - - >>> is_sorted(['1', '2', '3', '4', '5'], key=int) - True - >>> is_sorted([5, 4, 3, 1, 2], reverse=True) - False - - The function returns ``False`` after encountering the first out-of-order - item. If there are no out-of-order items, the iterable is exhausted. - """ - - compare = lt if reverse else gt - it = iterable if (key is None) else map(key, iterable) - return not any(starmap(compare, pairwise(it))) - - -class AbortThread(BaseException): - pass - - -class callback_iter: - """Convert a function that uses callbacks to an iterator. - - Let *func* be a function that takes a `callback` keyword argument. - For example: - - >>> def func(callback=None): - ... for i, c in [(1, 'a'), (2, 'b'), (3, 'c')]: - ... if callback: - ... callback(i, c) - ... return 4 - - - Use ``with callback_iter(func)`` to get an iterator over the parameters - that are delivered to the callback. - - >>> with callback_iter(func) as it: - ... for args, kwargs in it: - ... print(args) - (1, 'a') - (2, 'b') - (3, 'c') - - The function will be called in a background thread. The ``done`` property - indicates whether it has completed execution. - - >>> it.done - True - - If it completes successfully, its return value will be available - in the ``result`` property. - - >>> it.result - 4 - - Notes: - - * If the function uses some keyword argument besides ``callback``, supply - *callback_kwd*. - * If it finished executing, but raised an exception, accessing the - ``result`` property will raise the same exception. - * If it hasn't finished executing, accessing the ``result`` - property from within the ``with`` block will raise ``RuntimeError``. - * If it hasn't finished executing, accessing the ``result`` property from - outside the ``with`` block will raise a - ``more_itertools.AbortThread`` exception. - * Provide *wait_seconds* to adjust how frequently the it is polled for - output. - - """ - - def __init__(self, func, callback_kwd='callback', wait_seconds=0.1): - self._func = func - self._callback_kwd = callback_kwd - self._aborted = False - self._future = None - self._wait_seconds = wait_seconds - self._executor = ThreadPoolExecutor(max_workers=1) - self._iterator = self._reader() - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - self._aborted = True - self._executor.shutdown() - - def __iter__(self): - return self - - def __next__(self): - return next(self._iterator) - - @property - def done(self): - if self._future is None: - return False - return self._future.done() - - @property - def result(self): - if not self.done: - raise RuntimeError('Function has not yet completed') - - return self._future.result() - - def _reader(self): - q = Queue() - - def callback(*args, **kwargs): - if self._aborted: - raise AbortThread('canceled by user') - - q.put((args, kwargs)) - - self._future = self._executor.submit( - self._func, **{self._callback_kwd: callback} - ) - - while True: - try: - item = q.get(timeout=self._wait_seconds) - except Empty: - pass - else: - q.task_done() - yield item - - if self._future.done(): - break - - remaining = [] - while True: - try: - item = q.get_nowait() - except Empty: - break - else: - q.task_done() - remaining.append(item) - q.join() - yield from remaining - - -def windowed_complete(iterable, n): - """ - Yield ``(beginning, middle, end)`` tuples, where: - - * Each ``middle`` has *n* items from *iterable* - * Each ``beginning`` has the items before the ones in ``middle`` - * Each ``end`` has the items after the ones in ``middle`` - - >>> iterable = range(7) - >>> n = 3 - >>> for beginning, middle, end in windowed_complete(iterable, n): - ... print(beginning, middle, end) - () (0, 1, 2) (3, 4, 5, 6) - (0,) (1, 2, 3) (4, 5, 6) - (0, 1) (2, 3, 4) (5, 6) - (0, 1, 2) (3, 4, 5) (6,) - (0, 1, 2, 3) (4, 5, 6) () - - Note that *n* must be at least 0 and most equal to the length of - *iterable*. - - This function will exhaust the iterable and may require significant - storage. - """ - if n < 0: - raise ValueError('n must be >= 0') - - seq = tuple(iterable) - size = len(seq) - - if n > size: - raise ValueError('n must be <= len(seq)') - - for i in range(size - n + 1): - beginning = seq[:i] - middle = seq[i : i + n] - end = seq[i + n :] - yield beginning, middle, end - - -def all_unique(iterable, key=None): - """ - Returns ``True`` if all the elements of *iterable* are unique (no two - elements are equal). - - >>> all_unique('ABCB') - False - - If a *key* function is specified, it will be used to make comparisons. - - >>> all_unique('ABCb') - True - >>> all_unique('ABCb', str.lower) - False - - The function returns as soon as the first non-unique element is - encountered. Iterables with a mix of hashable and unhashable items can - be used, but the function will be slower for unhashable items. - """ - seenset = set() - seenset_add = seenset.add - seenlist = [] - seenlist_add = seenlist.append - for element in map(key, iterable) if key else iterable: - try: - if element in seenset: - return False - seenset_add(element) - except TypeError: - if element in seenlist: - return False - seenlist_add(element) - return True - - -def nth_product(index, *args): - """Equivalent to ``list(product(*args))[index]``. - - The products of *args* can be ordered lexicographically. - :func:`nth_product` computes the product at sort position *index* without - computing the previous products. - - >>> nth_product(8, range(2), range(2), range(2), range(2)) - (1, 0, 0, 0) - - ``IndexError`` will be raised if the given *index* is invalid. - """ - pools = list(map(tuple, reversed(args))) - ns = list(map(len, pools)) - - c = reduce(mul, ns) - - if index < 0: - index += c - - if not 0 <= index < c: - raise IndexError - - result = [] - for pool, n in zip(pools, ns): - result.append(pool[index % n]) - index //= n - - return tuple(reversed(result)) - - -def nth_permutation(iterable, r, index): - """Equivalent to ``list(permutations(iterable, r))[index]``` - - The subsequences of *iterable* that are of length *r* where order is - important can be ordered lexicographically. :func:`nth_permutation` - computes the subsequence at sort position *index* directly, without - computing the previous subsequences. - - >>> nth_permutation('ghijk', 2, 5) - ('h', 'i') - - ``ValueError`` will be raised If *r* is negative or greater than the length - of *iterable*. - ``IndexError`` will be raised if the given *index* is invalid. - """ - pool = list(iterable) - n = len(pool) - - if r is None or r == n: - r, c = n, factorial(n) - elif not 0 <= r < n: - raise ValueError - else: - c = factorial(n) // factorial(n - r) - - if index < 0: - index += c - - if not 0 <= index < c: - raise IndexError - - if c == 0: - return tuple() - - result = [0] * r - q = index * factorial(n) // c if r < n else index - for d in range(1, n + 1): - q, i = divmod(q, d) - if 0 <= n - d < r: - result[n - d] = i - if q == 0: - break - - return tuple(map(pool.pop, result)) - - -def value_chain(*args): - """Yield all arguments passed to the function in the same order in which - they were passed. If an argument itself is iterable then iterate over its - values. - - >>> list(value_chain(1, 2, 3, [4, 5, 6])) - [1, 2, 3, 4, 5, 6] - - Binary and text strings are not considered iterable and are emitted - as-is: - - >>> list(value_chain('12', '34', ['56', '78'])) - ['12', '34', '56', '78'] - - - Multiple levels of nesting are not flattened. - - """ - for value in args: - if isinstance(value, (str, bytes)): - yield value - continue - try: - yield from value - except TypeError: - yield value - - -def product_index(element, *args): - """Equivalent to ``list(product(*args)).index(element)`` - - The products of *args* can be ordered lexicographically. - :func:`product_index` computes the first index of *element* without - computing the previous products. - - >>> product_index([8, 2], range(10), range(5)) - 42 - - ``ValueError`` will be raised if the given *element* isn't in the product - of *args*. - """ - index = 0 - - for x, pool in zip_longest(element, args, fillvalue=_marker): - if x is _marker or pool is _marker: - raise ValueError('element is not a product of args') - - pool = tuple(pool) - index = index * len(pool) + pool.index(x) - - return index - - -def combination_index(element, iterable): - """Equivalent to ``list(combinations(iterable, r)).index(element)`` - - The subsequences of *iterable* that are of length *r* can be ordered - lexicographically. :func:`combination_index` computes the index of the - first *element*, without computing the previous combinations. - - >>> combination_index('adf', 'abcdefg') - 10 - - ``ValueError`` will be raised if the given *element* isn't one of the - combinations of *iterable*. - """ - element = enumerate(element) - k, y = next(element, (None, None)) - if k is None: - return 0 - - indexes = [] - pool = enumerate(iterable) - for n, x in pool: - if x == y: - indexes.append(n) - tmp, y = next(element, (None, None)) - if tmp is None: - break - else: - k = tmp - else: - raise ValueError('element is not a combination of iterable') - - n, _ = last(pool, default=(n, None)) - - # Python versiosn below 3.8 don't have math.comb - index = 1 - for i, j in enumerate(reversed(indexes), start=1): - j = n - j - if i <= j: - index += factorial(j) // (factorial(i) * factorial(j - i)) - - return factorial(n + 1) // (factorial(k + 1) * factorial(n - k)) - index - - -def permutation_index(element, iterable): - """Equivalent to ``list(permutations(iterable, r)).index(element)``` - - The subsequences of *iterable* that are of length *r* where order is - important can be ordered lexicographically. :func:`permutation_index` - computes the index of the first *element* directly, without computing - the previous permutations. - - >>> permutation_index([1, 3, 2], range(5)) - 19 - - ``ValueError`` will be raised if the given *element* isn't one of the - permutations of *iterable*. - """ - index = 0 - pool = list(iterable) - for i, x in zip(range(len(pool), -1, -1), element): - r = pool.index(x) - index = index * i + r - del pool[r] - - return index - - -class countable: - """Wrap *iterable* and keep a count of how many items have been consumed. - - The ``items_seen`` attribute starts at ``0`` and increments as the iterable - is consumed: - - >>> iterable = map(str, range(10)) - >>> it = countable(iterable) - >>> it.items_seen - 0 - >>> next(it), next(it) - ('0', '1') - >>> list(it) - ['2', '3', '4', '5', '6', '7', '8', '9'] - >>> it.items_seen - 10 - """ - - def __init__(self, iterable): - self._it = iter(iterable) - self.items_seen = 0 - - def __iter__(self): - return self - - def __next__(self): - item = next(self._it) - self.items_seen += 1 - - return item diff --git a/setuptools/_vendor/more_itertools/more.pyi b/setuptools/_vendor/more_itertools/more.pyi deleted file mode 100644 index 2fba9cb300..0000000000 --- a/setuptools/_vendor/more_itertools/more.pyi +++ /dev/null @@ -1,480 +0,0 @@ -"""Stubs for more_itertools.more""" - -from typing import ( - Any, - Callable, - Container, - Dict, - Generic, - Hashable, - Iterable, - Iterator, - List, - Optional, - Reversible, - Sequence, - Sized, - Tuple, - Union, - TypeVar, - type_check_only, -) -from types import TracebackType -from typing_extensions import ContextManager, Protocol, Type, overload - -# Type and type variable definitions -_T = TypeVar('_T') -_U = TypeVar('_U') -_V = TypeVar('_V') -_W = TypeVar('_W') -_T_co = TypeVar('_T_co', covariant=True) -_GenFn = TypeVar('_GenFn', bound=Callable[..., Iterator[object]]) -_Raisable = Union[BaseException, 'Type[BaseException]'] - -@type_check_only -class _SizedIterable(Protocol[_T_co], Sized, Iterable[_T_co]): ... - -@type_check_only -class _SizedReversible(Protocol[_T_co], Sized, Reversible[_T_co]): ... - -def chunked( - iterable: Iterable[_T], n: int, strict: bool = ... -) -> Iterator[List[_T]]: ... -@overload -def first(iterable: Iterable[_T]) -> _T: ... -@overload -def first(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... -@overload -def last(iterable: Iterable[_T]) -> _T: ... -@overload -def last(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... -@overload -def nth_or_last(iterable: Iterable[_T], n: int) -> _T: ... -@overload -def nth_or_last( - iterable: Iterable[_T], n: int, default: _U -) -> Union[_T, _U]: ... - -class peekable(Generic[_T], Iterator[_T]): - def __init__(self, iterable: Iterable[_T]) -> None: ... - def __iter__(self) -> peekable[_T]: ... - def __bool__(self) -> bool: ... - @overload - def peek(self) -> _T: ... - @overload - def peek(self, default: _U) -> Union[_T, _U]: ... - def prepend(self, *items: _T) -> None: ... - def __next__(self) -> _T: ... - @overload - def __getitem__(self, index: int) -> _T: ... - @overload - def __getitem__(self, index: slice) -> List[_T]: ... - -def collate(*iterables: Iterable[_T], **kwargs: Any) -> Iterable[_T]: ... -def consumer(func: _GenFn) -> _GenFn: ... -def ilen(iterable: Iterable[object]) -> int: ... -def iterate(func: Callable[[_T], _T], start: _T) -> Iterator[_T]: ... -def with_iter( - context_manager: ContextManager[Iterable[_T]], -) -> Iterator[_T]: ... -def one( - iterable: Iterable[_T], - too_short: Optional[_Raisable] = ..., - too_long: Optional[_Raisable] = ..., -) -> _T: ... -def distinct_permutations( - iterable: Iterable[_T], r: Optional[int] = ... -) -> Iterator[Tuple[_T, ...]]: ... -def intersperse( - e: _U, iterable: Iterable[_T], n: int = ... -) -> Iterator[Union[_T, _U]]: ... -def unique_to_each(*iterables: Iterable[_T]) -> List[List[_T]]: ... -@overload -def windowed( - seq: Iterable[_T], n: int, *, step: int = ... -) -> Iterator[Tuple[Optional[_T], ...]]: ... -@overload -def windowed( - seq: Iterable[_T], n: int, fillvalue: _U, step: int = ... -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... -def substrings(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... -def substrings_indexes( - seq: Sequence[_T], reverse: bool = ... -) -> Iterator[Tuple[Sequence[_T], int, int]]: ... - -class bucket(Generic[_T, _U], Container[_U]): - def __init__( - self, - iterable: Iterable[_T], - key: Callable[[_T], _U], - validator: Optional[Callable[[object], object]] = ..., - ) -> None: ... - def __contains__(self, value: object) -> bool: ... - def __iter__(self) -> Iterator[_U]: ... - def __getitem__(self, value: object) -> Iterator[_T]: ... - -def spy( - iterable: Iterable[_T], n: int = ... -) -> Tuple[List[_T], Iterator[_T]]: ... -def interleave(*iterables: Iterable[_T]) -> Iterator[_T]: ... -def interleave_longest(*iterables: Iterable[_T]) -> Iterator[_T]: ... -def collapse( - iterable: Iterable[Any], - base_type: Optional[type] = ..., - levels: Optional[int] = ..., -) -> Iterator[Any]: ... -@overload -def side_effect( - func: Callable[[_T], object], - iterable: Iterable[_T], - chunk_size: None = ..., - before: Optional[Callable[[], object]] = ..., - after: Optional[Callable[[], object]] = ..., -) -> Iterator[_T]: ... -@overload -def side_effect( - func: Callable[[List[_T]], object], - iterable: Iterable[_T], - chunk_size: int, - before: Optional[Callable[[], object]] = ..., - after: Optional[Callable[[], object]] = ..., -) -> Iterator[_T]: ... -def sliced( - seq: Sequence[_T], n: int, strict: bool = ... -) -> Iterator[Sequence[_T]]: ... -def split_at( - iterable: Iterable[_T], - pred: Callable[[_T], object], - maxsplit: int = ..., - keep_separator: bool = ..., -) -> Iterator[List[_T]]: ... -def split_before( - iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... -) -> Iterator[List[_T]]: ... -def split_after( - iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... -) -> Iterator[List[_T]]: ... -def split_when( - iterable: Iterable[_T], - pred: Callable[[_T, _T], object], - maxsplit: int = ..., -) -> Iterator[List[_T]]: ... -def split_into( - iterable: Iterable[_T], sizes: Iterable[Optional[int]] -) -> Iterator[List[_T]]: ... -@overload -def padded( - iterable: Iterable[_T], - *, - n: Optional[int] = ..., - next_multiple: bool = ... -) -> Iterator[Optional[_T]]: ... -@overload -def padded( - iterable: Iterable[_T], - fillvalue: _U, - n: Optional[int] = ..., - next_multiple: bool = ..., -) -> Iterator[Union[_T, _U]]: ... -@overload -def repeat_last(iterable: Iterable[_T]) -> Iterator[_T]: ... -@overload -def repeat_last( - iterable: Iterable[_T], default: _U -) -> Iterator[Union[_T, _U]]: ... -def distribute(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... -@overload -def stagger( - iterable: Iterable[_T], - offsets: _SizedIterable[int] = ..., - longest: bool = ..., -) -> Iterator[Tuple[Optional[_T], ...]]: ... -@overload -def stagger( - iterable: Iterable[_T], - offsets: _SizedIterable[int] = ..., - longest: bool = ..., - fillvalue: _U = ..., -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... - -class UnequalIterablesError(ValueError): - def __init__( - self, details: Optional[Tuple[int, int, int]] = ... - ) -> None: ... - -def zip_equal(*iterables: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... -@overload -def zip_offset( - *iterables: Iterable[_T], offsets: _SizedIterable[int], longest: bool = ... -) -> Iterator[Tuple[Optional[_T], ...]]: ... -@overload -def zip_offset( - *iterables: Iterable[_T], - offsets: _SizedIterable[int], - longest: bool = ..., - fillvalue: _U -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... -def sort_together( - iterables: Iterable[Iterable[_T]], - key_list: Iterable[int] = ..., - key: Optional[Callable[..., Any]] = ..., - reverse: bool = ..., -) -> List[Tuple[_T, ...]]: ... -def unzip(iterable: Iterable[Sequence[_T]]) -> Tuple[Iterator[_T], ...]: ... -def divide(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... -def always_iterable( - obj: object, - base_type: Union[ - type, Tuple[Union[type, Tuple[Any, ...]], ...], None - ] = ..., -) -> Iterator[Any]: ... -def adjacent( - predicate: Callable[[_T], bool], - iterable: Iterable[_T], - distance: int = ..., -) -> Iterator[Tuple[bool, _T]]: ... -def groupby_transform( - iterable: Iterable[_T], - keyfunc: Optional[Callable[[_T], _U]] = ..., - valuefunc: Optional[Callable[[_T], _V]] = ..., - reducefunc: Optional[Callable[..., _W]] = ..., -) -> Iterator[Tuple[_T, _W]]: ... - -class numeric_range(Generic[_T, _U], Sequence[_T], Hashable, Reversible[_T]): - @overload - def __init__(self, __stop: _T) -> None: ... - @overload - def __init__(self, __start: _T, __stop: _T) -> None: ... - @overload - def __init__(self, __start: _T, __stop: _T, __step: _U) -> None: ... - def __bool__(self) -> bool: ... - def __contains__(self, elem: object) -> bool: ... - def __eq__(self, other: object) -> bool: ... - @overload - def __getitem__(self, key: int) -> _T: ... - @overload - def __getitem__(self, key: slice) -> numeric_range[_T, _U]: ... - def __hash__(self) -> int: ... - def __iter__(self) -> Iterator[_T]: ... - def __len__(self) -> int: ... - def __reduce__( - self, - ) -> Tuple[Type[numeric_range[_T, _U]], Tuple[_T, _T, _U]]: ... - def __repr__(self) -> str: ... - def __reversed__(self) -> Iterator[_T]: ... - def count(self, value: _T) -> int: ... - def index(self, value: _T) -> int: ... # type: ignore - -def count_cycle( - iterable: Iterable[_T], n: Optional[int] = ... -) -> Iterable[Tuple[int, _T]]: ... -def mark_ends( - iterable: Iterable[_T], -) -> Iterable[Tuple[bool, bool, _T]]: ... -def locate( - iterable: Iterable[object], - pred: Callable[..., Any] = ..., - window_size: Optional[int] = ..., -) -> Iterator[int]: ... -def lstrip( - iterable: Iterable[_T], pred: Callable[[_T], object] -) -> Iterator[_T]: ... -def rstrip( - iterable: Iterable[_T], pred: Callable[[_T], object] -) -> Iterator[_T]: ... -def strip( - iterable: Iterable[_T], pred: Callable[[_T], object] -) -> Iterator[_T]: ... - -class islice_extended(Generic[_T], Iterator[_T]): - def __init__( - self, iterable: Iterable[_T], *args: Optional[int] - ) -> None: ... - def __iter__(self) -> islice_extended[_T]: ... - def __next__(self) -> _T: ... - def __getitem__(self, index: slice) -> islice_extended[_T]: ... - -def always_reversible(iterable: Iterable[_T]) -> Iterator[_T]: ... -def consecutive_groups( - iterable: Iterable[_T], ordering: Callable[[_T], int] = ... -) -> Iterator[Iterator[_T]]: ... -@overload -def difference( - iterable: Iterable[_T], - func: Callable[[_T, _T], _U] = ..., - *, - initial: None = ... -) -> Iterator[Union[_T, _U]]: ... -@overload -def difference( - iterable: Iterable[_T], func: Callable[[_T, _T], _U] = ..., *, initial: _U -) -> Iterator[_U]: ... - -class SequenceView(Generic[_T], Sequence[_T]): - def __init__(self, target: Sequence[_T]) -> None: ... - @overload - def __getitem__(self, index: int) -> _T: ... - @overload - def __getitem__(self, index: slice) -> Sequence[_T]: ... - def __len__(self) -> int: ... - -class seekable(Generic[_T], Iterator[_T]): - def __init__( - self, iterable: Iterable[_T], maxlen: Optional[int] = ... - ) -> None: ... - def __iter__(self) -> seekable[_T]: ... - def __next__(self) -> _T: ... - def __bool__(self) -> bool: ... - @overload - def peek(self) -> _T: ... - @overload - def peek(self, default: _U) -> Union[_T, _U]: ... - def elements(self) -> SequenceView[_T]: ... - def seek(self, index: int) -> None: ... - -class run_length: - @staticmethod - def encode(iterable: Iterable[_T]) -> Iterator[Tuple[_T, int]]: ... - @staticmethod - def decode(iterable: Iterable[Tuple[_T, int]]) -> Iterator[_T]: ... - -def exactly_n( - iterable: Iterable[_T], n: int, predicate: Callable[[_T], object] = ... -) -> bool: ... -def circular_shifts(iterable: Iterable[_T]) -> List[Tuple[_T, ...]]: ... -def make_decorator( - wrapping_func: Callable[..., _U], result_index: int = ... -) -> Callable[..., Callable[[Callable[..., Any]], Callable[..., _U]]]: ... -@overload -def map_reduce( - iterable: Iterable[_T], - keyfunc: Callable[[_T], _U], - valuefunc: None = ..., - reducefunc: None = ..., -) -> Dict[_U, List[_T]]: ... -@overload -def map_reduce( - iterable: Iterable[_T], - keyfunc: Callable[[_T], _U], - valuefunc: Callable[[_T], _V], - reducefunc: None = ..., -) -> Dict[_U, List[_V]]: ... -@overload -def map_reduce( - iterable: Iterable[_T], - keyfunc: Callable[[_T], _U], - valuefunc: None = ..., - reducefunc: Callable[[List[_T]], _W] = ..., -) -> Dict[_U, _W]: ... -@overload -def map_reduce( - iterable: Iterable[_T], - keyfunc: Callable[[_T], _U], - valuefunc: Callable[[_T], _V], - reducefunc: Callable[[List[_V]], _W], -) -> Dict[_U, _W]: ... -def rlocate( - iterable: Iterable[_T], - pred: Callable[..., object] = ..., - window_size: Optional[int] = ..., -) -> Iterator[int]: ... -def replace( - iterable: Iterable[_T], - pred: Callable[..., object], - substitutes: Iterable[_U], - count: Optional[int] = ..., - window_size: int = ..., -) -> Iterator[Union[_T, _U]]: ... -def partitions(iterable: Iterable[_T]) -> Iterator[List[List[_T]]]: ... -def set_partitions( - iterable: Iterable[_T], k: Optional[int] = ... -) -> Iterator[List[List[_T]]]: ... - -class time_limited(Generic[_T], Iterator[_T]): - def __init__( - self, limit_seconds: float, iterable: Iterable[_T] - ) -> None: ... - def __iter__(self) -> islice_extended[_T]: ... - def __next__(self) -> _T: ... - -@overload -def only( - iterable: Iterable[_T], *, too_long: Optional[_Raisable] = ... -) -> Optional[_T]: ... -@overload -def only( - iterable: Iterable[_T], default: _U, too_long: Optional[_Raisable] = ... -) -> Union[_T, _U]: ... -def ichunked(iterable: Iterable[_T], n: int) -> Iterator[Iterator[_T]]: ... -def distinct_combinations( - iterable: Iterable[_T], r: int -) -> Iterator[Tuple[_T, ...]]: ... -def filter_except( - validator: Callable[[Any], object], - iterable: Iterable[_T], - *exceptions: Type[BaseException] -) -> Iterator[_T]: ... -def map_except( - function: Callable[[Any], _U], - iterable: Iterable[_T], - *exceptions: Type[BaseException] -) -> Iterator[_U]: ... -def sample( - iterable: Iterable[_T], - k: int, - weights: Optional[Iterable[float]] = ..., -) -> List[_T]: ... -def is_sorted( - iterable: Iterable[_T], - key: Optional[Callable[[_T], _U]] = ..., - reverse: bool = False, -) -> bool: ... - -class AbortThread(BaseException): - pass - -class callback_iter(Generic[_T], Iterator[_T]): - def __init__( - self, - func: Callable[..., Any], - callback_kwd: str = ..., - wait_seconds: float = ..., - ) -> None: ... - def __enter__(self) -> callback_iter[_T]: ... - def __exit__( - self, - exc_type: Optional[Type[BaseException]], - exc_value: Optional[BaseException], - traceback: Optional[TracebackType], - ) -> Optional[bool]: ... - def __iter__(self) -> callback_iter[_T]: ... - def __next__(self) -> _T: ... - def _reader(self) -> Iterator[_T]: ... - @property - def done(self) -> bool: ... - @property - def result(self) -> Any: ... - -def windowed_complete( - iterable: Iterable[_T], n: int -) -> Iterator[Tuple[_T, ...]]: ... -def all_unique( - iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... -) -> bool: ... -def nth_product(index: int, *args: Iterable[_T]) -> Tuple[_T, ...]: ... -def nth_permutation( - iterable: Iterable[_T], r: int, index: int -) -> Tuple[_T, ...]: ... -def value_chain(*args: Union[_T, Iterable[_T]]) -> Iterable[_T]: ... -def product_index(element: Iterable[_T], *args: Iterable[_T]) -> int: ... -def combination_index( - element: Iterable[_T], iterable: Iterable[_T] -) -> int: ... -def permutation_index( - element: Iterable[_T], iterable: Iterable[_T] -) -> int: ... - -class countable(Generic[_T], Iterator[_T]): - def __init__(self, iterable: Iterable[_T]) -> None: ... - def __iter__(self) -> countable[_T]: ... - def __next__(self) -> _T: ... diff --git a/setuptools/_vendor/more_itertools/py.typed b/setuptools/_vendor/more_itertools/py.typed deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/setuptools/_vendor/more_itertools/recipes.py b/setuptools/_vendor/more_itertools/recipes.py deleted file mode 100644 index 521abd7c2c..0000000000 --- a/setuptools/_vendor/more_itertools/recipes.py +++ /dev/null @@ -1,620 +0,0 @@ -"""Imported from the recipes section of the itertools documentation. - -All functions taken from the recipes section of the itertools library docs -[1]_. -Some backward-compatible usability improvements have been made. - -.. [1] http://docs.python.org/library/itertools.html#recipes - -""" -import warnings -from collections import deque -from itertools import ( - chain, - combinations, - count, - cycle, - groupby, - islice, - repeat, - starmap, - tee, - zip_longest, -) -import operator -from random import randrange, sample, choice - -__all__ = [ - 'all_equal', - 'consume', - 'convolve', - 'dotproduct', - 'first_true', - 'flatten', - 'grouper', - 'iter_except', - 'ncycles', - 'nth', - 'nth_combination', - 'padnone', - 'pad_none', - 'pairwise', - 'partition', - 'powerset', - 'prepend', - 'quantify', - 'random_combination_with_replacement', - 'random_combination', - 'random_permutation', - 'random_product', - 'repeatfunc', - 'roundrobin', - 'tabulate', - 'tail', - 'take', - 'unique_everseen', - 'unique_justseen', -] - - -def take(n, iterable): - """Return first *n* items of the iterable as a list. - - >>> take(3, range(10)) - [0, 1, 2] - - If there are fewer than *n* items in the iterable, all of them are - returned. - - >>> take(10, range(3)) - [0, 1, 2] - - """ - return list(islice(iterable, n)) - - -def tabulate(function, start=0): - """Return an iterator over the results of ``func(start)``, - ``func(start + 1)``, ``func(start + 2)``... - - *func* should be a function that accepts one integer argument. - - If *start* is not specified it defaults to 0. It will be incremented each - time the iterator is advanced. - - >>> square = lambda x: x ** 2 - >>> iterator = tabulate(square, -3) - >>> take(4, iterator) - [9, 4, 1, 0] - - """ - return map(function, count(start)) - - -def tail(n, iterable): - """Return an iterator over the last *n* items of *iterable*. - - >>> t = tail(3, 'ABCDEFG') - >>> list(t) - ['E', 'F', 'G'] - - """ - return iter(deque(iterable, maxlen=n)) - - -def consume(iterator, n=None): - """Advance *iterable* by *n* steps. If *n* is ``None``, consume it - entirely. - - Efficiently exhausts an iterator without returning values. Defaults to - consuming the whole iterator, but an optional second argument may be - provided to limit consumption. - - >>> i = (x for x in range(10)) - >>> next(i) - 0 - >>> consume(i, 3) - >>> next(i) - 4 - >>> consume(i) - >>> next(i) - Traceback (most recent call last): - File "", line 1, in - StopIteration - - If the iterator has fewer items remaining than the provided limit, the - whole iterator will be consumed. - - >>> i = (x for x in range(3)) - >>> consume(i, 5) - >>> next(i) - Traceback (most recent call last): - File "", line 1, in - StopIteration - - """ - # Use functions that consume iterators at C speed. - if n is None: - # feed the entire iterator into a zero-length deque - deque(iterator, maxlen=0) - else: - # advance to the empty slice starting at position n - next(islice(iterator, n, n), None) - - -def nth(iterable, n, default=None): - """Returns the nth item or a default value. - - >>> l = range(10) - >>> nth(l, 3) - 3 - >>> nth(l, 20, "zebra") - 'zebra' - - """ - return next(islice(iterable, n, None), default) - - -def all_equal(iterable): - """ - Returns ``True`` if all the elements are equal to each other. - - >>> all_equal('aaaa') - True - >>> all_equal('aaab') - False - - """ - g = groupby(iterable) - return next(g, True) and not next(g, False) - - -def quantify(iterable, pred=bool): - """Return the how many times the predicate is true. - - >>> quantify([True, False, True]) - 2 - - """ - return sum(map(pred, iterable)) - - -def pad_none(iterable): - """Returns the sequence of elements and then returns ``None`` indefinitely. - - >>> take(5, pad_none(range(3))) - [0, 1, 2, None, None] - - Useful for emulating the behavior of the built-in :func:`map` function. - - See also :func:`padded`. - - """ - return chain(iterable, repeat(None)) - - -padnone = pad_none - - -def ncycles(iterable, n): - """Returns the sequence elements *n* times - - >>> list(ncycles(["a", "b"], 3)) - ['a', 'b', 'a', 'b', 'a', 'b'] - - """ - return chain.from_iterable(repeat(tuple(iterable), n)) - - -def dotproduct(vec1, vec2): - """Returns the dot product of the two iterables. - - >>> dotproduct([10, 10], [20, 20]) - 400 - - """ - return sum(map(operator.mul, vec1, vec2)) - - -def flatten(listOfLists): - """Return an iterator flattening one level of nesting in a list of lists. - - >>> list(flatten([[0, 1], [2, 3]])) - [0, 1, 2, 3] - - See also :func:`collapse`, which can flatten multiple levels of nesting. - - """ - return chain.from_iterable(listOfLists) - - -def repeatfunc(func, times=None, *args): - """Call *func* with *args* repeatedly, returning an iterable over the - results. - - If *times* is specified, the iterable will terminate after that many - repetitions: - - >>> from operator import add - >>> times = 4 - >>> args = 3, 5 - >>> list(repeatfunc(add, times, *args)) - [8, 8, 8, 8] - - If *times* is ``None`` the iterable will not terminate: - - >>> from random import randrange - >>> times = None - >>> args = 1, 11 - >>> take(6, repeatfunc(randrange, times, *args)) # doctest:+SKIP - [2, 4, 8, 1, 8, 4] - - """ - if times is None: - return starmap(func, repeat(args)) - return starmap(func, repeat(args, times)) - - -def _pairwise(iterable): - """Returns an iterator of paired items, overlapping, from the original - - >>> take(4, pairwise(count())) - [(0, 1), (1, 2), (2, 3), (3, 4)] - - On Python 3.10 and above, this is an alias for :func:`itertools.pairwise`. - - """ - a, b = tee(iterable) - next(b, None) - yield from zip(a, b) - - -try: - from itertools import pairwise as itertools_pairwise -except ImportError: - pairwise = _pairwise -else: - - def pairwise(iterable): - yield from itertools_pairwise(iterable) - - pairwise.__doc__ = _pairwise.__doc__ - - -def grouper(iterable, n, fillvalue=None): - """Collect data into fixed-length chunks or blocks. - - >>> list(grouper('ABCDEFG', 3, 'x')) - [('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')] - - """ - if isinstance(iterable, int): - warnings.warn( - "grouper expects iterable as first parameter", DeprecationWarning - ) - n, iterable = iterable, n - args = [iter(iterable)] * n - return zip_longest(fillvalue=fillvalue, *args) - - -def roundrobin(*iterables): - """Yields an item from each iterable, alternating between them. - - >>> list(roundrobin('ABC', 'D', 'EF')) - ['A', 'D', 'E', 'B', 'F', 'C'] - - This function produces the same output as :func:`interleave_longest`, but - may perform better for some inputs (in particular when the number of - iterables is small). - - """ - # Recipe credited to George Sakkis - pending = len(iterables) - nexts = cycle(iter(it).__next__ for it in iterables) - while pending: - try: - for next in nexts: - yield next() - except StopIteration: - pending -= 1 - nexts = cycle(islice(nexts, pending)) - - -def partition(pred, iterable): - """ - Returns a 2-tuple of iterables derived from the input iterable. - The first yields the items that have ``pred(item) == False``. - The second yields the items that have ``pred(item) == True``. - - >>> is_odd = lambda x: x % 2 != 0 - >>> iterable = range(10) - >>> even_items, odd_items = partition(is_odd, iterable) - >>> list(even_items), list(odd_items) - ([0, 2, 4, 6, 8], [1, 3, 5, 7, 9]) - - If *pred* is None, :func:`bool` is used. - - >>> iterable = [0, 1, False, True, '', ' '] - >>> false_items, true_items = partition(None, iterable) - >>> list(false_items), list(true_items) - ([0, False, ''], [1, True, ' ']) - - """ - if pred is None: - pred = bool - - evaluations = ((pred(x), x) for x in iterable) - t1, t2 = tee(evaluations) - return ( - (x for (cond, x) in t1 if not cond), - (x for (cond, x) in t2 if cond), - ) - - -def powerset(iterable): - """Yields all possible subsets of the iterable. - - >>> list(powerset([1, 2, 3])) - [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] - - :func:`powerset` will operate on iterables that aren't :class:`set` - instances, so repeated elements in the input will produce repeated elements - in the output. Use :func:`unique_everseen` on the input to avoid generating - duplicates: - - >>> seq = [1, 1, 0] - >>> list(powerset(seq)) - [(), (1,), (1,), (0,), (1, 1), (1, 0), (1, 0), (1, 1, 0)] - >>> from more_itertools import unique_everseen - >>> list(powerset(unique_everseen(seq))) - [(), (1,), (0,), (1, 0)] - - """ - s = list(iterable) - return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1)) - - -def unique_everseen(iterable, key=None): - """ - Yield unique elements, preserving order. - - >>> list(unique_everseen('AAAABBBCCDAABBB')) - ['A', 'B', 'C', 'D'] - >>> list(unique_everseen('ABBCcAD', str.lower)) - ['A', 'B', 'C', 'D'] - - Sequences with a mix of hashable and unhashable items can be used. - The function will be slower (i.e., `O(n^2)`) for unhashable items. - - Remember that ``list`` objects are unhashable - you can use the *key* - parameter to transform the list to a tuple (which is hashable) to - avoid a slowdown. - - >>> iterable = ([1, 2], [2, 3], [1, 2]) - >>> list(unique_everseen(iterable)) # Slow - [[1, 2], [2, 3]] - >>> list(unique_everseen(iterable, key=tuple)) # Faster - [[1, 2], [2, 3]] - - Similary, you may want to convert unhashable ``set`` objects with - ``key=frozenset``. For ``dict`` objects, - ``key=lambda x: frozenset(x.items())`` can be used. - - """ - seenset = set() - seenset_add = seenset.add - seenlist = [] - seenlist_add = seenlist.append - use_key = key is not None - - for element in iterable: - k = key(element) if use_key else element - try: - if k not in seenset: - seenset_add(k) - yield element - except TypeError: - if k not in seenlist: - seenlist_add(k) - yield element - - -def unique_justseen(iterable, key=None): - """Yields elements in order, ignoring serial duplicates - - >>> list(unique_justseen('AAAABBBCCDAABBB')) - ['A', 'B', 'C', 'D', 'A', 'B'] - >>> list(unique_justseen('ABBCcAD', str.lower)) - ['A', 'B', 'C', 'A', 'D'] - - """ - return map(next, map(operator.itemgetter(1), groupby(iterable, key))) - - -def iter_except(func, exception, first=None): - """Yields results from a function repeatedly until an exception is raised. - - Converts a call-until-exception interface to an iterator interface. - Like ``iter(func, sentinel)``, but uses an exception instead of a sentinel - to end the loop. - - >>> l = [0, 1, 2] - >>> list(iter_except(l.pop, IndexError)) - [2, 1, 0] - - """ - try: - if first is not None: - yield first() - while 1: - yield func() - except exception: - pass - - -def first_true(iterable, default=None, pred=None): - """ - Returns the first true value in the iterable. - - If no true value is found, returns *default* - - If *pred* is not None, returns the first item for which - ``pred(item) == True`` . - - >>> first_true(range(10)) - 1 - >>> first_true(range(10), pred=lambda x: x > 5) - 6 - >>> first_true(range(10), default='missing', pred=lambda x: x > 9) - 'missing' - - """ - return next(filter(pred, iterable), default) - - -def random_product(*args, repeat=1): - """Draw an item at random from each of the input iterables. - - >>> random_product('abc', range(4), 'XYZ') # doctest:+SKIP - ('c', 3, 'Z') - - If *repeat* is provided as a keyword argument, that many items will be - drawn from each iterable. - - >>> random_product('abcd', range(4), repeat=2) # doctest:+SKIP - ('a', 2, 'd', 3) - - This equivalent to taking a random selection from - ``itertools.product(*args, **kwarg)``. - - """ - pools = [tuple(pool) for pool in args] * repeat - return tuple(choice(pool) for pool in pools) - - -def random_permutation(iterable, r=None): - """Return a random *r* length permutation of the elements in *iterable*. - - If *r* is not specified or is ``None``, then *r* defaults to the length of - *iterable*. - - >>> random_permutation(range(5)) # doctest:+SKIP - (3, 4, 0, 1, 2) - - This equivalent to taking a random selection from - ``itertools.permutations(iterable, r)``. - - """ - pool = tuple(iterable) - r = len(pool) if r is None else r - return tuple(sample(pool, r)) - - -def random_combination(iterable, r): - """Return a random *r* length subsequence of the elements in *iterable*. - - >>> random_combination(range(5), 3) # doctest:+SKIP - (2, 3, 4) - - This equivalent to taking a random selection from - ``itertools.combinations(iterable, r)``. - - """ - pool = tuple(iterable) - n = len(pool) - indices = sorted(sample(range(n), r)) - return tuple(pool[i] for i in indices) - - -def random_combination_with_replacement(iterable, r): - """Return a random *r* length subsequence of elements in *iterable*, - allowing individual elements to be repeated. - - >>> random_combination_with_replacement(range(3), 5) # doctest:+SKIP - (0, 0, 1, 2, 2) - - This equivalent to taking a random selection from - ``itertools.combinations_with_replacement(iterable, r)``. - - """ - pool = tuple(iterable) - n = len(pool) - indices = sorted(randrange(n) for i in range(r)) - return tuple(pool[i] for i in indices) - - -def nth_combination(iterable, r, index): - """Equivalent to ``list(combinations(iterable, r))[index]``. - - The subsequences of *iterable* that are of length *r* can be ordered - lexicographically. :func:`nth_combination` computes the subsequence at - sort position *index* directly, without computing the previous - subsequences. - - >>> nth_combination(range(5), 3, 5) - (0, 3, 4) - - ``ValueError`` will be raised If *r* is negative or greater than the length - of *iterable*. - ``IndexError`` will be raised if the given *index* is invalid. - """ - pool = tuple(iterable) - n = len(pool) - if (r < 0) or (r > n): - raise ValueError - - c = 1 - k = min(r, n - r) - for i in range(1, k + 1): - c = c * (n - k + i) // i - - if index < 0: - index += c - - if (index < 0) or (index >= c): - raise IndexError - - result = [] - while r: - c, n, r = c * r // n, n - 1, r - 1 - while index >= c: - index -= c - c, n = c * (n - r) // n, n - 1 - result.append(pool[-1 - n]) - - return tuple(result) - - -def prepend(value, iterator): - """Yield *value*, followed by the elements in *iterator*. - - >>> value = '0' - >>> iterator = ['1', '2', '3'] - >>> list(prepend(value, iterator)) - ['0', '1', '2', '3'] - - To prepend multiple values, see :func:`itertools.chain` - or :func:`value_chain`. - - """ - return chain([value], iterator) - - -def convolve(signal, kernel): - """Convolve the iterable *signal* with the iterable *kernel*. - - >>> signal = (1, 2, 3, 4, 5) - >>> kernel = [3, 2, 1] - >>> list(convolve(signal, kernel)) - [3, 8, 14, 20, 26, 14, 5] - - Note: the input arguments are not interchangeable, as the *kernel* - is immediately consumed and stored. - - """ - kernel = tuple(kernel)[::-1] - n = len(kernel) - window = deque([0], maxlen=n) * n - for x in chain(signal, repeat(0, n - 1)): - window.append(x) - yield sum(map(operator.mul, kernel, window)) diff --git a/setuptools/_vendor/more_itertools/recipes.pyi b/setuptools/_vendor/more_itertools/recipes.pyi deleted file mode 100644 index 5e39d96390..0000000000 --- a/setuptools/_vendor/more_itertools/recipes.pyi +++ /dev/null @@ -1,103 +0,0 @@ -"""Stubs for more_itertools.recipes""" -from typing import ( - Any, - Callable, - Iterable, - Iterator, - List, - Optional, - Tuple, - TypeVar, - Union, -) -from typing_extensions import overload, Type - -# Type and type variable definitions -_T = TypeVar('_T') -_U = TypeVar('_U') - -def take(n: int, iterable: Iterable[_T]) -> List[_T]: ... -def tabulate( - function: Callable[[int], _T], start: int = ... -) -> Iterator[_T]: ... -def tail(n: int, iterable: Iterable[_T]) -> Iterator[_T]: ... -def consume(iterator: Iterable[object], n: Optional[int] = ...) -> None: ... -@overload -def nth(iterable: Iterable[_T], n: int) -> Optional[_T]: ... -@overload -def nth(iterable: Iterable[_T], n: int, default: _U) -> Union[_T, _U]: ... -def all_equal(iterable: Iterable[object]) -> bool: ... -def quantify( - iterable: Iterable[_T], pred: Callable[[_T], bool] = ... -) -> int: ... -def pad_none(iterable: Iterable[_T]) -> Iterator[Optional[_T]]: ... -def padnone(iterable: Iterable[_T]) -> Iterator[Optional[_T]]: ... -def ncycles(iterable: Iterable[_T], n: int) -> Iterator[_T]: ... -def dotproduct(vec1: Iterable[object], vec2: Iterable[object]) -> object: ... -def flatten(listOfLists: Iterable[Iterable[_T]]) -> Iterator[_T]: ... -def repeatfunc( - func: Callable[..., _U], times: Optional[int] = ..., *args: Any -) -> Iterator[_U]: ... -def pairwise(iterable: Iterable[_T]) -> Iterator[Tuple[_T, _T]]: ... -@overload -def grouper( - iterable: Iterable[_T], n: int -) -> Iterator[Tuple[Optional[_T], ...]]: ... -@overload -def grouper( - iterable: Iterable[_T], n: int, fillvalue: _U -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... -@overload -def grouper( # Deprecated interface - iterable: int, n: Iterable[_T] -) -> Iterator[Tuple[Optional[_T], ...]]: ... -@overload -def grouper( # Deprecated interface - iterable: int, n: Iterable[_T], fillvalue: _U -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... -def roundrobin(*iterables: Iterable[_T]) -> Iterator[_T]: ... -def partition( - pred: Optional[Callable[[_T], object]], iterable: Iterable[_T] -) -> Tuple[Iterator[_T], Iterator[_T]]: ... -def powerset(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... -def unique_everseen( - iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... -) -> Iterator[_T]: ... -def unique_justseen( - iterable: Iterable[_T], key: Optional[Callable[[_T], object]] = ... -) -> Iterator[_T]: ... -@overload -def iter_except( - func: Callable[[], _T], exception: Type[BaseException], first: None = ... -) -> Iterator[_T]: ... -@overload -def iter_except( - func: Callable[[], _T], - exception: Type[BaseException], - first: Callable[[], _U], -) -> Iterator[Union[_T, _U]]: ... -@overload -def first_true( - iterable: Iterable[_T], *, pred: Optional[Callable[[_T], object]] = ... -) -> Optional[_T]: ... -@overload -def first_true( - iterable: Iterable[_T], - default: _U, - pred: Optional[Callable[[_T], object]] = ..., -) -> Union[_T, _U]: ... -def random_product( - *args: Iterable[_T], repeat: int = ... -) -> Tuple[_T, ...]: ... -def random_permutation( - iterable: Iterable[_T], r: Optional[int] = ... -) -> Tuple[_T, ...]: ... -def random_combination(iterable: Iterable[_T], r: int) -> Tuple[_T, ...]: ... -def random_combination_with_replacement( - iterable: Iterable[_T], r: int -) -> Tuple[_T, ...]: ... -def nth_combination( - iterable: Iterable[_T], r: int, index: int -) -> Tuple[_T, ...]: ... -def prepend(value: _T, iterator: Iterable[_U]) -> Iterator[Union[_T, _U]]: ... -def convolve(signal: Iterable[_T], kernel: Iterable[_T]) -> Iterator[_T]: ... diff --git a/setuptools/_vendor/ordered_set.py b/setuptools/_vendor/ordered_set.py deleted file mode 100644 index 14876000de..0000000000 --- a/setuptools/_vendor/ordered_set.py +++ /dev/null @@ -1,488 +0,0 @@ -""" -An OrderedSet is a custom MutableSet that remembers its order, so that every -entry has an index that can be looked up. - -Based on a recipe originally posted to ActiveState Recipes by Raymond Hettiger, -and released under the MIT license. -""" -import itertools as it -from collections import deque - -try: - # Python 3 - from collections.abc import MutableSet, Sequence -except ImportError: - # Python 2.7 - from collections import MutableSet, Sequence - -SLICE_ALL = slice(None) -__version__ = "3.1" - - -def is_iterable(obj): - """ - Are we being asked to look up a list of things, instead of a single thing? - We check for the `__iter__` attribute so that this can cover types that - don't have to be known by this module, such as NumPy arrays. - - Strings, however, should be considered as atomic values to look up, not - iterables. The same goes for tuples, since they are immutable and therefore - valid entries. - - We don't need to check for the Python 2 `unicode` type, because it doesn't - have an `__iter__` attribute anyway. - """ - return ( - hasattr(obj, "__iter__") - and not isinstance(obj, str) - and not isinstance(obj, tuple) - ) - - -class OrderedSet(MutableSet, Sequence): - """ - An OrderedSet is a custom MutableSet that remembers its order, so that - every entry has an index that can be looked up. - - Example: - >>> OrderedSet([1, 1, 2, 3, 2]) - OrderedSet([1, 2, 3]) - """ - - def __init__(self, iterable=None): - self.items = [] - self.map = {} - if iterable is not None: - self |= iterable - - def __len__(self): - """ - Returns the number of unique elements in the ordered set - - Example: - >>> len(OrderedSet([])) - 0 - >>> len(OrderedSet([1, 2])) - 2 - """ - return len(self.items) - - def __getitem__(self, index): - """ - Get the item at a given index. - - If `index` is a slice, you will get back that slice of items, as a - new OrderedSet. - - If `index` is a list or a similar iterable, you'll get a list of - items corresponding to those indices. This is similar to NumPy's - "fancy indexing". The result is not an OrderedSet because you may ask - for duplicate indices, and the number of elements returned should be - the number of elements asked for. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset[1] - 2 - """ - if isinstance(index, slice) and index == SLICE_ALL: - return self.copy() - elif is_iterable(index): - return [self.items[i] for i in index] - elif hasattr(index, "__index__") or isinstance(index, slice): - result = self.items[index] - if isinstance(result, list): - return self.__class__(result) - else: - return result - else: - raise TypeError("Don't know how to index an OrderedSet by %r" % index) - - def copy(self): - """ - Return a shallow copy of this object. - - Example: - >>> this = OrderedSet([1, 2, 3]) - >>> other = this.copy() - >>> this == other - True - >>> this is other - False - """ - return self.__class__(self) - - def __getstate__(self): - if len(self) == 0: - # The state can't be an empty list. - # We need to return a truthy value, or else __setstate__ won't be run. - # - # This could have been done more gracefully by always putting the state - # in a tuple, but this way is backwards- and forwards- compatible with - # previous versions of OrderedSet. - return (None,) - else: - return list(self) - - def __setstate__(self, state): - if state == (None,): - self.__init__([]) - else: - self.__init__(state) - - def __contains__(self, key): - """ - Test if the item is in this ordered set - - Example: - >>> 1 in OrderedSet([1, 3, 2]) - True - >>> 5 in OrderedSet([1, 3, 2]) - False - """ - return key in self.map - - def add(self, key): - """ - Add `key` as an item to this OrderedSet, then return its index. - - If `key` is already in the OrderedSet, return the index it already - had. - - Example: - >>> oset = OrderedSet() - >>> oset.append(3) - 0 - >>> print(oset) - OrderedSet([3]) - """ - if key not in self.map: - self.map[key] = len(self.items) - self.items.append(key) - return self.map[key] - - append = add - - def update(self, sequence): - """ - Update the set with the given iterable sequence, then return the index - of the last element inserted. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.update([3, 1, 5, 1, 4]) - 4 - >>> print(oset) - OrderedSet([1, 2, 3, 5, 4]) - """ - item_index = None - try: - for item in sequence: - item_index = self.add(item) - except TypeError: - raise ValueError( - "Argument needs to be an iterable, got %s" % type(sequence) - ) - return item_index - - def index(self, key): - """ - Get the index of a given entry, raising an IndexError if it's not - present. - - `key` can be an iterable of entries that is not a string, in which case - this returns a list of indices. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.index(2) - 1 - """ - if is_iterable(key): - return [self.index(subkey) for subkey in key] - return self.map[key] - - # Provide some compatibility with pd.Index - get_loc = index - get_indexer = index - - def pop(self): - """ - Remove and return the last element from the set. - - Raises KeyError if the set is empty. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.pop() - 3 - """ - if not self.items: - raise KeyError("Set is empty") - - elem = self.items[-1] - del self.items[-1] - del self.map[elem] - return elem - - def discard(self, key): - """ - Remove an element. Do not raise an exception if absent. - - The MutableSet mixin uses this to implement the .remove() method, which - *does* raise an error when asked to remove a non-existent item. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.discard(2) - >>> print(oset) - OrderedSet([1, 3]) - >>> oset.discard(2) - >>> print(oset) - OrderedSet([1, 3]) - """ - if key in self: - i = self.map[key] - del self.items[i] - del self.map[key] - for k, v in self.map.items(): - if v >= i: - self.map[k] = v - 1 - - def clear(self): - """ - Remove all items from this OrderedSet. - """ - del self.items[:] - self.map.clear() - - def __iter__(self): - """ - Example: - >>> list(iter(OrderedSet([1, 2, 3]))) - [1, 2, 3] - """ - return iter(self.items) - - def __reversed__(self): - """ - Example: - >>> list(reversed(OrderedSet([1, 2, 3]))) - [3, 2, 1] - """ - return reversed(self.items) - - def __repr__(self): - if not self: - return "%s()" % (self.__class__.__name__,) - return "%s(%r)" % (self.__class__.__name__, list(self)) - - def __eq__(self, other): - """ - Returns true if the containers have the same items. If `other` is a - Sequence, then order is checked, otherwise it is ignored. - - Example: - >>> oset = OrderedSet([1, 3, 2]) - >>> oset == [1, 3, 2] - True - >>> oset == [1, 2, 3] - False - >>> oset == [2, 3] - False - >>> oset == OrderedSet([3, 2, 1]) - False - """ - # In Python 2 deque is not a Sequence, so treat it as one for - # consistent behavior with Python 3. - if isinstance(other, (Sequence, deque)): - # Check that this OrderedSet contains the same elements, in the - # same order, as the other object. - return list(self) == list(other) - try: - other_as_set = set(other) - except TypeError: - # If `other` can't be converted into a set, it's not equal. - return False - else: - return set(self) == other_as_set - - def union(self, *sets): - """ - Combines all unique items. - Each items order is defined by its first appearance. - - Example: - >>> oset = OrderedSet.union(OrderedSet([3, 1, 4, 1, 5]), [1, 3], [2, 0]) - >>> print(oset) - OrderedSet([3, 1, 4, 5, 2, 0]) - >>> oset.union([8, 9]) - OrderedSet([3, 1, 4, 5, 2, 0, 8, 9]) - >>> oset | {10} - OrderedSet([3, 1, 4, 5, 2, 0, 10]) - """ - cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet - containers = map(list, it.chain([self], sets)) - items = it.chain.from_iterable(containers) - return cls(items) - - def __and__(self, other): - # the parent implementation of this is backwards - return self.intersection(other) - - def intersection(self, *sets): - """ - Returns elements in common between all sets. Order is defined only - by the first set. - - Example: - >>> oset = OrderedSet.intersection(OrderedSet([0, 1, 2, 3]), [1, 2, 3]) - >>> print(oset) - OrderedSet([1, 2, 3]) - >>> oset.intersection([2, 4, 5], [1, 2, 3, 4]) - OrderedSet([2]) - >>> oset.intersection() - OrderedSet([1, 2, 3]) - """ - cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet - if sets: - common = set.intersection(*map(set, sets)) - items = (item for item in self if item in common) - else: - items = self - return cls(items) - - def difference(self, *sets): - """ - Returns all elements that are in this set but not the others. - - Example: - >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2])) - OrderedSet([1, 3]) - >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2]), OrderedSet([3])) - OrderedSet([1]) - >>> OrderedSet([1, 2, 3]) - OrderedSet([2]) - OrderedSet([1, 3]) - >>> OrderedSet([1, 2, 3]).difference() - OrderedSet([1, 2, 3]) - """ - cls = self.__class__ - if sets: - other = set.union(*map(set, sets)) - items = (item for item in self if item not in other) - else: - items = self - return cls(items) - - def issubset(self, other): - """ - Report whether another set contains this set. - - Example: - >>> OrderedSet([1, 2, 3]).issubset({1, 2}) - False - >>> OrderedSet([1, 2, 3]).issubset({1, 2, 3, 4}) - True - >>> OrderedSet([1, 2, 3]).issubset({1, 4, 3, 5}) - False - """ - if len(self) > len(other): # Fast check for obvious cases - return False - return all(item in other for item in self) - - def issuperset(self, other): - """ - Report whether this set contains another set. - - Example: - >>> OrderedSet([1, 2]).issuperset([1, 2, 3]) - False - >>> OrderedSet([1, 2, 3, 4]).issuperset({1, 2, 3}) - True - >>> OrderedSet([1, 4, 3, 5]).issuperset({1, 2, 3}) - False - """ - if len(self) < len(other): # Fast check for obvious cases - return False - return all(item in self for item in other) - - def symmetric_difference(self, other): - """ - Return the symmetric difference of two OrderedSets as a new set. - That is, the new set will contain all elements that are in exactly - one of the sets. - - Their order will be preserved, with elements from `self` preceding - elements from `other`. - - Example: - >>> this = OrderedSet([1, 4, 3, 5, 7]) - >>> other = OrderedSet([9, 7, 1, 3, 2]) - >>> this.symmetric_difference(other) - OrderedSet([4, 5, 9, 2]) - """ - cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet - diff1 = cls(self).difference(other) - diff2 = cls(other).difference(self) - return diff1.union(diff2) - - def _update_items(self, items): - """ - Replace the 'items' list of this OrderedSet with a new one, updating - self.map accordingly. - """ - self.items = items - self.map = {item: idx for (idx, item) in enumerate(items)} - - def difference_update(self, *sets): - """ - Update this OrderedSet to remove items from one or more other sets. - - Example: - >>> this = OrderedSet([1, 2, 3]) - >>> this.difference_update(OrderedSet([2, 4])) - >>> print(this) - OrderedSet([1, 3]) - - >>> this = OrderedSet([1, 2, 3, 4, 5]) - >>> this.difference_update(OrderedSet([2, 4]), OrderedSet([1, 4, 6])) - >>> print(this) - OrderedSet([3, 5]) - """ - items_to_remove = set() - for other in sets: - items_to_remove |= set(other) - self._update_items([item for item in self.items if item not in items_to_remove]) - - def intersection_update(self, other): - """ - Update this OrderedSet to keep only items in another set, preserving - their order in this set. - - Example: - >>> this = OrderedSet([1, 4, 3, 5, 7]) - >>> other = OrderedSet([9, 7, 1, 3, 2]) - >>> this.intersection_update(other) - >>> print(this) - OrderedSet([1, 3, 7]) - """ - other = set(other) - self._update_items([item for item in self.items if item in other]) - - def symmetric_difference_update(self, other): - """ - Update this OrderedSet to remove items from another set, then - add items from the other set that were not present in this set. - - Example: - >>> this = OrderedSet([1, 4, 3, 5, 7]) - >>> other = OrderedSet([9, 7, 1, 3, 2]) - >>> this.symmetric_difference_update(other) - >>> print(this) - OrderedSet([4, 5, 9, 2]) - """ - items_to_add = [item for item in other if item not in self] - items_to_remove = set(other) - self._update_items( - [item for item in self.items if item not in items_to_remove] + items_to_add - ) diff --git a/setuptools/_vendor/packaging/__about__.py b/setuptools/_vendor/packaging/__about__.py deleted file mode 100644 index 4d998578d7..0000000000 --- a/setuptools/_vendor/packaging/__about__.py +++ /dev/null @@ -1,27 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] - -__title__ = "packaging" -__summary__ = "Core utilities for Python packages" -__uri__ = "https://github.com/pypa/packaging" - -__version__ = "20.4" - -__author__ = "Donald Stufft and individual contributors" -__email__ = "donald@stufft.io" - -__license__ = "BSD-2-Clause or Apache-2.0" -__copyright__ = "Copyright 2014-2019 %s" % __author__ diff --git a/setuptools/_vendor/packaging/__init__.py b/setuptools/_vendor/packaging/__init__.py deleted file mode 100644 index a0cf67df52..0000000000 --- a/setuptools/_vendor/packaging/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -from .__about__ import ( - __author__, - __copyright__, - __email__, - __license__, - __summary__, - __title__, - __uri__, - __version__, -) - -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] diff --git a/setuptools/_vendor/packaging/_compat.py b/setuptools/_vendor/packaging/_compat.py deleted file mode 100644 index e54bd4ede8..0000000000 --- a/setuptools/_vendor/packaging/_compat.py +++ /dev/null @@ -1,38 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import sys - -from ._typing import TYPE_CHECKING - -if TYPE_CHECKING: # pragma: no cover - from typing import Any, Dict, Tuple, Type - - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -# flake8: noqa - -if PY3: - string_types = (str,) -else: - string_types = (basestring,) - - -def with_metaclass(meta, *bases): - # type: (Type[Any], Tuple[Type[Any], ...]) -> Any - """ - Create a base class with a metaclass. - """ - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): # type: ignore - def __new__(cls, name, this_bases, d): - # type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any - return meta(name, bases, d) - - return type.__new__(metaclass, "temporary_class", (), {}) diff --git a/setuptools/_vendor/packaging/_structures.py b/setuptools/_vendor/packaging/_structures.py deleted file mode 100644 index 800d5c5588..0000000000 --- a/setuptools/_vendor/packaging/_structures.py +++ /dev/null @@ -1,86 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - - -class InfinityType(object): - def __repr__(self): - # type: () -> str - return "Infinity" - - def __hash__(self): - # type: () -> int - return hash(repr(self)) - - def __lt__(self, other): - # type: (object) -> bool - return False - - def __le__(self, other): - # type: (object) -> bool - return False - - def __eq__(self, other): - # type: (object) -> bool - return isinstance(other, self.__class__) - - def __ne__(self, other): - # type: (object) -> bool - return not isinstance(other, self.__class__) - - def __gt__(self, other): - # type: (object) -> bool - return True - - def __ge__(self, other): - # type: (object) -> bool - return True - - def __neg__(self): - # type: (object) -> NegativeInfinityType - return NegativeInfinity - - -Infinity = InfinityType() - - -class NegativeInfinityType(object): - def __repr__(self): - # type: () -> str - return "-Infinity" - - def __hash__(self): - # type: () -> int - return hash(repr(self)) - - def __lt__(self, other): - # type: (object) -> bool - return True - - def __le__(self, other): - # type: (object) -> bool - return True - - def __eq__(self, other): - # type: (object) -> bool - return isinstance(other, self.__class__) - - def __ne__(self, other): - # type: (object) -> bool - return not isinstance(other, self.__class__) - - def __gt__(self, other): - # type: (object) -> bool - return False - - def __ge__(self, other): - # type: (object) -> bool - return False - - def __neg__(self): - # type: (object) -> InfinityType - return Infinity - - -NegativeInfinity = NegativeInfinityType() diff --git a/setuptools/_vendor/packaging/_typing.py b/setuptools/_vendor/packaging/_typing.py deleted file mode 100644 index 77a8b9185a..0000000000 --- a/setuptools/_vendor/packaging/_typing.py +++ /dev/null @@ -1,48 +0,0 @@ -"""For neatly implementing static typing in packaging. - -`mypy` - the static type analysis tool we use - uses the `typing` module, which -provides core functionality fundamental to mypy's functioning. - -Generally, `typing` would be imported at runtime and used in that fashion - -it acts as a no-op at runtime and does not have any run-time overhead by -design. - -As it turns out, `typing` is not vendorable - it uses separate sources for -Python 2/Python 3. Thus, this codebase can not expect it to be present. -To work around this, mypy allows the typing import to be behind a False-y -optional to prevent it from running at runtime and type-comments can be used -to remove the need for the types to be accessible directly during runtime. - -This module provides the False-y guard in a nicely named fashion so that a -curious maintainer can reach here to read this. - -In packaging, all static-typing related imports should be guarded as follows: - - from packaging._typing import TYPE_CHECKING - - if TYPE_CHECKING: - from typing import ... - -Ref: https://github.com/python/mypy/issues/3216 -""" - -__all__ = ["TYPE_CHECKING", "cast"] - -# The TYPE_CHECKING constant defined by the typing module is False at runtime -# but True while type checking. -if False: # pragma: no cover - from typing import TYPE_CHECKING -else: - TYPE_CHECKING = False - -# typing's cast syntax requires calling typing.cast at runtime, but we don't -# want to import typing at runtime. Here, we inform the type checkers that -# we're importing `typing.cast` as `cast` and re-implement typing.cast's -# runtime behavior in a block that is ignored by type checkers. -if TYPE_CHECKING: # pragma: no cover - # not executed at runtime - from typing import cast -else: - # executed at runtime - def cast(type_, value): # noqa - return value diff --git a/setuptools/_vendor/packaging/markers.py b/setuptools/_vendor/packaging/markers.py deleted file mode 100644 index 03fbdfcc94..0000000000 --- a/setuptools/_vendor/packaging/markers.py +++ /dev/null @@ -1,328 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import operator -import os -import platform -import sys - -from setuptools.extern.pyparsing import ParseException, ParseResults, stringStart, stringEnd -from setuptools.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString -from setuptools.extern.pyparsing import Literal as L # noqa - -from ._compat import string_types -from ._typing import TYPE_CHECKING -from .specifiers import Specifier, InvalidSpecifier - -if TYPE_CHECKING: # pragma: no cover - from typing import Any, Callable, Dict, List, Optional, Tuple, Union - - Operator = Callable[[str, str], bool] - - -__all__ = [ - "InvalidMarker", - "UndefinedComparison", - "UndefinedEnvironmentName", - "Marker", - "default_environment", -] - - -class InvalidMarker(ValueError): - """ - An invalid marker was found, users should refer to PEP 508. - """ - - -class UndefinedComparison(ValueError): - """ - An invalid operation was attempted on a value that doesn't support it. - """ - - -class UndefinedEnvironmentName(ValueError): - """ - A name was attempted to be used that does not exist inside of the - environment. - """ - - -class Node(object): - def __init__(self, value): - # type: (Any) -> None - self.value = value - - def __str__(self): - # type: () -> str - return str(self.value) - - def __repr__(self): - # type: () -> str - return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) - - def serialize(self): - # type: () -> str - raise NotImplementedError - - -class Variable(Node): - def serialize(self): - # type: () -> str - return str(self) - - -class Value(Node): - def serialize(self): - # type: () -> str - return '"{0}"'.format(self) - - -class Op(Node): - def serialize(self): - # type: () -> str - return str(self) - - -VARIABLE = ( - L("implementation_version") - | L("platform_python_implementation") - | L("implementation_name") - | L("python_full_version") - | L("platform_release") - | L("platform_version") - | L("platform_machine") - | L("platform_system") - | L("python_version") - | L("sys_platform") - | L("os_name") - | L("os.name") # PEP-345 - | L("sys.platform") # PEP-345 - | L("platform.version") # PEP-345 - | L("platform.machine") # PEP-345 - | L("platform.python_implementation") # PEP-345 - | L("python_implementation") # undocumented setuptools legacy - | L("extra") # PEP-508 -) -ALIASES = { - "os.name": "os_name", - "sys.platform": "sys_platform", - "platform.version": "platform_version", - "platform.machine": "platform_machine", - "platform.python_implementation": "platform_python_implementation", - "python_implementation": "platform_python_implementation", -} -VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) - -VERSION_CMP = ( - L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") -) - -MARKER_OP = VERSION_CMP | L("not in") | L("in") -MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) - -MARKER_VALUE = QuotedString("'") | QuotedString('"') -MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) - -BOOLOP = L("and") | L("or") - -MARKER_VAR = VARIABLE | MARKER_VALUE - -MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) -MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) - -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() - -MARKER_EXPR = Forward() -MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) -MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) - -MARKER = stringStart + MARKER_EXPR + stringEnd - - -def _coerce_parse_result(results): - # type: (Union[ParseResults, List[Any]]) -> List[Any] - if isinstance(results, ParseResults): - return [_coerce_parse_result(i) for i in results] - else: - return results - - -def _format_marker(marker, first=True): - # type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str - - assert isinstance(marker, (list, tuple, string_types)) - - # Sometimes we have a structure like [[...]] which is a single item list - # where the single item is itself it's own list. In that case we want skip - # the rest of this function so that we don't get extraneous () on the - # outside. - if ( - isinstance(marker, list) - and len(marker) == 1 - and isinstance(marker[0], (list, tuple)) - ): - return _format_marker(marker[0]) - - if isinstance(marker, list): - inner = (_format_marker(m, first=False) for m in marker) - if first: - return " ".join(inner) - else: - return "(" + " ".join(inner) + ")" - elif isinstance(marker, tuple): - return " ".join([m.serialize() for m in marker]) - else: - return marker - - -_operators = { - "in": lambda lhs, rhs: lhs in rhs, - "not in": lambda lhs, rhs: lhs not in rhs, - "<": operator.lt, - "<=": operator.le, - "==": operator.eq, - "!=": operator.ne, - ">=": operator.ge, - ">": operator.gt, -} # type: Dict[str, Operator] - - -def _eval_op(lhs, op, rhs): - # type: (str, Op, str) -> bool - try: - spec = Specifier("".join([op.serialize(), rhs])) - except InvalidSpecifier: - pass - else: - return spec.contains(lhs) - - oper = _operators.get(op.serialize()) # type: Optional[Operator] - if oper is None: - raise UndefinedComparison( - "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) - ) - - return oper(lhs, rhs) - - -class Undefined(object): - pass - - -_undefined = Undefined() - - -def _get_env(environment, name): - # type: (Dict[str, str], str) -> str - value = environment.get(name, _undefined) # type: Union[str, Undefined] - - if isinstance(value, Undefined): - raise UndefinedEnvironmentName( - "{0!r} does not exist in evaluation environment.".format(name) - ) - - return value - - -def _evaluate_markers(markers, environment): - # type: (List[Any], Dict[str, str]) -> bool - groups = [[]] # type: List[List[bool]] - - for marker in markers: - assert isinstance(marker, (list, tuple, string_types)) - - if isinstance(marker, list): - groups[-1].append(_evaluate_markers(marker, environment)) - elif isinstance(marker, tuple): - lhs, op, rhs = marker - - if isinstance(lhs, Variable): - lhs_value = _get_env(environment, lhs.value) - rhs_value = rhs.value - else: - lhs_value = lhs.value - rhs_value = _get_env(environment, rhs.value) - - groups[-1].append(_eval_op(lhs_value, op, rhs_value)) - else: - assert marker in ["and", "or"] - if marker == "or": - groups.append([]) - - return any(all(item) for item in groups) - - -def format_full_version(info): - # type: (sys._version_info) -> str - version = "{0.major}.{0.minor}.{0.micro}".format(info) - kind = info.releaselevel - if kind != "final": - version += kind[0] + str(info.serial) - return version - - -def default_environment(): - # type: () -> Dict[str, str] - if hasattr(sys, "implementation"): - # Ignoring the `sys.implementation` reference for type checking due to - # mypy not liking that the attribute doesn't exist in Python 2.7 when - # run with the `--py27` flag. - iver = format_full_version(sys.implementation.version) # type: ignore - implementation_name = sys.implementation.name # type: ignore - else: - iver = "0" - implementation_name = "" - - return { - "implementation_name": implementation_name, - "implementation_version": iver, - "os_name": os.name, - "platform_machine": platform.machine(), - "platform_release": platform.release(), - "platform_system": platform.system(), - "platform_version": platform.version(), - "python_full_version": platform.python_version(), - "platform_python_implementation": platform.python_implementation(), - "python_version": ".".join(platform.python_version_tuple()[:2]), - "sys_platform": sys.platform, - } - - -class Marker(object): - def __init__(self, marker): - # type: (str) -> None - try: - self._markers = _coerce_parse_result(MARKER.parseString(marker)) - except ParseException as e: - err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( - marker, marker[e.loc : e.loc + 8] - ) - raise InvalidMarker(err_str) - - def __str__(self): - # type: () -> str - return _format_marker(self._markers) - - def __repr__(self): - # type: () -> str - return "".format(str(self)) - - def evaluate(self, environment=None): - # type: (Optional[Dict[str, str]]) -> bool - """Evaluate a marker. - - Return the boolean from evaluating the given marker against the - environment. environment is an optional argument to override all or - part of the determined environment. - - The environment is determined from the current Python process. - """ - current_environment = default_environment() - if environment is not None: - current_environment.update(environment) - - return _evaluate_markers(self._markers, current_environment) diff --git a/setuptools/_vendor/packaging/py.typed b/setuptools/_vendor/packaging/py.typed deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/setuptools/_vendor/packaging/requirements.py b/setuptools/_vendor/packaging/requirements.py deleted file mode 100644 index 5d50c7d7e2..0000000000 --- a/setuptools/_vendor/packaging/requirements.py +++ /dev/null @@ -1,145 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import string -import re - -from setuptools.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException -from setuptools.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine -from setuptools.extern.pyparsing import Literal as L # noqa -from urllib import parse as urlparse - -from ._typing import TYPE_CHECKING -from .markers import MARKER_EXPR, Marker -from .specifiers import LegacySpecifier, Specifier, SpecifierSet - -if TYPE_CHECKING: # pragma: no cover - from typing import List - - -class InvalidRequirement(ValueError): - """ - An invalid requirement was found, users should refer to PEP 508. - """ - - -ALPHANUM = Word(string.ascii_letters + string.digits) - -LBRACKET = L("[").suppress() -RBRACKET = L("]").suppress() -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() -COMMA = L(",").suppress() -SEMICOLON = L(";").suppress() -AT = L("@").suppress() - -PUNCTUATION = Word("-_.") -IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) -IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) - -NAME = IDENTIFIER("name") -EXTRA = IDENTIFIER - -URI = Regex(r"[^ ]+")("url") -URL = AT + URI - -EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) -EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") - -VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) -VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) - -VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY -VERSION_MANY = Combine( - VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False -)("_raw_spec") -_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) -_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") - -VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") -VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) - -MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") -MARKER_EXPR.setParseAction( - lambda s, l, t: Marker(s[t._original_start : t._original_end]) -) -MARKER_SEPARATOR = SEMICOLON -MARKER = MARKER_SEPARATOR + MARKER_EXPR - -VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) -URL_AND_MARKER = URL + Optional(MARKER) - -NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) - -REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd -# setuptools.extern.pyparsing isn't thread safe during initialization, so we do it eagerly, see -# issue #104 -REQUIREMENT.parseString("x[]") - - -class Requirement(object): - """Parse a requirement. - - Parse a given requirement string into its parts, such as name, specifier, - URL, and extras. Raises InvalidRequirement on a badly-formed requirement - string. - """ - - # TODO: Can we test whether something is contained within a requirement? - # If so how do we do that? Do we need to test against the _name_ of - # the thing as well as the version? What about the markers? - # TODO: Can we normalize the name and extra name? - - def __init__(self, requirement_string): - # type: (str) -> None - try: - req = REQUIREMENT.parseString(requirement_string) - except ParseException as e: - raise InvalidRequirement( - 'Parse error at "{0!r}": {1}'.format( - requirement_string[e.loc : e.loc + 8], e.msg - ) - ) - - self.name = req.name - if req.url: - parsed_url = urlparse.urlparse(req.url) - if parsed_url.scheme == "file": - if urlparse.urlunparse(parsed_url) != req.url: - raise InvalidRequirement("Invalid URL given") - elif not (parsed_url.scheme and parsed_url.netloc) or ( - not parsed_url.scheme and not parsed_url.netloc - ): - raise InvalidRequirement("Invalid URL: {0}".format(req.url)) - self.url = req.url - else: - self.url = None - self.extras = set(req.extras.asList() if req.extras else []) - self.specifier = SpecifierSet(req.specifier) - self.marker = req.marker if req.marker else None - - def __str__(self): - # type: () -> str - parts = [self.name] # type: List[str] - - if self.extras: - parts.append("[{0}]".format(",".join(sorted(self.extras)))) - - if self.specifier: - parts.append(str(self.specifier)) - - if self.url: - parts.append("@ {0}".format(self.url)) - if self.marker: - parts.append(" ") - - if self.marker: - parts.append("; {0}".format(self.marker)) - - return "".join(parts) - - def __repr__(self): - # type: () -> str - return "".format(str(self)) diff --git a/setuptools/_vendor/packaging/specifiers.py b/setuptools/_vendor/packaging/specifiers.py deleted file mode 100644 index fe09bb1dbb..0000000000 --- a/setuptools/_vendor/packaging/specifiers.py +++ /dev/null @@ -1,863 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import abc -import functools -import itertools -import re - -from ._compat import string_types, with_metaclass -from ._typing import TYPE_CHECKING -from .utils import canonicalize_version -from .version import Version, LegacyVersion, parse - -if TYPE_CHECKING: # pragma: no cover - from typing import ( - List, - Dict, - Union, - Iterable, - Iterator, - Optional, - Callable, - Tuple, - FrozenSet, - ) - - ParsedVersion = Union[Version, LegacyVersion] - UnparsedVersion = Union[Version, LegacyVersion, str] - CallableOperator = Callable[[ParsedVersion, str], bool] - - -class InvalidSpecifier(ValueError): - """ - An invalid specifier was found, users should refer to PEP 440. - """ - - -class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore - @abc.abstractmethod - def __str__(self): - # type: () -> str - """ - Returns the str representation of this Specifier like object. This - should be representative of the Specifier itself. - """ - - @abc.abstractmethod - def __hash__(self): - # type: () -> int - """ - Returns a hash value for this Specifier like object. - """ - - @abc.abstractmethod - def __eq__(self, other): - # type: (object) -> bool - """ - Returns a boolean representing whether or not the two Specifier like - objects are equal. - """ - - @abc.abstractmethod - def __ne__(self, other): - # type: (object) -> bool - """ - Returns a boolean representing whether or not the two Specifier like - objects are not equal. - """ - - @abc.abstractproperty - def prereleases(self): - # type: () -> Optional[bool] - """ - Returns whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None - """ - Sets whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @abc.abstractmethod - def contains(self, item, prereleases=None): - # type: (str, Optional[bool]) -> bool - """ - Determines if the given item is contained within this specifier. - """ - - @abc.abstractmethod - def filter(self, iterable, prereleases=None): - # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] - """ - Takes an iterable of items and filters them so that only items which - are contained within this specifier are allowed in it. - """ - - -class _IndividualSpecifier(BaseSpecifier): - - _operators = {} # type: Dict[str, str] - - def __init__(self, spec="", prereleases=None): - # type: (str, Optional[bool]) -> None - match = self._regex.search(spec) - if not match: - raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) - - self._spec = ( - match.group("operator").strip(), - match.group("version").strip(), - ) # type: Tuple[str, str] - - # Store whether or not this Specifier should accept prereleases - self._prereleases = prereleases - - def __repr__(self): - # type: () -> str - pre = ( - ", prereleases={0!r}".format(self.prereleases) - if self._prereleases is not None - else "" - ) - - return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre) - - def __str__(self): - # type: () -> str - return "{0}{1}".format(*self._spec) - - @property - def _canonical_spec(self): - # type: () -> Tuple[str, Union[Version, str]] - return self._spec[0], canonicalize_version(self._spec[1]) - - def __hash__(self): - # type: () -> int - return hash(self._canonical_spec) - - def __eq__(self, other): - # type: (object) -> bool - if isinstance(other, string_types): - try: - other = self.__class__(str(other)) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._canonical_spec == other._canonical_spec - - def __ne__(self, other): - # type: (object) -> bool - if isinstance(other, string_types): - try: - other = self.__class__(str(other)) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._spec != other._spec - - def _get_operator(self, op): - # type: (str) -> CallableOperator - operator_callable = getattr( - self, "_compare_{0}".format(self._operators[op]) - ) # type: CallableOperator - return operator_callable - - def _coerce_version(self, version): - # type: (UnparsedVersion) -> ParsedVersion - if not isinstance(version, (LegacyVersion, Version)): - version = parse(version) - return version - - @property - def operator(self): - # type: () -> str - return self._spec[0] - - @property - def version(self): - # type: () -> str - return self._spec[1] - - @property - def prereleases(self): - # type: () -> Optional[bool] - return self._prereleases - - @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None - self._prereleases = value - - def __contains__(self, item): - # type: (str) -> bool - return self.contains(item) - - def contains(self, item, prereleases=None): - # type: (UnparsedVersion, Optional[bool]) -> bool - - # Determine if prereleases are to be allowed or not. - if prereleases is None: - prereleases = self.prereleases - - # Normalize item to a Version or LegacyVersion, this allows us to have - # a shortcut for ``"2.0" in Specifier(">=2") - normalized_item = self._coerce_version(item) - - # Determine if we should be supporting prereleases in this specifier - # or not, if we do not support prereleases than we can short circuit - # logic if this version is a prereleases. - if normalized_item.is_prerelease and not prereleases: - return False - - # Actually do the comparison to determine if this item is contained - # within this Specifier or not. - operator_callable = self._get_operator(self.operator) # type: CallableOperator - return operator_callable(normalized_item, self.version) - - def filter(self, iterable, prereleases=None): - # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] - - yielded = False - found_prereleases = [] - - kw = {"prereleases": prereleases if prereleases is not None else True} - - # Attempt to iterate over all the values in the iterable and if any of - # them match, yield them. - for version in iterable: - parsed_version = self._coerce_version(version) - - if self.contains(parsed_version, **kw): - # If our version is a prerelease, and we were not set to allow - # prereleases, then we'll store it for later incase nothing - # else matches this specifier. - if parsed_version.is_prerelease and not ( - prereleases or self.prereleases - ): - found_prereleases.append(version) - # Either this is not a prerelease, or we should have been - # accepting prereleases from the beginning. - else: - yielded = True - yield version - - # Now that we've iterated over everything, determine if we've yielded - # any values, and if we have not and we have any prereleases stored up - # then we will go ahead and yield the prereleases. - if not yielded and found_prereleases: - for version in found_prereleases: - yield version - - -class LegacySpecifier(_IndividualSpecifier): - - _regex_str = r""" - (?P(==|!=|<=|>=|<|>)) - \s* - (?P - [^,;\s)]* # Since this is a "legacy" specifier, and the version - # string can be just about anything, we match everything - # except for whitespace, a semi-colon for marker support, - # a closing paren since versions can be enclosed in - # them, and a comma since it's a version separator. - ) - """ - - _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) - - _operators = { - "==": "equal", - "!=": "not_equal", - "<=": "less_than_equal", - ">=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - } - - def _coerce_version(self, version): - # type: (Union[ParsedVersion, str]) -> LegacyVersion - if not isinstance(version, LegacyVersion): - version = LegacyVersion(str(version)) - return version - - def _compare_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective == self._coerce_version(spec) - - def _compare_not_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective != self._coerce_version(spec) - - def _compare_less_than_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective <= self._coerce_version(spec) - - def _compare_greater_than_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective >= self._coerce_version(spec) - - def _compare_less_than(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective < self._coerce_version(spec) - - def _compare_greater_than(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective > self._coerce_version(spec) - - -def _require_version_compare( - fn # type: (Callable[[Specifier, ParsedVersion, str], bool]) -): - # type: (...) -> Callable[[Specifier, ParsedVersion, str], bool] - @functools.wraps(fn) - def wrapped(self, prospective, spec): - # type: (Specifier, ParsedVersion, str) -> bool - if not isinstance(prospective, Version): - return False - return fn(self, prospective, spec) - - return wrapped - - -class Specifier(_IndividualSpecifier): - - _regex_str = r""" - (?P(~=|==|!=|<=|>=|<|>|===)) - (?P - (?: - # The identity operators allow for an escape hatch that will - # do an exact string match of the version you wish to install. - # This will not be parsed by PEP 440 and we cannot determine - # any semantic meaning from it. This operator is discouraged - # but included entirely as an escape hatch. - (?<====) # Only match for the identity operator - \s* - [^\s]* # We just match everything, except for whitespace - # since we are only testing for strict identity. - ) - | - (?: - # The (non)equality operators allow for wild card and local - # versions to be specified so we have to define these two - # operators separately to enable that. - (?<===|!=) # Only match for equals and not equals - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)* # release - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - - # You cannot use a wild card and a dev or local version - # together so group them with a | and make them optional. - (?: - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local - | - \.\* # Wild card syntax of .* - )? - ) - | - (?: - # The compatible operator requires at least two digits in the - # release segment. - (?<=~=) # Only match for the compatible operator - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - ) - | - (?: - # All other operators only allow a sub set of what the - # (non)equality operators do. Specifically they do not allow - # local versions to be specified nor do they allow the prefix - # matching wild cards. - (?=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - "===": "arbitrary", - } - - @_require_version_compare - def _compare_compatible(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - - # Compatible releases have an equivalent combination of >= and ==. That - # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to - # implement this in terms of the other specifiers instead of - # implementing it ourselves. The only thing we need to do is construct - # the other specifiers. - - # We want everything but the last item in the version, but we want to - # ignore post and dev releases and we want to treat the pre-release as - # it's own separate segment. - prefix = ".".join( - list( - itertools.takewhile( - lambda x: (not x.startswith("post") and not x.startswith("dev")), - _version_split(spec), - ) - )[:-1] - ) - - # Add the prefix notation to the end of our string - prefix += ".*" - - return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( - prospective, prefix - ) - - @_require_version_compare - def _compare_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - - # We need special logic to handle prefix matching - if spec.endswith(".*"): - # In the case of prefix matching we want to ignore local segment. - prospective = Version(prospective.public) - # Split the spec out by dots, and pretend that there is an implicit - # dot in between a release segment and a pre-release segment. - split_spec = _version_split(spec[:-2]) # Remove the trailing .* - - # Split the prospective version out by dots, and pretend that there - # is an implicit dot in between a release segment and a pre-release - # segment. - split_prospective = _version_split(str(prospective)) - - # Shorten the prospective version to be the same length as the spec - # so that we can determine if the specifier is a prefix of the - # prospective version or not. - shortened_prospective = split_prospective[: len(split_spec)] - - # Pad out our two sides with zeros so that they both equal the same - # length. - padded_spec, padded_prospective = _pad_version( - split_spec, shortened_prospective - ) - - return padded_prospective == padded_spec - else: - # Convert our spec string into a Version - spec_version = Version(spec) - - # If the specifier does not have a local segment, then we want to - # act as if the prospective version also does not have a local - # segment. - if not spec_version.local: - prospective = Version(prospective.public) - - return prospective == spec_version - - @_require_version_compare - def _compare_not_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - return not self._compare_equal(prospective, spec) - - @_require_version_compare - def _compare_less_than_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return Version(prospective.public) <= Version(spec) - - @_require_version_compare - def _compare_greater_than_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return Version(prospective.public) >= Version(spec) - - @_require_version_compare - def _compare_less_than(self, prospective, spec_str): - # type: (ParsedVersion, str) -> bool - - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec_str) - - # Check to see if the prospective version is less than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective < spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a pre-release version, that we do not accept pre-release - # versions for the version mentioned in the specifier (e.g. <3.1 should - # not match 3.1.dev0, but should match 3.0.dev0). - if not spec.is_prerelease and prospective.is_prerelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # less than the spec version *and* it's not a pre-release of the same - # version in the spec. - return True - - @_require_version_compare - def _compare_greater_than(self, prospective, spec_str): - # type: (ParsedVersion, str) -> bool - - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec_str) - - # Check to see if the prospective version is greater than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective > spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a post-release version, that we do not accept - # post-release versions for the version mentioned in the specifier - # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). - if not spec.is_postrelease and prospective.is_postrelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # Ensure that we do not allow a local version of the version mentioned - # in the specifier, which is technically greater than, to match. - if prospective.local is not None: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # greater than the spec version *and* it's not a pre-release of the - # same version in the spec. - return True - - def _compare_arbitrary(self, prospective, spec): - # type: (Version, str) -> bool - return str(prospective).lower() == str(spec).lower() - - @property - def prereleases(self): - # type: () -> bool - - # If there is an explicit prereleases set for this, then we'll just - # blindly use that. - if self._prereleases is not None: - return self._prereleases - - # Look at all of our specifiers and determine if they are inclusive - # operators, and if they are if they are including an explicit - # prerelease. - operator, version = self._spec - if operator in ["==", ">=", "<=", "~=", "==="]: - # The == specifier can include a trailing .*, if it does we - # want to remove before parsing. - if operator == "==" and version.endswith(".*"): - version = version[:-2] - - # Parse the version, and if it is a pre-release than this - # specifier allows pre-releases. - if parse(version).is_prerelease: - return True - - return False - - @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None - self._prereleases = value - - -_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") - - -def _version_split(version): - # type: (str) -> List[str] - result = [] # type: List[str] - for item in version.split("."): - match = _prefix_regex.search(item) - if match: - result.extend(match.groups()) - else: - result.append(item) - return result - - -def _pad_version(left, right): - # type: (List[str], List[str]) -> Tuple[List[str], List[str]] - left_split, right_split = [], [] - - # Get the release segment of our versions - left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) - right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) - - # Get the rest of our versions - left_split.append(left[len(left_split[0]) :]) - right_split.append(right[len(right_split[0]) :]) - - # Insert our padding - left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) - right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) - - return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) - - -class SpecifierSet(BaseSpecifier): - def __init__(self, specifiers="", prereleases=None): - # type: (str, Optional[bool]) -> None - - # Split on , to break each individual specifier into it's own item, and - # strip each item to remove leading/trailing whitespace. - split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] - - # Parsed each individual specifier, attempting first to make it a - # Specifier and falling back to a LegacySpecifier. - parsed = set() - for specifier in split_specifiers: - try: - parsed.add(Specifier(specifier)) - except InvalidSpecifier: - parsed.add(LegacySpecifier(specifier)) - - # Turn our parsed specifiers into a frozen set and save them for later. - self._specs = frozenset(parsed) - - # Store our prereleases value so we can use it later to determine if - # we accept prereleases or not. - self._prereleases = prereleases - - def __repr__(self): - # type: () -> str - pre = ( - ", prereleases={0!r}".format(self.prereleases) - if self._prereleases is not None - else "" - ) - - return "".format(str(self), pre) - - def __str__(self): - # type: () -> str - return ",".join(sorted(str(s) for s in self._specs)) - - def __hash__(self): - # type: () -> int - return hash(self._specs) - - def __and__(self, other): - # type: (Union[SpecifierSet, str]) -> SpecifierSet - if isinstance(other, string_types): - other = SpecifierSet(other) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - specifier = SpecifierSet() - specifier._specs = frozenset(self._specs | other._specs) - - if self._prereleases is None and other._prereleases is not None: - specifier._prereleases = other._prereleases - elif self._prereleases is not None and other._prereleases is None: - specifier._prereleases = self._prereleases - elif self._prereleases == other._prereleases: - specifier._prereleases = self._prereleases - else: - raise ValueError( - "Cannot combine SpecifierSets with True and False prerelease " - "overrides." - ) - - return specifier - - def __eq__(self, other): - # type: (object) -> bool - if isinstance(other, (string_types, _IndividualSpecifier)): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs == other._specs - - def __ne__(self, other): - # type: (object) -> bool - if isinstance(other, (string_types, _IndividualSpecifier)): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs != other._specs - - def __len__(self): - # type: () -> int - return len(self._specs) - - def __iter__(self): - # type: () -> Iterator[FrozenSet[_IndividualSpecifier]] - return iter(self._specs) - - @property - def prereleases(self): - # type: () -> Optional[bool] - - # If we have been given an explicit prerelease modifier, then we'll - # pass that through here. - if self._prereleases is not None: - return self._prereleases - - # If we don't have any specifiers, and we don't have a forced value, - # then we'll just return None since we don't know if this should have - # pre-releases or not. - if not self._specs: - return None - - # Otherwise we'll see if any of the given specifiers accept - # prereleases, if any of them do we'll return True, otherwise False. - return any(s.prereleases for s in self._specs) - - @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None - self._prereleases = value - - def __contains__(self, item): - # type: (Union[ParsedVersion, str]) -> bool - return self.contains(item) - - def contains(self, item, prereleases=None): - # type: (Union[ParsedVersion, str], Optional[bool]) -> bool - - # Ensure that our item is a Version or LegacyVersion instance. - if not isinstance(item, (LegacyVersion, Version)): - item = parse(item) - - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # We can determine if we're going to allow pre-releases by looking to - # see if any of the underlying items supports them. If none of them do - # and this item is a pre-release then we do not allow it and we can - # short circuit that here. - # Note: This means that 1.0.dev1 would not be contained in something - # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 - if not prereleases and item.is_prerelease: - return False - - # We simply dispatch to the underlying specs here to make sure that the - # given version is contained within all of them. - # Note: This use of all() here means that an empty set of specifiers - # will always return True, this is an explicit design decision. - return all(s.contains(item, prereleases=prereleases) for s in self._specs) - - def filter( - self, - iterable, # type: Iterable[Union[ParsedVersion, str]] - prereleases=None, # type: Optional[bool] - ): - # type: (...) -> Iterable[Union[ParsedVersion, str]] - - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # If we have any specifiers, then we want to wrap our iterable in the - # filter method for each one, this will act as a logical AND amongst - # each specifier. - if self._specs: - for spec in self._specs: - iterable = spec.filter(iterable, prereleases=bool(prereleases)) - return iterable - # If we do not have any specifiers, then we need to have a rough filter - # which will filter out any pre-releases, unless there are no final - # releases, and which will filter out LegacyVersion in general. - else: - filtered = [] # type: List[Union[ParsedVersion, str]] - found_prereleases = [] # type: List[Union[ParsedVersion, str]] - - for item in iterable: - # Ensure that we some kind of Version class for this item. - if not isinstance(item, (LegacyVersion, Version)): - parsed_version = parse(item) - else: - parsed_version = item - - # Filter out any item which is parsed as a LegacyVersion - if isinstance(parsed_version, LegacyVersion): - continue - - # Store any item which is a pre-release for later unless we've - # already found a final version or we are accepting prereleases - if parsed_version.is_prerelease and not prereleases: - if not filtered: - found_prereleases.append(item) - else: - filtered.append(item) - - # If we've found no items except for pre-releases, then we'll go - # ahead and use the pre-releases - if not filtered and found_prereleases and prereleases is None: - return found_prereleases - - return filtered diff --git a/setuptools/_vendor/packaging/tags.py b/setuptools/_vendor/packaging/tags.py deleted file mode 100644 index 9064910b8b..0000000000 --- a/setuptools/_vendor/packaging/tags.py +++ /dev/null @@ -1,751 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import absolute_import - -import distutils.util - -try: - from importlib.machinery import EXTENSION_SUFFIXES -except ImportError: # pragma: no cover - import imp - - EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] - del imp -import logging -import os -import platform -import re -import struct -import sys -import sysconfig -import warnings - -from ._typing import TYPE_CHECKING, cast - -if TYPE_CHECKING: # pragma: no cover - from typing import ( - Dict, - FrozenSet, - IO, - Iterable, - Iterator, - List, - Optional, - Sequence, - Tuple, - Union, - ) - - PythonVersion = Sequence[int] - MacVersion = Tuple[int, int] - GlibcVersion = Tuple[int, int] - - -logger = logging.getLogger(__name__) - -INTERPRETER_SHORT_NAMES = { - "python": "py", # Generic. - "cpython": "cp", - "pypy": "pp", - "ironpython": "ip", - "jython": "jy", -} # type: Dict[str, str] - - -_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 - - -class Tag(object): - """ - A representation of the tag triple for a wheel. - - Instances are considered immutable and thus are hashable. Equality checking - is also supported. - """ - - __slots__ = ["_interpreter", "_abi", "_platform"] - - def __init__(self, interpreter, abi, platform): - # type: (str, str, str) -> None - self._interpreter = interpreter.lower() - self._abi = abi.lower() - self._platform = platform.lower() - - @property - def interpreter(self): - # type: () -> str - return self._interpreter - - @property - def abi(self): - # type: () -> str - return self._abi - - @property - def platform(self): - # type: () -> str - return self._platform - - def __eq__(self, other): - # type: (object) -> bool - if not isinstance(other, Tag): - return NotImplemented - - return ( - (self.platform == other.platform) - and (self.abi == other.abi) - and (self.interpreter == other.interpreter) - ) - - def __hash__(self): - # type: () -> int - return hash((self._interpreter, self._abi, self._platform)) - - def __str__(self): - # type: () -> str - return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) - - def __repr__(self): - # type: () -> str - return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) - - -def parse_tag(tag): - # type: (str) -> FrozenSet[Tag] - """ - Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. - - Returning a set is required due to the possibility that the tag is a - compressed tag set. - """ - tags = set() - interpreters, abis, platforms = tag.split("-") - for interpreter in interpreters.split("."): - for abi in abis.split("."): - for platform_ in platforms.split("."): - tags.add(Tag(interpreter, abi, platform_)) - return frozenset(tags) - - -def _warn_keyword_parameter(func_name, kwargs): - # type: (str, Dict[str, bool]) -> bool - """ - Backwards-compatibility with Python 2.7 to allow treating 'warn' as keyword-only. - """ - if not kwargs: - return False - elif len(kwargs) > 1 or "warn" not in kwargs: - kwargs.pop("warn", None) - arg = next(iter(kwargs.keys())) - raise TypeError( - "{}() got an unexpected keyword argument {!r}".format(func_name, arg) - ) - return kwargs["warn"] - - -def _get_config_var(name, warn=False): - # type: (str, bool) -> Union[int, str, None] - value = sysconfig.get_config_var(name) - if value is None and warn: - logger.debug( - "Config variable '%s' is unset, Python ABI tag may be incorrect", name - ) - return value - - -def _normalize_string(string): - # type: (str) -> str - return string.replace(".", "_").replace("-", "_") - - -def _abi3_applies(python_version): - # type: (PythonVersion) -> bool - """ - Determine if the Python version supports abi3. - - PEP 384 was first implemented in Python 3.2. - """ - return len(python_version) > 1 and tuple(python_version) >= (3, 2) - - -def _cpython_abis(py_version, warn=False): - # type: (PythonVersion, bool) -> List[str] - py_version = tuple(py_version) # To allow for version comparison. - abis = [] - version = _version_nodot(py_version[:2]) - debug = pymalloc = ucs4 = "" - with_debug = _get_config_var("Py_DEBUG", warn) - has_refcount = hasattr(sys, "gettotalrefcount") - # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled - # extension modules is the best option. - # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 - has_ext = "_d.pyd" in EXTENSION_SUFFIXES - if with_debug or (with_debug is None and (has_refcount or has_ext)): - debug = "d" - if py_version < (3, 8): - with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) - if with_pymalloc or with_pymalloc is None: - pymalloc = "m" - if py_version < (3, 3): - unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) - if unicode_size == 4 or ( - unicode_size is None and sys.maxunicode == 0x10FFFF - ): - ucs4 = "u" - elif debug: - # Debug builds can also load "normal" extension modules. - # We can also assume no UCS-4 or pymalloc requirement. - abis.append("cp{version}".format(version=version)) - abis.insert( - 0, - "cp{version}{debug}{pymalloc}{ucs4}".format( - version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4 - ), - ) - return abis - - -def cpython_tags( - python_version=None, # type: Optional[PythonVersion] - abis=None, # type: Optional[Iterable[str]] - platforms=None, # type: Optional[Iterable[str]] - **kwargs # type: bool -): - # type: (...) -> Iterator[Tag] - """ - Yields the tags for a CPython interpreter. - - The tags consist of: - - cp-- - - cp-abi3- - - cp-none- - - cp-abi3- # Older Python versions down to 3.2. - - If python_version only specifies a major version then user-provided ABIs and - the 'none' ABItag will be used. - - If 'abi3' or 'none' are specified in 'abis' then they will be yielded at - their normal position and not at the beginning. - """ - warn = _warn_keyword_parameter("cpython_tags", kwargs) - if not python_version: - python_version = sys.version_info[:2] - - interpreter = "cp{}".format(_version_nodot(python_version[:2])) - - if abis is None: - if len(python_version) > 1: - abis = _cpython_abis(python_version, warn) - else: - abis = [] - abis = list(abis) - # 'abi3' and 'none' are explicitly handled later. - for explicit_abi in ("abi3", "none"): - try: - abis.remove(explicit_abi) - except ValueError: - pass - - platforms = list(platforms or _platform_tags()) - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - if _abi3_applies(python_version): - for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): - yield tag - for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): - yield tag - - if _abi3_applies(python_version): - for minor_version in range(python_version[1] - 1, 1, -1): - for platform_ in platforms: - interpreter = "cp{version}".format( - version=_version_nodot((python_version[0], minor_version)) - ) - yield Tag(interpreter, "abi3", platform_) - - -def _generic_abi(): - # type: () -> Iterator[str] - abi = sysconfig.get_config_var("SOABI") - if abi: - yield _normalize_string(abi) - - -def generic_tags( - interpreter=None, # type: Optional[str] - abis=None, # type: Optional[Iterable[str]] - platforms=None, # type: Optional[Iterable[str]] - **kwargs # type: bool -): - # type: (...) -> Iterator[Tag] - """ - Yields the tags for a generic interpreter. - - The tags consist of: - - -- - - The "none" ABI will be added if it was not explicitly provided. - """ - warn = _warn_keyword_parameter("generic_tags", kwargs) - if not interpreter: - interp_name = interpreter_name() - interp_version = interpreter_version(warn=warn) - interpreter = "".join([interp_name, interp_version]) - if abis is None: - abis = _generic_abi() - platforms = list(platforms or _platform_tags()) - abis = list(abis) - if "none" not in abis: - abis.append("none") - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - - -def _py_interpreter_range(py_version): - # type: (PythonVersion) -> Iterator[str] - """ - Yields Python versions in descending order. - - After the latest version, the major-only version will be yielded, and then - all previous versions of that major version. - """ - if len(py_version) > 1: - yield "py{version}".format(version=_version_nodot(py_version[:2])) - yield "py{major}".format(major=py_version[0]) - if len(py_version) > 1: - for minor in range(py_version[1] - 1, -1, -1): - yield "py{version}".format(version=_version_nodot((py_version[0], minor))) - - -def compatible_tags( - python_version=None, # type: Optional[PythonVersion] - interpreter=None, # type: Optional[str] - platforms=None, # type: Optional[Iterable[str]] -): - # type: (...) -> Iterator[Tag] - """ - Yields the sequence of tags that are compatible with a specific version of Python. - - The tags consist of: - - py*-none- - - -none-any # ... if `interpreter` is provided. - - py*-none-any - """ - if not python_version: - python_version = sys.version_info[:2] - platforms = list(platforms or _platform_tags()) - for version in _py_interpreter_range(python_version): - for platform_ in platforms: - yield Tag(version, "none", platform_) - if interpreter: - yield Tag(interpreter, "none", "any") - for version in _py_interpreter_range(python_version): - yield Tag(version, "none", "any") - - -def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): - # type: (str, bool) -> str - if not is_32bit: - return arch - - if arch.startswith("ppc"): - return "ppc" - - return "i386" - - -def _mac_binary_formats(version, cpu_arch): - # type: (MacVersion, str) -> List[str] - formats = [cpu_arch] - if cpu_arch == "x86_64": - if version < (10, 4): - return [] - formats.extend(["intel", "fat64", "fat32"]) - - elif cpu_arch == "i386": - if version < (10, 4): - return [] - formats.extend(["intel", "fat32", "fat"]) - - elif cpu_arch == "ppc64": - # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? - if version > (10, 5) or version < (10, 4): - return [] - formats.append("fat64") - - elif cpu_arch == "ppc": - if version > (10, 6): - return [] - formats.extend(["fat32", "fat"]) - - formats.append("universal") - return formats - - -def mac_platforms(version=None, arch=None): - # type: (Optional[MacVersion], Optional[str]) -> Iterator[str] - """ - Yields the platform tags for a macOS system. - - The `version` parameter is a two-item tuple specifying the macOS version to - generate platform tags for. The `arch` parameter is the CPU architecture to - generate platform tags for. Both parameters default to the appropriate value - for the current system. - """ - version_str, _, cpu_arch = platform.mac_ver() # type: ignore - if version is None: - version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) - else: - version = version - if arch is None: - arch = _mac_arch(cpu_arch) - else: - arch = arch - for minor_version in range(version[1], -1, -1): - compat_version = version[0], minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) - - -# From PEP 513. -def _is_manylinux_compatible(name, glibc_version): - # type: (str, GlibcVersion) -> bool - # Check for presence of _manylinux module. - try: - import _manylinux # noqa - - return bool(getattr(_manylinux, name + "_compatible")) - except (ImportError, AttributeError): - # Fall through to heuristic check below. - pass - - return _have_compatible_glibc(*glibc_version) - - -def _glibc_version_string(): - # type: () -> Optional[str] - # Returns glibc version string, or None if not using glibc. - return _glibc_version_string_confstr() or _glibc_version_string_ctypes() - - -def _glibc_version_string_confstr(): - # type: () -> Optional[str] - """ - Primary implementation of glibc_version_string using os.confstr. - """ - # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely - # to be broken or missing. This strategy is used in the standard library - # platform module. - # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 - try: - # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". - version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821 - "CS_GNU_LIBC_VERSION" - ) - assert version_string is not None - _, version = version_string.split() # type: Tuple[str, str] - except (AssertionError, AttributeError, OSError, ValueError): - # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... - return None - return version - - -def _glibc_version_string_ctypes(): - # type: () -> Optional[str] - """ - Fallback implementation of glibc_version_string using ctypes. - """ - try: - import ctypes - except ImportError: - return None - - # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen - # manpage says, "If filename is NULL, then the returned handle is for the - # main program". This way we can let the linker do the work to figure out - # which libc our process is actually using. - # - # Note: typeshed is wrong here so we are ignoring this line. - process_namespace = ctypes.CDLL(None) # type: ignore - try: - gnu_get_libc_version = process_namespace.gnu_get_libc_version - except AttributeError: - # Symbol doesn't exist -> therefore, we are not linked to - # glibc. - return None - - # Call gnu_get_libc_version, which returns a string like "2.5" - gnu_get_libc_version.restype = ctypes.c_char_p - version_str = gnu_get_libc_version() # type: str - # py2 / py3 compatibility: - if not isinstance(version_str, str): - version_str = version_str.decode("ascii") - - return version_str - - -# Separated out from have_compatible_glibc for easier unit testing. -def _check_glibc_version(version_str, required_major, minimum_minor): - # type: (str, int, int) -> bool - # Parse string and check against requested version. - # - # We use a regexp instead of str.split because we want to discard any - # random junk that might come after the minor version -- this might happen - # in patched/forked versions of glibc (e.g. Linaro's version of glibc - # uses version strings like "2.20-2014.11"). See gh-3588. - m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) - if not m: - warnings.warn( - "Expected glibc version with 2 components major.minor," - " got: %s" % version_str, - RuntimeWarning, - ) - return False - return ( - int(m.group("major")) == required_major - and int(m.group("minor")) >= minimum_minor - ) - - -def _have_compatible_glibc(required_major, minimum_minor): - # type: (int, int) -> bool - version_str = _glibc_version_string() - if version_str is None: - return False - return _check_glibc_version(version_str, required_major, minimum_minor) - - -# Python does not provide platform information at sufficient granularity to -# identify the architecture of the running executable in some cases, so we -# determine it dynamically by reading the information from the running -# process. This only applies on Linux, which uses the ELF format. -class _ELFFileHeader(object): - # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header - class _InvalidELFFileHeader(ValueError): - """ - An invalid ELF file header was found. - """ - - ELF_MAGIC_NUMBER = 0x7F454C46 - ELFCLASS32 = 1 - ELFCLASS64 = 2 - ELFDATA2LSB = 1 - ELFDATA2MSB = 2 - EM_386 = 3 - EM_S390 = 22 - EM_ARM = 40 - EM_X86_64 = 62 - EF_ARM_ABIMASK = 0xFF000000 - EF_ARM_ABI_VER5 = 0x05000000 - EF_ARM_ABI_FLOAT_HARD = 0x00000400 - - def __init__(self, file): - # type: (IO[bytes]) -> None - def unpack(fmt): - # type: (str) -> int - try: - (result,) = struct.unpack( - fmt, file.read(struct.calcsize(fmt)) - ) # type: (int, ) - except struct.error: - raise _ELFFileHeader._InvalidELFFileHeader() - return result - - self.e_ident_magic = unpack(">I") - if self.e_ident_magic != self.ELF_MAGIC_NUMBER: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_class = unpack("B") - if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_data = unpack("B") - if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_version = unpack("B") - self.e_ident_osabi = unpack("B") - self.e_ident_abiversion = unpack("B") - self.e_ident_pad = file.read(7) - format_h = "H" - format_i = "I" - format_q = "Q" - format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q - self.e_type = unpack(format_h) - self.e_machine = unpack(format_h) - self.e_version = unpack(format_i) - self.e_entry = unpack(format_p) - self.e_phoff = unpack(format_p) - self.e_shoff = unpack(format_p) - self.e_flags = unpack(format_i) - self.e_ehsize = unpack(format_h) - self.e_phentsize = unpack(format_h) - self.e_phnum = unpack(format_h) - self.e_shentsize = unpack(format_h) - self.e_shnum = unpack(format_h) - self.e_shstrndx = unpack(format_h) - - -def _get_elf_header(): - # type: () -> Optional[_ELFFileHeader] - try: - with open(sys.executable, "rb") as f: - elf_header = _ELFFileHeader(f) - except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): - return None - return elf_header - - -def _is_linux_armhf(): - # type: () -> bool - # hard-float ABI can be detected from the ELF header of the running - # process - # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_ARM - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABIMASK - ) == elf_header.EF_ARM_ABI_VER5 - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD - ) == elf_header.EF_ARM_ABI_FLOAT_HARD - return result - - -def _is_linux_i686(): - # type: () -> bool - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_386 - return result - - -def _have_compatible_manylinux_abi(arch): - # type: (str) -> bool - if arch == "armv7l": - return _is_linux_armhf() - if arch == "i686": - return _is_linux_i686() - return True - - -def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): - # type: (bool) -> Iterator[str] - linux = _normalize_string(distutils.util.get_platform()) - if is_32bit: - if linux == "linux_x86_64": - linux = "linux_i686" - elif linux == "linux_aarch64": - linux = "linux_armv7l" - manylinux_support = [] - _, arch = linux.split("_", 1) - if _have_compatible_manylinux_abi(arch): - if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}: - manylinux_support.append( - ("manylinux2014", (2, 17)) - ) # CentOS 7 w/ glibc 2.17 (PEP 599) - if arch in {"x86_64", "i686"}: - manylinux_support.append( - ("manylinux2010", (2, 12)) - ) # CentOS 6 w/ glibc 2.12 (PEP 571) - manylinux_support.append( - ("manylinux1", (2, 5)) - ) # CentOS 5 w/ glibc 2.5 (PEP 513) - manylinux_support_iter = iter(manylinux_support) - for name, glibc_version in manylinux_support_iter: - if _is_manylinux_compatible(name, glibc_version): - yield linux.replace("linux", name) - break - # Support for a later manylinux implies support for an earlier version. - for name, _ in manylinux_support_iter: - yield linux.replace("linux", name) - yield linux - - -def _generic_platforms(): - # type: () -> Iterator[str] - yield _normalize_string(distutils.util.get_platform()) - - -def _platform_tags(): - # type: () -> Iterator[str] - """ - Provides the platform tags for this installation. - """ - if platform.system() == "Darwin": - return mac_platforms() - elif platform.system() == "Linux": - return _linux_platforms() - else: - return _generic_platforms() - - -def interpreter_name(): - # type: () -> str - """ - Returns the name of the running interpreter. - """ - try: - name = sys.implementation.name # type: ignore - except AttributeError: # pragma: no cover - # Python 2.7 compatibility. - name = platform.python_implementation().lower() - return INTERPRETER_SHORT_NAMES.get(name) or name - - -def interpreter_version(**kwargs): - # type: (bool) -> str - """ - Returns the version of the running interpreter. - """ - warn = _warn_keyword_parameter("interpreter_version", kwargs) - version = _get_config_var("py_version_nodot", warn=warn) - if version: - version = str(version) - else: - version = _version_nodot(sys.version_info[:2]) - return version - - -def _version_nodot(version): - # type: (PythonVersion) -> str - if any(v >= 10 for v in version): - sep = "_" - else: - sep = "" - return sep.join(map(str, version)) - - -def sys_tags(**kwargs): - # type: (bool) -> Iterator[Tag] - """ - Returns the sequence of tag triples for the running interpreter. - - The order of the sequence corresponds to priority order for the - interpreter, from most to least important. - """ - warn = _warn_keyword_parameter("sys_tags", kwargs) - - interp_name = interpreter_name() - if interp_name == "cp": - for tag in cpython_tags(warn=warn): - yield tag - else: - for tag in generic_tags(): - yield tag - - for tag in compatible_tags(): - yield tag diff --git a/setuptools/_vendor/packaging/utils.py b/setuptools/_vendor/packaging/utils.py deleted file mode 100644 index 19579c1a0f..0000000000 --- a/setuptools/_vendor/packaging/utils.py +++ /dev/null @@ -1,65 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import re - -from ._typing import TYPE_CHECKING, cast -from .version import InvalidVersion, Version - -if TYPE_CHECKING: # pragma: no cover - from typing import NewType, Union - - NormalizedName = NewType("NormalizedName", str) - -_canonicalize_regex = re.compile(r"[-_.]+") - - -def canonicalize_name(name): - # type: (str) -> NormalizedName - # This is taken from PEP 503. - value = _canonicalize_regex.sub("-", name).lower() - return cast("NormalizedName", value) - - -def canonicalize_version(_version): - # type: (str) -> Union[Version, str] - """ - This is very similar to Version.__str__, but has one subtle difference - with the way it handles the release segment. - """ - - try: - version = Version(_version) - except InvalidVersion: - # Legacy versions cannot be normalized - return _version - - parts = [] - - # Epoch - if version.epoch != 0: - parts.append("{0}!".format(version.epoch)) - - # Release segment - # NB: This strips trailing '.0's to normalize - parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release))) - - # Pre-release - if version.pre is not None: - parts.append("".join(str(x) for x in version.pre)) - - # Post-release - if version.post is not None: - parts.append(".post{0}".format(version.post)) - - # Development release - if version.dev is not None: - parts.append(".dev{0}".format(version.dev)) - - # Local version segment - if version.local is not None: - parts.append("+{0}".format(version.local)) - - return "".join(parts) diff --git a/setuptools/_vendor/packaging/version.py b/setuptools/_vendor/packaging/version.py deleted file mode 100644 index 00371e86a8..0000000000 --- a/setuptools/_vendor/packaging/version.py +++ /dev/null @@ -1,535 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import collections -import itertools -import re - -from ._structures import Infinity, NegativeInfinity -from ._typing import TYPE_CHECKING - -if TYPE_CHECKING: # pragma: no cover - from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union - - from ._structures import InfinityType, NegativeInfinityType - - InfiniteTypes = Union[InfinityType, NegativeInfinityType] - PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] - SubLocalType = Union[InfiniteTypes, int, str] - LocalType = Union[ - NegativeInfinityType, - Tuple[ - Union[ - SubLocalType, - Tuple[SubLocalType, str], - Tuple[NegativeInfinityType, SubLocalType], - ], - ..., - ], - ] - CmpKey = Tuple[ - int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType - ] - LegacyCmpKey = Tuple[int, Tuple[str, ...]] - VersionComparisonMethod = Callable[ - [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool - ] - -__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] - - -_Version = collections.namedtuple( - "_Version", ["epoch", "release", "dev", "pre", "post", "local"] -) - - -def parse(version): - # type: (str) -> Union[LegacyVersion, Version] - """ - Parse the given version string and return either a :class:`Version` object - or a :class:`LegacyVersion` object depending on if the given version is - a valid PEP 440 version or a legacy version. - """ - try: - return Version(version) - except InvalidVersion: - return LegacyVersion(version) - - -class InvalidVersion(ValueError): - """ - An invalid version was found, users should refer to PEP 440. - """ - - -class _BaseVersion(object): - _key = None # type: Union[CmpKey, LegacyCmpKey] - - def __hash__(self): - # type: () -> int - return hash(self._key) - - def __lt__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s < o) - - def __le__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s <= o) - - def __eq__(self, other): - # type: (object) -> bool - return self._compare(other, lambda s, o: s == o) - - def __ge__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s >= o) - - def __gt__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s > o) - - def __ne__(self, other): - # type: (object) -> bool - return self._compare(other, lambda s, o: s != o) - - def _compare(self, other, method): - # type: (object, VersionComparisonMethod) -> Union[bool, NotImplemented] - if not isinstance(other, _BaseVersion): - return NotImplemented - - return method(self._key, other._key) - - -class LegacyVersion(_BaseVersion): - def __init__(self, version): - # type: (str) -> None - self._version = str(version) - self._key = _legacy_cmpkey(self._version) - - def __str__(self): - # type: () -> str - return self._version - - def __repr__(self): - # type: () -> str - return "".format(repr(str(self))) - - @property - def public(self): - # type: () -> str - return self._version - - @property - def base_version(self): - # type: () -> str - return self._version - - @property - def epoch(self): - # type: () -> int - return -1 - - @property - def release(self): - # type: () -> None - return None - - @property - def pre(self): - # type: () -> None - return None - - @property - def post(self): - # type: () -> None - return None - - @property - def dev(self): - # type: () -> None - return None - - @property - def local(self): - # type: () -> None - return None - - @property - def is_prerelease(self): - # type: () -> bool - return False - - @property - def is_postrelease(self): - # type: () -> bool - return False - - @property - def is_devrelease(self): - # type: () -> bool - return False - - -_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) - -_legacy_version_replacement_map = { - "pre": "c", - "preview": "c", - "-": "final-", - "rc": "c", - "dev": "@", -} - - -def _parse_version_parts(s): - # type: (str) -> Iterator[str] - for part in _legacy_version_component_re.split(s): - part = _legacy_version_replacement_map.get(part, part) - - if not part or part == ".": - continue - - if part[:1] in "0123456789": - # pad for numeric comparison - yield part.zfill(8) - else: - yield "*" + part - - # ensure that alpha/beta/candidate are before final - yield "*final" - - -def _legacy_cmpkey(version): - # type: (str) -> LegacyCmpKey - - # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch - # greater than or equal to 0. This will effectively put the LegacyVersion, - # which uses the defacto standard originally implemented by setuptools, - # as before all PEP 440 versions. - epoch = -1 - - # This scheme is taken from pkg_resources.parse_version setuptools prior to - # it's adoption of the packaging library. - parts = [] # type: List[str] - for part in _parse_version_parts(version.lower()): - if part.startswith("*"): - # remove "-" before a prerelease tag - if part < "*final": - while parts and parts[-1] == "*final-": - parts.pop() - - # remove trailing zeros from each series of numeric parts - while parts and parts[-1] == "00000000": - parts.pop() - - parts.append(part) - - return epoch, tuple(parts) - - -# Deliberately not anchored to the start and end of the string, to make it -# easier for 3rd party code to reuse -VERSION_PATTERN = r""" - v? - (?: - (?:(?P[0-9]+)!)? # epoch - (?P[0-9]+(?:\.[0-9]+)*) # release segment - (?P
                                          # pre-release
-            [-_\.]?
-            (?P(a|b|c|rc|alpha|beta|pre|preview))
-            [-_\.]?
-            (?P[0-9]+)?
-        )?
-        (?P                                         # post release
-            (?:-(?P[0-9]+))
-            |
-            (?:
-                [-_\.]?
-                (?Ppost|rev|r)
-                [-_\.]?
-                (?P[0-9]+)?
-            )
-        )?
-        (?P                                          # dev release
-            [-_\.]?
-            (?Pdev)
-            [-_\.]?
-            (?P[0-9]+)?
-        )?
-    )
-    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
-"""
-
-
-class Version(_BaseVersion):
-
-    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
-
-    def __init__(self, version):
-        # type: (str) -> None
-
-        # Validate the version and parse it into pieces
-        match = self._regex.search(version)
-        if not match:
-            raise InvalidVersion("Invalid version: '{0}'".format(version))
-
-        # Store the parsed out pieces of the version
-        self._version = _Version(
-            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
-            release=tuple(int(i) for i in match.group("release").split(".")),
-            pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
-            post=_parse_letter_version(
-                match.group("post_l"), match.group("post_n1") or match.group("post_n2")
-            ),
-            dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
-            local=_parse_local_version(match.group("local")),
-        )
-
-        # Generate a key which will be used for sorting
-        self._key = _cmpkey(
-            self._version.epoch,
-            self._version.release,
-            self._version.pre,
-            self._version.post,
-            self._version.dev,
-            self._version.local,
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return "".format(repr(str(self)))
-
-    def __str__(self):
-        # type: () -> str
-        parts = []
-
-        # Epoch
-        if self.epoch != 0:
-            parts.append("{0}!".format(self.epoch))
-
-        # Release segment
-        parts.append(".".join(str(x) for x in self.release))
-
-        # Pre-release
-        if self.pre is not None:
-            parts.append("".join(str(x) for x in self.pre))
-
-        # Post-release
-        if self.post is not None:
-            parts.append(".post{0}".format(self.post))
-
-        # Development release
-        if self.dev is not None:
-            parts.append(".dev{0}".format(self.dev))
-
-        # Local version segment
-        if self.local is not None:
-            parts.append("+{0}".format(self.local))
-
-        return "".join(parts)
-
-    @property
-    def epoch(self):
-        # type: () -> int
-        _epoch = self._version.epoch  # type: int
-        return _epoch
-
-    @property
-    def release(self):
-        # type: () -> Tuple[int, ...]
-        _release = self._version.release  # type: Tuple[int, ...]
-        return _release
-
-    @property
-    def pre(self):
-        # type: () -> Optional[Tuple[str, int]]
-        _pre = self._version.pre  # type: Optional[Tuple[str, int]]
-        return _pre
-
-    @property
-    def post(self):
-        # type: () -> Optional[Tuple[str, int]]
-        return self._version.post[1] if self._version.post else None
-
-    @property
-    def dev(self):
-        # type: () -> Optional[Tuple[str, int]]
-        return self._version.dev[1] if self._version.dev else None
-
-    @property
-    def local(self):
-        # type: () -> Optional[str]
-        if self._version.local:
-            return ".".join(str(x) for x in self._version.local)
-        else:
-            return None
-
-    @property
-    def public(self):
-        # type: () -> str
-        return str(self).split("+", 1)[0]
-
-    @property
-    def base_version(self):
-        # type: () -> str
-        parts = []
-
-        # Epoch
-        if self.epoch != 0:
-            parts.append("{0}!".format(self.epoch))
-
-        # Release segment
-        parts.append(".".join(str(x) for x in self.release))
-
-        return "".join(parts)
-
-    @property
-    def is_prerelease(self):
-        # type: () -> bool
-        return self.dev is not None or self.pre is not None
-
-    @property
-    def is_postrelease(self):
-        # type: () -> bool
-        return self.post is not None
-
-    @property
-    def is_devrelease(self):
-        # type: () -> bool
-        return self.dev is not None
-
-    @property
-    def major(self):
-        # type: () -> int
-        return self.release[0] if len(self.release) >= 1 else 0
-
-    @property
-    def minor(self):
-        # type: () -> int
-        return self.release[1] if len(self.release) >= 2 else 0
-
-    @property
-    def micro(self):
-        # type: () -> int
-        return self.release[2] if len(self.release) >= 3 else 0
-
-
-def _parse_letter_version(
-    letter,  # type: str
-    number,  # type: Union[str, bytes, SupportsInt]
-):
-    # type: (...) -> Optional[Tuple[str, int]]
-
-    if letter:
-        # We consider there to be an implicit 0 in a pre-release if there is
-        # not a numeral associated with it.
-        if number is None:
-            number = 0
-
-        # We normalize any letters to their lower case form
-        letter = letter.lower()
-
-        # We consider some words to be alternate spellings of other words and
-        # in those cases we want to normalize the spellings to our preferred
-        # spelling.
-        if letter == "alpha":
-            letter = "a"
-        elif letter == "beta":
-            letter = "b"
-        elif letter in ["c", "pre", "preview"]:
-            letter = "rc"
-        elif letter in ["rev", "r"]:
-            letter = "post"
-
-        return letter, int(number)
-    if not letter and number:
-        # We assume if we are given a number, but we are not given a letter
-        # then this is using the implicit post release syntax (e.g. 1.0-1)
-        letter = "post"
-
-        return letter, int(number)
-
-    return None
-
-
-_local_version_separators = re.compile(r"[\._-]")
-
-
-def _parse_local_version(local):
-    # type: (str) -> Optional[LocalType]
-    """
-    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
-    """
-    if local is not None:
-        return tuple(
-            part.lower() if not part.isdigit() else int(part)
-            for part in _local_version_separators.split(local)
-        )
-    return None
-
-
-def _cmpkey(
-    epoch,  # type: int
-    release,  # type: Tuple[int, ...]
-    pre,  # type: Optional[Tuple[str, int]]
-    post,  # type: Optional[Tuple[str, int]]
-    dev,  # type: Optional[Tuple[str, int]]
-    local,  # type: Optional[Tuple[SubLocalType]]
-):
-    # type: (...) -> CmpKey
-
-    # When we compare a release version, we want to compare it with all of the
-    # trailing zeros removed. So we'll use a reverse the list, drop all the now
-    # leading zeros until we come to something non zero, then take the rest
-    # re-reverse it back into the correct order and make it a tuple and use
-    # that for our sorting key.
-    _release = tuple(
-        reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
-    )
-
-    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
-    # We'll do this by abusing the pre segment, but we _only_ want to do this
-    # if there is not a pre or a post segment. If we have one of those then
-    # the normal sorting rules will handle this case correctly.
-    if pre is None and post is None and dev is not None:
-        _pre = NegativeInfinity  # type: PrePostDevType
-    # Versions without a pre-release (except as noted above) should sort after
-    # those with one.
-    elif pre is None:
-        _pre = Infinity
-    else:
-        _pre = pre
-
-    # Versions without a post segment should sort before those with one.
-    if post is None:
-        _post = NegativeInfinity  # type: PrePostDevType
-
-    else:
-        _post = post
-
-    # Versions without a development segment should sort after those with one.
-    if dev is None:
-        _dev = Infinity  # type: PrePostDevType
-
-    else:
-        _dev = dev
-
-    if local is None:
-        # Versions without a local segment should sort before those with one.
-        _local = NegativeInfinity  # type: LocalType
-    else:
-        # Versions with a local segment need that segment parsed to implement
-        # the sorting rules in PEP440.
-        # - Alpha numeric segments sort before numeric segments
-        # - Alpha numeric segments sort lexicographically
-        # - Numeric segments sort numerically
-        # - Shorter versions sort before longer versions when the prefixes
-        #   match exactly
-        _local = tuple(
-            (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local
-        )
-
-    return epoch, _release, _pre, _post, _dev, _local
diff --git a/setuptools/_vendor/pyparsing.py b/setuptools/_vendor/pyparsing.py
deleted file mode 100644
index 4cae788387..0000000000
--- a/setuptools/_vendor/pyparsing.py
+++ /dev/null
@@ -1,5742 +0,0 @@
-# module pyparsing.py
-#
-# Copyright (c) 2003-2018  Paul T. McGuire
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__doc__ = \
-"""
-pyparsing module - Classes and methods to define and execute parsing grammars
-=============================================================================
-
-The pyparsing module is an alternative approach to creating and executing simple grammars,
-vs. the traditional lex/yacc approach, or the use of regular expressions.  With pyparsing, you
-don't need to learn a new syntax for defining grammars or matching expressions - the parsing module
-provides a library of classes that you use to construct the grammar directly in Python.
-
-Here is a program to parse "Hello, World!" (or any greeting of the form 
-C{", !"}), built up using L{Word}, L{Literal}, and L{And} elements 
-(L{'+'} operator gives L{And} expressions, strings are auto-converted to
-L{Literal} expressions)::
-
-    from pyparsing import Word, alphas
-
-    # define grammar of a greeting
-    greet = Word(alphas) + "," + Word(alphas) + "!"
-
-    hello = "Hello, World!"
-    print (hello, "->", greet.parseString(hello))
-
-The program outputs the following::
-
-    Hello, World! -> ['Hello', ',', 'World', '!']
-
-The Python representation of the grammar is quite readable, owing to the self-explanatory
-class names, and the use of '+', '|' and '^' operators.
-
-The L{ParseResults} object returned from L{ParserElement.parseString} can be accessed as a nested list, a dictionary, or an
-object with named attributes.
-
-The pyparsing module handles some of the problems that are typically vexing when writing text parsers:
- - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello  ,  World  !", etc.)
- - quoted strings
- - embedded comments
-
-
-Getting Started -
------------------
-Visit the classes L{ParserElement} and L{ParseResults} to see the base classes that most other pyparsing
-classes inherit from. Use the docstrings for examples of how to:
- - construct literal match expressions from L{Literal} and L{CaselessLiteral} classes
- - construct character word-group expressions using the L{Word} class
- - see how to create repetitive expressions using L{ZeroOrMore} and L{OneOrMore} classes
- - use L{'+'}, L{'|'}, L{'^'}, and L{'&'} operators to combine simple expressions into more complex ones
- - associate names with your parsed results using L{ParserElement.setResultsName}
- - find some helpful expression short-cuts like L{delimitedList} and L{oneOf}
- - find more useful common expressions in the L{pyparsing_common} namespace class
-"""
-
-__version__ = "2.2.1"
-__versionTime__ = "18 Sep 2018 00:49 UTC"
-__author__ = "Paul McGuire "
-
-import string
-from weakref import ref as wkref
-import copy
-import sys
-import warnings
-import re
-import sre_constants
-import collections
-import pprint
-import traceback
-import types
-from datetime import datetime
-
-try:
-    from _thread import RLock
-except ImportError:
-    from threading import RLock
-
-try:
-    # Python 3
-    from collections.abc import Iterable
-    from collections.abc import MutableMapping
-except ImportError:
-    # Python 2.7
-    from collections import Iterable
-    from collections import MutableMapping
-
-try:
-    from collections import OrderedDict as _OrderedDict
-except ImportError:
-    try:
-        from ordereddict import OrderedDict as _OrderedDict
-    except ImportError:
-        _OrderedDict = None
-
-#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) )
-
-__all__ = [
-'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty',
-'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal',
-'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or',
-'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException',
-'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException',
-'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 
-'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore',
-'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col',
-'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString',
-'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums',
-'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno',
-'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral',
-'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables',
-'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', 
-'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd',
-'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute',
-'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass',
-'CloseMatch', 'tokenMap', 'pyparsing_common',
-]
-
-system_version = tuple(sys.version_info)[:3]
-PY_3 = system_version[0] == 3
-if PY_3:
-    _MAX_INT = sys.maxsize
-    basestring = str
-    unichr = chr
-    _ustr = str
-
-    # build list of single arg builtins, that can be used as parse actions
-    singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max]
-
-else:
-    _MAX_INT = sys.maxint
-    range = xrange
-
-    def _ustr(obj):
-        """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries
-           str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It
-           then < returns the unicode object | encodes it with the default encoding | ... >.
-        """
-        if isinstance(obj,unicode):
-            return obj
-
-        try:
-            # If this works, then _ustr(obj) has the same behaviour as str(obj), so
-            # it won't break any existing code.
-            return str(obj)
-
-        except UnicodeEncodeError:
-            # Else encode it
-            ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace')
-            xmlcharref = Regex(r'&#\d+;')
-            xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:])
-            return xmlcharref.transformString(ret)
-
-    # build list of single arg builtins, tolerant of Python version, that can be used as parse actions
-    singleArgBuiltins = []
-    import __builtin__
-    for fname in "sum len sorted reversed list tuple set any all min max".split():
-        try:
-            singleArgBuiltins.append(getattr(__builtin__,fname))
-        except AttributeError:
-            continue
-            
-_generatorType = type((y for y in range(1)))
- 
-def _xml_escape(data):
-    """Escape &, <, >, ", ', etc. in a string of data."""
-
-    # ampersand must be replaced first
-    from_symbols = '&><"\''
-    to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split())
-    for from_,to_ in zip(from_symbols, to_symbols):
-        data = data.replace(from_, to_)
-    return data
-
-class _Constants(object):
-    pass
-
-alphas     = string.ascii_uppercase + string.ascii_lowercase
-nums       = "0123456789"
-hexnums    = nums + "ABCDEFabcdef"
-alphanums  = alphas + nums
-_bslash    = chr(92)
-printables = "".join(c for c in string.printable if c not in string.whitespace)
-
-class ParseBaseException(Exception):
-    """base exception class for all parsing runtime exceptions"""
-    # Performance tuning: we construct a *lot* of these, so keep this
-    # constructor as small and fast as possible
-    def __init__( self, pstr, loc=0, msg=None, elem=None ):
-        self.loc = loc
-        if msg is None:
-            self.msg = pstr
-            self.pstr = ""
-        else:
-            self.msg = msg
-            self.pstr = pstr
-        self.parserElement = elem
-        self.args = (pstr, loc, msg)
-
-    @classmethod
-    def _from_exception(cls, pe):
-        """
-        internal factory method to simplify creating one type of ParseException 
-        from another - avoids having __init__ signature conflicts among subclasses
-        """
-        return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement)
-
-    def __getattr__( self, aname ):
-        """supported attributes by name are:
-            - lineno - returns the line number of the exception text
-            - col - returns the column number of the exception text
-            - line - returns the line containing the exception text
-        """
-        if( aname == "lineno" ):
-            return lineno( self.loc, self.pstr )
-        elif( aname in ("col", "column") ):
-            return col( self.loc, self.pstr )
-        elif( aname == "line" ):
-            return line( self.loc, self.pstr )
-        else:
-            raise AttributeError(aname)
-
-    def __str__( self ):
-        return "%s (at char %d), (line:%d, col:%d)" % \
-                ( self.msg, self.loc, self.lineno, self.column )
-    def __repr__( self ):
-        return _ustr(self)
-    def markInputline( self, markerString = ">!<" ):
-        """Extracts the exception line from the input string, and marks
-           the location of the exception with a special symbol.
-        """
-        line_str = self.line
-        line_column = self.column - 1
-        if markerString:
-            line_str = "".join((line_str[:line_column],
-                                markerString, line_str[line_column:]))
-        return line_str.strip()
-    def __dir__(self):
-        return "lineno col line".split() + dir(type(self))
-
-class ParseException(ParseBaseException):
-    """
-    Exception thrown when parse expressions don't match class;
-    supported attributes by name are:
-     - lineno - returns the line number of the exception text
-     - col - returns the column number of the exception text
-     - line - returns the line containing the exception text
-        
-    Example::
-        try:
-            Word(nums).setName("integer").parseString("ABC")
-        except ParseException as pe:
-            print(pe)
-            print("column: {}".format(pe.col))
-            
-    prints::
-       Expected integer (at char 0), (line:1, col:1)
-        column: 1
-    """
-    pass
-
-class ParseFatalException(ParseBaseException):
-    """user-throwable exception thrown when inconsistent parse content
-       is found; stops all parsing immediately"""
-    pass
-
-class ParseSyntaxException(ParseFatalException):
-    """just like L{ParseFatalException}, but thrown internally when an
-       L{ErrorStop} ('-' operator) indicates that parsing is to stop 
-       immediately because an unbacktrackable syntax error has been found"""
-    pass
-
-#~ class ReparseException(ParseBaseException):
-    #~ """Experimental class - parse actions can raise this exception to cause
-       #~ pyparsing to reparse the input string:
-        #~ - with a modified input string, and/or
-        #~ - with a modified start location
-       #~ Set the values of the ReparseException in the constructor, and raise the
-       #~ exception in a parse action to cause pyparsing to use the new string/location.
-       #~ Setting the values as None causes no change to be made.
-       #~ """
-    #~ def __init_( self, newstring, restartLoc ):
-        #~ self.newParseText = newstring
-        #~ self.reparseLoc = restartLoc
-
-class RecursiveGrammarException(Exception):
-    """exception thrown by L{ParserElement.validate} if the grammar could be improperly recursive"""
-    def __init__( self, parseElementList ):
-        self.parseElementTrace = parseElementList
-
-    def __str__( self ):
-        return "RecursiveGrammarException: %s" % self.parseElementTrace
-
-class _ParseResultsWithOffset(object):
-    def __init__(self,p1,p2):
-        self.tup = (p1,p2)
-    def __getitem__(self,i):
-        return self.tup[i]
-    def __repr__(self):
-        return repr(self.tup[0])
-    def setOffset(self,i):
-        self.tup = (self.tup[0],i)
-
-class ParseResults(object):
-    """
-    Structured parse results, to provide multiple means of access to the parsed data:
-       - as a list (C{len(results)})
-       - by list index (C{results[0], results[1]}, etc.)
-       - by attribute (C{results.} - see L{ParserElement.setResultsName})
-
-    Example::
-        integer = Word(nums)
-        date_str = (integer.setResultsName("year") + '/' 
-                        + integer.setResultsName("month") + '/' 
-                        + integer.setResultsName("day"))
-        # equivalent form:
-        # date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-
-        # parseString returns a ParseResults object
-        result = date_str.parseString("1999/12/31")
-
-        def test(s, fn=repr):
-            print("%s -> %s" % (s, fn(eval(s))))
-        test("list(result)")
-        test("result[0]")
-        test("result['month']")
-        test("result.day")
-        test("'month' in result")
-        test("'minutes' in result")
-        test("result.dump()", str)
-    prints::
-        list(result) -> ['1999', '/', '12', '/', '31']
-        result[0] -> '1999'
-        result['month'] -> '12'
-        result.day -> '31'
-        'month' in result -> True
-        'minutes' in result -> False
-        result.dump() -> ['1999', '/', '12', '/', '31']
-        - day: 31
-        - month: 12
-        - year: 1999
-    """
-    def __new__(cls, toklist=None, name=None, asList=True, modal=True ):
-        if isinstance(toklist, cls):
-            return toklist
-        retobj = object.__new__(cls)
-        retobj.__doinit = True
-        return retobj
-
-    # Performance tuning: we construct a *lot* of these, so keep this
-    # constructor as small and fast as possible
-    def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ):
-        if self.__doinit:
-            self.__doinit = False
-            self.__name = None
-            self.__parent = None
-            self.__accumNames = {}
-            self.__asList = asList
-            self.__modal = modal
-            if toklist is None:
-                toklist = []
-            if isinstance(toklist, list):
-                self.__toklist = toklist[:]
-            elif isinstance(toklist, _generatorType):
-                self.__toklist = list(toklist)
-            else:
-                self.__toklist = [toklist]
-            self.__tokdict = dict()
-
-        if name is not None and name:
-            if not modal:
-                self.__accumNames[name] = 0
-            if isinstance(name,int):
-                name = _ustr(name) # will always return a str, but use _ustr for consistency
-            self.__name = name
-            if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])):
-                if isinstance(toklist,basestring):
-                    toklist = [ toklist ]
-                if asList:
-                    if isinstance(toklist,ParseResults):
-                        self[name] = _ParseResultsWithOffset(toklist.copy(),0)
-                    else:
-                        self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0)
-                    self[name].__name = name
-                else:
-                    try:
-                        self[name] = toklist[0]
-                    except (KeyError,TypeError,IndexError):
-                        self[name] = toklist
-
-    def __getitem__( self, i ):
-        if isinstance( i, (int,slice) ):
-            return self.__toklist[i]
-        else:
-            if i not in self.__accumNames:
-                return self.__tokdict[i][-1][0]
-            else:
-                return ParseResults([ v[0] for v in self.__tokdict[i] ])
-
-    def __setitem__( self, k, v, isinstance=isinstance ):
-        if isinstance(v,_ParseResultsWithOffset):
-            self.__tokdict[k] = self.__tokdict.get(k,list()) + [v]
-            sub = v[0]
-        elif isinstance(k,(int,slice)):
-            self.__toklist[k] = v
-            sub = v
-        else:
-            self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)]
-            sub = v
-        if isinstance(sub,ParseResults):
-            sub.__parent = wkref(self)
-
-    def __delitem__( self, i ):
-        if isinstance(i,(int,slice)):
-            mylen = len( self.__toklist )
-            del self.__toklist[i]
-
-            # convert int to slice
-            if isinstance(i, int):
-                if i < 0:
-                    i += mylen
-                i = slice(i, i+1)
-            # get removed indices
-            removed = list(range(*i.indices(mylen)))
-            removed.reverse()
-            # fixup indices in token dictionary
-            for name,occurrences in self.__tokdict.items():
-                for j in removed:
-                    for k, (value, position) in enumerate(occurrences):
-                        occurrences[k] = _ParseResultsWithOffset(value, position - (position > j))
-        else:
-            del self.__tokdict[i]
-
-    def __contains__( self, k ):
-        return k in self.__tokdict
-
-    def __len__( self ): return len( self.__toklist )
-    def __bool__(self): return ( not not self.__toklist )
-    __nonzero__ = __bool__
-    def __iter__( self ): return iter( self.__toklist )
-    def __reversed__( self ): return iter( self.__toklist[::-1] )
-    def _iterkeys( self ):
-        if hasattr(self.__tokdict, "iterkeys"):
-            return self.__tokdict.iterkeys()
-        else:
-            return iter(self.__tokdict)
-
-    def _itervalues( self ):
-        return (self[k] for k in self._iterkeys())
-            
-    def _iteritems( self ):
-        return ((k, self[k]) for k in self._iterkeys())
-
-    if PY_3:
-        keys = _iterkeys       
-        """Returns an iterator of all named result keys (Python 3.x only)."""
-
-        values = _itervalues
-        """Returns an iterator of all named result values (Python 3.x only)."""
-
-        items = _iteritems
-        """Returns an iterator of all named result key-value tuples (Python 3.x only)."""
-
-    else:
-        iterkeys = _iterkeys
-        """Returns an iterator of all named result keys (Python 2.x only)."""
-
-        itervalues = _itervalues
-        """Returns an iterator of all named result values (Python 2.x only)."""
-
-        iteritems = _iteritems
-        """Returns an iterator of all named result key-value tuples (Python 2.x only)."""
-
-        def keys( self ):
-            """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.iterkeys())
-
-        def values( self ):
-            """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.itervalues())
-                
-        def items( self ):
-            """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.iteritems())
-
-    def haskeys( self ):
-        """Since keys() returns an iterator, this method is helpful in bypassing
-           code that looks for the existence of any defined results names."""
-        return bool(self.__tokdict)
-        
-    def pop( self, *args, **kwargs):
-        """
-        Removes and returns item at specified index (default=C{last}).
-        Supports both C{list} and C{dict} semantics for C{pop()}. If passed no
-        argument or an integer argument, it will use C{list} semantics
-        and pop tokens from the list of parsed tokens. If passed a 
-        non-integer argument (most likely a string), it will use C{dict}
-        semantics and pop the corresponding value from any defined 
-        results names. A second default return value argument is 
-        supported, just as in C{dict.pop()}.
-
-        Example::
-            def remove_first(tokens):
-                tokens.pop(0)
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-            print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321']
-
-            label = Word(alphas)
-            patt = label("LABEL") + OneOrMore(Word(nums))
-            print(patt.parseString("AAB 123 321").dump())
-
-            # Use pop() in a parse action to remove named result (note that corresponding value is not
-            # removed from list form of results)
-            def remove_LABEL(tokens):
-                tokens.pop("LABEL")
-                return tokens
-            patt.addParseAction(remove_LABEL)
-            print(patt.parseString("AAB 123 321").dump())
-        prints::
-            ['AAB', '123', '321']
-            - LABEL: AAB
-
-            ['AAB', '123', '321']
-        """
-        if not args:
-            args = [-1]
-        for k,v in kwargs.items():
-            if k == 'default':
-                args = (args[0], v)
-            else:
-                raise TypeError("pop() got an unexpected keyword argument '%s'" % k)
-        if (isinstance(args[0], int) or 
-                        len(args) == 1 or 
-                        args[0] in self):
-            index = args[0]
-            ret = self[index]
-            del self[index]
-            return ret
-        else:
-            defaultvalue = args[1]
-            return defaultvalue
-
-    def get(self, key, defaultValue=None):
-        """
-        Returns named result matching the given key, or if there is no
-        such name, then returns the given C{defaultValue} or C{None} if no
-        C{defaultValue} is specified.
-
-        Similar to C{dict.get()}.
-        
-        Example::
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
-
-            result = date_str.parseString("1999/12/31")
-            print(result.get("year")) # -> '1999'
-            print(result.get("hour", "not specified")) # -> 'not specified'
-            print(result.get("hour")) # -> None
-        """
-        if key in self:
-            return self[key]
-        else:
-            return defaultValue
-
-    def insert( self, index, insStr ):
-        """
-        Inserts new element at location index in the list of parsed tokens.
-        
-        Similar to C{list.insert()}.
-
-        Example::
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-
-            # use a parse action to insert the parse location in the front of the parsed results
-            def insert_locn(locn, tokens):
-                tokens.insert(0, locn)
-            print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321']
-        """
-        self.__toklist.insert(index, insStr)
-        # fixup indices in token dictionary
-        for name,occurrences in self.__tokdict.items():
-            for k, (value, position) in enumerate(occurrences):
-                occurrences[k] = _ParseResultsWithOffset(value, position + (position > index))
-
-    def append( self, item ):
-        """
-        Add single element to end of ParseResults list of elements.
-
-        Example::
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-            
-            # use a parse action to compute the sum of the parsed integers, and add it to the end
-            def append_sum(tokens):
-                tokens.append(sum(map(int, tokens)))
-            print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444]
-        """
-        self.__toklist.append(item)
-
-    def extend( self, itemseq ):
-        """
-        Add sequence of elements to end of ParseResults list of elements.
-
-        Example::
-            patt = OneOrMore(Word(alphas))
-            
-            # use a parse action to append the reverse of the matched strings, to make a palindrome
-            def make_palindrome(tokens):
-                tokens.extend(reversed([t[::-1] for t in tokens]))
-                return ''.join(tokens)
-            print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl'
-        """
-        if isinstance(itemseq, ParseResults):
-            self += itemseq
-        else:
-            self.__toklist.extend(itemseq)
-
-    def clear( self ):
-        """
-        Clear all elements and results names.
-        """
-        del self.__toklist[:]
-        self.__tokdict.clear()
-
-    def __getattr__( self, name ):
-        try:
-            return self[name]
-        except KeyError:
-            return ""
-            
-        if name in self.__tokdict:
-            if name not in self.__accumNames:
-                return self.__tokdict[name][-1][0]
-            else:
-                return ParseResults([ v[0] for v in self.__tokdict[name] ])
-        else:
-            return ""
-
-    def __add__( self, other ):
-        ret = self.copy()
-        ret += other
-        return ret
-
-    def __iadd__( self, other ):
-        if other.__tokdict:
-            offset = len(self.__toklist)
-            addoffset = lambda a: offset if a<0 else a+offset
-            otheritems = other.__tokdict.items()
-            otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) )
-                                for (k,vlist) in otheritems for v in vlist]
-            for k,v in otherdictitems:
-                self[k] = v
-                if isinstance(v[0],ParseResults):
-                    v[0].__parent = wkref(self)
-            
-        self.__toklist += other.__toklist
-        self.__accumNames.update( other.__accumNames )
-        return self
-
-    def __radd__(self, other):
-        if isinstance(other,int) and other == 0:
-            # useful for merging many ParseResults using sum() builtin
-            return self.copy()
-        else:
-            # this may raise a TypeError - so be it
-            return other + self
-        
-    def __repr__( self ):
-        return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) )
-
-    def __str__( self ):
-        return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']'
-
-    def _asStringList( self, sep='' ):
-        out = []
-        for item in self.__toklist:
-            if out and sep:
-                out.append(sep)
-            if isinstance( item, ParseResults ):
-                out += item._asStringList()
-            else:
-                out.append( _ustr(item) )
-        return out
-
-    def asList( self ):
-        """
-        Returns the parse results as a nested list of matching tokens, all converted to strings.
-
-        Example::
-            patt = OneOrMore(Word(alphas))
-            result = patt.parseString("sldkj lsdkj sldkj")
-            # even though the result prints in string-like form, it is actually a pyparsing ParseResults
-            print(type(result), result) # ->  ['sldkj', 'lsdkj', 'sldkj']
-            
-            # Use asList() to create an actual list
-            result_list = result.asList()
-            print(type(result_list), result_list) # ->  ['sldkj', 'lsdkj', 'sldkj']
-        """
-        return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist]
-
-    def asDict( self ):
-        """
-        Returns the named parse results as a nested dictionary.
-
-        Example::
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-            
-            result = date_str.parseString('12/31/1999')
-            print(type(result), repr(result)) # ->  (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
-            
-            result_dict = result.asDict()
-            print(type(result_dict), repr(result_dict)) # ->  {'day': '1999', 'year': '12', 'month': '31'}
-
-            # even though a ParseResults supports dict-like access, sometime you just need to have a dict
-            import json
-            print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable
-            print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"}
-        """
-        if PY_3:
-            item_fn = self.items
-        else:
-            item_fn = self.iteritems
-            
-        def toItem(obj):
-            if isinstance(obj, ParseResults):
-                if obj.haskeys():
-                    return obj.asDict()
-                else:
-                    return [toItem(v) for v in obj]
-            else:
-                return obj
-                
-        return dict((k,toItem(v)) for k,v in item_fn())
-
-    def copy( self ):
-        """
-        Returns a new copy of a C{ParseResults} object.
-        """
-        ret = ParseResults( self.__toklist )
-        ret.__tokdict = self.__tokdict.copy()
-        ret.__parent = self.__parent
-        ret.__accumNames.update( self.__accumNames )
-        ret.__name = self.__name
-        return ret
-
-    def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ):
-        """
-        (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.
-        """
-        nl = "\n"
-        out = []
-        namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items()
-                                                            for v in vlist)
-        nextLevelIndent = indent + "  "
-
-        # collapse out indents if formatting is not desired
-        if not formatted:
-            indent = ""
-            nextLevelIndent = ""
-            nl = ""
-
-        selfTag = None
-        if doctag is not None:
-            selfTag = doctag
-        else:
-            if self.__name:
-                selfTag = self.__name
-
-        if not selfTag:
-            if namedItemsOnly:
-                return ""
-            else:
-                selfTag = "ITEM"
-
-        out += [ nl, indent, "<", selfTag, ">" ]
-
-        for i,res in enumerate(self.__toklist):
-            if isinstance(res,ParseResults):
-                if i in namedItems:
-                    out += [ res.asXML(namedItems[i],
-                                        namedItemsOnly and doctag is None,
-                                        nextLevelIndent,
-                                        formatted)]
-                else:
-                    out += [ res.asXML(None,
-                                        namedItemsOnly and doctag is None,
-                                        nextLevelIndent,
-                                        formatted)]
-            else:
-                # individual token, see if there is a name for it
-                resTag = None
-                if i in namedItems:
-                    resTag = namedItems[i]
-                if not resTag:
-                    if namedItemsOnly:
-                        continue
-                    else:
-                        resTag = "ITEM"
-                xmlBodyText = _xml_escape(_ustr(res))
-                out += [ nl, nextLevelIndent, "<", resTag, ">",
-                                                xmlBodyText,
-                                                "" ]
-
-        out += [ nl, indent, "" ]
-        return "".join(out)
-
-    def __lookup(self,sub):
-        for k,vlist in self.__tokdict.items():
-            for v,loc in vlist:
-                if sub is v:
-                    return k
-        return None
-
-    def getName(self):
-        r"""
-        Returns the results name for this token expression. Useful when several 
-        different expressions might match at a particular location.
-
-        Example::
-            integer = Word(nums)
-            ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d")
-            house_number_expr = Suppress('#') + Word(nums, alphanums)
-            user_data = (Group(house_number_expr)("house_number") 
-                        | Group(ssn_expr)("ssn")
-                        | Group(integer)("age"))
-            user_info = OneOrMore(user_data)
-            
-            result = user_info.parseString("22 111-22-3333 #221B")
-            for item in result:
-                print(item.getName(), ':', item[0])
-        prints::
-            age : 22
-            ssn : 111-22-3333
-            house_number : 221B
-        """
-        if self.__name:
-            return self.__name
-        elif self.__parent:
-            par = self.__parent()
-            if par:
-                return par.__lookup(self)
-            else:
-                return None
-        elif (len(self) == 1 and
-               len(self.__tokdict) == 1 and
-               next(iter(self.__tokdict.values()))[0][1] in (0,-1)):
-            return next(iter(self.__tokdict.keys()))
-        else:
-            return None
-
-    def dump(self, indent='', depth=0, full=True):
-        """
-        Diagnostic method for listing out the contents of a C{ParseResults}.
-        Accepts an optional C{indent} argument so that this string can be embedded
-        in a nested display of other data.
-
-        Example::
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-            
-            result = date_str.parseString('12/31/1999')
-            print(result.dump())
-        prints::
-            ['12', '/', '31', '/', '1999']
-            - day: 1999
-            - month: 31
-            - year: 12
-        """
-        out = []
-        NL = '\n'
-        out.append( indent+_ustr(self.asList()) )
-        if full:
-            if self.haskeys():
-                items = sorted((str(k), v) for k,v in self.items())
-                for k,v in items:
-                    if out:
-                        out.append(NL)
-                    out.append( "%s%s- %s: " % (indent,('  '*depth), k) )
-                    if isinstance(v,ParseResults):
-                        if v:
-                            out.append( v.dump(indent,depth+1) )
-                        else:
-                            out.append(_ustr(v))
-                    else:
-                        out.append(repr(v))
-            elif any(isinstance(vv,ParseResults) for vv in self):
-                v = self
-                for i,vv in enumerate(v):
-                    if isinstance(vv,ParseResults):
-                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),vv.dump(indent,depth+1) ))
-                    else:
-                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),_ustr(vv)))
-            
-        return "".join(out)
-
-    def pprint(self, *args, **kwargs):
-        """
-        Pretty-printer for parsed results as a list, using the C{pprint} module.
-        Accepts additional positional or keyword args as defined for the 
-        C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint})
-
-        Example::
-            ident = Word(alphas, alphanums)
-            num = Word(nums)
-            func = Forward()
-            term = ident | num | Group('(' + func + ')')
-            func <<= ident + Group(Optional(delimitedList(term)))
-            result = func.parseString("fna a,b,(fnb c,d,200),100")
-            result.pprint(width=40)
-        prints::
-            ['fna',
-             ['a',
-              'b',
-              ['(', 'fnb', ['c', 'd', '200'], ')'],
-              '100']]
-        """
-        pprint.pprint(self.asList(), *args, **kwargs)
-
-    # add support for pickle protocol
-    def __getstate__(self):
-        return ( self.__toklist,
-                 ( self.__tokdict.copy(),
-                   self.__parent is not None and self.__parent() or None,
-                   self.__accumNames,
-                   self.__name ) )
-
-    def __setstate__(self,state):
-        self.__toklist = state[0]
-        (self.__tokdict,
-         par,
-         inAccumNames,
-         self.__name) = state[1]
-        self.__accumNames = {}
-        self.__accumNames.update(inAccumNames)
-        if par is not None:
-            self.__parent = wkref(par)
-        else:
-            self.__parent = None
-
-    def __getnewargs__(self):
-        return self.__toklist, self.__name, self.__asList, self.__modal
-
-    def __dir__(self):
-        return (dir(type(self)) + list(self.keys()))
-
-MutableMapping.register(ParseResults)
-
-def col (loc,strg):
-    """Returns current column within a string, counting newlines as line separators.
-   The first column is number 1.
-
-   Note: the default parsing behavior is to expand tabs in the input string
-   before starting the parsing process.  See L{I{ParserElement.parseString}} for more information
-   on parsing strings containing C{}s, and suggested methods to maintain a
-   consistent view of the parsed string, the parse location, and line and column
-   positions within the parsed string.
-   """
-    s = strg
-    return 1 if 0} for more information
-   on parsing strings containing C{}s, and suggested methods to maintain a
-   consistent view of the parsed string, the parse location, and line and column
-   positions within the parsed string.
-   """
-    return strg.count("\n",0,loc) + 1
-
-def line( loc, strg ):
-    """Returns the line of text containing loc within a string, counting newlines as line separators.
-       """
-    lastCR = strg.rfind("\n", 0, loc)
-    nextCR = strg.find("\n", loc)
-    if nextCR >= 0:
-        return strg[lastCR+1:nextCR]
-    else:
-        return strg[lastCR+1:]
-
-def _defaultStartDebugAction( instring, loc, expr ):
-    print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )))
-
-def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ):
-    print ("Matched " + _ustr(expr) + " -> " + str(toks.asList()))
-
-def _defaultExceptionDebugAction( instring, loc, expr, exc ):
-    print ("Exception raised:" + _ustr(exc))
-
-def nullDebugAction(*args):
-    """'Do-nothing' debug action, to suppress debugging output during parsing."""
-    pass
-
-# Only works on Python 3.x - nonlocal is toxic to Python 2 installs
-#~ 'decorator to trim function calls to match the arity of the target'
-#~ def _trim_arity(func, maxargs=3):
-    #~ if func in singleArgBuiltins:
-        #~ return lambda s,l,t: func(t)
-    #~ limit = 0
-    #~ foundArity = False
-    #~ def wrapper(*args):
-        #~ nonlocal limit,foundArity
-        #~ while 1:
-            #~ try:
-                #~ ret = func(*args[limit:])
-                #~ foundArity = True
-                #~ return ret
-            #~ except TypeError:
-                #~ if limit == maxargs or foundArity:
-                    #~ raise
-                #~ limit += 1
-                #~ continue
-    #~ return wrapper
-
-# this version is Python 2.x-3.x cross-compatible
-'decorator to trim function calls to match the arity of the target'
-def _trim_arity(func, maxargs=2):
-    if func in singleArgBuiltins:
-        return lambda s,l,t: func(t)
-    limit = [0]
-    foundArity = [False]
-    
-    # traceback return data structure changed in Py3.5 - normalize back to plain tuples
-    if system_version[:2] >= (3,5):
-        def extract_stack(limit=0):
-            # special handling for Python 3.5.0 - extra deep call stack by 1
-            offset = -3 if system_version == (3,5,0) else -2
-            frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset]
-            return [frame_summary[:2]]
-        def extract_tb(tb, limit=0):
-            frames = traceback.extract_tb(tb, limit=limit)
-            frame_summary = frames[-1]
-            return [frame_summary[:2]]
-    else:
-        extract_stack = traceback.extract_stack
-        extract_tb = traceback.extract_tb
-    
-    # synthesize what would be returned by traceback.extract_stack at the call to 
-    # user's parse action 'func', so that we don't incur call penalty at parse time
-    
-    LINE_DIFF = 6
-    # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND 
-    # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!!
-    this_line = extract_stack(limit=2)[-1]
-    pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF)
-
-    def wrapper(*args):
-        while 1:
-            try:
-                ret = func(*args[limit[0]:])
-                foundArity[0] = True
-                return ret
-            except TypeError:
-                # re-raise TypeErrors if they did not come from our arity testing
-                if foundArity[0]:
-                    raise
-                else:
-                    try:
-                        tb = sys.exc_info()[-1]
-                        if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth:
-                            raise
-                    finally:
-                        del tb
-
-                if limit[0] <= maxargs:
-                    limit[0] += 1
-                    continue
-                raise
-
-    # copy func name to wrapper for sensible debug output
-    func_name = ""
-    try:
-        func_name = getattr(func, '__name__', 
-                            getattr(func, '__class__').__name__)
-    except Exception:
-        func_name = str(func)
-    wrapper.__name__ = func_name
-
-    return wrapper
-
-class ParserElement(object):
-    """Abstract base level parser element class."""
-    DEFAULT_WHITE_CHARS = " \n\t\r"
-    verbose_stacktrace = False
-
-    @staticmethod
-    def setDefaultWhitespaceChars( chars ):
-        r"""
-        Overrides the default whitespace chars
-
-        Example::
-            # default whitespace chars are space,  and newline
-            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def', 'ghi', 'jkl']
-            
-            # change to just treat newline as significant
-            ParserElement.setDefaultWhitespaceChars(" \t")
-            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def']
-        """
-        ParserElement.DEFAULT_WHITE_CHARS = chars
-
-    @staticmethod
-    def inlineLiteralsUsing(cls):
-        """
-        Set class to be used for inclusion of string literals into a parser.
-        
-        Example::
-            # default literal class used is Literal
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
-
-
-            # change to Suppress
-            ParserElement.inlineLiteralsUsing(Suppress)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '12', '31']
-        """
-        ParserElement._literalStringClass = cls
-
-    def __init__( self, savelist=False ):
-        self.parseAction = list()
-        self.failAction = None
-        #~ self.name = ""  # don't define self.name, let subclasses try/except upcall
-        self.strRepr = None
-        self.resultsName = None
-        self.saveAsList = savelist
-        self.skipWhitespace = True
-        self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
-        self.copyDefaultWhiteChars = True
-        self.mayReturnEmpty = False # used when checking for left-recursion
-        self.keepTabs = False
-        self.ignoreExprs = list()
-        self.debug = False
-        self.streamlined = False
-        self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index
-        self.errmsg = ""
-        self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all)
-        self.debugActions = ( None, None, None ) #custom debug actions
-        self.re = None
-        self.callPreparse = True # used to avoid redundant calls to preParse
-        self.callDuringTry = False
-
-    def copy( self ):
-        """
-        Make a copy of this C{ParserElement}.  Useful for defining different parse actions
-        for the same parsing pattern, using copies of the original parse element.
-        
-        Example::
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K")
-            integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
-            
-            print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M"))
-        prints::
-            [5120, 100, 655360, 268435456]
-        Equivalent form of C{expr.copy()} is just C{expr()}::
-            integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
-        """
-        cpy = copy.copy( self )
-        cpy.parseAction = self.parseAction[:]
-        cpy.ignoreExprs = self.ignoreExprs[:]
-        if self.copyDefaultWhiteChars:
-            cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
-        return cpy
-
-    def setName( self, name ):
-        """
-        Define name for this expression, makes debugging and exception messages clearer.
-        
-        Example::
-            Word(nums).parseString("ABC")  # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1)
-            Word(nums).setName("integer").parseString("ABC")  # -> Exception: Expected integer (at char 0), (line:1, col:1)
-        """
-        self.name = name
-        self.errmsg = "Expected " + self.name
-        if hasattr(self,"exception"):
-            self.exception.msg = self.errmsg
-        return self
-
-    def setResultsName( self, name, listAllMatches=False ):
-        """
-        Define name for referencing matching tokens as a nested attribute
-        of the returned parse results.
-        NOTE: this returns a *copy* of the original C{ParserElement} object;
-        this is so that the client can define a basic element, such as an
-        integer, and reference it in multiple places with different names.
-
-        You can also set results names using the abbreviated syntax,
-        C{expr("name")} in place of C{expr.setResultsName("name")} - 
-        see L{I{__call__}<__call__>}.
-
-        Example::
-            date_str = (integer.setResultsName("year") + '/' 
-                        + integer.setResultsName("month") + '/' 
-                        + integer.setResultsName("day"))
-
-            # equivalent form:
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-        """
-        newself = self.copy()
-        if name.endswith("*"):
-            name = name[:-1]
-            listAllMatches=True
-        newself.resultsName = name
-        newself.modalResults = not listAllMatches
-        return newself
-
-    def setBreak(self,breakFlag = True):
-        """Method to invoke the Python pdb debugger when this element is
-           about to be parsed. Set C{breakFlag} to True to enable, False to
-           disable.
-        """
-        if breakFlag:
-            _parseMethod = self._parse
-            def breaker(instring, loc, doActions=True, callPreParse=True):
-                import pdb
-                pdb.set_trace()
-                return _parseMethod( instring, loc, doActions, callPreParse )
-            breaker._originalParseMethod = _parseMethod
-            self._parse = breaker
-        else:
-            if hasattr(self._parse,"_originalParseMethod"):
-                self._parse = self._parse._originalParseMethod
-        return self
-
-    def setParseAction( self, *fns, **kwargs ):
-        """
-        Define one or more actions to perform when successfully matching parse element definition.
-        Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)},
-        C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where:
-         - s   = the original string being parsed (see note below)
-         - loc = the location of the matching substring
-         - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object
-        If the functions in fns modify the tokens, they can return them as the return
-        value from fn, and the modified list of tokens will replace the original.
-        Otherwise, fn does not need to return any value.
-
-        Optional keyword arguments:
-         - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing
-
-        Note: the default parsing behavior is to expand tabs in the input string
-        before starting the parsing process.  See L{I{parseString}} for more information
-        on parsing strings containing C{}s, and suggested methods to maintain a
-        consistent view of the parsed string, the parse location, and line and column
-        positions within the parsed string.
-        
-        Example::
-            integer = Word(nums)
-            date_str = integer + '/' + integer + '/' + integer
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
-
-            # use parse action to convert to ints at parse time
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            date_str = integer + '/' + integer + '/' + integer
-
-            # note that integer fields are now ints, not strings
-            date_str.parseString("1999/12/31")  # -> [1999, '/', 12, '/', 31]
-        """
-        self.parseAction = list(map(_trim_arity, list(fns)))
-        self.callDuringTry = kwargs.get("callDuringTry", False)
-        return self
-
-    def addParseAction( self, *fns, **kwargs ):
-        """
-        Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}}.
-        
-        See examples in L{I{copy}}.
-        """
-        self.parseAction += list(map(_trim_arity, list(fns)))
-        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
-        return self
-
-    def addCondition(self, *fns, **kwargs):
-        """Add a boolean predicate function to expression's list of parse actions. See 
-        L{I{setParseAction}} for function call signatures. Unlike C{setParseAction}, 
-        functions passed to C{addCondition} need to return boolean success/fail of the condition.
-
-        Optional keyword arguments:
-         - message = define a custom message to be used in the raised exception
-         - fatal   = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException
-         
-        Example::
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            year_int = integer.copy()
-            year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later")
-            date_str = year_int + '/' + integer + '/' + integer
-
-            result = date_str.parseString("1999/12/31")  # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1)
-        """
-        msg = kwargs.get("message", "failed user-defined condition")
-        exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException
-        for fn in fns:
-            def pa(s,l,t):
-                if not bool(_trim_arity(fn)(s,l,t)):
-                    raise exc_type(s,l,msg)
-            self.parseAction.append(pa)
-        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
-        return self
-
-    def setFailAction( self, fn ):
-        """Define action to perform if parsing fails at this expression.
-           Fail acton fn is a callable function that takes the arguments
-           C{fn(s,loc,expr,err)} where:
-            - s = string being parsed
-            - loc = location where expression match was attempted and failed
-            - expr = the parse expression that failed
-            - err = the exception thrown
-           The function returns no value.  It may throw C{L{ParseFatalException}}
-           if it is desired to stop parsing immediately."""
-        self.failAction = fn
-        return self
-
-    def _skipIgnorables( self, instring, loc ):
-        exprsFound = True
-        while exprsFound:
-            exprsFound = False
-            for e in self.ignoreExprs:
-                try:
-                    while 1:
-                        loc,dummy = e._parse( instring, loc )
-                        exprsFound = True
-                except ParseException:
-                    pass
-        return loc
-
-    def preParse( self, instring, loc ):
-        if self.ignoreExprs:
-            loc = self._skipIgnorables( instring, loc )
-
-        if self.skipWhitespace:
-            wt = self.whiteChars
-            instrlen = len(instring)
-            while loc < instrlen and instring[loc] in wt:
-                loc += 1
-
-        return loc
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        return loc, []
-
-    def postParse( self, instring, loc, tokenlist ):
-        return tokenlist
-
-    #~ @profile
-    def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ):
-        debugging = ( self.debug ) #and doActions )
-
-        if debugging or self.failAction:
-            #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))
-            if (self.debugActions[0] ):
-                self.debugActions[0]( instring, loc, self )
-            if callPreParse and self.callPreparse:
-                preloc = self.preParse( instring, loc )
-            else:
-                preloc = loc
-            tokensStart = preloc
-            try:
-                try:
-                    loc,tokens = self.parseImpl( instring, preloc, doActions )
-                except IndexError:
-                    raise ParseException( instring, len(instring), self.errmsg, self )
-            except ParseBaseException as err:
-                #~ print ("Exception raised:", err)
-                if self.debugActions[2]:
-                    self.debugActions[2]( instring, tokensStart, self, err )
-                if self.failAction:
-                    self.failAction( instring, tokensStart, self, err )
-                raise
-        else:
-            if callPreParse and self.callPreparse:
-                preloc = self.preParse( instring, loc )
-            else:
-                preloc = loc
-            tokensStart = preloc
-            if self.mayIndexError or preloc >= len(instring):
-                try:
-                    loc,tokens = self.parseImpl( instring, preloc, doActions )
-                except IndexError:
-                    raise ParseException( instring, len(instring), self.errmsg, self )
-            else:
-                loc,tokens = self.parseImpl( instring, preloc, doActions )
-
-        tokens = self.postParse( instring, loc, tokens )
-
-        retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults )
-        if self.parseAction and (doActions or self.callDuringTry):
-            if debugging:
-                try:
-                    for fn in self.parseAction:
-                        tokens = fn( instring, tokensStart, retTokens )
-                        if tokens is not None:
-                            retTokens = ParseResults( tokens,
-                                                      self.resultsName,
-                                                      asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
-                                                      modal=self.modalResults )
-                except ParseBaseException as err:
-                    #~ print "Exception raised in user parse action:", err
-                    if (self.debugActions[2] ):
-                        self.debugActions[2]( instring, tokensStart, self, err )
-                    raise
-            else:
-                for fn in self.parseAction:
-                    tokens = fn( instring, tokensStart, retTokens )
-                    if tokens is not None:
-                        retTokens = ParseResults( tokens,
-                                                  self.resultsName,
-                                                  asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
-                                                  modal=self.modalResults )
-        if debugging:
-            #~ print ("Matched",self,"->",retTokens.asList())
-            if (self.debugActions[1] ):
-                self.debugActions[1]( instring, tokensStart, loc, self, retTokens )
-
-        return loc, retTokens
-
-    def tryParse( self, instring, loc ):
-        try:
-            return self._parse( instring, loc, doActions=False )[0]
-        except ParseFatalException:
-            raise ParseException( instring, loc, self.errmsg, self)
-    
-    def canParseNext(self, instring, loc):
-        try:
-            self.tryParse(instring, loc)
-        except (ParseException, IndexError):
-            return False
-        else:
-            return True
-
-    class _UnboundedCache(object):
-        def __init__(self):
-            cache = {}
-            self.not_in_cache = not_in_cache = object()
-
-            def get(self, key):
-                return cache.get(key, not_in_cache)
-
-            def set(self, key, value):
-                cache[key] = value
-
-            def clear(self):
-                cache.clear()
-                
-            def cache_len(self):
-                return len(cache)
-
-            self.get = types.MethodType(get, self)
-            self.set = types.MethodType(set, self)
-            self.clear = types.MethodType(clear, self)
-            self.__len__ = types.MethodType(cache_len, self)
-
-    if _OrderedDict is not None:
-        class _FifoCache(object):
-            def __init__(self, size):
-                self.not_in_cache = not_in_cache = object()
-
-                cache = _OrderedDict()
-
-                def get(self, key):
-                    return cache.get(key, not_in_cache)
-
-                def set(self, key, value):
-                    cache[key] = value
-                    while len(cache) > size:
-                        try:
-                            cache.popitem(False)
-                        except KeyError:
-                            pass
-
-                def clear(self):
-                    cache.clear()
-
-                def cache_len(self):
-                    return len(cache)
-
-                self.get = types.MethodType(get, self)
-                self.set = types.MethodType(set, self)
-                self.clear = types.MethodType(clear, self)
-                self.__len__ = types.MethodType(cache_len, self)
-
-    else:
-        class _FifoCache(object):
-            def __init__(self, size):
-                self.not_in_cache = not_in_cache = object()
-
-                cache = {}
-                key_fifo = collections.deque([], size)
-
-                def get(self, key):
-                    return cache.get(key, not_in_cache)
-
-                def set(self, key, value):
-                    cache[key] = value
-                    while len(key_fifo) > size:
-                        cache.pop(key_fifo.popleft(), None)
-                    key_fifo.append(key)
-
-                def clear(self):
-                    cache.clear()
-                    key_fifo.clear()
-
-                def cache_len(self):
-                    return len(cache)
-
-                self.get = types.MethodType(get, self)
-                self.set = types.MethodType(set, self)
-                self.clear = types.MethodType(clear, self)
-                self.__len__ = types.MethodType(cache_len, self)
-
-    # argument cache for optimizing repeated calls when backtracking through recursive expressions
-    packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail
-    packrat_cache_lock = RLock()
-    packrat_cache_stats = [0, 0]
-
-    # this method gets repeatedly called during backtracking with the same arguments -
-    # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression
-    def _parseCache( self, instring, loc, doActions=True, callPreParse=True ):
-        HIT, MISS = 0, 1
-        lookup = (self, instring, loc, callPreParse, doActions)
-        with ParserElement.packrat_cache_lock:
-            cache = ParserElement.packrat_cache
-            value = cache.get(lookup)
-            if value is cache.not_in_cache:
-                ParserElement.packrat_cache_stats[MISS] += 1
-                try:
-                    value = self._parseNoCache(instring, loc, doActions, callPreParse)
-                except ParseBaseException as pe:
-                    # cache a copy of the exception, without the traceback
-                    cache.set(lookup, pe.__class__(*pe.args))
-                    raise
-                else:
-                    cache.set(lookup, (value[0], value[1].copy()))
-                    return value
-            else:
-                ParserElement.packrat_cache_stats[HIT] += 1
-                if isinstance(value, Exception):
-                    raise value
-                return (value[0], value[1].copy())
-
-    _parse = _parseNoCache
-
-    @staticmethod
-    def resetCache():
-        ParserElement.packrat_cache.clear()
-        ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats)
-
-    _packratEnabled = False
-    @staticmethod
-    def enablePackrat(cache_size_limit=128):
-        """Enables "packrat" parsing, which adds memoizing to the parsing logic.
-           Repeated parse attempts at the same string location (which happens
-           often in many complex grammars) can immediately return a cached value,
-           instead of re-executing parsing/validating code.  Memoizing is done of
-           both valid results and parsing exceptions.
-           
-           Parameters:
-            - cache_size_limit - (default=C{128}) - if an integer value is provided
-              will limit the size of the packrat cache; if None is passed, then
-              the cache size will be unbounded; if 0 is passed, the cache will
-              be effectively disabled.
-            
-           This speedup may break existing programs that use parse actions that
-           have side-effects.  For this reason, packrat parsing is disabled when
-           you first import pyparsing.  To activate the packrat feature, your
-           program must call the class method C{ParserElement.enablePackrat()}.  If
-           your program uses C{psyco} to "compile as you go", you must call
-           C{enablePackrat} before calling C{psyco.full()}.  If you do not do this,
-           Python will crash.  For best results, call C{enablePackrat()} immediately
-           after importing pyparsing.
-           
-           Example::
-               import pyparsing
-               pyparsing.ParserElement.enablePackrat()
-        """
-        if not ParserElement._packratEnabled:
-            ParserElement._packratEnabled = True
-            if cache_size_limit is None:
-                ParserElement.packrat_cache = ParserElement._UnboundedCache()
-            else:
-                ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit)
-            ParserElement._parse = ParserElement._parseCache
-
-    def parseString( self, instring, parseAll=False ):
-        """
-        Execute the parse expression with the given string.
-        This is the main interface to the client code, once the complete
-        expression has been built.
-
-        If you want the grammar to require that the entire input string be
-        successfully parsed, then set C{parseAll} to True (equivalent to ending
-        the grammar with C{L{StringEnd()}}).
-
-        Note: C{parseString} implicitly calls C{expandtabs()} on the input string,
-        in order to report proper column numbers in parse actions.
-        If the input string contains tabs and
-        the grammar uses parse actions that use the C{loc} argument to index into the
-        string being parsed, you can ensure you have a consistent view of the input
-        string by:
-         - calling C{parseWithTabs} on your grammar before calling C{parseString}
-           (see L{I{parseWithTabs}})
-         - define your parse action using the full C{(s,loc,toks)} signature, and
-           reference the input string using the parse action's C{s} argument
-         - explicitly expand the tabs in your input string before calling
-           C{parseString}
-        
-        Example::
-            Word('a').parseString('aaaaabaaa')  # -> ['aaaaa']
-            Word('a').parseString('aaaaabaaa', parseAll=True)  # -> Exception: Expected end of text
-        """
-        ParserElement.resetCache()
-        if not self.streamlined:
-            self.streamline()
-            #~ self.saveAsList = True
-        for e in self.ignoreExprs:
-            e.streamline()
-        if not self.keepTabs:
-            instring = instring.expandtabs()
-        try:
-            loc, tokens = self._parse( instring, 0 )
-            if parseAll:
-                loc = self.preParse( instring, loc )
-                se = Empty() + StringEnd()
-                se._parse( instring, loc )
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-        else:
-            return tokens
-
-    def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ):
-        """
-        Scan the input string for expression matches.  Each match will return the
-        matching tokens, start location, and end location.  May be called with optional
-        C{maxMatches} argument, to clip scanning after 'n' matches are found.  If
-        C{overlap} is specified, then overlapping matches will be reported.
-
-        Note that the start and end locations are reported relative to the string
-        being parsed.  See L{I{parseString}} for more information on parsing
-        strings with embedded tabs.
-
-        Example::
-            source = "sldjf123lsdjjkf345sldkjf879lkjsfd987"
-            print(source)
-            for tokens,start,end in Word(alphas).scanString(source):
-                print(' '*start + '^'*(end-start))
-                print(' '*start + tokens[0])
-        
-        prints::
-        
-            sldjf123lsdjjkf345sldkjf879lkjsfd987
-            ^^^^^
-            sldjf
-                    ^^^^^^^
-                    lsdjjkf
-                              ^^^^^^
-                              sldkjf
-                                       ^^^^^^
-                                       lkjsfd
-        """
-        if not self.streamlined:
-            self.streamline()
-        for e in self.ignoreExprs:
-            e.streamline()
-
-        if not self.keepTabs:
-            instring = _ustr(instring).expandtabs()
-        instrlen = len(instring)
-        loc = 0
-        preparseFn = self.preParse
-        parseFn = self._parse
-        ParserElement.resetCache()
-        matches = 0
-        try:
-            while loc <= instrlen and matches < maxMatches:
-                try:
-                    preloc = preparseFn( instring, loc )
-                    nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
-                except ParseException:
-                    loc = preloc+1
-                else:
-                    if nextLoc > loc:
-                        matches += 1
-                        yield tokens, preloc, nextLoc
-                        if overlap:
-                            nextloc = preparseFn( instring, loc )
-                            if nextloc > loc:
-                                loc = nextLoc
-                            else:
-                                loc += 1
-                        else:
-                            loc = nextLoc
-                    else:
-                        loc = preloc+1
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def transformString( self, instring ):
-        """
-        Extension to C{L{scanString}}, to modify matching text with modified tokens that may
-        be returned from a parse action.  To use C{transformString}, define a grammar and
-        attach a parse action to it that modifies the returned token list.
-        Invoking C{transformString()} on a target string will then scan for matches,
-        and replace the matched text patterns according to the logic in the parse
-        action.  C{transformString()} returns the resulting transformed string.
-        
-        Example::
-            wd = Word(alphas)
-            wd.setParseAction(lambda toks: toks[0].title())
-            
-            print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york."))
-        Prints::
-            Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York.
-        """
-        out = []
-        lastE = 0
-        # force preservation of s, to minimize unwanted transformation of string, and to
-        # keep string locs straight between transformString and scanString
-        self.keepTabs = True
-        try:
-            for t,s,e in self.scanString( instring ):
-                out.append( instring[lastE:s] )
-                if t:
-                    if isinstance(t,ParseResults):
-                        out += t.asList()
-                    elif isinstance(t,list):
-                        out += t
-                    else:
-                        out.append(t)
-                lastE = e
-            out.append(instring[lastE:])
-            out = [o for o in out if o]
-            return "".join(map(_ustr,_flatten(out)))
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def searchString( self, instring, maxMatches=_MAX_INT ):
-        """
-        Another extension to C{L{scanString}}, simplifying the access to the tokens found
-        to match the given parse expression.  May be called with optional
-        C{maxMatches} argument, to clip searching after 'n' matches are found.
-        
-        Example::
-            # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters
-            cap_word = Word(alphas.upper(), alphas.lower())
-            
-            print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))
-
-            # the sum() builtin can be used to merge results into a single ParseResults object
-            print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")))
-        prints::
-            [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']]
-            ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity']
-        """
-        try:
-            return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ])
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False):
-        """
-        Generator method to split a string using the given expression as a separator.
-        May be called with optional C{maxsplit} argument, to limit the number of splits;
-        and the optional C{includeSeparators} argument (default=C{False}), if the separating
-        matching text should be included in the split results.
-        
-        Example::        
-            punc = oneOf(list(".,;:/-!?"))
-            print(list(punc.split("This, this?, this sentence, is badly punctuated!")))
-        prints::
-            ['This', ' this', '', ' this sentence', ' is badly punctuated', '']
-        """
-        splits = 0
-        last = 0
-        for t,s,e in self.scanString(instring, maxMatches=maxsplit):
-            yield instring[last:s]
-            if includeSeparators:
-                yield t[0]
-            last = e
-        yield instring[last:]
-
-    def __add__(self, other ):
-        """
-        Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement
-        converts them to L{Literal}s by default.
-        
-        Example::
-            greet = Word(alphas) + "," + Word(alphas) + "!"
-            hello = "Hello, World!"
-            print (hello, "->", greet.parseString(hello))
-        Prints::
-            Hello, World! -> ['Hello', ',', 'World', '!']
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return And( [ self, other ] )
-
-    def __radd__(self, other ):
-        """
-        Implementation of + operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other + self
-
-    def __sub__(self, other):
-        """
-        Implementation of - operator, returns C{L{And}} with error stop
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return self + And._ErrorStop() + other
-
-    def __rsub__(self, other ):
-        """
-        Implementation of - operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other - self
-
-    def __mul__(self,other):
-        """
-        Implementation of * operator, allows use of C{expr * 3} in place of
-        C{expr + expr + expr}.  Expressions may also me multiplied by a 2-integer
-        tuple, similar to C{{min,max}} multipliers in regular expressions.  Tuples
-        may also include C{None} as in:
-         - C{expr*(n,None)} or C{expr*(n,)} is equivalent
-              to C{expr*n + L{ZeroOrMore}(expr)}
-              (read as "at least n instances of C{expr}")
-         - C{expr*(None,n)} is equivalent to C{expr*(0,n)}
-              (read as "0 to n instances of C{expr}")
-         - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)}
-         - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)}
-
-        Note that C{expr*(None,n)} does not raise an exception if
-        more than n exprs exist in the input stream; that is,
-        C{expr*(None,n)} does not enforce a maximum number of expr
-        occurrences.  If this behavior is desired, then write
-        C{expr*(None,n) + ~expr}
-        """
-        if isinstance(other,int):
-            minElements, optElements = other,0
-        elif isinstance(other,tuple):
-            other = (other + (None, None))[:2]
-            if other[0] is None:
-                other = (0, other[1])
-            if isinstance(other[0],int) and other[1] is None:
-                if other[0] == 0:
-                    return ZeroOrMore(self)
-                if other[0] == 1:
-                    return OneOrMore(self)
-                else:
-                    return self*other[0] + ZeroOrMore(self)
-            elif isinstance(other[0],int) and isinstance(other[1],int):
-                minElements, optElements = other
-                optElements -= minElements
-            else:
-                raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1]))
-        else:
-            raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other))
-
-        if minElements < 0:
-            raise ValueError("cannot multiply ParserElement by negative value")
-        if optElements < 0:
-            raise ValueError("second tuple value must be greater or equal to first tuple value")
-        if minElements == optElements == 0:
-            raise ValueError("cannot multiply ParserElement by 0 or (0,0)")
-
-        if (optElements):
-            def makeOptionalList(n):
-                if n>1:
-                    return Optional(self + makeOptionalList(n-1))
-                else:
-                    return Optional(self)
-            if minElements:
-                if minElements == 1:
-                    ret = self + makeOptionalList(optElements)
-                else:
-                    ret = And([self]*minElements) + makeOptionalList(optElements)
-            else:
-                ret = makeOptionalList(optElements)
-        else:
-            if minElements == 1:
-                ret = self
-            else:
-                ret = And([self]*minElements)
-        return ret
-
-    def __rmul__(self, other):
-        return self.__mul__(other)
-
-    def __or__(self, other ):
-        """
-        Implementation of | operator - returns C{L{MatchFirst}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return MatchFirst( [ self, other ] )
-
-    def __ror__(self, other ):
-        """
-        Implementation of | operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other | self
-
-    def __xor__(self, other ):
-        """
-        Implementation of ^ operator - returns C{L{Or}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return Or( [ self, other ] )
-
-    def __rxor__(self, other ):
-        """
-        Implementation of ^ operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other ^ self
-
-    def __and__(self, other ):
-        """
-        Implementation of & operator - returns C{L{Each}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return Each( [ self, other ] )
-
-    def __rand__(self, other ):
-        """
-        Implementation of & operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other & self
-
-    def __invert__( self ):
-        """
-        Implementation of ~ operator - returns C{L{NotAny}}
-        """
-        return NotAny( self )
-
-    def __call__(self, name=None):
-        """
-        Shortcut for C{L{setResultsName}}, with C{listAllMatches=False}.
-        
-        If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be
-        passed as C{True}.
-           
-        If C{name} is omitted, same as calling C{L{copy}}.
-
-        Example::
-            # these are equivalent
-            userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno")
-            userdata = Word(alphas)("name") + Word(nums+"-")("socsecno")             
-        """
-        if name is not None:
-            return self.setResultsName(name)
-        else:
-            return self.copy()
-
-    def suppress( self ):
-        """
-        Suppresses the output of this C{ParserElement}; useful to keep punctuation from
-        cluttering up returned output.
-        """
-        return Suppress( self )
-
-    def leaveWhitespace( self ):
-        """
-        Disables the skipping of whitespace before matching the characters in the
-        C{ParserElement}'s defined pattern.  This is normally only used internally by
-        the pyparsing module, but may be needed in some whitespace-sensitive grammars.
-        """
-        self.skipWhitespace = False
-        return self
-
-    def setWhitespaceChars( self, chars ):
-        """
-        Overrides the default whitespace chars
-        """
-        self.skipWhitespace = True
-        self.whiteChars = chars
-        self.copyDefaultWhiteChars = False
-        return self
-
-    def parseWithTabs( self ):
-        """
-        Overrides default behavior to expand C{}s to spaces before parsing the input string.
-        Must be called before C{parseString} when the input grammar contains elements that
-        match C{} characters.
-        """
-        self.keepTabs = True
-        return self
-
-    def ignore( self, other ):
-        """
-        Define expression to be ignored (e.g., comments) while doing pattern
-        matching; may be called repeatedly, to define multiple comment or other
-        ignorable patterns.
-        
-        Example::
-            patt = OneOrMore(Word(alphas))
-            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj']
-            
-            patt.ignore(cStyleComment)
-            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd']
-        """
-        if isinstance(other, basestring):
-            other = Suppress(other)
-
-        if isinstance( other, Suppress ):
-            if other not in self.ignoreExprs:
-                self.ignoreExprs.append(other)
-        else:
-            self.ignoreExprs.append( Suppress( other.copy() ) )
-        return self
-
-    def setDebugActions( self, startAction, successAction, exceptionAction ):
-        """
-        Enable display of debugging messages while doing pattern matching.
-        """
-        self.debugActions = (startAction or _defaultStartDebugAction,
-                             successAction or _defaultSuccessDebugAction,
-                             exceptionAction or _defaultExceptionDebugAction)
-        self.debug = True
-        return self
-
-    def setDebug( self, flag=True ):
-        """
-        Enable display of debugging messages while doing pattern matching.
-        Set C{flag} to True to enable, False to disable.
-
-        Example::
-            wd = Word(alphas).setName("alphaword")
-            integer = Word(nums).setName("numword")
-            term = wd | integer
-            
-            # turn on debugging for wd
-            wd.setDebug()
-
-            OneOrMore(term).parseString("abc 123 xyz 890")
-        
-        prints::
-            Match alphaword at loc 0(1,1)
-            Matched alphaword -> ['abc']
-            Match alphaword at loc 3(1,4)
-            Exception raised:Expected alphaword (at char 4), (line:1, col:5)
-            Match alphaword at loc 7(1,8)
-            Matched alphaword -> ['xyz']
-            Match alphaword at loc 11(1,12)
-            Exception raised:Expected alphaword (at char 12), (line:1, col:13)
-            Match alphaword at loc 15(1,16)
-            Exception raised:Expected alphaword (at char 15), (line:1, col:16)
-
-        The output shown is that produced by the default debug actions - custom debug actions can be
-        specified using L{setDebugActions}. Prior to attempting
-        to match the C{wd} expression, the debugging message C{"Match  at loc (,)"}
-        is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"}
-        message is shown. Also note the use of L{setName} to assign a human-readable name to the expression,
-        which makes debugging and exception messages easier to understand - for instance, the default
-        name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}.
-        """
-        if flag:
-            self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction )
-        else:
-            self.debug = False
-        return self
-
-    def __str__( self ):
-        return self.name
-
-    def __repr__( self ):
-        return _ustr(self)
-
-    def streamline( self ):
-        self.streamlined = True
-        self.strRepr = None
-        return self
-
-    def checkRecursion( self, parseElementList ):
-        pass
-
-    def validate( self, validateTrace=[] ):
-        """
-        Check defined expressions for valid structure, check for infinite recursive definitions.
-        """
-        self.checkRecursion( [] )
-
-    def parseFile( self, file_or_filename, parseAll=False ):
-        """
-        Execute the parse expression on the given file or filename.
-        If a filename is specified (instead of a file object),
-        the entire file is opened, read, and closed before parsing.
-        """
-        try:
-            file_contents = file_or_filename.read()
-        except AttributeError:
-            with open(file_or_filename, "r") as f:
-                file_contents = f.read()
-        try:
-            return self.parseString(file_contents, parseAll)
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def __eq__(self,other):
-        if isinstance(other, ParserElement):
-            return self is other or vars(self) == vars(other)
-        elif isinstance(other, basestring):
-            return self.matches(other)
-        else:
-            return super(ParserElement,self)==other
-
-    def __ne__(self,other):
-        return not (self == other)
-
-    def __hash__(self):
-        return hash(id(self))
-
-    def __req__(self,other):
-        return self == other
-
-    def __rne__(self,other):
-        return not (self == other)
-
-    def matches(self, testString, parseAll=True):
-        """
-        Method for quick testing of a parser against a test string. Good for simple 
-        inline microtests of sub expressions while building up larger parser.
-           
-        Parameters:
-         - testString - to test against this expression for a match
-         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests
-            
-        Example::
-            expr = Word(nums)
-            assert expr.matches("100")
-        """
-        try:
-            self.parseString(_ustr(testString), parseAll=parseAll)
-            return True
-        except ParseBaseException:
-            return False
-                
-    def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False):
-        """
-        Execute the parse expression on a series of test strings, showing each
-        test, the parsed results or where the parse failed. Quick and easy way to
-        run a parse expression against a list of sample strings.
-           
-        Parameters:
-         - tests - a list of separate test strings, or a multiline string of test strings
-         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests           
-         - comment - (default=C{'#'}) - expression for indicating embedded comments in the test 
-              string; pass None to disable comment filtering
-         - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline;
-              if False, only dump nested list
-         - printResults - (default=C{True}) prints test output to stdout
-         - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing
-
-        Returns: a (success, results) tuple, where success indicates that all tests succeeded
-        (or failed if C{failureTests} is True), and the results contain a list of lines of each 
-        test's output
-        
-        Example::
-            number_expr = pyparsing_common.number.copy()
-
-            result = number_expr.runTests('''
-                # unsigned integer
-                100
-                # negative integer
-                -100
-                # float with scientific notation
-                6.02e23
-                # integer with scientific notation
-                1e-12
-                ''')
-            print("Success" if result[0] else "Failed!")
-
-            result = number_expr.runTests('''
-                # stray character
-                100Z
-                # missing leading digit before '.'
-                -.100
-                # too many '.'
-                3.14.159
-                ''', failureTests=True)
-            print("Success" if result[0] else "Failed!")
-        prints::
-            # unsigned integer
-            100
-            [100]
-
-            # negative integer
-            -100
-            [-100]
-
-            # float with scientific notation
-            6.02e23
-            [6.02e+23]
-
-            # integer with scientific notation
-            1e-12
-            [1e-12]
-
-            Success
-            
-            # stray character
-            100Z
-               ^
-            FAIL: Expected end of text (at char 3), (line:1, col:4)
-
-            # missing leading digit before '.'
-            -.100
-            ^
-            FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1)
-
-            # too many '.'
-            3.14.159
-                ^
-            FAIL: Expected end of text (at char 4), (line:1, col:5)
-
-            Success
-
-        Each test string must be on a single line. If you want to test a string that spans multiple
-        lines, create a test like this::
-
-            expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines")
-        
-        (Note that this is a raw string literal, you must include the leading 'r'.)
-        """
-        if isinstance(tests, basestring):
-            tests = list(map(str.strip, tests.rstrip().splitlines()))
-        if isinstance(comment, basestring):
-            comment = Literal(comment)
-        allResults = []
-        comments = []
-        success = True
-        for t in tests:
-            if comment is not None and comment.matches(t, False) or comments and not t:
-                comments.append(t)
-                continue
-            if not t:
-                continue
-            out = ['\n'.join(comments), t]
-            comments = []
-            try:
-                t = t.replace(r'\n','\n')
-                result = self.parseString(t, parseAll=parseAll)
-                out.append(result.dump(full=fullDump))
-                success = success and not failureTests
-            except ParseBaseException as pe:
-                fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else ""
-                if '\n' in t:
-                    out.append(line(pe.loc, t))
-                    out.append(' '*(col(pe.loc,t)-1) + '^' + fatal)
-                else:
-                    out.append(' '*pe.loc + '^' + fatal)
-                out.append("FAIL: " + str(pe))
-                success = success and failureTests
-                result = pe
-            except Exception as exc:
-                out.append("FAIL-EXCEPTION: " + str(exc))
-                success = success and failureTests
-                result = exc
-
-            if printResults:
-                if fullDump:
-                    out.append('')
-                print('\n'.join(out))
-
-            allResults.append((t, result))
-        
-        return success, allResults
-
-        
-class Token(ParserElement):
-    """
-    Abstract C{ParserElement} subclass, for defining atomic matching patterns.
-    """
-    def __init__( self ):
-        super(Token,self).__init__( savelist=False )
-
-
-class Empty(Token):
-    """
-    An empty token, will always match.
-    """
-    def __init__( self ):
-        super(Empty,self).__init__()
-        self.name = "Empty"
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-
-
-class NoMatch(Token):
-    """
-    A token that will never match.
-    """
-    def __init__( self ):
-        super(NoMatch,self).__init__()
-        self.name = "NoMatch"
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-        self.errmsg = "Unmatchable token"
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        raise ParseException(instring, loc, self.errmsg, self)
-
-
-class Literal(Token):
-    """
-    Token to exactly match a specified string.
-    
-    Example::
-        Literal('blah').parseString('blah')  # -> ['blah']
-        Literal('blah').parseString('blahfooblah')  # -> ['blah']
-        Literal('blah').parseString('bla')  # -> Exception: Expected "blah"
-    
-    For case-insensitive matching, use L{CaselessLiteral}.
-    
-    For keyword matching (force word break before and after the matched string),
-    use L{Keyword} or L{CaselessKeyword}.
-    """
-    def __init__( self, matchString ):
-        super(Literal,self).__init__()
-        self.match = matchString
-        self.matchLen = len(matchString)
-        try:
-            self.firstMatchChar = matchString[0]
-        except IndexError:
-            warnings.warn("null string passed to Literal; use Empty() instead",
-                            SyntaxWarning, stacklevel=2)
-            self.__class__ = Empty
-        self.name = '"%s"' % _ustr(self.match)
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = False
-        self.mayIndexError = False
-
-    # Performance tuning: this routine gets called a *lot*
-    # if this is a single character match string  and the first character matches,
-    # short-circuit as quickly as possible, and avoid calling startswith
-    #~ @profile
-    def parseImpl( self, instring, loc, doActions=True ):
-        if (instring[loc] == self.firstMatchChar and
-            (self.matchLen==1 or instring.startswith(self.match,loc)) ):
-            return loc+self.matchLen, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-_L = Literal
-ParserElement._literalStringClass = Literal
-
-class Keyword(Token):
-    """
-    Token to exactly match a specified string as a keyword, that is, it must be
-    immediately followed by a non-keyword character.  Compare with C{L{Literal}}:
-     - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}.
-     - C{Keyword("if")} will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'}
-    Accepts two optional constructor arguments in addition to the keyword string:
-     - C{identChars} is a string of characters that would be valid identifier characters,
-          defaulting to all alphanumerics + "_" and "$"
-     - C{caseless} allows case-insensitive matching, default is C{False}.
-       
-    Example::
-        Keyword("start").parseString("start")  # -> ['start']
-        Keyword("start").parseString("starting")  # -> Exception
-
-    For case-insensitive matching, use L{CaselessKeyword}.
-    """
-    DEFAULT_KEYWORD_CHARS = alphanums+"_$"
-
-    def __init__( self, matchString, identChars=None, caseless=False ):
-        super(Keyword,self).__init__()
-        if identChars is None:
-            identChars = Keyword.DEFAULT_KEYWORD_CHARS
-        self.match = matchString
-        self.matchLen = len(matchString)
-        try:
-            self.firstMatchChar = matchString[0]
-        except IndexError:
-            warnings.warn("null string passed to Keyword; use Empty() instead",
-                            SyntaxWarning, stacklevel=2)
-        self.name = '"%s"' % self.match
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = False
-        self.mayIndexError = False
-        self.caseless = caseless
-        if caseless:
-            self.caselessmatch = matchString.upper()
-            identChars = identChars.upper()
-        self.identChars = set(identChars)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.caseless:
-            if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
-                 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and
-                 (loc == 0 or instring[loc-1].upper() not in self.identChars) ):
-                return loc+self.matchLen, self.match
-        else:
-            if (instring[loc] == self.firstMatchChar and
-                (self.matchLen==1 or instring.startswith(self.match,loc)) and
-                (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and
-                (loc == 0 or instring[loc-1] not in self.identChars) ):
-                return loc+self.matchLen, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-
-    def copy(self):
-        c = super(Keyword,self).copy()
-        c.identChars = Keyword.DEFAULT_KEYWORD_CHARS
-        return c
-
-    @staticmethod
-    def setDefaultKeywordChars( chars ):
-        """Overrides the default Keyword chars
-        """
-        Keyword.DEFAULT_KEYWORD_CHARS = chars
-
-class CaselessLiteral(Literal):
-    """
-    Token to match a specified string, ignoring case of letters.
-    Note: the matched results will always be in the case of the given
-    match string, NOT the case of the input text.
-
-    Example::
-        OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD']
-        
-    (Contrast with example for L{CaselessKeyword}.)
-    """
-    def __init__( self, matchString ):
-        super(CaselessLiteral,self).__init__( matchString.upper() )
-        # Preserve the defining literal.
-        self.returnString = matchString
-        self.name = "'%s'" % self.returnString
-        self.errmsg = "Expected " + self.name
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if instring[ loc:loc+self.matchLen ].upper() == self.match:
-            return loc+self.matchLen, self.returnString
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class CaselessKeyword(Keyword):
-    """
-    Caseless version of L{Keyword}.
-
-    Example::
-        OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD']
-        
-    (Contrast with example for L{CaselessLiteral}.)
-    """
-    def __init__( self, matchString, identChars=None ):
-        super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True )
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
-             (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ):
-            return loc+self.matchLen, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class CloseMatch(Token):
-    """
-    A variation on L{Literal} which matches "close" matches, that is, 
-    strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters:
-     - C{match_string} - string to be matched
-     - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match
-    
-    The results from a successful parse will contain the matched text from the input string and the following named results:
-     - C{mismatches} - a list of the positions within the match_string where mismatches were found
-     - C{original} - the original match_string used to compare against the input string
-    
-    If C{mismatches} is an empty list, then the match was an exact match.
-    
-    Example::
-        patt = CloseMatch("ATCATCGAATGGA")
-        patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']})
-        patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1)
-
-        # exact match
-        patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']})
-
-        # close match allowing up to 2 mismatches
-        patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2)
-        patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']})
-    """
-    def __init__(self, match_string, maxMismatches=1):
-        super(CloseMatch,self).__init__()
-        self.name = match_string
-        self.match_string = match_string
-        self.maxMismatches = maxMismatches
-        self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches)
-        self.mayIndexError = False
-        self.mayReturnEmpty = False
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        start = loc
-        instrlen = len(instring)
-        maxloc = start + len(self.match_string)
-
-        if maxloc <= instrlen:
-            match_string = self.match_string
-            match_stringloc = 0
-            mismatches = []
-            maxMismatches = self.maxMismatches
-
-            for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)):
-                src,mat = s_m
-                if src != mat:
-                    mismatches.append(match_stringloc)
-                    if len(mismatches) > maxMismatches:
-                        break
-            else:
-                loc = match_stringloc + 1
-                results = ParseResults([instring[start:loc]])
-                results['original'] = self.match_string
-                results['mismatches'] = mismatches
-                return loc, results
-
-        raise ParseException(instring, loc, self.errmsg, self)
-
-
-class Word(Token):
-    """
-    Token for matching words composed of allowed character sets.
-    Defined with string containing all allowed initial characters,
-    an optional string containing allowed body characters (if omitted,
-    defaults to the initial character set), and an optional minimum,
-    maximum, and/or exact length.  The default value for C{min} is 1 (a
-    minimum value < 1 is not valid); the default values for C{max} and C{exact}
-    are 0, meaning no maximum or exact length restriction. An optional
-    C{excludeChars} parameter can list characters that might be found in 
-    the input C{bodyChars} string; useful to define a word of all printables
-    except for one or two characters, for instance.
-    
-    L{srange} is useful for defining custom character set strings for defining 
-    C{Word} expressions, using range notation from regular expression character sets.
-    
-    A common mistake is to use C{Word} to match a specific literal string, as in 
-    C{Word("Address")}. Remember that C{Word} uses the string argument to define
-    I{sets} of matchable characters. This expression would match "Add", "AAA",
-    "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'.
-    To match an exact literal string, use L{Literal} or L{Keyword}.
-
-    pyparsing includes helper strings for building Words:
-     - L{alphas}
-     - L{nums}
-     - L{alphanums}
-     - L{hexnums}
-     - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.)
-     - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.)
-     - L{printables} (any non-whitespace character)
-
-    Example::
-        # a word composed of digits
-        integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9"))
-        
-        # a word with a leading capital, and zero or more lowercase
-        capital_word = Word(alphas.upper(), alphas.lower())
-
-        # hostnames are alphanumeric, with leading alpha, and '-'
-        hostname = Word(alphas, alphanums+'-')
-        
-        # roman numeral (not a strict parser, accepts invalid mix of characters)
-        roman = Word("IVXLCDM")
-        
-        # any string of non-whitespace characters, except for ','
-        csv_value = Word(printables, excludeChars=",")
-    """
-    def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ):
-        super(Word,self).__init__()
-        if excludeChars:
-            initChars = ''.join(c for c in initChars if c not in excludeChars)
-            if bodyChars:
-                bodyChars = ''.join(c for c in bodyChars if c not in excludeChars)
-        self.initCharsOrig = initChars
-        self.initChars = set(initChars)
-        if bodyChars :
-            self.bodyCharsOrig = bodyChars
-            self.bodyChars = set(bodyChars)
-        else:
-            self.bodyCharsOrig = initChars
-            self.bodyChars = set(initChars)
-
-        self.maxSpecified = max > 0
-
-        if min < 1:
-            raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted")
-
-        self.minLen = min
-
-        if max > 0:
-            self.maxLen = max
-        else:
-            self.maxLen = _MAX_INT
-
-        if exact > 0:
-            self.maxLen = exact
-            self.minLen = exact
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.asKeyword = asKeyword
-
-        if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0):
-            if self.bodyCharsOrig == self.initCharsOrig:
-                self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig)
-            elif len(self.initCharsOrig) == 1:
-                self.reString = "%s[%s]*" % \
-                                      (re.escape(self.initCharsOrig),
-                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
-            else:
-                self.reString = "[%s][%s]*" % \
-                                      (_escapeRegexRangeChars(self.initCharsOrig),
-                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
-            if self.asKeyword:
-                self.reString = r"\b"+self.reString+r"\b"
-            try:
-                self.re = re.compile( self.reString )
-            except Exception:
-                self.re = None
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.re:
-            result = self.re.match(instring,loc)
-            if not result:
-                raise ParseException(instring, loc, self.errmsg, self)
-
-            loc = result.end()
-            return loc, result.group()
-
-        if not(instring[ loc ] in self.initChars):
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        start = loc
-        loc += 1
-        instrlen = len(instring)
-        bodychars = self.bodyChars
-        maxloc = start + self.maxLen
-        maxloc = min( maxloc, instrlen )
-        while loc < maxloc and instring[loc] in bodychars:
-            loc += 1
-
-        throwException = False
-        if loc - start < self.minLen:
-            throwException = True
-        if self.maxSpecified and loc < instrlen and instring[loc] in bodychars:
-            throwException = True
-        if self.asKeyword:
-            if (start>0 and instring[start-1] in bodychars) or (loc4:
-                    return s[:4]+"..."
-                else:
-                    return s
-
-            if ( self.initCharsOrig != self.bodyCharsOrig ):
-                self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) )
-            else:
-                self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig)
-
-        return self.strRepr
-
-
-class Regex(Token):
-    r"""
-    Token for matching strings that match a given regular expression.
-    Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module.
-    If the given regex contains named groups (defined using C{(?P...)}), these will be preserved as 
-    named parse results.
-
-    Example::
-        realnum = Regex(r"[+-]?\d+\.\d*")
-        date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\d\d?)')
-        # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression
-        roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})")
-    """
-    compiledREtype = type(re.compile("[A-Z]"))
-    def __init__( self, pattern, flags=0):
-        """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags."""
-        super(Regex,self).__init__()
-
-        if isinstance(pattern, basestring):
-            if not pattern:
-                warnings.warn("null string passed to Regex; use Empty() instead",
-                        SyntaxWarning, stacklevel=2)
-
-            self.pattern = pattern
-            self.flags = flags
-
-            try:
-                self.re = re.compile(self.pattern, self.flags)
-                self.reString = self.pattern
-            except sre_constants.error:
-                warnings.warn("invalid pattern (%s) passed to Regex" % pattern,
-                    SyntaxWarning, stacklevel=2)
-                raise
-
-        elif isinstance(pattern, Regex.compiledREtype):
-            self.re = pattern
-            self.pattern = \
-            self.reString = str(pattern)
-            self.flags = flags
-            
-        else:
-            raise ValueError("Regex may only be constructed with a string or a compiled RE object")
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        result = self.re.match(instring,loc)
-        if not result:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        loc = result.end()
-        d = result.groupdict()
-        ret = ParseResults(result.group())
-        if d:
-            for k in d:
-                ret[k] = d[k]
-        return loc,ret
-
-    def __str__( self ):
-        try:
-            return super(Regex,self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-            self.strRepr = "Re:(%s)" % repr(self.pattern)
-
-        return self.strRepr
-
-
-class QuotedString(Token):
-    r"""
-    Token for matching strings that are delimited by quoting characters.
-    
-    Defined with the following parameters:
-        - quoteChar - string of one or more characters defining the quote delimiting string
-        - escChar - character to escape quotes, typically backslash (default=C{None})
-        - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{None})
-        - multiline - boolean indicating whether quotes can span multiple lines (default=C{False})
-        - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True})
-        - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar)
-        - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True})
-
-    Example::
-        qs = QuotedString('"')
-        print(qs.searchString('lsjdf "This is the quote" sldjf'))
-        complex_qs = QuotedString('{{', endQuoteChar='}}')
-        print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf'))
-        sql_qs = QuotedString('"', escQuote='""')
-        print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf'))
-    prints::
-        [['This is the quote']]
-        [['This is the "quote"']]
-        [['This is the quote with "embedded" quotes']]
-    """
-    def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True):
-        super(QuotedString,self).__init__()
-
-        # remove white space from quote chars - wont work anyway
-        quoteChar = quoteChar.strip()
-        if not quoteChar:
-            warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
-            raise SyntaxError()
-
-        if endQuoteChar is None:
-            endQuoteChar = quoteChar
-        else:
-            endQuoteChar = endQuoteChar.strip()
-            if not endQuoteChar:
-                warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
-                raise SyntaxError()
-
-        self.quoteChar = quoteChar
-        self.quoteCharLen = len(quoteChar)
-        self.firstQuoteChar = quoteChar[0]
-        self.endQuoteChar = endQuoteChar
-        self.endQuoteCharLen = len(endQuoteChar)
-        self.escChar = escChar
-        self.escQuote = escQuote
-        self.unquoteResults = unquoteResults
-        self.convertWhitespaceEscapes = convertWhitespaceEscapes
-
-        if multiline:
-            self.flags = re.MULTILINE | re.DOTALL
-            self.pattern = r'%s(?:[^%s%s]' % \
-                ( re.escape(self.quoteChar),
-                  _escapeRegexRangeChars(self.endQuoteChar[0]),
-                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
-        else:
-            self.flags = 0
-            self.pattern = r'%s(?:[^%s\n\r%s]' % \
-                ( re.escape(self.quoteChar),
-                  _escapeRegexRangeChars(self.endQuoteChar[0]),
-                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
-        if len(self.endQuoteChar) > 1:
-            self.pattern += (
-                '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]),
-                                               _escapeRegexRangeChars(self.endQuoteChar[i]))
-                                    for i in range(len(self.endQuoteChar)-1,0,-1)) + ')'
-                )
-        if escQuote:
-            self.pattern += (r'|(?:%s)' % re.escape(escQuote))
-        if escChar:
-            self.pattern += (r'|(?:%s.)' % re.escape(escChar))
-            self.escCharReplacePattern = re.escape(self.escChar)+"(.)"
-        self.pattern += (r')*%s' % re.escape(self.endQuoteChar))
-
-        try:
-            self.re = re.compile(self.pattern, self.flags)
-            self.reString = self.pattern
-        except sre_constants.error:
-            warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern,
-                SyntaxWarning, stacklevel=2)
-            raise
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None
-        if not result:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        loc = result.end()
-        ret = result.group()
-
-        if self.unquoteResults:
-
-            # strip off quotes
-            ret = ret[self.quoteCharLen:-self.endQuoteCharLen]
-
-            if isinstance(ret,basestring):
-                # replace escaped whitespace
-                if '\\' in ret and self.convertWhitespaceEscapes:
-                    ws_map = {
-                        r'\t' : '\t',
-                        r'\n' : '\n',
-                        r'\f' : '\f',
-                        r'\r' : '\r',
-                    }
-                    for wslit,wschar in ws_map.items():
-                        ret = ret.replace(wslit, wschar)
-
-                # replace escaped characters
-                if self.escChar:
-                    ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret)
-
-                # replace escaped quotes
-                if self.escQuote:
-                    ret = ret.replace(self.escQuote, self.endQuoteChar)
-
-        return loc, ret
-
-    def __str__( self ):
-        try:
-            return super(QuotedString,self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-            self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar)
-
-        return self.strRepr
-
-
-class CharsNotIn(Token):
-    """
-    Token for matching words composed of characters I{not} in a given set (will
-    include whitespace in matched characters if not listed in the provided exclusion set - see example).
-    Defined with string containing all disallowed characters, and an optional
-    minimum, maximum, and/or exact length.  The default value for C{min} is 1 (a
-    minimum value < 1 is not valid); the default values for C{max} and C{exact}
-    are 0, meaning no maximum or exact length restriction.
-
-    Example::
-        # define a comma-separated-value as anything that is not a ','
-        csv_value = CharsNotIn(',')
-        print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213"))
-    prints::
-        ['dkls', 'lsdkjf', 's12 34', '@!#', '213']
-    """
-    def __init__( self, notChars, min=1, max=0, exact=0 ):
-        super(CharsNotIn,self).__init__()
-        self.skipWhitespace = False
-        self.notChars = notChars
-
-        if min < 1:
-            raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted")
-
-        self.minLen = min
-
-        if max > 0:
-            self.maxLen = max
-        else:
-            self.maxLen = _MAX_INT
-
-        if exact > 0:
-            self.maxLen = exact
-            self.minLen = exact
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = ( self.minLen == 0 )
-        self.mayIndexError = False
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if instring[loc] in self.notChars:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        start = loc
-        loc += 1
-        notchars = self.notChars
-        maxlen = min( start+self.maxLen, len(instring) )
-        while loc < maxlen and \
-              (instring[loc] not in notchars):
-            loc += 1
-
-        if loc - start < self.minLen:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        return loc, instring[start:loc]
-
-    def __str__( self ):
-        try:
-            return super(CharsNotIn, self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-            if len(self.notChars) > 4:
-                self.strRepr = "!W:(%s...)" % self.notChars[:4]
-            else:
-                self.strRepr = "!W:(%s)" % self.notChars
-
-        return self.strRepr
-
-class White(Token):
-    """
-    Special matching class for matching whitespace.  Normally, whitespace is ignored
-    by pyparsing grammars.  This class is included when some whitespace structures
-    are significant.  Define with a string containing the whitespace characters to be
-    matched; default is C{" \\t\\r\\n"}.  Also takes optional C{min}, C{max}, and C{exact} arguments,
-    as defined for the C{L{Word}} class.
-    """
-    whiteStrs = {
-        " " : "",
-        "\t": "",
-        "\n": "",
-        "\r": "",
-        "\f": "",
-        }
-    def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0):
-        super(White,self).__init__()
-        self.matchWhite = ws
-        self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) )
-        #~ self.leaveWhitespace()
-        self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite))
-        self.mayReturnEmpty = True
-        self.errmsg = "Expected " + self.name
-
-        self.minLen = min
-
-        if max > 0:
-            self.maxLen = max
-        else:
-            self.maxLen = _MAX_INT
-
-        if exact > 0:
-            self.maxLen = exact
-            self.minLen = exact
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if not(instring[ loc ] in self.matchWhite):
-            raise ParseException(instring, loc, self.errmsg, self)
-        start = loc
-        loc += 1
-        maxloc = start + self.maxLen
-        maxloc = min( maxloc, len(instring) )
-        while loc < maxloc and instring[loc] in self.matchWhite:
-            loc += 1
-
-        if loc - start < self.minLen:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        return loc, instring[start:loc]
-
-
-class _PositionToken(Token):
-    def __init__( self ):
-        super(_PositionToken,self).__init__()
-        self.name=self.__class__.__name__
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-
-class GoToColumn(_PositionToken):
-    """
-    Token to advance to a specific column of input text; useful for tabular report scraping.
-    """
-    def __init__( self, colno ):
-        super(GoToColumn,self).__init__()
-        self.col = colno
-
-    def preParse( self, instring, loc ):
-        if col(loc,instring) != self.col:
-            instrlen = len(instring)
-            if self.ignoreExprs:
-                loc = self._skipIgnorables( instring, loc )
-            while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col :
-                loc += 1
-        return loc
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        thiscol = col( loc, instring )
-        if thiscol > self.col:
-            raise ParseException( instring, loc, "Text not in expected column", self )
-        newloc = loc + self.col - thiscol
-        ret = instring[ loc: newloc ]
-        return newloc, ret
-
-
-class LineStart(_PositionToken):
-    """
-    Matches if current position is at the beginning of a line within the parse string
-    
-    Example::
-    
-        test = '''\
-        AAA this line
-        AAA and this line
-          AAA but not this one
-        B AAA and definitely not this one
-        '''
-
-        for t in (LineStart() + 'AAA' + restOfLine).searchString(test):
-            print(t)
-    
-    Prints::
-        ['AAA', ' this line']
-        ['AAA', ' and this line']    
-
-    """
-    def __init__( self ):
-        super(LineStart,self).__init__()
-        self.errmsg = "Expected start of line"
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if col(loc, instring) == 1:
-            return loc, []
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class LineEnd(_PositionToken):
-    """
-    Matches if current position is at the end of a line within the parse string
-    """
-    def __init__( self ):
-        super(LineEnd,self).__init__()
-        self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") )
-        self.errmsg = "Expected end of line"
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if loc len(instring):
-            return loc, []
-        else:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-class WordStart(_PositionToken):
-    """
-    Matches if the current position is at the beginning of a Word, and
-    is not preceded by any character in a given set of C{wordChars}
-    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
-    use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of
-    the string being parsed, or at the beginning of a line.
-    """
-    def __init__(self, wordChars = printables):
-        super(WordStart,self).__init__()
-        self.wordChars = set(wordChars)
-        self.errmsg = "Not at the start of a word"
-
-    def parseImpl(self, instring, loc, doActions=True ):
-        if loc != 0:
-            if (instring[loc-1] in self.wordChars or
-                instring[loc] not in self.wordChars):
-                raise ParseException(instring, loc, self.errmsg, self)
-        return loc, []
-
-class WordEnd(_PositionToken):
-    """
-    Matches if the current position is at the end of a Word, and
-    is not followed by any character in a given set of C{wordChars}
-    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
-    use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of
-    the string being parsed, or at the end of a line.
-    """
-    def __init__(self, wordChars = printables):
-        super(WordEnd,self).__init__()
-        self.wordChars = set(wordChars)
-        self.skipWhitespace = False
-        self.errmsg = "Not at the end of a word"
-
-    def parseImpl(self, instring, loc, doActions=True ):
-        instrlen = len(instring)
-        if instrlen>0 and loc maxExcLoc:
-                    maxException = err
-                    maxExcLoc = err.loc
-            except IndexError:
-                if len(instring) > maxExcLoc:
-                    maxException = ParseException(instring,len(instring),e.errmsg,self)
-                    maxExcLoc = len(instring)
-            else:
-                # save match among all matches, to retry longest to shortest
-                matches.append((loc2, e))
-
-        if matches:
-            matches.sort(key=lambda x: -x[0])
-            for _,e in matches:
-                try:
-                    return e._parse( instring, loc, doActions )
-                except ParseException as err:
-                    err.__traceback__ = None
-                    if err.loc > maxExcLoc:
-                        maxException = err
-                        maxExcLoc = err.loc
-
-        if maxException is not None:
-            maxException.msg = self.errmsg
-            raise maxException
-        else:
-            raise ParseException(instring, loc, "no defined alternatives to match", self)
-
-
-    def __ixor__(self, other ):
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        return self.append( other ) #Or( [ self, other ] )
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}"
-
-        return self.strRepr
-
-    def checkRecursion( self, parseElementList ):
-        subRecCheckList = parseElementList[:] + [ self ]
-        for e in self.exprs:
-            e.checkRecursion( subRecCheckList )
-
-
-class MatchFirst(ParseExpression):
-    """
-    Requires that at least one C{ParseExpression} is found.
-    If two expressions match, the first one listed is the one that will match.
-    May be constructed using the C{'|'} operator.
-
-    Example::
-        # construct MatchFirst using '|' operator
-        
-        # watch the order of expressions to match
-        number = Word(nums) | Combine(Word(nums) + '.' + Word(nums))
-        print(number.searchString("123 3.1416 789")) #  Fail! -> [['123'], ['3'], ['1416'], ['789']]
-
-        # put more selective expression first
-        number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums)
-        print(number.searchString("123 3.1416 789")) #  Better -> [['123'], ['3.1416'], ['789']]
-    """
-    def __init__( self, exprs, savelist = False ):
-        super(MatchFirst,self).__init__(exprs, savelist)
-        if self.exprs:
-            self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
-        else:
-            self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        maxExcLoc = -1
-        maxException = None
-        for e in self.exprs:
-            try:
-                ret = e._parse( instring, loc, doActions )
-                return ret
-            except ParseException as err:
-                if err.loc > maxExcLoc:
-                    maxException = err
-                    maxExcLoc = err.loc
-            except IndexError:
-                if len(instring) > maxExcLoc:
-                    maxException = ParseException(instring,len(instring),e.errmsg,self)
-                    maxExcLoc = len(instring)
-
-        # only got here if no expression matched, raise exception for match that made it the furthest
-        else:
-            if maxException is not None:
-                maxException.msg = self.errmsg
-                raise maxException
-            else:
-                raise ParseException(instring, loc, "no defined alternatives to match", self)
-
-    def __ior__(self, other ):
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        return self.append( other ) #MatchFirst( [ self, other ] )
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}"
-
-        return self.strRepr
-
-    def checkRecursion( self, parseElementList ):
-        subRecCheckList = parseElementList[:] + [ self ]
-        for e in self.exprs:
-            e.checkRecursion( subRecCheckList )
-
-
-class Each(ParseExpression):
-    """
-    Requires all given C{ParseExpression}s to be found, but in any order.
-    Expressions may be separated by whitespace.
-    May be constructed using the C{'&'} operator.
-
-    Example::
-        color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN")
-        shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON")
-        integer = Word(nums)
-        shape_attr = "shape:" + shape_type("shape")
-        posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn")
-        color_attr = "color:" + color("color")
-        size_attr = "size:" + integer("size")
-
-        # use Each (using operator '&') to accept attributes in any order 
-        # (shape and posn are required, color and size are optional)
-        shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr)
-
-        shape_spec.runTests('''
-            shape: SQUARE color: BLACK posn: 100, 120
-            shape: CIRCLE size: 50 color: BLUE posn: 50,80
-            color:GREEN size:20 shape:TRIANGLE posn:20,40
-            '''
-            )
-    prints::
-        shape: SQUARE color: BLACK posn: 100, 120
-        ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']]
-        - color: BLACK
-        - posn: ['100', ',', '120']
-          - x: 100
-          - y: 120
-        - shape: SQUARE
-
-
-        shape: CIRCLE size: 50 color: BLUE posn: 50,80
-        ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']]
-        - color: BLUE
-        - posn: ['50', ',', '80']
-          - x: 50
-          - y: 80
-        - shape: CIRCLE
-        - size: 50
-
-
-        color: GREEN size: 20 shape: TRIANGLE posn: 20,40
-        ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']]
-        - color: GREEN
-        - posn: ['20', ',', '40']
-          - x: 20
-          - y: 40
-        - shape: TRIANGLE
-        - size: 20
-    """
-    def __init__( self, exprs, savelist = True ):
-        super(Each,self).__init__(exprs, savelist)
-        self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
-        self.skipWhitespace = True
-        self.initExprGroups = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.initExprGroups:
-            self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional))
-            opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ]
-            opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)]
-            self.optionals = opt1 + opt2
-            self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ]
-            self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ]
-            self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ]
-            self.required += self.multirequired
-            self.initExprGroups = False
-        tmpLoc = loc
-        tmpReqd = self.required[:]
-        tmpOpt  = self.optionals[:]
-        matchOrder = []
-
-        keepMatching = True
-        while keepMatching:
-            tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired
-            failed = []
-            for e in tmpExprs:
-                try:
-                    tmpLoc = e.tryParse( instring, tmpLoc )
-                except ParseException:
-                    failed.append(e)
-                else:
-                    matchOrder.append(self.opt1map.get(id(e),e))
-                    if e in tmpReqd:
-                        tmpReqd.remove(e)
-                    elif e in tmpOpt:
-                        tmpOpt.remove(e)
-            if len(failed) == len(tmpExprs):
-                keepMatching = False
-
-        if tmpReqd:
-            missing = ", ".join(_ustr(e) for e in tmpReqd)
-            raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing )
-
-        # add any unmatched Optionals, in case they have default values defined
-        matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt]
-
-        resultlist = []
-        for e in matchOrder:
-            loc,results = e._parse(instring,loc,doActions)
-            resultlist.append(results)
-
-        finalResults = sum(resultlist, ParseResults([]))
-        return loc, finalResults
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}"
-
-        return self.strRepr
-
-    def checkRecursion( self, parseElementList ):
-        subRecCheckList = parseElementList[:] + [ self ]
-        for e in self.exprs:
-            e.checkRecursion( subRecCheckList )
-
-
-class ParseElementEnhance(ParserElement):
-    """
-    Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens.
-    """
-    def __init__( self, expr, savelist=False ):
-        super(ParseElementEnhance,self).__init__(savelist)
-        if isinstance( expr, basestring ):
-            if issubclass(ParserElement._literalStringClass, Token):
-                expr = ParserElement._literalStringClass(expr)
-            else:
-                expr = ParserElement._literalStringClass(Literal(expr))
-        self.expr = expr
-        self.strRepr = None
-        if expr is not None:
-            self.mayIndexError = expr.mayIndexError
-            self.mayReturnEmpty = expr.mayReturnEmpty
-            self.setWhitespaceChars( expr.whiteChars )
-            self.skipWhitespace = expr.skipWhitespace
-            self.saveAsList = expr.saveAsList
-            self.callPreparse = expr.callPreparse
-            self.ignoreExprs.extend(expr.ignoreExprs)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.expr is not None:
-            return self.expr._parse( instring, loc, doActions, callPreParse=False )
-        else:
-            raise ParseException("",loc,self.errmsg,self)
-
-    def leaveWhitespace( self ):
-        self.skipWhitespace = False
-        self.expr = self.expr.copy()
-        if self.expr is not None:
-            self.expr.leaveWhitespace()
-        return self
-
-    def ignore( self, other ):
-        if isinstance( other, Suppress ):
-            if other not in self.ignoreExprs:
-                super( ParseElementEnhance, self).ignore( other )
-                if self.expr is not None:
-                    self.expr.ignore( self.ignoreExprs[-1] )
-        else:
-            super( ParseElementEnhance, self).ignore( other )
-            if self.expr is not None:
-                self.expr.ignore( self.ignoreExprs[-1] )
-        return self
-
-    def streamline( self ):
-        super(ParseElementEnhance,self).streamline()
-        if self.expr is not None:
-            self.expr.streamline()
-        return self
-
-    def checkRecursion( self, parseElementList ):
-        if self in parseElementList:
-            raise RecursiveGrammarException( parseElementList+[self] )
-        subRecCheckList = parseElementList[:] + [ self ]
-        if self.expr is not None:
-            self.expr.checkRecursion( subRecCheckList )
-
-    def validate( self, validateTrace=[] ):
-        tmp = validateTrace[:]+[self]
-        if self.expr is not None:
-            self.expr.validate(tmp)
-        self.checkRecursion( [] )
-
-    def __str__( self ):
-        try:
-            return super(ParseElementEnhance,self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None and self.expr is not None:
-            self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) )
-        return self.strRepr
-
-
-class FollowedBy(ParseElementEnhance):
-    """
-    Lookahead matching of the given parse expression.  C{FollowedBy}
-    does I{not} advance the parsing position within the input string, it only
-    verifies that the specified parse expression matches at the current
-    position.  C{FollowedBy} always returns a null token list.
-
-    Example::
-        # use FollowedBy to match a label only if it is followed by a ':'
-        data_word = Word(alphas)
-        label = data_word + FollowedBy(':')
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        
-        OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint()
-    prints::
-        [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']]
-    """
-    def __init__( self, expr ):
-        super(FollowedBy,self).__init__(expr)
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        self.expr.tryParse( instring, loc )
-        return loc, []
-
-
-class NotAny(ParseElementEnhance):
-    """
-    Lookahead to disallow matching with the given parse expression.  C{NotAny}
-    does I{not} advance the parsing position within the input string, it only
-    verifies that the specified parse expression does I{not} match at the current
-    position.  Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny}
-    always returns a null token list.  May be constructed using the '~' operator.
-
-    Example::
-        
-    """
-    def __init__( self, expr ):
-        super(NotAny,self).__init__(expr)
-        #~ self.leaveWhitespace()
-        self.skipWhitespace = False  # do NOT use self.leaveWhitespace(), don't want to propagate to exprs
-        self.mayReturnEmpty = True
-        self.errmsg = "Found unwanted token, "+_ustr(self.expr)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.expr.canParseNext(instring, loc):
-            raise ParseException(instring, loc, self.errmsg, self)
-        return loc, []
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "~{" + _ustr(self.expr) + "}"
-
-        return self.strRepr
-
-class _MultipleMatch(ParseElementEnhance):
-    def __init__( self, expr, stopOn=None):
-        super(_MultipleMatch, self).__init__(expr)
-        self.saveAsList = True
-        ender = stopOn
-        if isinstance(ender, basestring):
-            ender = ParserElement._literalStringClass(ender)
-        self.not_ender = ~ender if ender is not None else None
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        self_expr_parse = self.expr._parse
-        self_skip_ignorables = self._skipIgnorables
-        check_ender = self.not_ender is not None
-        if check_ender:
-            try_not_ender = self.not_ender.tryParse
-        
-        # must be at least one (but first see if we are the stopOn sentinel;
-        # if so, fail)
-        if check_ender:
-            try_not_ender(instring, loc)
-        loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False )
-        try:
-            hasIgnoreExprs = (not not self.ignoreExprs)
-            while 1:
-                if check_ender:
-                    try_not_ender(instring, loc)
-                if hasIgnoreExprs:
-                    preloc = self_skip_ignorables( instring, loc )
-                else:
-                    preloc = loc
-                loc, tmptokens = self_expr_parse( instring, preloc, doActions )
-                if tmptokens or tmptokens.haskeys():
-                    tokens += tmptokens
-        except (ParseException,IndexError):
-            pass
-
-        return loc, tokens
-        
-class OneOrMore(_MultipleMatch):
-    """
-    Repetition of one or more of the given expression.
-    
-    Parameters:
-     - expr - expression that must match one or more times
-     - stopOn - (default=C{None}) - expression for a terminating sentinel
-          (only required if the sentinel would ordinarily match the repetition 
-          expression)          
-
-    Example::
-        data_word = Word(alphas)
-        label = data_word + FollowedBy(':')
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
-
-        text = "shape: SQUARE posn: upper left color: BLACK"
-        OneOrMore(attr_expr).parseString(text).pprint()  # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']]
-
-        # use stopOn attribute for OneOrMore to avoid reading label string as part of the data
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']]
-        
-        # could also be written as
-        (attr_expr * (1,)).parseString(text).pprint()
-    """
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + _ustr(self.expr) + "}..."
-
-        return self.strRepr
-
-class ZeroOrMore(_MultipleMatch):
-    """
-    Optional repetition of zero or more of the given expression.
-    
-    Parameters:
-     - expr - expression that must match zero or more times
-     - stopOn - (default=C{None}) - expression for a terminating sentinel
-          (only required if the sentinel would ordinarily match the repetition 
-          expression)          
-
-    Example: similar to L{OneOrMore}
-    """
-    def __init__( self, expr, stopOn=None):
-        super(ZeroOrMore,self).__init__(expr, stopOn=stopOn)
-        self.mayReturnEmpty = True
-        
-    def parseImpl( self, instring, loc, doActions=True ):
-        try:
-            return super(ZeroOrMore, self).parseImpl(instring, loc, doActions)
-        except (ParseException,IndexError):
-            return loc, []
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "[" + _ustr(self.expr) + "]..."
-
-        return self.strRepr
-
-class _NullToken(object):
-    def __bool__(self):
-        return False
-    __nonzero__ = __bool__
-    def __str__(self):
-        return ""
-
-_optionalNotMatched = _NullToken()
-class Optional(ParseElementEnhance):
-    """
-    Optional matching of the given expression.
-
-    Parameters:
-     - expr - expression that must match zero or more times
-     - default (optional) - value to be returned if the optional expression is not found.
-
-    Example::
-        # US postal code can be a 5-digit zip, plus optional 4-digit qualifier
-        zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4)))
-        zip.runTests('''
-            # traditional ZIP code
-            12345
-            
-            # ZIP+4 form
-            12101-0001
-            
-            # invalid ZIP
-            98765-
-            ''')
-    prints::
-        # traditional ZIP code
-        12345
-        ['12345']
-
-        # ZIP+4 form
-        12101-0001
-        ['12101-0001']
-
-        # invalid ZIP
-        98765-
-             ^
-        FAIL: Expected end of text (at char 5), (line:1, col:6)
-    """
-    def __init__( self, expr, default=_optionalNotMatched ):
-        super(Optional,self).__init__( expr, savelist=False )
-        self.saveAsList = self.expr.saveAsList
-        self.defaultValue = default
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        try:
-            loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False )
-        except (ParseException,IndexError):
-            if self.defaultValue is not _optionalNotMatched:
-                if self.expr.resultsName:
-                    tokens = ParseResults([ self.defaultValue ])
-                    tokens[self.expr.resultsName] = self.defaultValue
-                else:
-                    tokens = [ self.defaultValue ]
-            else:
-                tokens = []
-        return loc, tokens
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "[" + _ustr(self.expr) + "]"
-
-        return self.strRepr
-
-class SkipTo(ParseElementEnhance):
-    """
-    Token for skipping over all undefined text until the matched expression is found.
-
-    Parameters:
-     - expr - target expression marking the end of the data to be skipped
-     - include - (default=C{False}) if True, the target expression is also parsed 
-          (the skipped text and target expression are returned as a 2-element list).
-     - ignore - (default=C{None}) used to define grammars (typically quoted strings and 
-          comments) that might contain false matches to the target expression
-     - failOn - (default=C{None}) define expressions that are not allowed to be 
-          included in the skipped test; if found before the target expression is found, 
-          the SkipTo is not a match
-
-    Example::
-        report = '''
-            Outstanding Issues Report - 1 Jan 2000
-
-               # | Severity | Description                               |  Days Open
-            -----+----------+-------------------------------------------+-----------
-             101 | Critical | Intermittent system crash                 |          6
-              94 | Cosmetic | Spelling error on Login ('log|n')         |         14
-              79 | Minor    | System slow when running too many reports |         47
-            '''
-        integer = Word(nums)
-        SEP = Suppress('|')
-        # use SkipTo to simply match everything up until the next SEP
-        # - ignore quoted strings, so that a '|' character inside a quoted string does not match
-        # - parse action will call token.strip() for each matched token, i.e., the description body
-        string_data = SkipTo(SEP, ignore=quotedString)
-        string_data.setParseAction(tokenMap(str.strip))
-        ticket_expr = (integer("issue_num") + SEP 
-                      + string_data("sev") + SEP 
-                      + string_data("desc") + SEP 
-                      + integer("days_open"))
-        
-        for tkt in ticket_expr.searchString(report):
-            print tkt.dump()
-    prints::
-        ['101', 'Critical', 'Intermittent system crash', '6']
-        - days_open: 6
-        - desc: Intermittent system crash
-        - issue_num: 101
-        - sev: Critical
-        ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14']
-        - days_open: 14
-        - desc: Spelling error on Login ('log|n')
-        - issue_num: 94
-        - sev: Cosmetic
-        ['79', 'Minor', 'System slow when running too many reports', '47']
-        - days_open: 47
-        - desc: System slow when running too many reports
-        - issue_num: 79
-        - sev: Minor
-    """
-    def __init__( self, other, include=False, ignore=None, failOn=None ):
-        super( SkipTo, self ).__init__( other )
-        self.ignoreExpr = ignore
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-        self.includeMatch = include
-        self.asList = False
-        if isinstance(failOn, basestring):
-            self.failOn = ParserElement._literalStringClass(failOn)
-        else:
-            self.failOn = failOn
-        self.errmsg = "No match found for "+_ustr(self.expr)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        startloc = loc
-        instrlen = len(instring)
-        expr = self.expr
-        expr_parse = self.expr._parse
-        self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None
-        self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None
-        
-        tmploc = loc
-        while tmploc <= instrlen:
-            if self_failOn_canParseNext is not None:
-                # break if failOn expression matches
-                if self_failOn_canParseNext(instring, tmploc):
-                    break
-                    
-            if self_ignoreExpr_tryParse is not None:
-                # advance past ignore expressions
-                while 1:
-                    try:
-                        tmploc = self_ignoreExpr_tryParse(instring, tmploc)
-                    except ParseBaseException:
-                        break
-            
-            try:
-                expr_parse(instring, tmploc, doActions=False, callPreParse=False)
-            except (ParseException, IndexError):
-                # no match, advance loc in string
-                tmploc += 1
-            else:
-                # matched skipto expr, done
-                break
-
-        else:
-            # ran off the end of the input string without matching skipto expr, fail
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        # build up return values
-        loc = tmploc
-        skiptext = instring[startloc:loc]
-        skipresult = ParseResults(skiptext)
-        
-        if self.includeMatch:
-            loc, mat = expr_parse(instring,loc,doActions,callPreParse=False)
-            skipresult += mat
-
-        return loc, skipresult
-
-class Forward(ParseElementEnhance):
-    """
-    Forward declaration of an expression to be defined later -
-    used for recursive grammars, such as algebraic infix notation.
-    When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator.
-
-    Note: take care when assigning to C{Forward} not to overlook precedence of operators.
-    Specifically, '|' has a lower precedence than '<<', so that::
-        fwdExpr << a | b | c
-    will actually be evaluated as::
-        (fwdExpr << a) | b | c
-    thereby leaving b and c out as parseable alternatives.  It is recommended that you
-    explicitly group the values inserted into the C{Forward}::
-        fwdExpr << (a | b | c)
-    Converting to use the '<<=' operator instead will avoid this problem.
-
-    See L{ParseResults.pprint} for an example of a recursive parser created using
-    C{Forward}.
-    """
-    def __init__( self, other=None ):
-        super(Forward,self).__init__( other, savelist=False )
-
-    def __lshift__( self, other ):
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass(other)
-        self.expr = other
-        self.strRepr = None
-        self.mayIndexError = self.expr.mayIndexError
-        self.mayReturnEmpty = self.expr.mayReturnEmpty
-        self.setWhitespaceChars( self.expr.whiteChars )
-        self.skipWhitespace = self.expr.skipWhitespace
-        self.saveAsList = self.expr.saveAsList
-        self.ignoreExprs.extend(self.expr.ignoreExprs)
-        return self
-        
-    def __ilshift__(self, other):
-        return self << other
-    
-    def leaveWhitespace( self ):
-        self.skipWhitespace = False
-        return self
-
-    def streamline( self ):
-        if not self.streamlined:
-            self.streamlined = True
-            if self.expr is not None:
-                self.expr.streamline()
-        return self
-
-    def validate( self, validateTrace=[] ):
-        if self not in validateTrace:
-            tmp = validateTrace[:]+[self]
-            if self.expr is not None:
-                self.expr.validate(tmp)
-        self.checkRecursion([])
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-        return self.__class__.__name__ + ": ..."
-
-        # stubbed out for now - creates awful memory and perf issues
-        self._revertClass = self.__class__
-        self.__class__ = _ForwardNoRecurse
-        try:
-            if self.expr is not None:
-                retString = _ustr(self.expr)
-            else:
-                retString = "None"
-        finally:
-            self.__class__ = self._revertClass
-        return self.__class__.__name__ + ": " + retString
-
-    def copy(self):
-        if self.expr is not None:
-            return super(Forward,self).copy()
-        else:
-            ret = Forward()
-            ret <<= self
-            return ret
-
-class _ForwardNoRecurse(Forward):
-    def __str__( self ):
-        return "..."
-
-class TokenConverter(ParseElementEnhance):
-    """
-    Abstract subclass of C{ParseExpression}, for converting parsed results.
-    """
-    def __init__( self, expr, savelist=False ):
-        super(TokenConverter,self).__init__( expr )#, savelist )
-        self.saveAsList = False
-
-class Combine(TokenConverter):
-    """
-    Converter to concatenate all matching tokens to a single string.
-    By default, the matching patterns must also be contiguous in the input string;
-    this can be disabled by specifying C{'adjacent=False'} in the constructor.
-
-    Example::
-        real = Word(nums) + '.' + Word(nums)
-        print(real.parseString('3.1416')) # -> ['3', '.', '1416']
-        # will also erroneously match the following
-        print(real.parseString('3. 1416')) # -> ['3', '.', '1416']
-
-        real = Combine(Word(nums) + '.' + Word(nums))
-        print(real.parseString('3.1416')) # -> ['3.1416']
-        # no match when there are internal spaces
-        print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...)
-    """
-    def __init__( self, expr, joinString="", adjacent=True ):
-        super(Combine,self).__init__( expr )
-        # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself
-        if adjacent:
-            self.leaveWhitespace()
-        self.adjacent = adjacent
-        self.skipWhitespace = True
-        self.joinString = joinString
-        self.callPreparse = True
-
-    def ignore( self, other ):
-        if self.adjacent:
-            ParserElement.ignore(self, other)
-        else:
-            super( Combine, self).ignore( other )
-        return self
-
-    def postParse( self, instring, loc, tokenlist ):
-        retToks = tokenlist.copy()
-        del retToks[:]
-        retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults)
-
-        if self.resultsName and retToks.haskeys():
-            return [ retToks ]
-        else:
-            return retToks
-
-class Group(TokenConverter):
-    """
-    Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions.
-
-    Example::
-        ident = Word(alphas)
-        num = Word(nums)
-        term = ident | num
-        func = ident + Optional(delimitedList(term))
-        print(func.parseString("fn a,b,100"))  # -> ['fn', 'a', 'b', '100']
-
-        func = ident + Group(Optional(delimitedList(term)))
-        print(func.parseString("fn a,b,100"))  # -> ['fn', ['a', 'b', '100']]
-    """
-    def __init__( self, expr ):
-        super(Group,self).__init__( expr )
-        self.saveAsList = True
-
-    def postParse( self, instring, loc, tokenlist ):
-        return [ tokenlist ]
-
-class Dict(TokenConverter):
-    """
-    Converter to return a repetitive expression as a list, but also as a dictionary.
-    Each element can also be referenced using the first token in the expression as its key.
-    Useful for tabular report scraping when the first column can be used as a item key.
-
-    Example::
-        data_word = Word(alphas)
-        label = data_word + FollowedBy(':')
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
-
-        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
-        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        
-        # print attributes as plain groups
-        print(OneOrMore(attr_expr).parseString(text).dump())
-        
-        # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names
-        result = Dict(OneOrMore(Group(attr_expr))).parseString(text)
-        print(result.dump())
-        
-        # access named fields as dict entries, or output as dict
-        print(result['shape'])        
-        print(result.asDict())
-    prints::
-        ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap']
-
-        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
-        - color: light blue
-        - posn: upper left
-        - shape: SQUARE
-        - texture: burlap
-        SQUARE
-        {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'}
-    See more examples at L{ParseResults} of accessing fields by results name.
-    """
-    def __init__( self, expr ):
-        super(Dict,self).__init__( expr )
-        self.saveAsList = True
-
-    def postParse( self, instring, loc, tokenlist ):
-        for i,tok in enumerate(tokenlist):
-            if len(tok) == 0:
-                continue
-            ikey = tok[0]
-            if isinstance(ikey,int):
-                ikey = _ustr(tok[0]).strip()
-            if len(tok)==1:
-                tokenlist[ikey] = _ParseResultsWithOffset("",i)
-            elif len(tok)==2 and not isinstance(tok[1],ParseResults):
-                tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i)
-            else:
-                dictvalue = tok.copy() #ParseResults(i)
-                del dictvalue[0]
-                if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()):
-                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i)
-                else:
-                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i)
-
-        if self.resultsName:
-            return [ tokenlist ]
-        else:
-            return tokenlist
-
-
-class Suppress(TokenConverter):
-    """
-    Converter for ignoring the results of a parsed expression.
-
-    Example::
-        source = "a, b, c,d"
-        wd = Word(alphas)
-        wd_list1 = wd + ZeroOrMore(',' + wd)
-        print(wd_list1.parseString(source))
-
-        # often, delimiters that are useful during parsing are just in the
-        # way afterward - use Suppress to keep them out of the parsed output
-        wd_list2 = wd + ZeroOrMore(Suppress(',') + wd)
-        print(wd_list2.parseString(source))
-    prints::
-        ['a', ',', 'b', ',', 'c', ',', 'd']
-        ['a', 'b', 'c', 'd']
-    (See also L{delimitedList}.)
-    """
-    def postParse( self, instring, loc, tokenlist ):
-        return []
-
-    def suppress( self ):
-        return self
-
-
-class OnlyOnce(object):
-    """
-    Wrapper for parse actions, to ensure they are only called once.
-    """
-    def __init__(self, methodCall):
-        self.callable = _trim_arity(methodCall)
-        self.called = False
-    def __call__(self,s,l,t):
-        if not self.called:
-            results = self.callable(s,l,t)
-            self.called = True
-            return results
-        raise ParseException(s,l,"")
-    def reset(self):
-        self.called = False
-
-def traceParseAction(f):
-    """
-    Decorator for debugging parse actions. 
-    
-    When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".}
-    When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised.
-
-    Example::
-        wd = Word(alphas)
-
-        @traceParseAction
-        def remove_duplicate_chars(tokens):
-            return ''.join(sorted(set(''.join(tokens))))
-
-        wds = OneOrMore(wd).setParseAction(remove_duplicate_chars)
-        print(wds.parseString("slkdjs sld sldd sdlf sdljf"))
-    prints::
-        >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {}))
-        <3:
-            thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc
-        sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\n" % (thisFunc,line(l,s),l,t) )
-        try:
-            ret = f(*paArgs)
-        except Exception as exc:
-            sys.stderr.write( "< ['aa', 'bb', 'cc']
-        delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE']
-    """
-    dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..."
-    if combine:
-        return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName)
-    else:
-        return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName)
-
-def countedArray( expr, intExpr=None ):
-    """
-    Helper to define a counted list of expressions.
-    This helper defines a pattern of the form::
-        integer expr expr expr...
-    where the leading integer tells how many expr expressions follow.
-    The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed.
-    
-    If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value.
-
-    Example::
-        countedArray(Word(alphas)).parseString('2 ab cd ef')  # -> ['ab', 'cd']
-
-        # in this parser, the leading integer value is given in binary,
-        # '10' indicating that 2 values are in the array
-        binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2))
-        countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef')  # -> ['ab', 'cd']
-    """
-    arrayExpr = Forward()
-    def countFieldParseAction(s,l,t):
-        n = t[0]
-        arrayExpr << (n and Group(And([expr]*n)) or Group(empty))
-        return []
-    if intExpr is None:
-        intExpr = Word(nums).setParseAction(lambda t:int(t[0]))
-    else:
-        intExpr = intExpr.copy()
-    intExpr.setName("arrayLen")
-    intExpr.addParseAction(countFieldParseAction, callDuringTry=True)
-    return ( intExpr + arrayExpr ).setName('(len) ' + _ustr(expr) + '...')
-
-def _flatten(L):
-    ret = []
-    for i in L:
-        if isinstance(i,list):
-            ret.extend(_flatten(i))
-        else:
-            ret.append(i)
-    return ret
-
-def matchPreviousLiteral(expr):
-    """
-    Helper to define an expression that is indirectly defined from
-    the tokens matched in a previous expression, that is, it looks
-    for a 'repeat' of a previous expression.  For example::
-        first = Word(nums)
-        second = matchPreviousLiteral(first)
-        matchExpr = first + ":" + second
-    will match C{"1:1"}, but not C{"1:2"}.  Because this matches a
-    previous literal, will also match the leading C{"1:1"} in C{"1:10"}.
-    If this is not desired, use C{matchPreviousExpr}.
-    Do I{not} use with packrat parsing enabled.
-    """
-    rep = Forward()
-    def copyTokenToRepeater(s,l,t):
-        if t:
-            if len(t) == 1:
-                rep << t[0]
-            else:
-                # flatten t tokens
-                tflat = _flatten(t.asList())
-                rep << And(Literal(tt) for tt in tflat)
-        else:
-            rep << Empty()
-    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
-    rep.setName('(prev) ' + _ustr(expr))
-    return rep
-
-def matchPreviousExpr(expr):
-    """
-    Helper to define an expression that is indirectly defined from
-    the tokens matched in a previous expression, that is, it looks
-    for a 'repeat' of a previous expression.  For example::
-        first = Word(nums)
-        second = matchPreviousExpr(first)
-        matchExpr = first + ":" + second
-    will match C{"1:1"}, but not C{"1:2"}.  Because this matches by
-    expressions, will I{not} match the leading C{"1:1"} in C{"1:10"};
-    the expressions are evaluated first, and then compared, so
-    C{"1"} is compared with C{"10"}.
-    Do I{not} use with packrat parsing enabled.
-    """
-    rep = Forward()
-    e2 = expr.copy()
-    rep <<= e2
-    def copyTokenToRepeater(s,l,t):
-        matchTokens = _flatten(t.asList())
-        def mustMatchTheseTokens(s,l,t):
-            theseTokens = _flatten(t.asList())
-            if  theseTokens != matchTokens:
-                raise ParseException("",0,"")
-        rep.setParseAction( mustMatchTheseTokens, callDuringTry=True )
-    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
-    rep.setName('(prev) ' + _ustr(expr))
-    return rep
-
-def _escapeRegexRangeChars(s):
-    #~  escape these chars: ^-]
-    for c in r"\^-]":
-        s = s.replace(c,_bslash+c)
-    s = s.replace("\n",r"\n")
-    s = s.replace("\t",r"\t")
-    return _ustr(s)
-
-def oneOf( strs, caseless=False, useRegex=True ):
-    """
-    Helper to quickly define a set of alternative Literals, and makes sure to do
-    longest-first testing when there is a conflict, regardless of the input order,
-    but returns a C{L{MatchFirst}} for best performance.
-
-    Parameters:
-     - strs - a string of space-delimited literals, or a collection of string literals
-     - caseless - (default=C{False}) - treat all literals as caseless
-     - useRegex - (default=C{True}) - as an optimization, will generate a Regex
-          object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or
-          if creating a C{Regex} raises an exception)
-
-    Example::
-        comp_oper = oneOf("< = > <= >= !=")
-        var = Word(alphas)
-        number = Word(nums)
-        term = var | number
-        comparison_expr = term + comp_oper + term
-        print(comparison_expr.searchString("B = 12  AA=23 B<=AA AA>12"))
-    prints::
-        [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']]
-    """
-    if caseless:
-        isequal = ( lambda a,b: a.upper() == b.upper() )
-        masks = ( lambda a,b: b.upper().startswith(a.upper()) )
-        parseElementClass = CaselessLiteral
-    else:
-        isequal = ( lambda a,b: a == b )
-        masks = ( lambda a,b: b.startswith(a) )
-        parseElementClass = Literal
-
-    symbols = []
-    if isinstance(strs,basestring):
-        symbols = strs.split()
-    elif isinstance(strs, Iterable):
-        symbols = list(strs)
-    else:
-        warnings.warn("Invalid argument to oneOf, expected string or iterable",
-                SyntaxWarning, stacklevel=2)
-    if not symbols:
-        return NoMatch()
-
-    i = 0
-    while i < len(symbols)-1:
-        cur = symbols[i]
-        for j,other in enumerate(symbols[i+1:]):
-            if ( isequal(other, cur) ):
-                del symbols[i+j+1]
-                break
-            elif ( masks(cur, other) ):
-                del symbols[i+j+1]
-                symbols.insert(i,other)
-                cur = other
-                break
-        else:
-            i += 1
-
-    if not caseless and useRegex:
-        #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] ))
-        try:
-            if len(symbols)==len("".join(symbols)):
-                return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols))
-            else:
-                return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols))
-        except Exception:
-            warnings.warn("Exception creating Regex for oneOf, building MatchFirst",
-                    SyntaxWarning, stacklevel=2)
-
-
-    # last resort, just use MatchFirst
-    return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols))
-
-def dictOf( key, value ):
-    """
-    Helper to easily and clearly define a dictionary by specifying the respective patterns
-    for the key and value.  Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens
-    in the proper order.  The key pattern can include delimiting markers or punctuation,
-    as long as they are suppressed, thereby leaving the significant key text.  The value
-    pattern can include named results, so that the C{Dict} results can include named token
-    fields.
-
-    Example::
-        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
-        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        print(OneOrMore(attr_expr).parseString(text).dump())
-        
-        attr_label = label
-        attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)
-
-        # similar to Dict, but simpler call format
-        result = dictOf(attr_label, attr_value).parseString(text)
-        print(result.dump())
-        print(result['shape'])
-        print(result.shape)  # object attribute access works too
-        print(result.asDict())
-    prints::
-        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
-        - color: light blue
-        - posn: upper left
-        - shape: SQUARE
-        - texture: burlap
-        SQUARE
-        SQUARE
-        {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'}
-    """
-    return Dict( ZeroOrMore( Group ( key + value ) ) )
-
-def originalTextFor(expr, asString=True):
-    """
-    Helper to return the original, untokenized text for a given expression.  Useful to
-    restore the parsed fields of an HTML start tag into the raw tag text itself, or to
-    revert separate tokens with intervening whitespace back to the original matching
-    input text. By default, returns astring containing the original parsed text.  
-       
-    If the optional C{asString} argument is passed as C{False}, then the return value is a 
-    C{L{ParseResults}} containing any results names that were originally matched, and a 
-    single token containing the original matched text from the input string.  So if 
-    the expression passed to C{L{originalTextFor}} contains expressions with defined
-    results names, you must set C{asString} to C{False} if you want to preserve those
-    results name values.
-
-    Example::
-        src = "this is test  bold text  normal text "
-        for tag in ("b","i"):
-            opener,closer = makeHTMLTags(tag)
-            patt = originalTextFor(opener + SkipTo(closer) + closer)
-            print(patt.searchString(src)[0])
-    prints::
-        [' bold text ']
-        ['text']
-    """
-    locMarker = Empty().setParseAction(lambda s,loc,t: loc)
-    endlocMarker = locMarker.copy()
-    endlocMarker.callPreparse = False
-    matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end")
-    if asString:
-        extractText = lambda s,l,t: s[t._original_start:t._original_end]
-    else:
-        def extractText(s,l,t):
-            t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]]
-    matchExpr.setParseAction(extractText)
-    matchExpr.ignoreExprs = expr.ignoreExprs
-    return matchExpr
-
-def ungroup(expr): 
-    """
-    Helper to undo pyparsing's default grouping of And expressions, even
-    if all but one are non-empty.
-    """
-    return TokenConverter(expr).setParseAction(lambda t:t[0])
-
-def locatedExpr(expr):
-    """
-    Helper to decorate a returned token with its starting and ending locations in the input string.
-    This helper adds the following results names:
-     - locn_start = location where matched expression begins
-     - locn_end = location where matched expression ends
-     - value = the actual parsed results
-
-    Be careful if the input text contains C{} characters, you may want to call
-    C{L{ParserElement.parseWithTabs}}
-
-    Example::
-        wd = Word(alphas)
-        for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"):
-            print(match)
-    prints::
-        [[0, 'ljsdf', 5]]
-        [[8, 'lksdjjf', 15]]
-        [[18, 'lkkjj', 23]]
-    """
-    locator = Empty().setParseAction(lambda s,l,t: l)
-    return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end"))
-
-
-# convenience constants for positional expressions
-empty       = Empty().setName("empty")
-lineStart   = LineStart().setName("lineStart")
-lineEnd     = LineEnd().setName("lineEnd")
-stringStart = StringStart().setName("stringStart")
-stringEnd   = StringEnd().setName("stringEnd")
-
-_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1])
-_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16)))
-_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8)))
-_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | CharsNotIn(r'\]', exact=1)
-_charRange = Group(_singleChar + Suppress("-") + _singleChar)
-_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]"
-
-def srange(s):
-    r"""
-    Helper to easily define string ranges for use in Word construction.  Borrows
-    syntax from regexp '[]' string range definitions::
-        srange("[0-9]")   -> "0123456789"
-        srange("[a-z]")   -> "abcdefghijklmnopqrstuvwxyz"
-        srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_"
-    The input string must be enclosed in []'s, and the returned string is the expanded
-    character set joined into a single string.
-    The values enclosed in the []'s may be:
-     - a single character
-     - an escaped character with a leading backslash (such as C{\-} or C{\]})
-     - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character) 
-         (C{\0x##} is also supported for backwards compatibility) 
-     - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character)
-     - a range of any of the above, separated by a dash (C{'a-z'}, etc.)
-     - any combination of the above (C{'aeiouy'}, C{'a-zA-Z0-9_$'}, etc.)
-    """
-    _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1))
-    try:
-        return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body)
-    except Exception:
-        return ""
-
-def matchOnlyAtCol(n):
-    """
-    Helper method for defining parse actions that require matching at a specific
-    column in the input text.
-    """
-    def verifyCol(strg,locn,toks):
-        if col(locn,strg) != n:
-            raise ParseException(strg,locn,"matched token not at column %d" % n)
-    return verifyCol
-
-def replaceWith(replStr):
-    """
-    Helper method for common parse actions that simply return a literal value.  Especially
-    useful when used with C{L{transformString}()}.
-
-    Example::
-        num = Word(nums).setParseAction(lambda toks: int(toks[0]))
-        na = oneOf("N/A NA").setParseAction(replaceWith(math.nan))
-        term = na | num
-        
-        OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234]
-    """
-    return lambda s,l,t: [replStr]
-
-def removeQuotes(s,l,t):
-    """
-    Helper parse action for removing quotation marks from parsed quoted strings.
-
-    Example::
-        # by default, quotation marks are included in parsed results
-        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"]
-
-        # use removeQuotes to strip quotation marks from parsed results
-        quotedString.setParseAction(removeQuotes)
-        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"]
-    """
-    return t[0][1:-1]
-
-def tokenMap(func, *args):
-    """
-    Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional 
-    args are passed, they are forwarded to the given function as additional arguments after
-    the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the
-    parsed data to an integer using base 16.
-
-    Example (compare the last to example in L{ParserElement.transformString}::
-        hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16))
-        hex_ints.runTests('''
-            00 11 22 aa FF 0a 0d 1a
-            ''')
-        
-        upperword = Word(alphas).setParseAction(tokenMap(str.upper))
-        OneOrMore(upperword).runTests('''
-            my kingdom for a horse
-            ''')
-
-        wd = Word(alphas).setParseAction(tokenMap(str.title))
-        OneOrMore(wd).setParseAction(' '.join).runTests('''
-            now is the winter of our discontent made glorious summer by this sun of york
-            ''')
-    prints::
-        00 11 22 aa FF 0a 0d 1a
-        [0, 17, 34, 170, 255, 10, 13, 26]
-
-        my kingdom for a horse
-        ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE']
-
-        now is the winter of our discontent made glorious summer by this sun of york
-        ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York']
-    """
-    def pa(s,l,t):
-        return [func(tokn, *args) for tokn in t]
-
-    try:
-        func_name = getattr(func, '__name__', 
-                            getattr(func, '__class__').__name__)
-    except Exception:
-        func_name = str(func)
-    pa.__name__ = func_name
-
-    return pa
-
-upcaseTokens = tokenMap(lambda t: _ustr(t).upper())
-"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}"""
-
-downcaseTokens = tokenMap(lambda t: _ustr(t).lower())
-"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}"""
-    
-def _makeTags(tagStr, xml):
-    """Internal helper to construct opening and closing tag expressions, given a tag name"""
-    if isinstance(tagStr,basestring):
-        resname = tagStr
-        tagStr = Keyword(tagStr, caseless=not xml)
-    else:
-        resname = tagStr.name
-
-    tagAttrName = Word(alphas,alphanums+"_-:")
-    if (xml):
-        tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes )
-        openTag = Suppress("<") + tagStr("tag") + \
-                Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \
-                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
-    else:
-        printablesLessRAbrack = "".join(c for c in printables if c not in ">")
-        tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack)
-        openTag = Suppress("<") + tagStr("tag") + \
-                Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \
-                Optional( Suppress("=") + tagAttrValue ) ))) + \
-                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
-    closeTag = Combine(_L("")
-
-    openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname)
-    closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % resname)
-    openTag.tag = resname
-    closeTag.tag = resname
-    return openTag, closeTag
-
-def makeHTMLTags(tagStr):
-    """
-    Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches
-    tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values.
-
-    Example::
-        text = 'More info at the pyparsing wiki page'
-        # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple
-        a,a_end = makeHTMLTags("A")
-        link_expr = a + SkipTo(a_end)("link_text") + a_end
-        
-        for link in link_expr.searchString(text):
-            # attributes in the  tag (like "href" shown here) are also accessible as named results
-            print(link.link_text, '->', link.href)
-    prints::
-        pyparsing -> http://pyparsing.wikispaces.com
-    """
-    return _makeTags( tagStr, False )
-
-def makeXMLTags(tagStr):
-    """
-    Helper to construct opening and closing tag expressions for XML, given a tag name. Matches
-    tags only in the given upper/lower case.
-
-    Example: similar to L{makeHTMLTags}
-    """
-    return _makeTags( tagStr, True )
-
-def withAttribute(*args,**attrDict):
-    """
-    Helper to create a validating parse action to be used with start tags created
-    with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag
-    with a required attribute value, to avoid false matches on common tags such as
-    C{} or C{
}. - - Call C{withAttribute} with a series of attribute names and values. Specify the list - of filter attributes names and values as: - - keyword arguments, as in C{(align="right")}, or - - as an explicit dict with C{**} operator, when an attribute name is also a Python - reserved word, as in C{**{"class":"Customer", "align":"right"}} - - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) - For attribute names with a namespace prefix, you must use the second form. Attribute - names are matched insensitive to upper/lower case. - - If just testing for C{class} (with or without a namespace), use C{L{withClass}}. - - To verify that the attribute exists, but without specifying a value, pass - C{withAttribute.ANY_VALUE} as the value. - - Example:: - html = ''' -
- Some text -
1 4 0 1 0
-
1,3 2,3 1,1
-
this has no type
-
- - ''' - div,div_end = makeHTMLTags("div") - - # only match div tag having a type attribute with value "grid" - div_grid = div().setParseAction(withAttribute(type="grid")) - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.searchString(html): - print(grid_header.body) - - # construct a match with any div tag having a type attribute, regardless of the value - div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.searchString(html): - print(div_header.body) - prints:: - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - if args: - attrs = args[:] - else: - attrs = attrDict.items() - attrs = [(k,v) for k,v in attrs] - def pa(s,l,tokens): - for attrName,attrValue in attrs: - if attrName not in tokens: - raise ParseException(s,l,"no matching attribute " + attrName) - if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: - raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % - (attrName, tokens[attrName], attrValue)) - return pa -withAttribute.ANY_VALUE = object() - -def withClass(classname, namespace=''): - """ - Simplified version of C{L{withAttribute}} when matching on a div class - made - difficult because C{class} is a reserved word in Python. - - Example:: - html = ''' -
- Some text -
1 4 0 1 0
-
1,3 2,3 1,1
-
this <div> has no class
-
- - ''' - div,div_end = makeHTMLTags("div") - div_grid = div().setParseAction(withClass("grid")) - - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.searchString(html): - print(grid_header.body) - - div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.searchString(html): - print(div_header.body) - prints:: - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - classattr = "%s:class" % namespace if namespace else "class" - return withAttribute(**{classattr : classname}) - -opAssoc = _Constants() -opAssoc.LEFT = object() -opAssoc.RIGHT = object() - -def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): - """ - Helper method for constructing grammars of expressions made up of - operators working in a precedence hierarchy. Operators may be unary or - binary, left- or right-associative. Parse actions can also be attached - to operator expressions. The generated parser will also recognize the use - of parentheses to override operator precedences (see example below). - - Note: if you define a deep operator list, you may see performance issues - when using infixNotation. See L{ParserElement.enablePackrat} for a - mechanism to potentially improve your parser performance. - - Parameters: - - baseExpr - expression representing the most basic element for the nested - - opList - list of tuples, one for each operator precedence level in the - expression grammar; each tuple is of the form - (opExpr, numTerms, rightLeftAssoc, parseAction), where: - - opExpr is the pyparsing expression for the operator; - may also be a string, which will be converted to a Literal; - if numTerms is 3, opExpr is a tuple of two expressions, for the - two operators separating the 3 terms - - numTerms is the number of terms for this operator (must - be 1, 2, or 3) - - rightLeftAssoc is the indicator whether the operator is - right or left associative, using the pyparsing-defined - constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. - - parseAction is the parse action to be associated with - expressions matching this operator expression (the - parse action tuple member may be omitted); if the parse action - is passed a tuple or list of functions, this is equivalent to - calling C{setParseAction(*fn)} (L{ParserElement.setParseAction}) - - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) - - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) - - Example:: - # simple example of four-function arithmetic with ints and variable names - integer = pyparsing_common.signed_integer - varname = pyparsing_common.identifier - - arith_expr = infixNotation(integer | varname, - [ - ('-', 1, opAssoc.RIGHT), - (oneOf('* /'), 2, opAssoc.LEFT), - (oneOf('+ -'), 2, opAssoc.LEFT), - ]) - - arith_expr.runTests(''' - 5+3*6 - (5+3)*6 - -2--11 - ''', fullDump=False) - prints:: - 5+3*6 - [[5, '+', [3, '*', 6]]] - - (5+3)*6 - [[[5, '+', 3], '*', 6]] - - -2--11 - [[['-', 2], '-', ['-', 11]]] - """ - ret = Forward() - lastExpr = baseExpr | ( lpar + ret + rpar ) - for i,operDef in enumerate(opList): - opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] - termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr - if arity == 3: - if opExpr is None or len(opExpr) != 2: - raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") - opExpr1, opExpr2 = opExpr - thisExpr = Forward().setName(termName) - if rightLeftAssoc == opAssoc.LEFT: - if arity == 1: - matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) - elif arity == 2: - if opExpr is not None: - matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) - else: - matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) - elif arity == 3: - matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ - Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) - else: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - elif rightLeftAssoc == opAssoc.RIGHT: - if arity == 1: - # try to avoid LR with this extra test - if not isinstance(opExpr, Optional): - opExpr = Optional(opExpr) - matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) - elif arity == 2: - if opExpr is not None: - matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) - else: - matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) - elif arity == 3: - matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ - Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) - else: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - else: - raise ValueError("operator must indicate right or left associativity") - if pa: - if isinstance(pa, (tuple, list)): - matchExpr.setParseAction(*pa) - else: - matchExpr.setParseAction(pa) - thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) - lastExpr = thisExpr - ret <<= lastExpr - return ret - -operatorPrecedence = infixNotation -"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" - -dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") -sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") -quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| - Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") -unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") - -def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): - """ - Helper method for defining nested lists enclosed in opening and closing - delimiters ("(" and ")" are the default). - - Parameters: - - opener - opening character for a nested list (default=C{"("}); can also be a pyparsing expression - - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression - - content - expression for items within the nested lists (default=C{None}) - - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{quotedString}) - - If an expression is not provided for the content argument, the nested - expression will capture all whitespace-delimited content between delimiters - as a list of separate values. - - Use the C{ignoreExpr} argument to define expressions that may contain - opening or closing characters that should not be treated as opening - or closing characters for nesting, such as quotedString or a comment - expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. - The default is L{quotedString}, but if no expressions are to be ignored, - then pass C{None} for this argument. - - Example:: - data_type = oneOf("void int short long char float double") - decl_data_type = Combine(data_type + Optional(Word('*'))) - ident = Word(alphas+'_', alphanums+'_') - number = pyparsing_common.number - arg = Group(decl_data_type + ident) - LPAR,RPAR = map(Suppress, "()") - - code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) - - c_function = (decl_data_type("type") - + ident("name") - + LPAR + Optional(delimitedList(arg), [])("args") + RPAR - + code_body("body")) - c_function.ignore(cStyleComment) - - source_code = ''' - int is_odd(int x) { - return (x%2); - } - - int dec_to_hex(char hchar) { - if (hchar >= '0' && hchar <= '9') { - return (ord(hchar)-ord('0')); - } else { - return (10+ord(hchar)-ord('A')); - } - } - ''' - for func in c_function.searchString(source_code): - print("%(name)s (%(type)s) args: %(args)s" % func) - - prints:: - is_odd (int) args: [['int', 'x']] - dec_to_hex (int) args: [['char', 'hchar']] - """ - if opener == closer: - raise ValueError("opening and closing strings cannot be the same") - if content is None: - if isinstance(opener,basestring) and isinstance(closer,basestring): - if len(opener) == 1 and len(closer)==1: - if ignoreExpr is not None: - content = (Combine(OneOrMore(~ignoreExpr + - CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS - ).setParseAction(lambda t:t[0].strip())) - else: - if ignoreExpr is not None: - content = (Combine(OneOrMore(~ignoreExpr + - ~Literal(opener) + ~Literal(closer) + - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - raise ValueError("opening and closing arguments must be strings if no content expression is given") - ret = Forward() - if ignoreExpr is not None: - ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) - else: - ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) - ret.setName('nested %s%s expression' % (opener,closer)) - return ret - -def indentedBlock(blockStatementExpr, indentStack, indent=True): - """ - Helper method for defining space-delimited indentation blocks, such as - those used to define block statements in Python source code. - - Parameters: - - blockStatementExpr - expression defining syntax of statement that - is repeated within the indented block - - indentStack - list created by caller to manage indentation stack - (multiple statementWithIndentedBlock expressions within a single grammar - should share a common indentStack) - - indent - boolean indicating whether block must be indented beyond the - the current level; set to False for block of left-most statements - (default=C{True}) - - A valid block must contain at least one C{blockStatement}. - - Example:: - data = ''' - def A(z): - A1 - B = 100 - G = A2 - A2 - A3 - B - def BB(a,b,c): - BB1 - def BBA(): - bba1 - bba2 - bba3 - C - D - def spam(x,y): - def eggs(z): - pass - ''' - - - indentStack = [1] - stmt = Forward() - - identifier = Word(alphas, alphanums) - funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") - func_body = indentedBlock(stmt, indentStack) - funcDef = Group( funcDecl + func_body ) - - rvalue = Forward() - funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") - rvalue << (funcCall | identifier | Word(nums)) - assignment = Group(identifier + "=" + rvalue) - stmt << ( funcDef | assignment | identifier ) - - module_body = OneOrMore(stmt) - - parseTree = module_body.parseString(data) - parseTree.pprint() - prints:: - [['def', - 'A', - ['(', 'z', ')'], - ':', - [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], - 'B', - ['def', - 'BB', - ['(', 'a', 'b', 'c', ')'], - ':', - [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], - 'C', - 'D', - ['def', - 'spam', - ['(', 'x', 'y', ')'], - ':', - [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] - """ - def checkPeerIndent(s,l,t): - if l >= len(s): return - curCol = col(l,s) - if curCol != indentStack[-1]: - if curCol > indentStack[-1]: - raise ParseFatalException(s,l,"illegal nesting") - raise ParseException(s,l,"not a peer entry") - - def checkSubIndent(s,l,t): - curCol = col(l,s) - if curCol > indentStack[-1]: - indentStack.append( curCol ) - else: - raise ParseException(s,l,"not a subentry") - - def checkUnindent(s,l,t): - if l >= len(s): return - curCol = col(l,s) - if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): - raise ParseException(s,l,"not an unindent") - indentStack.pop() - - NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) - INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') - PEER = Empty().setParseAction(checkPeerIndent).setName('') - UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') - if indent: - smExpr = Group( Optional(NL) + - #~ FollowedBy(blockStatementExpr) + - INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) - else: - smExpr = Group( Optional(NL) + - (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) - blockStatementExpr.ignore(_bslash + LineEnd()) - return smExpr.setName('indented block') - -alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") -punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") - -anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) -_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) -commonHTMLEntity = Regex('&(?P' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") -def replaceHTMLEntity(t): - """Helper parser action to replace common HTML entities with their special characters""" - return _htmlEntityMap.get(t.entity) - -# it's easy to get these comment structures wrong - they're very common, so may as well make them available -cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") -"Comment of the form C{/* ... */}" - -htmlComment = Regex(r"").setName("HTML comment") -"Comment of the form C{}" - -restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") -dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") -"Comment of the form C{// ... (to end of line)}" - -cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") -"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" - -javaStyleComment = cppStyleComment -"Same as C{L{cppStyleComment}}" - -pythonStyleComment = Regex(r"#.*").setName("Python style comment") -"Comment of the form C{# ... (to end of line)}" - -_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + - Optional( Word(" \t") + - ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") -commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") -"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. - This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" - -# some other useful expressions - using lower-case class name since we are really using this as a namespace -class pyparsing_common: - """ - Here are some common low-level expressions that may be useful in jump-starting parser development: - - numeric forms (L{integers}, L{reals}, L{scientific notation}) - - common L{programming identifiers} - - network addresses (L{MAC}, L{IPv4}, L{IPv6}) - - ISO8601 L{dates} and L{datetime} - - L{UUID} - - L{comma-separated list} - Parse actions: - - C{L{convertToInteger}} - - C{L{convertToFloat}} - - C{L{convertToDate}} - - C{L{convertToDatetime}} - - C{L{stripHTMLTags}} - - C{L{upcaseTokens}} - - C{L{downcaseTokens}} - - Example:: - pyparsing_common.number.runTests(''' - # any int or real number, returned as the appropriate type - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.fnumber.runTests(''' - # any int or real number, returned as float - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.hex_integer.runTests(''' - # hex numbers - 100 - FF - ''') - - pyparsing_common.fraction.runTests(''' - # fractions - 1/2 - -3/4 - ''') - - pyparsing_common.mixed_integer.runTests(''' - # mixed fractions - 1 - 1/2 - -3/4 - 1-3/4 - ''') - - import uuid - pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) - pyparsing_common.uuid.runTests(''' - # uuid - 12345678-1234-5678-1234-567812345678 - ''') - prints:: - # any int or real number, returned as the appropriate type - 100 - [100] - - -100 - [-100] - - +100 - [100] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # any int or real number, returned as float - 100 - [100.0] - - -100 - [-100.0] - - +100 - [100.0] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # hex numbers - 100 - [256] - - FF - [255] - - # fractions - 1/2 - [0.5] - - -3/4 - [-0.75] - - # mixed fractions - 1 - [1] - - 1/2 - [0.5] - - -3/4 - [-0.75] - - 1-3/4 - [1.75] - - # uuid - 12345678-1234-5678-1234-567812345678 - [UUID('12345678-1234-5678-1234-567812345678')] - """ - - convertToInteger = tokenMap(int) - """ - Parse action for converting parsed integers to Python int - """ - - convertToFloat = tokenMap(float) - """ - Parse action for converting parsed numbers to Python float - """ - - integer = Word(nums).setName("integer").setParseAction(convertToInteger) - """expression that parses an unsigned integer, returns an int""" - - hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) - """expression that parses a hexadecimal integer, returns an int""" - - signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) - """expression that parses an integer with optional leading sign, returns an int""" - - fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") - """fractional expression of an integer divided by an integer, returns a float""" - fraction.addParseAction(lambda t: t[0]/t[-1]) - - mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") - """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" - mixed_integer.addParseAction(sum) - - real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) - """expression that parses a floating point number and returns a float""" - - sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) - """expression that parses a floating point number with optional scientific notation and returns a float""" - - # streamlining this expression makes the docs nicer-looking - number = (sci_real | real | signed_integer).streamline() - """any numeric expression, returns the corresponding Python type""" - - fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) - """any int or real number, returned as float""" - - identifier = Word(alphas+'_', alphanums+'_').setName("identifier") - """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" - - ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") - "IPv4 address (C{0.0.0.0 - 255.255.255.255})" - - _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") - _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") - _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") - _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) - _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") - ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") - "IPv6 address (long, short, or mixed form)" - - mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") - "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" - - @staticmethod - def convertToDate(fmt="%Y-%m-%d"): - """ - Helper to create a parse action for converting parsed date string to Python datetime.date - - Params - - - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) - - Example:: - date_expr = pyparsing_common.iso8601_date.copy() - date_expr.setParseAction(pyparsing_common.convertToDate()) - print(date_expr.parseString("1999-12-31")) - prints:: - [datetime.date(1999, 12, 31)] - """ - def cvt_fn(s,l,t): - try: - return datetime.strptime(t[0], fmt).date() - except ValueError as ve: - raise ParseException(s, l, str(ve)) - return cvt_fn - - @staticmethod - def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): - """ - Helper to create a parse action for converting parsed datetime string to Python datetime.datetime - - Params - - - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) - - Example:: - dt_expr = pyparsing_common.iso8601_datetime.copy() - dt_expr.setParseAction(pyparsing_common.convertToDatetime()) - print(dt_expr.parseString("1999-12-31T23:59:59.999")) - prints:: - [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] - """ - def cvt_fn(s,l,t): - try: - return datetime.strptime(t[0], fmt) - except ValueError as ve: - raise ParseException(s, l, str(ve)) - return cvt_fn - - iso8601_date = Regex(r'(?P\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?').setName("ISO8601 date") - "ISO8601 date (C{yyyy-mm-dd})" - - iso8601_datetime = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") - "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" - - uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") - "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" - - _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() - @staticmethod - def stripHTMLTags(s, l, tokens): - """ - Parse action to remove HTML tags from web page HTML source - - Example:: - # strip HTML links from normal text - text = 'More info at the
pyparsing wiki page' - td,td_end = makeHTMLTags("TD") - table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end - - print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' - """ - return pyparsing_common._html_stripper.transformString(tokens[0]) - - _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') - + Optional( White(" \t") ) ) ).streamline().setName("commaItem") - comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") - """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" - - upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) - """Parse action to convert tokens to upper case.""" - - downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) - """Parse action to convert tokens to lower case.""" - - -if __name__ == "__main__": - - selectToken = CaselessLiteral("select") - fromToken = CaselessLiteral("from") - - ident = Word(alphas, alphanums + "_$") - - columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) - columnNameList = Group(delimitedList(columnName)).setName("columns") - columnSpec = ('*' | columnNameList) - - tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) - tableNameList = Group(delimitedList(tableName)).setName("tables") - - simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") - - # demo runTests method, including embedded comments in test string - simpleSQL.runTests(""" - # '*' as column list and dotted table name - select * from SYS.XYZZY - - # caseless match on "SELECT", and casts back to "select" - SELECT * from XYZZY, ABC - - # list of column names, and mixed case SELECT keyword - Select AA,BB,CC from Sys.dual - - # multiple tables - Select A, B, C from Sys.dual, Table2 - - # invalid SELECT keyword - should fail - Xelect A, B, C from Sys.dual - - # incomplete command - should fail - Select - - # invalid column name - should fail - Select ^^^ frox Sys.dual - - """) - - pyparsing_common.number.runTests(""" - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - """) - - # any int or real number, returned as float - pyparsing_common.fnumber.runTests(""" - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - """) - - pyparsing_common.hex_integer.runTests(""" - 100 - FF - """) - - import uuid - pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) - pyparsing_common.uuid.runTests(""" - 12345678-1234-5678-1234-567812345678 - """) diff --git a/setuptools/_vendor/vendored.txt b/setuptools/_vendor/vendored.txt deleted file mode 100644 index d980474163..0000000000 --- a/setuptools/_vendor/vendored.txt +++ /dev/null @@ -1,4 +0,0 @@ -packaging==20.4 -pyparsing==2.2.1 -ordered-set==3.1.1 -more_itertools==8.8.0 diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py index 6a61543342..7cbf94d7ee 100644 --- a/setuptools/command/build_py.py +++ b/setuptools/command/build_py.py @@ -8,7 +8,7 @@ import distutils.errors import itertools import stat -from setuptools.extern.more_itertools import unique_everseen +from more_itertools import unique_everseen def make_writable(target): diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 18b81340a7..2ed235321d 100644 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -28,7 +28,7 @@ import setuptools.unicode_utils as unicode_utils from setuptools.glob import glob -from setuptools.extern import packaging +import packaging from setuptools import SetuptoolsDeprecationWarning diff --git a/setuptools/command/test.py b/setuptools/command/test.py index 4a389e4d07..aaebe5f234 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -19,7 +19,7 @@ EntryPoint, ) from setuptools import Command -from setuptools.extern.more_itertools import unique_everseen +from more_itertools import unique_everseen class ScanningLoader(TestLoader): diff --git a/setuptools/config.py b/setuptools/config.py index e3e44c25b8..cedba01614 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -13,8 +13,8 @@ import contextlib from distutils.errors import DistutilsOptionError, DistutilsFileError -from setuptools.extern.packaging.version import LegacyVersion, parse -from setuptools.extern.packaging.specifiers import SpecifierSet +from packaging.version import LegacyVersion, parse +from packaging.specifiers import SpecifierSet class StaticModule: diff --git a/setuptools/dist.py b/setuptools/dist.py index eec0b27eb0..bcb1326a1c 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -27,9 +27,10 @@ from distutils.util import rfc822_escape from distutils.version import StrictVersion -from setuptools.extern import packaging -from setuptools.extern import ordered_set -from setuptools.extern.more_itertools import unique_everseen +import packaging.specifiers +import packaging.version +import ordered_set +from more_itertools import unique_everseen from . import SetuptoolsDeprecationWarning @@ -43,9 +44,6 @@ if TYPE_CHECKING: from email.message import Message -__import__('setuptools.extern.packaging.specifiers') -__import__('setuptools.extern.packaging.version') - def _get_unpatched(cls): warnings.warn("Do not call this function", DistDeprecationWarning) diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py deleted file mode 100644 index baca1afabe..0000000000 --- a/setuptools/extern/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -import importlib.util -import sys - - -class VendorImporter: - """ - A PEP 302 meta path importer for finding optionally-vendored - or otherwise naturally-installed packages from root_name. - """ - - def __init__(self, root_name, vendored_names=(), vendor_pkg=None): - self.root_name = root_name - self.vendored_names = set(vendored_names) - self.vendor_pkg = vendor_pkg or root_name.replace('extern', '_vendor') - - @property - def search_path(self): - """ - Search first the vendor package then as a natural package. - """ - yield self.vendor_pkg + '.' - yield '' - - def _module_matches_namespace(self, fullname): - """Figure out if the target module is vendored.""" - root, base, target = fullname.partition(self.root_name + '.') - return not root and any(map(target.startswith, self.vendored_names)) - - def load_module(self, fullname): - """ - Iterate over the search path to locate and load fullname. - """ - root, base, target = fullname.partition(self.root_name + '.') - for prefix in self.search_path: - try: - extant = prefix + target - __import__(extant) - mod = sys.modules[extant] - sys.modules[fullname] = mod - return mod - except ImportError: - pass - else: - raise ImportError( - "The '{target}' package is required; " - "normally this is bundled with this package so if you get " - "this warning, consult the packager of your " - "distribution.".format(**locals()) - ) - - def create_module(self, spec): - return self.load_module(spec.name) - - def exec_module(self, module): - pass - - def find_spec(self, fullname, path=None, target=None): - """Return a module spec for vendored names.""" - return ( - importlib.util.spec_from_loader(fullname, self) - if self._module_matches_namespace(fullname) else None - ) - - def install(self): - """ - Install this importer into sys.meta_path if not already present. - """ - if self not in sys.meta_path: - sys.meta_path.append(self) - - -names = 'packaging', 'pyparsing', 'ordered_set', 'more_itertools', -VendorImporter(__name__, names, 'setuptools._vendor').install() diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 281ea1c2af..754f8eb4aa 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -29,8 +29,8 @@ import itertools import subprocess import distutils.errors -from setuptools.extern.packaging.version import LegacyVersion -from setuptools.extern.more_itertools import unique_everseen +from packaging.version import LegacyVersion +from more_itertools import unique_everseen from .monkey import get_unpatched diff --git a/setuptools/package_index.py b/setuptools/package_index.py index d818f44ade..b9fed358f0 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -27,7 +27,7 @@ from distutils.errors import DistutilsError from fnmatch import translate from setuptools.wheel import Wheel -from setuptools.extern.more_itertools import unique_everseen +from more_itertools import unique_everseen EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$') diff --git a/setuptools/tests/test_extern.py b/setuptools/tests/test_extern.py index 0d6b164f53..da01b25b98 100644 --- a/setuptools/tests/test_extern.py +++ b/setuptools/tests/test_extern.py @@ -2,7 +2,7 @@ import pickle from setuptools import Distribution -from setuptools.extern import ordered_set +import ordered_set def test_reimport_extern(): diff --git a/setuptools/tests/test_wheel.py b/setuptools/tests/test_wheel.py index 7345b135fd..2dba311455 100644 --- a/setuptools/tests/test_wheel.py +++ b/setuptools/tests/test_wheel.py @@ -18,8 +18,8 @@ from jaraco import path from pkg_resources import Distribution, PathMetadata, PY_MAJOR -from setuptools.extern.packaging.utils import canonicalize_name -from setuptools.extern.packaging.tags import parse_tag +from packaging.utils import canonicalize_name +from packaging.tags import parse_tag from setuptools.wheel import Wheel from .contexts import tempdir diff --git a/setuptools/wheel.py b/setuptools/wheel.py index 0be811af2c..f31cd231e0 100644 --- a/setuptools/wheel.py +++ b/setuptools/wheel.py @@ -12,8 +12,8 @@ import pkg_resources import setuptools from pkg_resources import parse_version -from setuptools.extern.packaging.tags import sys_tags -from setuptools.extern.packaging.utils import canonicalize_name +from packaging.tags import sys_tags +from packaging.utils import canonicalize_name from setuptools.command.egg_info import write_requirements From 1bcfd8107af3b42ae1f423770c1a1381901a4e77 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 5 Sep 2021 14:25:48 -0400 Subject: [PATCH 2/9] Expand the vendored dependencies. --- _setuptools_vendored/__init__.py | 2 +- .../appdirs-1.4.3.dist-info/DESCRIPTION.rst | 227 + .../appdirs-1.4.3.dist-info/METADATA | 254 + .../appdirs-1.4.3.dist-info/RECORD | 7 + .../appdirs-1.4.3.dist-info/WHEEL | 6 + .../appdirs-1.4.3.dist-info/metadata.json | 1 + .../appdirs-1.4.3.dist-info/top_level.txt | 1 + _setuptools_vendored/appdirs.py | 608 ++ .../more_itertools-8.8.0.dist-info/LICENSE | 19 + .../more_itertools-8.8.0.dist-info/METADATA | 462 ++ .../more_itertools-8.8.0.dist-info/RECORD | 12 + .../more_itertools-8.8.0.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../more_itertools/__init__.py | 4 + .../more_itertools/__init__.pyi | 2 + _setuptools_vendored/more_itertools/more.py | 3825 +++++++++++ _setuptools_vendored/more_itertools/more.pyi | 480 ++ _setuptools_vendored/more_itertools/py.typed | 0 .../more_itertools/recipes.py | 620 ++ .../more_itertools/recipes.pyi | 103 + .../ordered_set-3.1.1.dist-info/METADATA | 156 + .../ordered_set-3.1.1.dist-info/MIT-LICENSE | 19 + .../ordered_set-3.1.1.dist-info/RECORD | 6 + .../ordered_set-3.1.1.dist-info/WHEEL | 6 + .../ordered_set-3.1.1.dist-info/top_level.txt | 1 + _setuptools_vendored/ordered_set.py | 488 ++ .../packaging-20.4.dist-info/LICENSE | 3 + .../packaging-20.4.dist-info/LICENSE.APACHE | 177 + .../packaging-20.4.dist-info/LICENSE.BSD | 23 + .../packaging-20.4.dist-info/METADATA | 373 ++ .../packaging-20.4.dist-info/RECORD | 19 + .../packaging-20.4.dist-info/WHEEL | 6 + .../packaging-20.4.dist-info/top_level.txt | 1 + _setuptools_vendored/packaging/__about__.py | 27 + _setuptools_vendored/packaging/__init__.py | 26 + _setuptools_vendored/packaging/_compat.py | 38 + _setuptools_vendored/packaging/_structures.py | 86 + _setuptools_vendored/packaging/_typing.py | 48 + _setuptools_vendored/packaging/markers.py | 328 + _setuptools_vendored/packaging/py.typed | 0 .../packaging/requirements.py | 145 + _setuptools_vendored/packaging/specifiers.py | 863 +++ _setuptools_vendored/packaging/tags.py | 751 +++ _setuptools_vendored/packaging/utils.py | 65 + _setuptools_vendored/packaging/version.py | 535 ++ .../pyparsing-2.2.1.dist-info/DESCRIPTION.rst | 3 + .../pyparsing-2.2.1.dist-info/LICENSE.txt | 18 + .../pyparsing-2.2.1.dist-info/METADATA | 30 + .../pyparsing-2.2.1.dist-info/RECORD | 8 + .../pyparsing-2.2.1.dist-info/WHEEL | 6 + .../pyparsing-2.2.1.dist-info/metadata.json | 1 + .../pyparsing-2.2.1.dist-info/top_level.txt | 1 + _setuptools_vendored/pyparsing.py | 5742 +++++++++++++++++ .../six-1.16.0.dist-info/LICENSE | 18 + .../six-1.16.0.dist-info/METADATA | 49 + .../six-1.16.0.dist-info/RECORD | 6 + .../six-1.16.0.dist-info/WHEEL | 6 + .../six-1.16.0.dist-info/top_level.txt | 1 + _setuptools_vendored/six.py | 998 +++ 59 files changed, 17715 insertions(+), 1 deletion(-) create mode 100644 _setuptools_vendored/appdirs-1.4.3.dist-info/DESCRIPTION.rst create mode 100644 _setuptools_vendored/appdirs-1.4.3.dist-info/METADATA create mode 100644 _setuptools_vendored/appdirs-1.4.3.dist-info/RECORD create mode 100644 _setuptools_vendored/appdirs-1.4.3.dist-info/WHEEL create mode 100644 _setuptools_vendored/appdirs-1.4.3.dist-info/metadata.json create mode 100644 _setuptools_vendored/appdirs-1.4.3.dist-info/top_level.txt create mode 100644 _setuptools_vendored/appdirs.py create mode 100644 _setuptools_vendored/more_itertools-8.8.0.dist-info/LICENSE create mode 100644 _setuptools_vendored/more_itertools-8.8.0.dist-info/METADATA create mode 100644 _setuptools_vendored/more_itertools-8.8.0.dist-info/RECORD create mode 100644 _setuptools_vendored/more_itertools-8.8.0.dist-info/WHEEL create mode 100644 _setuptools_vendored/more_itertools-8.8.0.dist-info/top_level.txt create mode 100644 _setuptools_vendored/more_itertools/__init__.py create mode 100644 _setuptools_vendored/more_itertools/__init__.pyi create mode 100644 _setuptools_vendored/more_itertools/more.py create mode 100644 _setuptools_vendored/more_itertools/more.pyi create mode 100644 _setuptools_vendored/more_itertools/py.typed create mode 100644 _setuptools_vendored/more_itertools/recipes.py create mode 100644 _setuptools_vendored/more_itertools/recipes.pyi create mode 100644 _setuptools_vendored/ordered_set-3.1.1.dist-info/METADATA create mode 100644 _setuptools_vendored/ordered_set-3.1.1.dist-info/MIT-LICENSE create mode 100644 _setuptools_vendored/ordered_set-3.1.1.dist-info/RECORD create mode 100644 _setuptools_vendored/ordered_set-3.1.1.dist-info/WHEEL create mode 100644 _setuptools_vendored/ordered_set-3.1.1.dist-info/top_level.txt create mode 100644 _setuptools_vendored/ordered_set.py create mode 100644 _setuptools_vendored/packaging-20.4.dist-info/LICENSE create mode 100644 _setuptools_vendored/packaging-20.4.dist-info/LICENSE.APACHE create mode 100644 _setuptools_vendored/packaging-20.4.dist-info/LICENSE.BSD create mode 100644 _setuptools_vendored/packaging-20.4.dist-info/METADATA create mode 100644 _setuptools_vendored/packaging-20.4.dist-info/RECORD create mode 100644 _setuptools_vendored/packaging-20.4.dist-info/WHEEL create mode 100644 _setuptools_vendored/packaging-20.4.dist-info/top_level.txt create mode 100644 _setuptools_vendored/packaging/__about__.py create mode 100644 _setuptools_vendored/packaging/__init__.py create mode 100644 _setuptools_vendored/packaging/_compat.py create mode 100644 _setuptools_vendored/packaging/_structures.py create mode 100644 _setuptools_vendored/packaging/_typing.py create mode 100644 _setuptools_vendored/packaging/markers.py create mode 100644 _setuptools_vendored/packaging/py.typed create mode 100644 _setuptools_vendored/packaging/requirements.py create mode 100644 _setuptools_vendored/packaging/specifiers.py create mode 100644 _setuptools_vendored/packaging/tags.py create mode 100644 _setuptools_vendored/packaging/utils.py create mode 100644 _setuptools_vendored/packaging/version.py create mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/DESCRIPTION.rst create mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/LICENSE.txt create mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/METADATA create mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/RECORD create mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/WHEEL create mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/metadata.json create mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/top_level.txt create mode 100644 _setuptools_vendored/pyparsing.py create mode 100644 _setuptools_vendored/six-1.16.0.dist-info/LICENSE create mode 100644 _setuptools_vendored/six-1.16.0.dist-info/METADATA create mode 100644 _setuptools_vendored/six-1.16.0.dist-info/RECORD create mode 100644 _setuptools_vendored/six-1.16.0.dist-info/WHEEL create mode 100644 _setuptools_vendored/six-1.16.0.dist-info/top_level.txt create mode 100644 _setuptools_vendored/six.py diff --git a/_setuptools_vendored/__init__.py b/_setuptools_vendored/__init__.py index 8299cd32be..9951c59bd0 100644 --- a/_setuptools_vendored/__init__.py +++ b/_setuptools_vendored/__init__.py @@ -4,4 +4,4 @@ import sys import pathlib -sys.path.extend(map(str, pathlib.Path(__file__).parent.glob('*.whl'))) +sys.path.append(str(pathlib.Path(__file__).parent)) diff --git a/_setuptools_vendored/appdirs-1.4.3.dist-info/DESCRIPTION.rst b/_setuptools_vendored/appdirs-1.4.3.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000000..c605ec2634 --- /dev/null +++ b/_setuptools_vendored/appdirs-1.4.3.dist-info/DESCRIPTION.rst @@ -0,0 +1,227 @@ + +.. image:: https://secure.travis-ci.org/ActiveState/appdirs.png + :target: http://travis-ci.org/ActiveState/appdirs + +the problem +=========== + +What directory should your app use for storing user data? If running on Mac OS X, you +should use:: + + ~/Library/Application Support/ + +If on Windows (at least English Win XP) that should be:: + + C:\Documents and Settings\\Application Data\Local Settings\\ + +or possibly:: + + C:\Documents and Settings\\Application Data\\ + +for `roaming profiles `_ but that is another story. + +On Linux (and other Unices) the dir, according to the `XDG +spec `_, is:: + + ~/.local/share/ + + +``appdirs`` to the rescue +========================= + +This kind of thing is what the ``appdirs`` module is for. ``appdirs`` will +help you choose an appropriate: + +- user data dir (``user_data_dir``) +- user config dir (``user_config_dir``) +- user cache dir (``user_cache_dir``) +- site data dir (``site_data_dir``) +- site config dir (``site_config_dir``) +- user log dir (``user_log_dir``) + +and also: + +- is a single module so other Python packages can include their own private copy +- is slightly opinionated on the directory names used. Look for "OPINION" in + documentation and code for when an opinion is being applied. + + +some example output +=================== + +On Mac OS X:: + + >>> from appdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + '/Users/trentm/Library/Application Support/SuperApp' + >>> site_data_dir(appname, appauthor) + '/Library/Application Support/SuperApp' + >>> user_cache_dir(appname, appauthor) + '/Users/trentm/Library/Caches/SuperApp' + >>> user_log_dir(appname, appauthor) + '/Users/trentm/Library/Logs/SuperApp' + +On Windows 7:: + + >>> from appdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' + >>> user_data_dir(appname, appauthor, roaming=True) + 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp' + >>> user_cache_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache' + >>> user_log_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs' + +On Linux:: + + >>> from appdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + '/home/trentm/.local/share/SuperApp + >>> site_data_dir(appname, appauthor) + '/usr/local/share/SuperApp' + >>> site_data_dir(appname, appauthor, multipath=True) + '/usr/local/share/SuperApp:/usr/share/SuperApp' + >>> user_cache_dir(appname, appauthor) + '/home/trentm/.cache/SuperApp' + >>> user_log_dir(appname, appauthor) + '/home/trentm/.cache/SuperApp/log' + >>> user_config_dir(appname) + '/home/trentm/.config/SuperApp' + >>> site_config_dir(appname) + '/etc/xdg/SuperApp' + >>> os.environ['XDG_CONFIG_DIRS'] = '/etc:/usr/local/etc' + >>> site_config_dir(appname, multipath=True) + '/etc/SuperApp:/usr/local/etc/SuperApp' + + +``AppDirs`` for convenience +=========================== + +:: + + >>> from appdirs import AppDirs + >>> dirs = AppDirs("SuperApp", "Acme") + >>> dirs.user_data_dir + '/Users/trentm/Library/Application Support/SuperApp' + >>> dirs.site_data_dir + '/Library/Application Support/SuperApp' + >>> dirs.user_cache_dir + '/Users/trentm/Library/Caches/SuperApp' + >>> dirs.user_log_dir + '/Users/trentm/Library/Logs/SuperApp' + + + +Per-version isolation +===================== + +If you have multiple versions of your app in use that you want to be +able to run side-by-side, then you may want version-isolation for these +dirs:: + + >>> from appdirs import AppDirs + >>> dirs = AppDirs("SuperApp", "Acme", version="1.0") + >>> dirs.user_data_dir + '/Users/trentm/Library/Application Support/SuperApp/1.0' + >>> dirs.site_data_dir + '/Library/Application Support/SuperApp/1.0' + >>> dirs.user_cache_dir + '/Users/trentm/Library/Caches/SuperApp/1.0' + >>> dirs.user_log_dir + '/Users/trentm/Library/Logs/SuperApp/1.0' + + + +appdirs Changelog +================= + +appdirs 1.4.3 +------------- +- [PR #76] Python 3.6 invalid escape sequence deprecation fixes +- Fix for Python 3.6 support + +appdirs 1.4.2 +------------- +- [PR #84] Allow installing without setuptools +- [PR #86] Fix string delimiters in setup.py description +- Add Python 3.6 support + +appdirs 1.4.1 +------------- +- [issue #38] Fix _winreg import on Windows Py3 +- [issue #55] Make appname optional + +appdirs 1.4.0 +------------- +- [PR #42] AppAuthor is now optional on Windows +- [issue 41] Support Jython on Windows, Mac, and Unix-like platforms. Windows + support requires `JNA `_. +- [PR #44] Fix incorrect behaviour of the site_config_dir method + +appdirs 1.3.0 +------------- +- [Unix, issue 16] Conform to XDG standard, instead of breaking it for + everybody +- [Unix] Removes gratuitous case mangling of the case, since \*nix-es are + usually case sensitive, so mangling is not wise +- [Unix] Fixes the utterly wrong behaviour in ``site_data_dir``, return result + based on XDG_DATA_DIRS and make room for respecting the standard which + specifies XDG_DATA_DIRS is a multiple-value variable +- [Issue 6] Add ``*_config_dir`` which are distinct on nix-es, according to + XDG specs; on Windows and Mac return the corresponding ``*_data_dir`` + +appdirs 1.2.0 +------------- + +- [Unix] Put ``user_log_dir`` under the *cache* dir on Unix. Seems to be more + typical. +- [issue 9] Make ``unicode`` work on py3k. + +appdirs 1.1.0 +------------- + +- [issue 4] Add ``AppDirs.user_log_dir``. +- [Unix, issue 2, issue 7] appdirs now conforms to `XDG base directory spec + `_. +- [Mac, issue 5] Fix ``site_data_dir()`` on Mac. +- [Mac] Drop use of 'Carbon' module in favour of hardcoded paths; supports + Python3 now. +- [Windows] Append "Cache" to ``user_cache_dir`` on Windows by default. Use + ``opinion=False`` option to disable this. +- Add ``appdirs.AppDirs`` convenience class. Usage: + + >>> dirs = AppDirs("SuperApp", "Acme", version="1.0") + >>> dirs.user_data_dir + '/Users/trentm/Library/Application Support/SuperApp/1.0' + +- [Windows] Cherry-pick Komodo's change to downgrade paths to the Windows short + paths if there are high bit chars. +- [Linux] Change default ``user_cache_dir()`` on Linux to be singular, e.g. + "~/.superapp/cache". +- [Windows] Add ``roaming`` option to ``user_data_dir()`` (for use on Windows only) + and change the default ``user_data_dir`` behaviour to use a *non*-roaming + profile dir (``CSIDL_LOCAL_APPDATA`` instead of ``CSIDL_APPDATA``). Why? Because + a large roaming profile can cause login speed issues. The "only syncs on + logout" behaviour can cause surprises in appdata info. + + +appdirs 1.0.1 (never released) +------------------------------ + +Started this changelog 27 July 2010. Before that this module originated in the +`Komodo `_ product as ``applib.py`` and then +as `applib/location.py +`_ (used by +`PyPM `_ in `ActivePython +`_). This is basically a fork of +applib.py 1.0.1 and applib/location.py 1.0.1. + + + diff --git a/_setuptools_vendored/appdirs-1.4.3.dist-info/METADATA b/_setuptools_vendored/appdirs-1.4.3.dist-info/METADATA new file mode 100644 index 0000000000..69ddf93459 --- /dev/null +++ b/_setuptools_vendored/appdirs-1.4.3.dist-info/METADATA @@ -0,0 +1,254 @@ +Metadata-Version: 2.0 +Name: appdirs +Version: 1.4.3 +Summary: A small Python module for determining appropriate platform-specific dirs, e.g. a "user data dir". +Home-page: http://github.com/ActiveState/appdirs +Author: Trent Mick; Sridhar Ratnakumar; Jeff Rouse +Author-email: trentm@gmail.com; github@srid.name; jr@its.to +License: MIT +Keywords: application directory log cache user +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Topic :: Software Development :: Libraries :: Python Modules + + +.. image:: https://secure.travis-ci.org/ActiveState/appdirs.png + :target: http://travis-ci.org/ActiveState/appdirs + +the problem +=========== + +What directory should your app use for storing user data? If running on Mac OS X, you +should use:: + + ~/Library/Application Support/ + +If on Windows (at least English Win XP) that should be:: + + C:\Documents and Settings\\Application Data\Local Settings\\ + +or possibly:: + + C:\Documents and Settings\\Application Data\\ + +for `roaming profiles `_ but that is another story. + +On Linux (and other Unices) the dir, according to the `XDG +spec `_, is:: + + ~/.local/share/ + + +``appdirs`` to the rescue +========================= + +This kind of thing is what the ``appdirs`` module is for. ``appdirs`` will +help you choose an appropriate: + +- user data dir (``user_data_dir``) +- user config dir (``user_config_dir``) +- user cache dir (``user_cache_dir``) +- site data dir (``site_data_dir``) +- site config dir (``site_config_dir``) +- user log dir (``user_log_dir``) + +and also: + +- is a single module so other Python packages can include their own private copy +- is slightly opinionated on the directory names used. Look for "OPINION" in + documentation and code for when an opinion is being applied. + + +some example output +=================== + +On Mac OS X:: + + >>> from appdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + '/Users/trentm/Library/Application Support/SuperApp' + >>> site_data_dir(appname, appauthor) + '/Library/Application Support/SuperApp' + >>> user_cache_dir(appname, appauthor) + '/Users/trentm/Library/Caches/SuperApp' + >>> user_log_dir(appname, appauthor) + '/Users/trentm/Library/Logs/SuperApp' + +On Windows 7:: + + >>> from appdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' + >>> user_data_dir(appname, appauthor, roaming=True) + 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp' + >>> user_cache_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache' + >>> user_log_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs' + +On Linux:: + + >>> from appdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + '/home/trentm/.local/share/SuperApp + >>> site_data_dir(appname, appauthor) + '/usr/local/share/SuperApp' + >>> site_data_dir(appname, appauthor, multipath=True) + '/usr/local/share/SuperApp:/usr/share/SuperApp' + >>> user_cache_dir(appname, appauthor) + '/home/trentm/.cache/SuperApp' + >>> user_log_dir(appname, appauthor) + '/home/trentm/.cache/SuperApp/log' + >>> user_config_dir(appname) + '/home/trentm/.config/SuperApp' + >>> site_config_dir(appname) + '/etc/xdg/SuperApp' + >>> os.environ['XDG_CONFIG_DIRS'] = '/etc:/usr/local/etc' + >>> site_config_dir(appname, multipath=True) + '/etc/SuperApp:/usr/local/etc/SuperApp' + + +``AppDirs`` for convenience +=========================== + +:: + + >>> from appdirs import AppDirs + >>> dirs = AppDirs("SuperApp", "Acme") + >>> dirs.user_data_dir + '/Users/trentm/Library/Application Support/SuperApp' + >>> dirs.site_data_dir + '/Library/Application Support/SuperApp' + >>> dirs.user_cache_dir + '/Users/trentm/Library/Caches/SuperApp' + >>> dirs.user_log_dir + '/Users/trentm/Library/Logs/SuperApp' + + + +Per-version isolation +===================== + +If you have multiple versions of your app in use that you want to be +able to run side-by-side, then you may want version-isolation for these +dirs:: + + >>> from appdirs import AppDirs + >>> dirs = AppDirs("SuperApp", "Acme", version="1.0") + >>> dirs.user_data_dir + '/Users/trentm/Library/Application Support/SuperApp/1.0' + >>> dirs.site_data_dir + '/Library/Application Support/SuperApp/1.0' + >>> dirs.user_cache_dir + '/Users/trentm/Library/Caches/SuperApp/1.0' + >>> dirs.user_log_dir + '/Users/trentm/Library/Logs/SuperApp/1.0' + + + +appdirs Changelog +================= + +appdirs 1.4.3 +------------- +- [PR #76] Python 3.6 invalid escape sequence deprecation fixes +- Fix for Python 3.6 support + +appdirs 1.4.2 +------------- +- [PR #84] Allow installing without setuptools +- [PR #86] Fix string delimiters in setup.py description +- Add Python 3.6 support + +appdirs 1.4.1 +------------- +- [issue #38] Fix _winreg import on Windows Py3 +- [issue #55] Make appname optional + +appdirs 1.4.0 +------------- +- [PR #42] AppAuthor is now optional on Windows +- [issue 41] Support Jython on Windows, Mac, and Unix-like platforms. Windows + support requires `JNA `_. +- [PR #44] Fix incorrect behaviour of the site_config_dir method + +appdirs 1.3.0 +------------- +- [Unix, issue 16] Conform to XDG standard, instead of breaking it for + everybody +- [Unix] Removes gratuitous case mangling of the case, since \*nix-es are + usually case sensitive, so mangling is not wise +- [Unix] Fixes the utterly wrong behaviour in ``site_data_dir``, return result + based on XDG_DATA_DIRS and make room for respecting the standard which + specifies XDG_DATA_DIRS is a multiple-value variable +- [Issue 6] Add ``*_config_dir`` which are distinct on nix-es, according to + XDG specs; on Windows and Mac return the corresponding ``*_data_dir`` + +appdirs 1.2.0 +------------- + +- [Unix] Put ``user_log_dir`` under the *cache* dir on Unix. Seems to be more + typical. +- [issue 9] Make ``unicode`` work on py3k. + +appdirs 1.1.0 +------------- + +- [issue 4] Add ``AppDirs.user_log_dir``. +- [Unix, issue 2, issue 7] appdirs now conforms to `XDG base directory spec + `_. +- [Mac, issue 5] Fix ``site_data_dir()`` on Mac. +- [Mac] Drop use of 'Carbon' module in favour of hardcoded paths; supports + Python3 now. +- [Windows] Append "Cache" to ``user_cache_dir`` on Windows by default. Use + ``opinion=False`` option to disable this. +- Add ``appdirs.AppDirs`` convenience class. Usage: + + >>> dirs = AppDirs("SuperApp", "Acme", version="1.0") + >>> dirs.user_data_dir + '/Users/trentm/Library/Application Support/SuperApp/1.0' + +- [Windows] Cherry-pick Komodo's change to downgrade paths to the Windows short + paths if there are high bit chars. +- [Linux] Change default ``user_cache_dir()`` on Linux to be singular, e.g. + "~/.superapp/cache". +- [Windows] Add ``roaming`` option to ``user_data_dir()`` (for use on Windows only) + and change the default ``user_data_dir`` behaviour to use a *non*-roaming + profile dir (``CSIDL_LOCAL_APPDATA`` instead of ``CSIDL_APPDATA``). Why? Because + a large roaming profile can cause login speed issues. The "only syncs on + logout" behaviour can cause surprises in appdata info. + + +appdirs 1.0.1 (never released) +------------------------------ + +Started this changelog 27 July 2010. Before that this module originated in the +`Komodo `_ product as ``applib.py`` and then +as `applib/location.py +`_ (used by +`PyPM `_ in `ActivePython +`_). This is basically a fork of +applib.py 1.0.1 and applib/location.py 1.0.1. + + + diff --git a/_setuptools_vendored/appdirs-1.4.3.dist-info/RECORD b/_setuptools_vendored/appdirs-1.4.3.dist-info/RECORD new file mode 100644 index 0000000000..117115b941 --- /dev/null +++ b/_setuptools_vendored/appdirs-1.4.3.dist-info/RECORD @@ -0,0 +1,7 @@ +appdirs.py,sha256=MievUEuv3l_mQISH5SF0shDk_BNhHHzYiAPrT3ITN4I,24701 +appdirs-1.4.3.dist-info/DESCRIPTION.rst,sha256=77Fe8OIOLSjDSNdLiL5xywMKO-AGE42rdXkqKo4Ee-k,7531 +appdirs-1.4.3.dist-info/METADATA,sha256=3IFw6jTfImdOqsCb2GYvVR157tL7KEzfRAszn382csk,8773 +appdirs-1.4.3.dist-info/RECORD,, +appdirs-1.4.3.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 +appdirs-1.4.3.dist-info/metadata.json,sha256=fL_Q-GuFJu3PJxMrwU7SdsI8RGqjIfi2AvouCSF5DSA,1359 +appdirs-1.4.3.dist-info/top_level.txt,sha256=nKncE8CUqZERJ6VuQWL4_bkunSPDNfn7KZqb4Tr5YEM,8 diff --git a/_setuptools_vendored/appdirs-1.4.3.dist-info/WHEEL b/_setuptools_vendored/appdirs-1.4.3.dist-info/WHEEL new file mode 100644 index 0000000000..8b6dd1b5a8 --- /dev/null +++ b/_setuptools_vendored/appdirs-1.4.3.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.29.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/_setuptools_vendored/appdirs-1.4.3.dist-info/metadata.json b/_setuptools_vendored/appdirs-1.4.3.dist-info/metadata.json new file mode 100644 index 0000000000..da1e5f3a7e --- /dev/null +++ b/_setuptools_vendored/appdirs-1.4.3.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Software Development :: Libraries :: Python Modules"], "extensions": {"python.details": {"contacts": [{"email": "trentm@gmail.com; github@srid.name; jr@its.to", "name": "Trent Mick; Sridhar Ratnakumar; Jeff Rouse", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/ActiveState/appdirs"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["application", "directory", "log", "cache", "user"], "license": "MIT", "metadata_version": "2.0", "name": "appdirs", "summary": "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\".", "test_requires": [{"requires": []}], "version": "1.4.3"} \ No newline at end of file diff --git a/_setuptools_vendored/appdirs-1.4.3.dist-info/top_level.txt b/_setuptools_vendored/appdirs-1.4.3.dist-info/top_level.txt new file mode 100644 index 0000000000..d64bc321a1 --- /dev/null +++ b/_setuptools_vendored/appdirs-1.4.3.dist-info/top_level.txt @@ -0,0 +1 @@ +appdirs diff --git a/_setuptools_vendored/appdirs.py b/_setuptools_vendored/appdirs.py new file mode 100644 index 0000000000..ae67001af8 --- /dev/null +++ b/_setuptools_vendored/appdirs.py @@ -0,0 +1,608 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2010 ActiveState Software Inc. +# Copyright (c) 2013 Eddy Petrișor + +"""Utilities for determining application-specific dirs. + +See for details and usage. +""" +# Dev Notes: +# - MSDN on where to store app data files: +# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 +# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html +# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + +__version_info__ = (1, 4, 3) +__version__ = '.'.join(map(str, __version_info__)) + + +import sys +import os + +PY3 = sys.version_info[0] == 3 + +if PY3: + unicode = str + +if sys.platform.startswith('java'): + import platform + os_name = platform.java_ver()[3][0] + if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. + system = 'win32' + elif os_name.startswith('Mac'): # "Mac OS X", etc. + system = 'darwin' + else: # "Linux", "SunOS", "FreeBSD", etc. + # Setting this to "linux2" is not ideal, but only Windows or Mac + # are actually checked for and the rest of the module expects + # *sys.platform* style strings. + system = 'linux2' +else: + system = sys.platform + + + +def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user data directories are: + Mac OS X: ~/Library/Application Support/ + Unix: ~/.local/share/ # or in $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\\Application Data\\ + Win XP (roaming): C:\Documents and Settings\\Local Settings\Application Data\\ + Win 7 (not roaming): C:\Users\\AppData\Local\\ + Win 7 (roaming): C:\Users\\AppData\Roaming\\ + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/". + """ + if system == "win32": + if appauthor is None: + appauthor = appname + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.normpath(_get_win_folder(const)) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('~/Library/Application Support/') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of data dirs should be + returned. By default, the first item from XDG_DATA_DIRS is + returned, or '/usr/local/share/', + if XDG_DATA_DIRS is not set + + Typical site data directories are: + Mac OS X: /Library/Application Support/ + Unix: /usr/local/share/ or /usr/share/ + Win XP: C:\Documents and Settings\All Users\Application Data\\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + Win 7: C:\ProgramData\\ # Hidden, but writeable on Win 7. + + For Unix, this is using the $XDG_DATA_DIRS[0] default. + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('/Library/Application Support') + if appname: + path = os.path.join(path, appname) + else: + # XDG default for $XDG_DATA_DIRS + # only first, if multipath is False + path = os.getenv('XDG_DATA_DIRS', + os.pathsep.join(['/usr/local/share', '/usr/share'])) + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + if appname and version: + path = os.path.join(path, version) + return path + + +def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific config dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user config directories are: + Mac OS X: same as user_data_dir + Unix: ~/.config/ # or in $XDG_CONFIG_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by default "~/.config/". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of config dirs should be + returned. By default, the first item from XDG_CONFIG_DIRS is + returned, or '/etc/xdg/', if XDG_CONFIG_DIRS is not set + + Typical site config directories are: + Mac OS X: same as site_data_dir + Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ for each value in + $XDG_CONFIG_DIRS + Win *: same as site_data_dir + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + + For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system in ["win32", "darwin"]: + path = site_data_dir(appname, appauthor) + if appname and version: + path = os.path.join(path, version) + else: + # XDG default for $XDG_CONFIG_DIRS + # only first, if multipath is False + path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + +def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Cache" to the base app data dir for Windows. See + discussion below. + + Typical user cache directories are: + Mac OS X: ~/Library/Caches/ + Unix: ~/.cache/ (XDG default) + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache + Vista: C:\Users\\AppData\Local\\\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go in + the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming + app data dir (the default returned by `user_data_dir` above). Apps typically + put cache data somewhere *under* the given dir here. Some examples: + ...\Mozilla\Firefox\Profiles\\Cache + ...\Acme\SuperApp\Cache\1.0 + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + This can be disabled with the `opinion=False` option. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + if opinion: + path = os.path.join(path, "Cache") + elif system == 'darwin': + path = os.path.expanduser('~/Library/Caches') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific state dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user state directories are: + Mac OS X: same as user_data_dir + Unix: ~/.local/state/ # or in $XDG_STATE_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow this Debian proposal + to extend the XDG spec and support $XDG_STATE_HOME. + + That means, by default "~/.local/state/". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific log dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Logs" to the base app data dir for Windows, and "log" to the + base cache dir for Unix. See discussion below. + + Typical user log directories are: + Mac OS X: ~/Library/Logs/ + Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs + Vista: C:\Users\\AppData\Local\\\Logs + + On Windows the only suggestion in the MSDN docs is that local settings + go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in + examples of what some windows apps use for a logs dir.) + + OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` + value for Windows and appends "log" to the user cache dir for Unix. + This can be disabled with the `opinion=False` option. + """ + if system == "darwin": + path = os.path.join( + os.path.expanduser('~/Library/Logs'), + appname) + elif system == "win32": + path = user_data_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "Logs") + else: + path = user_cache_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "log") + if appname and version: + path = os.path.join(path, version) + return path + + +class AppDirs(object): + """Convenience wrapper for getting application dirs.""" + def __init__(self, appname=None, appauthor=None, version=None, + roaming=False, multipath=False): + self.appname = appname + self.appauthor = appauthor + self.version = version + self.roaming = roaming + self.multipath = multipath + + @property + def user_data_dir(self): + return user_data_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_data_dir(self): + return site_data_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_config_dir(self): + return user_config_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_config_dir(self): + return site_config_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_cache_dir(self): + return user_cache_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_state_dir(self): + return user_state_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_log_dir(self): + return user_log_dir(self.appname, self.appauthor, + version=self.version) + + +#---- internal support stuff + +def _get_win_folder_from_registry(csidl_name): + """This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + if PY3: + import winreg as _winreg + else: + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey( + _winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" + ) + dir, type = _winreg.QueryValueEx(key, shell_folder_name) + return dir + + +def _get_win_folder_with_pywin32(csidl_name): + from win32com.shell import shellcon, shell + dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) + # Try to make this a unicode path because SHGetFolderPath does + # not return unicode strings when there is unicode data in the + # path. + try: + dir = unicode(dir) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + try: + import win32api + dir = win32api.GetShortPathName(dir) + except ImportError: + pass + except UnicodeError: + pass + return dir + + +def _get_win_folder_with_ctypes(csidl_name): + import ctypes + + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + +def _get_win_folder_with_jna(csidl_name): + import array + from com.sun import jna + from com.sun.jna.platform import win32 + + buf_size = win32.WinDef.MAX_PATH * 2 + buf = array.zeros('c', buf_size) + shell = win32.Shell32.INSTANCE + shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf = array.zeros('c', buf_size) + kernel = win32.Kernel32.INSTANCE + if kernel.GetShortPathName(dir, buf, buf_size): + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + return dir + +if system == "win32": + try: + import win32com.shell + _get_win_folder = _get_win_folder_with_pywin32 + except ImportError: + try: + from ctypes import windll + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + try: + import com.sun.jna + _get_win_folder = _get_win_folder_with_jna + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +#---- self test code + +if __name__ == "__main__": + appname = "MyApp" + appauthor = "MyCompany" + + props = ("user_data_dir", + "user_config_dir", + "user_cache_dir", + "user_state_dir", + "user_log_dir", + "site_data_dir", + "site_config_dir") + + print("-- app dirs %s --" % __version__) + + print("-- app dirs (with optional 'version')") + dirs = AppDirs(appname, appauthor, version="1.0") + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'version')") + dirs = AppDirs(appname, appauthor) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'appauthor')") + dirs = AppDirs(appname) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (with disabled 'appauthor')") + dirs = AppDirs(appname, appauthor=False) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/_setuptools_vendored/more_itertools-8.8.0.dist-info/LICENSE b/_setuptools_vendored/more_itertools-8.8.0.dist-info/LICENSE new file mode 100644 index 0000000000..0a523bece3 --- /dev/null +++ b/_setuptools_vendored/more_itertools-8.8.0.dist-info/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 Erik Rose + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/_setuptools_vendored/more_itertools-8.8.0.dist-info/METADATA b/_setuptools_vendored/more_itertools-8.8.0.dist-info/METADATA new file mode 100644 index 0000000000..bdaee6553f --- /dev/null +++ b/_setuptools_vendored/more_itertools-8.8.0.dist-info/METADATA @@ -0,0 +1,462 @@ +Metadata-Version: 2.1 +Name: more-itertools +Version: 8.8.0 +Summary: More routines for operating on iterables, beyond itertools +Home-page: https://github.com/more-itertools/more-itertools +Author: Erik Rose +Author-email: erikrose@grinchcentral.com +License: MIT +Keywords: itertools,iterator,iteration,filter,peek,peekable,collate,chunk,chunked +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.5 +Description-Content-Type: text/x-rst + +============== +More Itertools +============== + +.. image:: https://readthedocs.org/projects/more-itertools/badge/?version=latest + :target: https://more-itertools.readthedocs.io/en/stable/ + +Python's ``itertools`` library is a gem - you can compose elegant solutions +for a variety of problems with the functions it provides. In ``more-itertools`` +we collect additional building blocks, recipes, and routines for working with +Python iterables. + ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Grouping | `chunked `_, | +| | `ichunked `_, | +| | `sliced `_, | +| | `distribute `_, | +| | `divide `_, | +| | `split_at `_, | +| | `split_before `_, | +| | `split_after `_, | +| | `split_into `_, | +| | `split_when `_, | +| | `bucket `_, | +| | `unzip `_, | +| | `grouper `_, | +| | `partition `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Lookahead and lookback | `spy `_, | +| | `peekable `_, | +| | `seekable `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Windowing | `windowed `_, | +| | `substrings `_, | +| | `substrings_indexes `_, | +| | `stagger `_, | +| | `windowed_complete `_, | +| | `pairwise `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Augmenting | `count_cycle `_, | +| | `intersperse `_, | +| | `padded `_, | +| | `mark_ends `_, | +| | `repeat_last `_, | +| | `adjacent `_, | +| | `groupby_transform `_, | +| | `padnone `_, | +| | `ncycles `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Combining | `collapse `_, | +| | `sort_together `_, | +| | `interleave `_, | +| | `interleave_longest `_, | +| | `zip_offset `_, | +| | `zip_equal `_, | +| | `dotproduct `_, | +| | `convolve `_, | +| | `flatten `_, | +| | `roundrobin `_, | +| | `prepend `_, | +| | `value_chain `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Summarizing | `ilen `_, | +| | `unique_to_each `_, | +| | `sample `_, | +| | `consecutive_groups `_, | +| | `run_length `_, | +| | `map_reduce `_, | +| | `exactly_n `_, | +| | `is_sorted `_, | +| | `all_equal `_, | +| | `all_unique `_, | +| | `first_true `_, | +| | `quantify `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Selecting | `islice_extended `_, | +| | `first `_, | +| | `last `_, | +| | `one `_, | +| | `only `_, | +| | `strip `_, | +| | `lstrip `_, | +| | `rstrip `_, | +| | `filter_except `_ | +| | `map_except `_ | +| | `nth_or_last `_, | +| | `nth `_, | +| | `take `_, | +| | `tail `_, | +| | `unique_everseen `_, | +| | `unique_justseen `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Combinatorics | `distinct_permutations `_, | +| | `distinct_combinations `_, | +| | `circular_shifts `_, | +| | `partitions `_, | +| | `set_partitions `_, | +| | `product_index `_, | +| | `combination_index `_, | +| | `permutation_index `_, | +| | `powerset `_, | +| | `random_product `_, | +| | `random_permutation `_, | +| | `random_combination `_, | +| | `random_combination_with_replacement `_, | +| | `nth_product `_ | +| | `nth_permutation `_ | +| | `nth_combination `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Wrapping | `always_iterable `_, | +| | `always_reversible `_, | +| | `countable `_, | +| | `consumer `_, | +| | `with_iter `_, | +| | `iter_except `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Others | `locate `_, | +| | `rlocate `_, | +| | `replace `_, | +| | `numeric_range `_, | +| | `side_effect `_, | +| | `iterate `_, | +| | `difference `_, | +| | `make_decorator `_, | +| | `SequenceView `_, | +| | `time_limited `_, | +| | `consume `_, | +| | `tabulate `_, | +| | `repeatfunc `_ | ++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + + +Getting started +=============== + +To get started, install the library with `pip `_: + +.. code-block:: shell + + pip install more-itertools + +The recipes from the `itertools docs `_ +are included in the top-level package: + +.. code-block:: python + + >>> from more_itertools import flatten + >>> iterable = [(0, 1), (2, 3)] + >>> list(flatten(iterable)) + [0, 1, 2, 3] + +Several new recipes are available as well: + +.. code-block:: python + + >>> from more_itertools import chunked + >>> iterable = [0, 1, 2, 3, 4, 5, 6, 7, 8] + >>> list(chunked(iterable, 3)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8]] + + >>> from more_itertools import spy + >>> iterable = (x * x for x in range(1, 6)) + >>> head, iterable = spy(iterable, n=3) + >>> list(head) + [1, 4, 9] + >>> list(iterable) + [1, 4, 9, 16, 25] + + + +For the full listing of functions, see the `API documentation `_. + + +Links elsewhere +=============== + +Blog posts about ``more-itertools``: + +* `Yo, I heard you like decorators `__ +* `Tour of Python Itertools `__ (`Alternate `__) + + +Development +=========== + +``more-itertools`` is maintained by `@erikrose `_ +and `@bbayles `_, with help from `many others `_. +If you have a problem or suggestion, please file a bug or pull request in this +repository. Thanks for contributing! + + +Version History +=============== + + + :noindex: + +8.8.0 +----- + +* New functions + * countable (thanks to krzysieq) + +* Changes to existing functions + * split_before was updated to handle empy collections (thanks to TiunovNN) + * unique_everseen got a performance boost (thanks to Numerlor) + * The type hint for value_chain was corrected (thanks to vr2262) + +8.7.0 +----- + +* New functions + * convolve (from the Python itertools docs) + * product_index, combination_index, and permutation_index (thanks to N8Brooks) + * value_chain (thanks to jenstroeger) + +* Changes to existing functions + * distinct_combinations now uses a non-recursive algorithm (thanks to knutdrand) + * pad_none is now the preferred name for padnone, though the latter remains available. + * pairwise will now use the Python standard library implementation on Python 3.10+ + * sort_together now accepts a ``key`` argument (thanks to brianmaissy) + * seekable now has a ``peek`` method, and can indicate whether the iterator it's wrapping is exhausted (thanks to gsakkis) + * time_limited can now indicate whether its iterator has expired (thanks to roysmith) + * The implementation of unique_everseen was improved (thanks to plammens) + +* Other changes: + * Various documentation updates (thanks to cthoyt, Evantm, and cyphase) + +8.6.0 +----- + +* New itertools + * all_unique (thanks to brianmaissy) + * nth_product and nth_permutation (thanks to N8Brooks) + +* Changes to existing itertools + * chunked and sliced now accept a ``strict`` parameter (thanks to shlomif and jtwool) + +* Other changes + * Python 3.5 has reached its end of life and is no longer supported. + * Python 3.9 is officially supported. + * Various documentation fixes (thanks to timgates42) + +8.5.0 +----- + +* New itertools + * windowed_complete (thanks to MarcinKonowalczyk) + +* Changes to existing itertools: + * The is_sorted implementation was improved (thanks to cool-RR) + * The groupby_transform now accepts a ``reducefunc`` parameter. + * The last implementation was improved (thanks to brianmaissy) + +* Other changes + * Various documentation fixes (thanks to craigrosie, samuelstjean, PiCT0) + * The tests for distinct_combinations were improved (thanks to Minabsapi) + * Automated tests now run on GitHub Actions. All commits now check: + * That unit tests pass + * That the examples in docstrings work + * That test coverage remains high (using `coverage`) + * For linting errors (using `flake8`) + * For consistent style (using `black`) + * That the type stubs work (using `mypy`) + * That the docs build correctly (using `sphinx`) + * That packages build correctly (using `twine`) + +8.4.0 +----- + +* New itertools + * mark_ends (thanks to kalekundert) + * is_sorted + +* Changes to existing itertools: + * islice_extended can now be used with real slices (thanks to cool-RR) + * The implementations for filter_except and map_except were improved (thanks to SergBobrovsky) + +* Other changes + * Automated tests now enforce code style (using `black `__) + * The various signatures of islice_extended and numeric_range now appear in the docs (thanks to dsfulf) + * The test configuration for mypy was updated (thanks to blueyed) + + +8.3.0 +----- + +* New itertools + * zip_equal (thanks to frankier and alexmojaki) + +* Changes to existing itertools: + * split_at, split_before, split_after, and split_when all got a ``maxsplit`` paramter (thanks to jferard and ilai-deutel) + * split_at now accepts a ``keep_separator`` parameter (thanks to jferard) + * distinct_permutations can now generate ``r``-length permutations (thanks to SergBobrovsky and ilai-deutel) + * The windowed implementation was improved (thanks to SergBobrovsky) + * The spy implementation was improved (thanks to has2k1) + +* Other changes + * Type stubs are now tested with ``stubtest`` (thanks to ilai-deutel) + * Tests now run with ``python -m unittest`` instead of ``python setup.py test`` (thanks to jdufresne) + +8.2.0 +----- + +* Bug fixes + * The .pyi files for typing were updated. (thanks to blueyed and ilai-deutel) + +* Changes to existing itertools: + * numeric_range now behaves more like the built-in range. (thanks to jferard) + * bucket now allows for enumerating keys. (thanks to alexchandel) + * sliced now should now work for numpy arrays. (thanks to sswingle) + * seekable now has a ``maxlen`` parameter. + +8.1.0 +----- + +* Bug fixes + * partition works with ``pred=None`` again. (thanks to MSeifert04) + +* New itertools + * sample (thanks to tommyod) + * nth_or_last (thanks to d-ryzhikov) + +* Changes to existing itertools: + * The implementation for divide was improved. (thanks to jferard) + +8.0.2 +----- + +* Bug fixes + * The type stub files are now part of the wheel distribution (thanks to keisheiled) + +8.0.1 +----- + +* Bug fixes + * The type stub files now work for functions imported from the + root package (thanks to keisheiled) + +8.0.0 +----- + +* New itertools and other additions + * This library now ships type hints for use with mypy. + (thanks to ilai-deutel for the implementation, and to gabbard and fmagin for assistance) + * split_when (thanks to jferard) + * repeat_last (thanks to d-ryzhikov) + +* Changes to existing itertools: + * The implementation for set_partitions was improved. (thanks to jferard) + * partition was optimized for expensive predicates. (thanks to stevecj) + * unique_everseen and groupby_transform were re-factored. (thanks to SergBobrovsky) + * The implementation for difference was improved. (thanks to Jabbey92) + +* Other changes + * Python 3.4 has reached its end of life and is no longer supported. + * Python 3.8 is officially supported. (thanks to jdufresne) + * The ``collate`` function has been deprecated. + It raises a ``DeprecationWarning`` if used, and will be removed in a future release. + * one and only now provide more informative error messages. (thanks to gabbard) + * Unit tests were moved outside of the main package (thanks to jdufresne) + * Various documentation fixes (thanks to kriomant, gabbard, jdufresne) + + +7.2.0 +----- + +* New itertools + * distinct_combinations + * set_partitions (thanks to kbarrett) + * filter_except + * map_except + +7.1.0 +----- + +* New itertools + * ichunked (thanks davebelais and youtux) + * only (thanks jaraco) + +* Changes to existing itertools: + * numeric_range now supports ranges specified by + ``datetime.datetime`` and ``datetime.timedelta`` objects (thanks to MSeifert04 for tests). + * difference now supports an *initial* keyword argument. + + +* Other changes + * Various documentation fixes (thanks raimon49, pylang) + +7.0.0 +----- + +* New itertools: + * time_limited + * partitions (thanks to rominf and Saluev) + * substrings_indexes (thanks to rominf) + +* Changes to existing itertools: + * collapse now treats ``bytes`` objects the same as ``str`` objects. (thanks to Sweenpet) + +The major version update is due to the change in the default behavior of +collapse. It now treats ``bytes`` objects the same as ``str`` objects. +This aligns its behavior with always_iterable. + +.. code-block:: python + + >>> from more_itertools import collapse + >>> iterable = [[1, 2], b'345', [6]] + >>> print(list(collapse(iterable))) + [1, 2, b'345', 6] + +6.0.0 +----- + +* Major changes: + * Python 2.7 is no longer supported. The 5.0.0 release will be the last + version targeting Python 2.7. + * All future releases will target the active versions of Python 3. + As of 2019, those are Python 3.4 and above. + * The ``six`` library is no longer a dependency. + * The accumulate function is no longer part of this library. You + may import a better version from the standard ``itertools`` module. + +* Changes to existing itertools: + * The order of the parameters in grouper have changed to match + the latest recipe in the itertools documentation. Use of the old order + will be supported in this release, but emit a ``DeprecationWarning``. + The legacy behavior will be dropped in a future release. (thanks to jaraco) + * distinct_permutations was improved (thanks to jferard - see also `permutations with unique values `_ at StackOverflow.) + * An unused parameter was removed from substrings. (thanks to pylang) + +* Other changes: + * The docs for unique_everseen were improved. (thanks to jferard and MSeifert04) + * Several Python 2-isms were removed. (thanks to jaraco, MSeifert04, and hugovk) + + diff --git a/_setuptools_vendored/more_itertools-8.8.0.dist-info/RECORD b/_setuptools_vendored/more_itertools-8.8.0.dist-info/RECORD new file mode 100644 index 0000000000..e21e6bcc21 --- /dev/null +++ b/_setuptools_vendored/more_itertools-8.8.0.dist-info/RECORD @@ -0,0 +1,12 @@ +more_itertools/__init__.py,sha256=C7sXffHTXM3P-iaLPPfqfmDoxOflQMJLcM7ed9p3jak,82 +more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43 +more_itertools/more.py,sha256=DlZa8v6JihVwfQ5zHidOA-xDE0orcQIUyxVnCaUoDKE,117968 +more_itertools/more.pyi,sha256=r32pH2raBC1zih3evK4fyvAXvrUamJqc6dgV7QCRL_M,14977 +more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +more_itertools/recipes.py,sha256=UkNkrsZyqiwgLHANBTmvMhCvaNSvSNYhyOpz_Jc55DY,16256 +more_itertools/recipes.pyi,sha256=9BpeKd5_qalYVSnuHfqPSCfoGgqnQY2Xu9pNwrDlHU8,3551 +more_itertools-8.8.0.dist-info/LICENSE,sha256=CfHIyelBrz5YTVlkHqm4fYPAyw_QB-te85Gn4mQ8GkY,1053 +more_itertools-8.8.0.dist-info/METADATA,sha256=Gke9w7RnfiAvveik_iBBrzd0RjrDhsQ8uRYNBJdo4qQ,40482 +more_itertools-8.8.0.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92 +more_itertools-8.8.0.dist-info/top_level.txt,sha256=fAuqRXu9LPhxdB9ujJowcFOu1rZ8wzSpOW9_jlKis6M,15 +more_itertools-8.8.0.dist-info/RECORD,, diff --git a/_setuptools_vendored/more_itertools-8.8.0.dist-info/WHEEL b/_setuptools_vendored/more_itertools-8.8.0.dist-info/WHEEL new file mode 100644 index 0000000000..385faab052 --- /dev/null +++ b/_setuptools_vendored/more_itertools-8.8.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/_setuptools_vendored/more_itertools-8.8.0.dist-info/top_level.txt b/_setuptools_vendored/more_itertools-8.8.0.dist-info/top_level.txt new file mode 100644 index 0000000000..a5035befb3 --- /dev/null +++ b/_setuptools_vendored/more_itertools-8.8.0.dist-info/top_level.txt @@ -0,0 +1 @@ +more_itertools diff --git a/_setuptools_vendored/more_itertools/__init__.py b/_setuptools_vendored/more_itertools/__init__.py new file mode 100644 index 0000000000..19a169fc30 --- /dev/null +++ b/_setuptools_vendored/more_itertools/__init__.py @@ -0,0 +1,4 @@ +from .more import * # noqa +from .recipes import * # noqa + +__version__ = '8.8.0' diff --git a/_setuptools_vendored/more_itertools/__init__.pyi b/_setuptools_vendored/more_itertools/__init__.pyi new file mode 100644 index 0000000000..96f6e36c7f --- /dev/null +++ b/_setuptools_vendored/more_itertools/__init__.pyi @@ -0,0 +1,2 @@ +from .more import * +from .recipes import * diff --git a/_setuptools_vendored/more_itertools/more.py b/_setuptools_vendored/more_itertools/more.py new file mode 100644 index 0000000000..0f7d282aa5 --- /dev/null +++ b/_setuptools_vendored/more_itertools/more.py @@ -0,0 +1,3825 @@ +import warnings + +from collections import Counter, defaultdict, deque, abc +from collections.abc import Sequence +from concurrent.futures import ThreadPoolExecutor +from functools import partial, reduce, wraps +from heapq import merge, heapify, heapreplace, heappop +from itertools import ( + chain, + compress, + count, + cycle, + dropwhile, + groupby, + islice, + repeat, + starmap, + takewhile, + tee, + zip_longest, +) +from math import exp, factorial, floor, log +from queue import Empty, Queue +from random import random, randrange, uniform +from operator import itemgetter, mul, sub, gt, lt +from sys import hexversion, maxsize +from time import monotonic + +from .recipes import ( + consume, + flatten, + pairwise, + powerset, + take, + unique_everseen, +) + +__all__ = [ + 'AbortThread', + 'adjacent', + 'always_iterable', + 'always_reversible', + 'bucket', + 'callback_iter', + 'chunked', + 'circular_shifts', + 'collapse', + 'collate', + 'consecutive_groups', + 'consumer', + 'countable', + 'count_cycle', + 'mark_ends', + 'difference', + 'distinct_combinations', + 'distinct_permutations', + 'distribute', + 'divide', + 'exactly_n', + 'filter_except', + 'first', + 'groupby_transform', + 'ilen', + 'interleave_longest', + 'interleave', + 'intersperse', + 'islice_extended', + 'iterate', + 'ichunked', + 'is_sorted', + 'last', + 'locate', + 'lstrip', + 'make_decorator', + 'map_except', + 'map_reduce', + 'nth_or_last', + 'nth_permutation', + 'nth_product', + 'numeric_range', + 'one', + 'only', + 'padded', + 'partitions', + 'set_partitions', + 'peekable', + 'repeat_last', + 'replace', + 'rlocate', + 'rstrip', + 'run_length', + 'sample', + 'seekable', + 'SequenceView', + 'side_effect', + 'sliced', + 'sort_together', + 'split_at', + 'split_after', + 'split_before', + 'split_when', + 'split_into', + 'spy', + 'stagger', + 'strip', + 'substrings', + 'substrings_indexes', + 'time_limited', + 'unique_to_each', + 'unzip', + 'windowed', + 'with_iter', + 'UnequalIterablesError', + 'zip_equal', + 'zip_offset', + 'windowed_complete', + 'all_unique', + 'value_chain', + 'product_index', + 'combination_index', + 'permutation_index', +] + +_marker = object() + + +def chunked(iterable, n, strict=False): + """Break *iterable* into lists of length *n*: + + >>> list(chunked([1, 2, 3, 4, 5, 6], 3)) + [[1, 2, 3], [4, 5, 6]] + + By the default, the last yielded list will have fewer than *n* elements + if the length of *iterable* is not divisible by *n*: + + >>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3)) + [[1, 2, 3], [4, 5, 6], [7, 8]] + + To use a fill-in value instead, see the :func:`grouper` recipe. + + If the length of *iterable* is not divisible by *n* and *strict* is + ``True``, then ``ValueError`` will be raised before the last + list is yielded. + + """ + iterator = iter(partial(take, n, iter(iterable)), []) + if strict: + + def ret(): + for chunk in iterator: + if len(chunk) != n: + raise ValueError('iterable is not divisible by n.') + yield chunk + + return iter(ret()) + else: + return iterator + + +def first(iterable, default=_marker): + """Return the first item of *iterable*, or *default* if *iterable* is + empty. + + >>> first([0, 1, 2, 3]) + 0 + >>> first([], 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + + :func:`first` is useful when you have a generator of expensive-to-retrieve + values and want any arbitrary one. It is marginally shorter than + ``next(iter(iterable), default)``. + + """ + try: + return next(iter(iterable)) + except StopIteration as e: + if default is _marker: + raise ValueError( + 'first() was called on an empty iterable, and no ' + 'default value was provided.' + ) from e + return default + + +def last(iterable, default=_marker): + """Return the last item of *iterable*, or *default* if *iterable* is + empty. + + >>> last([0, 1, 2, 3]) + 3 + >>> last([], 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + """ + try: + if isinstance(iterable, Sequence): + return iterable[-1] + # Work around https://bugs.python.org/issue38525 + elif hasattr(iterable, '__reversed__') and (hexversion != 0x030800F0): + return next(reversed(iterable)) + else: + return deque(iterable, maxlen=1)[-1] + except (IndexError, TypeError, StopIteration): + if default is _marker: + raise ValueError( + 'last() was called on an empty iterable, and no default was ' + 'provided.' + ) + return default + + +def nth_or_last(iterable, n, default=_marker): + """Return the nth or the last item of *iterable*, + or *default* if *iterable* is empty. + + >>> nth_or_last([0, 1, 2, 3], 2) + 2 + >>> nth_or_last([0, 1], 2) + 1 + >>> nth_or_last([], 0, 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + """ + return last(islice(iterable, n + 1), default=default) + + +class peekable: + """Wrap an iterator to allow lookahead and prepending elements. + + Call :meth:`peek` on the result to get the value that will be returned + by :func:`next`. This won't advance the iterator: + + >>> p = peekable(['a', 'b']) + >>> p.peek() + 'a' + >>> next(p) + 'a' + + Pass :meth:`peek` a default value to return that instead of raising + ``StopIteration`` when the iterator is exhausted. + + >>> p = peekable([]) + >>> p.peek('hi') + 'hi' + + peekables also offer a :meth:`prepend` method, which "inserts" items + at the head of the iterable: + + >>> p = peekable([1, 2, 3]) + >>> p.prepend(10, 11, 12) + >>> next(p) + 10 + >>> p.peek() + 11 + >>> list(p) + [11, 12, 1, 2, 3] + + peekables can be indexed. Index 0 is the item that will be returned by + :func:`next`, index 1 is the item after that, and so on: + The values up to the given index will be cached. + + >>> p = peekable(['a', 'b', 'c', 'd']) + >>> p[0] + 'a' + >>> p[1] + 'b' + >>> next(p) + 'a' + + Negative indexes are supported, but be aware that they will cache the + remaining items in the source iterator, which may require significant + storage. + + To check whether a peekable is exhausted, check its truth value: + + >>> p = peekable(['a', 'b']) + >>> if p: # peekable has items + ... list(p) + ['a', 'b'] + >>> if not p: # peekable is exhausted + ... list(p) + [] + + """ + + def __init__(self, iterable): + self._it = iter(iterable) + self._cache = deque() + + def __iter__(self): + return self + + def __bool__(self): + try: + self.peek() + except StopIteration: + return False + return True + + def peek(self, default=_marker): + """Return the item that will be next returned from ``next()``. + + Return ``default`` if there are no items left. If ``default`` is not + provided, raise ``StopIteration``. + + """ + if not self._cache: + try: + self._cache.append(next(self._it)) + except StopIteration: + if default is _marker: + raise + return default + return self._cache[0] + + def prepend(self, *items): + """Stack up items to be the next ones returned from ``next()`` or + ``self.peek()``. The items will be returned in + first in, first out order:: + + >>> p = peekable([1, 2, 3]) + >>> p.prepend(10, 11, 12) + >>> next(p) + 10 + >>> list(p) + [11, 12, 1, 2, 3] + + It is possible, by prepending items, to "resurrect" a peekable that + previously raised ``StopIteration``. + + >>> p = peekable([]) + >>> next(p) + Traceback (most recent call last): + ... + StopIteration + >>> p.prepend(1) + >>> next(p) + 1 + >>> next(p) + Traceback (most recent call last): + ... + StopIteration + + """ + self._cache.extendleft(reversed(items)) + + def __next__(self): + if self._cache: + return self._cache.popleft() + + return next(self._it) + + def _get_slice(self, index): + # Normalize the slice's arguments + step = 1 if (index.step is None) else index.step + if step > 0: + start = 0 if (index.start is None) else index.start + stop = maxsize if (index.stop is None) else index.stop + elif step < 0: + start = -1 if (index.start is None) else index.start + stop = (-maxsize - 1) if (index.stop is None) else index.stop + else: + raise ValueError('slice step cannot be zero') + + # If either the start or stop index is negative, we'll need to cache + # the rest of the iterable in order to slice from the right side. + if (start < 0) or (stop < 0): + self._cache.extend(self._it) + # Otherwise we'll need to find the rightmost index and cache to that + # point. + else: + n = min(max(start, stop) + 1, maxsize) + cache_len = len(self._cache) + if n >= cache_len: + self._cache.extend(islice(self._it, n - cache_len)) + + return list(self._cache)[index] + + def __getitem__(self, index): + if isinstance(index, slice): + return self._get_slice(index) + + cache_len = len(self._cache) + if index < 0: + self._cache.extend(self._it) + elif index >= cache_len: + self._cache.extend(islice(self._it, index + 1 - cache_len)) + + return self._cache[index] + + +def collate(*iterables, **kwargs): + """Return a sorted merge of the items from each of several already-sorted + *iterables*. + + >>> list(collate('ACDZ', 'AZ', 'JKL')) + ['A', 'A', 'C', 'D', 'J', 'K', 'L', 'Z', 'Z'] + + Works lazily, keeping only the next value from each iterable in memory. Use + :func:`collate` to, for example, perform a n-way mergesort of items that + don't fit in memory. + + If a *key* function is specified, the iterables will be sorted according + to its result: + + >>> key = lambda s: int(s) # Sort by numeric value, not by string + >>> list(collate(['1', '10'], ['2', '11'], key=key)) + ['1', '2', '10', '11'] + + + If the *iterables* are sorted in descending order, set *reverse* to + ``True``: + + >>> list(collate([5, 3, 1], [4, 2, 0], reverse=True)) + [5, 4, 3, 2, 1, 0] + + If the elements of the passed-in iterables are out of order, you might get + unexpected results. + + On Python 3.5+, this function is an alias for :func:`heapq.merge`. + + """ + warnings.warn( + "collate is no longer part of more_itertools, use heapq.merge", + DeprecationWarning, + ) + return merge(*iterables, **kwargs) + + +def consumer(func): + """Decorator that automatically advances a PEP-342-style "reverse iterator" + to its first yield point so you don't have to call ``next()`` on it + manually. + + >>> @consumer + ... def tally(): + ... i = 0 + ... while True: + ... print('Thing number %s is %s.' % (i, (yield))) + ... i += 1 + ... + >>> t = tally() + >>> t.send('red') + Thing number 0 is red. + >>> t.send('fish') + Thing number 1 is fish. + + Without the decorator, you would have to call ``next(t)`` before + ``t.send()`` could be used. + + """ + + @wraps(func) + def wrapper(*args, **kwargs): + gen = func(*args, **kwargs) + next(gen) + return gen + + return wrapper + + +def ilen(iterable): + """Return the number of items in *iterable*. + + >>> ilen(x for x in range(1000000) if x % 3 == 0) + 333334 + + This consumes the iterable, so handle with care. + + """ + # This approach was selected because benchmarks showed it's likely the + # fastest of the known implementations at the time of writing. + # See GitHub tracker: #236, #230. + counter = count() + deque(zip(iterable, counter), maxlen=0) + return next(counter) + + +def iterate(func, start): + """Return ``start``, ``func(start)``, ``func(func(start))``, ... + + >>> from itertools import islice + >>> list(islice(iterate(lambda x: 2*x, 1), 10)) + [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] + + """ + while True: + yield start + start = func(start) + + +def with_iter(context_manager): + """Wrap an iterable in a ``with`` statement, so it closes once exhausted. + + For example, this will close the file when the iterator is exhausted:: + + upper_lines = (line.upper() for line in with_iter(open('foo'))) + + Any context manager which returns an iterable is a candidate for + ``with_iter``. + + """ + with context_manager as iterable: + yield from iterable + + +def one(iterable, too_short=None, too_long=None): + """Return the first item from *iterable*, which is expected to contain only + that item. Raise an exception if *iterable* is empty or has more than one + item. + + :func:`one` is useful for ensuring that an iterable contains only one item. + For example, it can be used to retrieve the result of a database query + that is expected to return a single row. + + If *iterable* is empty, ``ValueError`` will be raised. You may specify a + different exception with the *too_short* keyword: + + >>> it = [] + >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: too many items in iterable (expected 1)' + >>> too_short = IndexError('too few items') + >>> one(it, too_short=too_short) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + IndexError: too few items + + Similarly, if *iterable* contains more than one item, ``ValueError`` will + be raised. You may specify a different exception with the *too_long* + keyword: + + >>> it = ['too', 'many'] + >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Expected exactly one item in iterable, but got 'too', + 'many', and perhaps more. + >>> too_long = RuntimeError + >>> one(it, too_long=too_long) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + RuntimeError + + Note that :func:`one` attempts to advance *iterable* twice to ensure there + is only one item. See :func:`spy` or :func:`peekable` to check iterable + contents less destructively. + + """ + it = iter(iterable) + + try: + first_value = next(it) + except StopIteration as e: + raise ( + too_short or ValueError('too few items in iterable (expected 1)') + ) from e + + try: + second_value = next(it) + except StopIteration: + pass + else: + msg = ( + 'Expected exactly one item in iterable, but got {!r}, {!r}, ' + 'and perhaps more.'.format(first_value, second_value) + ) + raise too_long or ValueError(msg) + + return first_value + + +def distinct_permutations(iterable, r=None): + """Yield successive distinct permutations of the elements in *iterable*. + + >>> sorted(distinct_permutations([1, 0, 1])) + [(0, 1, 1), (1, 0, 1), (1, 1, 0)] + + Equivalent to ``set(permutations(iterable))``, except duplicates are not + generated and thrown away. For larger input sequences this is much more + efficient. + + Duplicate permutations arise when there are duplicated elements in the + input iterable. The number of items returned is + `n! / (x_1! * x_2! * ... * x_n!)`, where `n` is the total number of + items input, and each `x_i` is the count of a distinct item in the input + sequence. + + If *r* is given, only the *r*-length permutations are yielded. + + >>> sorted(distinct_permutations([1, 0, 1], r=2)) + [(0, 1), (1, 0), (1, 1)] + >>> sorted(distinct_permutations(range(3), r=2)) + [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] + + """ + # Algorithm: https://w.wiki/Qai + def _full(A): + while True: + # Yield the permutation we have + yield tuple(A) + + # Find the largest index i such that A[i] < A[i + 1] + for i in range(size - 2, -1, -1): + if A[i] < A[i + 1]: + break + # If no such index exists, this permutation is the last one + else: + return + + # Find the largest index j greater than j such that A[i] < A[j] + for j in range(size - 1, i, -1): + if A[i] < A[j]: + break + + # Swap the value of A[i] with that of A[j], then reverse the + # sequence from A[i + 1] to form the new permutation + A[i], A[j] = A[j], A[i] + A[i + 1 :] = A[: i - size : -1] # A[i + 1:][::-1] + + # Algorithm: modified from the above + def _partial(A, r): + # Split A into the first r items and the last r items + head, tail = A[:r], A[r:] + right_head_indexes = range(r - 1, -1, -1) + left_tail_indexes = range(len(tail)) + + while True: + # Yield the permutation we have + yield tuple(head) + + # Starting from the right, find the first index of the head with + # value smaller than the maximum value of the tail - call it i. + pivot = tail[-1] + for i in right_head_indexes: + if head[i] < pivot: + break + pivot = head[i] + else: + return + + # Starting from the left, find the first value of the tail + # with a value greater than head[i] and swap. + for j in left_tail_indexes: + if tail[j] > head[i]: + head[i], tail[j] = tail[j], head[i] + break + # If we didn't find one, start from the right and find the first + # index of the head with a value greater than head[i] and swap. + else: + for j in right_head_indexes: + if head[j] > head[i]: + head[i], head[j] = head[j], head[i] + break + + # Reverse head[i + 1:] and swap it with tail[:r - (i + 1)] + tail += head[: i - r : -1] # head[i + 1:][::-1] + i += 1 + head[i:], tail[:] = tail[: r - i], tail[r - i :] + + items = sorted(iterable) + + size = len(items) + if r is None: + r = size + + if 0 < r <= size: + return _full(items) if (r == size) else _partial(items, r) + + return iter(() if r else ((),)) + + +def intersperse(e, iterable, n=1): + """Intersperse filler element *e* among the items in *iterable*, leaving + *n* items between each filler element. + + >>> list(intersperse('!', [1, 2, 3, 4, 5])) + [1, '!', 2, '!', 3, '!', 4, '!', 5] + + >>> list(intersperse(None, [1, 2, 3, 4, 5], n=2)) + [1, 2, None, 3, 4, None, 5] + + """ + if n == 0: + raise ValueError('n must be > 0') + elif n == 1: + # interleave(repeat(e), iterable) -> e, x_0, e, e, x_1, e, x_2... + # islice(..., 1, None) -> x_0, e, e, x_1, e, x_2... + return islice(interleave(repeat(e), iterable), 1, None) + else: + # interleave(filler, chunks) -> [e], [x_0, x_1], [e], [x_2, x_3]... + # islice(..., 1, None) -> [x_0, x_1], [e], [x_2, x_3]... + # flatten(...) -> x_0, x_1, e, x_2, x_3... + filler = repeat([e]) + chunks = chunked(iterable, n) + return flatten(islice(interleave(filler, chunks), 1, None)) + + +def unique_to_each(*iterables): + """Return the elements from each of the input iterables that aren't in the + other input iterables. + + For example, suppose you have a set of packages, each with a set of + dependencies:: + + {'pkg_1': {'A', 'B'}, 'pkg_2': {'B', 'C'}, 'pkg_3': {'B', 'D'}} + + If you remove one package, which dependencies can also be removed? + + If ``pkg_1`` is removed, then ``A`` is no longer necessary - it is not + associated with ``pkg_2`` or ``pkg_3``. Similarly, ``C`` is only needed for + ``pkg_2``, and ``D`` is only needed for ``pkg_3``:: + + >>> unique_to_each({'A', 'B'}, {'B', 'C'}, {'B', 'D'}) + [['A'], ['C'], ['D']] + + If there are duplicates in one input iterable that aren't in the others + they will be duplicated in the output. Input order is preserved:: + + >>> unique_to_each("mississippi", "missouri") + [['p', 'p'], ['o', 'u', 'r']] + + It is assumed that the elements of each iterable are hashable. + + """ + pool = [list(it) for it in iterables] + counts = Counter(chain.from_iterable(map(set, pool))) + uniques = {element for element in counts if counts[element] == 1} + return [list(filter(uniques.__contains__, it)) for it in pool] + + +def windowed(seq, n, fillvalue=None, step=1): + """Return a sliding window of width *n* over the given iterable. + + >>> all_windows = windowed([1, 2, 3, 4, 5], 3) + >>> list(all_windows) + [(1, 2, 3), (2, 3, 4), (3, 4, 5)] + + When the window is larger than the iterable, *fillvalue* is used in place + of missing values: + + >>> list(windowed([1, 2, 3], 4)) + [(1, 2, 3, None)] + + Each window will advance in increments of *step*: + + >>> list(windowed([1, 2, 3, 4, 5, 6], 3, fillvalue='!', step=2)) + [(1, 2, 3), (3, 4, 5), (5, 6, '!')] + + To slide into the iterable's items, use :func:`chain` to add filler items + to the left: + + >>> iterable = [1, 2, 3, 4] + >>> n = 3 + >>> padding = [None] * (n - 1) + >>> list(windowed(chain(padding, iterable), 3)) + [(None, None, 1), (None, 1, 2), (1, 2, 3), (2, 3, 4)] + """ + if n < 0: + raise ValueError('n must be >= 0') + if n == 0: + yield tuple() + return + if step < 1: + raise ValueError('step must be >= 1') + + window = deque(maxlen=n) + i = n + for _ in map(window.append, seq): + i -= 1 + if not i: + i = step + yield tuple(window) + + size = len(window) + if size < n: + yield tuple(chain(window, repeat(fillvalue, n - size))) + elif 0 < i < min(step, n): + window += (fillvalue,) * i + yield tuple(window) + + +def substrings(iterable): + """Yield all of the substrings of *iterable*. + + >>> [''.join(s) for s in substrings('more')] + ['m', 'o', 'r', 'e', 'mo', 'or', 're', 'mor', 'ore', 'more'] + + Note that non-string iterables can also be subdivided. + + >>> list(substrings([0, 1, 2])) + [(0,), (1,), (2,), (0, 1), (1, 2), (0, 1, 2)] + + """ + # The length-1 substrings + seq = [] + for item in iter(iterable): + seq.append(item) + yield (item,) + seq = tuple(seq) + item_count = len(seq) + + # And the rest + for n in range(2, item_count + 1): + for i in range(item_count - n + 1): + yield seq[i : i + n] + + +def substrings_indexes(seq, reverse=False): + """Yield all substrings and their positions in *seq* + + The items yielded will be a tuple of the form ``(substr, i, j)``, where + ``substr == seq[i:j]``. + + This function only works for iterables that support slicing, such as + ``str`` objects. + + >>> for item in substrings_indexes('more'): + ... print(item) + ('m', 0, 1) + ('o', 1, 2) + ('r', 2, 3) + ('e', 3, 4) + ('mo', 0, 2) + ('or', 1, 3) + ('re', 2, 4) + ('mor', 0, 3) + ('ore', 1, 4) + ('more', 0, 4) + + Set *reverse* to ``True`` to yield the same items in the opposite order. + + + """ + r = range(1, len(seq) + 1) + if reverse: + r = reversed(r) + return ( + (seq[i : i + L], i, i + L) for L in r for i in range(len(seq) - L + 1) + ) + + +class bucket: + """Wrap *iterable* and return an object that buckets it iterable into + child iterables based on a *key* function. + + >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3'] + >>> s = bucket(iterable, key=lambda x: x[0]) # Bucket by 1st character + >>> sorted(list(s)) # Get the keys + ['a', 'b', 'c'] + >>> a_iterable = s['a'] + >>> next(a_iterable) + 'a1' + >>> next(a_iterable) + 'a2' + >>> list(s['b']) + ['b1', 'b2', 'b3'] + + The original iterable will be advanced and its items will be cached until + they are used by the child iterables. This may require significant storage. + + By default, attempting to select a bucket to which no items belong will + exhaust the iterable and cache all values. + If you specify a *validator* function, selected buckets will instead be + checked against it. + + >>> from itertools import count + >>> it = count(1, 2) # Infinite sequence of odd numbers + >>> key = lambda x: x % 10 # Bucket by last digit + >>> validator = lambda x: x in {1, 3, 5, 7, 9} # Odd digits only + >>> s = bucket(it, key=key, validator=validator) + >>> 2 in s + False + >>> list(s[2]) + [] + + """ + + def __init__(self, iterable, key, validator=None): + self._it = iter(iterable) + self._key = key + self._cache = defaultdict(deque) + self._validator = validator or (lambda x: True) + + def __contains__(self, value): + if not self._validator(value): + return False + + try: + item = next(self[value]) + except StopIteration: + return False + else: + self._cache[value].appendleft(item) + + return True + + def _get_values(self, value): + """ + Helper to yield items from the parent iterator that match *value*. + Items that don't match are stored in the local cache as they + are encountered. + """ + while True: + # If we've cached some items that match the target value, emit + # the first one and evict it from the cache. + if self._cache[value]: + yield self._cache[value].popleft() + # Otherwise we need to advance the parent iterator to search for + # a matching item, caching the rest. + else: + while True: + try: + item = next(self._it) + except StopIteration: + return + item_value = self._key(item) + if item_value == value: + yield item + break + elif self._validator(item_value): + self._cache[item_value].append(item) + + def __iter__(self): + for item in self._it: + item_value = self._key(item) + if self._validator(item_value): + self._cache[item_value].append(item) + + yield from self._cache.keys() + + def __getitem__(self, value): + if not self._validator(value): + return iter(()) + + return self._get_values(value) + + +def spy(iterable, n=1): + """Return a 2-tuple with a list containing the first *n* elements of + *iterable*, and an iterator with the same items as *iterable*. + This allows you to "look ahead" at the items in the iterable without + advancing it. + + There is one item in the list by default: + + >>> iterable = 'abcdefg' + >>> head, iterable = spy(iterable) + >>> head + ['a'] + >>> list(iterable) + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + + You may use unpacking to retrieve items instead of lists: + + >>> (head,), iterable = spy('abcdefg') + >>> head + 'a' + >>> (first, second), iterable = spy('abcdefg', 2) + >>> first + 'a' + >>> second + 'b' + + The number of items requested can be larger than the number of items in + the iterable: + + >>> iterable = [1, 2, 3, 4, 5] + >>> head, iterable = spy(iterable, 10) + >>> head + [1, 2, 3, 4, 5] + >>> list(iterable) + [1, 2, 3, 4, 5] + + """ + it = iter(iterable) + head = take(n, it) + + return head.copy(), chain(head, it) + + +def interleave(*iterables): + """Return a new iterable yielding from each iterable in turn, + until the shortest is exhausted. + + >>> list(interleave([1, 2, 3], [4, 5], [6, 7, 8])) + [1, 4, 6, 2, 5, 7] + + For a version that doesn't terminate after the shortest iterable is + exhausted, see :func:`interleave_longest`. + + """ + return chain.from_iterable(zip(*iterables)) + + +def interleave_longest(*iterables): + """Return a new iterable yielding from each iterable in turn, + skipping any that are exhausted. + + >>> list(interleave_longest([1, 2, 3], [4, 5], [6, 7, 8])) + [1, 4, 6, 2, 5, 7, 3, 8] + + This function produces the same output as :func:`roundrobin`, but may + perform better for some inputs (in particular when the number of iterables + is large). + + """ + i = chain.from_iterable(zip_longest(*iterables, fillvalue=_marker)) + return (x for x in i if x is not _marker) + + +def collapse(iterable, base_type=None, levels=None): + """Flatten an iterable with multiple levels of nesting (e.g., a list of + lists of tuples) into non-iterable types. + + >>> iterable = [(1, 2), ([3, 4], [[5], [6]])] + >>> list(collapse(iterable)) + [1, 2, 3, 4, 5, 6] + + Binary and text strings are not considered iterable and + will not be collapsed. + + To avoid collapsing other types, specify *base_type*: + + >>> iterable = ['ab', ('cd', 'ef'), ['gh', 'ij']] + >>> list(collapse(iterable, base_type=tuple)) + ['ab', ('cd', 'ef'), 'gh', 'ij'] + + Specify *levels* to stop flattening after a certain level: + + >>> iterable = [('a', ['b']), ('c', ['d'])] + >>> list(collapse(iterable)) # Fully flattened + ['a', 'b', 'c', 'd'] + >>> list(collapse(iterable, levels=1)) # Only one level flattened + ['a', ['b'], 'c', ['d']] + + """ + + def walk(node, level): + if ( + ((levels is not None) and (level > levels)) + or isinstance(node, (str, bytes)) + or ((base_type is not None) and isinstance(node, base_type)) + ): + yield node + return + + try: + tree = iter(node) + except TypeError: + yield node + return + else: + for child in tree: + yield from walk(child, level + 1) + + yield from walk(iterable, 0) + + +def side_effect(func, iterable, chunk_size=None, before=None, after=None): + """Invoke *func* on each item in *iterable* (or on each *chunk_size* group + of items) before yielding the item. + + `func` must be a function that takes a single argument. Its return value + will be discarded. + + *before* and *after* are optional functions that take no arguments. They + will be executed before iteration starts and after it ends, respectively. + + `side_effect` can be used for logging, updating progress bars, or anything + that is not functionally "pure." + + Emitting a status message: + + >>> from more_itertools import consume + >>> func = lambda item: print('Received {}'.format(item)) + >>> consume(side_effect(func, range(2))) + Received 0 + Received 1 + + Operating on chunks of items: + + >>> pair_sums = [] + >>> func = lambda chunk: pair_sums.append(sum(chunk)) + >>> list(side_effect(func, [0, 1, 2, 3, 4, 5], 2)) + [0, 1, 2, 3, 4, 5] + >>> list(pair_sums) + [1, 5, 9] + + Writing to a file-like object: + + >>> from io import StringIO + >>> from more_itertools import consume + >>> f = StringIO() + >>> func = lambda x: print(x, file=f) + >>> before = lambda: print(u'HEADER', file=f) + >>> after = f.close + >>> it = [u'a', u'b', u'c'] + >>> consume(side_effect(func, it, before=before, after=after)) + >>> f.closed + True + + """ + try: + if before is not None: + before() + + if chunk_size is None: + for item in iterable: + func(item) + yield item + else: + for chunk in chunked(iterable, chunk_size): + func(chunk) + yield from chunk + finally: + if after is not None: + after() + + +def sliced(seq, n, strict=False): + """Yield slices of length *n* from the sequence *seq*. + + >>> list(sliced((1, 2, 3, 4, 5, 6), 3)) + [(1, 2, 3), (4, 5, 6)] + + By the default, the last yielded slice will have fewer than *n* elements + if the length of *seq* is not divisible by *n*: + + >>> list(sliced((1, 2, 3, 4, 5, 6, 7, 8), 3)) + [(1, 2, 3), (4, 5, 6), (7, 8)] + + If the length of *seq* is not divisible by *n* and *strict* is + ``True``, then ``ValueError`` will be raised before the last + slice is yielded. + + This function will only work for iterables that support slicing. + For non-sliceable iterables, see :func:`chunked`. + + """ + iterator = takewhile(len, (seq[i : i + n] for i in count(0, n))) + if strict: + + def ret(): + for _slice in iterator: + if len(_slice) != n: + raise ValueError("seq is not divisible by n.") + yield _slice + + return iter(ret()) + else: + return iterator + + +def split_at(iterable, pred, maxsplit=-1, keep_separator=False): + """Yield lists of items from *iterable*, where each list is delimited by + an item where callable *pred* returns ``True``. + + >>> list(split_at('abcdcba', lambda x: x == 'b')) + [['a'], ['c', 'd', 'c'], ['a']] + + >>> list(split_at(range(10), lambda n: n % 2 == 1)) + [[0], [2], [4], [6], [8], []] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_at(range(10), lambda n: n % 2 == 1, maxsplit=2)) + [[0], [2], [4, 5, 6, 7, 8, 9]] + + By default, the delimiting items are not included in the output. + The include them, set *keep_separator* to ``True``. + + >>> list(split_at('abcdcba', lambda x: x == 'b', keep_separator=True)) + [['a'], ['b'], ['c', 'd', 'c'], ['b'], ['a']] + + """ + if maxsplit == 0: + yield list(iterable) + return + + buf = [] + it = iter(iterable) + for item in it: + if pred(item): + yield buf + if keep_separator: + yield [item] + if maxsplit == 1: + yield list(it) + return + buf = [] + maxsplit -= 1 + else: + buf.append(item) + yield buf + + +def split_before(iterable, pred, maxsplit=-1): + """Yield lists of items from *iterable*, where each list ends just before + an item for which callable *pred* returns ``True``: + + >>> list(split_before('OneTwo', lambda s: s.isupper())) + [['O', 'n', 'e'], ['T', 'w', 'o']] + + >>> list(split_before(range(10), lambda n: n % 3 == 0)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_before(range(10), lambda n: n % 3 == 0, maxsplit=2)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]] + """ + if maxsplit == 0: + yield list(iterable) + return + + buf = [] + it = iter(iterable) + for item in it: + if pred(item) and buf: + yield buf + if maxsplit == 1: + yield [item] + list(it) + return + buf = [] + maxsplit -= 1 + buf.append(item) + if buf: + yield buf + + +def split_after(iterable, pred, maxsplit=-1): + """Yield lists of items from *iterable*, where each list ends with an + item where callable *pred* returns ``True``: + + >>> list(split_after('one1two2', lambda s: s.isdigit())) + [['o', 'n', 'e', '1'], ['t', 'w', 'o', '2']] + + >>> list(split_after(range(10), lambda n: n % 3 == 0)) + [[0], [1, 2, 3], [4, 5, 6], [7, 8, 9]] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_after(range(10), lambda n: n % 3 == 0, maxsplit=2)) + [[0], [1, 2, 3], [4, 5, 6, 7, 8, 9]] + + """ + if maxsplit == 0: + yield list(iterable) + return + + buf = [] + it = iter(iterable) + for item in it: + buf.append(item) + if pred(item) and buf: + yield buf + if maxsplit == 1: + yield list(it) + return + buf = [] + maxsplit -= 1 + if buf: + yield buf + + +def split_when(iterable, pred, maxsplit=-1): + """Split *iterable* into pieces based on the output of *pred*. + *pred* should be a function that takes successive pairs of items and + returns ``True`` if the iterable should be split in between them. + + For example, to find runs of increasing numbers, split the iterable when + element ``i`` is larger than element ``i + 1``: + + >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], lambda x, y: x > y)) + [[1, 2, 3, 3], [2, 5], [2, 4], [2]] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], + ... lambda x, y: x > y, maxsplit=2)) + [[1, 2, 3, 3], [2, 5], [2, 4, 2]] + + """ + if maxsplit == 0: + yield list(iterable) + return + + it = iter(iterable) + try: + cur_item = next(it) + except StopIteration: + return + + buf = [cur_item] + for next_item in it: + if pred(cur_item, next_item): + yield buf + if maxsplit == 1: + yield [next_item] + list(it) + return + buf = [] + maxsplit -= 1 + + buf.append(next_item) + cur_item = next_item + + yield buf + + +def split_into(iterable, sizes): + """Yield a list of sequential items from *iterable* of length 'n' for each + integer 'n' in *sizes*. + + >>> list(split_into([1,2,3,4,5,6], [1,2,3])) + [[1], [2, 3], [4, 5, 6]] + + If the sum of *sizes* is smaller than the length of *iterable*, then the + remaining items of *iterable* will not be returned. + + >>> list(split_into([1,2,3,4,5,6], [2,3])) + [[1, 2], [3, 4, 5]] + + If the sum of *sizes* is larger than the length of *iterable*, fewer items + will be returned in the iteration that overruns *iterable* and further + lists will be empty: + + >>> list(split_into([1,2,3,4], [1,2,3,4])) + [[1], [2, 3], [4], []] + + When a ``None`` object is encountered in *sizes*, the returned list will + contain items up to the end of *iterable* the same way that itertools.slice + does: + + >>> list(split_into([1,2,3,4,5,6,7,8,9,0], [2,3,None])) + [[1, 2], [3, 4, 5], [6, 7, 8, 9, 0]] + + :func:`split_into` can be useful for grouping a series of items where the + sizes of the groups are not uniform. An example would be where in a row + from a table, multiple columns represent elements of the same feature + (e.g. a point represented by x,y,z) but, the format is not the same for + all columns. + """ + # convert the iterable argument into an iterator so its contents can + # be consumed by islice in case it is a generator + it = iter(iterable) + + for size in sizes: + if size is None: + yield list(it) + return + else: + yield list(islice(it, size)) + + +def padded(iterable, fillvalue=None, n=None, next_multiple=False): + """Yield the elements from *iterable*, followed by *fillvalue*, such that + at least *n* items are emitted. + + >>> list(padded([1, 2, 3], '?', 5)) + [1, 2, 3, '?', '?'] + + If *next_multiple* is ``True``, *fillvalue* will be emitted until the + number of items emitted is a multiple of *n*:: + + >>> list(padded([1, 2, 3, 4], n=3, next_multiple=True)) + [1, 2, 3, 4, None, None] + + If *n* is ``None``, *fillvalue* will be emitted indefinitely. + + """ + it = iter(iterable) + if n is None: + yield from chain(it, repeat(fillvalue)) + elif n < 1: + raise ValueError('n must be at least 1') + else: + item_count = 0 + for item in it: + yield item + item_count += 1 + + remaining = (n - item_count) % n if next_multiple else n - item_count + for _ in range(remaining): + yield fillvalue + + +def repeat_last(iterable, default=None): + """After the *iterable* is exhausted, keep yielding its last element. + + >>> list(islice(repeat_last(range(3)), 5)) + [0, 1, 2, 2, 2] + + If the iterable is empty, yield *default* forever:: + + >>> list(islice(repeat_last(range(0), 42), 5)) + [42, 42, 42, 42, 42] + + """ + item = _marker + for item in iterable: + yield item + final = default if item is _marker else item + yield from repeat(final) + + +def distribute(n, iterable): + """Distribute the items from *iterable* among *n* smaller iterables. + + >>> group_1, group_2 = distribute(2, [1, 2, 3, 4, 5, 6]) + >>> list(group_1) + [1, 3, 5] + >>> list(group_2) + [2, 4, 6] + + If the length of *iterable* is not evenly divisible by *n*, then the + length of the returned iterables will not be identical: + + >>> children = distribute(3, [1, 2, 3, 4, 5, 6, 7]) + >>> [list(c) for c in children] + [[1, 4, 7], [2, 5], [3, 6]] + + If the length of *iterable* is smaller than *n*, then the last returned + iterables will be empty: + + >>> children = distribute(5, [1, 2, 3]) + >>> [list(c) for c in children] + [[1], [2], [3], [], []] + + This function uses :func:`itertools.tee` and may require significant + storage. If you need the order items in the smaller iterables to match the + original iterable, see :func:`divide`. + + """ + if n < 1: + raise ValueError('n must be at least 1') + + children = tee(iterable, n) + return [islice(it, index, None, n) for index, it in enumerate(children)] + + +def stagger(iterable, offsets=(-1, 0, 1), longest=False, fillvalue=None): + """Yield tuples whose elements are offset from *iterable*. + The amount by which the `i`-th item in each tuple is offset is given by + the `i`-th item in *offsets*. + + >>> list(stagger([0, 1, 2, 3])) + [(None, 0, 1), (0, 1, 2), (1, 2, 3)] + >>> list(stagger(range(8), offsets=(0, 2, 4))) + [(0, 2, 4), (1, 3, 5), (2, 4, 6), (3, 5, 7)] + + By default, the sequence will end when the final element of a tuple is the + last item in the iterable. To continue until the first element of a tuple + is the last item in the iterable, set *longest* to ``True``:: + + >>> list(stagger([0, 1, 2, 3], longest=True)) + [(None, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, None), (3, None, None)] + + By default, ``None`` will be used to replace offsets beyond the end of the + sequence. Specify *fillvalue* to use some other value. + + """ + children = tee(iterable, len(offsets)) + + return zip_offset( + *children, offsets=offsets, longest=longest, fillvalue=fillvalue + ) + + +class UnequalIterablesError(ValueError): + def __init__(self, details=None): + msg = 'Iterables have different lengths' + if details is not None: + msg += (': index 0 has length {}; index {} has length {}').format( + *details + ) + + super().__init__(msg) + + +def _zip_equal_generator(iterables): + for combo in zip_longest(*iterables, fillvalue=_marker): + for val in combo: + if val is _marker: + raise UnequalIterablesError() + yield combo + + +def zip_equal(*iterables): + """``zip`` the input *iterables* together, but raise + ``UnequalIterablesError`` if they aren't all the same length. + + >>> it_1 = range(3) + >>> it_2 = iter('abc') + >>> list(zip_equal(it_1, it_2)) + [(0, 'a'), (1, 'b'), (2, 'c')] + + >>> it_1 = range(3) + >>> it_2 = iter('abcd') + >>> list(zip_equal(it_1, it_2)) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + more_itertools.more.UnequalIterablesError: Iterables have different + lengths + + """ + if hexversion >= 0x30A00A6: + warnings.warn( + ( + 'zip_equal will be removed in a future version of ' + 'more-itertools. Use the builtin zip function with ' + 'strict=True instead.' + ), + DeprecationWarning, + ) + # Check whether the iterables are all the same size. + try: + first_size = len(iterables[0]) + for i, it in enumerate(iterables[1:], 1): + size = len(it) + if size != first_size: + break + else: + # If we didn't break out, we can use the built-in zip. + return zip(*iterables) + + # If we did break out, there was a mismatch. + raise UnequalIterablesError(details=(first_size, i, size)) + # If any one of the iterables didn't have a length, start reading + # them until one runs out. + except TypeError: + return _zip_equal_generator(iterables) + + +def zip_offset(*iterables, offsets, longest=False, fillvalue=None): + """``zip`` the input *iterables* together, but offset the `i`-th iterable + by the `i`-th item in *offsets*. + + >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1))) + [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e')] + + This can be used as a lightweight alternative to SciPy or pandas to analyze + data sets in which some series have a lead or lag relationship. + + By default, the sequence will end when the shortest iterable is exhausted. + To continue until the longest iterable is exhausted, set *longest* to + ``True``. + + >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1), longest=True)) + [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e'), (None, 'f')] + + By default, ``None`` will be used to replace offsets beyond the end of the + sequence. Specify *fillvalue* to use some other value. + + """ + if len(iterables) != len(offsets): + raise ValueError("Number of iterables and offsets didn't match") + + staggered = [] + for it, n in zip(iterables, offsets): + if n < 0: + staggered.append(chain(repeat(fillvalue, -n), it)) + elif n > 0: + staggered.append(islice(it, n, None)) + else: + staggered.append(it) + + if longest: + return zip_longest(*staggered, fillvalue=fillvalue) + + return zip(*staggered) + + +def sort_together(iterables, key_list=(0,), key=None, reverse=False): + """Return the input iterables sorted together, with *key_list* as the + priority for sorting. All iterables are trimmed to the length of the + shortest one. + + This can be used like the sorting function in a spreadsheet. If each + iterable represents a column of data, the key list determines which + columns are used for sorting. + + By default, all iterables are sorted using the ``0``-th iterable:: + + >>> iterables = [(4, 3, 2, 1), ('a', 'b', 'c', 'd')] + >>> sort_together(iterables) + [(1, 2, 3, 4), ('d', 'c', 'b', 'a')] + + Set a different key list to sort according to another iterable. + Specifying multiple keys dictates how ties are broken:: + + >>> iterables = [(3, 1, 2), (0, 1, 0), ('c', 'b', 'a')] + >>> sort_together(iterables, key_list=(1, 2)) + [(2, 3, 1), (0, 0, 1), ('a', 'c', 'b')] + + To sort by a function of the elements of the iterable, pass a *key* + function. Its arguments are the elements of the iterables corresponding to + the key list:: + + >>> names = ('a', 'b', 'c') + >>> lengths = (1, 2, 3) + >>> widths = (5, 2, 1) + >>> def area(length, width): + ... return length * width + >>> sort_together([names, lengths, widths], key_list=(1, 2), key=area) + [('c', 'b', 'a'), (3, 2, 1), (1, 2, 5)] + + Set *reverse* to ``True`` to sort in descending order. + + >>> sort_together([(1, 2, 3), ('c', 'b', 'a')], reverse=True) + [(3, 2, 1), ('a', 'b', 'c')] + + """ + if key is None: + # if there is no key function, the key argument to sorted is an + # itemgetter + key_argument = itemgetter(*key_list) + else: + # if there is a key function, call it with the items at the offsets + # specified by the key function as arguments + key_list = list(key_list) + if len(key_list) == 1: + # if key_list contains a single item, pass the item at that offset + # as the only argument to the key function + key_offset = key_list[0] + key_argument = lambda zipped_items: key(zipped_items[key_offset]) + else: + # if key_list contains multiple items, use itemgetter to return a + # tuple of items, which we pass as *args to the key function + get_key_items = itemgetter(*key_list) + key_argument = lambda zipped_items: key( + *get_key_items(zipped_items) + ) + + return list( + zip(*sorted(zip(*iterables), key=key_argument, reverse=reverse)) + ) + + +def unzip(iterable): + """The inverse of :func:`zip`, this function disaggregates the elements + of the zipped *iterable*. + + The ``i``-th iterable contains the ``i``-th element from each element + of the zipped iterable. The first element is used to to determine the + length of the remaining elements. + + >>> iterable = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + >>> letters, numbers = unzip(iterable) + >>> list(letters) + ['a', 'b', 'c', 'd'] + >>> list(numbers) + [1, 2, 3, 4] + + This is similar to using ``zip(*iterable)``, but it avoids reading + *iterable* into memory. Note, however, that this function uses + :func:`itertools.tee` and thus may require significant storage. + + """ + head, iterable = spy(iter(iterable)) + if not head: + # empty iterable, e.g. zip([], [], []) + return () + # spy returns a one-length iterable as head + head = head[0] + iterables = tee(iterable, len(head)) + + def itemgetter(i): + def getter(obj): + try: + return obj[i] + except IndexError: + # basically if we have an iterable like + # iter([(1, 2, 3), (4, 5), (6,)]) + # the second unzipped iterable would fail at the third tuple + # since it would try to access tup[1] + # same with the third unzipped iterable and the second tuple + # to support these "improperly zipped" iterables, + # we create a custom itemgetter + # which just stops the unzipped iterables + # at first length mismatch + raise StopIteration + + return getter + + return tuple(map(itemgetter(i), it) for i, it in enumerate(iterables)) + + +def divide(n, iterable): + """Divide the elements from *iterable* into *n* parts, maintaining + order. + + >>> group_1, group_2 = divide(2, [1, 2, 3, 4, 5, 6]) + >>> list(group_1) + [1, 2, 3] + >>> list(group_2) + [4, 5, 6] + + If the length of *iterable* is not evenly divisible by *n*, then the + length of the returned iterables will not be identical: + + >>> children = divide(3, [1, 2, 3, 4, 5, 6, 7]) + >>> [list(c) for c in children] + [[1, 2, 3], [4, 5], [6, 7]] + + If the length of the iterable is smaller than n, then the last returned + iterables will be empty: + + >>> children = divide(5, [1, 2, 3]) + >>> [list(c) for c in children] + [[1], [2], [3], [], []] + + This function will exhaust the iterable before returning and may require + significant storage. If order is not important, see :func:`distribute`, + which does not first pull the iterable into memory. + + """ + if n < 1: + raise ValueError('n must be at least 1') + + try: + iterable[:0] + except TypeError: + seq = tuple(iterable) + else: + seq = iterable + + q, r = divmod(len(seq), n) + + ret = [] + stop = 0 + for i in range(1, n + 1): + start = stop + stop += q + 1 if i <= r else q + ret.append(iter(seq[start:stop])) + + return ret + + +def always_iterable(obj, base_type=(str, bytes)): + """If *obj* is iterable, return an iterator over its items:: + + >>> obj = (1, 2, 3) + >>> list(always_iterable(obj)) + [1, 2, 3] + + If *obj* is not iterable, return a one-item iterable containing *obj*:: + + >>> obj = 1 + >>> list(always_iterable(obj)) + [1] + + If *obj* is ``None``, return an empty iterable: + + >>> obj = None + >>> list(always_iterable(None)) + [] + + By default, binary and text strings are not considered iterable:: + + >>> obj = 'foo' + >>> list(always_iterable(obj)) + ['foo'] + + If *base_type* is set, objects for which ``isinstance(obj, base_type)`` + returns ``True`` won't be considered iterable. + + >>> obj = {'a': 1} + >>> list(always_iterable(obj)) # Iterate over the dict's keys + ['a'] + >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit + [{'a': 1}] + + Set *base_type* to ``None`` to avoid any special handling and treat objects + Python considers iterable as iterable: + + >>> obj = 'foo' + >>> list(always_iterable(obj, base_type=None)) + ['f', 'o', 'o'] + """ + if obj is None: + return iter(()) + + if (base_type is not None) and isinstance(obj, base_type): + return iter((obj,)) + + try: + return iter(obj) + except TypeError: + return iter((obj,)) + + +def adjacent(predicate, iterable, distance=1): + """Return an iterable over `(bool, item)` tuples where the `item` is + drawn from *iterable* and the `bool` indicates whether + that item satisfies the *predicate* or is adjacent to an item that does. + + For example, to find whether items are adjacent to a ``3``:: + + >>> list(adjacent(lambda x: x == 3, range(6))) + [(False, 0), (False, 1), (True, 2), (True, 3), (True, 4), (False, 5)] + + Set *distance* to change what counts as adjacent. For example, to find + whether items are two places away from a ``3``: + + >>> list(adjacent(lambda x: x == 3, range(6), distance=2)) + [(False, 0), (True, 1), (True, 2), (True, 3), (True, 4), (True, 5)] + + This is useful for contextualizing the results of a search function. + For example, a code comparison tool might want to identify lines that + have changed, but also surrounding lines to give the viewer of the diff + context. + + The predicate function will only be called once for each item in the + iterable. + + See also :func:`groupby_transform`, which can be used with this function + to group ranges of items with the same `bool` value. + + """ + # Allow distance=0 mainly for testing that it reproduces results with map() + if distance < 0: + raise ValueError('distance must be at least 0') + + i1, i2 = tee(iterable) + padding = [False] * distance + selected = chain(padding, map(predicate, i1), padding) + adjacent_to_selected = map(any, windowed(selected, 2 * distance + 1)) + return zip(adjacent_to_selected, i2) + + +def groupby_transform(iterable, keyfunc=None, valuefunc=None, reducefunc=None): + """An extension of :func:`itertools.groupby` that can apply transformations + to the grouped data. + + * *keyfunc* is a function computing a key value for each item in *iterable* + * *valuefunc* is a function that transforms the individual items from + *iterable* after grouping + * *reducefunc* is a function that transforms each group of items + + >>> iterable = 'aAAbBBcCC' + >>> keyfunc = lambda k: k.upper() + >>> valuefunc = lambda v: v.lower() + >>> reducefunc = lambda g: ''.join(g) + >>> list(groupby_transform(iterable, keyfunc, valuefunc, reducefunc)) + [('A', 'aaa'), ('B', 'bbb'), ('C', 'ccc')] + + Each optional argument defaults to an identity function if not specified. + + :func:`groupby_transform` is useful when grouping elements of an iterable + using a separate iterable as the key. To do this, :func:`zip` the iterables + and pass a *keyfunc* that extracts the first element and a *valuefunc* + that extracts the second element:: + + >>> from operator import itemgetter + >>> keys = [0, 0, 1, 1, 1, 2, 2, 2, 3] + >>> values = 'abcdefghi' + >>> iterable = zip(keys, values) + >>> grouper = groupby_transform(iterable, itemgetter(0), itemgetter(1)) + >>> [(k, ''.join(g)) for k, g in grouper] + [(0, 'ab'), (1, 'cde'), (2, 'fgh'), (3, 'i')] + + Note that the order of items in the iterable is significant. + Only adjacent items are grouped together, so if you don't want any + duplicate groups, you should sort the iterable by the key function. + + """ + ret = groupby(iterable, keyfunc) + if valuefunc: + ret = ((k, map(valuefunc, g)) for k, g in ret) + if reducefunc: + ret = ((k, reducefunc(g)) for k, g in ret) + + return ret + + +class numeric_range(abc.Sequence, abc.Hashable): + """An extension of the built-in ``range()`` function whose arguments can + be any orderable numeric type. + + With only *stop* specified, *start* defaults to ``0`` and *step* + defaults to ``1``. The output items will match the type of *stop*: + + >>> list(numeric_range(3.5)) + [0.0, 1.0, 2.0, 3.0] + + With only *start* and *stop* specified, *step* defaults to ``1``. The + output items will match the type of *start*: + + >>> from decimal import Decimal + >>> start = Decimal('2.1') + >>> stop = Decimal('5.1') + >>> list(numeric_range(start, stop)) + [Decimal('2.1'), Decimal('3.1'), Decimal('4.1')] + + With *start*, *stop*, and *step* specified the output items will match + the type of ``start + step``: + + >>> from fractions import Fraction + >>> start = Fraction(1, 2) # Start at 1/2 + >>> stop = Fraction(5, 2) # End at 5/2 + >>> step = Fraction(1, 2) # Count by 1/2 + >>> list(numeric_range(start, stop, step)) + [Fraction(1, 2), Fraction(1, 1), Fraction(3, 2), Fraction(2, 1)] + + If *step* is zero, ``ValueError`` is raised. Negative steps are supported: + + >>> list(numeric_range(3, -1, -1.0)) + [3.0, 2.0, 1.0, 0.0] + + Be aware of the limitations of floating point numbers; the representation + of the yielded numbers may be surprising. + + ``datetime.datetime`` objects can be used for *start* and *stop*, if *step* + is a ``datetime.timedelta`` object: + + >>> import datetime + >>> start = datetime.datetime(2019, 1, 1) + >>> stop = datetime.datetime(2019, 1, 3) + >>> step = datetime.timedelta(days=1) + >>> items = iter(numeric_range(start, stop, step)) + >>> next(items) + datetime.datetime(2019, 1, 1, 0, 0) + >>> next(items) + datetime.datetime(2019, 1, 2, 0, 0) + + """ + + _EMPTY_HASH = hash(range(0, 0)) + + def __init__(self, *args): + argc = len(args) + if argc == 1: + (self._stop,) = args + self._start = type(self._stop)(0) + self._step = type(self._stop - self._start)(1) + elif argc == 2: + self._start, self._stop = args + self._step = type(self._stop - self._start)(1) + elif argc == 3: + self._start, self._stop, self._step = args + elif argc == 0: + raise TypeError( + 'numeric_range expected at least ' + '1 argument, got {}'.format(argc) + ) + else: + raise TypeError( + 'numeric_range expected at most ' + '3 arguments, got {}'.format(argc) + ) + + self._zero = type(self._step)(0) + if self._step == self._zero: + raise ValueError('numeric_range() arg 3 must not be zero') + self._growing = self._step > self._zero + self._init_len() + + def __bool__(self): + if self._growing: + return self._start < self._stop + else: + return self._start > self._stop + + def __contains__(self, elem): + if self._growing: + if self._start <= elem < self._stop: + return (elem - self._start) % self._step == self._zero + else: + if self._start >= elem > self._stop: + return (self._start - elem) % (-self._step) == self._zero + + return False + + def __eq__(self, other): + if isinstance(other, numeric_range): + empty_self = not bool(self) + empty_other = not bool(other) + if empty_self or empty_other: + return empty_self and empty_other # True if both empty + else: + return ( + self._start == other._start + and self._step == other._step + and self._get_by_index(-1) == other._get_by_index(-1) + ) + else: + return False + + def __getitem__(self, key): + if isinstance(key, int): + return self._get_by_index(key) + elif isinstance(key, slice): + step = self._step if key.step is None else key.step * self._step + + if key.start is None or key.start <= -self._len: + start = self._start + elif key.start >= self._len: + start = self._stop + else: # -self._len < key.start < self._len + start = self._get_by_index(key.start) + + if key.stop is None or key.stop >= self._len: + stop = self._stop + elif key.stop <= -self._len: + stop = self._start + else: # -self._len < key.stop < self._len + stop = self._get_by_index(key.stop) + + return numeric_range(start, stop, step) + else: + raise TypeError( + 'numeric range indices must be ' + 'integers or slices, not {}'.format(type(key).__name__) + ) + + def __hash__(self): + if self: + return hash((self._start, self._get_by_index(-1), self._step)) + else: + return self._EMPTY_HASH + + def __iter__(self): + values = (self._start + (n * self._step) for n in count()) + if self._growing: + return takewhile(partial(gt, self._stop), values) + else: + return takewhile(partial(lt, self._stop), values) + + def __len__(self): + return self._len + + def _init_len(self): + if self._growing: + start = self._start + stop = self._stop + step = self._step + else: + start = self._stop + stop = self._start + step = -self._step + distance = stop - start + if distance <= self._zero: + self._len = 0 + else: # distance > 0 and step > 0: regular euclidean division + q, r = divmod(distance, step) + self._len = int(q) + int(r != self._zero) + + def __reduce__(self): + return numeric_range, (self._start, self._stop, self._step) + + def __repr__(self): + if self._step == 1: + return "numeric_range({}, {})".format( + repr(self._start), repr(self._stop) + ) + else: + return "numeric_range({}, {}, {})".format( + repr(self._start), repr(self._stop), repr(self._step) + ) + + def __reversed__(self): + return iter( + numeric_range( + self._get_by_index(-1), self._start - self._step, -self._step + ) + ) + + def count(self, value): + return int(value in self) + + def index(self, value): + if self._growing: + if self._start <= value < self._stop: + q, r = divmod(value - self._start, self._step) + if r == self._zero: + return int(q) + else: + if self._start >= value > self._stop: + q, r = divmod(self._start - value, -self._step) + if r == self._zero: + return int(q) + + raise ValueError("{} is not in numeric range".format(value)) + + def _get_by_index(self, i): + if i < 0: + i += self._len + if i < 0 or i >= self._len: + raise IndexError("numeric range object index out of range") + return self._start + i * self._step + + +def count_cycle(iterable, n=None): + """Cycle through the items from *iterable* up to *n* times, yielding + the number of completed cycles along with each item. If *n* is omitted the + process repeats indefinitely. + + >>> list(count_cycle('AB', 3)) + [(0, 'A'), (0, 'B'), (1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')] + + """ + iterable = tuple(iterable) + if not iterable: + return iter(()) + counter = count() if n is None else range(n) + return ((i, item) for i in counter for item in iterable) + + +def mark_ends(iterable): + """Yield 3-tuples of the form ``(is_first, is_last, item)``. + + >>> list(mark_ends('ABC')) + [(True, False, 'A'), (False, False, 'B'), (False, True, 'C')] + + Use this when looping over an iterable to take special action on its first + and/or last items: + + >>> iterable = ['Header', 100, 200, 'Footer'] + >>> total = 0 + >>> for is_first, is_last, item in mark_ends(iterable): + ... if is_first: + ... continue # Skip the header + ... if is_last: + ... continue # Skip the footer + ... total += item + >>> print(total) + 300 + """ + it = iter(iterable) + + try: + b = next(it) + except StopIteration: + return + + try: + for i in count(): + a = b + b = next(it) + yield i == 0, False, a + + except StopIteration: + yield i == 0, True, a + + +def locate(iterable, pred=bool, window_size=None): + """Yield the index of each item in *iterable* for which *pred* returns + ``True``. + + *pred* defaults to :func:`bool`, which will select truthy items: + + >>> list(locate([0, 1, 1, 0, 1, 0, 0])) + [1, 2, 4] + + Set *pred* to a custom function to, e.g., find the indexes for a particular + item. + + >>> list(locate(['a', 'b', 'c', 'b'], lambda x: x == 'b')) + [1, 3] + + If *window_size* is given, then the *pred* function will be called with + that many items. This enables searching for sub-sequences: + + >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] + >>> pred = lambda *args: args == (1, 2, 3) + >>> list(locate(iterable, pred=pred, window_size=3)) + [1, 5, 9] + + Use with :func:`seekable` to find indexes and then retrieve the associated + items: + + >>> from itertools import count + >>> from more_itertools import seekable + >>> source = (3 * n + 1 if (n % 2) else n // 2 for n in count()) + >>> it = seekable(source) + >>> pred = lambda x: x > 100 + >>> indexes = locate(it, pred=pred) + >>> i = next(indexes) + >>> it.seek(i) + >>> next(it) + 106 + + """ + if window_size is None: + return compress(count(), map(pred, iterable)) + + if window_size < 1: + raise ValueError('window size must be at least 1') + + it = windowed(iterable, window_size, fillvalue=_marker) + return compress(count(), starmap(pred, it)) + + +def lstrip(iterable, pred): + """Yield the items from *iterable*, but strip any from the beginning + for which *pred* returns ``True``. + + For example, to remove a set of items from the start of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(lstrip(iterable, pred)) + [1, 2, None, 3, False, None] + + This function is analogous to to :func:`str.lstrip`, and is essentially + an wrapper for :func:`itertools.dropwhile`. + + """ + return dropwhile(pred, iterable) + + +def rstrip(iterable, pred): + """Yield the items from *iterable*, but strip any from the end + for which *pred* returns ``True``. + + For example, to remove a set of items from the end of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(rstrip(iterable, pred)) + [None, False, None, 1, 2, None, 3] + + This function is analogous to :func:`str.rstrip`. + + """ + cache = [] + cache_append = cache.append + cache_clear = cache.clear + for x in iterable: + if pred(x): + cache_append(x) + else: + yield from cache + cache_clear() + yield x + + +def strip(iterable, pred): + """Yield the items from *iterable*, but strip any from the + beginning and end for which *pred* returns ``True``. + + For example, to remove a set of items from both ends of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(strip(iterable, pred)) + [1, 2, None, 3] + + This function is analogous to :func:`str.strip`. + + """ + return rstrip(lstrip(iterable, pred), pred) + + +class islice_extended: + """An extension of :func:`itertools.islice` that supports negative values + for *stop*, *start*, and *step*. + + >>> iterable = iter('abcdefgh') + >>> list(islice_extended(iterable, -4, -1)) + ['e', 'f', 'g'] + + Slices with negative values require some caching of *iterable*, but this + function takes care to minimize the amount of memory required. + + For example, you can use a negative step with an infinite iterator: + + >>> from itertools import count + >>> list(islice_extended(count(), 110, 99, -2)) + [110, 108, 106, 104, 102, 100] + + You can also use slice notation directly: + + >>> iterable = map(str, count()) + >>> it = islice_extended(iterable)[10:20:2] + >>> list(it) + ['10', '12', '14', '16', '18'] + + """ + + def __init__(self, iterable, *args): + it = iter(iterable) + if args: + self._iterable = _islice_helper(it, slice(*args)) + else: + self._iterable = it + + def __iter__(self): + return self + + def __next__(self): + return next(self._iterable) + + def __getitem__(self, key): + if isinstance(key, slice): + return islice_extended(_islice_helper(self._iterable, key)) + + raise TypeError('islice_extended.__getitem__ argument must be a slice') + + +def _islice_helper(it, s): + start = s.start + stop = s.stop + if s.step == 0: + raise ValueError('step argument must be a non-zero integer or None.') + step = s.step or 1 + + if step > 0: + start = 0 if (start is None) else start + + if start < 0: + # Consume all but the last -start items + cache = deque(enumerate(it, 1), maxlen=-start) + len_iter = cache[-1][0] if cache else 0 + + # Adjust start to be positive + i = max(len_iter + start, 0) + + # Adjust stop to be positive + if stop is None: + j = len_iter + elif stop >= 0: + j = min(stop, len_iter) + else: + j = max(len_iter + stop, 0) + + # Slice the cache + n = j - i + if n <= 0: + return + + for index, item in islice(cache, 0, n, step): + yield item + elif (stop is not None) and (stop < 0): + # Advance to the start position + next(islice(it, start, start), None) + + # When stop is negative, we have to carry -stop items while + # iterating + cache = deque(islice(it, -stop), maxlen=-stop) + + for index, item in enumerate(it): + cached_item = cache.popleft() + if index % step == 0: + yield cached_item + cache.append(item) + else: + # When both start and stop are positive we have the normal case + yield from islice(it, start, stop, step) + else: + start = -1 if (start is None) else start + + if (stop is not None) and (stop < 0): + # Consume all but the last items + n = -stop - 1 + cache = deque(enumerate(it, 1), maxlen=n) + len_iter = cache[-1][0] if cache else 0 + + # If start and stop are both negative they are comparable and + # we can just slice. Otherwise we can adjust start to be negative + # and then slice. + if start < 0: + i, j = start, stop + else: + i, j = min(start - len_iter, -1), None + + for index, item in list(cache)[i:j:step]: + yield item + else: + # Advance to the stop position + if stop is not None: + m = stop + 1 + next(islice(it, m, m), None) + + # stop is positive, so if start is negative they are not comparable + # and we need the rest of the items. + if start < 0: + i = start + n = None + # stop is None and start is positive, so we just need items up to + # the start index. + elif stop is None: + i = None + n = start + 1 + # Both stop and start are positive, so they are comparable. + else: + i = None + n = start - stop + if n <= 0: + return + + cache = list(islice(it, n)) + + yield from cache[i::step] + + +def always_reversible(iterable): + """An extension of :func:`reversed` that supports all iterables, not + just those which implement the ``Reversible`` or ``Sequence`` protocols. + + >>> print(*always_reversible(x for x in range(3))) + 2 1 0 + + If the iterable is already reversible, this function returns the + result of :func:`reversed()`. If the iterable is not reversible, + this function will cache the remaining items in the iterable and + yield them in reverse order, which may require significant storage. + """ + try: + return reversed(iterable) + except TypeError: + return reversed(list(iterable)) + + +def consecutive_groups(iterable, ordering=lambda x: x): + """Yield groups of consecutive items using :func:`itertools.groupby`. + The *ordering* function determines whether two items are adjacent by + returning their position. + + By default, the ordering function is the identity function. This is + suitable for finding runs of numbers: + + >>> iterable = [1, 10, 11, 12, 20, 30, 31, 32, 33, 40] + >>> for group in consecutive_groups(iterable): + ... print(list(group)) + [1] + [10, 11, 12] + [20] + [30, 31, 32, 33] + [40] + + For finding runs of adjacent letters, try using the :meth:`index` method + of a string of letters: + + >>> from string import ascii_lowercase + >>> iterable = 'abcdfgilmnop' + >>> ordering = ascii_lowercase.index + >>> for group in consecutive_groups(iterable, ordering): + ... print(list(group)) + ['a', 'b', 'c', 'd'] + ['f', 'g'] + ['i'] + ['l', 'm', 'n', 'o', 'p'] + + Each group of consecutive items is an iterator that shares it source with + *iterable*. When an an output group is advanced, the previous group is + no longer available unless its elements are copied (e.g., into a ``list``). + + >>> iterable = [1, 2, 11, 12, 21, 22] + >>> saved_groups = [] + >>> for group in consecutive_groups(iterable): + ... saved_groups.append(list(group)) # Copy group elements + >>> saved_groups + [[1, 2], [11, 12], [21, 22]] + + """ + for k, g in groupby( + enumerate(iterable), key=lambda x: x[0] - ordering(x[1]) + ): + yield map(itemgetter(1), g) + + +def difference(iterable, func=sub, *, initial=None): + """This function is the inverse of :func:`itertools.accumulate`. By default + it will compute the first difference of *iterable* using + :func:`operator.sub`: + + >>> from itertools import accumulate + >>> iterable = accumulate([0, 1, 2, 3, 4]) # produces 0, 1, 3, 6, 10 + >>> list(difference(iterable)) + [0, 1, 2, 3, 4] + + *func* defaults to :func:`operator.sub`, but other functions can be + specified. They will be applied as follows:: + + A, B, C, D, ... --> A, func(B, A), func(C, B), func(D, C), ... + + For example, to do progressive division: + + >>> iterable = [1, 2, 6, 24, 120] + >>> func = lambda x, y: x // y + >>> list(difference(iterable, func)) + [1, 2, 3, 4, 5] + + If the *initial* keyword is set, the first element will be skipped when + computing successive differences. + + >>> it = [10, 11, 13, 16] # from accumulate([1, 2, 3], initial=10) + >>> list(difference(it, initial=10)) + [1, 2, 3] + + """ + a, b = tee(iterable) + try: + first = [next(b)] + except StopIteration: + return iter([]) + + if initial is not None: + first = [] + + return chain(first, starmap(func, zip(b, a))) + + +class SequenceView(Sequence): + """Return a read-only view of the sequence object *target*. + + :class:`SequenceView` objects are analogous to Python's built-in + "dictionary view" types. They provide a dynamic view of a sequence's items, + meaning that when the sequence updates, so does the view. + + >>> seq = ['0', '1', '2'] + >>> view = SequenceView(seq) + >>> view + SequenceView(['0', '1', '2']) + >>> seq.append('3') + >>> view + SequenceView(['0', '1', '2', '3']) + + Sequence views support indexing, slicing, and length queries. They act + like the underlying sequence, except they don't allow assignment: + + >>> view[1] + '1' + >>> view[1:-1] + ['1', '2'] + >>> len(view) + 4 + + Sequence views are useful as an alternative to copying, as they don't + require (much) extra storage. + + """ + + def __init__(self, target): + if not isinstance(target, Sequence): + raise TypeError + self._target = target + + def __getitem__(self, index): + return self._target[index] + + def __len__(self): + return len(self._target) + + def __repr__(self): + return '{}({})'.format(self.__class__.__name__, repr(self._target)) + + +class seekable: + """Wrap an iterator to allow for seeking backward and forward. This + progressively caches the items in the source iterable so they can be + re-visited. + + Call :meth:`seek` with an index to seek to that position in the source + iterable. + + To "reset" an iterator, seek to ``0``: + + >>> from itertools import count + >>> it = seekable((str(n) for n in count())) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> it.seek(0) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> next(it) + '3' + + You can also seek forward: + + >>> it = seekable((str(n) for n in range(20))) + >>> it.seek(10) + >>> next(it) + '10' + >>> it.seek(20) # Seeking past the end of the source isn't a problem + >>> list(it) + [] + >>> it.seek(0) # Resetting works even after hitting the end + >>> next(it), next(it), next(it) + ('0', '1', '2') + + Call :meth:`peek` to look ahead one item without advancing the iterator: + + >>> it = seekable('1234') + >>> it.peek() + '1' + >>> list(it) + ['1', '2', '3', '4'] + >>> it.peek(default='empty') + 'empty' + + Before the iterator is at its end, calling :func:`bool` on it will return + ``True``. After it will return ``False``: + + >>> it = seekable('5678') + >>> bool(it) + True + >>> list(it) + ['5', '6', '7', '8'] + >>> bool(it) + False + + You may view the contents of the cache with the :meth:`elements` method. + That returns a :class:`SequenceView`, a view that updates automatically: + + >>> it = seekable((str(n) for n in range(10))) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> elements = it.elements() + >>> elements + SequenceView(['0', '1', '2']) + >>> next(it) + '3' + >>> elements + SequenceView(['0', '1', '2', '3']) + + By default, the cache grows as the source iterable progresses, so beware of + wrapping very large or infinite iterables. Supply *maxlen* to limit the + size of the cache (this of course limits how far back you can seek). + + >>> from itertools import count + >>> it = seekable((str(n) for n in count()), maxlen=2) + >>> next(it), next(it), next(it), next(it) + ('0', '1', '2', '3') + >>> list(it.elements()) + ['2', '3'] + >>> it.seek(0) + >>> next(it), next(it), next(it), next(it) + ('2', '3', '4', '5') + >>> next(it) + '6' + + """ + + def __init__(self, iterable, maxlen=None): + self._source = iter(iterable) + if maxlen is None: + self._cache = [] + else: + self._cache = deque([], maxlen) + self._index = None + + def __iter__(self): + return self + + def __next__(self): + if self._index is not None: + try: + item = self._cache[self._index] + except IndexError: + self._index = None + else: + self._index += 1 + return item + + item = next(self._source) + self._cache.append(item) + return item + + def __bool__(self): + try: + self.peek() + except StopIteration: + return False + return True + + def peek(self, default=_marker): + try: + peeked = next(self) + except StopIteration: + if default is _marker: + raise + return default + if self._index is None: + self._index = len(self._cache) + self._index -= 1 + return peeked + + def elements(self): + return SequenceView(self._cache) + + def seek(self, index): + self._index = index + remainder = index - len(self._cache) + if remainder > 0: + consume(self, remainder) + + +class run_length: + """ + :func:`run_length.encode` compresses an iterable with run-length encoding. + It yields groups of repeated items with the count of how many times they + were repeated: + + >>> uncompressed = 'abbcccdddd' + >>> list(run_length.encode(uncompressed)) + [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + + :func:`run_length.decode` decompresses an iterable that was previously + compressed with run-length encoding. It yields the items of the + decompressed iterable: + + >>> compressed = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + >>> list(run_length.decode(compressed)) + ['a', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd', 'd'] + + """ + + @staticmethod + def encode(iterable): + return ((k, ilen(g)) for k, g in groupby(iterable)) + + @staticmethod + def decode(iterable): + return chain.from_iterable(repeat(k, n) for k, n in iterable) + + +def exactly_n(iterable, n, predicate=bool): + """Return ``True`` if exactly ``n`` items in the iterable are ``True`` + according to the *predicate* function. + + >>> exactly_n([True, True, False], 2) + True + >>> exactly_n([True, True, False], 1) + False + >>> exactly_n([0, 1, 2, 3, 4, 5], 3, lambda x: x < 3) + True + + The iterable will be advanced until ``n + 1`` truthy items are encountered, + so avoid calling it on infinite iterables. + + """ + return len(take(n + 1, filter(predicate, iterable))) == n + + +def circular_shifts(iterable): + """Return a list of circular shifts of *iterable*. + + >>> circular_shifts(range(4)) + [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)] + """ + lst = list(iterable) + return take(len(lst), windowed(cycle(lst), len(lst))) + + +def make_decorator(wrapping_func, result_index=0): + """Return a decorator version of *wrapping_func*, which is a function that + modifies an iterable. *result_index* is the position in that function's + signature where the iterable goes. + + This lets you use itertools on the "production end," i.e. at function + definition. This can augment what the function returns without changing the + function's code. + + For example, to produce a decorator version of :func:`chunked`: + + >>> from more_itertools import chunked + >>> chunker = make_decorator(chunked, result_index=0) + >>> @chunker(3) + ... def iter_range(n): + ... return iter(range(n)) + ... + >>> list(iter_range(9)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8]] + + To only allow truthy items to be returned: + + >>> truth_serum = make_decorator(filter, result_index=1) + >>> @truth_serum(bool) + ... def boolean_test(): + ... return [0, 1, '', ' ', False, True] + ... + >>> list(boolean_test()) + [1, ' ', True] + + The :func:`peekable` and :func:`seekable` wrappers make for practical + decorators: + + >>> from more_itertools import peekable + >>> peekable_function = make_decorator(peekable) + >>> @peekable_function() + ... def str_range(*args): + ... return (str(x) for x in range(*args)) + ... + >>> it = str_range(1, 20, 2) + >>> next(it), next(it), next(it) + ('1', '3', '5') + >>> it.peek() + '7' + >>> next(it) + '7' + + """ + # See https://sites.google.com/site/bbayles/index/decorator_factory for + # notes on how this works. + def decorator(*wrapping_args, **wrapping_kwargs): + def outer_wrapper(f): + def inner_wrapper(*args, **kwargs): + result = f(*args, **kwargs) + wrapping_args_ = list(wrapping_args) + wrapping_args_.insert(result_index, result) + return wrapping_func(*wrapping_args_, **wrapping_kwargs) + + return inner_wrapper + + return outer_wrapper + + return decorator + + +def map_reduce(iterable, keyfunc, valuefunc=None, reducefunc=None): + """Return a dictionary that maps the items in *iterable* to categories + defined by *keyfunc*, transforms them with *valuefunc*, and + then summarizes them by category with *reducefunc*. + + *valuefunc* defaults to the identity function if it is unspecified. + If *reducefunc* is unspecified, no summarization takes place: + + >>> keyfunc = lambda x: x.upper() + >>> result = map_reduce('abbccc', keyfunc) + >>> sorted(result.items()) + [('A', ['a']), ('B', ['b', 'b']), ('C', ['c', 'c', 'c'])] + + Specifying *valuefunc* transforms the categorized items: + + >>> keyfunc = lambda x: x.upper() + >>> valuefunc = lambda x: 1 + >>> result = map_reduce('abbccc', keyfunc, valuefunc) + >>> sorted(result.items()) + [('A', [1]), ('B', [1, 1]), ('C', [1, 1, 1])] + + Specifying *reducefunc* summarizes the categorized items: + + >>> keyfunc = lambda x: x.upper() + >>> valuefunc = lambda x: 1 + >>> reducefunc = sum + >>> result = map_reduce('abbccc', keyfunc, valuefunc, reducefunc) + >>> sorted(result.items()) + [('A', 1), ('B', 2), ('C', 3)] + + You may want to filter the input iterable before applying the map/reduce + procedure: + + >>> all_items = range(30) + >>> items = [x for x in all_items if 10 <= x <= 20] # Filter + >>> keyfunc = lambda x: x % 2 # Evens map to 0; odds to 1 + >>> categories = map_reduce(items, keyfunc=keyfunc) + >>> sorted(categories.items()) + [(0, [10, 12, 14, 16, 18, 20]), (1, [11, 13, 15, 17, 19])] + >>> summaries = map_reduce(items, keyfunc=keyfunc, reducefunc=sum) + >>> sorted(summaries.items()) + [(0, 90), (1, 75)] + + Note that all items in the iterable are gathered into a list before the + summarization step, which may require significant storage. + + The returned object is a :obj:`collections.defaultdict` with the + ``default_factory`` set to ``None``, such that it behaves like a normal + dictionary. + + """ + valuefunc = (lambda x: x) if (valuefunc is None) else valuefunc + + ret = defaultdict(list) + for item in iterable: + key = keyfunc(item) + value = valuefunc(item) + ret[key].append(value) + + if reducefunc is not None: + for key, value_list in ret.items(): + ret[key] = reducefunc(value_list) + + ret.default_factory = None + return ret + + +def rlocate(iterable, pred=bool, window_size=None): + """Yield the index of each item in *iterable* for which *pred* returns + ``True``, starting from the right and moving left. + + *pred* defaults to :func:`bool`, which will select truthy items: + + >>> list(rlocate([0, 1, 1, 0, 1, 0, 0])) # Truthy at 1, 2, and 4 + [4, 2, 1] + + Set *pred* to a custom function to, e.g., find the indexes for a particular + item: + + >>> iterable = iter('abcb') + >>> pred = lambda x: x == 'b' + >>> list(rlocate(iterable, pred)) + [3, 1] + + If *window_size* is given, then the *pred* function will be called with + that many items. This enables searching for sub-sequences: + + >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] + >>> pred = lambda *args: args == (1, 2, 3) + >>> list(rlocate(iterable, pred=pred, window_size=3)) + [9, 5, 1] + + Beware, this function won't return anything for infinite iterables. + If *iterable* is reversible, ``rlocate`` will reverse it and search from + the right. Otherwise, it will search from the left and return the results + in reverse order. + + See :func:`locate` to for other example applications. + + """ + if window_size is None: + try: + len_iter = len(iterable) + return (len_iter - i - 1 for i in locate(reversed(iterable), pred)) + except TypeError: + pass + + return reversed(list(locate(iterable, pred, window_size))) + + +def replace(iterable, pred, substitutes, count=None, window_size=1): + """Yield the items from *iterable*, replacing the items for which *pred* + returns ``True`` with the items from the iterable *substitutes*. + + >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1] + >>> pred = lambda x: x == 0 + >>> substitutes = (2, 3) + >>> list(replace(iterable, pred, substitutes)) + [1, 1, 2, 3, 1, 1, 2, 3, 1, 1] + + If *count* is given, the number of replacements will be limited: + + >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1, 0] + >>> pred = lambda x: x == 0 + >>> substitutes = [None] + >>> list(replace(iterable, pred, substitutes, count=2)) + [1, 1, None, 1, 1, None, 1, 1, 0] + + Use *window_size* to control the number of items passed as arguments to + *pred*. This allows for locating and replacing subsequences. + + >>> iterable = [0, 1, 2, 5, 0, 1, 2, 5] + >>> window_size = 3 + >>> pred = lambda *args: args == (0, 1, 2) # 3 items passed to pred + >>> substitutes = [3, 4] # Splice in these items + >>> list(replace(iterable, pred, substitutes, window_size=window_size)) + [3, 4, 5, 3, 4, 5] + + """ + if window_size < 1: + raise ValueError('window_size must be at least 1') + + # Save the substitutes iterable, since it's used more than once + substitutes = tuple(substitutes) + + # Add padding such that the number of windows matches the length of the + # iterable + it = chain(iterable, [_marker] * (window_size - 1)) + windows = windowed(it, window_size) + + n = 0 + for w in windows: + # If the current window matches our predicate (and we haven't hit + # our maximum number of replacements), splice in the substitutes + # and then consume the following windows that overlap with this one. + # For example, if the iterable is (0, 1, 2, 3, 4...) + # and the window size is 2, we have (0, 1), (1, 2), (2, 3)... + # If the predicate matches on (0, 1), we need to zap (0, 1) and (1, 2) + if pred(*w): + if (count is None) or (n < count): + n += 1 + yield from substitutes + consume(windows, window_size - 1) + continue + + # If there was no match (or we've reached the replacement limit), + # yield the first item from the window. + if w and (w[0] is not _marker): + yield w[0] + + +def partitions(iterable): + """Yield all possible order-preserving partitions of *iterable*. + + >>> iterable = 'abc' + >>> for part in partitions(iterable): + ... print([''.join(p) for p in part]) + ['abc'] + ['a', 'bc'] + ['ab', 'c'] + ['a', 'b', 'c'] + + This is unrelated to :func:`partition`. + + """ + sequence = list(iterable) + n = len(sequence) + for i in powerset(range(1, n)): + yield [sequence[i:j] for i, j in zip((0,) + i, i + (n,))] + + +def set_partitions(iterable, k=None): + """ + Yield the set partitions of *iterable* into *k* parts. Set partitions are + not order-preserving. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable, 2): + ... print([''.join(p) for p in part]) + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + + + If *k* is not given, every set partition is generated. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable): + ... print([''.join(p) for p in part]) + ['abc'] + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + ['a', 'b', 'c'] + + """ + L = list(iterable) + n = len(L) + if k is not None: + if k < 1: + raise ValueError( + "Can't partition in a negative or zero number of groups" + ) + elif k > n: + return + + def set_partitions_helper(L, k): + n = len(L) + if k == 1: + yield [L] + elif n == k: + yield [[s] for s in L] + else: + e, *M = L + for p in set_partitions_helper(M, k - 1): + yield [[e], *p] + for p in set_partitions_helper(M, k): + for i in range(len(p)): + yield p[:i] + [[e] + p[i]] + p[i + 1 :] + + if k is None: + for k in range(1, n + 1): + yield from set_partitions_helper(L, k) + else: + yield from set_partitions_helper(L, k) + + +class time_limited: + """ + Yield items from *iterable* until *limit_seconds* have passed. + If the time limit expires before all items have been yielded, the + ``timed_out`` parameter will be set to ``True``. + + >>> from time import sleep + >>> def generator(): + ... yield 1 + ... yield 2 + ... sleep(0.2) + ... yield 3 + >>> iterable = time_limited(0.1, generator()) + >>> list(iterable) + [1, 2] + >>> iterable.timed_out + True + + Note that the time is checked before each item is yielded, and iteration + stops if the time elapsed is greater than *limit_seconds*. If your time + limit is 1 second, but it takes 2 seconds to generate the first item from + the iterable, the function will run for 2 seconds and not yield anything. + + """ + + def __init__(self, limit_seconds, iterable): + if limit_seconds < 0: + raise ValueError('limit_seconds must be positive') + self.limit_seconds = limit_seconds + self._iterable = iter(iterable) + self._start_time = monotonic() + self.timed_out = False + + def __iter__(self): + return self + + def __next__(self): + item = next(self._iterable) + if monotonic() - self._start_time > self.limit_seconds: + self.timed_out = True + raise StopIteration + + return item + + +def only(iterable, default=None, too_long=None): + """If *iterable* has only one item, return it. + If it has zero items, return *default*. + If it has more than one item, raise the exception given by *too_long*, + which is ``ValueError`` by default. + + >>> only([], default='missing') + 'missing' + >>> only([1]) + 1 + >>> only([1, 2]) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Expected exactly one item in iterable, but got 1, 2, + and perhaps more.' + >>> only([1, 2], too_long=TypeError) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + TypeError + + Note that :func:`only` attempts to advance *iterable* twice to ensure there + is only one item. See :func:`spy` or :func:`peekable` to check + iterable contents less destructively. + """ + it = iter(iterable) + first_value = next(it, default) + + try: + second_value = next(it) + except StopIteration: + pass + else: + msg = ( + 'Expected exactly one item in iterable, but got {!r}, {!r}, ' + 'and perhaps more.'.format(first_value, second_value) + ) + raise too_long or ValueError(msg) + + return first_value + + +def ichunked(iterable, n): + """Break *iterable* into sub-iterables with *n* elements each. + :func:`ichunked` is like :func:`chunked`, but it yields iterables + instead of lists. + + If the sub-iterables are read in order, the elements of *iterable* + won't be stored in memory. + If they are read out of order, :func:`itertools.tee` is used to cache + elements as necessary. + + >>> from itertools import count + >>> all_chunks = ichunked(count(), 4) + >>> c_1, c_2, c_3 = next(all_chunks), next(all_chunks), next(all_chunks) + >>> list(c_2) # c_1's elements have been cached; c_3's haven't been + [4, 5, 6, 7] + >>> list(c_1) + [0, 1, 2, 3] + >>> list(c_3) + [8, 9, 10, 11] + + """ + source = iter(iterable) + + while True: + # Check to see whether we're at the end of the source iterable + item = next(source, _marker) + if item is _marker: + return + + # Clone the source and yield an n-length slice + source, it = tee(chain([item], source)) + yield islice(it, n) + + # Advance the source iterable + consume(source, n) + + +def distinct_combinations(iterable, r): + """Yield the distinct combinations of *r* items taken from *iterable*. + + >>> list(distinct_combinations([0, 0, 1], 2)) + [(0, 0), (0, 1)] + + Equivalent to ``set(combinations(iterable))``, except duplicates are not + generated and thrown away. For larger input sequences this is much more + efficient. + + """ + if r < 0: + raise ValueError('r must be non-negative') + elif r == 0: + yield () + return + pool = tuple(iterable) + generators = [unique_everseen(enumerate(pool), key=itemgetter(1))] + current_combo = [None] * r + level = 0 + while generators: + try: + cur_idx, p = next(generators[-1]) + except StopIteration: + generators.pop() + level -= 1 + continue + current_combo[level] = p + if level + 1 == r: + yield tuple(current_combo) + else: + generators.append( + unique_everseen( + enumerate(pool[cur_idx + 1 :], cur_idx + 1), + key=itemgetter(1), + ) + ) + level += 1 + + +def filter_except(validator, iterable, *exceptions): + """Yield the items from *iterable* for which the *validator* function does + not raise one of the specified *exceptions*. + + *validator* is called for each item in *iterable*. + It should be a function that accepts one argument and raises an exception + if that item is not valid. + + >>> iterable = ['1', '2', 'three', '4', None] + >>> list(filter_except(int, iterable, ValueError, TypeError)) + ['1', '2', '4'] + + If an exception other than one given by *exceptions* is raised by + *validator*, it is raised like normal. + """ + for item in iterable: + try: + validator(item) + except exceptions: + pass + else: + yield item + + +def map_except(function, iterable, *exceptions): + """Transform each item from *iterable* with *function* and yield the + result, unless *function* raises one of the specified *exceptions*. + + *function* is called to transform each item in *iterable*. + It should be a accept one argument. + + >>> iterable = ['1', '2', 'three', '4', None] + >>> list(map_except(int, iterable, ValueError, TypeError)) + [1, 2, 4] + + If an exception other than one given by *exceptions* is raised by + *function*, it is raised like normal. + """ + for item in iterable: + try: + yield function(item) + except exceptions: + pass + + +def _sample_unweighted(iterable, k): + # Implementation of "Algorithm L" from the 1994 paper by Kim-Hung Li: + # "Reservoir-Sampling Algorithms of Time Complexity O(n(1+log(N/n)))". + + # Fill up the reservoir (collection of samples) with the first `k` samples + reservoir = take(k, iterable) + + # Generate random number that's the largest in a sample of k U(0,1) numbers + # Largest order statistic: https://en.wikipedia.org/wiki/Order_statistic + W = exp(log(random()) / k) + + # The number of elements to skip before changing the reservoir is a random + # number with a geometric distribution. Sample it using random() and logs. + next_index = k + floor(log(random()) / log(1 - W)) + + for index, element in enumerate(iterable, k): + + if index == next_index: + reservoir[randrange(k)] = element + # The new W is the largest in a sample of k U(0, `old_W`) numbers + W *= exp(log(random()) / k) + next_index += floor(log(random()) / log(1 - W)) + 1 + + return reservoir + + +def _sample_weighted(iterable, k, weights): + # Implementation of "A-ExpJ" from the 2006 paper by Efraimidis et al. : + # "Weighted random sampling with a reservoir". + + # Log-transform for numerical stability for weights that are small/large + weight_keys = (log(random()) / weight for weight in weights) + + # Fill up the reservoir (collection of samples) with the first `k` + # weight-keys and elements, then heapify the list. + reservoir = take(k, zip(weight_keys, iterable)) + heapify(reservoir) + + # The number of jumps before changing the reservoir is a random variable + # with an exponential distribution. Sample it using random() and logs. + smallest_weight_key, _ = reservoir[0] + weights_to_skip = log(random()) / smallest_weight_key + + for weight, element in zip(weights, iterable): + if weight >= weights_to_skip: + # The notation here is consistent with the paper, but we store + # the weight-keys in log-space for better numerical stability. + smallest_weight_key, _ = reservoir[0] + t_w = exp(weight * smallest_weight_key) + r_2 = uniform(t_w, 1) # generate U(t_w, 1) + weight_key = log(r_2) / weight + heapreplace(reservoir, (weight_key, element)) + smallest_weight_key, _ = reservoir[0] + weights_to_skip = log(random()) / smallest_weight_key + else: + weights_to_skip -= weight + + # Equivalent to [element for weight_key, element in sorted(reservoir)] + return [heappop(reservoir)[1] for _ in range(k)] + + +def sample(iterable, k, weights=None): + """Return a *k*-length list of elements chosen (without replacement) + from the *iterable*. Like :func:`random.sample`, but works on iterables + of unknown length. + + >>> iterable = range(100) + >>> sample(iterable, 5) # doctest: +SKIP + [81, 60, 96, 16, 4] + + An iterable with *weights* may also be given: + + >>> iterable = range(100) + >>> weights = (i * i + 1 for i in range(100)) + >>> sampled = sample(iterable, 5, weights=weights) # doctest: +SKIP + [79, 67, 74, 66, 78] + + The algorithm can also be used to generate weighted random permutations. + The relative weight of each item determines the probability that it + appears late in the permutation. + + >>> data = "abcdefgh" + >>> weights = range(1, len(data) + 1) + >>> sample(data, k=len(data), weights=weights) # doctest: +SKIP + ['c', 'a', 'b', 'e', 'g', 'd', 'h', 'f'] + """ + if k == 0: + return [] + + iterable = iter(iterable) + if weights is None: + return _sample_unweighted(iterable, k) + else: + weights = iter(weights) + return _sample_weighted(iterable, k, weights) + + +def is_sorted(iterable, key=None, reverse=False): + """Returns ``True`` if the items of iterable are in sorted order, and + ``False`` otherwise. *key* and *reverse* have the same meaning that they do + in the built-in :func:`sorted` function. + + >>> is_sorted(['1', '2', '3', '4', '5'], key=int) + True + >>> is_sorted([5, 4, 3, 1, 2], reverse=True) + False + + The function returns ``False`` after encountering the first out-of-order + item. If there are no out-of-order items, the iterable is exhausted. + """ + + compare = lt if reverse else gt + it = iterable if (key is None) else map(key, iterable) + return not any(starmap(compare, pairwise(it))) + + +class AbortThread(BaseException): + pass + + +class callback_iter: + """Convert a function that uses callbacks to an iterator. + + Let *func* be a function that takes a `callback` keyword argument. + For example: + + >>> def func(callback=None): + ... for i, c in [(1, 'a'), (2, 'b'), (3, 'c')]: + ... if callback: + ... callback(i, c) + ... return 4 + + + Use ``with callback_iter(func)`` to get an iterator over the parameters + that are delivered to the callback. + + >>> with callback_iter(func) as it: + ... for args, kwargs in it: + ... print(args) + (1, 'a') + (2, 'b') + (3, 'c') + + The function will be called in a background thread. The ``done`` property + indicates whether it has completed execution. + + >>> it.done + True + + If it completes successfully, its return value will be available + in the ``result`` property. + + >>> it.result + 4 + + Notes: + + * If the function uses some keyword argument besides ``callback``, supply + *callback_kwd*. + * If it finished executing, but raised an exception, accessing the + ``result`` property will raise the same exception. + * If it hasn't finished executing, accessing the ``result`` + property from within the ``with`` block will raise ``RuntimeError``. + * If it hasn't finished executing, accessing the ``result`` property from + outside the ``with`` block will raise a + ``more_itertools.AbortThread`` exception. + * Provide *wait_seconds* to adjust how frequently the it is polled for + output. + + """ + + def __init__(self, func, callback_kwd='callback', wait_seconds=0.1): + self._func = func + self._callback_kwd = callback_kwd + self._aborted = False + self._future = None + self._wait_seconds = wait_seconds + self._executor = ThreadPoolExecutor(max_workers=1) + self._iterator = self._reader() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self._aborted = True + self._executor.shutdown() + + def __iter__(self): + return self + + def __next__(self): + return next(self._iterator) + + @property + def done(self): + if self._future is None: + return False + return self._future.done() + + @property + def result(self): + if not self.done: + raise RuntimeError('Function has not yet completed') + + return self._future.result() + + def _reader(self): + q = Queue() + + def callback(*args, **kwargs): + if self._aborted: + raise AbortThread('canceled by user') + + q.put((args, kwargs)) + + self._future = self._executor.submit( + self._func, **{self._callback_kwd: callback} + ) + + while True: + try: + item = q.get(timeout=self._wait_seconds) + except Empty: + pass + else: + q.task_done() + yield item + + if self._future.done(): + break + + remaining = [] + while True: + try: + item = q.get_nowait() + except Empty: + break + else: + q.task_done() + remaining.append(item) + q.join() + yield from remaining + + +def windowed_complete(iterable, n): + """ + Yield ``(beginning, middle, end)`` tuples, where: + + * Each ``middle`` has *n* items from *iterable* + * Each ``beginning`` has the items before the ones in ``middle`` + * Each ``end`` has the items after the ones in ``middle`` + + >>> iterable = range(7) + >>> n = 3 + >>> for beginning, middle, end in windowed_complete(iterable, n): + ... print(beginning, middle, end) + () (0, 1, 2) (3, 4, 5, 6) + (0,) (1, 2, 3) (4, 5, 6) + (0, 1) (2, 3, 4) (5, 6) + (0, 1, 2) (3, 4, 5) (6,) + (0, 1, 2, 3) (4, 5, 6) () + + Note that *n* must be at least 0 and most equal to the length of + *iterable*. + + This function will exhaust the iterable and may require significant + storage. + """ + if n < 0: + raise ValueError('n must be >= 0') + + seq = tuple(iterable) + size = len(seq) + + if n > size: + raise ValueError('n must be <= len(seq)') + + for i in range(size - n + 1): + beginning = seq[:i] + middle = seq[i : i + n] + end = seq[i + n :] + yield beginning, middle, end + + +def all_unique(iterable, key=None): + """ + Returns ``True`` if all the elements of *iterable* are unique (no two + elements are equal). + + >>> all_unique('ABCB') + False + + If a *key* function is specified, it will be used to make comparisons. + + >>> all_unique('ABCb') + True + >>> all_unique('ABCb', str.lower) + False + + The function returns as soon as the first non-unique element is + encountered. Iterables with a mix of hashable and unhashable items can + be used, but the function will be slower for unhashable items. + """ + seenset = set() + seenset_add = seenset.add + seenlist = [] + seenlist_add = seenlist.append + for element in map(key, iterable) if key else iterable: + try: + if element in seenset: + return False + seenset_add(element) + except TypeError: + if element in seenlist: + return False + seenlist_add(element) + return True + + +def nth_product(index, *args): + """Equivalent to ``list(product(*args))[index]``. + + The products of *args* can be ordered lexicographically. + :func:`nth_product` computes the product at sort position *index* without + computing the previous products. + + >>> nth_product(8, range(2), range(2), range(2), range(2)) + (1, 0, 0, 0) + + ``IndexError`` will be raised if the given *index* is invalid. + """ + pools = list(map(tuple, reversed(args))) + ns = list(map(len, pools)) + + c = reduce(mul, ns) + + if index < 0: + index += c + + if not 0 <= index < c: + raise IndexError + + result = [] + for pool, n in zip(pools, ns): + result.append(pool[index % n]) + index //= n + + return tuple(reversed(result)) + + +def nth_permutation(iterable, r, index): + """Equivalent to ``list(permutations(iterable, r))[index]``` + + The subsequences of *iterable* that are of length *r* where order is + important can be ordered lexicographically. :func:`nth_permutation` + computes the subsequence at sort position *index* directly, without + computing the previous subsequences. + + >>> nth_permutation('ghijk', 2, 5) + ('h', 'i') + + ``ValueError`` will be raised If *r* is negative or greater than the length + of *iterable*. + ``IndexError`` will be raised if the given *index* is invalid. + """ + pool = list(iterable) + n = len(pool) + + if r is None or r == n: + r, c = n, factorial(n) + elif not 0 <= r < n: + raise ValueError + else: + c = factorial(n) // factorial(n - r) + + if index < 0: + index += c + + if not 0 <= index < c: + raise IndexError + + if c == 0: + return tuple() + + result = [0] * r + q = index * factorial(n) // c if r < n else index + for d in range(1, n + 1): + q, i = divmod(q, d) + if 0 <= n - d < r: + result[n - d] = i + if q == 0: + break + + return tuple(map(pool.pop, result)) + + +def value_chain(*args): + """Yield all arguments passed to the function in the same order in which + they were passed. If an argument itself is iterable then iterate over its + values. + + >>> list(value_chain(1, 2, 3, [4, 5, 6])) + [1, 2, 3, 4, 5, 6] + + Binary and text strings are not considered iterable and are emitted + as-is: + + >>> list(value_chain('12', '34', ['56', '78'])) + ['12', '34', '56', '78'] + + + Multiple levels of nesting are not flattened. + + """ + for value in args: + if isinstance(value, (str, bytes)): + yield value + continue + try: + yield from value + except TypeError: + yield value + + +def product_index(element, *args): + """Equivalent to ``list(product(*args)).index(element)`` + + The products of *args* can be ordered lexicographically. + :func:`product_index` computes the first index of *element* without + computing the previous products. + + >>> product_index([8, 2], range(10), range(5)) + 42 + + ``ValueError`` will be raised if the given *element* isn't in the product + of *args*. + """ + index = 0 + + for x, pool in zip_longest(element, args, fillvalue=_marker): + if x is _marker or pool is _marker: + raise ValueError('element is not a product of args') + + pool = tuple(pool) + index = index * len(pool) + pool.index(x) + + return index + + +def combination_index(element, iterable): + """Equivalent to ``list(combinations(iterable, r)).index(element)`` + + The subsequences of *iterable* that are of length *r* can be ordered + lexicographically. :func:`combination_index` computes the index of the + first *element*, without computing the previous combinations. + + >>> combination_index('adf', 'abcdefg') + 10 + + ``ValueError`` will be raised if the given *element* isn't one of the + combinations of *iterable*. + """ + element = enumerate(element) + k, y = next(element, (None, None)) + if k is None: + return 0 + + indexes = [] + pool = enumerate(iterable) + for n, x in pool: + if x == y: + indexes.append(n) + tmp, y = next(element, (None, None)) + if tmp is None: + break + else: + k = tmp + else: + raise ValueError('element is not a combination of iterable') + + n, _ = last(pool, default=(n, None)) + + # Python versiosn below 3.8 don't have math.comb + index = 1 + for i, j in enumerate(reversed(indexes), start=1): + j = n - j + if i <= j: + index += factorial(j) // (factorial(i) * factorial(j - i)) + + return factorial(n + 1) // (factorial(k + 1) * factorial(n - k)) - index + + +def permutation_index(element, iterable): + """Equivalent to ``list(permutations(iterable, r)).index(element)``` + + The subsequences of *iterable* that are of length *r* where order is + important can be ordered lexicographically. :func:`permutation_index` + computes the index of the first *element* directly, without computing + the previous permutations. + + >>> permutation_index([1, 3, 2], range(5)) + 19 + + ``ValueError`` will be raised if the given *element* isn't one of the + permutations of *iterable*. + """ + index = 0 + pool = list(iterable) + for i, x in zip(range(len(pool), -1, -1), element): + r = pool.index(x) + index = index * i + r + del pool[r] + + return index + + +class countable: + """Wrap *iterable* and keep a count of how many items have been consumed. + + The ``items_seen`` attribute starts at ``0`` and increments as the iterable + is consumed: + + >>> iterable = map(str, range(10)) + >>> it = countable(iterable) + >>> it.items_seen + 0 + >>> next(it), next(it) + ('0', '1') + >>> list(it) + ['2', '3', '4', '5', '6', '7', '8', '9'] + >>> it.items_seen + 10 + """ + + def __init__(self, iterable): + self._it = iter(iterable) + self.items_seen = 0 + + def __iter__(self): + return self + + def __next__(self): + item = next(self._it) + self.items_seen += 1 + + return item diff --git a/_setuptools_vendored/more_itertools/more.pyi b/_setuptools_vendored/more_itertools/more.pyi new file mode 100644 index 0000000000..2fba9cb300 --- /dev/null +++ b/_setuptools_vendored/more_itertools/more.pyi @@ -0,0 +1,480 @@ +"""Stubs for more_itertools.more""" + +from typing import ( + Any, + Callable, + Container, + Dict, + Generic, + Hashable, + Iterable, + Iterator, + List, + Optional, + Reversible, + Sequence, + Sized, + Tuple, + Union, + TypeVar, + type_check_only, +) +from types import TracebackType +from typing_extensions import ContextManager, Protocol, Type, overload + +# Type and type variable definitions +_T = TypeVar('_T') +_U = TypeVar('_U') +_V = TypeVar('_V') +_W = TypeVar('_W') +_T_co = TypeVar('_T_co', covariant=True) +_GenFn = TypeVar('_GenFn', bound=Callable[..., Iterator[object]]) +_Raisable = Union[BaseException, 'Type[BaseException]'] + +@type_check_only +class _SizedIterable(Protocol[_T_co], Sized, Iterable[_T_co]): ... + +@type_check_only +class _SizedReversible(Protocol[_T_co], Sized, Reversible[_T_co]): ... + +def chunked( + iterable: Iterable[_T], n: int, strict: bool = ... +) -> Iterator[List[_T]]: ... +@overload +def first(iterable: Iterable[_T]) -> _T: ... +@overload +def first(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... +@overload +def last(iterable: Iterable[_T]) -> _T: ... +@overload +def last(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... +@overload +def nth_or_last(iterable: Iterable[_T], n: int) -> _T: ... +@overload +def nth_or_last( + iterable: Iterable[_T], n: int, default: _U +) -> Union[_T, _U]: ... + +class peekable(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T]) -> None: ... + def __iter__(self) -> peekable[_T]: ... + def __bool__(self) -> bool: ... + @overload + def peek(self) -> _T: ... + @overload + def peek(self, default: _U) -> Union[_T, _U]: ... + def prepend(self, *items: _T) -> None: ... + def __next__(self) -> _T: ... + @overload + def __getitem__(self, index: int) -> _T: ... + @overload + def __getitem__(self, index: slice) -> List[_T]: ... + +def collate(*iterables: Iterable[_T], **kwargs: Any) -> Iterable[_T]: ... +def consumer(func: _GenFn) -> _GenFn: ... +def ilen(iterable: Iterable[object]) -> int: ... +def iterate(func: Callable[[_T], _T], start: _T) -> Iterator[_T]: ... +def with_iter( + context_manager: ContextManager[Iterable[_T]], +) -> Iterator[_T]: ... +def one( + iterable: Iterable[_T], + too_short: Optional[_Raisable] = ..., + too_long: Optional[_Raisable] = ..., +) -> _T: ... +def distinct_permutations( + iterable: Iterable[_T], r: Optional[int] = ... +) -> Iterator[Tuple[_T, ...]]: ... +def intersperse( + e: _U, iterable: Iterable[_T], n: int = ... +) -> Iterator[Union[_T, _U]]: ... +def unique_to_each(*iterables: Iterable[_T]) -> List[List[_T]]: ... +@overload +def windowed( + seq: Iterable[_T], n: int, *, step: int = ... +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def windowed( + seq: Iterable[_T], n: int, fillvalue: _U, step: int = ... +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +def substrings(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... +def substrings_indexes( + seq: Sequence[_T], reverse: bool = ... +) -> Iterator[Tuple[Sequence[_T], int, int]]: ... + +class bucket(Generic[_T, _U], Container[_U]): + def __init__( + self, + iterable: Iterable[_T], + key: Callable[[_T], _U], + validator: Optional[Callable[[object], object]] = ..., + ) -> None: ... + def __contains__(self, value: object) -> bool: ... + def __iter__(self) -> Iterator[_U]: ... + def __getitem__(self, value: object) -> Iterator[_T]: ... + +def spy( + iterable: Iterable[_T], n: int = ... +) -> Tuple[List[_T], Iterator[_T]]: ... +def interleave(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def interleave_longest(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def collapse( + iterable: Iterable[Any], + base_type: Optional[type] = ..., + levels: Optional[int] = ..., +) -> Iterator[Any]: ... +@overload +def side_effect( + func: Callable[[_T], object], + iterable: Iterable[_T], + chunk_size: None = ..., + before: Optional[Callable[[], object]] = ..., + after: Optional[Callable[[], object]] = ..., +) -> Iterator[_T]: ... +@overload +def side_effect( + func: Callable[[List[_T]], object], + iterable: Iterable[_T], + chunk_size: int, + before: Optional[Callable[[], object]] = ..., + after: Optional[Callable[[], object]] = ..., +) -> Iterator[_T]: ... +def sliced( + seq: Sequence[_T], n: int, strict: bool = ... +) -> Iterator[Sequence[_T]]: ... +def split_at( + iterable: Iterable[_T], + pred: Callable[[_T], object], + maxsplit: int = ..., + keep_separator: bool = ..., +) -> Iterator[List[_T]]: ... +def split_before( + iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... +) -> Iterator[List[_T]]: ... +def split_after( + iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... +) -> Iterator[List[_T]]: ... +def split_when( + iterable: Iterable[_T], + pred: Callable[[_T, _T], object], + maxsplit: int = ..., +) -> Iterator[List[_T]]: ... +def split_into( + iterable: Iterable[_T], sizes: Iterable[Optional[int]] +) -> Iterator[List[_T]]: ... +@overload +def padded( + iterable: Iterable[_T], + *, + n: Optional[int] = ..., + next_multiple: bool = ... +) -> Iterator[Optional[_T]]: ... +@overload +def padded( + iterable: Iterable[_T], + fillvalue: _U, + n: Optional[int] = ..., + next_multiple: bool = ..., +) -> Iterator[Union[_T, _U]]: ... +@overload +def repeat_last(iterable: Iterable[_T]) -> Iterator[_T]: ... +@overload +def repeat_last( + iterable: Iterable[_T], default: _U +) -> Iterator[Union[_T, _U]]: ... +def distribute(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... +@overload +def stagger( + iterable: Iterable[_T], + offsets: _SizedIterable[int] = ..., + longest: bool = ..., +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def stagger( + iterable: Iterable[_T], + offsets: _SizedIterable[int] = ..., + longest: bool = ..., + fillvalue: _U = ..., +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... + +class UnequalIterablesError(ValueError): + def __init__( + self, details: Optional[Tuple[int, int, int]] = ... + ) -> None: ... + +def zip_equal(*iterables: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... +@overload +def zip_offset( + *iterables: Iterable[_T], offsets: _SizedIterable[int], longest: bool = ... +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def zip_offset( + *iterables: Iterable[_T], + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: _U +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +def sort_together( + iterables: Iterable[Iterable[_T]], + key_list: Iterable[int] = ..., + key: Optional[Callable[..., Any]] = ..., + reverse: bool = ..., +) -> List[Tuple[_T, ...]]: ... +def unzip(iterable: Iterable[Sequence[_T]]) -> Tuple[Iterator[_T], ...]: ... +def divide(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... +def always_iterable( + obj: object, + base_type: Union[ + type, Tuple[Union[type, Tuple[Any, ...]], ...], None + ] = ..., +) -> Iterator[Any]: ... +def adjacent( + predicate: Callable[[_T], bool], + iterable: Iterable[_T], + distance: int = ..., +) -> Iterator[Tuple[bool, _T]]: ... +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Optional[Callable[[_T], _U]] = ..., + valuefunc: Optional[Callable[[_T], _V]] = ..., + reducefunc: Optional[Callable[..., _W]] = ..., +) -> Iterator[Tuple[_T, _W]]: ... + +class numeric_range(Generic[_T, _U], Sequence[_T], Hashable, Reversible[_T]): + @overload + def __init__(self, __stop: _T) -> None: ... + @overload + def __init__(self, __start: _T, __stop: _T) -> None: ... + @overload + def __init__(self, __start: _T, __stop: _T, __step: _U) -> None: ... + def __bool__(self) -> bool: ... + def __contains__(self, elem: object) -> bool: ... + def __eq__(self, other: object) -> bool: ... + @overload + def __getitem__(self, key: int) -> _T: ... + @overload + def __getitem__(self, key: slice) -> numeric_range[_T, _U]: ... + def __hash__(self) -> int: ... + def __iter__(self) -> Iterator[_T]: ... + def __len__(self) -> int: ... + def __reduce__( + self, + ) -> Tuple[Type[numeric_range[_T, _U]], Tuple[_T, _T, _U]]: ... + def __repr__(self) -> str: ... + def __reversed__(self) -> Iterator[_T]: ... + def count(self, value: _T) -> int: ... + def index(self, value: _T) -> int: ... # type: ignore + +def count_cycle( + iterable: Iterable[_T], n: Optional[int] = ... +) -> Iterable[Tuple[int, _T]]: ... +def mark_ends( + iterable: Iterable[_T], +) -> Iterable[Tuple[bool, bool, _T]]: ... +def locate( + iterable: Iterable[object], + pred: Callable[..., Any] = ..., + window_size: Optional[int] = ..., +) -> Iterator[int]: ... +def lstrip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... +def rstrip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... +def strip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... + +class islice_extended(Generic[_T], Iterator[_T]): + def __init__( + self, iterable: Iterable[_T], *args: Optional[int] + ) -> None: ... + def __iter__(self) -> islice_extended[_T]: ... + def __next__(self) -> _T: ... + def __getitem__(self, index: slice) -> islice_extended[_T]: ... + +def always_reversible(iterable: Iterable[_T]) -> Iterator[_T]: ... +def consecutive_groups( + iterable: Iterable[_T], ordering: Callable[[_T], int] = ... +) -> Iterator[Iterator[_T]]: ... +@overload +def difference( + iterable: Iterable[_T], + func: Callable[[_T, _T], _U] = ..., + *, + initial: None = ... +) -> Iterator[Union[_T, _U]]: ... +@overload +def difference( + iterable: Iterable[_T], func: Callable[[_T, _T], _U] = ..., *, initial: _U +) -> Iterator[_U]: ... + +class SequenceView(Generic[_T], Sequence[_T]): + def __init__(self, target: Sequence[_T]) -> None: ... + @overload + def __getitem__(self, index: int) -> _T: ... + @overload + def __getitem__(self, index: slice) -> Sequence[_T]: ... + def __len__(self) -> int: ... + +class seekable(Generic[_T], Iterator[_T]): + def __init__( + self, iterable: Iterable[_T], maxlen: Optional[int] = ... + ) -> None: ... + def __iter__(self) -> seekable[_T]: ... + def __next__(self) -> _T: ... + def __bool__(self) -> bool: ... + @overload + def peek(self) -> _T: ... + @overload + def peek(self, default: _U) -> Union[_T, _U]: ... + def elements(self) -> SequenceView[_T]: ... + def seek(self, index: int) -> None: ... + +class run_length: + @staticmethod + def encode(iterable: Iterable[_T]) -> Iterator[Tuple[_T, int]]: ... + @staticmethod + def decode(iterable: Iterable[Tuple[_T, int]]) -> Iterator[_T]: ... + +def exactly_n( + iterable: Iterable[_T], n: int, predicate: Callable[[_T], object] = ... +) -> bool: ... +def circular_shifts(iterable: Iterable[_T]) -> List[Tuple[_T, ...]]: ... +def make_decorator( + wrapping_func: Callable[..., _U], result_index: int = ... +) -> Callable[..., Callable[[Callable[..., Any]], Callable[..., _U]]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None = ..., + reducefunc: None = ..., +) -> Dict[_U, List[_T]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: None = ..., +) -> Dict[_U, List[_V]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None = ..., + reducefunc: Callable[[List[_T]], _W] = ..., +) -> Dict[_U, _W]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: Callable[[List[_V]], _W], +) -> Dict[_U, _W]: ... +def rlocate( + iterable: Iterable[_T], + pred: Callable[..., object] = ..., + window_size: Optional[int] = ..., +) -> Iterator[int]: ... +def replace( + iterable: Iterable[_T], + pred: Callable[..., object], + substitutes: Iterable[_U], + count: Optional[int] = ..., + window_size: int = ..., +) -> Iterator[Union[_T, _U]]: ... +def partitions(iterable: Iterable[_T]) -> Iterator[List[List[_T]]]: ... +def set_partitions( + iterable: Iterable[_T], k: Optional[int] = ... +) -> Iterator[List[List[_T]]]: ... + +class time_limited(Generic[_T], Iterator[_T]): + def __init__( + self, limit_seconds: float, iterable: Iterable[_T] + ) -> None: ... + def __iter__(self) -> islice_extended[_T]: ... + def __next__(self) -> _T: ... + +@overload +def only( + iterable: Iterable[_T], *, too_long: Optional[_Raisable] = ... +) -> Optional[_T]: ... +@overload +def only( + iterable: Iterable[_T], default: _U, too_long: Optional[_Raisable] = ... +) -> Union[_T, _U]: ... +def ichunked(iterable: Iterable[_T], n: int) -> Iterator[Iterator[_T]]: ... +def distinct_combinations( + iterable: Iterable[_T], r: int +) -> Iterator[Tuple[_T, ...]]: ... +def filter_except( + validator: Callable[[Any], object], + iterable: Iterable[_T], + *exceptions: Type[BaseException] +) -> Iterator[_T]: ... +def map_except( + function: Callable[[Any], _U], + iterable: Iterable[_T], + *exceptions: Type[BaseException] +) -> Iterator[_U]: ... +def sample( + iterable: Iterable[_T], + k: int, + weights: Optional[Iterable[float]] = ..., +) -> List[_T]: ... +def is_sorted( + iterable: Iterable[_T], + key: Optional[Callable[[_T], _U]] = ..., + reverse: bool = False, +) -> bool: ... + +class AbortThread(BaseException): + pass + +class callback_iter(Generic[_T], Iterator[_T]): + def __init__( + self, + func: Callable[..., Any], + callback_kwd: str = ..., + wait_seconds: float = ..., + ) -> None: ... + def __enter__(self) -> callback_iter[_T]: ... + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> Optional[bool]: ... + def __iter__(self) -> callback_iter[_T]: ... + def __next__(self) -> _T: ... + def _reader(self) -> Iterator[_T]: ... + @property + def done(self) -> bool: ... + @property + def result(self) -> Any: ... + +def windowed_complete( + iterable: Iterable[_T], n: int +) -> Iterator[Tuple[_T, ...]]: ... +def all_unique( + iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... +) -> bool: ... +def nth_product(index: int, *args: Iterable[_T]) -> Tuple[_T, ...]: ... +def nth_permutation( + iterable: Iterable[_T], r: int, index: int +) -> Tuple[_T, ...]: ... +def value_chain(*args: Union[_T, Iterable[_T]]) -> Iterable[_T]: ... +def product_index(element: Iterable[_T], *args: Iterable[_T]) -> int: ... +def combination_index( + element: Iterable[_T], iterable: Iterable[_T] +) -> int: ... +def permutation_index( + element: Iterable[_T], iterable: Iterable[_T] +) -> int: ... + +class countable(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T]) -> None: ... + def __iter__(self) -> countable[_T]: ... + def __next__(self) -> _T: ... diff --git a/_setuptools_vendored/more_itertools/py.typed b/_setuptools_vendored/more_itertools/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_setuptools_vendored/more_itertools/recipes.py b/_setuptools_vendored/more_itertools/recipes.py new file mode 100644 index 0000000000..521abd7c2c --- /dev/null +++ b/_setuptools_vendored/more_itertools/recipes.py @@ -0,0 +1,620 @@ +"""Imported from the recipes section of the itertools documentation. + +All functions taken from the recipes section of the itertools library docs +[1]_. +Some backward-compatible usability improvements have been made. + +.. [1] http://docs.python.org/library/itertools.html#recipes + +""" +import warnings +from collections import deque +from itertools import ( + chain, + combinations, + count, + cycle, + groupby, + islice, + repeat, + starmap, + tee, + zip_longest, +) +import operator +from random import randrange, sample, choice + +__all__ = [ + 'all_equal', + 'consume', + 'convolve', + 'dotproduct', + 'first_true', + 'flatten', + 'grouper', + 'iter_except', + 'ncycles', + 'nth', + 'nth_combination', + 'padnone', + 'pad_none', + 'pairwise', + 'partition', + 'powerset', + 'prepend', + 'quantify', + 'random_combination_with_replacement', + 'random_combination', + 'random_permutation', + 'random_product', + 'repeatfunc', + 'roundrobin', + 'tabulate', + 'tail', + 'take', + 'unique_everseen', + 'unique_justseen', +] + + +def take(n, iterable): + """Return first *n* items of the iterable as a list. + + >>> take(3, range(10)) + [0, 1, 2] + + If there are fewer than *n* items in the iterable, all of them are + returned. + + >>> take(10, range(3)) + [0, 1, 2] + + """ + return list(islice(iterable, n)) + + +def tabulate(function, start=0): + """Return an iterator over the results of ``func(start)``, + ``func(start + 1)``, ``func(start + 2)``... + + *func* should be a function that accepts one integer argument. + + If *start* is not specified it defaults to 0. It will be incremented each + time the iterator is advanced. + + >>> square = lambda x: x ** 2 + >>> iterator = tabulate(square, -3) + >>> take(4, iterator) + [9, 4, 1, 0] + + """ + return map(function, count(start)) + + +def tail(n, iterable): + """Return an iterator over the last *n* items of *iterable*. + + >>> t = tail(3, 'ABCDEFG') + >>> list(t) + ['E', 'F', 'G'] + + """ + return iter(deque(iterable, maxlen=n)) + + +def consume(iterator, n=None): + """Advance *iterable* by *n* steps. If *n* is ``None``, consume it + entirely. + + Efficiently exhausts an iterator without returning values. Defaults to + consuming the whole iterator, but an optional second argument may be + provided to limit consumption. + + >>> i = (x for x in range(10)) + >>> next(i) + 0 + >>> consume(i, 3) + >>> next(i) + 4 + >>> consume(i) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + + If the iterator has fewer items remaining than the provided limit, the + whole iterator will be consumed. + + >>> i = (x for x in range(3)) + >>> consume(i, 5) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + + """ + # Use functions that consume iterators at C speed. + if n is None: + # feed the entire iterator into a zero-length deque + deque(iterator, maxlen=0) + else: + # advance to the empty slice starting at position n + next(islice(iterator, n, n), None) + + +def nth(iterable, n, default=None): + """Returns the nth item or a default value. + + >>> l = range(10) + >>> nth(l, 3) + 3 + >>> nth(l, 20, "zebra") + 'zebra' + + """ + return next(islice(iterable, n, None), default) + + +def all_equal(iterable): + """ + Returns ``True`` if all the elements are equal to each other. + + >>> all_equal('aaaa') + True + >>> all_equal('aaab') + False + + """ + g = groupby(iterable) + return next(g, True) and not next(g, False) + + +def quantify(iterable, pred=bool): + """Return the how many times the predicate is true. + + >>> quantify([True, False, True]) + 2 + + """ + return sum(map(pred, iterable)) + + +def pad_none(iterable): + """Returns the sequence of elements and then returns ``None`` indefinitely. + + >>> take(5, pad_none(range(3))) + [0, 1, 2, None, None] + + Useful for emulating the behavior of the built-in :func:`map` function. + + See also :func:`padded`. + + """ + return chain(iterable, repeat(None)) + + +padnone = pad_none + + +def ncycles(iterable, n): + """Returns the sequence elements *n* times + + >>> list(ncycles(["a", "b"], 3)) + ['a', 'b', 'a', 'b', 'a', 'b'] + + """ + return chain.from_iterable(repeat(tuple(iterable), n)) + + +def dotproduct(vec1, vec2): + """Returns the dot product of the two iterables. + + >>> dotproduct([10, 10], [20, 20]) + 400 + + """ + return sum(map(operator.mul, vec1, vec2)) + + +def flatten(listOfLists): + """Return an iterator flattening one level of nesting in a list of lists. + + >>> list(flatten([[0, 1], [2, 3]])) + [0, 1, 2, 3] + + See also :func:`collapse`, which can flatten multiple levels of nesting. + + """ + return chain.from_iterable(listOfLists) + + +def repeatfunc(func, times=None, *args): + """Call *func* with *args* repeatedly, returning an iterable over the + results. + + If *times* is specified, the iterable will terminate after that many + repetitions: + + >>> from operator import add + >>> times = 4 + >>> args = 3, 5 + >>> list(repeatfunc(add, times, *args)) + [8, 8, 8, 8] + + If *times* is ``None`` the iterable will not terminate: + + >>> from random import randrange + >>> times = None + >>> args = 1, 11 + >>> take(6, repeatfunc(randrange, times, *args)) # doctest:+SKIP + [2, 4, 8, 1, 8, 4] + + """ + if times is None: + return starmap(func, repeat(args)) + return starmap(func, repeat(args, times)) + + +def _pairwise(iterable): + """Returns an iterator of paired items, overlapping, from the original + + >>> take(4, pairwise(count())) + [(0, 1), (1, 2), (2, 3), (3, 4)] + + On Python 3.10 and above, this is an alias for :func:`itertools.pairwise`. + + """ + a, b = tee(iterable) + next(b, None) + yield from zip(a, b) + + +try: + from itertools import pairwise as itertools_pairwise +except ImportError: + pairwise = _pairwise +else: + + def pairwise(iterable): + yield from itertools_pairwise(iterable) + + pairwise.__doc__ = _pairwise.__doc__ + + +def grouper(iterable, n, fillvalue=None): + """Collect data into fixed-length chunks or blocks. + + >>> list(grouper('ABCDEFG', 3, 'x')) + [('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')] + + """ + if isinstance(iterable, int): + warnings.warn( + "grouper expects iterable as first parameter", DeprecationWarning + ) + n, iterable = iterable, n + args = [iter(iterable)] * n + return zip_longest(fillvalue=fillvalue, *args) + + +def roundrobin(*iterables): + """Yields an item from each iterable, alternating between them. + + >>> list(roundrobin('ABC', 'D', 'EF')) + ['A', 'D', 'E', 'B', 'F', 'C'] + + This function produces the same output as :func:`interleave_longest`, but + may perform better for some inputs (in particular when the number of + iterables is small). + + """ + # Recipe credited to George Sakkis + pending = len(iterables) + nexts = cycle(iter(it).__next__ for it in iterables) + while pending: + try: + for next in nexts: + yield next() + except StopIteration: + pending -= 1 + nexts = cycle(islice(nexts, pending)) + + +def partition(pred, iterable): + """ + Returns a 2-tuple of iterables derived from the input iterable. + The first yields the items that have ``pred(item) == False``. + The second yields the items that have ``pred(item) == True``. + + >>> is_odd = lambda x: x % 2 != 0 + >>> iterable = range(10) + >>> even_items, odd_items = partition(is_odd, iterable) + >>> list(even_items), list(odd_items) + ([0, 2, 4, 6, 8], [1, 3, 5, 7, 9]) + + If *pred* is None, :func:`bool` is used. + + >>> iterable = [0, 1, False, True, '', ' '] + >>> false_items, true_items = partition(None, iterable) + >>> list(false_items), list(true_items) + ([0, False, ''], [1, True, ' ']) + + """ + if pred is None: + pred = bool + + evaluations = ((pred(x), x) for x in iterable) + t1, t2 = tee(evaluations) + return ( + (x for (cond, x) in t1 if not cond), + (x for (cond, x) in t2 if cond), + ) + + +def powerset(iterable): + """Yields all possible subsets of the iterable. + + >>> list(powerset([1, 2, 3])) + [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] + + :func:`powerset` will operate on iterables that aren't :class:`set` + instances, so repeated elements in the input will produce repeated elements + in the output. Use :func:`unique_everseen` on the input to avoid generating + duplicates: + + >>> seq = [1, 1, 0] + >>> list(powerset(seq)) + [(), (1,), (1,), (0,), (1, 1), (1, 0), (1, 0), (1, 1, 0)] + >>> from more_itertools import unique_everseen + >>> list(powerset(unique_everseen(seq))) + [(), (1,), (0,), (1, 0)] + + """ + s = list(iterable) + return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1)) + + +def unique_everseen(iterable, key=None): + """ + Yield unique elements, preserving order. + + >>> list(unique_everseen('AAAABBBCCDAABBB')) + ['A', 'B', 'C', 'D'] + >>> list(unique_everseen('ABBCcAD', str.lower)) + ['A', 'B', 'C', 'D'] + + Sequences with a mix of hashable and unhashable items can be used. + The function will be slower (i.e., `O(n^2)`) for unhashable items. + + Remember that ``list`` objects are unhashable - you can use the *key* + parameter to transform the list to a tuple (which is hashable) to + avoid a slowdown. + + >>> iterable = ([1, 2], [2, 3], [1, 2]) + >>> list(unique_everseen(iterable)) # Slow + [[1, 2], [2, 3]] + >>> list(unique_everseen(iterable, key=tuple)) # Faster + [[1, 2], [2, 3]] + + Similary, you may want to convert unhashable ``set`` objects with + ``key=frozenset``. For ``dict`` objects, + ``key=lambda x: frozenset(x.items())`` can be used. + + """ + seenset = set() + seenset_add = seenset.add + seenlist = [] + seenlist_add = seenlist.append + use_key = key is not None + + for element in iterable: + k = key(element) if use_key else element + try: + if k not in seenset: + seenset_add(k) + yield element + except TypeError: + if k not in seenlist: + seenlist_add(k) + yield element + + +def unique_justseen(iterable, key=None): + """Yields elements in order, ignoring serial duplicates + + >>> list(unique_justseen('AAAABBBCCDAABBB')) + ['A', 'B', 'C', 'D', 'A', 'B'] + >>> list(unique_justseen('ABBCcAD', str.lower)) + ['A', 'B', 'C', 'A', 'D'] + + """ + return map(next, map(operator.itemgetter(1), groupby(iterable, key))) + + +def iter_except(func, exception, first=None): + """Yields results from a function repeatedly until an exception is raised. + + Converts a call-until-exception interface to an iterator interface. + Like ``iter(func, sentinel)``, but uses an exception instead of a sentinel + to end the loop. + + >>> l = [0, 1, 2] + >>> list(iter_except(l.pop, IndexError)) + [2, 1, 0] + + """ + try: + if first is not None: + yield first() + while 1: + yield func() + except exception: + pass + + +def first_true(iterable, default=None, pred=None): + """ + Returns the first true value in the iterable. + + If no true value is found, returns *default* + + If *pred* is not None, returns the first item for which + ``pred(item) == True`` . + + >>> first_true(range(10)) + 1 + >>> first_true(range(10), pred=lambda x: x > 5) + 6 + >>> first_true(range(10), default='missing', pred=lambda x: x > 9) + 'missing' + + """ + return next(filter(pred, iterable), default) + + +def random_product(*args, repeat=1): + """Draw an item at random from each of the input iterables. + + >>> random_product('abc', range(4), 'XYZ') # doctest:+SKIP + ('c', 3, 'Z') + + If *repeat* is provided as a keyword argument, that many items will be + drawn from each iterable. + + >>> random_product('abcd', range(4), repeat=2) # doctest:+SKIP + ('a', 2, 'd', 3) + + This equivalent to taking a random selection from + ``itertools.product(*args, **kwarg)``. + + """ + pools = [tuple(pool) for pool in args] * repeat + return tuple(choice(pool) for pool in pools) + + +def random_permutation(iterable, r=None): + """Return a random *r* length permutation of the elements in *iterable*. + + If *r* is not specified or is ``None``, then *r* defaults to the length of + *iterable*. + + >>> random_permutation(range(5)) # doctest:+SKIP + (3, 4, 0, 1, 2) + + This equivalent to taking a random selection from + ``itertools.permutations(iterable, r)``. + + """ + pool = tuple(iterable) + r = len(pool) if r is None else r + return tuple(sample(pool, r)) + + +def random_combination(iterable, r): + """Return a random *r* length subsequence of the elements in *iterable*. + + >>> random_combination(range(5), 3) # doctest:+SKIP + (2, 3, 4) + + This equivalent to taking a random selection from + ``itertools.combinations(iterable, r)``. + + """ + pool = tuple(iterable) + n = len(pool) + indices = sorted(sample(range(n), r)) + return tuple(pool[i] for i in indices) + + +def random_combination_with_replacement(iterable, r): + """Return a random *r* length subsequence of elements in *iterable*, + allowing individual elements to be repeated. + + >>> random_combination_with_replacement(range(3), 5) # doctest:+SKIP + (0, 0, 1, 2, 2) + + This equivalent to taking a random selection from + ``itertools.combinations_with_replacement(iterable, r)``. + + """ + pool = tuple(iterable) + n = len(pool) + indices = sorted(randrange(n) for i in range(r)) + return tuple(pool[i] for i in indices) + + +def nth_combination(iterable, r, index): + """Equivalent to ``list(combinations(iterable, r))[index]``. + + The subsequences of *iterable* that are of length *r* can be ordered + lexicographically. :func:`nth_combination` computes the subsequence at + sort position *index* directly, without computing the previous + subsequences. + + >>> nth_combination(range(5), 3, 5) + (0, 3, 4) + + ``ValueError`` will be raised If *r* is negative or greater than the length + of *iterable*. + ``IndexError`` will be raised if the given *index* is invalid. + """ + pool = tuple(iterable) + n = len(pool) + if (r < 0) or (r > n): + raise ValueError + + c = 1 + k = min(r, n - r) + for i in range(1, k + 1): + c = c * (n - k + i) // i + + if index < 0: + index += c + + if (index < 0) or (index >= c): + raise IndexError + + result = [] + while r: + c, n, r = c * r // n, n - 1, r - 1 + while index >= c: + index -= c + c, n = c * (n - r) // n, n - 1 + result.append(pool[-1 - n]) + + return tuple(result) + + +def prepend(value, iterator): + """Yield *value*, followed by the elements in *iterator*. + + >>> value = '0' + >>> iterator = ['1', '2', '3'] + >>> list(prepend(value, iterator)) + ['0', '1', '2', '3'] + + To prepend multiple values, see :func:`itertools.chain` + or :func:`value_chain`. + + """ + return chain([value], iterator) + + +def convolve(signal, kernel): + """Convolve the iterable *signal* with the iterable *kernel*. + + >>> signal = (1, 2, 3, 4, 5) + >>> kernel = [3, 2, 1] + >>> list(convolve(signal, kernel)) + [3, 8, 14, 20, 26, 14, 5] + + Note: the input arguments are not interchangeable, as the *kernel* + is immediately consumed and stored. + + """ + kernel = tuple(kernel)[::-1] + n = len(kernel) + window = deque([0], maxlen=n) * n + for x in chain(signal, repeat(0, n - 1)): + window.append(x) + yield sum(map(operator.mul, kernel, window)) diff --git a/_setuptools_vendored/more_itertools/recipes.pyi b/_setuptools_vendored/more_itertools/recipes.pyi new file mode 100644 index 0000000000..5e39d96390 --- /dev/null +++ b/_setuptools_vendored/more_itertools/recipes.pyi @@ -0,0 +1,103 @@ +"""Stubs for more_itertools.recipes""" +from typing import ( + Any, + Callable, + Iterable, + Iterator, + List, + Optional, + Tuple, + TypeVar, + Union, +) +from typing_extensions import overload, Type + +# Type and type variable definitions +_T = TypeVar('_T') +_U = TypeVar('_U') + +def take(n: int, iterable: Iterable[_T]) -> List[_T]: ... +def tabulate( + function: Callable[[int], _T], start: int = ... +) -> Iterator[_T]: ... +def tail(n: int, iterable: Iterable[_T]) -> Iterator[_T]: ... +def consume(iterator: Iterable[object], n: Optional[int] = ...) -> None: ... +@overload +def nth(iterable: Iterable[_T], n: int) -> Optional[_T]: ... +@overload +def nth(iterable: Iterable[_T], n: int, default: _U) -> Union[_T, _U]: ... +def all_equal(iterable: Iterable[object]) -> bool: ... +def quantify( + iterable: Iterable[_T], pred: Callable[[_T], bool] = ... +) -> int: ... +def pad_none(iterable: Iterable[_T]) -> Iterator[Optional[_T]]: ... +def padnone(iterable: Iterable[_T]) -> Iterator[Optional[_T]]: ... +def ncycles(iterable: Iterable[_T], n: int) -> Iterator[_T]: ... +def dotproduct(vec1: Iterable[object], vec2: Iterable[object]) -> object: ... +def flatten(listOfLists: Iterable[Iterable[_T]]) -> Iterator[_T]: ... +def repeatfunc( + func: Callable[..., _U], times: Optional[int] = ..., *args: Any +) -> Iterator[_U]: ... +def pairwise(iterable: Iterable[_T]) -> Iterator[Tuple[_T, _T]]: ... +@overload +def grouper( + iterable: Iterable[_T], n: int +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def grouper( + iterable: Iterable[_T], n: int, fillvalue: _U +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +@overload +def grouper( # Deprecated interface + iterable: int, n: Iterable[_T] +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def grouper( # Deprecated interface + iterable: int, n: Iterable[_T], fillvalue: _U +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +def roundrobin(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def partition( + pred: Optional[Callable[[_T], object]], iterable: Iterable[_T] +) -> Tuple[Iterator[_T], Iterator[_T]]: ... +def powerset(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... +def unique_everseen( + iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... +) -> Iterator[_T]: ... +def unique_justseen( + iterable: Iterable[_T], key: Optional[Callable[[_T], object]] = ... +) -> Iterator[_T]: ... +@overload +def iter_except( + func: Callable[[], _T], exception: Type[BaseException], first: None = ... +) -> Iterator[_T]: ... +@overload +def iter_except( + func: Callable[[], _T], + exception: Type[BaseException], + first: Callable[[], _U], +) -> Iterator[Union[_T, _U]]: ... +@overload +def first_true( + iterable: Iterable[_T], *, pred: Optional[Callable[[_T], object]] = ... +) -> Optional[_T]: ... +@overload +def first_true( + iterable: Iterable[_T], + default: _U, + pred: Optional[Callable[[_T], object]] = ..., +) -> Union[_T, _U]: ... +def random_product( + *args: Iterable[_T], repeat: int = ... +) -> Tuple[_T, ...]: ... +def random_permutation( + iterable: Iterable[_T], r: Optional[int] = ... +) -> Tuple[_T, ...]: ... +def random_combination(iterable: Iterable[_T], r: int) -> Tuple[_T, ...]: ... +def random_combination_with_replacement( + iterable: Iterable[_T], r: int +) -> Tuple[_T, ...]: ... +def nth_combination( + iterable: Iterable[_T], r: int, index: int +) -> Tuple[_T, ...]: ... +def prepend(value: _T, iterator: Iterable[_U]) -> Iterator[Union[_T, _U]]: ... +def convolve(signal: Iterable[_T], kernel: Iterable[_T]) -> Iterator[_T]: ... diff --git a/_setuptools_vendored/ordered_set-3.1.1.dist-info/METADATA b/_setuptools_vendored/ordered_set-3.1.1.dist-info/METADATA new file mode 100644 index 0000000000..e124369c0d --- /dev/null +++ b/_setuptools_vendored/ordered_set-3.1.1.dist-info/METADATA @@ -0,0 +1,156 @@ +Metadata-Version: 2.1 +Name: ordered-set +Version: 3.1.1 +Summary: A MutableSet that remembers its order, so that every entry has an index. +Home-page: https://github.com/LuminosoInsight/ordered-set +Maintainer: Robyn Speer +Maintainer-email: rspeer@luminoso.com +License: MIT-LICENSE +Platform: any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=2.7 +Description-Content-Type: text/markdown + +[![Travis](https://img.shields.io/travis/LuminosoInsight/ordered-set/master.svg?label=Travis%20CI)](https://travis-ci.org/LuminosoInsight/ordered-set) +[![Codecov](https://codecov.io/github/LuminosoInsight/ordered-set/badge.svg?branch=master&service=github)](https://codecov.io/github/LuminosoInsight/ordered-set?branch=master) +[![Pypi](https://img.shields.io/pypi/v/ordered-set.svg)](https://pypi.python.org/pypi/ordered-set) + +An OrderedSet is a mutable data structure that is a hybrid of a list and a set. +It remembers the order of its entries, and every entry has an index number that +can be looked up. + + +## Usage examples + +An OrderedSet is created and used like a set: + + >>> from ordered_set import OrderedSet + + >>> letters = OrderedSet('abracadabra') + + >>> letters + OrderedSet(['a', 'b', 'r', 'c', 'd']) + + >>> 'r' in letters + True + +It is efficient to find the index of an entry in an OrderedSet, or find an +entry by its index. To help with this use case, the `.add()` method returns +the index of the added item, whether it was already in the set or not. + + >>> letters.index('r') + 2 + + >>> letters[2] + 'r' + + >>> letters.add('r') + 2 + + >>> letters.add('x') + 5 + +OrderedSets implement the union (`|`), intersection (`&`), and difference (`-`) +operators like sets do. + + >>> letters |= OrderedSet('shazam') + + >>> letters + OrderedSet(['a', 'b', 'r', 'c', 'd', 'x', 's', 'h', 'z', 'm']) + + >>> letters & set('aeiou') + OrderedSet(['a']) + + >>> letters -= 'abcd' + + >>> letters + OrderedSet(['r', 'x', 's', 'h', 'z', 'm']) + +The `__getitem__()` and `index()` methods have been extended to accept any +iterable except a string, returning a list, to perform NumPy-like "fancy +indexing". + + >>> letters = OrderedSet('abracadabra') + + >>> letters[[0, 2, 3]] + ['a', 'r', 'c'] + + >>> letters.index(['a', 'r', 'c']) + [0, 2, 3] + +OrderedSet implements `__getstate__` and `__setstate__` so it can be pickled, +and implements the abstract base classes `collections.MutableSet` and +`collections.Sequence`. + + +## Interoperability with NumPy and Pandas + +An OrderedSet can be used as a bi-directional mapping between a sparse +vocabulary and dense index numbers. As of version 3.1, it accepts NumPy arrays +of index numbers as well as lists. + +This combination of features makes OrderedSet a simple implementation of many +of the things that `pandas.Index` is used for, and many of its operations are +faster than the equivalent pandas operations. + +For further compatibility with pandas.Index, `get_loc` (the pandas method for +looking up a single index) and `get_indexer` (the pandas method for fancy +indexing in reverse) are both aliases for `index` (which handles both cases +in OrderedSet). + + +## Type hinting +To use type hinting features install `ordered-set-stubs` package from +[PyPI](https://pypi.org/project/ordered-set-stubs/): + + $ pip install ordered-set-stubs + + +## Authors + +OrderedSet was implemented by Robyn Speer. Jon Crall contributed changes and +tests to make it fit the Python set API. + + +## Comparisons + +The original implementation of OrderedSet was a [recipe posted to ActiveState +Recipes][recipe] by Raymond Hettiger, released under the MIT license. + +[recipe]: https://code.activestate.com/recipes/576694-orderedset/ + +Hettiger's implementation kept its content in a doubly-linked list referenced by a +dict. As a result, looking up an item by its index was an O(N) operation, while +deletion was O(1). + +This version makes different trade-offs for the sake of efficient lookups. Its +content is a standard Python list instead of a doubly-linked list. This +provides O(1) lookups by index at the expense of O(N) deletion, as well as +slightly faster iteration. + +In Python 3.6 and later, the built-in `dict` type is inherently ordered. If you +ignore the dictionary values, that also gives you a simple ordered set, with +fast O(1) insertion, deletion, iteration and membership testing. However, `dict` +does not provide the list-like random access features of OrderedSet. You +would have to convert it to a list in O(N) to look up the index of an entry or +look up an entry by its index. + + +## Compatibility + +OrderedSet is automatically tested on Python 2.7, 3.4, 3.5, 3.6, and 3.7. +We've checked more informally that it works on PyPy and PyPy3. + + diff --git a/_setuptools_vendored/ordered_set-3.1.1.dist-info/MIT-LICENSE b/_setuptools_vendored/ordered_set-3.1.1.dist-info/MIT-LICENSE new file mode 100644 index 0000000000..25117ef4f1 --- /dev/null +++ b/_setuptools_vendored/ordered_set-3.1.1.dist-info/MIT-LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018 Luminoso Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/_setuptools_vendored/ordered_set-3.1.1.dist-info/RECORD b/_setuptools_vendored/ordered_set-3.1.1.dist-info/RECORD new file mode 100644 index 0000000000..55f40206ed --- /dev/null +++ b/_setuptools_vendored/ordered_set-3.1.1.dist-info/RECORD @@ -0,0 +1,6 @@ +ordered_set.py,sha256=dbaCcs27dyN9gnMWGF5nA_BrVn6Q-NrjKYJpV9_fgBs,15130 +ordered_set-3.1.1.dist-info/METADATA,sha256=uOwCSESslh9mHc_HHkWlFJND82Gk-1Oa3PoE0LQxxww,5333 +ordered_set-3.1.1.dist-info/MIT-LICENSE,sha256=TvRE7qUSUBcd0ols7wgNf3zDEEJWW7kv7WDRySrMBBE,1071 +ordered_set-3.1.1.dist-info/WHEEL,sha256=WzZ8cwjh8l0jtULNjYq1Hpr-WCqCRgPr--TX4P5I1Wo,110 +ordered_set-3.1.1.dist-info/top_level.txt,sha256=NTY2_aDi1Do9fl3Z9EmWPxasFkUeW2dzO2D3RDx5CfM,12 +ordered_set-3.1.1.dist-info/RECORD,, diff --git a/_setuptools_vendored/ordered_set-3.1.1.dist-info/WHEEL b/_setuptools_vendored/ordered_set-3.1.1.dist-info/WHEEL new file mode 100644 index 0000000000..b733a60d37 --- /dev/null +++ b/_setuptools_vendored/ordered_set-3.1.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/_setuptools_vendored/ordered_set-3.1.1.dist-info/top_level.txt b/_setuptools_vendored/ordered_set-3.1.1.dist-info/top_level.txt new file mode 100644 index 0000000000..1c191eef52 --- /dev/null +++ b/_setuptools_vendored/ordered_set-3.1.1.dist-info/top_level.txt @@ -0,0 +1 @@ +ordered_set diff --git a/_setuptools_vendored/ordered_set.py b/_setuptools_vendored/ordered_set.py new file mode 100644 index 0000000000..14876000de --- /dev/null +++ b/_setuptools_vendored/ordered_set.py @@ -0,0 +1,488 @@ +""" +An OrderedSet is a custom MutableSet that remembers its order, so that every +entry has an index that can be looked up. + +Based on a recipe originally posted to ActiveState Recipes by Raymond Hettiger, +and released under the MIT license. +""" +import itertools as it +from collections import deque + +try: + # Python 3 + from collections.abc import MutableSet, Sequence +except ImportError: + # Python 2.7 + from collections import MutableSet, Sequence + +SLICE_ALL = slice(None) +__version__ = "3.1" + + +def is_iterable(obj): + """ + Are we being asked to look up a list of things, instead of a single thing? + We check for the `__iter__` attribute so that this can cover types that + don't have to be known by this module, such as NumPy arrays. + + Strings, however, should be considered as atomic values to look up, not + iterables. The same goes for tuples, since they are immutable and therefore + valid entries. + + We don't need to check for the Python 2 `unicode` type, because it doesn't + have an `__iter__` attribute anyway. + """ + return ( + hasattr(obj, "__iter__") + and not isinstance(obj, str) + and not isinstance(obj, tuple) + ) + + +class OrderedSet(MutableSet, Sequence): + """ + An OrderedSet is a custom MutableSet that remembers its order, so that + every entry has an index that can be looked up. + + Example: + >>> OrderedSet([1, 1, 2, 3, 2]) + OrderedSet([1, 2, 3]) + """ + + def __init__(self, iterable=None): + self.items = [] + self.map = {} + if iterable is not None: + self |= iterable + + def __len__(self): + """ + Returns the number of unique elements in the ordered set + + Example: + >>> len(OrderedSet([])) + 0 + >>> len(OrderedSet([1, 2])) + 2 + """ + return len(self.items) + + def __getitem__(self, index): + """ + Get the item at a given index. + + If `index` is a slice, you will get back that slice of items, as a + new OrderedSet. + + If `index` is a list or a similar iterable, you'll get a list of + items corresponding to those indices. This is similar to NumPy's + "fancy indexing". The result is not an OrderedSet because you may ask + for duplicate indices, and the number of elements returned should be + the number of elements asked for. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset[1] + 2 + """ + if isinstance(index, slice) and index == SLICE_ALL: + return self.copy() + elif is_iterable(index): + return [self.items[i] for i in index] + elif hasattr(index, "__index__") or isinstance(index, slice): + result = self.items[index] + if isinstance(result, list): + return self.__class__(result) + else: + return result + else: + raise TypeError("Don't know how to index an OrderedSet by %r" % index) + + def copy(self): + """ + Return a shallow copy of this object. + + Example: + >>> this = OrderedSet([1, 2, 3]) + >>> other = this.copy() + >>> this == other + True + >>> this is other + False + """ + return self.__class__(self) + + def __getstate__(self): + if len(self) == 0: + # The state can't be an empty list. + # We need to return a truthy value, or else __setstate__ won't be run. + # + # This could have been done more gracefully by always putting the state + # in a tuple, but this way is backwards- and forwards- compatible with + # previous versions of OrderedSet. + return (None,) + else: + return list(self) + + def __setstate__(self, state): + if state == (None,): + self.__init__([]) + else: + self.__init__(state) + + def __contains__(self, key): + """ + Test if the item is in this ordered set + + Example: + >>> 1 in OrderedSet([1, 3, 2]) + True + >>> 5 in OrderedSet([1, 3, 2]) + False + """ + return key in self.map + + def add(self, key): + """ + Add `key` as an item to this OrderedSet, then return its index. + + If `key` is already in the OrderedSet, return the index it already + had. + + Example: + >>> oset = OrderedSet() + >>> oset.append(3) + 0 + >>> print(oset) + OrderedSet([3]) + """ + if key not in self.map: + self.map[key] = len(self.items) + self.items.append(key) + return self.map[key] + + append = add + + def update(self, sequence): + """ + Update the set with the given iterable sequence, then return the index + of the last element inserted. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset.update([3, 1, 5, 1, 4]) + 4 + >>> print(oset) + OrderedSet([1, 2, 3, 5, 4]) + """ + item_index = None + try: + for item in sequence: + item_index = self.add(item) + except TypeError: + raise ValueError( + "Argument needs to be an iterable, got %s" % type(sequence) + ) + return item_index + + def index(self, key): + """ + Get the index of a given entry, raising an IndexError if it's not + present. + + `key` can be an iterable of entries that is not a string, in which case + this returns a list of indices. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset.index(2) + 1 + """ + if is_iterable(key): + return [self.index(subkey) for subkey in key] + return self.map[key] + + # Provide some compatibility with pd.Index + get_loc = index + get_indexer = index + + def pop(self): + """ + Remove and return the last element from the set. + + Raises KeyError if the set is empty. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset.pop() + 3 + """ + if not self.items: + raise KeyError("Set is empty") + + elem = self.items[-1] + del self.items[-1] + del self.map[elem] + return elem + + def discard(self, key): + """ + Remove an element. Do not raise an exception if absent. + + The MutableSet mixin uses this to implement the .remove() method, which + *does* raise an error when asked to remove a non-existent item. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset.discard(2) + >>> print(oset) + OrderedSet([1, 3]) + >>> oset.discard(2) + >>> print(oset) + OrderedSet([1, 3]) + """ + if key in self: + i = self.map[key] + del self.items[i] + del self.map[key] + for k, v in self.map.items(): + if v >= i: + self.map[k] = v - 1 + + def clear(self): + """ + Remove all items from this OrderedSet. + """ + del self.items[:] + self.map.clear() + + def __iter__(self): + """ + Example: + >>> list(iter(OrderedSet([1, 2, 3]))) + [1, 2, 3] + """ + return iter(self.items) + + def __reversed__(self): + """ + Example: + >>> list(reversed(OrderedSet([1, 2, 3]))) + [3, 2, 1] + """ + return reversed(self.items) + + def __repr__(self): + if not self: + return "%s()" % (self.__class__.__name__,) + return "%s(%r)" % (self.__class__.__name__, list(self)) + + def __eq__(self, other): + """ + Returns true if the containers have the same items. If `other` is a + Sequence, then order is checked, otherwise it is ignored. + + Example: + >>> oset = OrderedSet([1, 3, 2]) + >>> oset == [1, 3, 2] + True + >>> oset == [1, 2, 3] + False + >>> oset == [2, 3] + False + >>> oset == OrderedSet([3, 2, 1]) + False + """ + # In Python 2 deque is not a Sequence, so treat it as one for + # consistent behavior with Python 3. + if isinstance(other, (Sequence, deque)): + # Check that this OrderedSet contains the same elements, in the + # same order, as the other object. + return list(self) == list(other) + try: + other_as_set = set(other) + except TypeError: + # If `other` can't be converted into a set, it's not equal. + return False + else: + return set(self) == other_as_set + + def union(self, *sets): + """ + Combines all unique items. + Each items order is defined by its first appearance. + + Example: + >>> oset = OrderedSet.union(OrderedSet([3, 1, 4, 1, 5]), [1, 3], [2, 0]) + >>> print(oset) + OrderedSet([3, 1, 4, 5, 2, 0]) + >>> oset.union([8, 9]) + OrderedSet([3, 1, 4, 5, 2, 0, 8, 9]) + >>> oset | {10} + OrderedSet([3, 1, 4, 5, 2, 0, 10]) + """ + cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet + containers = map(list, it.chain([self], sets)) + items = it.chain.from_iterable(containers) + return cls(items) + + def __and__(self, other): + # the parent implementation of this is backwards + return self.intersection(other) + + def intersection(self, *sets): + """ + Returns elements in common between all sets. Order is defined only + by the first set. + + Example: + >>> oset = OrderedSet.intersection(OrderedSet([0, 1, 2, 3]), [1, 2, 3]) + >>> print(oset) + OrderedSet([1, 2, 3]) + >>> oset.intersection([2, 4, 5], [1, 2, 3, 4]) + OrderedSet([2]) + >>> oset.intersection() + OrderedSet([1, 2, 3]) + """ + cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet + if sets: + common = set.intersection(*map(set, sets)) + items = (item for item in self if item in common) + else: + items = self + return cls(items) + + def difference(self, *sets): + """ + Returns all elements that are in this set but not the others. + + Example: + >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2])) + OrderedSet([1, 3]) + >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2]), OrderedSet([3])) + OrderedSet([1]) + >>> OrderedSet([1, 2, 3]) - OrderedSet([2]) + OrderedSet([1, 3]) + >>> OrderedSet([1, 2, 3]).difference() + OrderedSet([1, 2, 3]) + """ + cls = self.__class__ + if sets: + other = set.union(*map(set, sets)) + items = (item for item in self if item not in other) + else: + items = self + return cls(items) + + def issubset(self, other): + """ + Report whether another set contains this set. + + Example: + >>> OrderedSet([1, 2, 3]).issubset({1, 2}) + False + >>> OrderedSet([1, 2, 3]).issubset({1, 2, 3, 4}) + True + >>> OrderedSet([1, 2, 3]).issubset({1, 4, 3, 5}) + False + """ + if len(self) > len(other): # Fast check for obvious cases + return False + return all(item in other for item in self) + + def issuperset(self, other): + """ + Report whether this set contains another set. + + Example: + >>> OrderedSet([1, 2]).issuperset([1, 2, 3]) + False + >>> OrderedSet([1, 2, 3, 4]).issuperset({1, 2, 3}) + True + >>> OrderedSet([1, 4, 3, 5]).issuperset({1, 2, 3}) + False + """ + if len(self) < len(other): # Fast check for obvious cases + return False + return all(item in self for item in other) + + def symmetric_difference(self, other): + """ + Return the symmetric difference of two OrderedSets as a new set. + That is, the new set will contain all elements that are in exactly + one of the sets. + + Their order will be preserved, with elements from `self` preceding + elements from `other`. + + Example: + >>> this = OrderedSet([1, 4, 3, 5, 7]) + >>> other = OrderedSet([9, 7, 1, 3, 2]) + >>> this.symmetric_difference(other) + OrderedSet([4, 5, 9, 2]) + """ + cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet + diff1 = cls(self).difference(other) + diff2 = cls(other).difference(self) + return diff1.union(diff2) + + def _update_items(self, items): + """ + Replace the 'items' list of this OrderedSet with a new one, updating + self.map accordingly. + """ + self.items = items + self.map = {item: idx for (idx, item) in enumerate(items)} + + def difference_update(self, *sets): + """ + Update this OrderedSet to remove items from one or more other sets. + + Example: + >>> this = OrderedSet([1, 2, 3]) + >>> this.difference_update(OrderedSet([2, 4])) + >>> print(this) + OrderedSet([1, 3]) + + >>> this = OrderedSet([1, 2, 3, 4, 5]) + >>> this.difference_update(OrderedSet([2, 4]), OrderedSet([1, 4, 6])) + >>> print(this) + OrderedSet([3, 5]) + """ + items_to_remove = set() + for other in sets: + items_to_remove |= set(other) + self._update_items([item for item in self.items if item not in items_to_remove]) + + def intersection_update(self, other): + """ + Update this OrderedSet to keep only items in another set, preserving + their order in this set. + + Example: + >>> this = OrderedSet([1, 4, 3, 5, 7]) + >>> other = OrderedSet([9, 7, 1, 3, 2]) + >>> this.intersection_update(other) + >>> print(this) + OrderedSet([1, 3, 7]) + """ + other = set(other) + self._update_items([item for item in self.items if item in other]) + + def symmetric_difference_update(self, other): + """ + Update this OrderedSet to remove items from another set, then + add items from the other set that were not present in this set. + + Example: + >>> this = OrderedSet([1, 4, 3, 5, 7]) + >>> other = OrderedSet([9, 7, 1, 3, 2]) + >>> this.symmetric_difference_update(other) + >>> print(this) + OrderedSet([4, 5, 9, 2]) + """ + items_to_add = [item for item in other if item not in self] + items_to_remove = set(other) + self._update_items( + [item for item in self.items if item not in items_to_remove] + items_to_add + ) diff --git a/_setuptools_vendored/packaging-20.4.dist-info/LICENSE b/_setuptools_vendored/packaging-20.4.dist-info/LICENSE new file mode 100644 index 0000000000..6f62d44e4e --- /dev/null +++ b/_setuptools_vendored/packaging-20.4.dist-info/LICENSE @@ -0,0 +1,3 @@ +This software is made available under the terms of *either* of the licenses +found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made +under the terms of *both* these licenses. diff --git a/_setuptools_vendored/packaging-20.4.dist-info/LICENSE.APACHE b/_setuptools_vendored/packaging-20.4.dist-info/LICENSE.APACHE new file mode 100644 index 0000000000..f433b1a53f --- /dev/null +++ b/_setuptools_vendored/packaging-20.4.dist-info/LICENSE.APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/_setuptools_vendored/packaging-20.4.dist-info/LICENSE.BSD b/_setuptools_vendored/packaging-20.4.dist-info/LICENSE.BSD new file mode 100644 index 0000000000..42ce7b75c9 --- /dev/null +++ b/_setuptools_vendored/packaging-20.4.dist-info/LICENSE.BSD @@ -0,0 +1,23 @@ +Copyright (c) Donald Stufft and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/_setuptools_vendored/packaging-20.4.dist-info/METADATA b/_setuptools_vendored/packaging-20.4.dist-info/METADATA new file mode 100644 index 0000000000..4f5fb30492 --- /dev/null +++ b/_setuptools_vendored/packaging-20.4.dist-info/METADATA @@ -0,0 +1,373 @@ +Metadata-Version: 2.1 +Name: packaging +Version: 20.4 +Summary: Core utilities for Python packages +Home-page: https://github.com/pypa/packaging +Author: Donald Stufft and individual contributors +Author-email: donald@stufft.io +License: BSD-2-Clause or Apache-2.0 +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: License :: OSI Approved :: BSD License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* +Description-Content-Type: text/x-rst +Requires-Dist: pyparsing (>=2.0.2) +Requires-Dist: six + +packaging +========= + +Core utilities for Python packages. + +The ``packaging`` project includes the following: version handling, specifiers, +markers, requirements, tags, utilities. + +Documentation +------------- + +The `documentation`_ provides information and the API for the following: + +- Version Handling +- Specifiers +- Markers +- Requirements +- Tags +- Utilities + +Installation +------------ + +Use ``pip`` to install these utilities:: + + pip install packaging + +Discussion +---------- + +If you run into bugs, you can file them in our `issue tracker`_. + +You can also join ``#pypa`` on Freenode to ask questions or get involved. + + +.. _`documentation`: https://packaging.pypa.io/ +.. _`issue tracker`: https://github.com/pypa/packaging/issues + + +Code of Conduct +--------------- + +Everyone interacting in the packaging project's codebases, issue trackers, chat +rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_. + +.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ + +Contributing +------------ + +The ``CONTRIBUTING.rst`` file outlines how to contribute to this project as +well as how to report a potential security issue. The documentation for this +project also covers information about `project development`_ and `security`_. + +.. _`project development`: https://packaging.pypa.io/en/latest/development/ +.. _`security`: https://packaging.pypa.io/en/latest/security/ + +Project History +--------------- + +Please review the ``CHANGELOG.rst`` file or the `Changelog documentation`_ for +recent changes and project history. + +.. _`Changelog documentation`: https://packaging.pypa.io/en/latest/changelog/ + +Changelog +--------- + +20.4 - 2020-05-19 +~~~~~~~~~~~~~~~~~ + +* Canonicalize version before comparing specifiers. (`#282 `__) +* Change type hint for ``canonicalize_name`` to return + ``packaging.utils.NormalizedName``. + This enables the use of static typing tools (like mypy) to detect mixing of + normalized and un-normalized names. + +20.3 - 2020-03-05 +~~~~~~~~~~~~~~~~~ + +* Fix changelog for 20.2. + +20.2 - 2020-03-05 +~~~~~~~~~~~~~~~~~ + +* Fix a bug that caused a 32-bit OS that runs on a 64-bit ARM CPU (e.g. ARM-v8, + aarch64), to report the wrong bitness. + +20.1 - 2020-01-24 +~~~~~~~~~~~~~~~~~~~ + +* Fix a bug caused by reuse of an exhausted iterator. (`#257 `__) + +20.0 - 2020-01-06 +~~~~~~~~~~~~~~~~~ + +* Add type hints (`#191 `__) + +* Add proper trove classifiers for PyPy support (`#198 `__) + +* Scale back depending on ``ctypes`` for manylinux support detection (`#171 `__) + +* Use ``sys.implementation.name`` where appropriate for ``packaging.tags`` (`#193 `__) + +* Expand upon the API provded by ``packaging.tags``: ``interpreter_name()``, ``mac_platforms()``, ``compatible_tags()``, ``cpython_tags()``, ``generic_tags()`` (`#187 `__) + +* Officially support Python 3.8 (`#232 `__) + +* Add ``major``, ``minor``, and ``micro`` aliases to ``packaging.version.Version`` (`#226 `__) + +* Properly mark ``packaging`` has being fully typed by adding a `py.typed` file (`#226 `__) + +19.2 - 2019-09-18 +~~~~~~~~~~~~~~~~~ + +* Remove dependency on ``attrs`` (`#178 `__, `#179 `__) + +* Use appropriate fallbacks for CPython ABI tag (`#181 `__, `#185 `__) + +* Add manylinux2014 support (`#186 `__) + +* Improve ABI detection (`#181 `__) + +* Properly handle debug wheels for Python 3.8 (`#172 `__) + +* Improve detection of debug builds on Windows (`#194 `__) + +19.1 - 2019-07-30 +~~~~~~~~~~~~~~~~~ + +* Add the ``packaging.tags`` module. (`#156 `__) + +* Correctly handle two-digit versions in ``python_version`` (`#119 `__) + + +19.0 - 2019-01-20 +~~~~~~~~~~~~~~~~~ + +* Fix string representation of PEP 508 direct URL requirements with markers. + +* Better handling of file URLs + + This allows for using ``file:///absolute/path``, which was previously + prevented due to the missing ``netloc``. + + This allows for all file URLs that ``urlunparse`` turns back into the + original URL to be valid. + + +18.0 - 2018-09-26 +~~~~~~~~~~~~~~~~~ + +* Improve error messages when invalid requirements are given. (`#129 `__) + + +17.1 - 2017-02-28 +~~~~~~~~~~~~~~~~~ + +* Fix ``utils.canonicalize_version`` when supplying non PEP 440 versions. + + +17.0 - 2017-02-28 +~~~~~~~~~~~~~~~~~ + +* Drop support for python 2.6, 3.2, and 3.3. + +* Define minimal pyparsing version to 2.0.2 (`#91 `__). + +* Add ``epoch``, ``release``, ``pre``, ``dev``, and ``post`` attributes to + ``Version`` and ``LegacyVersion`` (`#34 `__). + +* Add ``Version().is_devrelease`` and ``LegacyVersion().is_devrelease`` to + make it easy to determine if a release is a development release. + +* Add ``utils.canonicalize_version`` to canonicalize version strings or + ``Version`` instances (`#121 `__). + + +16.8 - 2016-10-29 +~~~~~~~~~~~~~~~~~ + +* Fix markers that utilize ``in`` so that they render correctly. + +* Fix an erroneous test on Python RC releases. + + +16.7 - 2016-04-23 +~~~~~~~~~~~~~~~~~ + +* Add support for the deprecated ``python_implementation`` marker which was + an undocumented setuptools marker in addition to the newer markers. + + +16.6 - 2016-03-29 +~~~~~~~~~~~~~~~~~ + +* Add support for the deprecated, PEP 345 environment markers in addition to + the newer markers. + + +16.5 - 2016-02-26 +~~~~~~~~~~~~~~~~~ + +* Fix a regression in parsing requirements with whitespaces between the comma + separators. + + +16.4 - 2016-02-22 +~~~~~~~~~~~~~~~~~ + +* Fix a regression in parsing requirements like ``foo (==4)``. + + +16.3 - 2016-02-21 +~~~~~~~~~~~~~~~~~ + +* Fix a bug where ``packaging.requirements:Requirement`` was overly strict when + matching legacy requirements. + + +16.2 - 2016-02-09 +~~~~~~~~~~~~~~~~~ + +* Add a function that implements the name canonicalization from PEP 503. + + +16.1 - 2016-02-07 +~~~~~~~~~~~~~~~~~ + +* Implement requirement specifiers from PEP 508. + + +16.0 - 2016-01-19 +~~~~~~~~~~~~~~~~~ + +* Relicense so that packaging is available under *either* the Apache License, + Version 2.0 or a 2 Clause BSD license. + +* Support installation of packaging when only distutils is available. + +* Fix ``==`` comparison when there is a prefix and a local version in play. + (`#41 `__). + +* Implement environment markers from PEP 508. + + +15.3 - 2015-08-01 +~~~~~~~~~~~~~~~~~ + +* Normalize post-release spellings for rev/r prefixes. `#35 `__ + + +15.2 - 2015-05-13 +~~~~~~~~~~~~~~~~~ + +* Fix an error where the arbitary specifier (``===``) was not correctly + allowing pre-releases when it was being used. + +* Expose the specifier and version parts through properties on the + ``Specifier`` classes. + +* Allow iterating over the ``SpecifierSet`` to get access to all of the + ``Specifier`` instances. + +* Allow testing if a version is contained within a specifier via the ``in`` + operator. + + +15.1 - 2015-04-13 +~~~~~~~~~~~~~~~~~ + +* Fix a logic error that was causing inconsistent answers about whether or not + a pre-release was contained within a ``SpecifierSet`` or not. + + +15.0 - 2015-01-02 +~~~~~~~~~~~~~~~~~ + +* Add ``Version().is_postrelease`` and ``LegacyVersion().is_postrelease`` to + make it easy to determine if a release is a post release. + +* Add ``Version().base_version`` and ``LegacyVersion().base_version`` to make + it easy to get the public version without any pre or post release markers. + +* Support the update to PEP 440 which removed the implied ``!=V.*`` when using + either ``>V`` or ``V`` or ````) operator. + + +14.3 - 2014-11-19 +~~~~~~~~~~~~~~~~~ + +* **BACKWARDS INCOMPATIBLE** Refactor specifier support so that it can sanely + handle legacy specifiers as well as PEP 440 specifiers. + +* **BACKWARDS INCOMPATIBLE** Move the specifier support out of + ``packaging.version`` into ``packaging.specifiers``. + + +14.2 - 2014-09-10 +~~~~~~~~~~~~~~~~~ + +* Add prerelease support to ``Specifier``. +* Remove the ability to do ``item in Specifier()`` and replace it with + ``Specifier().contains(item)`` in order to allow flags that signal if a + prerelease should be accepted or not. +* Add a method ``Specifier().filter()`` which will take an iterable and returns + an iterable with items that do not match the specifier filtered out. + + +14.1 - 2014-09-08 +~~~~~~~~~~~~~~~~~ + +* Allow ``LegacyVersion`` and ``Version`` to be sorted together. +* Add ``packaging.version.parse()`` to enable easily parsing a version string + as either a ``Version`` or a ``LegacyVersion`` depending on it's PEP 440 + validity. + + +14.0 - 2014-09-05 +~~~~~~~~~~~~~~~~~ + +* Initial release. + + +.. _`master`: https://github.com/pypa/packaging/ + + diff --git a/_setuptools_vendored/packaging-20.4.dist-info/RECORD b/_setuptools_vendored/packaging-20.4.dist-info/RECORD new file mode 100644 index 0000000000..1880a93d64 --- /dev/null +++ b/_setuptools_vendored/packaging-20.4.dist-info/RECORD @@ -0,0 +1,19 @@ +packaging/__about__.py,sha256=PNMsaZn4UcCHyubgROH1bl6CluduPjI5kFrSp_Zgklo,736 +packaging/__init__.py,sha256=6enbp5XgRfjBjsI9-bn00HjHf5TH21PDMOKkJW8xw-w,562 +packaging/_compat.py,sha256=MXdsGpSE_W-ZrHoC87andI4LV2FAwU7HLL-eHe_CjhU,1128 +packaging/_structures.py,sha256=ozkCX8Q8f2qE1Eic3YiQ4buDVfgz2iYevY9e7R2y3iY,2022 +packaging/_typing.py,sha256=x59EhQ57TMT-kTRyLZV25HZvYGGwbucTo6iKh_O0tMw,1812 +packaging/markers.py,sha256=ynIh92WKxQTx663RE66zJ4r4MlNHPWTFYzP0Q1e-cLc,9455 +packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +packaging/requirements.py,sha256=nvrgDjjCJZbb4lylquBO5oXQbY5dIlcq6CyQxjYJzqM,4855 +packaging/specifiers.py,sha256=uYp9l13F0LcknS6d4N60ytiBgFmIhKideOq9AnsxTco,31944 +packaging/tags.py,sha256=NKMS37Zo_nWrZxgsD6zbXsXgc9edn9m160cBiLmHJdE,24067 +packaging/utils.py,sha256=RShlvnjO2CtYSD8uri32frMMFMTmB-3ihsq1-ghzLEw,1811 +packaging/version.py,sha256=Cnbm-OO9D_qd8ZTFxzFcjSavexSYFZmyeaoPvMsjgPc,15470 +packaging-20.4.dist-info/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197 +packaging-20.4.dist-info/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174 +packaging-20.4.dist-info/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344 +packaging-20.4.dist-info/METADATA,sha256=NPvtLUTeeRypc68HcgRkyEiIips2o9rChFrbj0t2MjE,11212 +packaging-20.4.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 +packaging-20.4.dist-info/top_level.txt,sha256=zFdHrhWnPslzsiP455HutQsqPB6v0KCtNUMtUtrefDw,10 +packaging-20.4.dist-info/RECORD,, diff --git a/_setuptools_vendored/packaging-20.4.dist-info/WHEEL b/_setuptools_vendored/packaging-20.4.dist-info/WHEEL new file mode 100644 index 0000000000..ef99c6cf32 --- /dev/null +++ b/_setuptools_vendored/packaging-20.4.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.34.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/_setuptools_vendored/packaging-20.4.dist-info/top_level.txt b/_setuptools_vendored/packaging-20.4.dist-info/top_level.txt new file mode 100644 index 0000000000..748809f75c --- /dev/null +++ b/_setuptools_vendored/packaging-20.4.dist-info/top_level.txt @@ -0,0 +1 @@ +packaging diff --git a/_setuptools_vendored/packaging/__about__.py b/_setuptools_vendored/packaging/__about__.py new file mode 100644 index 0000000000..4d998578d7 --- /dev/null +++ b/_setuptools_vendored/packaging/__about__.py @@ -0,0 +1,27 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +__all__ = [ + "__title__", + "__summary__", + "__uri__", + "__version__", + "__author__", + "__email__", + "__license__", + "__copyright__", +] + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "20.4" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD-2-Clause or Apache-2.0" +__copyright__ = "Copyright 2014-2019 %s" % __author__ diff --git a/_setuptools_vendored/packaging/__init__.py b/_setuptools_vendored/packaging/__init__.py new file mode 100644 index 0000000000..a0cf67df52 --- /dev/null +++ b/_setuptools_vendored/packaging/__init__.py @@ -0,0 +1,26 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +from .__about__ import ( + __author__, + __copyright__, + __email__, + __license__, + __summary__, + __title__, + __uri__, + __version__, +) + +__all__ = [ + "__title__", + "__summary__", + "__uri__", + "__version__", + "__author__", + "__email__", + "__license__", + "__copyright__", +] diff --git a/_setuptools_vendored/packaging/_compat.py b/_setuptools_vendored/packaging/_compat.py new file mode 100644 index 0000000000..e54bd4ede8 --- /dev/null +++ b/_setuptools_vendored/packaging/_compat.py @@ -0,0 +1,38 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import sys + +from ._typing import TYPE_CHECKING + +if TYPE_CHECKING: # pragma: no cover + from typing import Any, Dict, Tuple, Type + + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + +# flake8: noqa + +if PY3: + string_types = (str,) +else: + string_types = (basestring,) + + +def with_metaclass(meta, *bases): + # type: (Type[Any], Tuple[Type[Any], ...]) -> Any + """ + Create a base class with a metaclass. + """ + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): # type: ignore + def __new__(cls, name, this_bases, d): + # type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any + return meta(name, bases, d) + + return type.__new__(metaclass, "temporary_class", (), {}) diff --git a/_setuptools_vendored/packaging/_structures.py b/_setuptools_vendored/packaging/_structures.py new file mode 100644 index 0000000000..800d5c5588 --- /dev/null +++ b/_setuptools_vendored/packaging/_structures.py @@ -0,0 +1,86 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + + +class InfinityType(object): + def __repr__(self): + # type: () -> str + return "Infinity" + + def __hash__(self): + # type: () -> int + return hash(repr(self)) + + def __lt__(self, other): + # type: (object) -> bool + return False + + def __le__(self, other): + # type: (object) -> bool + return False + + def __eq__(self, other): + # type: (object) -> bool + return isinstance(other, self.__class__) + + def __ne__(self, other): + # type: (object) -> bool + return not isinstance(other, self.__class__) + + def __gt__(self, other): + # type: (object) -> bool + return True + + def __ge__(self, other): + # type: (object) -> bool + return True + + def __neg__(self): + # type: (object) -> NegativeInfinityType + return NegativeInfinity + + +Infinity = InfinityType() + + +class NegativeInfinityType(object): + def __repr__(self): + # type: () -> str + return "-Infinity" + + def __hash__(self): + # type: () -> int + return hash(repr(self)) + + def __lt__(self, other): + # type: (object) -> bool + return True + + def __le__(self, other): + # type: (object) -> bool + return True + + def __eq__(self, other): + # type: (object) -> bool + return isinstance(other, self.__class__) + + def __ne__(self, other): + # type: (object) -> bool + return not isinstance(other, self.__class__) + + def __gt__(self, other): + # type: (object) -> bool + return False + + def __ge__(self, other): + # type: (object) -> bool + return False + + def __neg__(self): + # type: (object) -> InfinityType + return Infinity + + +NegativeInfinity = NegativeInfinityType() diff --git a/_setuptools_vendored/packaging/_typing.py b/_setuptools_vendored/packaging/_typing.py new file mode 100644 index 0000000000..77a8b9185a --- /dev/null +++ b/_setuptools_vendored/packaging/_typing.py @@ -0,0 +1,48 @@ +"""For neatly implementing static typing in packaging. + +`mypy` - the static type analysis tool we use - uses the `typing` module, which +provides core functionality fundamental to mypy's functioning. + +Generally, `typing` would be imported at runtime and used in that fashion - +it acts as a no-op at runtime and does not have any run-time overhead by +design. + +As it turns out, `typing` is not vendorable - it uses separate sources for +Python 2/Python 3. Thus, this codebase can not expect it to be present. +To work around this, mypy allows the typing import to be behind a False-y +optional to prevent it from running at runtime and type-comments can be used +to remove the need for the types to be accessible directly during runtime. + +This module provides the False-y guard in a nicely named fashion so that a +curious maintainer can reach here to read this. + +In packaging, all static-typing related imports should be guarded as follows: + + from packaging._typing import TYPE_CHECKING + + if TYPE_CHECKING: + from typing import ... + +Ref: https://github.com/python/mypy/issues/3216 +""" + +__all__ = ["TYPE_CHECKING", "cast"] + +# The TYPE_CHECKING constant defined by the typing module is False at runtime +# but True while type checking. +if False: # pragma: no cover + from typing import TYPE_CHECKING +else: + TYPE_CHECKING = False + +# typing's cast syntax requires calling typing.cast at runtime, but we don't +# want to import typing at runtime. Here, we inform the type checkers that +# we're importing `typing.cast` as `cast` and re-implement typing.cast's +# runtime behavior in a block that is ignored by type checkers. +if TYPE_CHECKING: # pragma: no cover + # not executed at runtime + from typing import cast +else: + # executed at runtime + def cast(type_, value): # noqa + return value diff --git a/_setuptools_vendored/packaging/markers.py b/_setuptools_vendored/packaging/markers.py new file mode 100644 index 0000000000..87cd3f9585 --- /dev/null +++ b/_setuptools_vendored/packaging/markers.py @@ -0,0 +1,328 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import operator +import os +import platform +import sys + +from pyparsing import ParseException, ParseResults, stringStart, stringEnd +from pyparsing import ZeroOrMore, Group, Forward, QuotedString +from pyparsing import Literal as L # noqa + +from ._compat import string_types +from ._typing import TYPE_CHECKING +from .specifiers import Specifier, InvalidSpecifier + +if TYPE_CHECKING: # pragma: no cover + from typing import Any, Callable, Dict, List, Optional, Tuple, Union + + Operator = Callable[[str, str], bool] + + +__all__ = [ + "InvalidMarker", + "UndefinedComparison", + "UndefinedEnvironmentName", + "Marker", + "default_environment", +] + + +class InvalidMarker(ValueError): + """ + An invalid marker was found, users should refer to PEP 508. + """ + + +class UndefinedComparison(ValueError): + """ + An invalid operation was attempted on a value that doesn't support it. + """ + + +class UndefinedEnvironmentName(ValueError): + """ + A name was attempted to be used that does not exist inside of the + environment. + """ + + +class Node(object): + def __init__(self, value): + # type: (Any) -> None + self.value = value + + def __str__(self): + # type: () -> str + return str(self.value) + + def __repr__(self): + # type: () -> str + return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) + + def serialize(self): + # type: () -> str + raise NotImplementedError + + +class Variable(Node): + def serialize(self): + # type: () -> str + return str(self) + + +class Value(Node): + def serialize(self): + # type: () -> str + return '"{0}"'.format(self) + + +class Op(Node): + def serialize(self): + # type: () -> str + return str(self) + + +VARIABLE = ( + L("implementation_version") + | L("platform_python_implementation") + | L("implementation_name") + | L("python_full_version") + | L("platform_release") + | L("platform_version") + | L("platform_machine") + | L("platform_system") + | L("python_version") + | L("sys_platform") + | L("os_name") + | L("os.name") # PEP-345 + | L("sys.platform") # PEP-345 + | L("platform.version") # PEP-345 + | L("platform.machine") # PEP-345 + | L("platform.python_implementation") # PEP-345 + | L("python_implementation") # undocumented setuptools legacy + | L("extra") # PEP-508 +) +ALIASES = { + "os.name": "os_name", + "sys.platform": "sys_platform", + "platform.version": "platform_version", + "platform.machine": "platform_machine", + "platform.python_implementation": "platform_python_implementation", + "python_implementation": "platform_python_implementation", +} +VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) + +VERSION_CMP = ( + L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") +) + +MARKER_OP = VERSION_CMP | L("not in") | L("in") +MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) + +MARKER_VALUE = QuotedString("'") | QuotedString('"') +MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) + +BOOLOP = L("and") | L("or") + +MARKER_VAR = VARIABLE | MARKER_VALUE + +MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) +MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) + +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() + +MARKER_EXPR = Forward() +MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) +MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) + +MARKER = stringStart + MARKER_EXPR + stringEnd + + +def _coerce_parse_result(results): + # type: (Union[ParseResults, List[Any]]) -> List[Any] + if isinstance(results, ParseResults): + return [_coerce_parse_result(i) for i in results] + else: + return results + + +def _format_marker(marker, first=True): + # type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str + + assert isinstance(marker, (list, tuple, string_types)) + + # Sometimes we have a structure like [[...]] which is a single item list + # where the single item is itself it's own list. In that case we want skip + # the rest of this function so that we don't get extraneous () on the + # outside. + if ( + isinstance(marker, list) + and len(marker) == 1 + and isinstance(marker[0], (list, tuple)) + ): + return _format_marker(marker[0]) + + if isinstance(marker, list): + inner = (_format_marker(m, first=False) for m in marker) + if first: + return " ".join(inner) + else: + return "(" + " ".join(inner) + ")" + elif isinstance(marker, tuple): + return " ".join([m.serialize() for m in marker]) + else: + return marker + + +_operators = { + "in": lambda lhs, rhs: lhs in rhs, + "not in": lambda lhs, rhs: lhs not in rhs, + "<": operator.lt, + "<=": operator.le, + "==": operator.eq, + "!=": operator.ne, + ">=": operator.ge, + ">": operator.gt, +} # type: Dict[str, Operator] + + +def _eval_op(lhs, op, rhs): + # type: (str, Op, str) -> bool + try: + spec = Specifier("".join([op.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs) + + oper = _operators.get(op.serialize()) # type: Optional[Operator] + if oper is None: + raise UndefinedComparison( + "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) + ) + + return oper(lhs, rhs) + + +class Undefined(object): + pass + + +_undefined = Undefined() + + +def _get_env(environment, name): + # type: (Dict[str, str], str) -> str + value = environment.get(name, _undefined) # type: Union[str, Undefined] + + if isinstance(value, Undefined): + raise UndefinedEnvironmentName( + "{0!r} does not exist in evaluation environment.".format(name) + ) + + return value + + +def _evaluate_markers(markers, environment): + # type: (List[Any], Dict[str, str]) -> bool + groups = [[]] # type: List[List[bool]] + + for marker in markers: + assert isinstance(marker, (list, tuple, string_types)) + + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + lhs_value = _get_env(environment, lhs.value) + rhs_value = rhs.value + else: + lhs_value = lhs.value + rhs_value = _get_env(environment, rhs.value) + + groups[-1].append(_eval_op(lhs_value, op, rhs_value)) + else: + assert marker in ["and", "or"] + if marker == "or": + groups.append([]) + + return any(all(item) for item in groups) + + +def format_full_version(info): + # type: (sys._version_info) -> str + version = "{0.major}.{0.minor}.{0.micro}".format(info) + kind = info.releaselevel + if kind != "final": + version += kind[0] + str(info.serial) + return version + + +def default_environment(): + # type: () -> Dict[str, str] + if hasattr(sys, "implementation"): + # Ignoring the `sys.implementation` reference for type checking due to + # mypy not liking that the attribute doesn't exist in Python 2.7 when + # run with the `--py27` flag. + iver = format_full_version(sys.implementation.version) # type: ignore + implementation_name = sys.implementation.name # type: ignore + else: + iver = "0" + implementation_name = "" + + return { + "implementation_name": implementation_name, + "implementation_version": iver, + "os_name": os.name, + "platform_machine": platform.machine(), + "platform_release": platform.release(), + "platform_system": platform.system(), + "platform_version": platform.version(), + "python_full_version": platform.python_version(), + "platform_python_implementation": platform.python_implementation(), + "python_version": ".".join(platform.python_version_tuple()[:2]), + "sys_platform": sys.platform, + } + + +class Marker(object): + def __init__(self, marker): + # type: (str) -> None + try: + self._markers = _coerce_parse_result(MARKER.parseString(marker)) + except ParseException as e: + err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( + marker, marker[e.loc : e.loc + 8] + ) + raise InvalidMarker(err_str) + + def __str__(self): + # type: () -> str + return _format_marker(self._markers) + + def __repr__(self): + # type: () -> str + return "".format(str(self)) + + def evaluate(self, environment=None): + # type: (Optional[Dict[str, str]]) -> bool + """Evaluate a marker. + + Return the boolean from evaluating the given marker against the + environment. environment is an optional argument to override all or + part of the determined environment. + + The environment is determined from the current Python process. + """ + current_environment = default_environment() + if environment is not None: + current_environment.update(environment) + + return _evaluate_markers(self._markers, current_environment) diff --git a/_setuptools_vendored/packaging/py.typed b/_setuptools_vendored/packaging/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_setuptools_vendored/packaging/requirements.py b/_setuptools_vendored/packaging/requirements.py new file mode 100644 index 0000000000..91f81ede00 --- /dev/null +++ b/_setuptools_vendored/packaging/requirements.py @@ -0,0 +1,145 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import string +import re + +from pyparsing import stringStart, stringEnd, originalTextFor, ParseException +from pyparsing import ZeroOrMore, Word, Optional, Regex, Combine +from pyparsing import Literal as L # noqa +from six.moves.urllib import parse as urlparse + +from ._typing import TYPE_CHECKING +from .markers import MARKER_EXPR, Marker +from .specifiers import LegacySpecifier, Specifier, SpecifierSet + +if TYPE_CHECKING: # pragma: no cover + from typing import List + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +ALPHANUM = Word(string.ascii_letters + string.digits) + +LBRACKET = L("[").suppress() +RBRACKET = L("]").suppress() +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() +COMMA = L(",").suppress() +SEMICOLON = L(";").suppress() +AT = L("@").suppress() + +PUNCTUATION = Word("-_.") +IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) +IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) + +NAME = IDENTIFIER("name") +EXTRA = IDENTIFIER + +URI = Regex(r"[^ ]+")("url") +URL = AT + URI + +EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) +EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") + +VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) +VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) + +VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY +VERSION_MANY = Combine( + VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False +)("_raw_spec") +_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) +_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") + +VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") +VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) + +MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") +MARKER_EXPR.setParseAction( + lambda s, l, t: Marker(s[t._original_start : t._original_end]) +) +MARKER_SEPARATOR = SEMICOLON +MARKER = MARKER_SEPARATOR + MARKER_EXPR + +VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) +URL_AND_MARKER = URL + Optional(MARKER) + +NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) + +REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd +# pyparsing isn't thread safe during initialization, so we do it eagerly, see +# issue #104 +REQUIREMENT.parseString("x[]") + + +class Requirement(object): + """Parse a requirement. + + Parse a given requirement string into its parts, such as name, specifier, + URL, and extras. Raises InvalidRequirement on a badly-formed requirement + string. + """ + + # TODO: Can we test whether something is contained within a requirement? + # If so how do we do that? Do we need to test against the _name_ of + # the thing as well as the version? What about the markers? + # TODO: Can we normalize the name and extra name? + + def __init__(self, requirement_string): + # type: (str) -> None + try: + req = REQUIREMENT.parseString(requirement_string) + except ParseException as e: + raise InvalidRequirement( + 'Parse error at "{0!r}": {1}'.format( + requirement_string[e.loc : e.loc + 8], e.msg + ) + ) + + self.name = req.name + if req.url: + parsed_url = urlparse.urlparse(req.url) + if parsed_url.scheme == "file": + if urlparse.urlunparse(parsed_url) != req.url: + raise InvalidRequirement("Invalid URL given") + elif not (parsed_url.scheme and parsed_url.netloc) or ( + not parsed_url.scheme and not parsed_url.netloc + ): + raise InvalidRequirement("Invalid URL: {0}".format(req.url)) + self.url = req.url + else: + self.url = None + self.extras = set(req.extras.asList() if req.extras else []) + self.specifier = SpecifierSet(req.specifier) + self.marker = req.marker if req.marker else None + + def __str__(self): + # type: () -> str + parts = [self.name] # type: List[str] + + if self.extras: + parts.append("[{0}]".format(",".join(sorted(self.extras)))) + + if self.specifier: + parts.append(str(self.specifier)) + + if self.url: + parts.append("@ {0}".format(self.url)) + if self.marker: + parts.append(" ") + + if self.marker: + parts.append("; {0}".format(self.marker)) + + return "".join(parts) + + def __repr__(self): + # type: () -> str + return "".format(str(self)) diff --git a/_setuptools_vendored/packaging/specifiers.py b/_setuptools_vendored/packaging/specifiers.py new file mode 100644 index 0000000000..fe09bb1dbb --- /dev/null +++ b/_setuptools_vendored/packaging/specifiers.py @@ -0,0 +1,863 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import abc +import functools +import itertools +import re + +from ._compat import string_types, with_metaclass +from ._typing import TYPE_CHECKING +from .utils import canonicalize_version +from .version import Version, LegacyVersion, parse + +if TYPE_CHECKING: # pragma: no cover + from typing import ( + List, + Dict, + Union, + Iterable, + Iterator, + Optional, + Callable, + Tuple, + FrozenSet, + ) + + ParsedVersion = Union[Version, LegacyVersion] + UnparsedVersion = Union[Version, LegacyVersion, str] + CallableOperator = Callable[[ParsedVersion, str], bool] + + +class InvalidSpecifier(ValueError): + """ + An invalid specifier was found, users should refer to PEP 440. + """ + + +class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore + @abc.abstractmethod + def __str__(self): + # type: () -> str + """ + Returns the str representation of this Specifier like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self): + # type: () -> int + """ + Returns a hash value for this Specifier like object. + """ + + @abc.abstractmethod + def __eq__(self, other): + # type: (object) -> bool + """ + Returns a boolean representing whether or not the two Specifier like + objects are equal. + """ + + @abc.abstractmethod + def __ne__(self, other): + # type: (object) -> bool + """ + Returns a boolean representing whether or not the two Specifier like + objects are not equal. + """ + + @abc.abstractproperty + def prereleases(self): + # type: () -> Optional[bool] + """ + Returns whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @prereleases.setter + def prereleases(self, value): + # type: (bool) -> None + """ + Sets whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @abc.abstractmethod + def contains(self, item, prereleases=None): + # type: (str, Optional[bool]) -> bool + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter(self, iterable, prereleases=None): + # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class _IndividualSpecifier(BaseSpecifier): + + _operators = {} # type: Dict[str, str] + + def __init__(self, spec="", prereleases=None): + # type: (str, Optional[bool]) -> None + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) + + self._spec = ( + match.group("operator").strip(), + match.group("version").strip(), + ) # type: Tuple[str, str] + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + def __repr__(self): + # type: () -> str + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre) + + def __str__(self): + # type: () -> str + return "{0}{1}".format(*self._spec) + + @property + def _canonical_spec(self): + # type: () -> Tuple[str, Union[Version, str]] + return self._spec[0], canonicalize_version(self._spec[1]) + + def __hash__(self): + # type: () -> int + return hash(self._canonical_spec) + + def __eq__(self, other): + # type: (object) -> bool + if isinstance(other, string_types): + try: + other = self.__class__(str(other)) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._canonical_spec == other._canonical_spec + + def __ne__(self, other): + # type: (object) -> bool + if isinstance(other, string_types): + try: + other = self.__class__(str(other)) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec != other._spec + + def _get_operator(self, op): + # type: (str) -> CallableOperator + operator_callable = getattr( + self, "_compare_{0}".format(self._operators[op]) + ) # type: CallableOperator + return operator_callable + + def _coerce_version(self, version): + # type: (UnparsedVersion) -> ParsedVersion + if not isinstance(version, (LegacyVersion, Version)): + version = parse(version) + return version + + @property + def operator(self): + # type: () -> str + return self._spec[0] + + @property + def version(self): + # type: () -> str + return self._spec[1] + + @property + def prereleases(self): + # type: () -> Optional[bool] + return self._prereleases + + @prereleases.setter + def prereleases(self, value): + # type: (bool) -> None + self._prereleases = value + + def __contains__(self, item): + # type: (str) -> bool + return self.contains(item) + + def contains(self, item, prereleases=None): + # type: (UnparsedVersion, Optional[bool]) -> bool + + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version or LegacyVersion, this allows us to have + # a shortcut for ``"2.0" in Specifier(">=2") + normalized_item = self._coerce_version(item) + + # Determine if we should be supporting prereleases in this specifier + # or not, if we do not support prereleases than we can short circuit + # logic if this version is a prereleases. + if normalized_item.is_prerelease and not prereleases: + return False + + # Actually do the comparison to determine if this item is contained + # within this Specifier or not. + operator_callable = self._get_operator(self.operator) # type: CallableOperator + return operator_callable(normalized_item, self.version) + + def filter(self, iterable, prereleases=None): + # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] + + yielded = False + found_prereleases = [] + + kw = {"prereleases": prereleases if prereleases is not None else True} + + # Attempt to iterate over all the values in the iterable and if any of + # them match, yield them. + for version in iterable: + parsed_version = self._coerce_version(version) + + if self.contains(parsed_version, **kw): + # If our version is a prerelease, and we were not set to allow + # prereleases, then we'll store it for later incase nothing + # else matches this specifier. + if parsed_version.is_prerelease and not ( + prereleases or self.prereleases + ): + found_prereleases.append(version) + # Either this is not a prerelease, or we should have been + # accepting prereleases from the beginning. + else: + yielded = True + yield version + + # Now that we've iterated over everything, determine if we've yielded + # any values, and if we have not and we have any prereleases stored up + # then we will go ahead and yield the prereleases. + if not yielded and found_prereleases: + for version in found_prereleases: + yield version + + +class LegacySpecifier(_IndividualSpecifier): + + _regex_str = r""" + (?P(==|!=|<=|>=|<|>)) + \s* + (?P + [^,;\s)]* # Since this is a "legacy" specifier, and the version + # string can be just about anything, we match everything + # except for whitespace, a semi-colon for marker support, + # a closing paren since versions can be enclosed in + # them, and a comma since it's a version separator. + ) + """ + + _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + + _operators = { + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + } + + def _coerce_version(self, version): + # type: (Union[ParsedVersion, str]) -> LegacyVersion + if not isinstance(version, LegacyVersion): + version = LegacyVersion(str(version)) + return version + + def _compare_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool + return prospective == self._coerce_version(spec) + + def _compare_not_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool + return prospective != self._coerce_version(spec) + + def _compare_less_than_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool + return prospective <= self._coerce_version(spec) + + def _compare_greater_than_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool + return prospective >= self._coerce_version(spec) + + def _compare_less_than(self, prospective, spec): + # type: (LegacyVersion, str) -> bool + return prospective < self._coerce_version(spec) + + def _compare_greater_than(self, prospective, spec): + # type: (LegacyVersion, str) -> bool + return prospective > self._coerce_version(spec) + + +def _require_version_compare( + fn # type: (Callable[[Specifier, ParsedVersion, str], bool]) +): + # type: (...) -> Callable[[Specifier, ParsedVersion, str], bool] + @functools.wraps(fn) + def wrapped(self, prospective, spec): + # type: (Specifier, ParsedVersion, str) -> bool + if not isinstance(prospective, Version): + return False + return fn(self, prospective, spec) + + return wrapped + + +class Specifier(_IndividualSpecifier): + + _regex_str = r""" + (?P(~=|==|!=|<=|>=|<|>|===)) + (?P + (?: + # The identity operators allow for an escape hatch that will + # do an exact string match of the version you wish to install. + # This will not be parsed by PEP 440 and we cannot determine + # any semantic meaning from it. This operator is discouraged + # but included entirely as an escape hatch. + (?<====) # Only match for the identity operator + \s* + [^\s]* # We just match everything, except for whitespace + # since we are only testing for strict identity. + ) + | + (?: + # The (non)equality operators allow for wild card and local + # versions to be specified so we have to define these two + # operators separately to enable that. + (?<===|!=) # Only match for equals and not equals + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + + # You cannot use a wild card and a dev or local version + # together so group them with a | and make them optional. + (?: + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + | + \.\* # Wild card syntax of .* + )? + ) + | + (?: + # The compatible operator requires at least two digits in the + # release segment. + (?<=~=) # Only match for the compatible operator + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + | + (?: + # All other operators only allow a sub set of what the + # (non)equality operators do. Specifically they do not allow + # local versions to be specified nor do they allow the prefix + # matching wild cards. + (?=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + "===": "arbitrary", + } + + @_require_version_compare + def _compare_compatible(self, prospective, spec): + # type: (ParsedVersion, str) -> bool + + # Compatible releases have an equivalent combination of >= and ==. That + # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to + # implement this in terms of the other specifiers instead of + # implementing it ourselves. The only thing we need to do is construct + # the other specifiers. + + # We want everything but the last item in the version, but we want to + # ignore post and dev releases and we want to treat the pre-release as + # it's own separate segment. + prefix = ".".join( + list( + itertools.takewhile( + lambda x: (not x.startswith("post") and not x.startswith("dev")), + _version_split(spec), + ) + )[:-1] + ) + + # Add the prefix notation to the end of our string + prefix += ".*" + + return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( + prospective, prefix + ) + + @_require_version_compare + def _compare_equal(self, prospective, spec): + # type: (ParsedVersion, str) -> bool + + # We need special logic to handle prefix matching + if spec.endswith(".*"): + # In the case of prefix matching we want to ignore local segment. + prospective = Version(prospective.public) + # Split the spec out by dots, and pretend that there is an implicit + # dot in between a release segment and a pre-release segment. + split_spec = _version_split(spec[:-2]) # Remove the trailing .* + + # Split the prospective version out by dots, and pretend that there + # is an implicit dot in between a release segment and a pre-release + # segment. + split_prospective = _version_split(str(prospective)) + + # Shorten the prospective version to be the same length as the spec + # so that we can determine if the specifier is a prefix of the + # prospective version or not. + shortened_prospective = split_prospective[: len(split_spec)] + + # Pad out our two sides with zeros so that they both equal the same + # length. + padded_spec, padded_prospective = _pad_version( + split_spec, shortened_prospective + ) + + return padded_prospective == padded_spec + else: + # Convert our spec string into a Version + spec_version = Version(spec) + + # If the specifier does not have a local segment, then we want to + # act as if the prospective version also does not have a local + # segment. + if not spec_version.local: + prospective = Version(prospective.public) + + return prospective == spec_version + + @_require_version_compare + def _compare_not_equal(self, prospective, spec): + # type: (ParsedVersion, str) -> bool + return not self._compare_equal(prospective, spec) + + @_require_version_compare + def _compare_less_than_equal(self, prospective, spec): + # type: (ParsedVersion, str) -> bool + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) <= Version(spec) + + @_require_version_compare + def _compare_greater_than_equal(self, prospective, spec): + # type: (ParsedVersion, str) -> bool + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) >= Version(spec) + + @_require_version_compare + def _compare_less_than(self, prospective, spec_str): + # type: (ParsedVersion, str) -> bool + + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec_str) + + # Check to see if the prospective version is less than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective < spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a pre-release version, that we do not accept pre-release + # versions for the version mentioned in the specifier (e.g. <3.1 should + # not match 3.1.dev0, but should match 3.0.dev0). + if not spec.is_prerelease and prospective.is_prerelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # less than the spec version *and* it's not a pre-release of the same + # version in the spec. + return True + + @_require_version_compare + def _compare_greater_than(self, prospective, spec_str): + # type: (ParsedVersion, str) -> bool + + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec_str) + + # Check to see if the prospective version is greater than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective > spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a post-release version, that we do not accept + # post-release versions for the version mentioned in the specifier + # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). + if not spec.is_postrelease and prospective.is_postrelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # Ensure that we do not allow a local version of the version mentioned + # in the specifier, which is technically greater than, to match. + if prospective.local is not None: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # greater than the spec version *and* it's not a pre-release of the + # same version in the spec. + return True + + def _compare_arbitrary(self, prospective, spec): + # type: (Version, str) -> bool + return str(prospective).lower() == str(spec).lower() + + @property + def prereleases(self): + # type: () -> bool + + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Look at all of our specifiers and determine if they are inclusive + # operators, and if they are if they are including an explicit + # prerelease. + operator, version = self._spec + if operator in ["==", ">=", "<=", "~=", "==="]: + # The == specifier can include a trailing .*, if it does we + # want to remove before parsing. + if operator == "==" and version.endswith(".*"): + version = version[:-2] + + # Parse the version, and if it is a pre-release than this + # specifier allows pre-releases. + if parse(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value): + # type: (bool) -> None + self._prereleases = value + + +_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") + + +def _version_split(version): + # type: (str) -> List[str] + result = [] # type: List[str] + for item in version.split("."): + match = _prefix_regex.search(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _pad_version(left, right): + # type: (List[str], List[str]) -> Tuple[List[str], List[str]] + left_split, right_split = [], [] + + # Get the release segment of our versions + left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) + right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) + + # Get the rest of our versions + left_split.append(left[len(left_split[0]) :]) + right_split.append(right[len(right_split[0]) :]) + + # Insert our padding + left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) + right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) + + return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) + + +class SpecifierSet(BaseSpecifier): + def __init__(self, specifiers="", prereleases=None): + # type: (str, Optional[bool]) -> None + + # Split on , to break each individual specifier into it's own item, and + # strip each item to remove leading/trailing whitespace. + split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + + # Parsed each individual specifier, attempting first to make it a + # Specifier and falling back to a LegacySpecifier. + parsed = set() + for specifier in split_specifiers: + try: + parsed.add(Specifier(specifier)) + except InvalidSpecifier: + parsed.add(LegacySpecifier(specifier)) + + # Turn our parsed specifiers into a frozen set and save them for later. + self._specs = frozenset(parsed) + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + def __repr__(self): + # type: () -> str + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "".format(str(self), pre) + + def __str__(self): + # type: () -> str + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self): + # type: () -> int + return hash(self._specs) + + def __and__(self, other): + # type: (Union[SpecifierSet, str]) -> SpecifierSet + if isinstance(other, string_types): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = frozenset(self._specs | other._specs) + + if self._prereleases is None and other._prereleases is not None: + specifier._prereleases = other._prereleases + elif self._prereleases is not None and other._prereleases is None: + specifier._prereleases = self._prereleases + elif self._prereleases == other._prereleases: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease " + "overrides." + ) + + return specifier + + def __eq__(self, other): + # type: (object) -> bool + if isinstance(other, (string_types, _IndividualSpecifier)): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __ne__(self, other): + # type: (object) -> bool + if isinstance(other, (string_types, _IndividualSpecifier)): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs != other._specs + + def __len__(self): + # type: () -> int + return len(self._specs) + + def __iter__(self): + # type: () -> Iterator[FrozenSet[_IndividualSpecifier]] + return iter(self._specs) + + @property + def prereleases(self): + # type: () -> Optional[bool] + + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value): + # type: (bool) -> None + self._prereleases = value + + def __contains__(self, item): + # type: (Union[ParsedVersion, str]) -> bool + return self.contains(item) + + def contains(self, item, prereleases=None): + # type: (Union[ParsedVersion, str], Optional[bool]) -> bool + + # Ensure that our item is a Version or LegacyVersion instance. + if not isinstance(item, (LegacyVersion, Version)): + item = parse(item) + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # We can determine if we're going to allow pre-releases by looking to + # see if any of the underlying items supports them. If none of them do + # and this item is a pre-release then we do not allow it and we can + # short circuit that here. + # Note: This means that 1.0.dev1 would not be contained in something + # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 + if not prereleases and item.is_prerelease: + return False + + # We simply dispatch to the underlying specs here to make sure that the + # given version is contained within all of them. + # Note: This use of all() here means that an empty set of specifiers + # will always return True, this is an explicit design decision. + return all(s.contains(item, prereleases=prereleases) for s in self._specs) + + def filter( + self, + iterable, # type: Iterable[Union[ParsedVersion, str]] + prereleases=None, # type: Optional[bool] + ): + # type: (...) -> Iterable[Union[ParsedVersion, str]] + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # If we have any specifiers, then we want to wrap our iterable in the + # filter method for each one, this will act as a logical AND amongst + # each specifier. + if self._specs: + for spec in self._specs: + iterable = spec.filter(iterable, prereleases=bool(prereleases)) + return iterable + # If we do not have any specifiers, then we need to have a rough filter + # which will filter out any pre-releases, unless there are no final + # releases, and which will filter out LegacyVersion in general. + else: + filtered = [] # type: List[Union[ParsedVersion, str]] + found_prereleases = [] # type: List[Union[ParsedVersion, str]] + + for item in iterable: + # Ensure that we some kind of Version class for this item. + if not isinstance(item, (LegacyVersion, Version)): + parsed_version = parse(item) + else: + parsed_version = item + + # Filter out any item which is parsed as a LegacyVersion + if isinstance(parsed_version, LegacyVersion): + continue + + # Store any item which is a pre-release for later unless we've + # already found a final version or we are accepting prereleases + if parsed_version.is_prerelease and not prereleases: + if not filtered: + found_prereleases.append(item) + else: + filtered.append(item) + + # If we've found no items except for pre-releases, then we'll go + # ahead and use the pre-releases + if not filtered and found_prereleases and prereleases is None: + return found_prereleases + + return filtered diff --git a/_setuptools_vendored/packaging/tags.py b/_setuptools_vendored/packaging/tags.py new file mode 100644 index 0000000000..9064910b8b --- /dev/null +++ b/_setuptools_vendored/packaging/tags.py @@ -0,0 +1,751 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import + +import distutils.util + +try: + from importlib.machinery import EXTENSION_SUFFIXES +except ImportError: # pragma: no cover + import imp + + EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] + del imp +import logging +import os +import platform +import re +import struct +import sys +import sysconfig +import warnings + +from ._typing import TYPE_CHECKING, cast + +if TYPE_CHECKING: # pragma: no cover + from typing import ( + Dict, + FrozenSet, + IO, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, + ) + + PythonVersion = Sequence[int] + MacVersion = Tuple[int, int] + GlibcVersion = Tuple[int, int] + + +logger = logging.getLogger(__name__) + +INTERPRETER_SHORT_NAMES = { + "python": "py", # Generic. + "cpython": "cp", + "pypy": "pp", + "ironpython": "ip", + "jython": "jy", +} # type: Dict[str, str] + + +_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 + + +class Tag(object): + """ + A representation of the tag triple for a wheel. + + Instances are considered immutable and thus are hashable. Equality checking + is also supported. + """ + + __slots__ = ["_interpreter", "_abi", "_platform"] + + def __init__(self, interpreter, abi, platform): + # type: (str, str, str) -> None + self._interpreter = interpreter.lower() + self._abi = abi.lower() + self._platform = platform.lower() + + @property + def interpreter(self): + # type: () -> str + return self._interpreter + + @property + def abi(self): + # type: () -> str + return self._abi + + @property + def platform(self): + # type: () -> str + return self._platform + + def __eq__(self, other): + # type: (object) -> bool + if not isinstance(other, Tag): + return NotImplemented + + return ( + (self.platform == other.platform) + and (self.abi == other.abi) + and (self.interpreter == other.interpreter) + ) + + def __hash__(self): + # type: () -> int + return hash((self._interpreter, self._abi, self._platform)) + + def __str__(self): + # type: () -> str + return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) + + def __repr__(self): + # type: () -> str + return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) + + +def parse_tag(tag): + # type: (str) -> FrozenSet[Tag] + """ + Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. + + Returning a set is required due to the possibility that the tag is a + compressed tag set. + """ + tags = set() + interpreters, abis, platforms = tag.split("-") + for interpreter in interpreters.split("."): + for abi in abis.split("."): + for platform_ in platforms.split("."): + tags.add(Tag(interpreter, abi, platform_)) + return frozenset(tags) + + +def _warn_keyword_parameter(func_name, kwargs): + # type: (str, Dict[str, bool]) -> bool + """ + Backwards-compatibility with Python 2.7 to allow treating 'warn' as keyword-only. + """ + if not kwargs: + return False + elif len(kwargs) > 1 or "warn" not in kwargs: + kwargs.pop("warn", None) + arg = next(iter(kwargs.keys())) + raise TypeError( + "{}() got an unexpected keyword argument {!r}".format(func_name, arg) + ) + return kwargs["warn"] + + +def _get_config_var(name, warn=False): + # type: (str, bool) -> Union[int, str, None] + value = sysconfig.get_config_var(name) + if value is None and warn: + logger.debug( + "Config variable '%s' is unset, Python ABI tag may be incorrect", name + ) + return value + + +def _normalize_string(string): + # type: (str) -> str + return string.replace(".", "_").replace("-", "_") + + +def _abi3_applies(python_version): + # type: (PythonVersion) -> bool + """ + Determine if the Python version supports abi3. + + PEP 384 was first implemented in Python 3.2. + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) + + +def _cpython_abis(py_version, warn=False): + # type: (PythonVersion, bool) -> List[str] + py_version = tuple(py_version) # To allow for version comparison. + abis = [] + version = _version_nodot(py_version[:2]) + debug = pymalloc = ucs4 = "" + with_debug = _get_config_var("Py_DEBUG", warn) + has_refcount = hasattr(sys, "gettotalrefcount") + # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled + # extension modules is the best option. + # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 + has_ext = "_d.pyd" in EXTENSION_SUFFIXES + if with_debug or (with_debug is None and (has_refcount or has_ext)): + debug = "d" + if py_version < (3, 8): + with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) + if with_pymalloc or with_pymalloc is None: + pymalloc = "m" + if py_version < (3, 3): + unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) + if unicode_size == 4 or ( + unicode_size is None and sys.maxunicode == 0x10FFFF + ): + ucs4 = "u" + elif debug: + # Debug builds can also load "normal" extension modules. + # We can also assume no UCS-4 or pymalloc requirement. + abis.append("cp{version}".format(version=version)) + abis.insert( + 0, + "cp{version}{debug}{pymalloc}{ucs4}".format( + version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4 + ), + ) + return abis + + +def cpython_tags( + python_version=None, # type: Optional[PythonVersion] + abis=None, # type: Optional[Iterable[str]] + platforms=None, # type: Optional[Iterable[str]] + **kwargs # type: bool +): + # type: (...) -> Iterator[Tag] + """ + Yields the tags for a CPython interpreter. + + The tags consist of: + - cp-- + - cp-abi3- + - cp-none- + - cp-abi3- # Older Python versions down to 3.2. + + If python_version only specifies a major version then user-provided ABIs and + the 'none' ABItag will be used. + + If 'abi3' or 'none' are specified in 'abis' then they will be yielded at + their normal position and not at the beginning. + """ + warn = _warn_keyword_parameter("cpython_tags", kwargs) + if not python_version: + python_version = sys.version_info[:2] + + interpreter = "cp{}".format(_version_nodot(python_version[:2])) + + if abis is None: + if len(python_version) > 1: + abis = _cpython_abis(python_version, warn) + else: + abis = [] + abis = list(abis) + # 'abi3' and 'none' are explicitly handled later. + for explicit_abi in ("abi3", "none"): + try: + abis.remove(explicit_abi) + except ValueError: + pass + + platforms = list(platforms or _platform_tags()) + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + if _abi3_applies(python_version): + for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): + yield tag + for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): + yield tag + + if _abi3_applies(python_version): + for minor_version in range(python_version[1] - 1, 1, -1): + for platform_ in platforms: + interpreter = "cp{version}".format( + version=_version_nodot((python_version[0], minor_version)) + ) + yield Tag(interpreter, "abi3", platform_) + + +def _generic_abi(): + # type: () -> Iterator[str] + abi = sysconfig.get_config_var("SOABI") + if abi: + yield _normalize_string(abi) + + +def generic_tags( + interpreter=None, # type: Optional[str] + abis=None, # type: Optional[Iterable[str]] + platforms=None, # type: Optional[Iterable[str]] + **kwargs # type: bool +): + # type: (...) -> Iterator[Tag] + """ + Yields the tags for a generic interpreter. + + The tags consist of: + - -- + + The "none" ABI will be added if it was not explicitly provided. + """ + warn = _warn_keyword_parameter("generic_tags", kwargs) + if not interpreter: + interp_name = interpreter_name() + interp_version = interpreter_version(warn=warn) + interpreter = "".join([interp_name, interp_version]) + if abis is None: + abis = _generic_abi() + platforms = list(platforms or _platform_tags()) + abis = list(abis) + if "none" not in abis: + abis.append("none") + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + + +def _py_interpreter_range(py_version): + # type: (PythonVersion) -> Iterator[str] + """ + Yields Python versions in descending order. + + After the latest version, the major-only version will be yielded, and then + all previous versions of that major version. + """ + if len(py_version) > 1: + yield "py{version}".format(version=_version_nodot(py_version[:2])) + yield "py{major}".format(major=py_version[0]) + if len(py_version) > 1: + for minor in range(py_version[1] - 1, -1, -1): + yield "py{version}".format(version=_version_nodot((py_version[0], minor))) + + +def compatible_tags( + python_version=None, # type: Optional[PythonVersion] + interpreter=None, # type: Optional[str] + platforms=None, # type: Optional[Iterable[str]] +): + # type: (...) -> Iterator[Tag] + """ + Yields the sequence of tags that are compatible with a specific version of Python. + + The tags consist of: + - py*-none- + - -none-any # ... if `interpreter` is provided. + - py*-none-any + """ + if not python_version: + python_version = sys.version_info[:2] + platforms = list(platforms or _platform_tags()) + for version in _py_interpreter_range(python_version): + for platform_ in platforms: + yield Tag(version, "none", platform_) + if interpreter: + yield Tag(interpreter, "none", "any") + for version in _py_interpreter_range(python_version): + yield Tag(version, "none", "any") + + +def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): + # type: (str, bool) -> str + if not is_32bit: + return arch + + if arch.startswith("ppc"): + return "ppc" + + return "i386" + + +def _mac_binary_formats(version, cpu_arch): + # type: (MacVersion, str) -> List[str] + formats = [cpu_arch] + if cpu_arch == "x86_64": + if version < (10, 4): + return [] + formats.extend(["intel", "fat64", "fat32"]) + + elif cpu_arch == "i386": + if version < (10, 4): + return [] + formats.extend(["intel", "fat32", "fat"]) + + elif cpu_arch == "ppc64": + # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? + if version > (10, 5) or version < (10, 4): + return [] + formats.append("fat64") + + elif cpu_arch == "ppc": + if version > (10, 6): + return [] + formats.extend(["fat32", "fat"]) + + formats.append("universal") + return formats + + +def mac_platforms(version=None, arch=None): + # type: (Optional[MacVersion], Optional[str]) -> Iterator[str] + """ + Yields the platform tags for a macOS system. + + The `version` parameter is a two-item tuple specifying the macOS version to + generate platform tags for. The `arch` parameter is the CPU architecture to + generate platform tags for. Both parameters default to the appropriate value + for the current system. + """ + version_str, _, cpu_arch = platform.mac_ver() # type: ignore + if version is None: + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + else: + version = version + if arch is None: + arch = _mac_arch(cpu_arch) + else: + arch = arch + for minor_version in range(version[1], -1, -1): + compat_version = version[0], minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + + +# From PEP 513. +def _is_manylinux_compatible(name, glibc_version): + # type: (str, GlibcVersion) -> bool + # Check for presence of _manylinux module. + try: + import _manylinux # noqa + + return bool(getattr(_manylinux, name + "_compatible")) + except (ImportError, AttributeError): + # Fall through to heuristic check below. + pass + + return _have_compatible_glibc(*glibc_version) + + +def _glibc_version_string(): + # type: () -> Optional[str] + # Returns glibc version string, or None if not using glibc. + return _glibc_version_string_confstr() or _glibc_version_string_ctypes() + + +def _glibc_version_string_confstr(): + # type: () -> Optional[str] + """ + Primary implementation of glibc_version_string using os.confstr. + """ + # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely + # to be broken or missing. This strategy is used in the standard library + # platform module. + # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 + try: + # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". + version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821 + "CS_GNU_LIBC_VERSION" + ) + assert version_string is not None + _, version = version_string.split() # type: Tuple[str, str] + except (AssertionError, AttributeError, OSError, ValueError): + # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... + return None + return version + + +def _glibc_version_string_ctypes(): + # type: () -> Optional[str] + """ + Fallback implementation of glibc_version_string using ctypes. + """ + try: + import ctypes + except ImportError: + return None + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + # + # Note: typeshed is wrong here so we are ignoring this line. + process_namespace = ctypes.CDLL(None) # type: ignore + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5" + gnu_get_libc_version.restype = ctypes.c_char_p + version_str = gnu_get_libc_version() # type: str + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +# Separated out from have_compatible_glibc for easier unit testing. +def _check_glibc_version(version_str, required_major, minimum_minor): + # type: (str, int, int) -> bool + # Parse string and check against requested version. + # + # We use a regexp instead of str.split because we want to discard any + # random junk that might come after the minor version -- this might happen + # in patched/forked versions of glibc (e.g. Linaro's version of glibc + # uses version strings like "2.20-2014.11"). See gh-3588. + m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) + if not m: + warnings.warn( + "Expected glibc version with 2 components major.minor," + " got: %s" % version_str, + RuntimeWarning, + ) + return False + return ( + int(m.group("major")) == required_major + and int(m.group("minor")) >= minimum_minor + ) + + +def _have_compatible_glibc(required_major, minimum_minor): + # type: (int, int) -> bool + version_str = _glibc_version_string() + if version_str is None: + return False + return _check_glibc_version(version_str, required_major, minimum_minor) + + +# Python does not provide platform information at sufficient granularity to +# identify the architecture of the running executable in some cases, so we +# determine it dynamically by reading the information from the running +# process. This only applies on Linux, which uses the ELF format. +class _ELFFileHeader(object): + # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header + class _InvalidELFFileHeader(ValueError): + """ + An invalid ELF file header was found. + """ + + ELF_MAGIC_NUMBER = 0x7F454C46 + ELFCLASS32 = 1 + ELFCLASS64 = 2 + ELFDATA2LSB = 1 + ELFDATA2MSB = 2 + EM_386 = 3 + EM_S390 = 22 + EM_ARM = 40 + EM_X86_64 = 62 + EF_ARM_ABIMASK = 0xFF000000 + EF_ARM_ABI_VER5 = 0x05000000 + EF_ARM_ABI_FLOAT_HARD = 0x00000400 + + def __init__(self, file): + # type: (IO[bytes]) -> None + def unpack(fmt): + # type: (str) -> int + try: + (result,) = struct.unpack( + fmt, file.read(struct.calcsize(fmt)) + ) # type: (int, ) + except struct.error: + raise _ELFFileHeader._InvalidELFFileHeader() + return result + + self.e_ident_magic = unpack(">I") + if self.e_ident_magic != self.ELF_MAGIC_NUMBER: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_class = unpack("B") + if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_data = unpack("B") + if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_version = unpack("B") + self.e_ident_osabi = unpack("B") + self.e_ident_abiversion = unpack("B") + self.e_ident_pad = file.read(7) + format_h = "H" + format_i = "I" + format_q = "Q" + format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q + self.e_type = unpack(format_h) + self.e_machine = unpack(format_h) + self.e_version = unpack(format_i) + self.e_entry = unpack(format_p) + self.e_phoff = unpack(format_p) + self.e_shoff = unpack(format_p) + self.e_flags = unpack(format_i) + self.e_ehsize = unpack(format_h) + self.e_phentsize = unpack(format_h) + self.e_phnum = unpack(format_h) + self.e_shentsize = unpack(format_h) + self.e_shnum = unpack(format_h) + self.e_shstrndx = unpack(format_h) + + +def _get_elf_header(): + # type: () -> Optional[_ELFFileHeader] + try: + with open(sys.executable, "rb") as f: + elf_header = _ELFFileHeader(f) + except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): + return None + return elf_header + + +def _is_linux_armhf(): + # type: () -> bool + # hard-float ABI can be detected from the ELF header of the running + # process + # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf + elf_header = _get_elf_header() + if elf_header is None: + return False + result = elf_header.e_ident_class == elf_header.ELFCLASS32 + result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB + result &= elf_header.e_machine == elf_header.EM_ARM + result &= ( + elf_header.e_flags & elf_header.EF_ARM_ABIMASK + ) == elf_header.EF_ARM_ABI_VER5 + result &= ( + elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD + ) == elf_header.EF_ARM_ABI_FLOAT_HARD + return result + + +def _is_linux_i686(): + # type: () -> bool + elf_header = _get_elf_header() + if elf_header is None: + return False + result = elf_header.e_ident_class == elf_header.ELFCLASS32 + result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB + result &= elf_header.e_machine == elf_header.EM_386 + return result + + +def _have_compatible_manylinux_abi(arch): + # type: (str) -> bool + if arch == "armv7l": + return _is_linux_armhf() + if arch == "i686": + return _is_linux_i686() + return True + + +def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): + # type: (bool) -> Iterator[str] + linux = _normalize_string(distutils.util.get_platform()) + if is_32bit: + if linux == "linux_x86_64": + linux = "linux_i686" + elif linux == "linux_aarch64": + linux = "linux_armv7l" + manylinux_support = [] + _, arch = linux.split("_", 1) + if _have_compatible_manylinux_abi(arch): + if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}: + manylinux_support.append( + ("manylinux2014", (2, 17)) + ) # CentOS 7 w/ glibc 2.17 (PEP 599) + if arch in {"x86_64", "i686"}: + manylinux_support.append( + ("manylinux2010", (2, 12)) + ) # CentOS 6 w/ glibc 2.12 (PEP 571) + manylinux_support.append( + ("manylinux1", (2, 5)) + ) # CentOS 5 w/ glibc 2.5 (PEP 513) + manylinux_support_iter = iter(manylinux_support) + for name, glibc_version in manylinux_support_iter: + if _is_manylinux_compatible(name, glibc_version): + yield linux.replace("linux", name) + break + # Support for a later manylinux implies support for an earlier version. + for name, _ in manylinux_support_iter: + yield linux.replace("linux", name) + yield linux + + +def _generic_platforms(): + # type: () -> Iterator[str] + yield _normalize_string(distutils.util.get_platform()) + + +def _platform_tags(): + # type: () -> Iterator[str] + """ + Provides the platform tags for this installation. + """ + if platform.system() == "Darwin": + return mac_platforms() + elif platform.system() == "Linux": + return _linux_platforms() + else: + return _generic_platforms() + + +def interpreter_name(): + # type: () -> str + """ + Returns the name of the running interpreter. + """ + try: + name = sys.implementation.name # type: ignore + except AttributeError: # pragma: no cover + # Python 2.7 compatibility. + name = platform.python_implementation().lower() + return INTERPRETER_SHORT_NAMES.get(name) or name + + +def interpreter_version(**kwargs): + # type: (bool) -> str + """ + Returns the version of the running interpreter. + """ + warn = _warn_keyword_parameter("interpreter_version", kwargs) + version = _get_config_var("py_version_nodot", warn=warn) + if version: + version = str(version) + else: + version = _version_nodot(sys.version_info[:2]) + return version + + +def _version_nodot(version): + # type: (PythonVersion) -> str + if any(v >= 10 for v in version): + sep = "_" + else: + sep = "" + return sep.join(map(str, version)) + + +def sys_tags(**kwargs): + # type: (bool) -> Iterator[Tag] + """ + Returns the sequence of tag triples for the running interpreter. + + The order of the sequence corresponds to priority order for the + interpreter, from most to least important. + """ + warn = _warn_keyword_parameter("sys_tags", kwargs) + + interp_name = interpreter_name() + if interp_name == "cp": + for tag in cpython_tags(warn=warn): + yield tag + else: + for tag in generic_tags(): + yield tag + + for tag in compatible_tags(): + yield tag diff --git a/_setuptools_vendored/packaging/utils.py b/_setuptools_vendored/packaging/utils.py new file mode 100644 index 0000000000..19579c1a0f --- /dev/null +++ b/_setuptools_vendored/packaging/utils.py @@ -0,0 +1,65 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import re + +from ._typing import TYPE_CHECKING, cast +from .version import InvalidVersion, Version + +if TYPE_CHECKING: # pragma: no cover + from typing import NewType, Union + + NormalizedName = NewType("NormalizedName", str) + +_canonicalize_regex = re.compile(r"[-_.]+") + + +def canonicalize_name(name): + # type: (str) -> NormalizedName + # This is taken from PEP 503. + value = _canonicalize_regex.sub("-", name).lower() + return cast("NormalizedName", value) + + +def canonicalize_version(_version): + # type: (str) -> Union[Version, str] + """ + This is very similar to Version.__str__, but has one subtle difference + with the way it handles the release segment. + """ + + try: + version = Version(_version) + except InvalidVersion: + # Legacy versions cannot be normalized + return _version + + parts = [] + + # Epoch + if version.epoch != 0: + parts.append("{0}!".format(version.epoch)) + + # Release segment + # NB: This strips trailing '.0's to normalize + parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release))) + + # Pre-release + if version.pre is not None: + parts.append("".join(str(x) for x in version.pre)) + + # Post-release + if version.post is not None: + parts.append(".post{0}".format(version.post)) + + # Development release + if version.dev is not None: + parts.append(".dev{0}".format(version.dev)) + + # Local version segment + if version.local is not None: + parts.append("+{0}".format(version.local)) + + return "".join(parts) diff --git a/_setuptools_vendored/packaging/version.py b/_setuptools_vendored/packaging/version.py new file mode 100644 index 0000000000..00371e86a8 --- /dev/null +++ b/_setuptools_vendored/packaging/version.py @@ -0,0 +1,535 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import collections +import itertools +import re + +from ._structures import Infinity, NegativeInfinity +from ._typing import TYPE_CHECKING + +if TYPE_CHECKING: # pragma: no cover + from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union + + from ._structures import InfinityType, NegativeInfinityType + + InfiniteTypes = Union[InfinityType, NegativeInfinityType] + PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] + SubLocalType = Union[InfiniteTypes, int, str] + LocalType = Union[ + NegativeInfinityType, + Tuple[ + Union[ + SubLocalType, + Tuple[SubLocalType, str], + Tuple[NegativeInfinityType, SubLocalType], + ], + ..., + ], + ] + CmpKey = Tuple[ + int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType + ] + LegacyCmpKey = Tuple[int, Tuple[str, ...]] + VersionComparisonMethod = Callable[ + [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool + ] + +__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] + + +_Version = collections.namedtuple( + "_Version", ["epoch", "release", "dev", "pre", "post", "local"] +) + + +def parse(version): + # type: (str) -> Union[LegacyVersion, Version] + """ + Parse the given version string and return either a :class:`Version` object + or a :class:`LegacyVersion` object depending on if the given version is + a valid PEP 440 version or a legacy version. + """ + try: + return Version(version) + except InvalidVersion: + return LegacyVersion(version) + + +class InvalidVersion(ValueError): + """ + An invalid version was found, users should refer to PEP 440. + """ + + +class _BaseVersion(object): + _key = None # type: Union[CmpKey, LegacyCmpKey] + + def __hash__(self): + # type: () -> int + return hash(self._key) + + def __lt__(self, other): + # type: (_BaseVersion) -> bool + return self._compare(other, lambda s, o: s < o) + + def __le__(self, other): + # type: (_BaseVersion) -> bool + return self._compare(other, lambda s, o: s <= o) + + def __eq__(self, other): + # type: (object) -> bool + return self._compare(other, lambda s, o: s == o) + + def __ge__(self, other): + # type: (_BaseVersion) -> bool + return self._compare(other, lambda s, o: s >= o) + + def __gt__(self, other): + # type: (_BaseVersion) -> bool + return self._compare(other, lambda s, o: s > o) + + def __ne__(self, other): + # type: (object) -> bool + return self._compare(other, lambda s, o: s != o) + + def _compare(self, other, method): + # type: (object, VersionComparisonMethod) -> Union[bool, NotImplemented] + if not isinstance(other, _BaseVersion): + return NotImplemented + + return method(self._key, other._key) + + +class LegacyVersion(_BaseVersion): + def __init__(self, version): + # type: (str) -> None + self._version = str(version) + self._key = _legacy_cmpkey(self._version) + + def __str__(self): + # type: () -> str + return self._version + + def __repr__(self): + # type: () -> str + return "".format(repr(str(self))) + + @property + def public(self): + # type: () -> str + return self._version + + @property + def base_version(self): + # type: () -> str + return self._version + + @property + def epoch(self): + # type: () -> int + return -1 + + @property + def release(self): + # type: () -> None + return None + + @property + def pre(self): + # type: () -> None + return None + + @property + def post(self): + # type: () -> None + return None + + @property + def dev(self): + # type: () -> None + return None + + @property + def local(self): + # type: () -> None + return None + + @property + def is_prerelease(self): + # type: () -> bool + return False + + @property + def is_postrelease(self): + # type: () -> bool + return False + + @property + def is_devrelease(self): + # type: () -> bool + return False + + +_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) + +_legacy_version_replacement_map = { + "pre": "c", + "preview": "c", + "-": "final-", + "rc": "c", + "dev": "@", +} + + +def _parse_version_parts(s): + # type: (str) -> Iterator[str] + for part in _legacy_version_component_re.split(s): + part = _legacy_version_replacement_map.get(part, part) + + if not part or part == ".": + continue + + if part[:1] in "0123456789": + # pad for numeric comparison + yield part.zfill(8) + else: + yield "*" + part + + # ensure that alpha/beta/candidate are before final + yield "*final" + + +def _legacy_cmpkey(version): + # type: (str) -> LegacyCmpKey + + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch + # greater than or equal to 0. This will effectively put the LegacyVersion, + # which uses the defacto standard originally implemented by setuptools, + # as before all PEP 440 versions. + epoch = -1 + + # This scheme is taken from pkg_resources.parse_version setuptools prior to + # it's adoption of the packaging library. + parts = [] # type: List[str] + for part in _parse_version_parts(version.lower()): + if part.startswith("*"): + # remove "-" before a prerelease tag + if part < "*final": + while parts and parts[-1] == "*final-": + parts.pop() + + # remove trailing zeros from each series of numeric parts + while parts and parts[-1] == "00000000": + parts.pop() + + parts.append(part) + + return epoch, tuple(parts) + + +# Deliberately not anchored to the start and end of the string, to make it +# easier for 3rd party code to reuse +VERSION_PATTERN = r""" + v? + (?: + (?:(?P[0-9]+)!)? # epoch + (?P[0-9]+(?:\.[0-9]+)*) # release segment + (?P
                                          # pre-release
+            [-_\.]?
+            (?P(a|b|c|rc|alpha|beta|pre|preview))
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+        (?P                                         # post release
+            (?:-(?P[0-9]+))
+            |
+            (?:
+                [-_\.]?
+                (?Ppost|rev|r)
+                [-_\.]?
+                (?P[0-9]+)?
+            )
+        )?
+        (?P                                          # dev release
+            [-_\.]?
+            (?Pdev)
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+    )
+    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
+"""
+
+
+class Version(_BaseVersion):
+
+    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
+
+    def __init__(self, version):
+        # type: (str) -> None
+
+        # Validate the version and parse it into pieces
+        match = self._regex.search(version)
+        if not match:
+            raise InvalidVersion("Invalid version: '{0}'".format(version))
+
+        # Store the parsed out pieces of the version
+        self._version = _Version(
+            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
+            release=tuple(int(i) for i in match.group("release").split(".")),
+            pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
+            post=_parse_letter_version(
+                match.group("post_l"), match.group("post_n1") or match.group("post_n2")
+            ),
+            dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
+            local=_parse_local_version(match.group("local")),
+        )
+
+        # Generate a key which will be used for sorting
+        self._key = _cmpkey(
+            self._version.epoch,
+            self._version.release,
+            self._version.pre,
+            self._version.post,
+            self._version.dev,
+            self._version.local,
+        )
+
+    def __repr__(self):
+        # type: () -> str
+        return "".format(repr(str(self)))
+
+    def __str__(self):
+        # type: () -> str
+        parts = []
+
+        # Epoch
+        if self.epoch != 0:
+            parts.append("{0}!".format(self.epoch))
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self.release))
+
+        # Pre-release
+        if self.pre is not None:
+            parts.append("".join(str(x) for x in self.pre))
+
+        # Post-release
+        if self.post is not None:
+            parts.append(".post{0}".format(self.post))
+
+        # Development release
+        if self.dev is not None:
+            parts.append(".dev{0}".format(self.dev))
+
+        # Local version segment
+        if self.local is not None:
+            parts.append("+{0}".format(self.local))
+
+        return "".join(parts)
+
+    @property
+    def epoch(self):
+        # type: () -> int
+        _epoch = self._version.epoch  # type: int
+        return _epoch
+
+    @property
+    def release(self):
+        # type: () -> Tuple[int, ...]
+        _release = self._version.release  # type: Tuple[int, ...]
+        return _release
+
+    @property
+    def pre(self):
+        # type: () -> Optional[Tuple[str, int]]
+        _pre = self._version.pre  # type: Optional[Tuple[str, int]]
+        return _pre
+
+    @property
+    def post(self):
+        # type: () -> Optional[Tuple[str, int]]
+        return self._version.post[1] if self._version.post else None
+
+    @property
+    def dev(self):
+        # type: () -> Optional[Tuple[str, int]]
+        return self._version.dev[1] if self._version.dev else None
+
+    @property
+    def local(self):
+        # type: () -> Optional[str]
+        if self._version.local:
+            return ".".join(str(x) for x in self._version.local)
+        else:
+            return None
+
+    @property
+    def public(self):
+        # type: () -> str
+        return str(self).split("+", 1)[0]
+
+    @property
+    def base_version(self):
+        # type: () -> str
+        parts = []
+
+        # Epoch
+        if self.epoch != 0:
+            parts.append("{0}!".format(self.epoch))
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self.release))
+
+        return "".join(parts)
+
+    @property
+    def is_prerelease(self):
+        # type: () -> bool
+        return self.dev is not None or self.pre is not None
+
+    @property
+    def is_postrelease(self):
+        # type: () -> bool
+        return self.post is not None
+
+    @property
+    def is_devrelease(self):
+        # type: () -> bool
+        return self.dev is not None
+
+    @property
+    def major(self):
+        # type: () -> int
+        return self.release[0] if len(self.release) >= 1 else 0
+
+    @property
+    def minor(self):
+        # type: () -> int
+        return self.release[1] if len(self.release) >= 2 else 0
+
+    @property
+    def micro(self):
+        # type: () -> int
+        return self.release[2] if len(self.release) >= 3 else 0
+
+
+def _parse_letter_version(
+    letter,  # type: str
+    number,  # type: Union[str, bytes, SupportsInt]
+):
+    # type: (...) -> Optional[Tuple[str, int]]
+
+    if letter:
+        # We consider there to be an implicit 0 in a pre-release if there is
+        # not a numeral associated with it.
+        if number is None:
+            number = 0
+
+        # We normalize any letters to their lower case form
+        letter = letter.lower()
+
+        # We consider some words to be alternate spellings of other words and
+        # in those cases we want to normalize the spellings to our preferred
+        # spelling.
+        if letter == "alpha":
+            letter = "a"
+        elif letter == "beta":
+            letter = "b"
+        elif letter in ["c", "pre", "preview"]:
+            letter = "rc"
+        elif letter in ["rev", "r"]:
+            letter = "post"
+
+        return letter, int(number)
+    if not letter and number:
+        # We assume if we are given a number, but we are not given a letter
+        # then this is using the implicit post release syntax (e.g. 1.0-1)
+        letter = "post"
+
+        return letter, int(number)
+
+    return None
+
+
+_local_version_separators = re.compile(r"[\._-]")
+
+
+def _parse_local_version(local):
+    # type: (str) -> Optional[LocalType]
+    """
+    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
+    """
+    if local is not None:
+        return tuple(
+            part.lower() if not part.isdigit() else int(part)
+            for part in _local_version_separators.split(local)
+        )
+    return None
+
+
+def _cmpkey(
+    epoch,  # type: int
+    release,  # type: Tuple[int, ...]
+    pre,  # type: Optional[Tuple[str, int]]
+    post,  # type: Optional[Tuple[str, int]]
+    dev,  # type: Optional[Tuple[str, int]]
+    local,  # type: Optional[Tuple[SubLocalType]]
+):
+    # type: (...) -> CmpKey
+
+    # When we compare a release version, we want to compare it with all of the
+    # trailing zeros removed. So we'll use a reverse the list, drop all the now
+    # leading zeros until we come to something non zero, then take the rest
+    # re-reverse it back into the correct order and make it a tuple and use
+    # that for our sorting key.
+    _release = tuple(
+        reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
+    )
+
+    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
+    # We'll do this by abusing the pre segment, but we _only_ want to do this
+    # if there is not a pre or a post segment. If we have one of those then
+    # the normal sorting rules will handle this case correctly.
+    if pre is None and post is None and dev is not None:
+        _pre = NegativeInfinity  # type: PrePostDevType
+    # Versions without a pre-release (except as noted above) should sort after
+    # those with one.
+    elif pre is None:
+        _pre = Infinity
+    else:
+        _pre = pre
+
+    # Versions without a post segment should sort before those with one.
+    if post is None:
+        _post = NegativeInfinity  # type: PrePostDevType
+
+    else:
+        _post = post
+
+    # Versions without a development segment should sort after those with one.
+    if dev is None:
+        _dev = Infinity  # type: PrePostDevType
+
+    else:
+        _dev = dev
+
+    if local is None:
+        # Versions without a local segment should sort before those with one.
+        _local = NegativeInfinity  # type: LocalType
+    else:
+        # Versions with a local segment need that segment parsed to implement
+        # the sorting rules in PEP440.
+        # - Alpha numeric segments sort before numeric segments
+        # - Alpha numeric segments sort lexicographically
+        # - Numeric segments sort numerically
+        # - Shorter versions sort before longer versions when the prefixes
+        #   match exactly
+        _local = tuple(
+            (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local
+        )
+
+    return epoch, _release, _pre, _post, _dev, _local
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/DESCRIPTION.rst b/_setuptools_vendored/pyparsing-2.2.1.dist-info/DESCRIPTION.rst
new file mode 100644
index 0000000000..e1187231a3
--- /dev/null
+++ b/_setuptools_vendored/pyparsing-2.2.1.dist-info/DESCRIPTION.rst
@@ -0,0 +1,3 @@
+UNKNOWN
+
+
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/LICENSE.txt b/_setuptools_vendored/pyparsing-2.2.1.dist-info/LICENSE.txt
new file mode 100644
index 0000000000..bbc959e0d6
--- /dev/null
+++ b/_setuptools_vendored/pyparsing-2.2.1.dist-info/LICENSE.txt
@@ -0,0 +1,18 @@
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/METADATA b/_setuptools_vendored/pyparsing-2.2.1.dist-info/METADATA
new file mode 100644
index 0000000000..a15c350e36
--- /dev/null
+++ b/_setuptools_vendored/pyparsing-2.2.1.dist-info/METADATA
@@ -0,0 +1,30 @@
+Metadata-Version: 2.0
+Name: pyparsing
+Version: 2.2.1
+Summary: Python parsing module
+Home-page: https://github.com/pyparsing/pyparsing/
+Author: Paul McGuire
+Author-email: ptmcg@users.sourceforge.net
+License: MIT License
+Download-URL: https://pypi.org/project/pyparsing/
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Information Technology
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*
+
+UNKNOWN
+
+
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/RECORD b/_setuptools_vendored/pyparsing-2.2.1.dist-info/RECORD
new file mode 100644
index 0000000000..74cd1d53f6
--- /dev/null
+++ b/_setuptools_vendored/pyparsing-2.2.1.dist-info/RECORD
@@ -0,0 +1,8 @@
+pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055
+pyparsing-2.2.1.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10
+pyparsing-2.2.1.dist-info/LICENSE.txt,sha256=081Pq74Spe1XdwrGkewNKSqa078kLIh7UWI-wVjdj8I,1041
+pyparsing-2.2.1.dist-info/METADATA,sha256=I0jhx9vpUYlQXjn4gVDnFFoAt3nNrxwR4iuqA_pknYs,1091
+pyparsing-2.2.1.dist-info/RECORD,,
+pyparsing-2.2.1.dist-info/WHEEL,sha256=kdsN-5OJAZIiHN-iO4Rhl82KyS0bDWf4uBwMbkNafr8,110
+pyparsing-2.2.1.dist-info/metadata.json,sha256=v1_77-dSdajUZSItSJg8Ov9M713STY3PzhyrRvs1ax4,1185
+pyparsing-2.2.1.dist-info/top_level.txt,sha256=eUOjGzJVhlQ3WS2rFAy2mN3LX_7FKTM5GSJ04jfnLmU,10
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/WHEEL b/_setuptools_vendored/pyparsing-2.2.1.dist-info/WHEEL
new file mode 100644
index 0000000000..7332a419cd
--- /dev/null
+++ b/_setuptools_vendored/pyparsing-2.2.1.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.30.0)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/metadata.json b/_setuptools_vendored/pyparsing-2.2.1.dist-info/metadata.json
new file mode 100644
index 0000000000..b760b766b0
--- /dev/null
+++ b/_setuptools_vendored/pyparsing-2.2.1.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7"], "download_url": "https://pypi.org/project/pyparsing/", "extensions": {"python.details": {"contacts": [{"email": "ptmcg@users.sourceforge.net", "name": "Paul McGuire", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "project_urls": {"Home": "https://github.com/pyparsing/pyparsing/"}}}, "generator": "bdist_wheel (0.30.0)", "license": "MIT License", "metadata_version": "2.0", "name": "pyparsing", "requires_python": ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*", "summary": "Python parsing module", "version": "2.2.1"}
\ No newline at end of file
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/top_level.txt b/_setuptools_vendored/pyparsing-2.2.1.dist-info/top_level.txt
new file mode 100644
index 0000000000..210dfec50b
--- /dev/null
+++ b/_setuptools_vendored/pyparsing-2.2.1.dist-info/top_level.txt
@@ -0,0 +1 @@
+pyparsing
diff --git a/_setuptools_vendored/pyparsing.py b/_setuptools_vendored/pyparsing.py
new file mode 100644
index 0000000000..cf75e1e5fc
--- /dev/null
+++ b/_setuptools_vendored/pyparsing.py
@@ -0,0 +1,5742 @@
+# module pyparsing.py
+#
+# Copyright (c) 2003-2018  Paul T. McGuire
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__doc__ = \
+"""
+pyparsing module - Classes and methods to define and execute parsing grammars
+=============================================================================
+
+The pyparsing module is an alternative approach to creating and executing simple grammars,
+vs. the traditional lex/yacc approach, or the use of regular expressions.  With pyparsing, you
+don't need to learn a new syntax for defining grammars or matching expressions - the parsing module
+provides a library of classes that you use to construct the grammar directly in Python.
+
+Here is a program to parse "Hello, World!" (or any greeting of the form 
+C{", !"}), built up using L{Word}, L{Literal}, and L{And} elements 
+(L{'+'} operator gives L{And} expressions, strings are auto-converted to
+L{Literal} expressions)::
+
+    from pyparsing import Word, alphas
+
+    # define grammar of a greeting
+    greet = Word(alphas) + "," + Word(alphas) + "!"
+
+    hello = "Hello, World!"
+    print (hello, "->", greet.parseString(hello))
+
+The program outputs the following::
+
+    Hello, World! -> ['Hello', ',', 'World', '!']
+
+The Python representation of the grammar is quite readable, owing to the self-explanatory
+class names, and the use of '+', '|' and '^' operators.
+
+The L{ParseResults} object returned from L{ParserElement.parseString} can be accessed as a nested list, a dictionary, or an
+object with named attributes.
+
+The pyparsing module handles some of the problems that are typically vexing when writing text parsers:
+ - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello  ,  World  !", etc.)
+ - quoted strings
+ - embedded comments
+
+
+Getting Started -
+-----------------
+Visit the classes L{ParserElement} and L{ParseResults} to see the base classes that most other pyparsing
+classes inherit from. Use the docstrings for examples of how to:
+ - construct literal match expressions from L{Literal} and L{CaselessLiteral} classes
+ - construct character word-group expressions using the L{Word} class
+ - see how to create repetitive expressions using L{ZeroOrMore} and L{OneOrMore} classes
+ - use L{'+'}, L{'|'}, L{'^'}, and L{'&'} operators to combine simple expressions into more complex ones
+ - associate names with your parsed results using L{ParserElement.setResultsName}
+ - find some helpful expression short-cuts like L{delimitedList} and L{oneOf}
+ - find more useful common expressions in the L{pyparsing_common} namespace class
+"""
+
+__version__ = "2.2.1"
+__versionTime__ = "18 Sep 2018 00:49 UTC"
+__author__ = "Paul McGuire "
+
+import string
+from weakref import ref as wkref
+import copy
+import sys
+import warnings
+import re
+import sre_constants
+import collections
+import pprint
+import traceback
+import types
+from datetime import datetime
+
+try:
+    from _thread import RLock
+except ImportError:
+    from threading import RLock
+
+try:
+    # Python 3
+    from collections.abc import Iterable
+    from collections.abc import MutableMapping
+except ImportError:
+    # Python 2.7
+    from collections import Iterable
+    from collections import MutableMapping
+
+try:
+    from collections import OrderedDict as _OrderedDict
+except ImportError:
+    try:
+        from ordereddict import OrderedDict as _OrderedDict
+    except ImportError:
+        _OrderedDict = None
+
+#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) )
+
+__all__ = [
+'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty',
+'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal',
+'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or',
+'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException',
+'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException',
+'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 
+'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore',
+'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col',
+'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString',
+'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums',
+'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno',
+'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral',
+'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables',
+'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', 
+'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd',
+'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute',
+'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass',
+'CloseMatch', 'tokenMap', 'pyparsing_common',
+]
+
+system_version = tuple(sys.version_info)[:3]
+PY_3 = system_version[0] == 3
+if PY_3:
+    _MAX_INT = sys.maxsize
+    basestring = str
+    unichr = chr
+    _ustr = str
+
+    # build list of single arg builtins, that can be used as parse actions
+    singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max]
+
+else:
+    _MAX_INT = sys.maxint
+    range = xrange
+
+    def _ustr(obj):
+        """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries
+           str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It
+           then < returns the unicode object | encodes it with the default encoding | ... >.
+        """
+        if isinstance(obj,unicode):
+            return obj
+
+        try:
+            # If this works, then _ustr(obj) has the same behaviour as str(obj), so
+            # it won't break any existing code.
+            return str(obj)
+
+        except UnicodeEncodeError:
+            # Else encode it
+            ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace')
+            xmlcharref = Regex(r'&#\d+;')
+            xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:])
+            return xmlcharref.transformString(ret)
+
+    # build list of single arg builtins, tolerant of Python version, that can be used as parse actions
+    singleArgBuiltins = []
+    import __builtin__
+    for fname in "sum len sorted reversed list tuple set any all min max".split():
+        try:
+            singleArgBuiltins.append(getattr(__builtin__,fname))
+        except AttributeError:
+            continue
+            
+_generatorType = type((y for y in range(1)))
+ 
+def _xml_escape(data):
+    """Escape &, <, >, ", ', etc. in a string of data."""
+
+    # ampersand must be replaced first
+    from_symbols = '&><"\''
+    to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split())
+    for from_,to_ in zip(from_symbols, to_symbols):
+        data = data.replace(from_, to_)
+    return data
+
+class _Constants(object):
+    pass
+
+alphas     = string.ascii_uppercase + string.ascii_lowercase
+nums       = "0123456789"
+hexnums    = nums + "ABCDEFabcdef"
+alphanums  = alphas + nums
+_bslash    = chr(92)
+printables = "".join(c for c in string.printable if c not in string.whitespace)
+
+class ParseBaseException(Exception):
+    """base exception class for all parsing runtime exceptions"""
+    # Performance tuning: we construct a *lot* of these, so keep this
+    # constructor as small and fast as possible
+    def __init__( self, pstr, loc=0, msg=None, elem=None ):
+        self.loc = loc
+        if msg is None:
+            self.msg = pstr
+            self.pstr = ""
+        else:
+            self.msg = msg
+            self.pstr = pstr
+        self.parserElement = elem
+        self.args = (pstr, loc, msg)
+
+    @classmethod
+    def _from_exception(cls, pe):
+        """
+        internal factory method to simplify creating one type of ParseException 
+        from another - avoids having __init__ signature conflicts among subclasses
+        """
+        return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement)
+
+    def __getattr__( self, aname ):
+        """supported attributes by name are:
+            - lineno - returns the line number of the exception text
+            - col - returns the column number of the exception text
+            - line - returns the line containing the exception text
+        """
+        if( aname == "lineno" ):
+            return lineno( self.loc, self.pstr )
+        elif( aname in ("col", "column") ):
+            return col( self.loc, self.pstr )
+        elif( aname == "line" ):
+            return line( self.loc, self.pstr )
+        else:
+            raise AttributeError(aname)
+
+    def __str__( self ):
+        return "%s (at char %d), (line:%d, col:%d)" % \
+                ( self.msg, self.loc, self.lineno, self.column )
+    def __repr__( self ):
+        return _ustr(self)
+    def markInputline( self, markerString = ">!<" ):
+        """Extracts the exception line from the input string, and marks
+           the location of the exception with a special symbol.
+        """
+        line_str = self.line
+        line_column = self.column - 1
+        if markerString:
+            line_str = "".join((line_str[:line_column],
+                                markerString, line_str[line_column:]))
+        return line_str.strip()
+    def __dir__(self):
+        return "lineno col line".split() + dir(type(self))
+
+class ParseException(ParseBaseException):
+    """
+    Exception thrown when parse expressions don't match class;
+    supported attributes by name are:
+     - lineno - returns the line number of the exception text
+     - col - returns the column number of the exception text
+     - line - returns the line containing the exception text
+        
+    Example::
+        try:
+            Word(nums).setName("integer").parseString("ABC")
+        except ParseException as pe:
+            print(pe)
+            print("column: {}".format(pe.col))
+            
+    prints::
+       Expected integer (at char 0), (line:1, col:1)
+        column: 1
+    """
+    pass
+
+class ParseFatalException(ParseBaseException):
+    """user-throwable exception thrown when inconsistent parse content
+       is found; stops all parsing immediately"""
+    pass
+
+class ParseSyntaxException(ParseFatalException):
+    """just like L{ParseFatalException}, but thrown internally when an
+       L{ErrorStop} ('-' operator) indicates that parsing is to stop 
+       immediately because an unbacktrackable syntax error has been found"""
+    pass
+
+#~ class ReparseException(ParseBaseException):
+    #~ """Experimental class - parse actions can raise this exception to cause
+       #~ pyparsing to reparse the input string:
+        #~ - with a modified input string, and/or
+        #~ - with a modified start location
+       #~ Set the values of the ReparseException in the constructor, and raise the
+       #~ exception in a parse action to cause pyparsing to use the new string/location.
+       #~ Setting the values as None causes no change to be made.
+       #~ """
+    #~ def __init_( self, newstring, restartLoc ):
+        #~ self.newParseText = newstring
+        #~ self.reparseLoc = restartLoc
+
+class RecursiveGrammarException(Exception):
+    """exception thrown by L{ParserElement.validate} if the grammar could be improperly recursive"""
+    def __init__( self, parseElementList ):
+        self.parseElementTrace = parseElementList
+
+    def __str__( self ):
+        return "RecursiveGrammarException: %s" % self.parseElementTrace
+
+class _ParseResultsWithOffset(object):
+    def __init__(self,p1,p2):
+        self.tup = (p1,p2)
+    def __getitem__(self,i):
+        return self.tup[i]
+    def __repr__(self):
+        return repr(self.tup[0])
+    def setOffset(self,i):
+        self.tup = (self.tup[0],i)
+
+class ParseResults(object):
+    """
+    Structured parse results, to provide multiple means of access to the parsed data:
+       - as a list (C{len(results)})
+       - by list index (C{results[0], results[1]}, etc.)
+       - by attribute (C{results.} - see L{ParserElement.setResultsName})
+
+    Example::
+        integer = Word(nums)
+        date_str = (integer.setResultsName("year") + '/' 
+                        + integer.setResultsName("month") + '/' 
+                        + integer.setResultsName("day"))
+        # equivalent form:
+        # date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+
+        # parseString returns a ParseResults object
+        result = date_str.parseString("1999/12/31")
+
+        def test(s, fn=repr):
+            print("%s -> %s" % (s, fn(eval(s))))
+        test("list(result)")
+        test("result[0]")
+        test("result['month']")
+        test("result.day")
+        test("'month' in result")
+        test("'minutes' in result")
+        test("result.dump()", str)
+    prints::
+        list(result) -> ['1999', '/', '12', '/', '31']
+        result[0] -> '1999'
+        result['month'] -> '12'
+        result.day -> '31'
+        'month' in result -> True
+        'minutes' in result -> False
+        result.dump() -> ['1999', '/', '12', '/', '31']
+        - day: 31
+        - month: 12
+        - year: 1999
+    """
+    def __new__(cls, toklist=None, name=None, asList=True, modal=True ):
+        if isinstance(toklist, cls):
+            return toklist
+        retobj = object.__new__(cls)
+        retobj.__doinit = True
+        return retobj
+
+    # Performance tuning: we construct a *lot* of these, so keep this
+    # constructor as small and fast as possible
+    def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ):
+        if self.__doinit:
+            self.__doinit = False
+            self.__name = None
+            self.__parent = None
+            self.__accumNames = {}
+            self.__asList = asList
+            self.__modal = modal
+            if toklist is None:
+                toklist = []
+            if isinstance(toklist, list):
+                self.__toklist = toklist[:]
+            elif isinstance(toklist, _generatorType):
+                self.__toklist = list(toklist)
+            else:
+                self.__toklist = [toklist]
+            self.__tokdict = dict()
+
+        if name is not None and name:
+            if not modal:
+                self.__accumNames[name] = 0
+            if isinstance(name,int):
+                name = _ustr(name) # will always return a str, but use _ustr for consistency
+            self.__name = name
+            if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])):
+                if isinstance(toklist,basestring):
+                    toklist = [ toklist ]
+                if asList:
+                    if isinstance(toklist,ParseResults):
+                        self[name] = _ParseResultsWithOffset(toklist.copy(),0)
+                    else:
+                        self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0)
+                    self[name].__name = name
+                else:
+                    try:
+                        self[name] = toklist[0]
+                    except (KeyError,TypeError,IndexError):
+                        self[name] = toklist
+
+    def __getitem__( self, i ):
+        if isinstance( i, (int,slice) ):
+            return self.__toklist[i]
+        else:
+            if i not in self.__accumNames:
+                return self.__tokdict[i][-1][0]
+            else:
+                return ParseResults([ v[0] for v in self.__tokdict[i] ])
+
+    def __setitem__( self, k, v, isinstance=isinstance ):
+        if isinstance(v,_ParseResultsWithOffset):
+            self.__tokdict[k] = self.__tokdict.get(k,list()) + [v]
+            sub = v[0]
+        elif isinstance(k,(int,slice)):
+            self.__toklist[k] = v
+            sub = v
+        else:
+            self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)]
+            sub = v
+        if isinstance(sub,ParseResults):
+            sub.__parent = wkref(self)
+
+    def __delitem__( self, i ):
+        if isinstance(i,(int,slice)):
+            mylen = len( self.__toklist )
+            del self.__toklist[i]
+
+            # convert int to slice
+            if isinstance(i, int):
+                if i < 0:
+                    i += mylen
+                i = slice(i, i+1)
+            # get removed indices
+            removed = list(range(*i.indices(mylen)))
+            removed.reverse()
+            # fixup indices in token dictionary
+            for name,occurrences in self.__tokdict.items():
+                for j in removed:
+                    for k, (value, position) in enumerate(occurrences):
+                        occurrences[k] = _ParseResultsWithOffset(value, position - (position > j))
+        else:
+            del self.__tokdict[i]
+
+    def __contains__( self, k ):
+        return k in self.__tokdict
+
+    def __len__( self ): return len( self.__toklist )
+    def __bool__(self): return ( not not self.__toklist )
+    __nonzero__ = __bool__
+    def __iter__( self ): return iter( self.__toklist )
+    def __reversed__( self ): return iter( self.__toklist[::-1] )
+    def _iterkeys( self ):
+        if hasattr(self.__tokdict, "iterkeys"):
+            return self.__tokdict.iterkeys()
+        else:
+            return iter(self.__tokdict)
+
+    def _itervalues( self ):
+        return (self[k] for k in self._iterkeys())
+            
+    def _iteritems( self ):
+        return ((k, self[k]) for k in self._iterkeys())
+
+    if PY_3:
+        keys = _iterkeys       
+        """Returns an iterator of all named result keys (Python 3.x only)."""
+
+        values = _itervalues
+        """Returns an iterator of all named result values (Python 3.x only)."""
+
+        items = _iteritems
+        """Returns an iterator of all named result key-value tuples (Python 3.x only)."""
+
+    else:
+        iterkeys = _iterkeys
+        """Returns an iterator of all named result keys (Python 2.x only)."""
+
+        itervalues = _itervalues
+        """Returns an iterator of all named result values (Python 2.x only)."""
+
+        iteritems = _iteritems
+        """Returns an iterator of all named result key-value tuples (Python 2.x only)."""
+
+        def keys( self ):
+            """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x)."""
+            return list(self.iterkeys())
+
+        def values( self ):
+            """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x)."""
+            return list(self.itervalues())
+                
+        def items( self ):
+            """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x)."""
+            return list(self.iteritems())
+
+    def haskeys( self ):
+        """Since keys() returns an iterator, this method is helpful in bypassing
+           code that looks for the existence of any defined results names."""
+        return bool(self.__tokdict)
+        
+    def pop( self, *args, **kwargs):
+        """
+        Removes and returns item at specified index (default=C{last}).
+        Supports both C{list} and C{dict} semantics for C{pop()}. If passed no
+        argument or an integer argument, it will use C{list} semantics
+        and pop tokens from the list of parsed tokens. If passed a 
+        non-integer argument (most likely a string), it will use C{dict}
+        semantics and pop the corresponding value from any defined 
+        results names. A second default return value argument is 
+        supported, just as in C{dict.pop()}.
+
+        Example::
+            def remove_first(tokens):
+                tokens.pop(0)
+            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
+            print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321']
+
+            label = Word(alphas)
+            patt = label("LABEL") + OneOrMore(Word(nums))
+            print(patt.parseString("AAB 123 321").dump())
+
+            # Use pop() in a parse action to remove named result (note that corresponding value is not
+            # removed from list form of results)
+            def remove_LABEL(tokens):
+                tokens.pop("LABEL")
+                return tokens
+            patt.addParseAction(remove_LABEL)
+            print(patt.parseString("AAB 123 321").dump())
+        prints::
+            ['AAB', '123', '321']
+            - LABEL: AAB
+
+            ['AAB', '123', '321']
+        """
+        if not args:
+            args = [-1]
+        for k,v in kwargs.items():
+            if k == 'default':
+                args = (args[0], v)
+            else:
+                raise TypeError("pop() got an unexpected keyword argument '%s'" % k)
+        if (isinstance(args[0], int) or 
+                        len(args) == 1 or 
+                        args[0] in self):
+            index = args[0]
+            ret = self[index]
+            del self[index]
+            return ret
+        else:
+            defaultvalue = args[1]
+            return defaultvalue
+
+    def get(self, key, defaultValue=None):
+        """
+        Returns named result matching the given key, or if there is no
+        such name, then returns the given C{defaultValue} or C{None} if no
+        C{defaultValue} is specified.
+
+        Similar to C{dict.get()}.
+        
+        Example::
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
+
+            result = date_str.parseString("1999/12/31")
+            print(result.get("year")) # -> '1999'
+            print(result.get("hour", "not specified")) # -> 'not specified'
+            print(result.get("hour")) # -> None
+        """
+        if key in self:
+            return self[key]
+        else:
+            return defaultValue
+
+    def insert( self, index, insStr ):
+        """
+        Inserts new element at location index in the list of parsed tokens.
+        
+        Similar to C{list.insert()}.
+
+        Example::
+            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
+
+            # use a parse action to insert the parse location in the front of the parsed results
+            def insert_locn(locn, tokens):
+                tokens.insert(0, locn)
+            print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321']
+        """
+        self.__toklist.insert(index, insStr)
+        # fixup indices in token dictionary
+        for name,occurrences in self.__tokdict.items():
+            for k, (value, position) in enumerate(occurrences):
+                occurrences[k] = _ParseResultsWithOffset(value, position + (position > index))
+
+    def append( self, item ):
+        """
+        Add single element to end of ParseResults list of elements.
+
+        Example::
+            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
+            
+            # use a parse action to compute the sum of the parsed integers, and add it to the end
+            def append_sum(tokens):
+                tokens.append(sum(map(int, tokens)))
+            print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444]
+        """
+        self.__toklist.append(item)
+
+    def extend( self, itemseq ):
+        """
+        Add sequence of elements to end of ParseResults list of elements.
+
+        Example::
+            patt = OneOrMore(Word(alphas))
+            
+            # use a parse action to append the reverse of the matched strings, to make a palindrome
+            def make_palindrome(tokens):
+                tokens.extend(reversed([t[::-1] for t in tokens]))
+                return ''.join(tokens)
+            print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl'
+        """
+        if isinstance(itemseq, ParseResults):
+            self += itemseq
+        else:
+            self.__toklist.extend(itemseq)
+
+    def clear( self ):
+        """
+        Clear all elements and results names.
+        """
+        del self.__toklist[:]
+        self.__tokdict.clear()
+
+    def __getattr__( self, name ):
+        try:
+            return self[name]
+        except KeyError:
+            return ""
+            
+        if name in self.__tokdict:
+            if name not in self.__accumNames:
+                return self.__tokdict[name][-1][0]
+            else:
+                return ParseResults([ v[0] for v in self.__tokdict[name] ])
+        else:
+            return ""
+
+    def __add__( self, other ):
+        ret = self.copy()
+        ret += other
+        return ret
+
+    def __iadd__( self, other ):
+        if other.__tokdict:
+            offset = len(self.__toklist)
+            addoffset = lambda a: offset if a<0 else a+offset
+            otheritems = other.__tokdict.items()
+            otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) )
+                                for (k,vlist) in otheritems for v in vlist]
+            for k,v in otherdictitems:
+                self[k] = v
+                if isinstance(v[0],ParseResults):
+                    v[0].__parent = wkref(self)
+            
+        self.__toklist += other.__toklist
+        self.__accumNames.update( other.__accumNames )
+        return self
+
+    def __radd__(self, other):
+        if isinstance(other,int) and other == 0:
+            # useful for merging many ParseResults using sum() builtin
+            return self.copy()
+        else:
+            # this may raise a TypeError - so be it
+            return other + self
+        
+    def __repr__( self ):
+        return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) )
+
+    def __str__( self ):
+        return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']'
+
+    def _asStringList( self, sep='' ):
+        out = []
+        for item in self.__toklist:
+            if out and sep:
+                out.append(sep)
+            if isinstance( item, ParseResults ):
+                out += item._asStringList()
+            else:
+                out.append( _ustr(item) )
+        return out
+
+    def asList( self ):
+        """
+        Returns the parse results as a nested list of matching tokens, all converted to strings.
+
+        Example::
+            patt = OneOrMore(Word(alphas))
+            result = patt.parseString("sldkj lsdkj sldkj")
+            # even though the result prints in string-like form, it is actually a pyparsing ParseResults
+            print(type(result), result) # ->  ['sldkj', 'lsdkj', 'sldkj']
+            
+            # Use asList() to create an actual list
+            result_list = result.asList()
+            print(type(result_list), result_list) # ->  ['sldkj', 'lsdkj', 'sldkj']
+        """
+        return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist]
+
+    def asDict( self ):
+        """
+        Returns the named parse results as a nested dictionary.
+
+        Example::
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+            
+            result = date_str.parseString('12/31/1999')
+            print(type(result), repr(result)) # ->  (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
+            
+            result_dict = result.asDict()
+            print(type(result_dict), repr(result_dict)) # ->  {'day': '1999', 'year': '12', 'month': '31'}
+
+            # even though a ParseResults supports dict-like access, sometime you just need to have a dict
+            import json
+            print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable
+            print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"}
+        """
+        if PY_3:
+            item_fn = self.items
+        else:
+            item_fn = self.iteritems
+            
+        def toItem(obj):
+            if isinstance(obj, ParseResults):
+                if obj.haskeys():
+                    return obj.asDict()
+                else:
+                    return [toItem(v) for v in obj]
+            else:
+                return obj
+                
+        return dict((k,toItem(v)) for k,v in item_fn())
+
+    def copy( self ):
+        """
+        Returns a new copy of a C{ParseResults} object.
+        """
+        ret = ParseResults( self.__toklist )
+        ret.__tokdict = self.__tokdict.copy()
+        ret.__parent = self.__parent
+        ret.__accumNames.update( self.__accumNames )
+        ret.__name = self.__name
+        return ret
+
+    def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ):
+        """
+        (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.
+        """
+        nl = "\n"
+        out = []
+        namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items()
+                                                            for v in vlist)
+        nextLevelIndent = indent + "  "
+
+        # collapse out indents if formatting is not desired
+        if not formatted:
+            indent = ""
+            nextLevelIndent = ""
+            nl = ""
+
+        selfTag = None
+        if doctag is not None:
+            selfTag = doctag
+        else:
+            if self.__name:
+                selfTag = self.__name
+
+        if not selfTag:
+            if namedItemsOnly:
+                return ""
+            else:
+                selfTag = "ITEM"
+
+        out += [ nl, indent, "<", selfTag, ">" ]
+
+        for i,res in enumerate(self.__toklist):
+            if isinstance(res,ParseResults):
+                if i in namedItems:
+                    out += [ res.asXML(namedItems[i],
+                                        namedItemsOnly and doctag is None,
+                                        nextLevelIndent,
+                                        formatted)]
+                else:
+                    out += [ res.asXML(None,
+                                        namedItemsOnly and doctag is None,
+                                        nextLevelIndent,
+                                        formatted)]
+            else:
+                # individual token, see if there is a name for it
+                resTag = None
+                if i in namedItems:
+                    resTag = namedItems[i]
+                if not resTag:
+                    if namedItemsOnly:
+                        continue
+                    else:
+                        resTag = "ITEM"
+                xmlBodyText = _xml_escape(_ustr(res))
+                out += [ nl, nextLevelIndent, "<", resTag, ">",
+                                                xmlBodyText,
+                                                "" ]
+
+        out += [ nl, indent, "" ]
+        return "".join(out)
+
+    def __lookup(self,sub):
+        for k,vlist in self.__tokdict.items():
+            for v,loc in vlist:
+                if sub is v:
+                    return k
+        return None
+
+    def getName(self):
+        r"""
+        Returns the results name for this token expression. Useful when several 
+        different expressions might match at a particular location.
+
+        Example::
+            integer = Word(nums)
+            ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d")
+            house_number_expr = Suppress('#') + Word(nums, alphanums)
+            user_data = (Group(house_number_expr)("house_number") 
+                        | Group(ssn_expr)("ssn")
+                        | Group(integer)("age"))
+            user_info = OneOrMore(user_data)
+            
+            result = user_info.parseString("22 111-22-3333 #221B")
+            for item in result:
+                print(item.getName(), ':', item[0])
+        prints::
+            age : 22
+            ssn : 111-22-3333
+            house_number : 221B
+        """
+        if self.__name:
+            return self.__name
+        elif self.__parent:
+            par = self.__parent()
+            if par:
+                return par.__lookup(self)
+            else:
+                return None
+        elif (len(self) == 1 and
+               len(self.__tokdict) == 1 and
+               next(iter(self.__tokdict.values()))[0][1] in (0,-1)):
+            return next(iter(self.__tokdict.keys()))
+        else:
+            return None
+
+    def dump(self, indent='', depth=0, full=True):
+        """
+        Diagnostic method for listing out the contents of a C{ParseResults}.
+        Accepts an optional C{indent} argument so that this string can be embedded
+        in a nested display of other data.
+
+        Example::
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+            
+            result = date_str.parseString('12/31/1999')
+            print(result.dump())
+        prints::
+            ['12', '/', '31', '/', '1999']
+            - day: 1999
+            - month: 31
+            - year: 12
+        """
+        out = []
+        NL = '\n'
+        out.append( indent+_ustr(self.asList()) )
+        if full:
+            if self.haskeys():
+                items = sorted((str(k), v) for k,v in self.items())
+                for k,v in items:
+                    if out:
+                        out.append(NL)
+                    out.append( "%s%s- %s: " % (indent,('  '*depth), k) )
+                    if isinstance(v,ParseResults):
+                        if v:
+                            out.append( v.dump(indent,depth+1) )
+                        else:
+                            out.append(_ustr(v))
+                    else:
+                        out.append(repr(v))
+            elif any(isinstance(vv,ParseResults) for vv in self):
+                v = self
+                for i,vv in enumerate(v):
+                    if isinstance(vv,ParseResults):
+                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),vv.dump(indent,depth+1) ))
+                    else:
+                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),_ustr(vv)))
+            
+        return "".join(out)
+
+    def pprint(self, *args, **kwargs):
+        """
+        Pretty-printer for parsed results as a list, using the C{pprint} module.
+        Accepts additional positional or keyword args as defined for the 
+        C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint})
+
+        Example::
+            ident = Word(alphas, alphanums)
+            num = Word(nums)
+            func = Forward()
+            term = ident | num | Group('(' + func + ')')
+            func <<= ident + Group(Optional(delimitedList(term)))
+            result = func.parseString("fna a,b,(fnb c,d,200),100")
+            result.pprint(width=40)
+        prints::
+            ['fna',
+             ['a',
+              'b',
+              ['(', 'fnb', ['c', 'd', '200'], ')'],
+              '100']]
+        """
+        pprint.pprint(self.asList(), *args, **kwargs)
+
+    # add support for pickle protocol
+    def __getstate__(self):
+        return ( self.__toklist,
+                 ( self.__tokdict.copy(),
+                   self.__parent is not None and self.__parent() or None,
+                   self.__accumNames,
+                   self.__name ) )
+
+    def __setstate__(self,state):
+        self.__toklist = state[0]
+        (self.__tokdict,
+         par,
+         inAccumNames,
+         self.__name) = state[1]
+        self.__accumNames = {}
+        self.__accumNames.update(inAccumNames)
+        if par is not None:
+            self.__parent = wkref(par)
+        else:
+            self.__parent = None
+
+    def __getnewargs__(self):
+        return self.__toklist, self.__name, self.__asList, self.__modal
+
+    def __dir__(self):
+        return (dir(type(self)) + list(self.keys()))
+
+MutableMapping.register(ParseResults)
+
+def col (loc,strg):
+    """Returns current column within a string, counting newlines as line separators.
+   The first column is number 1.
+
+   Note: the default parsing behavior is to expand tabs in the input string
+   before starting the parsing process.  See L{I{ParserElement.parseString}} for more information
+   on parsing strings containing C{}s, and suggested methods to maintain a
+   consistent view of the parsed string, the parse location, and line and column
+   positions within the parsed string.
+   """
+    s = strg
+    return 1 if 0} for more information
+   on parsing strings containing C{}s, and suggested methods to maintain a
+   consistent view of the parsed string, the parse location, and line and column
+   positions within the parsed string.
+   """
+    return strg.count("\n",0,loc) + 1
+
+def line( loc, strg ):
+    """Returns the line of text containing loc within a string, counting newlines as line separators.
+       """
+    lastCR = strg.rfind("\n", 0, loc)
+    nextCR = strg.find("\n", loc)
+    if nextCR >= 0:
+        return strg[lastCR+1:nextCR]
+    else:
+        return strg[lastCR+1:]
+
+def _defaultStartDebugAction( instring, loc, expr ):
+    print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )))
+
+def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ):
+    print ("Matched " + _ustr(expr) + " -> " + str(toks.asList()))
+
+def _defaultExceptionDebugAction( instring, loc, expr, exc ):
+    print ("Exception raised:" + _ustr(exc))
+
+def nullDebugAction(*args):
+    """'Do-nothing' debug action, to suppress debugging output during parsing."""
+    pass
+
+# Only works on Python 3.x - nonlocal is toxic to Python 2 installs
+#~ 'decorator to trim function calls to match the arity of the target'
+#~ def _trim_arity(func, maxargs=3):
+    #~ if func in singleArgBuiltins:
+        #~ return lambda s,l,t: func(t)
+    #~ limit = 0
+    #~ foundArity = False
+    #~ def wrapper(*args):
+        #~ nonlocal limit,foundArity
+        #~ while 1:
+            #~ try:
+                #~ ret = func(*args[limit:])
+                #~ foundArity = True
+                #~ return ret
+            #~ except TypeError:
+                #~ if limit == maxargs or foundArity:
+                    #~ raise
+                #~ limit += 1
+                #~ continue
+    #~ return wrapper
+
+# this version is Python 2.x-3.x cross-compatible
+'decorator to trim function calls to match the arity of the target'
+def _trim_arity(func, maxargs=2):
+    if func in singleArgBuiltins:
+        return lambda s,l,t: func(t)
+    limit = [0]
+    foundArity = [False]
+    
+    # traceback return data structure changed in Py3.5 - normalize back to plain tuples
+    if system_version[:2] >= (3,5):
+        def extract_stack(limit=0):
+            # special handling for Python 3.5.0 - extra deep call stack by 1
+            offset = -3 if system_version == (3,5,0) else -2
+            frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset]
+            return [frame_summary[:2]]
+        def extract_tb(tb, limit=0):
+            frames = traceback.extract_tb(tb, limit=limit)
+            frame_summary = frames[-1]
+            return [frame_summary[:2]]
+    else:
+        extract_stack = traceback.extract_stack
+        extract_tb = traceback.extract_tb
+    
+    # synthesize what would be returned by traceback.extract_stack at the call to 
+    # user's parse action 'func', so that we don't incur call penalty at parse time
+    
+    LINE_DIFF = 6
+    # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND 
+    # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!!
+    this_line = extract_stack(limit=2)[-1]
+    pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF)
+
+    def wrapper(*args):
+        while 1:
+            try:
+                ret = func(*args[limit[0]:])
+                foundArity[0] = True
+                return ret
+            except TypeError:
+                # re-raise TypeErrors if they did not come from our arity testing
+                if foundArity[0]:
+                    raise
+                else:
+                    try:
+                        tb = sys.exc_info()[-1]
+                        if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth:
+                            raise
+                    finally:
+                        del tb
+
+                if limit[0] <= maxargs:
+                    limit[0] += 1
+                    continue
+                raise
+
+    # copy func name to wrapper for sensible debug output
+    func_name = ""
+    try:
+        func_name = getattr(func, '__name__', 
+                            getattr(func, '__class__').__name__)
+    except Exception:
+        func_name = str(func)
+    wrapper.__name__ = func_name
+
+    return wrapper
+
+class ParserElement(object):
+    """Abstract base level parser element class."""
+    DEFAULT_WHITE_CHARS = " \n\t\r"
+    verbose_stacktrace = False
+
+    @staticmethod
+    def setDefaultWhitespaceChars( chars ):
+        r"""
+        Overrides the default whitespace chars
+
+        Example::
+            # default whitespace chars are space,  and newline
+            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def', 'ghi', 'jkl']
+            
+            # change to just treat newline as significant
+            ParserElement.setDefaultWhitespaceChars(" \t")
+            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def']
+        """
+        ParserElement.DEFAULT_WHITE_CHARS = chars
+
+    @staticmethod
+    def inlineLiteralsUsing(cls):
+        """
+        Set class to be used for inclusion of string literals into a parser.
+        
+        Example::
+            # default literal class used is Literal
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
+
+            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
+
+
+            # change to Suppress
+            ParserElement.inlineLiteralsUsing(Suppress)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
+
+            date_str.parseString("1999/12/31")  # -> ['1999', '12', '31']
+        """
+        ParserElement._literalStringClass = cls
+
+    def __init__( self, savelist=False ):
+        self.parseAction = list()
+        self.failAction = None
+        #~ self.name = ""  # don't define self.name, let subclasses try/except upcall
+        self.strRepr = None
+        self.resultsName = None
+        self.saveAsList = savelist
+        self.skipWhitespace = True
+        self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
+        self.copyDefaultWhiteChars = True
+        self.mayReturnEmpty = False # used when checking for left-recursion
+        self.keepTabs = False
+        self.ignoreExprs = list()
+        self.debug = False
+        self.streamlined = False
+        self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index
+        self.errmsg = ""
+        self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all)
+        self.debugActions = ( None, None, None ) #custom debug actions
+        self.re = None
+        self.callPreparse = True # used to avoid redundant calls to preParse
+        self.callDuringTry = False
+
+    def copy( self ):
+        """
+        Make a copy of this C{ParserElement}.  Useful for defining different parse actions
+        for the same parsing pattern, using copies of the original parse element.
+        
+        Example::
+            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
+            integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K")
+            integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
+            
+            print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M"))
+        prints::
+            [5120, 100, 655360, 268435456]
+        Equivalent form of C{expr.copy()} is just C{expr()}::
+            integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
+        """
+        cpy = copy.copy( self )
+        cpy.parseAction = self.parseAction[:]
+        cpy.ignoreExprs = self.ignoreExprs[:]
+        if self.copyDefaultWhiteChars:
+            cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
+        return cpy
+
+    def setName( self, name ):
+        """
+        Define name for this expression, makes debugging and exception messages clearer.
+        
+        Example::
+            Word(nums).parseString("ABC")  # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1)
+            Word(nums).setName("integer").parseString("ABC")  # -> Exception: Expected integer (at char 0), (line:1, col:1)
+        """
+        self.name = name
+        self.errmsg = "Expected " + self.name
+        if hasattr(self,"exception"):
+            self.exception.msg = self.errmsg
+        return self
+
+    def setResultsName( self, name, listAllMatches=False ):
+        """
+        Define name for referencing matching tokens as a nested attribute
+        of the returned parse results.
+        NOTE: this returns a *copy* of the original C{ParserElement} object;
+        this is so that the client can define a basic element, such as an
+        integer, and reference it in multiple places with different names.
+
+        You can also set results names using the abbreviated syntax,
+        C{expr("name")} in place of C{expr.setResultsName("name")} - 
+        see L{I{__call__}<__call__>}.
+
+        Example::
+            date_str = (integer.setResultsName("year") + '/' 
+                        + integer.setResultsName("month") + '/' 
+                        + integer.setResultsName("day"))
+
+            # equivalent form:
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+        """
+        newself = self.copy()
+        if name.endswith("*"):
+            name = name[:-1]
+            listAllMatches=True
+        newself.resultsName = name
+        newself.modalResults = not listAllMatches
+        return newself
+
+    def setBreak(self,breakFlag = True):
+        """Method to invoke the Python pdb debugger when this element is
+           about to be parsed. Set C{breakFlag} to True to enable, False to
+           disable.
+        """
+        if breakFlag:
+            _parseMethod = self._parse
+            def breaker(instring, loc, doActions=True, callPreParse=True):
+                import pdb
+                pdb.set_trace()
+                return _parseMethod( instring, loc, doActions, callPreParse )
+            breaker._originalParseMethod = _parseMethod
+            self._parse = breaker
+        else:
+            if hasattr(self._parse,"_originalParseMethod"):
+                self._parse = self._parse._originalParseMethod
+        return self
+
+    def setParseAction( self, *fns, **kwargs ):
+        """
+        Define one or more actions to perform when successfully matching parse element definition.
+        Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)},
+        C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where:
+         - s   = the original string being parsed (see note below)
+         - loc = the location of the matching substring
+         - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object
+        If the functions in fns modify the tokens, they can return them as the return
+        value from fn, and the modified list of tokens will replace the original.
+        Otherwise, fn does not need to return any value.
+
+        Optional keyword arguments:
+         - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing
+
+        Note: the default parsing behavior is to expand tabs in the input string
+        before starting the parsing process.  See L{I{parseString}} for more information
+        on parsing strings containing C{}s, and suggested methods to maintain a
+        consistent view of the parsed string, the parse location, and line and column
+        positions within the parsed string.
+        
+        Example::
+            integer = Word(nums)
+            date_str = integer + '/' + integer + '/' + integer
+
+            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
+
+            # use parse action to convert to ints at parse time
+            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
+            date_str = integer + '/' + integer + '/' + integer
+
+            # note that integer fields are now ints, not strings
+            date_str.parseString("1999/12/31")  # -> [1999, '/', 12, '/', 31]
+        """
+        self.parseAction = list(map(_trim_arity, list(fns)))
+        self.callDuringTry = kwargs.get("callDuringTry", False)
+        return self
+
+    def addParseAction( self, *fns, **kwargs ):
+        """
+        Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}}.
+        
+        See examples in L{I{copy}}.
+        """
+        self.parseAction += list(map(_trim_arity, list(fns)))
+        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
+        return self
+
+    def addCondition(self, *fns, **kwargs):
+        """Add a boolean predicate function to expression's list of parse actions. See 
+        L{I{setParseAction}} for function call signatures. Unlike C{setParseAction}, 
+        functions passed to C{addCondition} need to return boolean success/fail of the condition.
+
+        Optional keyword arguments:
+         - message = define a custom message to be used in the raised exception
+         - fatal   = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException
+         
+        Example::
+            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
+            year_int = integer.copy()
+            year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later")
+            date_str = year_int + '/' + integer + '/' + integer
+
+            result = date_str.parseString("1999/12/31")  # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1)
+        """
+        msg = kwargs.get("message", "failed user-defined condition")
+        exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException
+        for fn in fns:
+            def pa(s,l,t):
+                if not bool(_trim_arity(fn)(s,l,t)):
+                    raise exc_type(s,l,msg)
+            self.parseAction.append(pa)
+        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
+        return self
+
+    def setFailAction( self, fn ):
+        """Define action to perform if parsing fails at this expression.
+           Fail acton fn is a callable function that takes the arguments
+           C{fn(s,loc,expr,err)} where:
+            - s = string being parsed
+            - loc = location where expression match was attempted and failed
+            - expr = the parse expression that failed
+            - err = the exception thrown
+           The function returns no value.  It may throw C{L{ParseFatalException}}
+           if it is desired to stop parsing immediately."""
+        self.failAction = fn
+        return self
+
+    def _skipIgnorables( self, instring, loc ):
+        exprsFound = True
+        while exprsFound:
+            exprsFound = False
+            for e in self.ignoreExprs:
+                try:
+                    while 1:
+                        loc,dummy = e._parse( instring, loc )
+                        exprsFound = True
+                except ParseException:
+                    pass
+        return loc
+
+    def preParse( self, instring, loc ):
+        if self.ignoreExprs:
+            loc = self._skipIgnorables( instring, loc )
+
+        if self.skipWhitespace:
+            wt = self.whiteChars
+            instrlen = len(instring)
+            while loc < instrlen and instring[loc] in wt:
+                loc += 1
+
+        return loc
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        return loc, []
+
+    def postParse( self, instring, loc, tokenlist ):
+        return tokenlist
+
+    #~ @profile
+    def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ):
+        debugging = ( self.debug ) #and doActions )
+
+        if debugging or self.failAction:
+            #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))
+            if (self.debugActions[0] ):
+                self.debugActions[0]( instring, loc, self )
+            if callPreParse and self.callPreparse:
+                preloc = self.preParse( instring, loc )
+            else:
+                preloc = loc
+            tokensStart = preloc
+            try:
+                try:
+                    loc,tokens = self.parseImpl( instring, preloc, doActions )
+                except IndexError:
+                    raise ParseException( instring, len(instring), self.errmsg, self )
+            except ParseBaseException as err:
+                #~ print ("Exception raised:", err)
+                if self.debugActions[2]:
+                    self.debugActions[2]( instring, tokensStart, self, err )
+                if self.failAction:
+                    self.failAction( instring, tokensStart, self, err )
+                raise
+        else:
+            if callPreParse and self.callPreparse:
+                preloc = self.preParse( instring, loc )
+            else:
+                preloc = loc
+            tokensStart = preloc
+            if self.mayIndexError or preloc >= len(instring):
+                try:
+                    loc,tokens = self.parseImpl( instring, preloc, doActions )
+                except IndexError:
+                    raise ParseException( instring, len(instring), self.errmsg, self )
+            else:
+                loc,tokens = self.parseImpl( instring, preloc, doActions )
+
+        tokens = self.postParse( instring, loc, tokens )
+
+        retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults )
+        if self.parseAction and (doActions or self.callDuringTry):
+            if debugging:
+                try:
+                    for fn in self.parseAction:
+                        tokens = fn( instring, tokensStart, retTokens )
+                        if tokens is not None:
+                            retTokens = ParseResults( tokens,
+                                                      self.resultsName,
+                                                      asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
+                                                      modal=self.modalResults )
+                except ParseBaseException as err:
+                    #~ print "Exception raised in user parse action:", err
+                    if (self.debugActions[2] ):
+                        self.debugActions[2]( instring, tokensStart, self, err )
+                    raise
+            else:
+                for fn in self.parseAction:
+                    tokens = fn( instring, tokensStart, retTokens )
+                    if tokens is not None:
+                        retTokens = ParseResults( tokens,
+                                                  self.resultsName,
+                                                  asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
+                                                  modal=self.modalResults )
+        if debugging:
+            #~ print ("Matched",self,"->",retTokens.asList())
+            if (self.debugActions[1] ):
+                self.debugActions[1]( instring, tokensStart, loc, self, retTokens )
+
+        return loc, retTokens
+
+    def tryParse( self, instring, loc ):
+        try:
+            return self._parse( instring, loc, doActions=False )[0]
+        except ParseFatalException:
+            raise ParseException( instring, loc, self.errmsg, self)
+    
+    def canParseNext(self, instring, loc):
+        try:
+            self.tryParse(instring, loc)
+        except (ParseException, IndexError):
+            return False
+        else:
+            return True
+
+    class _UnboundedCache(object):
+        def __init__(self):
+            cache = {}
+            self.not_in_cache = not_in_cache = object()
+
+            def get(self, key):
+                return cache.get(key, not_in_cache)
+
+            def set(self, key, value):
+                cache[key] = value
+
+            def clear(self):
+                cache.clear()
+                
+            def cache_len(self):
+                return len(cache)
+
+            self.get = types.MethodType(get, self)
+            self.set = types.MethodType(set, self)
+            self.clear = types.MethodType(clear, self)
+            self.__len__ = types.MethodType(cache_len, self)
+
+    if _OrderedDict is not None:
+        class _FifoCache(object):
+            def __init__(self, size):
+                self.not_in_cache = not_in_cache = object()
+
+                cache = _OrderedDict()
+
+                def get(self, key):
+                    return cache.get(key, not_in_cache)
+
+                def set(self, key, value):
+                    cache[key] = value
+                    while len(cache) > size:
+                        try:
+                            cache.popitem(False)
+                        except KeyError:
+                            pass
+
+                def clear(self):
+                    cache.clear()
+
+                def cache_len(self):
+                    return len(cache)
+
+                self.get = types.MethodType(get, self)
+                self.set = types.MethodType(set, self)
+                self.clear = types.MethodType(clear, self)
+                self.__len__ = types.MethodType(cache_len, self)
+
+    else:
+        class _FifoCache(object):
+            def __init__(self, size):
+                self.not_in_cache = not_in_cache = object()
+
+                cache = {}
+                key_fifo = collections.deque([], size)
+
+                def get(self, key):
+                    return cache.get(key, not_in_cache)
+
+                def set(self, key, value):
+                    cache[key] = value
+                    while len(key_fifo) > size:
+                        cache.pop(key_fifo.popleft(), None)
+                    key_fifo.append(key)
+
+                def clear(self):
+                    cache.clear()
+                    key_fifo.clear()
+
+                def cache_len(self):
+                    return len(cache)
+
+                self.get = types.MethodType(get, self)
+                self.set = types.MethodType(set, self)
+                self.clear = types.MethodType(clear, self)
+                self.__len__ = types.MethodType(cache_len, self)
+
+    # argument cache for optimizing repeated calls when backtracking through recursive expressions
+    packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail
+    packrat_cache_lock = RLock()
+    packrat_cache_stats = [0, 0]
+
+    # this method gets repeatedly called during backtracking with the same arguments -
+    # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression
+    def _parseCache( self, instring, loc, doActions=True, callPreParse=True ):
+        HIT, MISS = 0, 1
+        lookup = (self, instring, loc, callPreParse, doActions)
+        with ParserElement.packrat_cache_lock:
+            cache = ParserElement.packrat_cache
+            value = cache.get(lookup)
+            if value is cache.not_in_cache:
+                ParserElement.packrat_cache_stats[MISS] += 1
+                try:
+                    value = self._parseNoCache(instring, loc, doActions, callPreParse)
+                except ParseBaseException as pe:
+                    # cache a copy of the exception, without the traceback
+                    cache.set(lookup, pe.__class__(*pe.args))
+                    raise
+                else:
+                    cache.set(lookup, (value[0], value[1].copy()))
+                    return value
+            else:
+                ParserElement.packrat_cache_stats[HIT] += 1
+                if isinstance(value, Exception):
+                    raise value
+                return (value[0], value[1].copy())
+
+    _parse = _parseNoCache
+
+    @staticmethod
+    def resetCache():
+        ParserElement.packrat_cache.clear()
+        ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats)
+
+    _packratEnabled = False
+    @staticmethod
+    def enablePackrat(cache_size_limit=128):
+        """Enables "packrat" parsing, which adds memoizing to the parsing logic.
+           Repeated parse attempts at the same string location (which happens
+           often in many complex grammars) can immediately return a cached value,
+           instead of re-executing parsing/validating code.  Memoizing is done of
+           both valid results and parsing exceptions.
+           
+           Parameters:
+            - cache_size_limit - (default=C{128}) - if an integer value is provided
+              will limit the size of the packrat cache; if None is passed, then
+              the cache size will be unbounded; if 0 is passed, the cache will
+              be effectively disabled.
+            
+           This speedup may break existing programs that use parse actions that
+           have side-effects.  For this reason, packrat parsing is disabled when
+           you first import pyparsing.  To activate the packrat feature, your
+           program must call the class method C{ParserElement.enablePackrat()}.  If
+           your program uses C{psyco} to "compile as you go", you must call
+           C{enablePackrat} before calling C{psyco.full()}.  If you do not do this,
+           Python will crash.  For best results, call C{enablePackrat()} immediately
+           after importing pyparsing.
+           
+           Example::
+               import pyparsing
+               pyparsing.ParserElement.enablePackrat()
+        """
+        if not ParserElement._packratEnabled:
+            ParserElement._packratEnabled = True
+            if cache_size_limit is None:
+                ParserElement.packrat_cache = ParserElement._UnboundedCache()
+            else:
+                ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit)
+            ParserElement._parse = ParserElement._parseCache
+
+    def parseString( self, instring, parseAll=False ):
+        """
+        Execute the parse expression with the given string.
+        This is the main interface to the client code, once the complete
+        expression has been built.
+
+        If you want the grammar to require that the entire input string be
+        successfully parsed, then set C{parseAll} to True (equivalent to ending
+        the grammar with C{L{StringEnd()}}).
+
+        Note: C{parseString} implicitly calls C{expandtabs()} on the input string,
+        in order to report proper column numbers in parse actions.
+        If the input string contains tabs and
+        the grammar uses parse actions that use the C{loc} argument to index into the
+        string being parsed, you can ensure you have a consistent view of the input
+        string by:
+         - calling C{parseWithTabs} on your grammar before calling C{parseString}
+           (see L{I{parseWithTabs}})
+         - define your parse action using the full C{(s,loc,toks)} signature, and
+           reference the input string using the parse action's C{s} argument
+         - explictly expand the tabs in your input string before calling
+           C{parseString}
+        
+        Example::
+            Word('a').parseString('aaaaabaaa')  # -> ['aaaaa']
+            Word('a').parseString('aaaaabaaa', parseAll=True)  # -> Exception: Expected end of text
+        """
+        ParserElement.resetCache()
+        if not self.streamlined:
+            self.streamline()
+            #~ self.saveAsList = True
+        for e in self.ignoreExprs:
+            e.streamline()
+        if not self.keepTabs:
+            instring = instring.expandtabs()
+        try:
+            loc, tokens = self._parse( instring, 0 )
+            if parseAll:
+                loc = self.preParse( instring, loc )
+                se = Empty() + StringEnd()
+                se._parse( instring, loc )
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+        else:
+            return tokens
+
+    def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ):
+        """
+        Scan the input string for expression matches.  Each match will return the
+        matching tokens, start location, and end location.  May be called with optional
+        C{maxMatches} argument, to clip scanning after 'n' matches are found.  If
+        C{overlap} is specified, then overlapping matches will be reported.
+
+        Note that the start and end locations are reported relative to the string
+        being parsed.  See L{I{parseString}} for more information on parsing
+        strings with embedded tabs.
+
+        Example::
+            source = "sldjf123lsdjjkf345sldkjf879lkjsfd987"
+            print(source)
+            for tokens,start,end in Word(alphas).scanString(source):
+                print(' '*start + '^'*(end-start))
+                print(' '*start + tokens[0])
+        
+        prints::
+        
+            sldjf123lsdjjkf345sldkjf879lkjsfd987
+            ^^^^^
+            sldjf
+                    ^^^^^^^
+                    lsdjjkf
+                              ^^^^^^
+                              sldkjf
+                                       ^^^^^^
+                                       lkjsfd
+        """
+        if not self.streamlined:
+            self.streamline()
+        for e in self.ignoreExprs:
+            e.streamline()
+
+        if not self.keepTabs:
+            instring = _ustr(instring).expandtabs()
+        instrlen = len(instring)
+        loc = 0
+        preparseFn = self.preParse
+        parseFn = self._parse
+        ParserElement.resetCache()
+        matches = 0
+        try:
+            while loc <= instrlen and matches < maxMatches:
+                try:
+                    preloc = preparseFn( instring, loc )
+                    nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
+                except ParseException:
+                    loc = preloc+1
+                else:
+                    if nextLoc > loc:
+                        matches += 1
+                        yield tokens, preloc, nextLoc
+                        if overlap:
+                            nextloc = preparseFn( instring, loc )
+                            if nextloc > loc:
+                                loc = nextLoc
+                            else:
+                                loc += 1
+                        else:
+                            loc = nextLoc
+                    else:
+                        loc = preloc+1
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def transformString( self, instring ):
+        """
+        Extension to C{L{scanString}}, to modify matching text with modified tokens that may
+        be returned from a parse action.  To use C{transformString}, define a grammar and
+        attach a parse action to it that modifies the returned token list.
+        Invoking C{transformString()} on a target string will then scan for matches,
+        and replace the matched text patterns according to the logic in the parse
+        action.  C{transformString()} returns the resulting transformed string.
+        
+        Example::
+            wd = Word(alphas)
+            wd.setParseAction(lambda toks: toks[0].title())
+            
+            print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york."))
+        Prints::
+            Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York.
+        """
+        out = []
+        lastE = 0
+        # force preservation of s, to minimize unwanted transformation of string, and to
+        # keep string locs straight between transformString and scanString
+        self.keepTabs = True
+        try:
+            for t,s,e in self.scanString( instring ):
+                out.append( instring[lastE:s] )
+                if t:
+                    if isinstance(t,ParseResults):
+                        out += t.asList()
+                    elif isinstance(t,list):
+                        out += t
+                    else:
+                        out.append(t)
+                lastE = e
+            out.append(instring[lastE:])
+            out = [o for o in out if o]
+            return "".join(map(_ustr,_flatten(out)))
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def searchString( self, instring, maxMatches=_MAX_INT ):
+        """
+        Another extension to C{L{scanString}}, simplifying the access to the tokens found
+        to match the given parse expression.  May be called with optional
+        C{maxMatches} argument, to clip searching after 'n' matches are found.
+        
+        Example::
+            # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters
+            cap_word = Word(alphas.upper(), alphas.lower())
+            
+            print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))
+
+            # the sum() builtin can be used to merge results into a single ParseResults object
+            print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")))
+        prints::
+            [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']]
+            ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity']
+        """
+        try:
+            return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ])
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False):
+        """
+        Generator method to split a string using the given expression as a separator.
+        May be called with optional C{maxsplit} argument, to limit the number of splits;
+        and the optional C{includeSeparators} argument (default=C{False}), if the separating
+        matching text should be included in the split results.
+        
+        Example::        
+            punc = oneOf(list(".,;:/-!?"))
+            print(list(punc.split("This, this?, this sentence, is badly punctuated!")))
+        prints::
+            ['This', ' this', '', ' this sentence', ' is badly punctuated', '']
+        """
+        splits = 0
+        last = 0
+        for t,s,e in self.scanString(instring, maxMatches=maxsplit):
+            yield instring[last:s]
+            if includeSeparators:
+                yield t[0]
+            last = e
+        yield instring[last:]
+
+    def __add__(self, other ):
+        """
+        Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement
+        converts them to L{Literal}s by default.
+        
+        Example::
+            greet = Word(alphas) + "," + Word(alphas) + "!"
+            hello = "Hello, World!"
+            print (hello, "->", greet.parseString(hello))
+        Prints::
+            Hello, World! -> ['Hello', ',', 'World', '!']
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return And( [ self, other ] )
+
+    def __radd__(self, other ):
+        """
+        Implementation of + operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other + self
+
+    def __sub__(self, other):
+        """
+        Implementation of - operator, returns C{L{And}} with error stop
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return self + And._ErrorStop() + other
+
+    def __rsub__(self, other ):
+        """
+        Implementation of - operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other - self
+
+    def __mul__(self,other):
+        """
+        Implementation of * operator, allows use of C{expr * 3} in place of
+        C{expr + expr + expr}.  Expressions may also me multiplied by a 2-integer
+        tuple, similar to C{{min,max}} multipliers in regular expressions.  Tuples
+        may also include C{None} as in:
+         - C{expr*(n,None)} or C{expr*(n,)} is equivalent
+              to C{expr*n + L{ZeroOrMore}(expr)}
+              (read as "at least n instances of C{expr}")
+         - C{expr*(None,n)} is equivalent to C{expr*(0,n)}
+              (read as "0 to n instances of C{expr}")
+         - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)}
+         - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)}
+
+        Note that C{expr*(None,n)} does not raise an exception if
+        more than n exprs exist in the input stream; that is,
+        C{expr*(None,n)} does not enforce a maximum number of expr
+        occurrences.  If this behavior is desired, then write
+        C{expr*(None,n) + ~expr}
+        """
+        if isinstance(other,int):
+            minElements, optElements = other,0
+        elif isinstance(other,tuple):
+            other = (other + (None, None))[:2]
+            if other[0] is None:
+                other = (0, other[1])
+            if isinstance(other[0],int) and other[1] is None:
+                if other[0] == 0:
+                    return ZeroOrMore(self)
+                if other[0] == 1:
+                    return OneOrMore(self)
+                else:
+                    return self*other[0] + ZeroOrMore(self)
+            elif isinstance(other[0],int) and isinstance(other[1],int):
+                minElements, optElements = other
+                optElements -= minElements
+            else:
+                raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1]))
+        else:
+            raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other))
+
+        if minElements < 0:
+            raise ValueError("cannot multiply ParserElement by negative value")
+        if optElements < 0:
+            raise ValueError("second tuple value must be greater or equal to first tuple value")
+        if minElements == optElements == 0:
+            raise ValueError("cannot multiply ParserElement by 0 or (0,0)")
+
+        if (optElements):
+            def makeOptionalList(n):
+                if n>1:
+                    return Optional(self + makeOptionalList(n-1))
+                else:
+                    return Optional(self)
+            if minElements:
+                if minElements == 1:
+                    ret = self + makeOptionalList(optElements)
+                else:
+                    ret = And([self]*minElements) + makeOptionalList(optElements)
+            else:
+                ret = makeOptionalList(optElements)
+        else:
+            if minElements == 1:
+                ret = self
+            else:
+                ret = And([self]*minElements)
+        return ret
+
+    def __rmul__(self, other):
+        return self.__mul__(other)
+
+    def __or__(self, other ):
+        """
+        Implementation of | operator - returns C{L{MatchFirst}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return MatchFirst( [ self, other ] )
+
+    def __ror__(self, other ):
+        """
+        Implementation of | operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other | self
+
+    def __xor__(self, other ):
+        """
+        Implementation of ^ operator - returns C{L{Or}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return Or( [ self, other ] )
+
+    def __rxor__(self, other ):
+        """
+        Implementation of ^ operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other ^ self
+
+    def __and__(self, other ):
+        """
+        Implementation of & operator - returns C{L{Each}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return Each( [ self, other ] )
+
+    def __rand__(self, other ):
+        """
+        Implementation of & operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other & self
+
+    def __invert__( self ):
+        """
+        Implementation of ~ operator - returns C{L{NotAny}}
+        """
+        return NotAny( self )
+
+    def __call__(self, name=None):
+        """
+        Shortcut for C{L{setResultsName}}, with C{listAllMatches=False}.
+        
+        If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be
+        passed as C{True}.
+           
+        If C{name} is omitted, same as calling C{L{copy}}.
+
+        Example::
+            # these are equivalent
+            userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno")
+            userdata = Word(alphas)("name") + Word(nums+"-")("socsecno")             
+        """
+        if name is not None:
+            return self.setResultsName(name)
+        else:
+            return self.copy()
+
+    def suppress( self ):
+        """
+        Suppresses the output of this C{ParserElement}; useful to keep punctuation from
+        cluttering up returned output.
+        """
+        return Suppress( self )
+
+    def leaveWhitespace( self ):
+        """
+        Disables the skipping of whitespace before matching the characters in the
+        C{ParserElement}'s defined pattern.  This is normally only used internally by
+        the pyparsing module, but may be needed in some whitespace-sensitive grammars.
+        """
+        self.skipWhitespace = False
+        return self
+
+    def setWhitespaceChars( self, chars ):
+        """
+        Overrides the default whitespace chars
+        """
+        self.skipWhitespace = True
+        self.whiteChars = chars
+        self.copyDefaultWhiteChars = False
+        return self
+
+    def parseWithTabs( self ):
+        """
+        Overrides default behavior to expand C{}s to spaces before parsing the input string.
+        Must be called before C{parseString} when the input grammar contains elements that
+        match C{} characters.
+        """
+        self.keepTabs = True
+        return self
+
+    def ignore( self, other ):
+        """
+        Define expression to be ignored (e.g., comments) while doing pattern
+        matching; may be called repeatedly, to define multiple comment or other
+        ignorable patterns.
+        
+        Example::
+            patt = OneOrMore(Word(alphas))
+            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj']
+            
+            patt.ignore(cStyleComment)
+            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd']
+        """
+        if isinstance(other, basestring):
+            other = Suppress(other)
+
+        if isinstance( other, Suppress ):
+            if other not in self.ignoreExprs:
+                self.ignoreExprs.append(other)
+        else:
+            self.ignoreExprs.append( Suppress( other.copy() ) )
+        return self
+
+    def setDebugActions( self, startAction, successAction, exceptionAction ):
+        """
+        Enable display of debugging messages while doing pattern matching.
+        """
+        self.debugActions = (startAction or _defaultStartDebugAction,
+                             successAction or _defaultSuccessDebugAction,
+                             exceptionAction or _defaultExceptionDebugAction)
+        self.debug = True
+        return self
+
+    def setDebug( self, flag=True ):
+        """
+        Enable display of debugging messages while doing pattern matching.
+        Set C{flag} to True to enable, False to disable.
+
+        Example::
+            wd = Word(alphas).setName("alphaword")
+            integer = Word(nums).setName("numword")
+            term = wd | integer
+            
+            # turn on debugging for wd
+            wd.setDebug()
+
+            OneOrMore(term).parseString("abc 123 xyz 890")
+        
+        prints::
+            Match alphaword at loc 0(1,1)
+            Matched alphaword -> ['abc']
+            Match alphaword at loc 3(1,4)
+            Exception raised:Expected alphaword (at char 4), (line:1, col:5)
+            Match alphaword at loc 7(1,8)
+            Matched alphaword -> ['xyz']
+            Match alphaword at loc 11(1,12)
+            Exception raised:Expected alphaword (at char 12), (line:1, col:13)
+            Match alphaword at loc 15(1,16)
+            Exception raised:Expected alphaword (at char 15), (line:1, col:16)
+
+        The output shown is that produced by the default debug actions - custom debug actions can be
+        specified using L{setDebugActions}. Prior to attempting
+        to match the C{wd} expression, the debugging message C{"Match  at loc (,)"}
+        is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"}
+        message is shown. Also note the use of L{setName} to assign a human-readable name to the expression,
+        which makes debugging and exception messages easier to understand - for instance, the default
+        name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}.
+        """
+        if flag:
+            self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction )
+        else:
+            self.debug = False
+        return self
+
+    def __str__( self ):
+        return self.name
+
+    def __repr__( self ):
+        return _ustr(self)
+
+    def streamline( self ):
+        self.streamlined = True
+        self.strRepr = None
+        return self
+
+    def checkRecursion( self, parseElementList ):
+        pass
+
+    def validate( self, validateTrace=[] ):
+        """
+        Check defined expressions for valid structure, check for infinite recursive definitions.
+        """
+        self.checkRecursion( [] )
+
+    def parseFile( self, file_or_filename, parseAll=False ):
+        """
+        Execute the parse expression on the given file or filename.
+        If a filename is specified (instead of a file object),
+        the entire file is opened, read, and closed before parsing.
+        """
+        try:
+            file_contents = file_or_filename.read()
+        except AttributeError:
+            with open(file_or_filename, "r") as f:
+                file_contents = f.read()
+        try:
+            return self.parseString(file_contents, parseAll)
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def __eq__(self,other):
+        if isinstance(other, ParserElement):
+            return self is other or vars(self) == vars(other)
+        elif isinstance(other, basestring):
+            return self.matches(other)
+        else:
+            return super(ParserElement,self)==other
+
+    def __ne__(self,other):
+        return not (self == other)
+
+    def __hash__(self):
+        return hash(id(self))
+
+    def __req__(self,other):
+        return self == other
+
+    def __rne__(self,other):
+        return not (self == other)
+
+    def matches(self, testString, parseAll=True):
+        """
+        Method for quick testing of a parser against a test string. Good for simple 
+        inline microtests of sub expressions while building up larger parser.
+           
+        Parameters:
+         - testString - to test against this expression for a match
+         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests
+            
+        Example::
+            expr = Word(nums)
+            assert expr.matches("100")
+        """
+        try:
+            self.parseString(_ustr(testString), parseAll=parseAll)
+            return True
+        except ParseBaseException:
+            return False
+                
+    def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False):
+        """
+        Execute the parse expression on a series of test strings, showing each
+        test, the parsed results or where the parse failed. Quick and easy way to
+        run a parse expression against a list of sample strings.
+           
+        Parameters:
+         - tests - a list of separate test strings, or a multiline string of test strings
+         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests           
+         - comment - (default=C{'#'}) - expression for indicating embedded comments in the test 
+              string; pass None to disable comment filtering
+         - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline;
+              if False, only dump nested list
+         - printResults - (default=C{True}) prints test output to stdout
+         - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing
+
+        Returns: a (success, results) tuple, where success indicates that all tests succeeded
+        (or failed if C{failureTests} is True), and the results contain a list of lines of each 
+        test's output
+        
+        Example::
+            number_expr = pyparsing_common.number.copy()
+
+            result = number_expr.runTests('''
+                # unsigned integer
+                100
+                # negative integer
+                -100
+                # float with scientific notation
+                6.02e23
+                # integer with scientific notation
+                1e-12
+                ''')
+            print("Success" if result[0] else "Failed!")
+
+            result = number_expr.runTests('''
+                # stray character
+                100Z
+                # missing leading digit before '.'
+                -.100
+                # too many '.'
+                3.14.159
+                ''', failureTests=True)
+            print("Success" if result[0] else "Failed!")
+        prints::
+            # unsigned integer
+            100
+            [100]
+
+            # negative integer
+            -100
+            [-100]
+
+            # float with scientific notation
+            6.02e23
+            [6.02e+23]
+
+            # integer with scientific notation
+            1e-12
+            [1e-12]
+
+            Success
+            
+            # stray character
+            100Z
+               ^
+            FAIL: Expected end of text (at char 3), (line:1, col:4)
+
+            # missing leading digit before '.'
+            -.100
+            ^
+            FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1)
+
+            # too many '.'
+            3.14.159
+                ^
+            FAIL: Expected end of text (at char 4), (line:1, col:5)
+
+            Success
+
+        Each test string must be on a single line. If you want to test a string that spans multiple
+        lines, create a test like this::
+
+            expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines")
+        
+        (Note that this is a raw string literal, you must include the leading 'r'.)
+        """
+        if isinstance(tests, basestring):
+            tests = list(map(str.strip, tests.rstrip().splitlines()))
+        if isinstance(comment, basestring):
+            comment = Literal(comment)
+        allResults = []
+        comments = []
+        success = True
+        for t in tests:
+            if comment is not None and comment.matches(t, False) or comments and not t:
+                comments.append(t)
+                continue
+            if not t:
+                continue
+            out = ['\n'.join(comments), t]
+            comments = []
+            try:
+                t = t.replace(r'\n','\n')
+                result = self.parseString(t, parseAll=parseAll)
+                out.append(result.dump(full=fullDump))
+                success = success and not failureTests
+            except ParseBaseException as pe:
+                fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else ""
+                if '\n' in t:
+                    out.append(line(pe.loc, t))
+                    out.append(' '*(col(pe.loc,t)-1) + '^' + fatal)
+                else:
+                    out.append(' '*pe.loc + '^' + fatal)
+                out.append("FAIL: " + str(pe))
+                success = success and failureTests
+                result = pe
+            except Exception as exc:
+                out.append("FAIL-EXCEPTION: " + str(exc))
+                success = success and failureTests
+                result = exc
+
+            if printResults:
+                if fullDump:
+                    out.append('')
+                print('\n'.join(out))
+
+            allResults.append((t, result))
+        
+        return success, allResults
+
+        
+class Token(ParserElement):
+    """
+    Abstract C{ParserElement} subclass, for defining atomic matching patterns.
+    """
+    def __init__( self ):
+        super(Token,self).__init__( savelist=False )
+
+
+class Empty(Token):
+    """
+    An empty token, will always match.
+    """
+    def __init__( self ):
+        super(Empty,self).__init__()
+        self.name = "Empty"
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+
+
+class NoMatch(Token):
+    """
+    A token that will never match.
+    """
+    def __init__( self ):
+        super(NoMatch,self).__init__()
+        self.name = "NoMatch"
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+        self.errmsg = "Unmatchable token"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        raise ParseException(instring, loc, self.errmsg, self)
+
+
+class Literal(Token):
+    """
+    Token to exactly match a specified string.
+    
+    Example::
+        Literal('blah').parseString('blah')  # -> ['blah']
+        Literal('blah').parseString('blahfooblah')  # -> ['blah']
+        Literal('blah').parseString('bla')  # -> Exception: Expected "blah"
+    
+    For case-insensitive matching, use L{CaselessLiteral}.
+    
+    For keyword matching (force word break before and after the matched string),
+    use L{Keyword} or L{CaselessKeyword}.
+    """
+    def __init__( self, matchString ):
+        super(Literal,self).__init__()
+        self.match = matchString
+        self.matchLen = len(matchString)
+        try:
+            self.firstMatchChar = matchString[0]
+        except IndexError:
+            warnings.warn("null string passed to Literal; use Empty() instead",
+                            SyntaxWarning, stacklevel=2)
+            self.__class__ = Empty
+        self.name = '"%s"' % _ustr(self.match)
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = False
+        self.mayIndexError = False
+
+    # Performance tuning: this routine gets called a *lot*
+    # if this is a single character match string  and the first character matches,
+    # short-circuit as quickly as possible, and avoid calling startswith
+    #~ @profile
+    def parseImpl( self, instring, loc, doActions=True ):
+        if (instring[loc] == self.firstMatchChar and
+            (self.matchLen==1 or instring.startswith(self.match,loc)) ):
+            return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+_L = Literal
+ParserElement._literalStringClass = Literal
+
+class Keyword(Token):
+    """
+    Token to exactly match a specified string as a keyword, that is, it must be
+    immediately followed by a non-keyword character.  Compare with C{L{Literal}}:
+     - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}.
+     - C{Keyword("if")} will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'}
+    Accepts two optional constructor arguments in addition to the keyword string:
+     - C{identChars} is a string of characters that would be valid identifier characters,
+          defaulting to all alphanumerics + "_" and "$"
+     - C{caseless} allows case-insensitive matching, default is C{False}.
+       
+    Example::
+        Keyword("start").parseString("start")  # -> ['start']
+        Keyword("start").parseString("starting")  # -> Exception
+
+    For case-insensitive matching, use L{CaselessKeyword}.
+    """
+    DEFAULT_KEYWORD_CHARS = alphanums+"_$"
+
+    def __init__( self, matchString, identChars=None, caseless=False ):
+        super(Keyword,self).__init__()
+        if identChars is None:
+            identChars = Keyword.DEFAULT_KEYWORD_CHARS
+        self.match = matchString
+        self.matchLen = len(matchString)
+        try:
+            self.firstMatchChar = matchString[0]
+        except IndexError:
+            warnings.warn("null string passed to Keyword; use Empty() instead",
+                            SyntaxWarning, stacklevel=2)
+        self.name = '"%s"' % self.match
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = False
+        self.mayIndexError = False
+        self.caseless = caseless
+        if caseless:
+            self.caselessmatch = matchString.upper()
+            identChars = identChars.upper()
+        self.identChars = set(identChars)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.caseless:
+            if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
+                 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and
+                 (loc == 0 or instring[loc-1].upper() not in self.identChars) ):
+                return loc+self.matchLen, self.match
+        else:
+            if (instring[loc] == self.firstMatchChar and
+                (self.matchLen==1 or instring.startswith(self.match,loc)) and
+                (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and
+                (loc == 0 or instring[loc-1] not in self.identChars) ):
+                return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+
+    def copy(self):
+        c = super(Keyword,self).copy()
+        c.identChars = Keyword.DEFAULT_KEYWORD_CHARS
+        return c
+
+    @staticmethod
+    def setDefaultKeywordChars( chars ):
+        """Overrides the default Keyword chars
+        """
+        Keyword.DEFAULT_KEYWORD_CHARS = chars
+
+class CaselessLiteral(Literal):
+    """
+    Token to match a specified string, ignoring case of letters.
+    Note: the matched results will always be in the case of the given
+    match string, NOT the case of the input text.
+
+    Example::
+        OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD']
+        
+    (Contrast with example for L{CaselessKeyword}.)
+    """
+    def __init__( self, matchString ):
+        super(CaselessLiteral,self).__init__( matchString.upper() )
+        # Preserve the defining literal.
+        self.returnString = matchString
+        self.name = "'%s'" % self.returnString
+        self.errmsg = "Expected " + self.name
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if instring[ loc:loc+self.matchLen ].upper() == self.match:
+            return loc+self.matchLen, self.returnString
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class CaselessKeyword(Keyword):
+    """
+    Caseless version of L{Keyword}.
+
+    Example::
+        OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD']
+        
+    (Contrast with example for L{CaselessLiteral}.)
+    """
+    def __init__( self, matchString, identChars=None ):
+        super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True )
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
+             (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ):
+            return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class CloseMatch(Token):
+    """
+    A variation on L{Literal} which matches "close" matches, that is, 
+    strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters:
+     - C{match_string} - string to be matched
+     - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match
+    
+    The results from a successful parse will contain the matched text from the input string and the following named results:
+     - C{mismatches} - a list of the positions within the match_string where mismatches were found
+     - C{original} - the original match_string used to compare against the input string
+    
+    If C{mismatches} is an empty list, then the match was an exact match.
+    
+    Example::
+        patt = CloseMatch("ATCATCGAATGGA")
+        patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']})
+        patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1)
+
+        # exact match
+        patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']})
+
+        # close match allowing up to 2 mismatches
+        patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2)
+        patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']})
+    """
+    def __init__(self, match_string, maxMismatches=1):
+        super(CloseMatch,self).__init__()
+        self.name = match_string
+        self.match_string = match_string
+        self.maxMismatches = maxMismatches
+        self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches)
+        self.mayIndexError = False
+        self.mayReturnEmpty = False
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        start = loc
+        instrlen = len(instring)
+        maxloc = start + len(self.match_string)
+
+        if maxloc <= instrlen:
+            match_string = self.match_string
+            match_stringloc = 0
+            mismatches = []
+            maxMismatches = self.maxMismatches
+
+            for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)):
+                src,mat = s_m
+                if src != mat:
+                    mismatches.append(match_stringloc)
+                    if len(mismatches) > maxMismatches:
+                        break
+            else:
+                loc = match_stringloc + 1
+                results = ParseResults([instring[start:loc]])
+                results['original'] = self.match_string
+                results['mismatches'] = mismatches
+                return loc, results
+
+        raise ParseException(instring, loc, self.errmsg, self)
+
+
+class Word(Token):
+    """
+    Token for matching words composed of allowed character sets.
+    Defined with string containing all allowed initial characters,
+    an optional string containing allowed body characters (if omitted,
+    defaults to the initial character set), and an optional minimum,
+    maximum, and/or exact length.  The default value for C{min} is 1 (a
+    minimum value < 1 is not valid); the default values for C{max} and C{exact}
+    are 0, meaning no maximum or exact length restriction. An optional
+    C{excludeChars} parameter can list characters that might be found in 
+    the input C{bodyChars} string; useful to define a word of all printables
+    except for one or two characters, for instance.
+    
+    L{srange} is useful for defining custom character set strings for defining 
+    C{Word} expressions, using range notation from regular expression character sets.
+    
+    A common mistake is to use C{Word} to match a specific literal string, as in 
+    C{Word("Address")}. Remember that C{Word} uses the string argument to define
+    I{sets} of matchable characters. This expression would match "Add", "AAA",
+    "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'.
+    To match an exact literal string, use L{Literal} or L{Keyword}.
+
+    pyparsing includes helper strings for building Words:
+     - L{alphas}
+     - L{nums}
+     - L{alphanums}
+     - L{hexnums}
+     - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.)
+     - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.)
+     - L{printables} (any non-whitespace character)
+
+    Example::
+        # a word composed of digits
+        integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9"))
+        
+        # a word with a leading capital, and zero or more lowercase
+        capital_word = Word(alphas.upper(), alphas.lower())
+
+        # hostnames are alphanumeric, with leading alpha, and '-'
+        hostname = Word(alphas, alphanums+'-')
+        
+        # roman numeral (not a strict parser, accepts invalid mix of characters)
+        roman = Word("IVXLCDM")
+        
+        # any string of non-whitespace characters, except for ','
+        csv_value = Word(printables, excludeChars=",")
+    """
+    def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ):
+        super(Word,self).__init__()
+        if excludeChars:
+            initChars = ''.join(c for c in initChars if c not in excludeChars)
+            if bodyChars:
+                bodyChars = ''.join(c for c in bodyChars if c not in excludeChars)
+        self.initCharsOrig = initChars
+        self.initChars = set(initChars)
+        if bodyChars :
+            self.bodyCharsOrig = bodyChars
+            self.bodyChars = set(bodyChars)
+        else:
+            self.bodyCharsOrig = initChars
+            self.bodyChars = set(initChars)
+
+        self.maxSpecified = max > 0
+
+        if min < 1:
+            raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted")
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.asKeyword = asKeyword
+
+        if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0):
+            if self.bodyCharsOrig == self.initCharsOrig:
+                self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig)
+            elif len(self.initCharsOrig) == 1:
+                self.reString = "%s[%s]*" % \
+                                      (re.escape(self.initCharsOrig),
+                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
+            else:
+                self.reString = "[%s][%s]*" % \
+                                      (_escapeRegexRangeChars(self.initCharsOrig),
+                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
+            if self.asKeyword:
+                self.reString = r"\b"+self.reString+r"\b"
+            try:
+                self.re = re.compile( self.reString )
+            except Exception:
+                self.re = None
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.re:
+            result = self.re.match(instring,loc)
+            if not result:
+                raise ParseException(instring, loc, self.errmsg, self)
+
+            loc = result.end()
+            return loc, result.group()
+
+        if not(instring[ loc ] in self.initChars):
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        start = loc
+        loc += 1
+        instrlen = len(instring)
+        bodychars = self.bodyChars
+        maxloc = start + self.maxLen
+        maxloc = min( maxloc, instrlen )
+        while loc < maxloc and instring[loc] in bodychars:
+            loc += 1
+
+        throwException = False
+        if loc - start < self.minLen:
+            throwException = True
+        if self.maxSpecified and loc < instrlen and instring[loc] in bodychars:
+            throwException = True
+        if self.asKeyword:
+            if (start>0 and instring[start-1] in bodychars) or (loc4:
+                    return s[:4]+"..."
+                else:
+                    return s
+
+            if ( self.initCharsOrig != self.bodyCharsOrig ):
+                self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) )
+            else:
+                self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig)
+
+        return self.strRepr
+
+
+class Regex(Token):
+    r"""
+    Token for matching strings that match a given regular expression.
+    Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module.
+    If the given regex contains named groups (defined using C{(?P...)}), these will be preserved as 
+    named parse results.
+
+    Example::
+        realnum = Regex(r"[+-]?\d+\.\d*")
+        date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\d\d?)')
+        # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression
+        roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})")
+    """
+    compiledREtype = type(re.compile("[A-Z]"))
+    def __init__( self, pattern, flags=0):
+        """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags."""
+        super(Regex,self).__init__()
+
+        if isinstance(pattern, basestring):
+            if not pattern:
+                warnings.warn("null string passed to Regex; use Empty() instead",
+                        SyntaxWarning, stacklevel=2)
+
+            self.pattern = pattern
+            self.flags = flags
+
+            try:
+                self.re = re.compile(self.pattern, self.flags)
+                self.reString = self.pattern
+            except sre_constants.error:
+                warnings.warn("invalid pattern (%s) passed to Regex" % pattern,
+                    SyntaxWarning, stacklevel=2)
+                raise
+
+        elif isinstance(pattern, Regex.compiledREtype):
+            self.re = pattern
+            self.pattern = \
+            self.reString = str(pattern)
+            self.flags = flags
+            
+        else:
+            raise ValueError("Regex may only be constructed with a string or a compiled RE object")
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        result = self.re.match(instring,loc)
+        if not result:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        loc = result.end()
+        d = result.groupdict()
+        ret = ParseResults(result.group())
+        if d:
+            for k in d:
+                ret[k] = d[k]
+        return loc,ret
+
+    def __str__( self ):
+        try:
+            return super(Regex,self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None:
+            self.strRepr = "Re:(%s)" % repr(self.pattern)
+
+        return self.strRepr
+
+
+class QuotedString(Token):
+    r"""
+    Token for matching strings that are delimited by quoting characters.
+    
+    Defined with the following parameters:
+        - quoteChar - string of one or more characters defining the quote delimiting string
+        - escChar - character to escape quotes, typically backslash (default=C{None})
+        - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{None})
+        - multiline - boolean indicating whether quotes can span multiple lines (default=C{False})
+        - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True})
+        - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar)
+        - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True})
+
+    Example::
+        qs = QuotedString('"')
+        print(qs.searchString('lsjdf "This is the quote" sldjf'))
+        complex_qs = QuotedString('{{', endQuoteChar='}}')
+        print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf'))
+        sql_qs = QuotedString('"', escQuote='""')
+        print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf'))
+    prints::
+        [['This is the quote']]
+        [['This is the "quote"']]
+        [['This is the quote with "embedded" quotes']]
+    """
+    def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True):
+        super(QuotedString,self).__init__()
+
+        # remove white space from quote chars - wont work anyway
+        quoteChar = quoteChar.strip()
+        if not quoteChar:
+            warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
+            raise SyntaxError()
+
+        if endQuoteChar is None:
+            endQuoteChar = quoteChar
+        else:
+            endQuoteChar = endQuoteChar.strip()
+            if not endQuoteChar:
+                warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
+                raise SyntaxError()
+
+        self.quoteChar = quoteChar
+        self.quoteCharLen = len(quoteChar)
+        self.firstQuoteChar = quoteChar[0]
+        self.endQuoteChar = endQuoteChar
+        self.endQuoteCharLen = len(endQuoteChar)
+        self.escChar = escChar
+        self.escQuote = escQuote
+        self.unquoteResults = unquoteResults
+        self.convertWhitespaceEscapes = convertWhitespaceEscapes
+
+        if multiline:
+            self.flags = re.MULTILINE | re.DOTALL
+            self.pattern = r'%s(?:[^%s%s]' % \
+                ( re.escape(self.quoteChar),
+                  _escapeRegexRangeChars(self.endQuoteChar[0]),
+                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
+        else:
+            self.flags = 0
+            self.pattern = r'%s(?:[^%s\n\r%s]' % \
+                ( re.escape(self.quoteChar),
+                  _escapeRegexRangeChars(self.endQuoteChar[0]),
+                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
+        if len(self.endQuoteChar) > 1:
+            self.pattern += (
+                '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]),
+                                               _escapeRegexRangeChars(self.endQuoteChar[i]))
+                                    for i in range(len(self.endQuoteChar)-1,0,-1)) + ')'
+                )
+        if escQuote:
+            self.pattern += (r'|(?:%s)' % re.escape(escQuote))
+        if escChar:
+            self.pattern += (r'|(?:%s.)' % re.escape(escChar))
+            self.escCharReplacePattern = re.escape(self.escChar)+"(.)"
+        self.pattern += (r')*%s' % re.escape(self.endQuoteChar))
+
+        try:
+            self.re = re.compile(self.pattern, self.flags)
+            self.reString = self.pattern
+        except sre_constants.error:
+            warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern,
+                SyntaxWarning, stacklevel=2)
+            raise
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None
+        if not result:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        loc = result.end()
+        ret = result.group()
+
+        if self.unquoteResults:
+
+            # strip off quotes
+            ret = ret[self.quoteCharLen:-self.endQuoteCharLen]
+
+            if isinstance(ret,basestring):
+                # replace escaped whitespace
+                if '\\' in ret and self.convertWhitespaceEscapes:
+                    ws_map = {
+                        r'\t' : '\t',
+                        r'\n' : '\n',
+                        r'\f' : '\f',
+                        r'\r' : '\r',
+                    }
+                    for wslit,wschar in ws_map.items():
+                        ret = ret.replace(wslit, wschar)
+
+                # replace escaped characters
+                if self.escChar:
+                    ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret)
+
+                # replace escaped quotes
+                if self.escQuote:
+                    ret = ret.replace(self.escQuote, self.endQuoteChar)
+
+        return loc, ret
+
+    def __str__( self ):
+        try:
+            return super(QuotedString,self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None:
+            self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar)
+
+        return self.strRepr
+
+
+class CharsNotIn(Token):
+    """
+    Token for matching words composed of characters I{not} in a given set (will
+    include whitespace in matched characters if not listed in the provided exclusion set - see example).
+    Defined with string containing all disallowed characters, and an optional
+    minimum, maximum, and/or exact length.  The default value for C{min} is 1 (a
+    minimum value < 1 is not valid); the default values for C{max} and C{exact}
+    are 0, meaning no maximum or exact length restriction.
+
+    Example::
+        # define a comma-separated-value as anything that is not a ','
+        csv_value = CharsNotIn(',')
+        print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213"))
+    prints::
+        ['dkls', 'lsdkjf', 's12 34', '@!#', '213']
+    """
+    def __init__( self, notChars, min=1, max=0, exact=0 ):
+        super(CharsNotIn,self).__init__()
+        self.skipWhitespace = False
+        self.notChars = notChars
+
+        if min < 1:
+            raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted")
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = ( self.minLen == 0 )
+        self.mayIndexError = False
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if instring[loc] in self.notChars:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        start = loc
+        loc += 1
+        notchars = self.notChars
+        maxlen = min( start+self.maxLen, len(instring) )
+        while loc < maxlen and \
+              (instring[loc] not in notchars):
+            loc += 1
+
+        if loc - start < self.minLen:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        return loc, instring[start:loc]
+
+    def __str__( self ):
+        try:
+            return super(CharsNotIn, self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None:
+            if len(self.notChars) > 4:
+                self.strRepr = "!W:(%s...)" % self.notChars[:4]
+            else:
+                self.strRepr = "!W:(%s)" % self.notChars
+
+        return self.strRepr
+
+class White(Token):
+    """
+    Special matching class for matching whitespace.  Normally, whitespace is ignored
+    by pyparsing grammars.  This class is included when some whitespace structures
+    are significant.  Define with a string containing the whitespace characters to be
+    matched; default is C{" \\t\\r\\n"}.  Also takes optional C{min}, C{max}, and C{exact} arguments,
+    as defined for the C{L{Word}} class.
+    """
+    whiteStrs = {
+        " " : "",
+        "\t": "",
+        "\n": "",
+        "\r": "",
+        "\f": "",
+        }
+    def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0):
+        super(White,self).__init__()
+        self.matchWhite = ws
+        self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) )
+        #~ self.leaveWhitespace()
+        self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite))
+        self.mayReturnEmpty = True
+        self.errmsg = "Expected " + self.name
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if not(instring[ loc ] in self.matchWhite):
+            raise ParseException(instring, loc, self.errmsg, self)
+        start = loc
+        loc += 1
+        maxloc = start + self.maxLen
+        maxloc = min( maxloc, len(instring) )
+        while loc < maxloc and instring[loc] in self.matchWhite:
+            loc += 1
+
+        if loc - start < self.minLen:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        return loc, instring[start:loc]
+
+
+class _PositionToken(Token):
+    def __init__( self ):
+        super(_PositionToken,self).__init__()
+        self.name=self.__class__.__name__
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+
+class GoToColumn(_PositionToken):
+    """
+    Token to advance to a specific column of input text; useful for tabular report scraping.
+    """
+    def __init__( self, colno ):
+        super(GoToColumn,self).__init__()
+        self.col = colno
+
+    def preParse( self, instring, loc ):
+        if col(loc,instring) != self.col:
+            instrlen = len(instring)
+            if self.ignoreExprs:
+                loc = self._skipIgnorables( instring, loc )
+            while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col :
+                loc += 1
+        return loc
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        thiscol = col( loc, instring )
+        if thiscol > self.col:
+            raise ParseException( instring, loc, "Text not in expected column", self )
+        newloc = loc + self.col - thiscol
+        ret = instring[ loc: newloc ]
+        return newloc, ret
+
+
+class LineStart(_PositionToken):
+    """
+    Matches if current position is at the beginning of a line within the parse string
+    
+    Example::
+    
+        test = '''\
+        AAA this line
+        AAA and this line
+          AAA but not this one
+        B AAA and definitely not this one
+        '''
+
+        for t in (LineStart() + 'AAA' + restOfLine).searchString(test):
+            print(t)
+    
+    Prints::
+        ['AAA', ' this line']
+        ['AAA', ' and this line']    
+
+    """
+    def __init__( self ):
+        super(LineStart,self).__init__()
+        self.errmsg = "Expected start of line"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if col(loc, instring) == 1:
+            return loc, []
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class LineEnd(_PositionToken):
+    """
+    Matches if current position is at the end of a line within the parse string
+    """
+    def __init__( self ):
+        super(LineEnd,self).__init__()
+        self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") )
+        self.errmsg = "Expected end of line"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if loc len(instring):
+            return loc, []
+        else:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+class WordStart(_PositionToken):
+    """
+    Matches if the current position is at the beginning of a Word, and
+    is not preceded by any character in a given set of C{wordChars}
+    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
+    use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of
+    the string being parsed, or at the beginning of a line.
+    """
+    def __init__(self, wordChars = printables):
+        super(WordStart,self).__init__()
+        self.wordChars = set(wordChars)
+        self.errmsg = "Not at the start of a word"
+
+    def parseImpl(self, instring, loc, doActions=True ):
+        if loc != 0:
+            if (instring[loc-1] in self.wordChars or
+                instring[loc] not in self.wordChars):
+                raise ParseException(instring, loc, self.errmsg, self)
+        return loc, []
+
+class WordEnd(_PositionToken):
+    """
+    Matches if the current position is at the end of a Word, and
+    is not followed by any character in a given set of C{wordChars}
+    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
+    use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of
+    the string being parsed, or at the end of a line.
+    """
+    def __init__(self, wordChars = printables):
+        super(WordEnd,self).__init__()
+        self.wordChars = set(wordChars)
+        self.skipWhitespace = False
+        self.errmsg = "Not at the end of a word"
+
+    def parseImpl(self, instring, loc, doActions=True ):
+        instrlen = len(instring)
+        if instrlen>0 and loc maxExcLoc:
+                    maxException = err
+                    maxExcLoc = err.loc
+            except IndexError:
+                if len(instring) > maxExcLoc:
+                    maxException = ParseException(instring,len(instring),e.errmsg,self)
+                    maxExcLoc = len(instring)
+            else:
+                # save match among all matches, to retry longest to shortest
+                matches.append((loc2, e))
+
+        if matches:
+            matches.sort(key=lambda x: -x[0])
+            for _,e in matches:
+                try:
+                    return e._parse( instring, loc, doActions )
+                except ParseException as err:
+                    err.__traceback__ = None
+                    if err.loc > maxExcLoc:
+                        maxException = err
+                        maxExcLoc = err.loc
+
+        if maxException is not None:
+            maxException.msg = self.errmsg
+            raise maxException
+        else:
+            raise ParseException(instring, loc, "no defined alternatives to match", self)
+
+
+    def __ixor__(self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        return self.append( other ) #Or( [ self, other ] )
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class MatchFirst(ParseExpression):
+    """
+    Requires that at least one C{ParseExpression} is found.
+    If two expressions match, the first one listed is the one that will match.
+    May be constructed using the C{'|'} operator.
+
+    Example::
+        # construct MatchFirst using '|' operator
+        
+        # watch the order of expressions to match
+        number = Word(nums) | Combine(Word(nums) + '.' + Word(nums))
+        print(number.searchString("123 3.1416 789")) #  Fail! -> [['123'], ['3'], ['1416'], ['789']]
+
+        # put more selective expression first
+        number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums)
+        print(number.searchString("123 3.1416 789")) #  Better -> [['123'], ['3.1416'], ['789']]
+    """
+    def __init__( self, exprs, savelist = False ):
+        super(MatchFirst,self).__init__(exprs, savelist)
+        if self.exprs:
+            self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
+        else:
+            self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        maxExcLoc = -1
+        maxException = None
+        for e in self.exprs:
+            try:
+                ret = e._parse( instring, loc, doActions )
+                return ret
+            except ParseException as err:
+                if err.loc > maxExcLoc:
+                    maxException = err
+                    maxExcLoc = err.loc
+            except IndexError:
+                if len(instring) > maxExcLoc:
+                    maxException = ParseException(instring,len(instring),e.errmsg,self)
+                    maxExcLoc = len(instring)
+
+        # only got here if no expression matched, raise exception for match that made it the furthest
+        else:
+            if maxException is not None:
+                maxException.msg = self.errmsg
+                raise maxException
+            else:
+                raise ParseException(instring, loc, "no defined alternatives to match", self)
+
+    def __ior__(self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        return self.append( other ) #MatchFirst( [ self, other ] )
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class Each(ParseExpression):
+    """
+    Requires all given C{ParseExpression}s to be found, but in any order.
+    Expressions may be separated by whitespace.
+    May be constructed using the C{'&'} operator.
+
+    Example::
+        color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN")
+        shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON")
+        integer = Word(nums)
+        shape_attr = "shape:" + shape_type("shape")
+        posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn")
+        color_attr = "color:" + color("color")
+        size_attr = "size:" + integer("size")
+
+        # use Each (using operator '&') to accept attributes in any order 
+        # (shape and posn are required, color and size are optional)
+        shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr)
+
+        shape_spec.runTests('''
+            shape: SQUARE color: BLACK posn: 100, 120
+            shape: CIRCLE size: 50 color: BLUE posn: 50,80
+            color:GREEN size:20 shape:TRIANGLE posn:20,40
+            '''
+            )
+    prints::
+        shape: SQUARE color: BLACK posn: 100, 120
+        ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']]
+        - color: BLACK
+        - posn: ['100', ',', '120']
+          - x: 100
+          - y: 120
+        - shape: SQUARE
+
+
+        shape: CIRCLE size: 50 color: BLUE posn: 50,80
+        ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']]
+        - color: BLUE
+        - posn: ['50', ',', '80']
+          - x: 50
+          - y: 80
+        - shape: CIRCLE
+        - size: 50
+
+
+        color: GREEN size: 20 shape: TRIANGLE posn: 20,40
+        ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']]
+        - color: GREEN
+        - posn: ['20', ',', '40']
+          - x: 20
+          - y: 40
+        - shape: TRIANGLE
+        - size: 20
+    """
+    def __init__( self, exprs, savelist = True ):
+        super(Each,self).__init__(exprs, savelist)
+        self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
+        self.skipWhitespace = True
+        self.initExprGroups = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.initExprGroups:
+            self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional))
+            opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ]
+            opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)]
+            self.optionals = opt1 + opt2
+            self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ]
+            self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ]
+            self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ]
+            self.required += self.multirequired
+            self.initExprGroups = False
+        tmpLoc = loc
+        tmpReqd = self.required[:]
+        tmpOpt  = self.optionals[:]
+        matchOrder = []
+
+        keepMatching = True
+        while keepMatching:
+            tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired
+            failed = []
+            for e in tmpExprs:
+                try:
+                    tmpLoc = e.tryParse( instring, tmpLoc )
+                except ParseException:
+                    failed.append(e)
+                else:
+                    matchOrder.append(self.opt1map.get(id(e),e))
+                    if e in tmpReqd:
+                        tmpReqd.remove(e)
+                    elif e in tmpOpt:
+                        tmpOpt.remove(e)
+            if len(failed) == len(tmpExprs):
+                keepMatching = False
+
+        if tmpReqd:
+            missing = ", ".join(_ustr(e) for e in tmpReqd)
+            raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing )
+
+        # add any unmatched Optionals, in case they have default values defined
+        matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt]
+
+        resultlist = []
+        for e in matchOrder:
+            loc,results = e._parse(instring,loc,doActions)
+            resultlist.append(results)
+
+        finalResults = sum(resultlist, ParseResults([]))
+        return loc, finalResults
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class ParseElementEnhance(ParserElement):
+    """
+    Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens.
+    """
+    def __init__( self, expr, savelist=False ):
+        super(ParseElementEnhance,self).__init__(savelist)
+        if isinstance( expr, basestring ):
+            if issubclass(ParserElement._literalStringClass, Token):
+                expr = ParserElement._literalStringClass(expr)
+            else:
+                expr = ParserElement._literalStringClass(Literal(expr))
+        self.expr = expr
+        self.strRepr = None
+        if expr is not None:
+            self.mayIndexError = expr.mayIndexError
+            self.mayReturnEmpty = expr.mayReturnEmpty
+            self.setWhitespaceChars( expr.whiteChars )
+            self.skipWhitespace = expr.skipWhitespace
+            self.saveAsList = expr.saveAsList
+            self.callPreparse = expr.callPreparse
+            self.ignoreExprs.extend(expr.ignoreExprs)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.expr is not None:
+            return self.expr._parse( instring, loc, doActions, callPreParse=False )
+        else:
+            raise ParseException("",loc,self.errmsg,self)
+
+    def leaveWhitespace( self ):
+        self.skipWhitespace = False
+        self.expr = self.expr.copy()
+        if self.expr is not None:
+            self.expr.leaveWhitespace()
+        return self
+
+    def ignore( self, other ):
+        if isinstance( other, Suppress ):
+            if other not in self.ignoreExprs:
+                super( ParseElementEnhance, self).ignore( other )
+                if self.expr is not None:
+                    self.expr.ignore( self.ignoreExprs[-1] )
+        else:
+            super( ParseElementEnhance, self).ignore( other )
+            if self.expr is not None:
+                self.expr.ignore( self.ignoreExprs[-1] )
+        return self
+
+    def streamline( self ):
+        super(ParseElementEnhance,self).streamline()
+        if self.expr is not None:
+            self.expr.streamline()
+        return self
+
+    def checkRecursion( self, parseElementList ):
+        if self in parseElementList:
+            raise RecursiveGrammarException( parseElementList+[self] )
+        subRecCheckList = parseElementList[:] + [ self ]
+        if self.expr is not None:
+            self.expr.checkRecursion( subRecCheckList )
+
+    def validate( self, validateTrace=[] ):
+        tmp = validateTrace[:]+[self]
+        if self.expr is not None:
+            self.expr.validate(tmp)
+        self.checkRecursion( [] )
+
+    def __str__( self ):
+        try:
+            return super(ParseElementEnhance,self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None and self.expr is not None:
+            self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) )
+        return self.strRepr
+
+
+class FollowedBy(ParseElementEnhance):
+    """
+    Lookahead matching of the given parse expression.  C{FollowedBy}
+    does I{not} advance the parsing position within the input string, it only
+    verifies that the specified parse expression matches at the current
+    position.  C{FollowedBy} always returns a null token list.
+
+    Example::
+        # use FollowedBy to match a label only if it is followed by a ':'
+        data_word = Word(alphas)
+        label = data_word + FollowedBy(':')
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        
+        OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint()
+    prints::
+        [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']]
+    """
+    def __init__( self, expr ):
+        super(FollowedBy,self).__init__(expr)
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        self.expr.tryParse( instring, loc )
+        return loc, []
+
+
+class NotAny(ParseElementEnhance):
+    """
+    Lookahead to disallow matching with the given parse expression.  C{NotAny}
+    does I{not} advance the parsing position within the input string, it only
+    verifies that the specified parse expression does I{not} match at the current
+    position.  Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny}
+    always returns a null token list.  May be constructed using the '~' operator.
+
+    Example::
+        
+    """
+    def __init__( self, expr ):
+        super(NotAny,self).__init__(expr)
+        #~ self.leaveWhitespace()
+        self.skipWhitespace = False  # do NOT use self.leaveWhitespace(), don't want to propagate to exprs
+        self.mayReturnEmpty = True
+        self.errmsg = "Found unwanted token, "+_ustr(self.expr)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.expr.canParseNext(instring, loc):
+            raise ParseException(instring, loc, self.errmsg, self)
+        return loc, []
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "~{" + _ustr(self.expr) + "}"
+
+        return self.strRepr
+
+class _MultipleMatch(ParseElementEnhance):
+    def __init__( self, expr, stopOn=None):
+        super(_MultipleMatch, self).__init__(expr)
+        self.saveAsList = True
+        ender = stopOn
+        if isinstance(ender, basestring):
+            ender = ParserElement._literalStringClass(ender)
+        self.not_ender = ~ender if ender is not None else None
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        self_expr_parse = self.expr._parse
+        self_skip_ignorables = self._skipIgnorables
+        check_ender = self.not_ender is not None
+        if check_ender:
+            try_not_ender = self.not_ender.tryParse
+        
+        # must be at least one (but first see if we are the stopOn sentinel;
+        # if so, fail)
+        if check_ender:
+            try_not_ender(instring, loc)
+        loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False )
+        try:
+            hasIgnoreExprs = (not not self.ignoreExprs)
+            while 1:
+                if check_ender:
+                    try_not_ender(instring, loc)
+                if hasIgnoreExprs:
+                    preloc = self_skip_ignorables( instring, loc )
+                else:
+                    preloc = loc
+                loc, tmptokens = self_expr_parse( instring, preloc, doActions )
+                if tmptokens or tmptokens.haskeys():
+                    tokens += tmptokens
+        except (ParseException,IndexError):
+            pass
+
+        return loc, tokens
+        
+class OneOrMore(_MultipleMatch):
+    """
+    Repetition of one or more of the given expression.
+    
+    Parameters:
+     - expr - expression that must match one or more times
+     - stopOn - (default=C{None}) - expression for a terminating sentinel
+          (only required if the sentinel would ordinarily match the repetition 
+          expression)          
+
+    Example::
+        data_word = Word(alphas)
+        label = data_word + FollowedBy(':')
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
+
+        text = "shape: SQUARE posn: upper left color: BLACK"
+        OneOrMore(attr_expr).parseString(text).pprint()  # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']]
+
+        # use stopOn attribute for OneOrMore to avoid reading label string as part of the data
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']]
+        
+        # could also be written as
+        (attr_expr * (1,)).parseString(text).pprint()
+    """
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + _ustr(self.expr) + "}..."
+
+        return self.strRepr
+
+class ZeroOrMore(_MultipleMatch):
+    """
+    Optional repetition of zero or more of the given expression.
+    
+    Parameters:
+     - expr - expression that must match zero or more times
+     - stopOn - (default=C{None}) - expression for a terminating sentinel
+          (only required if the sentinel would ordinarily match the repetition 
+          expression)          
+
+    Example: similar to L{OneOrMore}
+    """
+    def __init__( self, expr, stopOn=None):
+        super(ZeroOrMore,self).__init__(expr, stopOn=stopOn)
+        self.mayReturnEmpty = True
+        
+    def parseImpl( self, instring, loc, doActions=True ):
+        try:
+            return super(ZeroOrMore, self).parseImpl(instring, loc, doActions)
+        except (ParseException,IndexError):
+            return loc, []
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "[" + _ustr(self.expr) + "]..."
+
+        return self.strRepr
+
+class _NullToken(object):
+    def __bool__(self):
+        return False
+    __nonzero__ = __bool__
+    def __str__(self):
+        return ""
+
+_optionalNotMatched = _NullToken()
+class Optional(ParseElementEnhance):
+    """
+    Optional matching of the given expression.
+
+    Parameters:
+     - expr - expression that must match zero or more times
+     - default (optional) - value to be returned if the optional expression is not found.
+
+    Example::
+        # US postal code can be a 5-digit zip, plus optional 4-digit qualifier
+        zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4)))
+        zip.runTests('''
+            # traditional ZIP code
+            12345
+            
+            # ZIP+4 form
+            12101-0001
+            
+            # invalid ZIP
+            98765-
+            ''')
+    prints::
+        # traditional ZIP code
+        12345
+        ['12345']
+
+        # ZIP+4 form
+        12101-0001
+        ['12101-0001']
+
+        # invalid ZIP
+        98765-
+             ^
+        FAIL: Expected end of text (at char 5), (line:1, col:6)
+    """
+    def __init__( self, expr, default=_optionalNotMatched ):
+        super(Optional,self).__init__( expr, savelist=False )
+        self.saveAsList = self.expr.saveAsList
+        self.defaultValue = default
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        try:
+            loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False )
+        except (ParseException,IndexError):
+            if self.defaultValue is not _optionalNotMatched:
+                if self.expr.resultsName:
+                    tokens = ParseResults([ self.defaultValue ])
+                    tokens[self.expr.resultsName] = self.defaultValue
+                else:
+                    tokens = [ self.defaultValue ]
+            else:
+                tokens = []
+        return loc, tokens
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "[" + _ustr(self.expr) + "]"
+
+        return self.strRepr
+
+class SkipTo(ParseElementEnhance):
+    """
+    Token for skipping over all undefined text until the matched expression is found.
+
+    Parameters:
+     - expr - target expression marking the end of the data to be skipped
+     - include - (default=C{False}) if True, the target expression is also parsed 
+          (the skipped text and target expression are returned as a 2-element list).
+     - ignore - (default=C{None}) used to define grammars (typically quoted strings and 
+          comments) that might contain false matches to the target expression
+     - failOn - (default=C{None}) define expressions that are not allowed to be 
+          included in the skipped test; if found before the target expression is found, 
+          the SkipTo is not a match
+
+    Example::
+        report = '''
+            Outstanding Issues Report - 1 Jan 2000
+
+               # | Severity | Description                               |  Days Open
+            -----+----------+-------------------------------------------+-----------
+             101 | Critical | Intermittent system crash                 |          6
+              94 | Cosmetic | Spelling error on Login ('log|n')         |         14
+              79 | Minor    | System slow when running too many reports |         47
+            '''
+        integer = Word(nums)
+        SEP = Suppress('|')
+        # use SkipTo to simply match everything up until the next SEP
+        # - ignore quoted strings, so that a '|' character inside a quoted string does not match
+        # - parse action will call token.strip() for each matched token, i.e., the description body
+        string_data = SkipTo(SEP, ignore=quotedString)
+        string_data.setParseAction(tokenMap(str.strip))
+        ticket_expr = (integer("issue_num") + SEP 
+                      + string_data("sev") + SEP 
+                      + string_data("desc") + SEP 
+                      + integer("days_open"))
+        
+        for tkt in ticket_expr.searchString(report):
+            print tkt.dump()
+    prints::
+        ['101', 'Critical', 'Intermittent system crash', '6']
+        - days_open: 6
+        - desc: Intermittent system crash
+        - issue_num: 101
+        - sev: Critical
+        ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14']
+        - days_open: 14
+        - desc: Spelling error on Login ('log|n')
+        - issue_num: 94
+        - sev: Cosmetic
+        ['79', 'Minor', 'System slow when running too many reports', '47']
+        - days_open: 47
+        - desc: System slow when running too many reports
+        - issue_num: 79
+        - sev: Minor
+    """
+    def __init__( self, other, include=False, ignore=None, failOn=None ):
+        super( SkipTo, self ).__init__( other )
+        self.ignoreExpr = ignore
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+        self.includeMatch = include
+        self.asList = False
+        if isinstance(failOn, basestring):
+            self.failOn = ParserElement._literalStringClass(failOn)
+        else:
+            self.failOn = failOn
+        self.errmsg = "No match found for "+_ustr(self.expr)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        startloc = loc
+        instrlen = len(instring)
+        expr = self.expr
+        expr_parse = self.expr._parse
+        self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None
+        self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None
+        
+        tmploc = loc
+        while tmploc <= instrlen:
+            if self_failOn_canParseNext is not None:
+                # break if failOn expression matches
+                if self_failOn_canParseNext(instring, tmploc):
+                    break
+                    
+            if self_ignoreExpr_tryParse is not None:
+                # advance past ignore expressions
+                while 1:
+                    try:
+                        tmploc = self_ignoreExpr_tryParse(instring, tmploc)
+                    except ParseBaseException:
+                        break
+            
+            try:
+                expr_parse(instring, tmploc, doActions=False, callPreParse=False)
+            except (ParseException, IndexError):
+                # no match, advance loc in string
+                tmploc += 1
+            else:
+                # matched skipto expr, done
+                break
+
+        else:
+            # ran off the end of the input string without matching skipto expr, fail
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        # build up return values
+        loc = tmploc
+        skiptext = instring[startloc:loc]
+        skipresult = ParseResults(skiptext)
+        
+        if self.includeMatch:
+            loc, mat = expr_parse(instring,loc,doActions,callPreParse=False)
+            skipresult += mat
+
+        return loc, skipresult
+
+class Forward(ParseElementEnhance):
+    """
+    Forward declaration of an expression to be defined later -
+    used for recursive grammars, such as algebraic infix notation.
+    When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator.
+
+    Note: take care when assigning to C{Forward} not to overlook precedence of operators.
+    Specifically, '|' has a lower precedence than '<<', so that::
+        fwdExpr << a | b | c
+    will actually be evaluated as::
+        (fwdExpr << a) | b | c
+    thereby leaving b and c out as parseable alternatives.  It is recommended that you
+    explicitly group the values inserted into the C{Forward}::
+        fwdExpr << (a | b | c)
+    Converting to use the '<<=' operator instead will avoid this problem.
+
+    See L{ParseResults.pprint} for an example of a recursive parser created using
+    C{Forward}.
+    """
+    def __init__( self, other=None ):
+        super(Forward,self).__init__( other, savelist=False )
+
+    def __lshift__( self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass(other)
+        self.expr = other
+        self.strRepr = None
+        self.mayIndexError = self.expr.mayIndexError
+        self.mayReturnEmpty = self.expr.mayReturnEmpty
+        self.setWhitespaceChars( self.expr.whiteChars )
+        self.skipWhitespace = self.expr.skipWhitespace
+        self.saveAsList = self.expr.saveAsList
+        self.ignoreExprs.extend(self.expr.ignoreExprs)
+        return self
+        
+    def __ilshift__(self, other):
+        return self << other
+    
+    def leaveWhitespace( self ):
+        self.skipWhitespace = False
+        return self
+
+    def streamline( self ):
+        if not self.streamlined:
+            self.streamlined = True
+            if self.expr is not None:
+                self.expr.streamline()
+        return self
+
+    def validate( self, validateTrace=[] ):
+        if self not in validateTrace:
+            tmp = validateTrace[:]+[self]
+            if self.expr is not None:
+                self.expr.validate(tmp)
+        self.checkRecursion([])
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+        return self.__class__.__name__ + ": ..."
+
+        # stubbed out for now - creates awful memory and perf issues
+        self._revertClass = self.__class__
+        self.__class__ = _ForwardNoRecurse
+        try:
+            if self.expr is not None:
+                retString = _ustr(self.expr)
+            else:
+                retString = "None"
+        finally:
+            self.__class__ = self._revertClass
+        return self.__class__.__name__ + ": " + retString
+
+    def copy(self):
+        if self.expr is not None:
+            return super(Forward,self).copy()
+        else:
+            ret = Forward()
+            ret <<= self
+            return ret
+
+class _ForwardNoRecurse(Forward):
+    def __str__( self ):
+        return "..."
+
+class TokenConverter(ParseElementEnhance):
+    """
+    Abstract subclass of C{ParseExpression}, for converting parsed results.
+    """
+    def __init__( self, expr, savelist=False ):
+        super(TokenConverter,self).__init__( expr )#, savelist )
+        self.saveAsList = False
+
+class Combine(TokenConverter):
+    """
+    Converter to concatenate all matching tokens to a single string.
+    By default, the matching patterns must also be contiguous in the input string;
+    this can be disabled by specifying C{'adjacent=False'} in the constructor.
+
+    Example::
+        real = Word(nums) + '.' + Word(nums)
+        print(real.parseString('3.1416')) # -> ['3', '.', '1416']
+        # will also erroneously match the following
+        print(real.parseString('3. 1416')) # -> ['3', '.', '1416']
+
+        real = Combine(Word(nums) + '.' + Word(nums))
+        print(real.parseString('3.1416')) # -> ['3.1416']
+        # no match when there are internal spaces
+        print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...)
+    """
+    def __init__( self, expr, joinString="", adjacent=True ):
+        super(Combine,self).__init__( expr )
+        # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself
+        if adjacent:
+            self.leaveWhitespace()
+        self.adjacent = adjacent
+        self.skipWhitespace = True
+        self.joinString = joinString
+        self.callPreparse = True
+
+    def ignore( self, other ):
+        if self.adjacent:
+            ParserElement.ignore(self, other)
+        else:
+            super( Combine, self).ignore( other )
+        return self
+
+    def postParse( self, instring, loc, tokenlist ):
+        retToks = tokenlist.copy()
+        del retToks[:]
+        retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults)
+
+        if self.resultsName and retToks.haskeys():
+            return [ retToks ]
+        else:
+            return retToks
+
+class Group(TokenConverter):
+    """
+    Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions.
+
+    Example::
+        ident = Word(alphas)
+        num = Word(nums)
+        term = ident | num
+        func = ident + Optional(delimitedList(term))
+        print(func.parseString("fn a,b,100"))  # -> ['fn', 'a', 'b', '100']
+
+        func = ident + Group(Optional(delimitedList(term)))
+        print(func.parseString("fn a,b,100"))  # -> ['fn', ['a', 'b', '100']]
+    """
+    def __init__( self, expr ):
+        super(Group,self).__init__( expr )
+        self.saveAsList = True
+
+    def postParse( self, instring, loc, tokenlist ):
+        return [ tokenlist ]
+
+class Dict(TokenConverter):
+    """
+    Converter to return a repetitive expression as a list, but also as a dictionary.
+    Each element can also be referenced using the first token in the expression as its key.
+    Useful for tabular report scraping when the first column can be used as a item key.
+
+    Example::
+        data_word = Word(alphas)
+        label = data_word + FollowedBy(':')
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
+
+        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
+        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        
+        # print attributes as plain groups
+        print(OneOrMore(attr_expr).parseString(text).dump())
+        
+        # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names
+        result = Dict(OneOrMore(Group(attr_expr))).parseString(text)
+        print(result.dump())
+        
+        # access named fields as dict entries, or output as dict
+        print(result['shape'])        
+        print(result.asDict())
+    prints::
+        ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap']
+
+        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
+        - color: light blue
+        - posn: upper left
+        - shape: SQUARE
+        - texture: burlap
+        SQUARE
+        {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'}
+    See more examples at L{ParseResults} of accessing fields by results name.
+    """
+    def __init__( self, expr ):
+        super(Dict,self).__init__( expr )
+        self.saveAsList = True
+
+    def postParse( self, instring, loc, tokenlist ):
+        for i,tok in enumerate(tokenlist):
+            if len(tok) == 0:
+                continue
+            ikey = tok[0]
+            if isinstance(ikey,int):
+                ikey = _ustr(tok[0]).strip()
+            if len(tok)==1:
+                tokenlist[ikey] = _ParseResultsWithOffset("",i)
+            elif len(tok)==2 and not isinstance(tok[1],ParseResults):
+                tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i)
+            else:
+                dictvalue = tok.copy() #ParseResults(i)
+                del dictvalue[0]
+                if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()):
+                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i)
+                else:
+                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i)
+
+        if self.resultsName:
+            return [ tokenlist ]
+        else:
+            return tokenlist
+
+
+class Suppress(TokenConverter):
+    """
+    Converter for ignoring the results of a parsed expression.
+
+    Example::
+        source = "a, b, c,d"
+        wd = Word(alphas)
+        wd_list1 = wd + ZeroOrMore(',' + wd)
+        print(wd_list1.parseString(source))
+
+        # often, delimiters that are useful during parsing are just in the
+        # way afterward - use Suppress to keep them out of the parsed output
+        wd_list2 = wd + ZeroOrMore(Suppress(',') + wd)
+        print(wd_list2.parseString(source))
+    prints::
+        ['a', ',', 'b', ',', 'c', ',', 'd']
+        ['a', 'b', 'c', 'd']
+    (See also L{delimitedList}.)
+    """
+    def postParse( self, instring, loc, tokenlist ):
+        return []
+
+    def suppress( self ):
+        return self
+
+
+class OnlyOnce(object):
+    """
+    Wrapper for parse actions, to ensure they are only called once.
+    """
+    def __init__(self, methodCall):
+        self.callable = _trim_arity(methodCall)
+        self.called = False
+    def __call__(self,s,l,t):
+        if not self.called:
+            results = self.callable(s,l,t)
+            self.called = True
+            return results
+        raise ParseException(s,l,"")
+    def reset(self):
+        self.called = False
+
+def traceParseAction(f):
+    """
+    Decorator for debugging parse actions. 
+    
+    When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".}
+    When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised.
+
+    Example::
+        wd = Word(alphas)
+
+        @traceParseAction
+        def remove_duplicate_chars(tokens):
+            return ''.join(sorted(set(''.join(tokens))))
+
+        wds = OneOrMore(wd).setParseAction(remove_duplicate_chars)
+        print(wds.parseString("slkdjs sld sldd sdlf sdljf"))
+    prints::
+        >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {}))
+        <3:
+            thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc
+        sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\n" % (thisFunc,line(l,s),l,t) )
+        try:
+            ret = f(*paArgs)
+        except Exception as exc:
+            sys.stderr.write( "< ['aa', 'bb', 'cc']
+        delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE']
+    """
+    dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..."
+    if combine:
+        return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName)
+    else:
+        return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName)
+
+def countedArray( expr, intExpr=None ):
+    """
+    Helper to define a counted list of expressions.
+    This helper defines a pattern of the form::
+        integer expr expr expr...
+    where the leading integer tells how many expr expressions follow.
+    The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed.
+    
+    If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value.
+
+    Example::
+        countedArray(Word(alphas)).parseString('2 ab cd ef')  # -> ['ab', 'cd']
+
+        # in this parser, the leading integer value is given in binary,
+        # '10' indicating that 2 values are in the array
+        binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2))
+        countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef')  # -> ['ab', 'cd']
+    """
+    arrayExpr = Forward()
+    def countFieldParseAction(s,l,t):
+        n = t[0]
+        arrayExpr << (n and Group(And([expr]*n)) or Group(empty))
+        return []
+    if intExpr is None:
+        intExpr = Word(nums).setParseAction(lambda t:int(t[0]))
+    else:
+        intExpr = intExpr.copy()
+    intExpr.setName("arrayLen")
+    intExpr.addParseAction(countFieldParseAction, callDuringTry=True)
+    return ( intExpr + arrayExpr ).setName('(len) ' + _ustr(expr) + '...')
+
+def _flatten(L):
+    ret = []
+    for i in L:
+        if isinstance(i,list):
+            ret.extend(_flatten(i))
+        else:
+            ret.append(i)
+    return ret
+
+def matchPreviousLiteral(expr):
+    """
+    Helper to define an expression that is indirectly defined from
+    the tokens matched in a previous expression, that is, it looks
+    for a 'repeat' of a previous expression.  For example::
+        first = Word(nums)
+        second = matchPreviousLiteral(first)
+        matchExpr = first + ":" + second
+    will match C{"1:1"}, but not C{"1:2"}.  Because this matches a
+    previous literal, will also match the leading C{"1:1"} in C{"1:10"}.
+    If this is not desired, use C{matchPreviousExpr}.
+    Do I{not} use with packrat parsing enabled.
+    """
+    rep = Forward()
+    def copyTokenToRepeater(s,l,t):
+        if t:
+            if len(t) == 1:
+                rep << t[0]
+            else:
+                # flatten t tokens
+                tflat = _flatten(t.asList())
+                rep << And(Literal(tt) for tt in tflat)
+        else:
+            rep << Empty()
+    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
+    rep.setName('(prev) ' + _ustr(expr))
+    return rep
+
+def matchPreviousExpr(expr):
+    """
+    Helper to define an expression that is indirectly defined from
+    the tokens matched in a previous expression, that is, it looks
+    for a 'repeat' of a previous expression.  For example::
+        first = Word(nums)
+        second = matchPreviousExpr(first)
+        matchExpr = first + ":" + second
+    will match C{"1:1"}, but not C{"1:2"}.  Because this matches by
+    expressions, will I{not} match the leading C{"1:1"} in C{"1:10"};
+    the expressions are evaluated first, and then compared, so
+    C{"1"} is compared with C{"10"}.
+    Do I{not} use with packrat parsing enabled.
+    """
+    rep = Forward()
+    e2 = expr.copy()
+    rep <<= e2
+    def copyTokenToRepeater(s,l,t):
+        matchTokens = _flatten(t.asList())
+        def mustMatchTheseTokens(s,l,t):
+            theseTokens = _flatten(t.asList())
+            if  theseTokens != matchTokens:
+                raise ParseException("",0,"")
+        rep.setParseAction( mustMatchTheseTokens, callDuringTry=True )
+    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
+    rep.setName('(prev) ' + _ustr(expr))
+    return rep
+
+def _escapeRegexRangeChars(s):
+    #~  escape these chars: ^-]
+    for c in r"\^-]":
+        s = s.replace(c,_bslash+c)
+    s = s.replace("\n",r"\n")
+    s = s.replace("\t",r"\t")
+    return _ustr(s)
+
+def oneOf( strs, caseless=False, useRegex=True ):
+    """
+    Helper to quickly define a set of alternative Literals, and makes sure to do
+    longest-first testing when there is a conflict, regardless of the input order,
+    but returns a C{L{MatchFirst}} for best performance.
+
+    Parameters:
+     - strs - a string of space-delimited literals, or a collection of string literals
+     - caseless - (default=C{False}) - treat all literals as caseless
+     - useRegex - (default=C{True}) - as an optimization, will generate a Regex
+          object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or
+          if creating a C{Regex} raises an exception)
+
+    Example::
+        comp_oper = oneOf("< = > <= >= !=")
+        var = Word(alphas)
+        number = Word(nums)
+        term = var | number
+        comparison_expr = term + comp_oper + term
+        print(comparison_expr.searchString("B = 12  AA=23 B<=AA AA>12"))
+    prints::
+        [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']]
+    """
+    if caseless:
+        isequal = ( lambda a,b: a.upper() == b.upper() )
+        masks = ( lambda a,b: b.upper().startswith(a.upper()) )
+        parseElementClass = CaselessLiteral
+    else:
+        isequal = ( lambda a,b: a == b )
+        masks = ( lambda a,b: b.startswith(a) )
+        parseElementClass = Literal
+
+    symbols = []
+    if isinstance(strs,basestring):
+        symbols = strs.split()
+    elif isinstance(strs, Iterable):
+        symbols = list(strs)
+    else:
+        warnings.warn("Invalid argument to oneOf, expected string or iterable",
+                SyntaxWarning, stacklevel=2)
+    if not symbols:
+        return NoMatch()
+
+    i = 0
+    while i < len(symbols)-1:
+        cur = symbols[i]
+        for j,other in enumerate(symbols[i+1:]):
+            if ( isequal(other, cur) ):
+                del symbols[i+j+1]
+                break
+            elif ( masks(cur, other) ):
+                del symbols[i+j+1]
+                symbols.insert(i,other)
+                cur = other
+                break
+        else:
+            i += 1
+
+    if not caseless and useRegex:
+        #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] ))
+        try:
+            if len(symbols)==len("".join(symbols)):
+                return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols))
+            else:
+                return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols))
+        except Exception:
+            warnings.warn("Exception creating Regex for oneOf, building MatchFirst",
+                    SyntaxWarning, stacklevel=2)
+
+
+    # last resort, just use MatchFirst
+    return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols))
+
+def dictOf( key, value ):
+    """
+    Helper to easily and clearly define a dictionary by specifying the respective patterns
+    for the key and value.  Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens
+    in the proper order.  The key pattern can include delimiting markers or punctuation,
+    as long as they are suppressed, thereby leaving the significant key text.  The value
+    pattern can include named results, so that the C{Dict} results can include named token
+    fields.
+
+    Example::
+        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
+        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        print(OneOrMore(attr_expr).parseString(text).dump())
+        
+        attr_label = label
+        attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)
+
+        # similar to Dict, but simpler call format
+        result = dictOf(attr_label, attr_value).parseString(text)
+        print(result.dump())
+        print(result['shape'])
+        print(result.shape)  # object attribute access works too
+        print(result.asDict())
+    prints::
+        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
+        - color: light blue
+        - posn: upper left
+        - shape: SQUARE
+        - texture: burlap
+        SQUARE
+        SQUARE
+        {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'}
+    """
+    return Dict( ZeroOrMore( Group ( key + value ) ) )
+
+def originalTextFor(expr, asString=True):
+    """
+    Helper to return the original, untokenized text for a given expression.  Useful to
+    restore the parsed fields of an HTML start tag into the raw tag text itself, or to
+    revert separate tokens with intervening whitespace back to the original matching
+    input text. By default, returns astring containing the original parsed text.  
+       
+    If the optional C{asString} argument is passed as C{False}, then the return value is a 
+    C{L{ParseResults}} containing any results names that were originally matched, and a 
+    single token containing the original matched text from the input string.  So if 
+    the expression passed to C{L{originalTextFor}} contains expressions with defined
+    results names, you must set C{asString} to C{False} if you want to preserve those
+    results name values.
+
+    Example::
+        src = "this is test  bold text  normal text "
+        for tag in ("b","i"):
+            opener,closer = makeHTMLTags(tag)
+            patt = originalTextFor(opener + SkipTo(closer) + closer)
+            print(patt.searchString(src)[0])
+    prints::
+        [' bold text ']
+        ['text']
+    """
+    locMarker = Empty().setParseAction(lambda s,loc,t: loc)
+    endlocMarker = locMarker.copy()
+    endlocMarker.callPreparse = False
+    matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end")
+    if asString:
+        extractText = lambda s,l,t: s[t._original_start:t._original_end]
+    else:
+        def extractText(s,l,t):
+            t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]]
+    matchExpr.setParseAction(extractText)
+    matchExpr.ignoreExprs = expr.ignoreExprs
+    return matchExpr
+
+def ungroup(expr): 
+    """
+    Helper to undo pyparsing's default grouping of And expressions, even
+    if all but one are non-empty.
+    """
+    return TokenConverter(expr).setParseAction(lambda t:t[0])
+
+def locatedExpr(expr):
+    """
+    Helper to decorate a returned token with its starting and ending locations in the input string.
+    This helper adds the following results names:
+     - locn_start = location where matched expression begins
+     - locn_end = location where matched expression ends
+     - value = the actual parsed results
+
+    Be careful if the input text contains C{} characters, you may want to call
+    C{L{ParserElement.parseWithTabs}}
+
+    Example::
+        wd = Word(alphas)
+        for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"):
+            print(match)
+    prints::
+        [[0, 'ljsdf', 5]]
+        [[8, 'lksdjjf', 15]]
+        [[18, 'lkkjj', 23]]
+    """
+    locator = Empty().setParseAction(lambda s,l,t: l)
+    return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end"))
+
+
+# convenience constants for positional expressions
+empty       = Empty().setName("empty")
+lineStart   = LineStart().setName("lineStart")
+lineEnd     = LineEnd().setName("lineEnd")
+stringStart = StringStart().setName("stringStart")
+stringEnd   = StringEnd().setName("stringEnd")
+
+_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1])
+_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16)))
+_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8)))
+_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | CharsNotIn(r'\]', exact=1)
+_charRange = Group(_singleChar + Suppress("-") + _singleChar)
+_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]"
+
+def srange(s):
+    r"""
+    Helper to easily define string ranges for use in Word construction.  Borrows
+    syntax from regexp '[]' string range definitions::
+        srange("[0-9]")   -> "0123456789"
+        srange("[a-z]")   -> "abcdefghijklmnopqrstuvwxyz"
+        srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_"
+    The input string must be enclosed in []'s, and the returned string is the expanded
+    character set joined into a single string.
+    The values enclosed in the []'s may be:
+     - a single character
+     - an escaped character with a leading backslash (such as C{\-} or C{\]})
+     - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character) 
+         (C{\0x##} is also supported for backwards compatibility) 
+     - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character)
+     - a range of any of the above, separated by a dash (C{'a-z'}, etc.)
+     - any combination of the above (C{'aeiouy'}, C{'a-zA-Z0-9_$'}, etc.)
+    """
+    _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1))
+    try:
+        return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body)
+    except Exception:
+        return ""
+
+def matchOnlyAtCol(n):
+    """
+    Helper method for defining parse actions that require matching at a specific
+    column in the input text.
+    """
+    def verifyCol(strg,locn,toks):
+        if col(locn,strg) != n:
+            raise ParseException(strg,locn,"matched token not at column %d" % n)
+    return verifyCol
+
+def replaceWith(replStr):
+    """
+    Helper method for common parse actions that simply return a literal value.  Especially
+    useful when used with C{L{transformString}()}.
+
+    Example::
+        num = Word(nums).setParseAction(lambda toks: int(toks[0]))
+        na = oneOf("N/A NA").setParseAction(replaceWith(math.nan))
+        term = na | num
+        
+        OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234]
+    """
+    return lambda s,l,t: [replStr]
+
+def removeQuotes(s,l,t):
+    """
+    Helper parse action for removing quotation marks from parsed quoted strings.
+
+    Example::
+        # by default, quotation marks are included in parsed results
+        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"]
+
+        # use removeQuotes to strip quotation marks from parsed results
+        quotedString.setParseAction(removeQuotes)
+        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"]
+    """
+    return t[0][1:-1]
+
+def tokenMap(func, *args):
+    """
+    Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional 
+    args are passed, they are forwarded to the given function as additional arguments after
+    the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the
+    parsed data to an integer using base 16.
+
+    Example (compare the last to example in L{ParserElement.transformString}::
+        hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16))
+        hex_ints.runTests('''
+            00 11 22 aa FF 0a 0d 1a
+            ''')
+        
+        upperword = Word(alphas).setParseAction(tokenMap(str.upper))
+        OneOrMore(upperword).runTests('''
+            my kingdom for a horse
+            ''')
+
+        wd = Word(alphas).setParseAction(tokenMap(str.title))
+        OneOrMore(wd).setParseAction(' '.join).runTests('''
+            now is the winter of our discontent made glorious summer by this sun of york
+            ''')
+    prints::
+        00 11 22 aa FF 0a 0d 1a
+        [0, 17, 34, 170, 255, 10, 13, 26]
+
+        my kingdom for a horse
+        ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE']
+
+        now is the winter of our discontent made glorious summer by this sun of york
+        ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York']
+    """
+    def pa(s,l,t):
+        return [func(tokn, *args) for tokn in t]
+
+    try:
+        func_name = getattr(func, '__name__', 
+                            getattr(func, '__class__').__name__)
+    except Exception:
+        func_name = str(func)
+    pa.__name__ = func_name
+
+    return pa
+
+upcaseTokens = tokenMap(lambda t: _ustr(t).upper())
+"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}"""
+
+downcaseTokens = tokenMap(lambda t: _ustr(t).lower())
+"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}"""
+    
+def _makeTags(tagStr, xml):
+    """Internal helper to construct opening and closing tag expressions, given a tag name"""
+    if isinstance(tagStr,basestring):
+        resname = tagStr
+        tagStr = Keyword(tagStr, caseless=not xml)
+    else:
+        resname = tagStr.name
+
+    tagAttrName = Word(alphas,alphanums+"_-:")
+    if (xml):
+        tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes )
+        openTag = Suppress("<") + tagStr("tag") + \
+                Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \
+                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
+    else:
+        printablesLessRAbrack = "".join(c for c in printables if c not in ">")
+        tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack)
+        openTag = Suppress("<") + tagStr("tag") + \
+                Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \
+                Optional( Suppress("=") + tagAttrValue ) ))) + \
+                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
+    closeTag = Combine(_L("")
+
+    openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname)
+    closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % resname)
+    openTag.tag = resname
+    closeTag.tag = resname
+    return openTag, closeTag
+
+def makeHTMLTags(tagStr):
+    """
+    Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches
+    tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values.
+
+    Example::
+        text = 'More info at the pyparsing wiki page'
+        # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple
+        a,a_end = makeHTMLTags("A")
+        link_expr = a + SkipTo(a_end)("link_text") + a_end
+        
+        for link in link_expr.searchString(text):
+            # attributes in the  tag (like "href" shown here) are also accessible as named results
+            print(link.link_text, '->', link.href)
+    prints::
+        pyparsing -> http://pyparsing.wikispaces.com
+    """
+    return _makeTags( tagStr, False )
+
+def makeXMLTags(tagStr):
+    """
+    Helper to construct opening and closing tag expressions for XML, given a tag name. Matches
+    tags only in the given upper/lower case.
+
+    Example: similar to L{makeHTMLTags}
+    """
+    return _makeTags( tagStr, True )
+
+def withAttribute(*args,**attrDict):
+    """
+    Helper to create a validating parse action to be used with start tags created
+    with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag
+    with a required attribute value, to avoid false matches on common tags such as
+    C{} or C{
}. + + Call C{withAttribute} with a series of attribute names and values. Specify the list + of filter attributes names and values as: + - keyword arguments, as in C{(align="right")}, or + - as an explicit dict with C{**} operator, when an attribute name is also a Python + reserved word, as in C{**{"class":"Customer", "align":"right"}} + - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) + For attribute names with a namespace prefix, you must use the second form. Attribute + names are matched insensitive to upper/lower case. + + If just testing for C{class} (with or without a namespace), use C{L{withClass}}. + + To verify that the attribute exists, but without specifying a value, pass + C{withAttribute.ANY_VALUE} as the value. + + Example:: + html = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this has no type
+
+ + ''' + div,div_end = makeHTMLTags("div") + + # only match div tag having a type attribute with value "grid" + div_grid = div().setParseAction(withAttribute(type="grid")) + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + # construct a match with any div tag having a type attribute, regardless of the value + div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + if args: + attrs = args[:] + else: + attrs = attrDict.items() + attrs = [(k,v) for k,v in attrs] + def pa(s,l,tokens): + for attrName,attrValue in attrs: + if attrName not in tokens: + raise ParseException(s,l,"no matching attribute " + attrName) + if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: + raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % + (attrName, tokens[attrName], attrValue)) + return pa +withAttribute.ANY_VALUE = object() + +def withClass(classname, namespace=''): + """ + Simplified version of C{L{withAttribute}} when matching on a div class - made + difficult because C{class} is a reserved word in Python. + + Example:: + html = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this <div> has no class
+
+ + ''' + div,div_end = makeHTMLTags("div") + div_grid = div().setParseAction(withClass("grid")) + + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + classattr = "%s:class" % namespace if namespace else "class" + return withAttribute(**{classattr : classname}) + +opAssoc = _Constants() +opAssoc.LEFT = object() +opAssoc.RIGHT = object() + +def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): + """ + Helper method for constructing grammars of expressions made up of + operators working in a precedence hierarchy. Operators may be unary or + binary, left- or right-associative. Parse actions can also be attached + to operator expressions. The generated parser will also recognize the use + of parentheses to override operator precedences (see example below). + + Note: if you define a deep operator list, you may see performance issues + when using infixNotation. See L{ParserElement.enablePackrat} for a + mechanism to potentially improve your parser performance. + + Parameters: + - baseExpr - expression representing the most basic element for the nested + - opList - list of tuples, one for each operator precedence level in the + expression grammar; each tuple is of the form + (opExpr, numTerms, rightLeftAssoc, parseAction), where: + - opExpr is the pyparsing expression for the operator; + may also be a string, which will be converted to a Literal; + if numTerms is 3, opExpr is a tuple of two expressions, for the + two operators separating the 3 terms + - numTerms is the number of terms for this operator (must + be 1, 2, or 3) + - rightLeftAssoc is the indicator whether the operator is + right or left associative, using the pyparsing-defined + constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. + - parseAction is the parse action to be associated with + expressions matching this operator expression (the + parse action tuple member may be omitted); if the parse action + is passed a tuple or list of functions, this is equivalent to + calling C{setParseAction(*fn)} (L{ParserElement.setParseAction}) + - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) + - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) + + Example:: + # simple example of four-function arithmetic with ints and variable names + integer = pyparsing_common.signed_integer + varname = pyparsing_common.identifier + + arith_expr = infixNotation(integer | varname, + [ + ('-', 1, opAssoc.RIGHT), + (oneOf('* /'), 2, opAssoc.LEFT), + (oneOf('+ -'), 2, opAssoc.LEFT), + ]) + + arith_expr.runTests(''' + 5+3*6 + (5+3)*6 + -2--11 + ''', fullDump=False) + prints:: + 5+3*6 + [[5, '+', [3, '*', 6]]] + + (5+3)*6 + [[[5, '+', 3], '*', 6]] + + -2--11 + [[['-', 2], '-', ['-', 11]]] + """ + ret = Forward() + lastExpr = baseExpr | ( lpar + ret + rpar ) + for i,operDef in enumerate(opList): + opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr + if arity == 3: + if opExpr is None or len(opExpr) != 2: + raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") + opExpr1, opExpr2 = opExpr + thisExpr = Forward().setName(termName) + if rightLeftAssoc == opAssoc.LEFT: + if arity == 1: + matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) + else: + matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ + Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + elif rightLeftAssoc == opAssoc.RIGHT: + if arity == 1: + # try to avoid LR with this extra test + if not isinstance(opExpr, Optional): + opExpr = Optional(opExpr) + matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) + else: + matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ + Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + else: + raise ValueError("operator must indicate right or left associativity") + if pa: + if isinstance(pa, (tuple, list)): + matchExpr.setParseAction(*pa) + else: + matchExpr.setParseAction(pa) + thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) + lastExpr = thisExpr + ret <<= lastExpr + return ret + +operatorPrecedence = infixNotation +"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" + +dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") +sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") +quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| + Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") + +def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): + """ + Helper method for defining nested lists enclosed in opening and closing + delimiters ("(" and ")" are the default). + + Parameters: + - opener - opening character for a nested list (default=C{"("}); can also be a pyparsing expression + - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression + - content - expression for items within the nested lists (default=C{None}) + - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{quotedString}) + + If an expression is not provided for the content argument, the nested + expression will capture all whitespace-delimited content between delimiters + as a list of separate values. + + Use the C{ignoreExpr} argument to define expressions that may contain + opening or closing characters that should not be treated as opening + or closing characters for nesting, such as quotedString or a comment + expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. + The default is L{quotedString}, but if no expressions are to be ignored, + then pass C{None} for this argument. + + Example:: + data_type = oneOf("void int short long char float double") + decl_data_type = Combine(data_type + Optional(Word('*'))) + ident = Word(alphas+'_', alphanums+'_') + number = pyparsing_common.number + arg = Group(decl_data_type + ident) + LPAR,RPAR = map(Suppress, "()") + + code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) + + c_function = (decl_data_type("type") + + ident("name") + + LPAR + Optional(delimitedList(arg), [])("args") + RPAR + + code_body("body")) + c_function.ignore(cStyleComment) + + source_code = ''' + int is_odd(int x) { + return (x%2); + } + + int dec_to_hex(char hchar) { + if (hchar >= '0' && hchar <= '9') { + return (ord(hchar)-ord('0')); + } else { + return (10+ord(hchar)-ord('A')); + } + } + ''' + for func in c_function.searchString(source_code): + print("%(name)s (%(type)s) args: %(args)s" % func) + + prints:: + is_odd (int) args: [['int', 'x']] + dec_to_hex (int) args: [['char', 'hchar']] + """ + if opener == closer: + raise ValueError("opening and closing strings cannot be the same") + if content is None: + if isinstance(opener,basestring) and isinstance(closer,basestring): + if len(opener) == 1 and len(closer)==1: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS + ).setParseAction(lambda t:t[0].strip())) + else: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + ~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + raise ValueError("opening and closing arguments must be strings if no content expression is given") + ret = Forward() + if ignoreExpr is not None: + ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) + else: + ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) + ret.setName('nested %s%s expression' % (opener,closer)) + return ret + +def indentedBlock(blockStatementExpr, indentStack, indent=True): + """ + Helper method for defining space-delimited indentation blocks, such as + those used to define block statements in Python source code. + + Parameters: + - blockStatementExpr - expression defining syntax of statement that + is repeated within the indented block + - indentStack - list created by caller to manage indentation stack + (multiple statementWithIndentedBlock expressions within a single grammar + should share a common indentStack) + - indent - boolean indicating whether block must be indented beyond the + the current level; set to False for block of left-most statements + (default=C{True}) + + A valid block must contain at least one C{blockStatement}. + + Example:: + data = ''' + def A(z): + A1 + B = 100 + G = A2 + A2 + A3 + B + def BB(a,b,c): + BB1 + def BBA(): + bba1 + bba2 + bba3 + C + D + def spam(x,y): + def eggs(z): + pass + ''' + + + indentStack = [1] + stmt = Forward() + + identifier = Word(alphas, alphanums) + funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") + func_body = indentedBlock(stmt, indentStack) + funcDef = Group( funcDecl + func_body ) + + rvalue = Forward() + funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") + rvalue << (funcCall | identifier | Word(nums)) + assignment = Group(identifier + "=" + rvalue) + stmt << ( funcDef | assignment | identifier ) + + module_body = OneOrMore(stmt) + + parseTree = module_body.parseString(data) + parseTree.pprint() + prints:: + [['def', + 'A', + ['(', 'z', ')'], + ':', + [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], + 'B', + ['def', + 'BB', + ['(', 'a', 'b', 'c', ')'], + ':', + [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], + 'C', + 'D', + ['def', + 'spam', + ['(', 'x', 'y', ')'], + ':', + [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] + """ + def checkPeerIndent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if curCol != indentStack[-1]: + if curCol > indentStack[-1]: + raise ParseFatalException(s,l,"illegal nesting") + raise ParseException(s,l,"not a peer entry") + + def checkSubIndent(s,l,t): + curCol = col(l,s) + if curCol > indentStack[-1]: + indentStack.append( curCol ) + else: + raise ParseException(s,l,"not a subentry") + + def checkUnindent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): + raise ParseException(s,l,"not an unindent") + indentStack.pop() + + NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) + INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') + PEER = Empty().setParseAction(checkPeerIndent).setName('') + UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') + if indent: + smExpr = Group( Optional(NL) + + #~ FollowedBy(blockStatementExpr) + + INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) + else: + smExpr = Group( Optional(NL) + + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) + blockStatementExpr.ignore(_bslash + LineEnd()) + return smExpr.setName('indented block') + +alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") +punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") + +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) +_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) +commonHTMLEntity = Regex('&(?P' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") +def replaceHTMLEntity(t): + """Helper parser action to replace common HTML entities with their special characters""" + return _htmlEntityMap.get(t.entity) + +# it's easy to get these comment structures wrong - they're very common, so may as well make them available +cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") +"Comment of the form C{/* ... */}" + +htmlComment = Regex(r"").setName("HTML comment") +"Comment of the form C{}" + +restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") +dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") +"Comment of the form C{// ... (to end of line)}" + +cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") +"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" + +javaStyleComment = cppStyleComment +"Same as C{L{cppStyleComment}}" + +pythonStyleComment = Regex(r"#.*").setName("Python style comment") +"Comment of the form C{# ... (to end of line)}" + +_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + + Optional( Word(" \t") + + ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") +commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") +"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. + This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" + +# some other useful expressions - using lower-case class name since we are really using this as a namespace +class pyparsing_common: + """ + Here are some common low-level expressions that may be useful in jump-starting parser development: + - numeric forms (L{integers}, L{reals}, L{scientific notation}) + - common L{programming identifiers} + - network addresses (L{MAC}, L{IPv4}, L{IPv6}) + - ISO8601 L{dates} and L{datetime} + - L{UUID} + - L{comma-separated list} + Parse actions: + - C{L{convertToInteger}} + - C{L{convertToFloat}} + - C{L{convertToDate}} + - C{L{convertToDatetime}} + - C{L{stripHTMLTags}} + - C{L{upcaseTokens}} + - C{L{downcaseTokens}} + + Example:: + pyparsing_common.number.runTests(''' + # any int or real number, returned as the appropriate type + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.fnumber.runTests(''' + # any int or real number, returned as float + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.hex_integer.runTests(''' + # hex numbers + 100 + FF + ''') + + pyparsing_common.fraction.runTests(''' + # fractions + 1/2 + -3/4 + ''') + + pyparsing_common.mixed_integer.runTests(''' + # mixed fractions + 1 + 1/2 + -3/4 + 1-3/4 + ''') + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(''' + # uuid + 12345678-1234-5678-1234-567812345678 + ''') + prints:: + # any int or real number, returned as the appropriate type + 100 + [100] + + -100 + [-100] + + +100 + [100] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # any int or real number, returned as float + 100 + [100.0] + + -100 + [-100.0] + + +100 + [100.0] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # hex numbers + 100 + [256] + + FF + [255] + + # fractions + 1/2 + [0.5] + + -3/4 + [-0.75] + + # mixed fractions + 1 + [1] + + 1/2 + [0.5] + + -3/4 + [-0.75] + + 1-3/4 + [1.75] + + # uuid + 12345678-1234-5678-1234-567812345678 + [UUID('12345678-1234-5678-1234-567812345678')] + """ + + convertToInteger = tokenMap(int) + """ + Parse action for converting parsed integers to Python int + """ + + convertToFloat = tokenMap(float) + """ + Parse action for converting parsed numbers to Python float + """ + + integer = Word(nums).setName("integer").setParseAction(convertToInteger) + """expression that parses an unsigned integer, returns an int""" + + hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) + """expression that parses a hexadecimal integer, returns an int""" + + signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) + """expression that parses an integer with optional leading sign, returns an int""" + + fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") + """fractional expression of an integer divided by an integer, returns a float""" + fraction.addParseAction(lambda t: t[0]/t[-1]) + + mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") + """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" + mixed_integer.addParseAction(sum) + + real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) + """expression that parses a floating point number and returns a float""" + + sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) + """expression that parses a floating point number with optional scientific notation and returns a float""" + + # streamlining this expression makes the docs nicer-looking + number = (sci_real | real | signed_integer).streamline() + """any numeric expression, returns the corresponding Python type""" + + fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) + """any int or real number, returned as float""" + + identifier = Word(alphas+'_', alphanums+'_').setName("identifier") + """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" + + ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") + "IPv4 address (C{0.0.0.0 - 255.255.255.255})" + + _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") + _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") + _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") + _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) + _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") + ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") + "IPv6 address (long, short, or mixed form)" + + mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") + "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" + + @staticmethod + def convertToDate(fmt="%Y-%m-%d"): + """ + Helper to create a parse action for converting parsed date string to Python datetime.date + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) + + Example:: + date_expr = pyparsing_common.iso8601_date.copy() + date_expr.setParseAction(pyparsing_common.convertToDate()) + print(date_expr.parseString("1999-12-31")) + prints:: + [datetime.date(1999, 12, 31)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt).date() + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + @staticmethod + def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): + """ + Helper to create a parse action for converting parsed datetime string to Python datetime.datetime + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) + + Example:: + dt_expr = pyparsing_common.iso8601_datetime.copy() + dt_expr.setParseAction(pyparsing_common.convertToDatetime()) + print(dt_expr.parseString("1999-12-31T23:59:59.999")) + prints:: + [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt) + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + iso8601_date = Regex(r'(?P\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?').setName("ISO8601 date") + "ISO8601 date (C{yyyy-mm-dd})" + + iso8601_datetime = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") + "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" + + uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") + "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" + + _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() + @staticmethod + def stripHTMLTags(s, l, tokens): + """ + Parse action to remove HTML tags from web page HTML source + + Example:: + # strip HTML links from normal text + text = 'More info at the
pyparsing wiki page' + td,td_end = makeHTMLTags("TD") + table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end + + print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' + """ + return pyparsing_common._html_stripper.transformString(tokens[0]) + + _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') + + Optional( White(" \t") ) ) ).streamline().setName("commaItem") + comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") + """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" + + upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) + """Parse action to convert tokens to upper case.""" + + downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) + """Parse action to convert tokens to lower case.""" + + +if __name__ == "__main__": + + selectToken = CaselessLiteral("select") + fromToken = CaselessLiteral("from") + + ident = Word(alphas, alphanums + "_$") + + columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + columnNameList = Group(delimitedList(columnName)).setName("columns") + columnSpec = ('*' | columnNameList) + + tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + tableNameList = Group(delimitedList(tableName)).setName("tables") + + simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") + + # demo runTests method, including embedded comments in test string + simpleSQL.runTests(""" + # '*' as column list and dotted table name + select * from SYS.XYZZY + + # caseless match on "SELECT", and casts back to "select" + SELECT * from XYZZY, ABC + + # list of column names, and mixed case SELECT keyword + Select AA,BB,CC from Sys.dual + + # multiple tables + Select A, B, C from Sys.dual, Table2 + + # invalid SELECT keyword - should fail + Xelect A, B, C from Sys.dual + + # incomplete command - should fail + Select + + # invalid column name - should fail + Select ^^^ frox Sys.dual + + """) + + pyparsing_common.number.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + # any int or real number, returned as float + pyparsing_common.fnumber.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + pyparsing_common.hex_integer.runTests(""" + 100 + FF + """) + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(""" + 12345678-1234-5678-1234-567812345678 + """) diff --git a/_setuptools_vendored/six-1.16.0.dist-info/LICENSE b/_setuptools_vendored/six-1.16.0.dist-info/LICENSE new file mode 100644 index 0000000000..de6633112c --- /dev/null +++ b/_setuptools_vendored/six-1.16.0.dist-info/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2010-2020 Benjamin Peterson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/_setuptools_vendored/six-1.16.0.dist-info/METADATA b/_setuptools_vendored/six-1.16.0.dist-info/METADATA new file mode 100644 index 0000000000..6d7525c2eb --- /dev/null +++ b/_setuptools_vendored/six-1.16.0.dist-info/METADATA @@ -0,0 +1,49 @@ +Metadata-Version: 2.1 +Name: six +Version: 1.16.0 +Summary: Python 2 and 3 compatibility utilities +Home-page: https://github.com/benjaminp/six +Author: Benjamin Peterson +Author-email: benjamin@python.org +License: MIT +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Utilities +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.* + +.. image:: https://img.shields.io/pypi/v/six.svg + :target: https://pypi.org/project/six/ + :alt: six on PyPI + +.. image:: https://travis-ci.org/benjaminp/six.svg?branch=master + :target: https://travis-ci.org/benjaminp/six + :alt: six on TravisCI + +.. image:: https://readthedocs.org/projects/six/badge/?version=latest + :target: https://six.readthedocs.io/ + :alt: six's documentation on Read the Docs + +.. image:: https://img.shields.io/badge/license-MIT-green.svg + :target: https://github.com/benjaminp/six/blob/master/LICENSE + :alt: MIT License badge + +Six is a Python 2 and 3 compatibility library. It provides utility functions +for smoothing over the differences between the Python versions with the goal of +writing Python code that is compatible on both Python versions. See the +documentation for more information on what is provided. + +Six supports Python 2.7 and 3.3+. It is contained in only one Python +file, so it can be easily copied into your project. (The copyright and license +notice must be retained.) + +Online documentation is at https://six.readthedocs.io/. + +Bugs can be reported to https://github.com/benjaminp/six. The code can also +be found there. + + diff --git a/_setuptools_vendored/six-1.16.0.dist-info/RECORD b/_setuptools_vendored/six-1.16.0.dist-info/RECORD new file mode 100644 index 0000000000..8de4af79fa --- /dev/null +++ b/_setuptools_vendored/six-1.16.0.dist-info/RECORD @@ -0,0 +1,6 @@ +six.py,sha256=TOOfQi7nFGfMrIvtdr6wX4wyHH8M7aknmuLfo2cBBrM,34549 +six-1.16.0.dist-info/LICENSE,sha256=i7hQxWWqOJ_cFvOkaWWtI9gq3_YPI5P8J2K2MYXo5sk,1066 +six-1.16.0.dist-info/METADATA,sha256=VQcGIFCAEmfZcl77E5riPCN4v2TIsc_qtacnjxKHJoI,1795 +six-1.16.0.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 +six-1.16.0.dist-info/top_level.txt,sha256=_iVH_iYEtEXnD8nYGQYpYFUvkUW9sEO1GYbkeKSAais,4 +six-1.16.0.dist-info/RECORD,, diff --git a/_setuptools_vendored/six-1.16.0.dist-info/WHEEL b/_setuptools_vendored/six-1.16.0.dist-info/WHEEL new file mode 100644 index 0000000000..01b8fc7d4a --- /dev/null +++ b/_setuptools_vendored/six-1.16.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/_setuptools_vendored/six-1.16.0.dist-info/top_level.txt b/_setuptools_vendored/six-1.16.0.dist-info/top_level.txt new file mode 100644 index 0000000000..ffe2fce498 --- /dev/null +++ b/_setuptools_vendored/six-1.16.0.dist-info/top_level.txt @@ -0,0 +1 @@ +six diff --git a/_setuptools_vendored/six.py b/_setuptools_vendored/six.py new file mode 100644 index 0000000000..4e15675d8b --- /dev/null +++ b/_setuptools_vendored/six.py @@ -0,0 +1,998 @@ +# Copyright (c) 2010-2020 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Utilities for writing code that runs on Python 2 and 3""" + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson " +__version__ = "1.16.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + +if PY34: + from importlib.util import spec_from_loader +else: + spec_from_loader = None + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def find_spec(self, fullname, path, target=None): + if fullname in self.known_modules: + return spec_from_loader(fullname, self) + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("getoutput", "commands", "subprocess"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("splitvalue", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), + MovedAttribute("parse_http_list", "urllib2", "urllib.request"), + MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + del io + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" + _assertNotRegex = "assertNotRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +def assertNotRegex(self, *args, **kwargs): + return getattr(self, _assertNotRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + try: + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None + tb = None + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + try: + raise tp, value, tb + finally: + tb = None +""") + + +if sys.version_info[:2] > (3,): + exec_("""def raise_from(value, from_value): + try: + raise value from from_value + finally: + value = None +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + # This does exactly the same what the :func:`py3:functools.update_wrapper` + # function does on Python versions after 3.2. It sets the ``__wrapped__`` + # attribute on ``wrapper`` object and it doesn't raise an error if any of + # the attributes mentioned in ``assigned`` and ``updated`` are missing on + # ``wrapped`` object. + def _update_wrapper(wrapper, wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + continue + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + wrapper.__wrapped__ = wrapped + return wrapper + _update_wrapper.__doc__ = functools.update_wrapper.__doc__ + + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + return functools.partial(_update_wrapper, wrapped=wrapped, + assigned=assigned, updated=updated) + wraps.__doc__ = functools.wraps.__doc__ + +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + + def __new__(cls, name, this_bases, d): + if sys.version_info[:2] >= (3, 7): + # This version introduced PEP 560 that requires a bit + # of extra care (we mimic what is done by __build_class__). + resolved_bases = types.resolve_bases(bases) + if resolved_bases is not bases: + d['__orig_bases__'] = bases + else: + resolved_bases = bases + return meta(name, resolved_bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + if hasattr(cls, '__qualname__'): + orig_vars['__qualname__'] = cls.__qualname__ + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def ensure_binary(s, encoding='utf-8', errors='strict'): + """Coerce **s** to six.binary_type. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> encoded to `bytes` + - `bytes` -> `bytes` + """ + if isinstance(s, binary_type): + return s + if isinstance(s, text_type): + return s.encode(encoding, errors) + raise TypeError("not expecting type '%s'" % type(s)) + + +def ensure_str(s, encoding='utf-8', errors='strict'): + """Coerce *s* to `str`. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + # Optimization: Fast return for the common case. + if type(s) is str: + return s + if PY2 and isinstance(s, text_type): + return s.encode(encoding, errors) + elif PY3 and isinstance(s, binary_type): + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) + return s + + +def ensure_text(s, encoding='utf-8', errors='strict'): + """Coerce *s* to six.text_type. + + For Python 2: + - `unicode` -> `unicode` + - `str` -> `unicode` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if isinstance(s, binary_type): + return s.decode(encoding, errors) + elif isinstance(s, text_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + +def python_2_unicode_compatible(klass): + """ + A class decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) From ad92a53d450459ad5db0da265663b2bda6e1c639 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 5 Sep 2021 14:26:04 -0400 Subject: [PATCH 3/9] Only include vendored dependencies if necessary. --- _setuptools_vendored/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/_setuptools_vendored/__init__.py b/_setuptools_vendored/__init__.py index 9951c59bd0..afa4abbef0 100644 --- a/_setuptools_vendored/__init__.py +++ b/_setuptools_vendored/__init__.py @@ -3,5 +3,12 @@ """ import sys import pathlib +import importlib -sys.path.append(str(pathlib.Path(__file__).parent)) +try: + # roughly check that dependencies are present + for name in 'packaging appdirs pyparsing ordered_set more_itertools'.split(): + importlib.import_module(name) +except ImportError: + # if any dependendencies appear to be missing, include vendored + sys.path.append(str(pathlib.Path(__file__).parent)) From 6c137ff8d59483b3c62950e638c2ed02d75f4017 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 19 Oct 2021 21:17:13 -0400 Subject: [PATCH 4/9] Skip pip upgrade from source tests on where they're failing. --- setuptools/tests/test_virtualenv.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/setuptools/tests/test_virtualenv.py b/setuptools/tests/test_virtualenv.py index 399dbaf0bb..9740145dc6 100644 --- a/setuptools/tests/test_virtualenv.py +++ b/setuptools/tests/test_virtualenv.py @@ -2,7 +2,6 @@ import os import sys import itertools - import pathlib import pytest @@ -79,10 +78,14 @@ def skip_network(param): reason="pypa/setuptools#2599", ) + issue2764 = pytest.mark.skip( + reason="pypa/setuptools#2764", + ) + network_versions = [ - mark('pip==9.0.3', issue2599), - mark('pip==10.0.1', issue2599), - mark('pip==18.1', issue2599), + mark('pip==9.0.3', issue2599, issue2764), + mark('pip==10.0.1', issue2599, issue2764), + mark('pip==18.1', issue2599, issue2764), mark('pip==19.3.1', pytest.mark.xfail(reason='pypa/pip#6599')), 'pip==20.0.2', 'https://github.com/pypa/pip/archive/main.zip', From 79370b16d68832285ea3e9fbd68e67b1665b1170 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 20 Oct 2021 16:40:13 -0400 Subject: [PATCH 5/9] Add changelog. --- changelog.d/2764.breaking.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2764.breaking.rst diff --git a/changelog.d/2764.breaking.rst b/changelog.d/2764.breaking.rst new file mode 100644 index 0000000000..1d62d56d61 --- /dev/null +++ b/changelog.d/2764.breaking.rst @@ -0,0 +1 @@ +Setuptools now declares its dependencies in metadata but also vendors libraries satisfying those dependencies if they're not present. This change means that Setuptools cannot install from source under pip earlier than 20. From 9152e36ed22e669d4ec29bd0839a69374c89b587 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Oct 2021 10:36:52 -0400 Subject: [PATCH 6/9] Remove wheels. Don't forget to squash-merge this change so the wheels aren't littering the repo. --- .../appdirs-1.4.3-py2.py3-none-any.whl | Bin 12154 -> 0 bytes .../more_itertools-8.8.0-py3-none-any.whl | Bin 48787 -> 0 bytes .../ordered_set-3.1.1-py2.py3-none-any.whl | Bin 7813 -> 0 bytes .../packaging-20.4-py2.py3-none-any.whl | Bin 37620 -> 0 bytes .../pyparsing-2.2.1-py2.py3-none-any.whl | Bin 57557 -> 0 bytes .../six-1.16.0-py2.py3-none-any.whl | Bin 11053 -> 0 bytes 6 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 _setuptools_vendored/appdirs-1.4.3-py2.py3-none-any.whl delete mode 100644 _setuptools_vendored/more_itertools-8.8.0-py3-none-any.whl delete mode 100644 _setuptools_vendored/ordered_set-3.1.1-py2.py3-none-any.whl delete mode 100644 _setuptools_vendored/packaging-20.4-py2.py3-none-any.whl delete mode 100644 _setuptools_vendored/pyparsing-2.2.1-py2.py3-none-any.whl delete mode 100644 _setuptools_vendored/six-1.16.0-py2.py3-none-any.whl diff --git a/_setuptools_vendored/appdirs-1.4.3-py2.py3-none-any.whl b/_setuptools_vendored/appdirs-1.4.3-py2.py3-none-any.whl deleted file mode 100644 index fda65956cad15586feb3f6bf2a5df8d63cd38324..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12154 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 uC;!E1? diff --git a/_setuptools_vendored/more_itertools-8.8.0-py3-none-any.whl b/_setuptools_vendored/more_itertools-8.8.0-py3-none-any.whl deleted file mode 100644 index 6bc13f23b27d2e6edde73f955b3669a8d5306123..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48787 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 z 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�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 diff --git a/_setuptools_vendored/ordered_set-3.1.1-py2.py3-none-any.whl b/_setuptools_vendored/ordered_set-3.1.1-py2.py3-none-any.whl deleted file mode 100644 index e2da8296dde68f342e0e0cbe10148a56ac475806..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7813 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 F{{cwQH>Lmp diff --git a/_setuptools_vendored/packaging-20.4-py2.py3-none-any.whl b/_setuptools_vendored/packaging-20.4-py2.py3-none-any.whl deleted file mode 100644 index 7ae35b72b693f86e66c298d86e4e79260107c5ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37620 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⪚r4#7D{4-ZZS%m)RuFj1ANUVpvE`J~`q8qV$9 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 From 62b5d9e87c906ca8619192da9c09560cb77e2222 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Oct 2021 10:45:45 -0400 Subject: [PATCH 7/9] Unpin dependencies, allowing later versions. --- setup.cfg | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/setup.cfg b/setup.cfg index cbffd75d2e..4469b8ea61 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,13 +26,14 @@ packages = find_namespace: # include_package_data = true python_requires = >=3.6 install_requires = + # pinned because LegacyVersion removed (#2497) packaging==20.4 - pyparsing==2.2.1 - ordered-set==3.1.1 - more_itertools==8.8.0 + pyparsing>=2.2.1 + ordered-set>=3.1.1 + more_itertools>=8.8.0 # for pkg_resources - appdirs==1.4.3 + appdirs>=1.4.3 [options.packages.find] exclude = From 70ecc9a4f1f211dc2a571eda872c79621454c11a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 29 Oct 2021 19:22:46 -0400 Subject: [PATCH 8/9] Simply declare the build dependencies as build dependencies. --- MANIFEST.in | 1 - _setuptools_vendored/__init__.py | 14 - .../appdirs-1.4.3.dist-info/DESCRIPTION.rst | 227 - .../appdirs-1.4.3.dist-info/METADATA | 254 - .../appdirs-1.4.3.dist-info/RECORD | 7 - .../appdirs-1.4.3.dist-info/WHEEL | 6 - .../appdirs-1.4.3.dist-info/metadata.json | 1 - .../appdirs-1.4.3.dist-info/top_level.txt | 1 - _setuptools_vendored/appdirs.py | 608 -- .../more_itertools-8.8.0.dist-info/LICENSE | 19 - .../more_itertools-8.8.0.dist-info/METADATA | 462 -- .../more_itertools-8.8.0.dist-info/RECORD | 12 - .../more_itertools-8.8.0.dist-info/WHEEL | 5 - .../top_level.txt | 1 - .../more_itertools/__init__.py | 4 - .../more_itertools/__init__.pyi | 2 - _setuptools_vendored/more_itertools/more.py | 3825 ----------- _setuptools_vendored/more_itertools/more.pyi | 480 -- _setuptools_vendored/more_itertools/py.typed | 0 .../more_itertools/recipes.py | 620 -- .../more_itertools/recipes.pyi | 103 - .../ordered_set-3.1.1.dist-info/METADATA | 156 - .../ordered_set-3.1.1.dist-info/MIT-LICENSE | 19 - .../ordered_set-3.1.1.dist-info/RECORD | 6 - .../ordered_set-3.1.1.dist-info/WHEEL | 6 - .../ordered_set-3.1.1.dist-info/top_level.txt | 1 - _setuptools_vendored/ordered_set.py | 488 -- .../packaging-20.4.dist-info/LICENSE | 3 - .../packaging-20.4.dist-info/LICENSE.APACHE | 177 - .../packaging-20.4.dist-info/LICENSE.BSD | 23 - .../packaging-20.4.dist-info/METADATA | 373 -- .../packaging-20.4.dist-info/RECORD | 19 - .../packaging-20.4.dist-info/WHEEL | 6 - .../packaging-20.4.dist-info/top_level.txt | 1 - _setuptools_vendored/packaging/__about__.py | 27 - _setuptools_vendored/packaging/__init__.py | 26 - _setuptools_vendored/packaging/_compat.py | 38 - _setuptools_vendored/packaging/_structures.py | 86 - _setuptools_vendored/packaging/_typing.py | 48 - _setuptools_vendored/packaging/markers.py | 328 - _setuptools_vendored/packaging/py.typed | 0 .../packaging/requirements.py | 145 - _setuptools_vendored/packaging/specifiers.py | 863 --- _setuptools_vendored/packaging/tags.py | 751 --- _setuptools_vendored/packaging/utils.py | 65 - _setuptools_vendored/packaging/version.py | 535 -- .../pyparsing-2.2.1.dist-info/DESCRIPTION.rst | 3 - .../pyparsing-2.2.1.dist-info/LICENSE.txt | 18 - .../pyparsing-2.2.1.dist-info/METADATA | 30 - .../pyparsing-2.2.1.dist-info/RECORD | 8 - .../pyparsing-2.2.1.dist-info/WHEEL | 6 - .../pyparsing-2.2.1.dist-info/metadata.json | 1 - .../pyparsing-2.2.1.dist-info/top_level.txt | 1 - _setuptools_vendored/pyparsing.py | 5742 ----------------- .../six-1.16.0.dist-info/LICENSE | 18 - .../six-1.16.0.dist-info/METADATA | 49 - .../six-1.16.0.dist-info/RECORD | 6 - .../six-1.16.0.dist-info/WHEEL | 6 - .../six-1.16.0.dist-info/top_level.txt | 1 - _setuptools_vendored/six.py | 998 --- conftest.py | 1 - pkg_resources/__init__.py | 2 - pyproject.toml | 7 + setuptools/__init__.py | 1 - 64 files changed, 7 insertions(+), 17733 deletions(-) delete mode 100644 _setuptools_vendored/__init__.py delete mode 100644 _setuptools_vendored/appdirs-1.4.3.dist-info/DESCRIPTION.rst delete mode 100644 _setuptools_vendored/appdirs-1.4.3.dist-info/METADATA delete mode 100644 _setuptools_vendored/appdirs-1.4.3.dist-info/RECORD delete mode 100644 _setuptools_vendored/appdirs-1.4.3.dist-info/WHEEL delete mode 100644 _setuptools_vendored/appdirs-1.4.3.dist-info/metadata.json delete mode 100644 _setuptools_vendored/appdirs-1.4.3.dist-info/top_level.txt delete mode 100644 _setuptools_vendored/appdirs.py delete mode 100644 _setuptools_vendored/more_itertools-8.8.0.dist-info/LICENSE delete mode 100644 _setuptools_vendored/more_itertools-8.8.0.dist-info/METADATA delete mode 100644 _setuptools_vendored/more_itertools-8.8.0.dist-info/RECORD delete mode 100644 _setuptools_vendored/more_itertools-8.8.0.dist-info/WHEEL delete mode 100644 _setuptools_vendored/more_itertools-8.8.0.dist-info/top_level.txt delete mode 100644 _setuptools_vendored/more_itertools/__init__.py delete mode 100644 _setuptools_vendored/more_itertools/__init__.pyi delete mode 100644 _setuptools_vendored/more_itertools/more.py delete mode 100644 _setuptools_vendored/more_itertools/more.pyi delete mode 100644 _setuptools_vendored/more_itertools/py.typed delete mode 100644 _setuptools_vendored/more_itertools/recipes.py delete mode 100644 _setuptools_vendored/more_itertools/recipes.pyi delete mode 100644 _setuptools_vendored/ordered_set-3.1.1.dist-info/METADATA delete mode 100644 _setuptools_vendored/ordered_set-3.1.1.dist-info/MIT-LICENSE delete mode 100644 _setuptools_vendored/ordered_set-3.1.1.dist-info/RECORD delete mode 100644 _setuptools_vendored/ordered_set-3.1.1.dist-info/WHEEL delete mode 100644 _setuptools_vendored/ordered_set-3.1.1.dist-info/top_level.txt delete mode 100644 _setuptools_vendored/ordered_set.py delete mode 100644 _setuptools_vendored/packaging-20.4.dist-info/LICENSE delete mode 100644 _setuptools_vendored/packaging-20.4.dist-info/LICENSE.APACHE delete mode 100644 _setuptools_vendored/packaging-20.4.dist-info/LICENSE.BSD delete mode 100644 _setuptools_vendored/packaging-20.4.dist-info/METADATA delete mode 100644 _setuptools_vendored/packaging-20.4.dist-info/RECORD delete mode 100644 _setuptools_vendored/packaging-20.4.dist-info/WHEEL delete mode 100644 _setuptools_vendored/packaging-20.4.dist-info/top_level.txt delete mode 100644 _setuptools_vendored/packaging/__about__.py delete mode 100644 _setuptools_vendored/packaging/__init__.py delete mode 100644 _setuptools_vendored/packaging/_compat.py delete mode 100644 _setuptools_vendored/packaging/_structures.py delete mode 100644 _setuptools_vendored/packaging/_typing.py delete mode 100644 _setuptools_vendored/packaging/markers.py delete mode 100644 _setuptools_vendored/packaging/py.typed delete mode 100644 _setuptools_vendored/packaging/requirements.py delete mode 100644 _setuptools_vendored/packaging/specifiers.py delete mode 100644 _setuptools_vendored/packaging/tags.py delete mode 100644 _setuptools_vendored/packaging/utils.py delete mode 100644 _setuptools_vendored/packaging/version.py delete mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/DESCRIPTION.rst delete mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/LICENSE.txt delete mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/METADATA delete mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/RECORD delete mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/WHEEL delete mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/metadata.json delete mode 100644 _setuptools_vendored/pyparsing-2.2.1.dist-info/top_level.txt delete mode 100644 _setuptools_vendored/pyparsing.py delete mode 100644 _setuptools_vendored/six-1.16.0.dist-info/LICENSE delete mode 100644 _setuptools_vendored/six-1.16.0.dist-info/METADATA delete mode 100644 _setuptools_vendored/six-1.16.0.dist-info/RECORD delete mode 100644 _setuptools_vendored/six-1.16.0.dist-info/WHEEL delete mode 100644 _setuptools_vendored/six-1.16.0.dist-info/top_level.txt delete mode 100644 _setuptools_vendored/six.py diff --git a/MANIFEST.in b/MANIFEST.in index 91bb3dbd88..4feb2b8abc 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,7 +2,6 @@ recursive-include setuptools *.py *.exe *.xml *.tmpl recursive-include tests *.py recursive-include setuptools/tests *.html recursive-include docs *.py *.txt *.rst *.conf *.css *.css_t Makefile indexsidebar.html -recursive-include _setuptools_vendored * recursive-include pkg_resources *.py *.txt recursive-include pkg_resources/tests/data * recursive-include tools * diff --git a/_setuptools_vendored/__init__.py b/_setuptools_vendored/__init__.py deleted file mode 100644 index afa4abbef0..0000000000 --- a/_setuptools_vendored/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -Ensure Setuptools dependencies are present. -""" -import sys -import pathlib -import importlib - -try: - # roughly check that dependencies are present - for name in 'packaging appdirs pyparsing ordered_set more_itertools'.split(): - importlib.import_module(name) -except ImportError: - # if any dependendencies appear to be missing, include vendored - sys.path.append(str(pathlib.Path(__file__).parent)) diff --git a/_setuptools_vendored/appdirs-1.4.3.dist-info/DESCRIPTION.rst b/_setuptools_vendored/appdirs-1.4.3.dist-info/DESCRIPTION.rst deleted file mode 100644 index c605ec2634..0000000000 --- a/_setuptools_vendored/appdirs-1.4.3.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,227 +0,0 @@ - -.. image:: https://secure.travis-ci.org/ActiveState/appdirs.png - :target: http://travis-ci.org/ActiveState/appdirs - -the problem -=========== - -What directory should your app use for storing user data? If running on Mac OS X, you -should use:: - - ~/Library/Application Support/ - -If on Windows (at least English Win XP) that should be:: - - C:\Documents and Settings\\Application Data\Local Settings\\ - -or possibly:: - - C:\Documents and Settings\\Application Data\\ - -for `roaming profiles `_ but that is another story. - -On Linux (and other Unices) the dir, according to the `XDG -spec `_, is:: - - ~/.local/share/ - - -``appdirs`` to the rescue -========================= - -This kind of thing is what the ``appdirs`` module is for. ``appdirs`` will -help you choose an appropriate: - -- user data dir (``user_data_dir``) -- user config dir (``user_config_dir``) -- user cache dir (``user_cache_dir``) -- site data dir (``site_data_dir``) -- site config dir (``site_config_dir``) -- user log dir (``user_log_dir``) - -and also: - -- is a single module so other Python packages can include their own private copy -- is slightly opinionated on the directory names used. Look for "OPINION" in - documentation and code for when an opinion is being applied. - - -some example output -=================== - -On Mac OS X:: - - >>> from appdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - '/Users/trentm/Library/Application Support/SuperApp' - >>> site_data_dir(appname, appauthor) - '/Library/Application Support/SuperApp' - >>> user_cache_dir(appname, appauthor) - '/Users/trentm/Library/Caches/SuperApp' - >>> user_log_dir(appname, appauthor) - '/Users/trentm/Library/Logs/SuperApp' - -On Windows 7:: - - >>> from appdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' - >>> user_data_dir(appname, appauthor, roaming=True) - 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp' - >>> user_cache_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache' - >>> user_log_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs' - -On Linux:: - - >>> from appdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - '/home/trentm/.local/share/SuperApp - >>> site_data_dir(appname, appauthor) - '/usr/local/share/SuperApp' - >>> site_data_dir(appname, appauthor, multipath=True) - '/usr/local/share/SuperApp:/usr/share/SuperApp' - >>> user_cache_dir(appname, appauthor) - '/home/trentm/.cache/SuperApp' - >>> user_log_dir(appname, appauthor) - '/home/trentm/.cache/SuperApp/log' - >>> user_config_dir(appname) - '/home/trentm/.config/SuperApp' - >>> site_config_dir(appname) - '/etc/xdg/SuperApp' - >>> os.environ['XDG_CONFIG_DIRS'] = '/etc:/usr/local/etc' - >>> site_config_dir(appname, multipath=True) - '/etc/SuperApp:/usr/local/etc/SuperApp' - - -``AppDirs`` for convenience -=========================== - -:: - - >>> from appdirs import AppDirs - >>> dirs = AppDirs("SuperApp", "Acme") - >>> dirs.user_data_dir - '/Users/trentm/Library/Application Support/SuperApp' - >>> dirs.site_data_dir - '/Library/Application Support/SuperApp' - >>> dirs.user_cache_dir - '/Users/trentm/Library/Caches/SuperApp' - >>> dirs.user_log_dir - '/Users/trentm/Library/Logs/SuperApp' - - - -Per-version isolation -===================== - -If you have multiple versions of your app in use that you want to be -able to run side-by-side, then you may want version-isolation for these -dirs:: - - >>> from appdirs import AppDirs - >>> dirs = AppDirs("SuperApp", "Acme", version="1.0") - >>> dirs.user_data_dir - '/Users/trentm/Library/Application Support/SuperApp/1.0' - >>> dirs.site_data_dir - '/Library/Application Support/SuperApp/1.0' - >>> dirs.user_cache_dir - '/Users/trentm/Library/Caches/SuperApp/1.0' - >>> dirs.user_log_dir - '/Users/trentm/Library/Logs/SuperApp/1.0' - - - -appdirs Changelog -================= - -appdirs 1.4.3 -------------- -- [PR #76] Python 3.6 invalid escape sequence deprecation fixes -- Fix for Python 3.6 support - -appdirs 1.4.2 -------------- -- [PR #84] Allow installing without setuptools -- [PR #86] Fix string delimiters in setup.py description -- Add Python 3.6 support - -appdirs 1.4.1 -------------- -- [issue #38] Fix _winreg import on Windows Py3 -- [issue #55] Make appname optional - -appdirs 1.4.0 -------------- -- [PR #42] AppAuthor is now optional on Windows -- [issue 41] Support Jython on Windows, Mac, and Unix-like platforms. Windows - support requires `JNA `_. -- [PR #44] Fix incorrect behaviour of the site_config_dir method - -appdirs 1.3.0 -------------- -- [Unix, issue 16] Conform to XDG standard, instead of breaking it for - everybody -- [Unix] Removes gratuitous case mangling of the case, since \*nix-es are - usually case sensitive, so mangling is not wise -- [Unix] Fixes the utterly wrong behaviour in ``site_data_dir``, return result - based on XDG_DATA_DIRS and make room for respecting the standard which - specifies XDG_DATA_DIRS is a multiple-value variable -- [Issue 6] Add ``*_config_dir`` which are distinct on nix-es, according to - XDG specs; on Windows and Mac return the corresponding ``*_data_dir`` - -appdirs 1.2.0 -------------- - -- [Unix] Put ``user_log_dir`` under the *cache* dir on Unix. Seems to be more - typical. -- [issue 9] Make ``unicode`` work on py3k. - -appdirs 1.1.0 -------------- - -- [issue 4] Add ``AppDirs.user_log_dir``. -- [Unix, issue 2, issue 7] appdirs now conforms to `XDG base directory spec - `_. -- [Mac, issue 5] Fix ``site_data_dir()`` on Mac. -- [Mac] Drop use of 'Carbon' module in favour of hardcoded paths; supports - Python3 now. -- [Windows] Append "Cache" to ``user_cache_dir`` on Windows by default. Use - ``opinion=False`` option to disable this. -- Add ``appdirs.AppDirs`` convenience class. Usage: - - >>> dirs = AppDirs("SuperApp", "Acme", version="1.0") - >>> dirs.user_data_dir - '/Users/trentm/Library/Application Support/SuperApp/1.0' - -- [Windows] Cherry-pick Komodo's change to downgrade paths to the Windows short - paths if there are high bit chars. -- [Linux] Change default ``user_cache_dir()`` on Linux to be singular, e.g. - "~/.superapp/cache". -- [Windows] Add ``roaming`` option to ``user_data_dir()`` (for use on Windows only) - and change the default ``user_data_dir`` behaviour to use a *non*-roaming - profile dir (``CSIDL_LOCAL_APPDATA`` instead of ``CSIDL_APPDATA``). Why? Because - a large roaming profile can cause login speed issues. The "only syncs on - logout" behaviour can cause surprises in appdata info. - - -appdirs 1.0.1 (never released) ------------------------------- - -Started this changelog 27 July 2010. Before that this module originated in the -`Komodo `_ product as ``applib.py`` and then -as `applib/location.py -`_ (used by -`PyPM `_ in `ActivePython -`_). This is basically a fork of -applib.py 1.0.1 and applib/location.py 1.0.1. - - - diff --git a/_setuptools_vendored/appdirs-1.4.3.dist-info/METADATA b/_setuptools_vendored/appdirs-1.4.3.dist-info/METADATA deleted file mode 100644 index 69ddf93459..0000000000 --- a/_setuptools_vendored/appdirs-1.4.3.dist-info/METADATA +++ /dev/null @@ -1,254 +0,0 @@ -Metadata-Version: 2.0 -Name: appdirs -Version: 1.4.3 -Summary: A small Python module for determining appropriate platform-specific dirs, e.g. a "user data dir". -Home-page: http://github.com/ActiveState/appdirs -Author: Trent Mick; Sridhar Ratnakumar; Jeff Rouse -Author-email: trentm@gmail.com; github@srid.name; jr@its.to -License: MIT -Keywords: application directory log cache user -Platform: UNKNOWN -Classifier: Development Status :: 4 - Beta -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.2 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Topic :: Software Development :: Libraries :: Python Modules - - -.. image:: https://secure.travis-ci.org/ActiveState/appdirs.png - :target: http://travis-ci.org/ActiveState/appdirs - -the problem -=========== - -What directory should your app use for storing user data? If running on Mac OS X, you -should use:: - - ~/Library/Application Support/ - -If on Windows (at least English Win XP) that should be:: - - C:\Documents and Settings\\Application Data\Local Settings\\ - -or possibly:: - - C:\Documents and Settings\\Application Data\\ - -for `roaming profiles `_ but that is another story. - -On Linux (and other Unices) the dir, according to the `XDG -spec `_, is:: - - ~/.local/share/ - - -``appdirs`` to the rescue -========================= - -This kind of thing is what the ``appdirs`` module is for. ``appdirs`` will -help you choose an appropriate: - -- user data dir (``user_data_dir``) -- user config dir (``user_config_dir``) -- user cache dir (``user_cache_dir``) -- site data dir (``site_data_dir``) -- site config dir (``site_config_dir``) -- user log dir (``user_log_dir``) - -and also: - -- is a single module so other Python packages can include their own private copy -- is slightly opinionated on the directory names used. Look for "OPINION" in - documentation and code for when an opinion is being applied. - - -some example output -=================== - -On Mac OS X:: - - >>> from appdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - '/Users/trentm/Library/Application Support/SuperApp' - >>> site_data_dir(appname, appauthor) - '/Library/Application Support/SuperApp' - >>> user_cache_dir(appname, appauthor) - '/Users/trentm/Library/Caches/SuperApp' - >>> user_log_dir(appname, appauthor) - '/Users/trentm/Library/Logs/SuperApp' - -On Windows 7:: - - >>> from appdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' - >>> user_data_dir(appname, appauthor, roaming=True) - 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp' - >>> user_cache_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache' - >>> user_log_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs' - -On Linux:: - - >>> from appdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - '/home/trentm/.local/share/SuperApp - >>> site_data_dir(appname, appauthor) - '/usr/local/share/SuperApp' - >>> site_data_dir(appname, appauthor, multipath=True) - '/usr/local/share/SuperApp:/usr/share/SuperApp' - >>> user_cache_dir(appname, appauthor) - '/home/trentm/.cache/SuperApp' - >>> user_log_dir(appname, appauthor) - '/home/trentm/.cache/SuperApp/log' - >>> user_config_dir(appname) - '/home/trentm/.config/SuperApp' - >>> site_config_dir(appname) - '/etc/xdg/SuperApp' - >>> os.environ['XDG_CONFIG_DIRS'] = '/etc:/usr/local/etc' - >>> site_config_dir(appname, multipath=True) - '/etc/SuperApp:/usr/local/etc/SuperApp' - - -``AppDirs`` for convenience -=========================== - -:: - - >>> from appdirs import AppDirs - >>> dirs = AppDirs("SuperApp", "Acme") - >>> dirs.user_data_dir - '/Users/trentm/Library/Application Support/SuperApp' - >>> dirs.site_data_dir - '/Library/Application Support/SuperApp' - >>> dirs.user_cache_dir - '/Users/trentm/Library/Caches/SuperApp' - >>> dirs.user_log_dir - '/Users/trentm/Library/Logs/SuperApp' - - - -Per-version isolation -===================== - -If you have multiple versions of your app in use that you want to be -able to run side-by-side, then you may want version-isolation for these -dirs:: - - >>> from appdirs import AppDirs - >>> dirs = AppDirs("SuperApp", "Acme", version="1.0") - >>> dirs.user_data_dir - '/Users/trentm/Library/Application Support/SuperApp/1.0' - >>> dirs.site_data_dir - '/Library/Application Support/SuperApp/1.0' - >>> dirs.user_cache_dir - '/Users/trentm/Library/Caches/SuperApp/1.0' - >>> dirs.user_log_dir - '/Users/trentm/Library/Logs/SuperApp/1.0' - - - -appdirs Changelog -================= - -appdirs 1.4.3 -------------- -- [PR #76] Python 3.6 invalid escape sequence deprecation fixes -- Fix for Python 3.6 support - -appdirs 1.4.2 -------------- -- [PR #84] Allow installing without setuptools -- [PR #86] Fix string delimiters in setup.py description -- Add Python 3.6 support - -appdirs 1.4.1 -------------- -- [issue #38] Fix _winreg import on Windows Py3 -- [issue #55] Make appname optional - -appdirs 1.4.0 -------------- -- [PR #42] AppAuthor is now optional on Windows -- [issue 41] Support Jython on Windows, Mac, and Unix-like platforms. Windows - support requires `JNA `_. -- [PR #44] Fix incorrect behaviour of the site_config_dir method - -appdirs 1.3.0 -------------- -- [Unix, issue 16] Conform to XDG standard, instead of breaking it for - everybody -- [Unix] Removes gratuitous case mangling of the case, since \*nix-es are - usually case sensitive, so mangling is not wise -- [Unix] Fixes the utterly wrong behaviour in ``site_data_dir``, return result - based on XDG_DATA_DIRS and make room for respecting the standard which - specifies XDG_DATA_DIRS is a multiple-value variable -- [Issue 6] Add ``*_config_dir`` which are distinct on nix-es, according to - XDG specs; on Windows and Mac return the corresponding ``*_data_dir`` - -appdirs 1.2.0 -------------- - -- [Unix] Put ``user_log_dir`` under the *cache* dir on Unix. Seems to be more - typical. -- [issue 9] Make ``unicode`` work on py3k. - -appdirs 1.1.0 -------------- - -- [issue 4] Add ``AppDirs.user_log_dir``. -- [Unix, issue 2, issue 7] appdirs now conforms to `XDG base directory spec - `_. -- [Mac, issue 5] Fix ``site_data_dir()`` on Mac. -- [Mac] Drop use of 'Carbon' module in favour of hardcoded paths; supports - Python3 now. -- [Windows] Append "Cache" to ``user_cache_dir`` on Windows by default. Use - ``opinion=False`` option to disable this. -- Add ``appdirs.AppDirs`` convenience class. Usage: - - >>> dirs = AppDirs("SuperApp", "Acme", version="1.0") - >>> dirs.user_data_dir - '/Users/trentm/Library/Application Support/SuperApp/1.0' - -- [Windows] Cherry-pick Komodo's change to downgrade paths to the Windows short - paths if there are high bit chars. -- [Linux] Change default ``user_cache_dir()`` on Linux to be singular, e.g. - "~/.superapp/cache". -- [Windows] Add ``roaming`` option to ``user_data_dir()`` (for use on Windows only) - and change the default ``user_data_dir`` behaviour to use a *non*-roaming - profile dir (``CSIDL_LOCAL_APPDATA`` instead of ``CSIDL_APPDATA``). Why? Because - a large roaming profile can cause login speed issues. The "only syncs on - logout" behaviour can cause surprises in appdata info. - - -appdirs 1.0.1 (never released) ------------------------------- - -Started this changelog 27 July 2010. Before that this module originated in the -`Komodo `_ product as ``applib.py`` and then -as `applib/location.py -`_ (used by -`PyPM `_ in `ActivePython -`_). This is basically a fork of -applib.py 1.0.1 and applib/location.py 1.0.1. - - - diff --git a/_setuptools_vendored/appdirs-1.4.3.dist-info/RECORD b/_setuptools_vendored/appdirs-1.4.3.dist-info/RECORD deleted file mode 100644 index 117115b941..0000000000 --- a/_setuptools_vendored/appdirs-1.4.3.dist-info/RECORD +++ /dev/null @@ -1,7 +0,0 @@ -appdirs.py,sha256=MievUEuv3l_mQISH5SF0shDk_BNhHHzYiAPrT3ITN4I,24701 -appdirs-1.4.3.dist-info/DESCRIPTION.rst,sha256=77Fe8OIOLSjDSNdLiL5xywMKO-AGE42rdXkqKo4Ee-k,7531 -appdirs-1.4.3.dist-info/METADATA,sha256=3IFw6jTfImdOqsCb2GYvVR157tL7KEzfRAszn382csk,8773 -appdirs-1.4.3.dist-info/RECORD,, -appdirs-1.4.3.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 -appdirs-1.4.3.dist-info/metadata.json,sha256=fL_Q-GuFJu3PJxMrwU7SdsI8RGqjIfi2AvouCSF5DSA,1359 -appdirs-1.4.3.dist-info/top_level.txt,sha256=nKncE8CUqZERJ6VuQWL4_bkunSPDNfn7KZqb4Tr5YEM,8 diff --git a/_setuptools_vendored/appdirs-1.4.3.dist-info/WHEEL b/_setuptools_vendored/appdirs-1.4.3.dist-info/WHEEL deleted file mode 100644 index 8b6dd1b5a8..0000000000 --- a/_setuptools_vendored/appdirs-1.4.3.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.29.0) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/_setuptools_vendored/appdirs-1.4.3.dist-info/metadata.json b/_setuptools_vendored/appdirs-1.4.3.dist-info/metadata.json deleted file mode 100644 index da1e5f3a7e..0000000000 --- a/_setuptools_vendored/appdirs-1.4.3.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Software Development :: Libraries :: Python Modules"], "extensions": {"python.details": {"contacts": [{"email": "trentm@gmail.com; github@srid.name; jr@its.to", "name": "Trent Mick; Sridhar Ratnakumar; Jeff Rouse", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/ActiveState/appdirs"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["application", "directory", "log", "cache", "user"], "license": "MIT", "metadata_version": "2.0", "name": "appdirs", "summary": "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\".", "test_requires": [{"requires": []}], "version": "1.4.3"} \ No newline at end of file diff --git a/_setuptools_vendored/appdirs-1.4.3.dist-info/top_level.txt b/_setuptools_vendored/appdirs-1.4.3.dist-info/top_level.txt deleted file mode 100644 index d64bc321a1..0000000000 --- a/_setuptools_vendored/appdirs-1.4.3.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -appdirs diff --git a/_setuptools_vendored/appdirs.py b/_setuptools_vendored/appdirs.py deleted file mode 100644 index ae67001af8..0000000000 --- a/_setuptools_vendored/appdirs.py +++ /dev/null @@ -1,608 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (c) 2005-2010 ActiveState Software Inc. -# Copyright (c) 2013 Eddy PetriÈ™or - -"""Utilities for determining application-specific dirs. - -See for details and usage. -""" -# Dev Notes: -# - MSDN on where to store app data files: -# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 -# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html -# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html - -__version_info__ = (1, 4, 3) -__version__ = '.'.join(map(str, __version_info__)) - - -import sys -import os - -PY3 = sys.version_info[0] == 3 - -if PY3: - unicode = str - -if sys.platform.startswith('java'): - import platform - os_name = platform.java_ver()[3][0] - if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. - system = 'win32' - elif os_name.startswith('Mac'): # "Mac OS X", etc. - system = 'darwin' - else: # "Linux", "SunOS", "FreeBSD", etc. - # Setting this to "linux2" is not ideal, but only Windows or Mac - # are actually checked for and the rest of the module expects - # *sys.platform* style strings. - system = 'linux2' -else: - system = sys.platform - - - -def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): - r"""Return full path to the user-specific data dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "roaming" (boolean, default False) can be set True to use the Windows - roaming appdata directory. That means that for users on a Windows - network setup for roaming profiles, this user data will be - sync'd on login. See - - for a discussion of issues. - - Typical user data directories are: - Mac OS X: ~/Library/Application Support/ - Unix: ~/.local/share/ # or in $XDG_DATA_HOME, if defined - Win XP (not roaming): C:\Documents and Settings\\Application Data\\ - Win XP (roaming): C:\Documents and Settings\\Local Settings\Application Data\\ - Win 7 (not roaming): C:\Users\\AppData\Local\\ - Win 7 (roaming): C:\Users\\AppData\Roaming\\ - - For Unix, we follow the XDG spec and support $XDG_DATA_HOME. - That means, by default "~/.local/share/". - """ - if system == "win32": - if appauthor is None: - appauthor = appname - const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" - path = os.path.normpath(_get_win_folder(const)) - if appname: - if appauthor is not False: - path = os.path.join(path, appauthor, appname) - else: - path = os.path.join(path, appname) - elif system == 'darwin': - path = os.path.expanduser('~/Library/Application Support/') - if appname: - path = os.path.join(path, appname) - else: - path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): - r"""Return full path to the user-shared data dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "multipath" is an optional parameter only applicable to *nix - which indicates that the entire list of data dirs should be - returned. By default, the first item from XDG_DATA_DIRS is - returned, or '/usr/local/share/', - if XDG_DATA_DIRS is not set - - Typical site data directories are: - Mac OS X: /Library/Application Support/ - Unix: /usr/local/share/ or /usr/share/ - Win XP: C:\Documents and Settings\All Users\Application Data\\ - Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) - Win 7: C:\ProgramData\\ # Hidden, but writeable on Win 7. - - For Unix, this is using the $XDG_DATA_DIRS[0] default. - - WARNING: Do not use this on Windows. See the Vista-Fail note above for why. - """ - if system == "win32": - if appauthor is None: - appauthor = appname - path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) - if appname: - if appauthor is not False: - path = os.path.join(path, appauthor, appname) - else: - path = os.path.join(path, appname) - elif system == 'darwin': - path = os.path.expanduser('/Library/Application Support') - if appname: - path = os.path.join(path, appname) - else: - # XDG default for $XDG_DATA_DIRS - # only first, if multipath is False - path = os.getenv('XDG_DATA_DIRS', - os.pathsep.join(['/usr/local/share', '/usr/share'])) - pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] - if appname: - if version: - appname = os.path.join(appname, version) - pathlist = [os.sep.join([x, appname]) for x in pathlist] - - if multipath: - path = os.pathsep.join(pathlist) - else: - path = pathlist[0] - return path - - if appname and version: - path = os.path.join(path, version) - return path - - -def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): - r"""Return full path to the user-specific config dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "roaming" (boolean, default False) can be set True to use the Windows - roaming appdata directory. That means that for users on a Windows - network setup for roaming profiles, this user data will be - sync'd on login. See - - for a discussion of issues. - - Typical user config directories are: - Mac OS X: same as user_data_dir - Unix: ~/.config/ # or in $XDG_CONFIG_HOME, if defined - Win *: same as user_data_dir - - For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. - That means, by default "~/.config/". - """ - if system in ["win32", "darwin"]: - path = user_data_dir(appname, appauthor, None, roaming) - else: - path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): - r"""Return full path to the user-shared data dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "multipath" is an optional parameter only applicable to *nix - which indicates that the entire list of config dirs should be - returned. By default, the first item from XDG_CONFIG_DIRS is - returned, or '/etc/xdg/', if XDG_CONFIG_DIRS is not set - - Typical site config directories are: - Mac OS X: same as site_data_dir - Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ for each value in - $XDG_CONFIG_DIRS - Win *: same as site_data_dir - Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) - - For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False - - WARNING: Do not use this on Windows. See the Vista-Fail note above for why. - """ - if system in ["win32", "darwin"]: - path = site_data_dir(appname, appauthor) - if appname and version: - path = os.path.join(path, version) - else: - # XDG default for $XDG_CONFIG_DIRS - # only first, if multipath is False - path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') - pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] - if appname: - if version: - appname = os.path.join(appname, version) - pathlist = [os.sep.join([x, appname]) for x in pathlist] - - if multipath: - path = os.pathsep.join(pathlist) - else: - path = pathlist[0] - return path - - -def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): - r"""Return full path to the user-specific cache dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "opinion" (boolean) can be False to disable the appending of - "Cache" to the base app data dir for Windows. See - discussion below. - - Typical user cache directories are: - Mac OS X: ~/Library/Caches/ - Unix: ~/.cache/ (XDG default) - Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache - Vista: C:\Users\\AppData\Local\\\Cache - - On Windows the only suggestion in the MSDN docs is that local settings go in - the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming - app data dir (the default returned by `user_data_dir` above). Apps typically - put cache data somewhere *under* the given dir here. Some examples: - ...\Mozilla\Firefox\Profiles\\Cache - ...\Acme\SuperApp\Cache\1.0 - OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. - This can be disabled with the `opinion=False` option. - """ - if system == "win32": - if appauthor is None: - appauthor = appname - path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) - if appname: - if appauthor is not False: - path = os.path.join(path, appauthor, appname) - else: - path = os.path.join(path, appname) - if opinion: - path = os.path.join(path, "Cache") - elif system == 'darwin': - path = os.path.expanduser('~/Library/Caches') - if appname: - path = os.path.join(path, appname) - else: - path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): - r"""Return full path to the user-specific state dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "roaming" (boolean, default False) can be set True to use the Windows - roaming appdata directory. That means that for users on a Windows - network setup for roaming profiles, this user data will be - sync'd on login. See - - for a discussion of issues. - - Typical user state directories are: - Mac OS X: same as user_data_dir - Unix: ~/.local/state/ # or in $XDG_STATE_HOME, if defined - Win *: same as user_data_dir - - For Unix, we follow this Debian proposal - to extend the XDG spec and support $XDG_STATE_HOME. - - That means, by default "~/.local/state/". - """ - if system in ["win32", "darwin"]: - path = user_data_dir(appname, appauthor, None, roaming) - else: - path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state")) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): - r"""Return full path to the user-specific log dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "opinion" (boolean) can be False to disable the appending of - "Logs" to the base app data dir for Windows, and "log" to the - base cache dir for Unix. See discussion below. - - Typical user log directories are: - Mac OS X: ~/Library/Logs/ - Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined - Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs - Vista: C:\Users\\AppData\Local\\\Logs - - On Windows the only suggestion in the MSDN docs is that local settings - go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in - examples of what some windows apps use for a logs dir.) - - OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` - value for Windows and appends "log" to the user cache dir for Unix. - This can be disabled with the `opinion=False` option. - """ - if system == "darwin": - path = os.path.join( - os.path.expanduser('~/Library/Logs'), - appname) - elif system == "win32": - path = user_data_dir(appname, appauthor, version) - version = False - if opinion: - path = os.path.join(path, "Logs") - else: - path = user_cache_dir(appname, appauthor, version) - version = False - if opinion: - path = os.path.join(path, "log") - if appname and version: - path = os.path.join(path, version) - return path - - -class AppDirs(object): - """Convenience wrapper for getting application dirs.""" - def __init__(self, appname=None, appauthor=None, version=None, - roaming=False, multipath=False): - self.appname = appname - self.appauthor = appauthor - self.version = version - self.roaming = roaming - self.multipath = multipath - - @property - def user_data_dir(self): - return user_data_dir(self.appname, self.appauthor, - version=self.version, roaming=self.roaming) - - @property - def site_data_dir(self): - return site_data_dir(self.appname, self.appauthor, - version=self.version, multipath=self.multipath) - - @property - def user_config_dir(self): - return user_config_dir(self.appname, self.appauthor, - version=self.version, roaming=self.roaming) - - @property - def site_config_dir(self): - return site_config_dir(self.appname, self.appauthor, - version=self.version, multipath=self.multipath) - - @property - def user_cache_dir(self): - return user_cache_dir(self.appname, self.appauthor, - version=self.version) - - @property - def user_state_dir(self): - return user_state_dir(self.appname, self.appauthor, - version=self.version) - - @property - def user_log_dir(self): - return user_log_dir(self.appname, self.appauthor, - version=self.version) - - -#---- internal support stuff - -def _get_win_folder_from_registry(csidl_name): - """This is a fallback technique at best. I'm not sure if using the - registry for this guarantees us the correct answer for all CSIDL_* - names. - """ - if PY3: - import winreg as _winreg - else: - import _winreg - - shell_folder_name = { - "CSIDL_APPDATA": "AppData", - "CSIDL_COMMON_APPDATA": "Common AppData", - "CSIDL_LOCAL_APPDATA": "Local AppData", - }[csidl_name] - - key = _winreg.OpenKey( - _winreg.HKEY_CURRENT_USER, - r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" - ) - dir, type = _winreg.QueryValueEx(key, shell_folder_name) - return dir - - -def _get_win_folder_with_pywin32(csidl_name): - from win32com.shell import shellcon, shell - dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) - # Try to make this a unicode path because SHGetFolderPath does - # not return unicode strings when there is unicode data in the - # path. - try: - dir = unicode(dir) - - # Downgrade to short path name if have highbit chars. See - # . - has_high_char = False - for c in dir: - if ord(c) > 255: - has_high_char = True - break - if has_high_char: - try: - import win32api - dir = win32api.GetShortPathName(dir) - except ImportError: - pass - except UnicodeError: - pass - return dir - - -def _get_win_folder_with_ctypes(csidl_name): - import ctypes - - csidl_const = { - "CSIDL_APPDATA": 26, - "CSIDL_COMMON_APPDATA": 35, - "CSIDL_LOCAL_APPDATA": 28, - }[csidl_name] - - buf = ctypes.create_unicode_buffer(1024) - ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) - - # Downgrade to short path name if have highbit chars. See - # . - has_high_char = False - for c in buf: - if ord(c) > 255: - has_high_char = True - break - if has_high_char: - buf2 = ctypes.create_unicode_buffer(1024) - if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): - buf = buf2 - - return buf.value - -def _get_win_folder_with_jna(csidl_name): - import array - from com.sun import jna - from com.sun.jna.platform import win32 - - buf_size = win32.WinDef.MAX_PATH * 2 - buf = array.zeros('c', buf_size) - shell = win32.Shell32.INSTANCE - shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) - dir = jna.Native.toString(buf.tostring()).rstrip("\0") - - # Downgrade to short path name if have highbit chars. See - # . - has_high_char = False - for c in dir: - if ord(c) > 255: - has_high_char = True - break - if has_high_char: - buf = array.zeros('c', buf_size) - kernel = win32.Kernel32.INSTANCE - if kernel.GetShortPathName(dir, buf, buf_size): - dir = jna.Native.toString(buf.tostring()).rstrip("\0") - - return dir - -if system == "win32": - try: - import win32com.shell - _get_win_folder = _get_win_folder_with_pywin32 - except ImportError: - try: - from ctypes import windll - _get_win_folder = _get_win_folder_with_ctypes - except ImportError: - try: - import com.sun.jna - _get_win_folder = _get_win_folder_with_jna - except ImportError: - _get_win_folder = _get_win_folder_from_registry - - -#---- self test code - -if __name__ == "__main__": - appname = "MyApp" - appauthor = "MyCompany" - - props = ("user_data_dir", - "user_config_dir", - "user_cache_dir", - "user_state_dir", - "user_log_dir", - "site_data_dir", - "site_config_dir") - - print("-- app dirs %s --" % __version__) - - print("-- app dirs (with optional 'version')") - dirs = AppDirs(appname, appauthor, version="1.0") - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) - - print("\n-- app dirs (without optional 'version')") - dirs = AppDirs(appname, appauthor) - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) - - print("\n-- app dirs (without optional 'appauthor')") - dirs = AppDirs(appname) - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) - - print("\n-- app dirs (with disabled 'appauthor')") - dirs = AppDirs(appname, appauthor=False) - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/_setuptools_vendored/more_itertools-8.8.0.dist-info/LICENSE b/_setuptools_vendored/more_itertools-8.8.0.dist-info/LICENSE deleted file mode 100644 index 0a523bece3..0000000000 --- a/_setuptools_vendored/more_itertools-8.8.0.dist-info/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2012 Erik Rose - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/_setuptools_vendored/more_itertools-8.8.0.dist-info/METADATA b/_setuptools_vendored/more_itertools-8.8.0.dist-info/METADATA deleted file mode 100644 index bdaee6553f..0000000000 --- a/_setuptools_vendored/more_itertools-8.8.0.dist-info/METADATA +++ /dev/null @@ -1,462 +0,0 @@ -Metadata-Version: 2.1 -Name: more-itertools -Version: 8.8.0 -Summary: More routines for operating on iterables, beyond itertools -Home-page: https://github.com/more-itertools/more-itertools -Author: Erik Rose -Author-email: erikrose@grinchcentral.com -License: MIT -Keywords: itertools,iterator,iteration,filter,peek,peekable,collate,chunk,chunked -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Natural Language :: English -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Software Development :: Libraries -Requires-Python: >=3.5 -Description-Content-Type: text/x-rst - -============== -More Itertools -============== - -.. image:: https://readthedocs.org/projects/more-itertools/badge/?version=latest - :target: https://more-itertools.readthedocs.io/en/stable/ - -Python's ``itertools`` library is a gem - you can compose elegant solutions -for a variety of problems with the functions it provides. In ``more-itertools`` -we collect additional building blocks, recipes, and routines for working with -Python iterables. - -+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Grouping | `chunked `_, | -| | `ichunked `_, | -| | `sliced `_, | -| | `distribute `_, | -| | `divide `_, | -| | `split_at `_, | -| | `split_before `_, | -| | `split_after `_, | -| | `split_into `_, | -| | `split_when `_, | -| | `bucket `_, | -| | `unzip `_, | -| | `grouper `_, | -| | `partition `_ | -+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Lookahead and lookback | `spy `_, | -| | `peekable `_, | -| | `seekable `_ | -+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Windowing | `windowed `_, | -| | `substrings `_, | -| | `substrings_indexes `_, | -| | `stagger `_, | -| | `windowed_complete `_, | -| | `pairwise `_ | -+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Augmenting | `count_cycle `_, | -| | `intersperse `_, | -| | `padded `_, | -| | `mark_ends `_, | -| | `repeat_last `_, | -| | `adjacent `_, | -| | `groupby_transform `_, | -| | `padnone `_, | -| | `ncycles `_ | -+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Combining | `collapse `_, | -| | `sort_together `_, | -| | `interleave `_, | -| | `interleave_longest `_, | -| | `zip_offset `_, | -| | `zip_equal `_, | -| | `dotproduct `_, | -| | `convolve `_, | -| | `flatten `_, | -| | `roundrobin `_, | -| | `prepend `_, | -| | `value_chain `_ | -+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Summarizing | `ilen `_, | -| | `unique_to_each `_, | -| | `sample `_, | -| | `consecutive_groups `_, | -| | `run_length `_, | -| | `map_reduce `_, | -| | `exactly_n `_, | -| | `is_sorted `_, | -| | `all_equal `_, | -| | `all_unique `_, | -| | `first_true `_, | -| | `quantify `_ | -+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Selecting | `islice_extended `_, | -| | `first `_, | -| | `last `_, | -| | `one `_, | -| | `only `_, | -| | `strip `_, | -| | `lstrip `_, | -| | `rstrip `_, | -| | `filter_except `_ | -| | `map_except `_ | -| | `nth_or_last `_, | -| | `nth `_, | -| | `take `_, | -| | `tail `_, | -| | `unique_everseen `_, | -| | `unique_justseen `_ | -+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Combinatorics | `distinct_permutations `_, | -| | `distinct_combinations `_, | -| | `circular_shifts `_, | -| | `partitions `_, | -| | `set_partitions `_, | -| | `product_index `_, | -| | `combination_index `_, | -| | `permutation_index `_, | -| | `powerset `_, | -| | `random_product `_, | -| | `random_permutation `_, | -| | `random_combination `_, | -| | `random_combination_with_replacement `_, | -| | `nth_product `_ | -| | `nth_permutation `_ | -| | `nth_combination `_ | -+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Wrapping | `always_iterable `_, | -| | `always_reversible `_, | -| | `countable `_, | -| | `consumer `_, | -| | `with_iter `_, | -| | `iter_except `_ | -+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Others | `locate `_, | -| | `rlocate `_, | -| | `replace `_, | -| | `numeric_range `_, | -| | `side_effect `_, | -| | `iterate `_, | -| | `difference `_, | -| | `make_decorator `_, | -| | `SequenceView `_, | -| | `time_limited `_, | -| | `consume `_, | -| | `tabulate `_, | -| | `repeatfunc `_ | -+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - - -Getting started -=============== - -To get started, install the library with `pip `_: - -.. code-block:: shell - - pip install more-itertools - -The recipes from the `itertools docs `_ -are included in the top-level package: - -.. code-block:: python - - >>> from more_itertools import flatten - >>> iterable = [(0, 1), (2, 3)] - >>> list(flatten(iterable)) - [0, 1, 2, 3] - -Several new recipes are available as well: - -.. code-block:: python - - >>> from more_itertools import chunked - >>> iterable = [0, 1, 2, 3, 4, 5, 6, 7, 8] - >>> list(chunked(iterable, 3)) - [[0, 1, 2], [3, 4, 5], [6, 7, 8]] - - >>> from more_itertools import spy - >>> iterable = (x * x for x in range(1, 6)) - >>> head, iterable = spy(iterable, n=3) - >>> list(head) - [1, 4, 9] - >>> list(iterable) - [1, 4, 9, 16, 25] - - - -For the full listing of functions, see the `API documentation `_. - - -Links elsewhere -=============== - -Blog posts about ``more-itertools``: - -* `Yo, I heard you like decorators `__ -* `Tour of Python Itertools `__ (`Alternate `__) - - -Development -=========== - -``more-itertools`` is maintained by `@erikrose `_ -and `@bbayles `_, with help from `many others `_. -If you have a problem or suggestion, please file a bug or pull request in this -repository. Thanks for contributing! - - -Version History -=============== - - - :noindex: - -8.8.0 ------ - -* New functions - * countable (thanks to krzysieq) - -* Changes to existing functions - * split_before was updated to handle empy collections (thanks to TiunovNN) - * unique_everseen got a performance boost (thanks to Numerlor) - * The type hint for value_chain was corrected (thanks to vr2262) - -8.7.0 ------ - -* New functions - * convolve (from the Python itertools docs) - * product_index, combination_index, and permutation_index (thanks to N8Brooks) - * value_chain (thanks to jenstroeger) - -* Changes to existing functions - * distinct_combinations now uses a non-recursive algorithm (thanks to knutdrand) - * pad_none is now the preferred name for padnone, though the latter remains available. - * pairwise will now use the Python standard library implementation on Python 3.10+ - * sort_together now accepts a ``key`` argument (thanks to brianmaissy) - * seekable now has a ``peek`` method, and can indicate whether the iterator it's wrapping is exhausted (thanks to gsakkis) - * time_limited can now indicate whether its iterator has expired (thanks to roysmith) - * The implementation of unique_everseen was improved (thanks to plammens) - -* Other changes: - * Various documentation updates (thanks to cthoyt, Evantm, and cyphase) - -8.6.0 ------ - -* New itertools - * all_unique (thanks to brianmaissy) - * nth_product and nth_permutation (thanks to N8Brooks) - -* Changes to existing itertools - * chunked and sliced now accept a ``strict`` parameter (thanks to shlomif and jtwool) - -* Other changes - * Python 3.5 has reached its end of life and is no longer supported. - * Python 3.9 is officially supported. - * Various documentation fixes (thanks to timgates42) - -8.5.0 ------ - -* New itertools - * windowed_complete (thanks to MarcinKonowalczyk) - -* Changes to existing itertools: - * The is_sorted implementation was improved (thanks to cool-RR) - * The groupby_transform now accepts a ``reducefunc`` parameter. - * The last implementation was improved (thanks to brianmaissy) - -* Other changes - * Various documentation fixes (thanks to craigrosie, samuelstjean, PiCT0) - * The tests for distinct_combinations were improved (thanks to Minabsapi) - * Automated tests now run on GitHub Actions. All commits now check: - * That unit tests pass - * That the examples in docstrings work - * That test coverage remains high (using `coverage`) - * For linting errors (using `flake8`) - * For consistent style (using `black`) - * That the type stubs work (using `mypy`) - * That the docs build correctly (using `sphinx`) - * That packages build correctly (using `twine`) - -8.4.0 ------ - -* New itertools - * mark_ends (thanks to kalekundert) - * is_sorted - -* Changes to existing itertools: - * islice_extended can now be used with real slices (thanks to cool-RR) - * The implementations for filter_except and map_except were improved (thanks to SergBobrovsky) - -* Other changes - * Automated tests now enforce code style (using `black `__) - * The various signatures of islice_extended and numeric_range now appear in the docs (thanks to dsfulf) - * The test configuration for mypy was updated (thanks to blueyed) - - -8.3.0 ------ - -* New itertools - * zip_equal (thanks to frankier and alexmojaki) - -* Changes to existing itertools: - * split_at, split_before, split_after, and split_when all got a ``maxsplit`` paramter (thanks to jferard and ilai-deutel) - * split_at now accepts a ``keep_separator`` parameter (thanks to jferard) - * distinct_permutations can now generate ``r``-length permutations (thanks to SergBobrovsky and ilai-deutel) - * The windowed implementation was improved (thanks to SergBobrovsky) - * The spy implementation was improved (thanks to has2k1) - -* Other changes - * Type stubs are now tested with ``stubtest`` (thanks to ilai-deutel) - * Tests now run with ``python -m unittest`` instead of ``python setup.py test`` (thanks to jdufresne) - -8.2.0 ------ - -* Bug fixes - * The .pyi files for typing were updated. (thanks to blueyed and ilai-deutel) - -* Changes to existing itertools: - * numeric_range now behaves more like the built-in range. (thanks to jferard) - * bucket now allows for enumerating keys. (thanks to alexchandel) - * sliced now should now work for numpy arrays. (thanks to sswingle) - * seekable now has a ``maxlen`` parameter. - -8.1.0 ------ - -* Bug fixes - * partition works with ``pred=None`` again. (thanks to MSeifert04) - -* New itertools - * sample (thanks to tommyod) - * nth_or_last (thanks to d-ryzhikov) - -* Changes to existing itertools: - * The implementation for divide was improved. (thanks to jferard) - -8.0.2 ------ - -* Bug fixes - * The type stub files are now part of the wheel distribution (thanks to keisheiled) - -8.0.1 ------ - -* Bug fixes - * The type stub files now work for functions imported from the - root package (thanks to keisheiled) - -8.0.0 ------ - -* New itertools and other additions - * This library now ships type hints for use with mypy. - (thanks to ilai-deutel for the implementation, and to gabbard and fmagin for assistance) - * split_when (thanks to jferard) - * repeat_last (thanks to d-ryzhikov) - -* Changes to existing itertools: - * The implementation for set_partitions was improved. (thanks to jferard) - * partition was optimized for expensive predicates. (thanks to stevecj) - * unique_everseen and groupby_transform were re-factored. (thanks to SergBobrovsky) - * The implementation for difference was improved. (thanks to Jabbey92) - -* Other changes - * Python 3.4 has reached its end of life and is no longer supported. - * Python 3.8 is officially supported. (thanks to jdufresne) - * The ``collate`` function has been deprecated. - It raises a ``DeprecationWarning`` if used, and will be removed in a future release. - * one and only now provide more informative error messages. (thanks to gabbard) - * Unit tests were moved outside of the main package (thanks to jdufresne) - * Various documentation fixes (thanks to kriomant, gabbard, jdufresne) - - -7.2.0 ------ - -* New itertools - * distinct_combinations - * set_partitions (thanks to kbarrett) - * filter_except - * map_except - -7.1.0 ------ - -* New itertools - * ichunked (thanks davebelais and youtux) - * only (thanks jaraco) - -* Changes to existing itertools: - * numeric_range now supports ranges specified by - ``datetime.datetime`` and ``datetime.timedelta`` objects (thanks to MSeifert04 for tests). - * difference now supports an *initial* keyword argument. - - -* Other changes - * Various documentation fixes (thanks raimon49, pylang) - -7.0.0 ------ - -* New itertools: - * time_limited - * partitions (thanks to rominf and Saluev) - * substrings_indexes (thanks to rominf) - -* Changes to existing itertools: - * collapse now treats ``bytes`` objects the same as ``str`` objects. (thanks to Sweenpet) - -The major version update is due to the change in the default behavior of -collapse. It now treats ``bytes`` objects the same as ``str`` objects. -This aligns its behavior with always_iterable. - -.. code-block:: python - - >>> from more_itertools import collapse - >>> iterable = [[1, 2], b'345', [6]] - >>> print(list(collapse(iterable))) - [1, 2, b'345', 6] - -6.0.0 ------ - -* Major changes: - * Python 2.7 is no longer supported. The 5.0.0 release will be the last - version targeting Python 2.7. - * All future releases will target the active versions of Python 3. - As of 2019, those are Python 3.4 and above. - * The ``six`` library is no longer a dependency. - * The accumulate function is no longer part of this library. You - may import a better version from the standard ``itertools`` module. - -* Changes to existing itertools: - * The order of the parameters in grouper have changed to match - the latest recipe in the itertools documentation. Use of the old order - will be supported in this release, but emit a ``DeprecationWarning``. - The legacy behavior will be dropped in a future release. (thanks to jaraco) - * distinct_permutations was improved (thanks to jferard - see also `permutations with unique values `_ at StackOverflow.) - * An unused parameter was removed from substrings. (thanks to pylang) - -* Other changes: - * The docs for unique_everseen were improved. (thanks to jferard and MSeifert04) - * Several Python 2-isms were removed. (thanks to jaraco, MSeifert04, and hugovk) - - diff --git a/_setuptools_vendored/more_itertools-8.8.0.dist-info/RECORD b/_setuptools_vendored/more_itertools-8.8.0.dist-info/RECORD deleted file mode 100644 index e21e6bcc21..0000000000 --- a/_setuptools_vendored/more_itertools-8.8.0.dist-info/RECORD +++ /dev/null @@ -1,12 +0,0 @@ -more_itertools/__init__.py,sha256=C7sXffHTXM3P-iaLPPfqfmDoxOflQMJLcM7ed9p3jak,82 -more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43 -more_itertools/more.py,sha256=DlZa8v6JihVwfQ5zHidOA-xDE0orcQIUyxVnCaUoDKE,117968 -more_itertools/more.pyi,sha256=r32pH2raBC1zih3evK4fyvAXvrUamJqc6dgV7QCRL_M,14977 -more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -more_itertools/recipes.py,sha256=UkNkrsZyqiwgLHANBTmvMhCvaNSvSNYhyOpz_Jc55DY,16256 -more_itertools/recipes.pyi,sha256=9BpeKd5_qalYVSnuHfqPSCfoGgqnQY2Xu9pNwrDlHU8,3551 -more_itertools-8.8.0.dist-info/LICENSE,sha256=CfHIyelBrz5YTVlkHqm4fYPAyw_QB-te85Gn4mQ8GkY,1053 -more_itertools-8.8.0.dist-info/METADATA,sha256=Gke9w7RnfiAvveik_iBBrzd0RjrDhsQ8uRYNBJdo4qQ,40482 -more_itertools-8.8.0.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92 -more_itertools-8.8.0.dist-info/top_level.txt,sha256=fAuqRXu9LPhxdB9ujJowcFOu1rZ8wzSpOW9_jlKis6M,15 -more_itertools-8.8.0.dist-info/RECORD,, diff --git a/_setuptools_vendored/more_itertools-8.8.0.dist-info/WHEEL b/_setuptools_vendored/more_itertools-8.8.0.dist-info/WHEEL deleted file mode 100644 index 385faab052..0000000000 --- a/_setuptools_vendored/more_itertools-8.8.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.36.2) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/_setuptools_vendored/more_itertools-8.8.0.dist-info/top_level.txt b/_setuptools_vendored/more_itertools-8.8.0.dist-info/top_level.txt deleted file mode 100644 index a5035befb3..0000000000 --- a/_setuptools_vendored/more_itertools-8.8.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -more_itertools diff --git a/_setuptools_vendored/more_itertools/__init__.py b/_setuptools_vendored/more_itertools/__init__.py deleted file mode 100644 index 19a169fc30..0000000000 --- a/_setuptools_vendored/more_itertools/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .more import * # noqa -from .recipes import * # noqa - -__version__ = '8.8.0' diff --git a/_setuptools_vendored/more_itertools/__init__.pyi b/_setuptools_vendored/more_itertools/__init__.pyi deleted file mode 100644 index 96f6e36c7f..0000000000 --- a/_setuptools_vendored/more_itertools/__init__.pyi +++ /dev/null @@ -1,2 +0,0 @@ -from .more import * -from .recipes import * diff --git a/_setuptools_vendored/more_itertools/more.py b/_setuptools_vendored/more_itertools/more.py deleted file mode 100644 index 0f7d282aa5..0000000000 --- a/_setuptools_vendored/more_itertools/more.py +++ /dev/null @@ -1,3825 +0,0 @@ -import warnings - -from collections import Counter, defaultdict, deque, abc -from collections.abc import Sequence -from concurrent.futures import ThreadPoolExecutor -from functools import partial, reduce, wraps -from heapq import merge, heapify, heapreplace, heappop -from itertools import ( - chain, - compress, - count, - cycle, - dropwhile, - groupby, - islice, - repeat, - starmap, - takewhile, - tee, - zip_longest, -) -from math import exp, factorial, floor, log -from queue import Empty, Queue -from random import random, randrange, uniform -from operator import itemgetter, mul, sub, gt, lt -from sys import hexversion, maxsize -from time import monotonic - -from .recipes import ( - consume, - flatten, - pairwise, - powerset, - take, - unique_everseen, -) - -__all__ = [ - 'AbortThread', - 'adjacent', - 'always_iterable', - 'always_reversible', - 'bucket', - 'callback_iter', - 'chunked', - 'circular_shifts', - 'collapse', - 'collate', - 'consecutive_groups', - 'consumer', - 'countable', - 'count_cycle', - 'mark_ends', - 'difference', - 'distinct_combinations', - 'distinct_permutations', - 'distribute', - 'divide', - 'exactly_n', - 'filter_except', - 'first', - 'groupby_transform', - 'ilen', - 'interleave_longest', - 'interleave', - 'intersperse', - 'islice_extended', - 'iterate', - 'ichunked', - 'is_sorted', - 'last', - 'locate', - 'lstrip', - 'make_decorator', - 'map_except', - 'map_reduce', - 'nth_or_last', - 'nth_permutation', - 'nth_product', - 'numeric_range', - 'one', - 'only', - 'padded', - 'partitions', - 'set_partitions', - 'peekable', - 'repeat_last', - 'replace', - 'rlocate', - 'rstrip', - 'run_length', - 'sample', - 'seekable', - 'SequenceView', - 'side_effect', - 'sliced', - 'sort_together', - 'split_at', - 'split_after', - 'split_before', - 'split_when', - 'split_into', - 'spy', - 'stagger', - 'strip', - 'substrings', - 'substrings_indexes', - 'time_limited', - 'unique_to_each', - 'unzip', - 'windowed', - 'with_iter', - 'UnequalIterablesError', - 'zip_equal', - 'zip_offset', - 'windowed_complete', - 'all_unique', - 'value_chain', - 'product_index', - 'combination_index', - 'permutation_index', -] - -_marker = object() - - -def chunked(iterable, n, strict=False): - """Break *iterable* into lists of length *n*: - - >>> list(chunked([1, 2, 3, 4, 5, 6], 3)) - [[1, 2, 3], [4, 5, 6]] - - By the default, the last yielded list will have fewer than *n* elements - if the length of *iterable* is not divisible by *n*: - - >>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3)) - [[1, 2, 3], [4, 5, 6], [7, 8]] - - To use a fill-in value instead, see the :func:`grouper` recipe. - - If the length of *iterable* is not divisible by *n* and *strict* is - ``True``, then ``ValueError`` will be raised before the last - list is yielded. - - """ - iterator = iter(partial(take, n, iter(iterable)), []) - if strict: - - def ret(): - for chunk in iterator: - if len(chunk) != n: - raise ValueError('iterable is not divisible by n.') - yield chunk - - return iter(ret()) - else: - return iterator - - -def first(iterable, default=_marker): - """Return the first item of *iterable*, or *default* if *iterable* is - empty. - - >>> first([0, 1, 2, 3]) - 0 - >>> first([], 'some default') - 'some default' - - If *default* is not provided and there are no items in the iterable, - raise ``ValueError``. - - :func:`first` is useful when you have a generator of expensive-to-retrieve - values and want any arbitrary one. It is marginally shorter than - ``next(iter(iterable), default)``. - - """ - try: - return next(iter(iterable)) - except StopIteration as e: - if default is _marker: - raise ValueError( - 'first() was called on an empty iterable, and no ' - 'default value was provided.' - ) from e - return default - - -def last(iterable, default=_marker): - """Return the last item of *iterable*, or *default* if *iterable* is - empty. - - >>> last([0, 1, 2, 3]) - 3 - >>> last([], 'some default') - 'some default' - - If *default* is not provided and there are no items in the iterable, - raise ``ValueError``. - """ - try: - if isinstance(iterable, Sequence): - return iterable[-1] - # Work around https://bugs.python.org/issue38525 - elif hasattr(iterable, '__reversed__') and (hexversion != 0x030800F0): - return next(reversed(iterable)) - else: - return deque(iterable, maxlen=1)[-1] - except (IndexError, TypeError, StopIteration): - if default is _marker: - raise ValueError( - 'last() was called on an empty iterable, and no default was ' - 'provided.' - ) - return default - - -def nth_or_last(iterable, n, default=_marker): - """Return the nth or the last item of *iterable*, - or *default* if *iterable* is empty. - - >>> nth_or_last([0, 1, 2, 3], 2) - 2 - >>> nth_or_last([0, 1], 2) - 1 - >>> nth_or_last([], 0, 'some default') - 'some default' - - If *default* is not provided and there are no items in the iterable, - raise ``ValueError``. - """ - return last(islice(iterable, n + 1), default=default) - - -class peekable: - """Wrap an iterator to allow lookahead and prepending elements. - - Call :meth:`peek` on the result to get the value that will be returned - by :func:`next`. This won't advance the iterator: - - >>> p = peekable(['a', 'b']) - >>> p.peek() - 'a' - >>> next(p) - 'a' - - Pass :meth:`peek` a default value to return that instead of raising - ``StopIteration`` when the iterator is exhausted. - - >>> p = peekable([]) - >>> p.peek('hi') - 'hi' - - peekables also offer a :meth:`prepend` method, which "inserts" items - at the head of the iterable: - - >>> p = peekable([1, 2, 3]) - >>> p.prepend(10, 11, 12) - >>> next(p) - 10 - >>> p.peek() - 11 - >>> list(p) - [11, 12, 1, 2, 3] - - peekables can be indexed. Index 0 is the item that will be returned by - :func:`next`, index 1 is the item after that, and so on: - The values up to the given index will be cached. - - >>> p = peekable(['a', 'b', 'c', 'd']) - >>> p[0] - 'a' - >>> p[1] - 'b' - >>> next(p) - 'a' - - Negative indexes are supported, but be aware that they will cache the - remaining items in the source iterator, which may require significant - storage. - - To check whether a peekable is exhausted, check its truth value: - - >>> p = peekable(['a', 'b']) - >>> if p: # peekable has items - ... list(p) - ['a', 'b'] - >>> if not p: # peekable is exhausted - ... list(p) - [] - - """ - - def __init__(self, iterable): - self._it = iter(iterable) - self._cache = deque() - - def __iter__(self): - return self - - def __bool__(self): - try: - self.peek() - except StopIteration: - return False - return True - - def peek(self, default=_marker): - """Return the item that will be next returned from ``next()``. - - Return ``default`` if there are no items left. If ``default`` is not - provided, raise ``StopIteration``. - - """ - if not self._cache: - try: - self._cache.append(next(self._it)) - except StopIteration: - if default is _marker: - raise - return default - return self._cache[0] - - def prepend(self, *items): - """Stack up items to be the next ones returned from ``next()`` or - ``self.peek()``. The items will be returned in - first in, first out order:: - - >>> p = peekable([1, 2, 3]) - >>> p.prepend(10, 11, 12) - >>> next(p) - 10 - >>> list(p) - [11, 12, 1, 2, 3] - - It is possible, by prepending items, to "resurrect" a peekable that - previously raised ``StopIteration``. - - >>> p = peekable([]) - >>> next(p) - Traceback (most recent call last): - ... - StopIteration - >>> p.prepend(1) - >>> next(p) - 1 - >>> next(p) - Traceback (most recent call last): - ... - StopIteration - - """ - self._cache.extendleft(reversed(items)) - - def __next__(self): - if self._cache: - return self._cache.popleft() - - return next(self._it) - - def _get_slice(self, index): - # Normalize the slice's arguments - step = 1 if (index.step is None) else index.step - if step > 0: - start = 0 if (index.start is None) else index.start - stop = maxsize if (index.stop is None) else index.stop - elif step < 0: - start = -1 if (index.start is None) else index.start - stop = (-maxsize - 1) if (index.stop is None) else index.stop - else: - raise ValueError('slice step cannot be zero') - - # If either the start or stop index is negative, we'll need to cache - # the rest of the iterable in order to slice from the right side. - if (start < 0) or (stop < 0): - self._cache.extend(self._it) - # Otherwise we'll need to find the rightmost index and cache to that - # point. - else: - n = min(max(start, stop) + 1, maxsize) - cache_len = len(self._cache) - if n >= cache_len: - self._cache.extend(islice(self._it, n - cache_len)) - - return list(self._cache)[index] - - def __getitem__(self, index): - if isinstance(index, slice): - return self._get_slice(index) - - cache_len = len(self._cache) - if index < 0: - self._cache.extend(self._it) - elif index >= cache_len: - self._cache.extend(islice(self._it, index + 1 - cache_len)) - - return self._cache[index] - - -def collate(*iterables, **kwargs): - """Return a sorted merge of the items from each of several already-sorted - *iterables*. - - >>> list(collate('ACDZ', 'AZ', 'JKL')) - ['A', 'A', 'C', 'D', 'J', 'K', 'L', 'Z', 'Z'] - - Works lazily, keeping only the next value from each iterable in memory. Use - :func:`collate` to, for example, perform a n-way mergesort of items that - don't fit in memory. - - If a *key* function is specified, the iterables will be sorted according - to its result: - - >>> key = lambda s: int(s) # Sort by numeric value, not by string - >>> list(collate(['1', '10'], ['2', '11'], key=key)) - ['1', '2', '10', '11'] - - - If the *iterables* are sorted in descending order, set *reverse* to - ``True``: - - >>> list(collate([5, 3, 1], [4, 2, 0], reverse=True)) - [5, 4, 3, 2, 1, 0] - - If the elements of the passed-in iterables are out of order, you might get - unexpected results. - - On Python 3.5+, this function is an alias for :func:`heapq.merge`. - - """ - warnings.warn( - "collate is no longer part of more_itertools, use heapq.merge", - DeprecationWarning, - ) - return merge(*iterables, **kwargs) - - -def consumer(func): - """Decorator that automatically advances a PEP-342-style "reverse iterator" - to its first yield point so you don't have to call ``next()`` on it - manually. - - >>> @consumer - ... def tally(): - ... i = 0 - ... while True: - ... print('Thing number %s is %s.' % (i, (yield))) - ... i += 1 - ... - >>> t = tally() - >>> t.send('red') - Thing number 0 is red. - >>> t.send('fish') - Thing number 1 is fish. - - Without the decorator, you would have to call ``next(t)`` before - ``t.send()`` could be used. - - """ - - @wraps(func) - def wrapper(*args, **kwargs): - gen = func(*args, **kwargs) - next(gen) - return gen - - return wrapper - - -def ilen(iterable): - """Return the number of items in *iterable*. - - >>> ilen(x for x in range(1000000) if x % 3 == 0) - 333334 - - This consumes the iterable, so handle with care. - - """ - # This approach was selected because benchmarks showed it's likely the - # fastest of the known implementations at the time of writing. - # See GitHub tracker: #236, #230. - counter = count() - deque(zip(iterable, counter), maxlen=0) - return next(counter) - - -def iterate(func, start): - """Return ``start``, ``func(start)``, ``func(func(start))``, ... - - >>> from itertools import islice - >>> list(islice(iterate(lambda x: 2*x, 1), 10)) - [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] - - """ - while True: - yield start - start = func(start) - - -def with_iter(context_manager): - """Wrap an iterable in a ``with`` statement, so it closes once exhausted. - - For example, this will close the file when the iterator is exhausted:: - - upper_lines = (line.upper() for line in with_iter(open('foo'))) - - Any context manager which returns an iterable is a candidate for - ``with_iter``. - - """ - with context_manager as iterable: - yield from iterable - - -def one(iterable, too_short=None, too_long=None): - """Return the first item from *iterable*, which is expected to contain only - that item. Raise an exception if *iterable* is empty or has more than one - item. - - :func:`one` is useful for ensuring that an iterable contains only one item. - For example, it can be used to retrieve the result of a database query - that is expected to return a single row. - - If *iterable* is empty, ``ValueError`` will be raised. You may specify a - different exception with the *too_short* keyword: - - >>> it = [] - >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - ValueError: too many items in iterable (expected 1)' - >>> too_short = IndexError('too few items') - >>> one(it, too_short=too_short) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - IndexError: too few items - - Similarly, if *iterable* contains more than one item, ``ValueError`` will - be raised. You may specify a different exception with the *too_long* - keyword: - - >>> it = ['too', 'many'] - >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - ValueError: Expected exactly one item in iterable, but got 'too', - 'many', and perhaps more. - >>> too_long = RuntimeError - >>> one(it, too_long=too_long) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - RuntimeError - - Note that :func:`one` attempts to advance *iterable* twice to ensure there - is only one item. See :func:`spy` or :func:`peekable` to check iterable - contents less destructively. - - """ - it = iter(iterable) - - try: - first_value = next(it) - except StopIteration as e: - raise ( - too_short or ValueError('too few items in iterable (expected 1)') - ) from e - - try: - second_value = next(it) - except StopIteration: - pass - else: - msg = ( - 'Expected exactly one item in iterable, but got {!r}, {!r}, ' - 'and perhaps more.'.format(first_value, second_value) - ) - raise too_long or ValueError(msg) - - return first_value - - -def distinct_permutations(iterable, r=None): - """Yield successive distinct permutations of the elements in *iterable*. - - >>> sorted(distinct_permutations([1, 0, 1])) - [(0, 1, 1), (1, 0, 1), (1, 1, 0)] - - Equivalent to ``set(permutations(iterable))``, except duplicates are not - generated and thrown away. For larger input sequences this is much more - efficient. - - Duplicate permutations arise when there are duplicated elements in the - input iterable. The number of items returned is - `n! / (x_1! * x_2! * ... * x_n!)`, where `n` is the total number of - items input, and each `x_i` is the count of a distinct item in the input - sequence. - - If *r* is given, only the *r*-length permutations are yielded. - - >>> sorted(distinct_permutations([1, 0, 1], r=2)) - [(0, 1), (1, 0), (1, 1)] - >>> sorted(distinct_permutations(range(3), r=2)) - [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] - - """ - # Algorithm: https://w.wiki/Qai - def _full(A): - while True: - # Yield the permutation we have - yield tuple(A) - - # Find the largest index i such that A[i] < A[i + 1] - for i in range(size - 2, -1, -1): - if A[i] < A[i + 1]: - break - # If no such index exists, this permutation is the last one - else: - return - - # Find the largest index j greater than j such that A[i] < A[j] - for j in range(size - 1, i, -1): - if A[i] < A[j]: - break - - # Swap the value of A[i] with that of A[j], then reverse the - # sequence from A[i + 1] to form the new permutation - A[i], A[j] = A[j], A[i] - A[i + 1 :] = A[: i - size : -1] # A[i + 1:][::-1] - - # Algorithm: modified from the above - def _partial(A, r): - # Split A into the first r items and the last r items - head, tail = A[:r], A[r:] - right_head_indexes = range(r - 1, -1, -1) - left_tail_indexes = range(len(tail)) - - while True: - # Yield the permutation we have - yield tuple(head) - - # Starting from the right, find the first index of the head with - # value smaller than the maximum value of the tail - call it i. - pivot = tail[-1] - for i in right_head_indexes: - if head[i] < pivot: - break - pivot = head[i] - else: - return - - # Starting from the left, find the first value of the tail - # with a value greater than head[i] and swap. - for j in left_tail_indexes: - if tail[j] > head[i]: - head[i], tail[j] = tail[j], head[i] - break - # If we didn't find one, start from the right and find the first - # index of the head with a value greater than head[i] and swap. - else: - for j in right_head_indexes: - if head[j] > head[i]: - head[i], head[j] = head[j], head[i] - break - - # Reverse head[i + 1:] and swap it with tail[:r - (i + 1)] - tail += head[: i - r : -1] # head[i + 1:][::-1] - i += 1 - head[i:], tail[:] = tail[: r - i], tail[r - i :] - - items = sorted(iterable) - - size = len(items) - if r is None: - r = size - - if 0 < r <= size: - return _full(items) if (r == size) else _partial(items, r) - - return iter(() if r else ((),)) - - -def intersperse(e, iterable, n=1): - """Intersperse filler element *e* among the items in *iterable*, leaving - *n* items between each filler element. - - >>> list(intersperse('!', [1, 2, 3, 4, 5])) - [1, '!', 2, '!', 3, '!', 4, '!', 5] - - >>> list(intersperse(None, [1, 2, 3, 4, 5], n=2)) - [1, 2, None, 3, 4, None, 5] - - """ - if n == 0: - raise ValueError('n must be > 0') - elif n == 1: - # interleave(repeat(e), iterable) -> e, x_0, e, e, x_1, e, x_2... - # islice(..., 1, None) -> x_0, e, e, x_1, e, x_2... - return islice(interleave(repeat(e), iterable), 1, None) - else: - # interleave(filler, chunks) -> [e], [x_0, x_1], [e], [x_2, x_3]... - # islice(..., 1, None) -> [x_0, x_1], [e], [x_2, x_3]... - # flatten(...) -> x_0, x_1, e, x_2, x_3... - filler = repeat([e]) - chunks = chunked(iterable, n) - return flatten(islice(interleave(filler, chunks), 1, None)) - - -def unique_to_each(*iterables): - """Return the elements from each of the input iterables that aren't in the - other input iterables. - - For example, suppose you have a set of packages, each with a set of - dependencies:: - - {'pkg_1': {'A', 'B'}, 'pkg_2': {'B', 'C'}, 'pkg_3': {'B', 'D'}} - - If you remove one package, which dependencies can also be removed? - - If ``pkg_1`` is removed, then ``A`` is no longer necessary - it is not - associated with ``pkg_2`` or ``pkg_3``. Similarly, ``C`` is only needed for - ``pkg_2``, and ``D`` is only needed for ``pkg_3``:: - - >>> unique_to_each({'A', 'B'}, {'B', 'C'}, {'B', 'D'}) - [['A'], ['C'], ['D']] - - If there are duplicates in one input iterable that aren't in the others - they will be duplicated in the output. Input order is preserved:: - - >>> unique_to_each("mississippi", "missouri") - [['p', 'p'], ['o', 'u', 'r']] - - It is assumed that the elements of each iterable are hashable. - - """ - pool = [list(it) for it in iterables] - counts = Counter(chain.from_iterable(map(set, pool))) - uniques = {element for element in counts if counts[element] == 1} - return [list(filter(uniques.__contains__, it)) for it in pool] - - -def windowed(seq, n, fillvalue=None, step=1): - """Return a sliding window of width *n* over the given iterable. - - >>> all_windows = windowed([1, 2, 3, 4, 5], 3) - >>> list(all_windows) - [(1, 2, 3), (2, 3, 4), (3, 4, 5)] - - When the window is larger than the iterable, *fillvalue* is used in place - of missing values: - - >>> list(windowed([1, 2, 3], 4)) - [(1, 2, 3, None)] - - Each window will advance in increments of *step*: - - >>> list(windowed([1, 2, 3, 4, 5, 6], 3, fillvalue='!', step=2)) - [(1, 2, 3), (3, 4, 5), (5, 6, '!')] - - To slide into the iterable's items, use :func:`chain` to add filler items - to the left: - - >>> iterable = [1, 2, 3, 4] - >>> n = 3 - >>> padding = [None] * (n - 1) - >>> list(windowed(chain(padding, iterable), 3)) - [(None, None, 1), (None, 1, 2), (1, 2, 3), (2, 3, 4)] - """ - if n < 0: - raise ValueError('n must be >= 0') - if n == 0: - yield tuple() - return - if step < 1: - raise ValueError('step must be >= 1') - - window = deque(maxlen=n) - i = n - for _ in map(window.append, seq): - i -= 1 - if not i: - i = step - yield tuple(window) - - size = len(window) - if size < n: - yield tuple(chain(window, repeat(fillvalue, n - size))) - elif 0 < i < min(step, n): - window += (fillvalue,) * i - yield tuple(window) - - -def substrings(iterable): - """Yield all of the substrings of *iterable*. - - >>> [''.join(s) for s in substrings('more')] - ['m', 'o', 'r', 'e', 'mo', 'or', 're', 'mor', 'ore', 'more'] - - Note that non-string iterables can also be subdivided. - - >>> list(substrings([0, 1, 2])) - [(0,), (1,), (2,), (0, 1), (1, 2), (0, 1, 2)] - - """ - # The length-1 substrings - seq = [] - for item in iter(iterable): - seq.append(item) - yield (item,) - seq = tuple(seq) - item_count = len(seq) - - # And the rest - for n in range(2, item_count + 1): - for i in range(item_count - n + 1): - yield seq[i : i + n] - - -def substrings_indexes(seq, reverse=False): - """Yield all substrings and their positions in *seq* - - The items yielded will be a tuple of the form ``(substr, i, j)``, where - ``substr == seq[i:j]``. - - This function only works for iterables that support slicing, such as - ``str`` objects. - - >>> for item in substrings_indexes('more'): - ... print(item) - ('m', 0, 1) - ('o', 1, 2) - ('r', 2, 3) - ('e', 3, 4) - ('mo', 0, 2) - ('or', 1, 3) - ('re', 2, 4) - ('mor', 0, 3) - ('ore', 1, 4) - ('more', 0, 4) - - Set *reverse* to ``True`` to yield the same items in the opposite order. - - - """ - r = range(1, len(seq) + 1) - if reverse: - r = reversed(r) - return ( - (seq[i : i + L], i, i + L) for L in r for i in range(len(seq) - L + 1) - ) - - -class bucket: - """Wrap *iterable* and return an object that buckets it iterable into - child iterables based on a *key* function. - - >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3'] - >>> s = bucket(iterable, key=lambda x: x[0]) # Bucket by 1st character - >>> sorted(list(s)) # Get the keys - ['a', 'b', 'c'] - >>> a_iterable = s['a'] - >>> next(a_iterable) - 'a1' - >>> next(a_iterable) - 'a2' - >>> list(s['b']) - ['b1', 'b2', 'b3'] - - The original iterable will be advanced and its items will be cached until - they are used by the child iterables. This may require significant storage. - - By default, attempting to select a bucket to which no items belong will - exhaust the iterable and cache all values. - If you specify a *validator* function, selected buckets will instead be - checked against it. - - >>> from itertools import count - >>> it = count(1, 2) # Infinite sequence of odd numbers - >>> key = lambda x: x % 10 # Bucket by last digit - >>> validator = lambda x: x in {1, 3, 5, 7, 9} # Odd digits only - >>> s = bucket(it, key=key, validator=validator) - >>> 2 in s - False - >>> list(s[2]) - [] - - """ - - def __init__(self, iterable, key, validator=None): - self._it = iter(iterable) - self._key = key - self._cache = defaultdict(deque) - self._validator = validator or (lambda x: True) - - def __contains__(self, value): - if not self._validator(value): - return False - - try: - item = next(self[value]) - except StopIteration: - return False - else: - self._cache[value].appendleft(item) - - return True - - def _get_values(self, value): - """ - Helper to yield items from the parent iterator that match *value*. - Items that don't match are stored in the local cache as they - are encountered. - """ - while True: - # If we've cached some items that match the target value, emit - # the first one and evict it from the cache. - if self._cache[value]: - yield self._cache[value].popleft() - # Otherwise we need to advance the parent iterator to search for - # a matching item, caching the rest. - else: - while True: - try: - item = next(self._it) - except StopIteration: - return - item_value = self._key(item) - if item_value == value: - yield item - break - elif self._validator(item_value): - self._cache[item_value].append(item) - - def __iter__(self): - for item in self._it: - item_value = self._key(item) - if self._validator(item_value): - self._cache[item_value].append(item) - - yield from self._cache.keys() - - def __getitem__(self, value): - if not self._validator(value): - return iter(()) - - return self._get_values(value) - - -def spy(iterable, n=1): - """Return a 2-tuple with a list containing the first *n* elements of - *iterable*, and an iterator with the same items as *iterable*. - This allows you to "look ahead" at the items in the iterable without - advancing it. - - There is one item in the list by default: - - >>> iterable = 'abcdefg' - >>> head, iterable = spy(iterable) - >>> head - ['a'] - >>> list(iterable) - ['a', 'b', 'c', 'd', 'e', 'f', 'g'] - - You may use unpacking to retrieve items instead of lists: - - >>> (head,), iterable = spy('abcdefg') - >>> head - 'a' - >>> (first, second), iterable = spy('abcdefg', 2) - >>> first - 'a' - >>> second - 'b' - - The number of items requested can be larger than the number of items in - the iterable: - - >>> iterable = [1, 2, 3, 4, 5] - >>> head, iterable = spy(iterable, 10) - >>> head - [1, 2, 3, 4, 5] - >>> list(iterable) - [1, 2, 3, 4, 5] - - """ - it = iter(iterable) - head = take(n, it) - - return head.copy(), chain(head, it) - - -def interleave(*iterables): - """Return a new iterable yielding from each iterable in turn, - until the shortest is exhausted. - - >>> list(interleave([1, 2, 3], [4, 5], [6, 7, 8])) - [1, 4, 6, 2, 5, 7] - - For a version that doesn't terminate after the shortest iterable is - exhausted, see :func:`interleave_longest`. - - """ - return chain.from_iterable(zip(*iterables)) - - -def interleave_longest(*iterables): - """Return a new iterable yielding from each iterable in turn, - skipping any that are exhausted. - - >>> list(interleave_longest([1, 2, 3], [4, 5], [6, 7, 8])) - [1, 4, 6, 2, 5, 7, 3, 8] - - This function produces the same output as :func:`roundrobin`, but may - perform better for some inputs (in particular when the number of iterables - is large). - - """ - i = chain.from_iterable(zip_longest(*iterables, fillvalue=_marker)) - return (x for x in i if x is not _marker) - - -def collapse(iterable, base_type=None, levels=None): - """Flatten an iterable with multiple levels of nesting (e.g., a list of - lists of tuples) into non-iterable types. - - >>> iterable = [(1, 2), ([3, 4], [[5], [6]])] - >>> list(collapse(iterable)) - [1, 2, 3, 4, 5, 6] - - Binary and text strings are not considered iterable and - will not be collapsed. - - To avoid collapsing other types, specify *base_type*: - - >>> iterable = ['ab', ('cd', 'ef'), ['gh', 'ij']] - >>> list(collapse(iterable, base_type=tuple)) - ['ab', ('cd', 'ef'), 'gh', 'ij'] - - Specify *levels* to stop flattening after a certain level: - - >>> iterable = [('a', ['b']), ('c', ['d'])] - >>> list(collapse(iterable)) # Fully flattened - ['a', 'b', 'c', 'd'] - >>> list(collapse(iterable, levels=1)) # Only one level flattened - ['a', ['b'], 'c', ['d']] - - """ - - def walk(node, level): - if ( - ((levels is not None) and (level > levels)) - or isinstance(node, (str, bytes)) - or ((base_type is not None) and isinstance(node, base_type)) - ): - yield node - return - - try: - tree = iter(node) - except TypeError: - yield node - return - else: - for child in tree: - yield from walk(child, level + 1) - - yield from walk(iterable, 0) - - -def side_effect(func, iterable, chunk_size=None, before=None, after=None): - """Invoke *func* on each item in *iterable* (or on each *chunk_size* group - of items) before yielding the item. - - `func` must be a function that takes a single argument. Its return value - will be discarded. - - *before* and *after* are optional functions that take no arguments. They - will be executed before iteration starts and after it ends, respectively. - - `side_effect` can be used for logging, updating progress bars, or anything - that is not functionally "pure." - - Emitting a status message: - - >>> from more_itertools import consume - >>> func = lambda item: print('Received {}'.format(item)) - >>> consume(side_effect(func, range(2))) - Received 0 - Received 1 - - Operating on chunks of items: - - >>> pair_sums = [] - >>> func = lambda chunk: pair_sums.append(sum(chunk)) - >>> list(side_effect(func, [0, 1, 2, 3, 4, 5], 2)) - [0, 1, 2, 3, 4, 5] - >>> list(pair_sums) - [1, 5, 9] - - Writing to a file-like object: - - >>> from io import StringIO - >>> from more_itertools import consume - >>> f = StringIO() - >>> func = lambda x: print(x, file=f) - >>> before = lambda: print(u'HEADER', file=f) - >>> after = f.close - >>> it = [u'a', u'b', u'c'] - >>> consume(side_effect(func, it, before=before, after=after)) - >>> f.closed - True - - """ - try: - if before is not None: - before() - - if chunk_size is None: - for item in iterable: - func(item) - yield item - else: - for chunk in chunked(iterable, chunk_size): - func(chunk) - yield from chunk - finally: - if after is not None: - after() - - -def sliced(seq, n, strict=False): - """Yield slices of length *n* from the sequence *seq*. - - >>> list(sliced((1, 2, 3, 4, 5, 6), 3)) - [(1, 2, 3), (4, 5, 6)] - - By the default, the last yielded slice will have fewer than *n* elements - if the length of *seq* is not divisible by *n*: - - >>> list(sliced((1, 2, 3, 4, 5, 6, 7, 8), 3)) - [(1, 2, 3), (4, 5, 6), (7, 8)] - - If the length of *seq* is not divisible by *n* and *strict* is - ``True``, then ``ValueError`` will be raised before the last - slice is yielded. - - This function will only work for iterables that support slicing. - For non-sliceable iterables, see :func:`chunked`. - - """ - iterator = takewhile(len, (seq[i : i + n] for i in count(0, n))) - if strict: - - def ret(): - for _slice in iterator: - if len(_slice) != n: - raise ValueError("seq is not divisible by n.") - yield _slice - - return iter(ret()) - else: - return iterator - - -def split_at(iterable, pred, maxsplit=-1, keep_separator=False): - """Yield lists of items from *iterable*, where each list is delimited by - an item where callable *pred* returns ``True``. - - >>> list(split_at('abcdcba', lambda x: x == 'b')) - [['a'], ['c', 'd', 'c'], ['a']] - - >>> list(split_at(range(10), lambda n: n % 2 == 1)) - [[0], [2], [4], [6], [8], []] - - At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, - then there is no limit on the number of splits: - - >>> list(split_at(range(10), lambda n: n % 2 == 1, maxsplit=2)) - [[0], [2], [4, 5, 6, 7, 8, 9]] - - By default, the delimiting items are not included in the output. - The include them, set *keep_separator* to ``True``. - - >>> list(split_at('abcdcba', lambda x: x == 'b', keep_separator=True)) - [['a'], ['b'], ['c', 'd', 'c'], ['b'], ['a']] - - """ - if maxsplit == 0: - yield list(iterable) - return - - buf = [] - it = iter(iterable) - for item in it: - if pred(item): - yield buf - if keep_separator: - yield [item] - if maxsplit == 1: - yield list(it) - return - buf = [] - maxsplit -= 1 - else: - buf.append(item) - yield buf - - -def split_before(iterable, pred, maxsplit=-1): - """Yield lists of items from *iterable*, where each list ends just before - an item for which callable *pred* returns ``True``: - - >>> list(split_before('OneTwo', lambda s: s.isupper())) - [['O', 'n', 'e'], ['T', 'w', 'o']] - - >>> list(split_before(range(10), lambda n: n % 3 == 0)) - [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] - - At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, - then there is no limit on the number of splits: - - >>> list(split_before(range(10), lambda n: n % 3 == 0, maxsplit=2)) - [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]] - """ - if maxsplit == 0: - yield list(iterable) - return - - buf = [] - it = iter(iterable) - for item in it: - if pred(item) and buf: - yield buf - if maxsplit == 1: - yield [item] + list(it) - return - buf = [] - maxsplit -= 1 - buf.append(item) - if buf: - yield buf - - -def split_after(iterable, pred, maxsplit=-1): - """Yield lists of items from *iterable*, where each list ends with an - item where callable *pred* returns ``True``: - - >>> list(split_after('one1two2', lambda s: s.isdigit())) - [['o', 'n', 'e', '1'], ['t', 'w', 'o', '2']] - - >>> list(split_after(range(10), lambda n: n % 3 == 0)) - [[0], [1, 2, 3], [4, 5, 6], [7, 8, 9]] - - At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, - then there is no limit on the number of splits: - - >>> list(split_after(range(10), lambda n: n % 3 == 0, maxsplit=2)) - [[0], [1, 2, 3], [4, 5, 6, 7, 8, 9]] - - """ - if maxsplit == 0: - yield list(iterable) - return - - buf = [] - it = iter(iterable) - for item in it: - buf.append(item) - if pred(item) and buf: - yield buf - if maxsplit == 1: - yield list(it) - return - buf = [] - maxsplit -= 1 - if buf: - yield buf - - -def split_when(iterable, pred, maxsplit=-1): - """Split *iterable* into pieces based on the output of *pred*. - *pred* should be a function that takes successive pairs of items and - returns ``True`` if the iterable should be split in between them. - - For example, to find runs of increasing numbers, split the iterable when - element ``i`` is larger than element ``i + 1``: - - >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], lambda x, y: x > y)) - [[1, 2, 3, 3], [2, 5], [2, 4], [2]] - - At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, - then there is no limit on the number of splits: - - >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], - ... lambda x, y: x > y, maxsplit=2)) - [[1, 2, 3, 3], [2, 5], [2, 4, 2]] - - """ - if maxsplit == 0: - yield list(iterable) - return - - it = iter(iterable) - try: - cur_item = next(it) - except StopIteration: - return - - buf = [cur_item] - for next_item in it: - if pred(cur_item, next_item): - yield buf - if maxsplit == 1: - yield [next_item] + list(it) - return - buf = [] - maxsplit -= 1 - - buf.append(next_item) - cur_item = next_item - - yield buf - - -def split_into(iterable, sizes): - """Yield a list of sequential items from *iterable* of length 'n' for each - integer 'n' in *sizes*. - - >>> list(split_into([1,2,3,4,5,6], [1,2,3])) - [[1], [2, 3], [4, 5, 6]] - - If the sum of *sizes* is smaller than the length of *iterable*, then the - remaining items of *iterable* will not be returned. - - >>> list(split_into([1,2,3,4,5,6], [2,3])) - [[1, 2], [3, 4, 5]] - - If the sum of *sizes* is larger than the length of *iterable*, fewer items - will be returned in the iteration that overruns *iterable* and further - lists will be empty: - - >>> list(split_into([1,2,3,4], [1,2,3,4])) - [[1], [2, 3], [4], []] - - When a ``None`` object is encountered in *sizes*, the returned list will - contain items up to the end of *iterable* the same way that itertools.slice - does: - - >>> list(split_into([1,2,3,4,5,6,7,8,9,0], [2,3,None])) - [[1, 2], [3, 4, 5], [6, 7, 8, 9, 0]] - - :func:`split_into` can be useful for grouping a series of items where the - sizes of the groups are not uniform. An example would be where in a row - from a table, multiple columns represent elements of the same feature - (e.g. a point represented by x,y,z) but, the format is not the same for - all columns. - """ - # convert the iterable argument into an iterator so its contents can - # be consumed by islice in case it is a generator - it = iter(iterable) - - for size in sizes: - if size is None: - yield list(it) - return - else: - yield list(islice(it, size)) - - -def padded(iterable, fillvalue=None, n=None, next_multiple=False): - """Yield the elements from *iterable*, followed by *fillvalue*, such that - at least *n* items are emitted. - - >>> list(padded([1, 2, 3], '?', 5)) - [1, 2, 3, '?', '?'] - - If *next_multiple* is ``True``, *fillvalue* will be emitted until the - number of items emitted is a multiple of *n*:: - - >>> list(padded([1, 2, 3, 4], n=3, next_multiple=True)) - [1, 2, 3, 4, None, None] - - If *n* is ``None``, *fillvalue* will be emitted indefinitely. - - """ - it = iter(iterable) - if n is None: - yield from chain(it, repeat(fillvalue)) - elif n < 1: - raise ValueError('n must be at least 1') - else: - item_count = 0 - for item in it: - yield item - item_count += 1 - - remaining = (n - item_count) % n if next_multiple else n - item_count - for _ in range(remaining): - yield fillvalue - - -def repeat_last(iterable, default=None): - """After the *iterable* is exhausted, keep yielding its last element. - - >>> list(islice(repeat_last(range(3)), 5)) - [0, 1, 2, 2, 2] - - If the iterable is empty, yield *default* forever:: - - >>> list(islice(repeat_last(range(0), 42), 5)) - [42, 42, 42, 42, 42] - - """ - item = _marker - for item in iterable: - yield item - final = default if item is _marker else item - yield from repeat(final) - - -def distribute(n, iterable): - """Distribute the items from *iterable* among *n* smaller iterables. - - >>> group_1, group_2 = distribute(2, [1, 2, 3, 4, 5, 6]) - >>> list(group_1) - [1, 3, 5] - >>> list(group_2) - [2, 4, 6] - - If the length of *iterable* is not evenly divisible by *n*, then the - length of the returned iterables will not be identical: - - >>> children = distribute(3, [1, 2, 3, 4, 5, 6, 7]) - >>> [list(c) for c in children] - [[1, 4, 7], [2, 5], [3, 6]] - - If the length of *iterable* is smaller than *n*, then the last returned - iterables will be empty: - - >>> children = distribute(5, [1, 2, 3]) - >>> [list(c) for c in children] - [[1], [2], [3], [], []] - - This function uses :func:`itertools.tee` and may require significant - storage. If you need the order items in the smaller iterables to match the - original iterable, see :func:`divide`. - - """ - if n < 1: - raise ValueError('n must be at least 1') - - children = tee(iterable, n) - return [islice(it, index, None, n) for index, it in enumerate(children)] - - -def stagger(iterable, offsets=(-1, 0, 1), longest=False, fillvalue=None): - """Yield tuples whose elements are offset from *iterable*. - The amount by which the `i`-th item in each tuple is offset is given by - the `i`-th item in *offsets*. - - >>> list(stagger([0, 1, 2, 3])) - [(None, 0, 1), (0, 1, 2), (1, 2, 3)] - >>> list(stagger(range(8), offsets=(0, 2, 4))) - [(0, 2, 4), (1, 3, 5), (2, 4, 6), (3, 5, 7)] - - By default, the sequence will end when the final element of a tuple is the - last item in the iterable. To continue until the first element of a tuple - is the last item in the iterable, set *longest* to ``True``:: - - >>> list(stagger([0, 1, 2, 3], longest=True)) - [(None, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, None), (3, None, None)] - - By default, ``None`` will be used to replace offsets beyond the end of the - sequence. Specify *fillvalue* to use some other value. - - """ - children = tee(iterable, len(offsets)) - - return zip_offset( - *children, offsets=offsets, longest=longest, fillvalue=fillvalue - ) - - -class UnequalIterablesError(ValueError): - def __init__(self, details=None): - msg = 'Iterables have different lengths' - if details is not None: - msg += (': index 0 has length {}; index {} has length {}').format( - *details - ) - - super().__init__(msg) - - -def _zip_equal_generator(iterables): - for combo in zip_longest(*iterables, fillvalue=_marker): - for val in combo: - if val is _marker: - raise UnequalIterablesError() - yield combo - - -def zip_equal(*iterables): - """``zip`` the input *iterables* together, but raise - ``UnequalIterablesError`` if they aren't all the same length. - - >>> it_1 = range(3) - >>> it_2 = iter('abc') - >>> list(zip_equal(it_1, it_2)) - [(0, 'a'), (1, 'b'), (2, 'c')] - - >>> it_1 = range(3) - >>> it_2 = iter('abcd') - >>> list(zip_equal(it_1, it_2)) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - more_itertools.more.UnequalIterablesError: Iterables have different - lengths - - """ - if hexversion >= 0x30A00A6: - warnings.warn( - ( - 'zip_equal will be removed in a future version of ' - 'more-itertools. Use the builtin zip function with ' - 'strict=True instead.' - ), - DeprecationWarning, - ) - # Check whether the iterables are all the same size. - try: - first_size = len(iterables[0]) - for i, it in enumerate(iterables[1:], 1): - size = len(it) - if size != first_size: - break - else: - # If we didn't break out, we can use the built-in zip. - return zip(*iterables) - - # If we did break out, there was a mismatch. - raise UnequalIterablesError(details=(first_size, i, size)) - # If any one of the iterables didn't have a length, start reading - # them until one runs out. - except TypeError: - return _zip_equal_generator(iterables) - - -def zip_offset(*iterables, offsets, longest=False, fillvalue=None): - """``zip`` the input *iterables* together, but offset the `i`-th iterable - by the `i`-th item in *offsets*. - - >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1))) - [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e')] - - This can be used as a lightweight alternative to SciPy or pandas to analyze - data sets in which some series have a lead or lag relationship. - - By default, the sequence will end when the shortest iterable is exhausted. - To continue until the longest iterable is exhausted, set *longest* to - ``True``. - - >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1), longest=True)) - [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e'), (None, 'f')] - - By default, ``None`` will be used to replace offsets beyond the end of the - sequence. Specify *fillvalue* to use some other value. - - """ - if len(iterables) != len(offsets): - raise ValueError("Number of iterables and offsets didn't match") - - staggered = [] - for it, n in zip(iterables, offsets): - if n < 0: - staggered.append(chain(repeat(fillvalue, -n), it)) - elif n > 0: - staggered.append(islice(it, n, None)) - else: - staggered.append(it) - - if longest: - return zip_longest(*staggered, fillvalue=fillvalue) - - return zip(*staggered) - - -def sort_together(iterables, key_list=(0,), key=None, reverse=False): - """Return the input iterables sorted together, with *key_list* as the - priority for sorting. All iterables are trimmed to the length of the - shortest one. - - This can be used like the sorting function in a spreadsheet. If each - iterable represents a column of data, the key list determines which - columns are used for sorting. - - By default, all iterables are sorted using the ``0``-th iterable:: - - >>> iterables = [(4, 3, 2, 1), ('a', 'b', 'c', 'd')] - >>> sort_together(iterables) - [(1, 2, 3, 4), ('d', 'c', 'b', 'a')] - - Set a different key list to sort according to another iterable. - Specifying multiple keys dictates how ties are broken:: - - >>> iterables = [(3, 1, 2), (0, 1, 0), ('c', 'b', 'a')] - >>> sort_together(iterables, key_list=(1, 2)) - [(2, 3, 1), (0, 0, 1), ('a', 'c', 'b')] - - To sort by a function of the elements of the iterable, pass a *key* - function. Its arguments are the elements of the iterables corresponding to - the key list:: - - >>> names = ('a', 'b', 'c') - >>> lengths = (1, 2, 3) - >>> widths = (5, 2, 1) - >>> def area(length, width): - ... return length * width - >>> sort_together([names, lengths, widths], key_list=(1, 2), key=area) - [('c', 'b', 'a'), (3, 2, 1), (1, 2, 5)] - - Set *reverse* to ``True`` to sort in descending order. - - >>> sort_together([(1, 2, 3), ('c', 'b', 'a')], reverse=True) - [(3, 2, 1), ('a', 'b', 'c')] - - """ - if key is None: - # if there is no key function, the key argument to sorted is an - # itemgetter - key_argument = itemgetter(*key_list) - else: - # if there is a key function, call it with the items at the offsets - # specified by the key function as arguments - key_list = list(key_list) - if len(key_list) == 1: - # if key_list contains a single item, pass the item at that offset - # as the only argument to the key function - key_offset = key_list[0] - key_argument = lambda zipped_items: key(zipped_items[key_offset]) - else: - # if key_list contains multiple items, use itemgetter to return a - # tuple of items, which we pass as *args to the key function - get_key_items = itemgetter(*key_list) - key_argument = lambda zipped_items: key( - *get_key_items(zipped_items) - ) - - return list( - zip(*sorted(zip(*iterables), key=key_argument, reverse=reverse)) - ) - - -def unzip(iterable): - """The inverse of :func:`zip`, this function disaggregates the elements - of the zipped *iterable*. - - The ``i``-th iterable contains the ``i``-th element from each element - of the zipped iterable. The first element is used to to determine the - length of the remaining elements. - - >>> iterable = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] - >>> letters, numbers = unzip(iterable) - >>> list(letters) - ['a', 'b', 'c', 'd'] - >>> list(numbers) - [1, 2, 3, 4] - - This is similar to using ``zip(*iterable)``, but it avoids reading - *iterable* into memory. Note, however, that this function uses - :func:`itertools.tee` and thus may require significant storage. - - """ - head, iterable = spy(iter(iterable)) - if not head: - # empty iterable, e.g. zip([], [], []) - return () - # spy returns a one-length iterable as head - head = head[0] - iterables = tee(iterable, len(head)) - - def itemgetter(i): - def getter(obj): - try: - return obj[i] - except IndexError: - # basically if we have an iterable like - # iter([(1, 2, 3), (4, 5), (6,)]) - # the second unzipped iterable would fail at the third tuple - # since it would try to access tup[1] - # same with the third unzipped iterable and the second tuple - # to support these "improperly zipped" iterables, - # we create a custom itemgetter - # which just stops the unzipped iterables - # at first length mismatch - raise StopIteration - - return getter - - return tuple(map(itemgetter(i), it) for i, it in enumerate(iterables)) - - -def divide(n, iterable): - """Divide the elements from *iterable* into *n* parts, maintaining - order. - - >>> group_1, group_2 = divide(2, [1, 2, 3, 4, 5, 6]) - >>> list(group_1) - [1, 2, 3] - >>> list(group_2) - [4, 5, 6] - - If the length of *iterable* is not evenly divisible by *n*, then the - length of the returned iterables will not be identical: - - >>> children = divide(3, [1, 2, 3, 4, 5, 6, 7]) - >>> [list(c) for c in children] - [[1, 2, 3], [4, 5], [6, 7]] - - If the length of the iterable is smaller than n, then the last returned - iterables will be empty: - - >>> children = divide(5, [1, 2, 3]) - >>> [list(c) for c in children] - [[1], [2], [3], [], []] - - This function will exhaust the iterable before returning and may require - significant storage. If order is not important, see :func:`distribute`, - which does not first pull the iterable into memory. - - """ - if n < 1: - raise ValueError('n must be at least 1') - - try: - iterable[:0] - except TypeError: - seq = tuple(iterable) - else: - seq = iterable - - q, r = divmod(len(seq), n) - - ret = [] - stop = 0 - for i in range(1, n + 1): - start = stop - stop += q + 1 if i <= r else q - ret.append(iter(seq[start:stop])) - - return ret - - -def always_iterable(obj, base_type=(str, bytes)): - """If *obj* is iterable, return an iterator over its items:: - - >>> obj = (1, 2, 3) - >>> list(always_iterable(obj)) - [1, 2, 3] - - If *obj* is not iterable, return a one-item iterable containing *obj*:: - - >>> obj = 1 - >>> list(always_iterable(obj)) - [1] - - If *obj* is ``None``, return an empty iterable: - - >>> obj = None - >>> list(always_iterable(None)) - [] - - By default, binary and text strings are not considered iterable:: - - >>> obj = 'foo' - >>> list(always_iterable(obj)) - ['foo'] - - If *base_type* is set, objects for which ``isinstance(obj, base_type)`` - returns ``True`` won't be considered iterable. - - >>> obj = {'a': 1} - >>> list(always_iterable(obj)) # Iterate over the dict's keys - ['a'] - >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit - [{'a': 1}] - - Set *base_type* to ``None`` to avoid any special handling and treat objects - Python considers iterable as iterable: - - >>> obj = 'foo' - >>> list(always_iterable(obj, base_type=None)) - ['f', 'o', 'o'] - """ - if obj is None: - return iter(()) - - if (base_type is not None) and isinstance(obj, base_type): - return iter((obj,)) - - try: - return iter(obj) - except TypeError: - return iter((obj,)) - - -def adjacent(predicate, iterable, distance=1): - """Return an iterable over `(bool, item)` tuples where the `item` is - drawn from *iterable* and the `bool` indicates whether - that item satisfies the *predicate* or is adjacent to an item that does. - - For example, to find whether items are adjacent to a ``3``:: - - >>> list(adjacent(lambda x: x == 3, range(6))) - [(False, 0), (False, 1), (True, 2), (True, 3), (True, 4), (False, 5)] - - Set *distance* to change what counts as adjacent. For example, to find - whether items are two places away from a ``3``: - - >>> list(adjacent(lambda x: x == 3, range(6), distance=2)) - [(False, 0), (True, 1), (True, 2), (True, 3), (True, 4), (True, 5)] - - This is useful for contextualizing the results of a search function. - For example, a code comparison tool might want to identify lines that - have changed, but also surrounding lines to give the viewer of the diff - context. - - The predicate function will only be called once for each item in the - iterable. - - See also :func:`groupby_transform`, which can be used with this function - to group ranges of items with the same `bool` value. - - """ - # Allow distance=0 mainly for testing that it reproduces results with map() - if distance < 0: - raise ValueError('distance must be at least 0') - - i1, i2 = tee(iterable) - padding = [False] * distance - selected = chain(padding, map(predicate, i1), padding) - adjacent_to_selected = map(any, windowed(selected, 2 * distance + 1)) - return zip(adjacent_to_selected, i2) - - -def groupby_transform(iterable, keyfunc=None, valuefunc=None, reducefunc=None): - """An extension of :func:`itertools.groupby` that can apply transformations - to the grouped data. - - * *keyfunc* is a function computing a key value for each item in *iterable* - * *valuefunc* is a function that transforms the individual items from - *iterable* after grouping - * *reducefunc* is a function that transforms each group of items - - >>> iterable = 'aAAbBBcCC' - >>> keyfunc = lambda k: k.upper() - >>> valuefunc = lambda v: v.lower() - >>> reducefunc = lambda g: ''.join(g) - >>> list(groupby_transform(iterable, keyfunc, valuefunc, reducefunc)) - [('A', 'aaa'), ('B', 'bbb'), ('C', 'ccc')] - - Each optional argument defaults to an identity function if not specified. - - :func:`groupby_transform` is useful when grouping elements of an iterable - using a separate iterable as the key. To do this, :func:`zip` the iterables - and pass a *keyfunc* that extracts the first element and a *valuefunc* - that extracts the second element:: - - >>> from operator import itemgetter - >>> keys = [0, 0, 1, 1, 1, 2, 2, 2, 3] - >>> values = 'abcdefghi' - >>> iterable = zip(keys, values) - >>> grouper = groupby_transform(iterable, itemgetter(0), itemgetter(1)) - >>> [(k, ''.join(g)) for k, g in grouper] - [(0, 'ab'), (1, 'cde'), (2, 'fgh'), (3, 'i')] - - Note that the order of items in the iterable is significant. - Only adjacent items are grouped together, so if you don't want any - duplicate groups, you should sort the iterable by the key function. - - """ - ret = groupby(iterable, keyfunc) - if valuefunc: - ret = ((k, map(valuefunc, g)) for k, g in ret) - if reducefunc: - ret = ((k, reducefunc(g)) for k, g in ret) - - return ret - - -class numeric_range(abc.Sequence, abc.Hashable): - """An extension of the built-in ``range()`` function whose arguments can - be any orderable numeric type. - - With only *stop* specified, *start* defaults to ``0`` and *step* - defaults to ``1``. The output items will match the type of *stop*: - - >>> list(numeric_range(3.5)) - [0.0, 1.0, 2.0, 3.0] - - With only *start* and *stop* specified, *step* defaults to ``1``. The - output items will match the type of *start*: - - >>> from decimal import Decimal - >>> start = Decimal('2.1') - >>> stop = Decimal('5.1') - >>> list(numeric_range(start, stop)) - [Decimal('2.1'), Decimal('3.1'), Decimal('4.1')] - - With *start*, *stop*, and *step* specified the output items will match - the type of ``start + step``: - - >>> from fractions import Fraction - >>> start = Fraction(1, 2) # Start at 1/2 - >>> stop = Fraction(5, 2) # End at 5/2 - >>> step = Fraction(1, 2) # Count by 1/2 - >>> list(numeric_range(start, stop, step)) - [Fraction(1, 2), Fraction(1, 1), Fraction(3, 2), Fraction(2, 1)] - - If *step* is zero, ``ValueError`` is raised. Negative steps are supported: - - >>> list(numeric_range(3, -1, -1.0)) - [3.0, 2.0, 1.0, 0.0] - - Be aware of the limitations of floating point numbers; the representation - of the yielded numbers may be surprising. - - ``datetime.datetime`` objects can be used for *start* and *stop*, if *step* - is a ``datetime.timedelta`` object: - - >>> import datetime - >>> start = datetime.datetime(2019, 1, 1) - >>> stop = datetime.datetime(2019, 1, 3) - >>> step = datetime.timedelta(days=1) - >>> items = iter(numeric_range(start, stop, step)) - >>> next(items) - datetime.datetime(2019, 1, 1, 0, 0) - >>> next(items) - datetime.datetime(2019, 1, 2, 0, 0) - - """ - - _EMPTY_HASH = hash(range(0, 0)) - - def __init__(self, *args): - argc = len(args) - if argc == 1: - (self._stop,) = args - self._start = type(self._stop)(0) - self._step = type(self._stop - self._start)(1) - elif argc == 2: - self._start, self._stop = args - self._step = type(self._stop - self._start)(1) - elif argc == 3: - self._start, self._stop, self._step = args - elif argc == 0: - raise TypeError( - 'numeric_range expected at least ' - '1 argument, got {}'.format(argc) - ) - else: - raise TypeError( - 'numeric_range expected at most ' - '3 arguments, got {}'.format(argc) - ) - - self._zero = type(self._step)(0) - if self._step == self._zero: - raise ValueError('numeric_range() arg 3 must not be zero') - self._growing = self._step > self._zero - self._init_len() - - def __bool__(self): - if self._growing: - return self._start < self._stop - else: - return self._start > self._stop - - def __contains__(self, elem): - if self._growing: - if self._start <= elem < self._stop: - return (elem - self._start) % self._step == self._zero - else: - if self._start >= elem > self._stop: - return (self._start - elem) % (-self._step) == self._zero - - return False - - def __eq__(self, other): - if isinstance(other, numeric_range): - empty_self = not bool(self) - empty_other = not bool(other) - if empty_self or empty_other: - return empty_self and empty_other # True if both empty - else: - return ( - self._start == other._start - and self._step == other._step - and self._get_by_index(-1) == other._get_by_index(-1) - ) - else: - return False - - def __getitem__(self, key): - if isinstance(key, int): - return self._get_by_index(key) - elif isinstance(key, slice): - step = self._step if key.step is None else key.step * self._step - - if key.start is None or key.start <= -self._len: - start = self._start - elif key.start >= self._len: - start = self._stop - else: # -self._len < key.start < self._len - start = self._get_by_index(key.start) - - if key.stop is None or key.stop >= self._len: - stop = self._stop - elif key.stop <= -self._len: - stop = self._start - else: # -self._len < key.stop < self._len - stop = self._get_by_index(key.stop) - - return numeric_range(start, stop, step) - else: - raise TypeError( - 'numeric range indices must be ' - 'integers or slices, not {}'.format(type(key).__name__) - ) - - def __hash__(self): - if self: - return hash((self._start, self._get_by_index(-1), self._step)) - else: - return self._EMPTY_HASH - - def __iter__(self): - values = (self._start + (n * self._step) for n in count()) - if self._growing: - return takewhile(partial(gt, self._stop), values) - else: - return takewhile(partial(lt, self._stop), values) - - def __len__(self): - return self._len - - def _init_len(self): - if self._growing: - start = self._start - stop = self._stop - step = self._step - else: - start = self._stop - stop = self._start - step = -self._step - distance = stop - start - if distance <= self._zero: - self._len = 0 - else: # distance > 0 and step > 0: regular euclidean division - q, r = divmod(distance, step) - self._len = int(q) + int(r != self._zero) - - def __reduce__(self): - return numeric_range, (self._start, self._stop, self._step) - - def __repr__(self): - if self._step == 1: - return "numeric_range({}, {})".format( - repr(self._start), repr(self._stop) - ) - else: - return "numeric_range({}, {}, {})".format( - repr(self._start), repr(self._stop), repr(self._step) - ) - - def __reversed__(self): - return iter( - numeric_range( - self._get_by_index(-1), self._start - self._step, -self._step - ) - ) - - def count(self, value): - return int(value in self) - - def index(self, value): - if self._growing: - if self._start <= value < self._stop: - q, r = divmod(value - self._start, self._step) - if r == self._zero: - return int(q) - else: - if self._start >= value > self._stop: - q, r = divmod(self._start - value, -self._step) - if r == self._zero: - return int(q) - - raise ValueError("{} is not in numeric range".format(value)) - - def _get_by_index(self, i): - if i < 0: - i += self._len - if i < 0 or i >= self._len: - raise IndexError("numeric range object index out of range") - return self._start + i * self._step - - -def count_cycle(iterable, n=None): - """Cycle through the items from *iterable* up to *n* times, yielding - the number of completed cycles along with each item. If *n* is omitted the - process repeats indefinitely. - - >>> list(count_cycle('AB', 3)) - [(0, 'A'), (0, 'B'), (1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')] - - """ - iterable = tuple(iterable) - if not iterable: - return iter(()) - counter = count() if n is None else range(n) - return ((i, item) for i in counter for item in iterable) - - -def mark_ends(iterable): - """Yield 3-tuples of the form ``(is_first, is_last, item)``. - - >>> list(mark_ends('ABC')) - [(True, False, 'A'), (False, False, 'B'), (False, True, 'C')] - - Use this when looping over an iterable to take special action on its first - and/or last items: - - >>> iterable = ['Header', 100, 200, 'Footer'] - >>> total = 0 - >>> for is_first, is_last, item in mark_ends(iterable): - ... if is_first: - ... continue # Skip the header - ... if is_last: - ... continue # Skip the footer - ... total += item - >>> print(total) - 300 - """ - it = iter(iterable) - - try: - b = next(it) - except StopIteration: - return - - try: - for i in count(): - a = b - b = next(it) - yield i == 0, False, a - - except StopIteration: - yield i == 0, True, a - - -def locate(iterable, pred=bool, window_size=None): - """Yield the index of each item in *iterable* for which *pred* returns - ``True``. - - *pred* defaults to :func:`bool`, which will select truthy items: - - >>> list(locate([0, 1, 1, 0, 1, 0, 0])) - [1, 2, 4] - - Set *pred* to a custom function to, e.g., find the indexes for a particular - item. - - >>> list(locate(['a', 'b', 'c', 'b'], lambda x: x == 'b')) - [1, 3] - - If *window_size* is given, then the *pred* function will be called with - that many items. This enables searching for sub-sequences: - - >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] - >>> pred = lambda *args: args == (1, 2, 3) - >>> list(locate(iterable, pred=pred, window_size=3)) - [1, 5, 9] - - Use with :func:`seekable` to find indexes and then retrieve the associated - items: - - >>> from itertools import count - >>> from more_itertools import seekable - >>> source = (3 * n + 1 if (n % 2) else n // 2 for n in count()) - >>> it = seekable(source) - >>> pred = lambda x: x > 100 - >>> indexes = locate(it, pred=pred) - >>> i = next(indexes) - >>> it.seek(i) - >>> next(it) - 106 - - """ - if window_size is None: - return compress(count(), map(pred, iterable)) - - if window_size < 1: - raise ValueError('window size must be at least 1') - - it = windowed(iterable, window_size, fillvalue=_marker) - return compress(count(), starmap(pred, it)) - - -def lstrip(iterable, pred): - """Yield the items from *iterable*, but strip any from the beginning - for which *pred* returns ``True``. - - For example, to remove a set of items from the start of an iterable: - - >>> iterable = (None, False, None, 1, 2, None, 3, False, None) - >>> pred = lambda x: x in {None, False, ''} - >>> list(lstrip(iterable, pred)) - [1, 2, None, 3, False, None] - - This function is analogous to to :func:`str.lstrip`, and is essentially - an wrapper for :func:`itertools.dropwhile`. - - """ - return dropwhile(pred, iterable) - - -def rstrip(iterable, pred): - """Yield the items from *iterable*, but strip any from the end - for which *pred* returns ``True``. - - For example, to remove a set of items from the end of an iterable: - - >>> iterable = (None, False, None, 1, 2, None, 3, False, None) - >>> pred = lambda x: x in {None, False, ''} - >>> list(rstrip(iterable, pred)) - [None, False, None, 1, 2, None, 3] - - This function is analogous to :func:`str.rstrip`. - - """ - cache = [] - cache_append = cache.append - cache_clear = cache.clear - for x in iterable: - if pred(x): - cache_append(x) - else: - yield from cache - cache_clear() - yield x - - -def strip(iterable, pred): - """Yield the items from *iterable*, but strip any from the - beginning and end for which *pred* returns ``True``. - - For example, to remove a set of items from both ends of an iterable: - - >>> iterable = (None, False, None, 1, 2, None, 3, False, None) - >>> pred = lambda x: x in {None, False, ''} - >>> list(strip(iterable, pred)) - [1, 2, None, 3] - - This function is analogous to :func:`str.strip`. - - """ - return rstrip(lstrip(iterable, pred), pred) - - -class islice_extended: - """An extension of :func:`itertools.islice` that supports negative values - for *stop*, *start*, and *step*. - - >>> iterable = iter('abcdefgh') - >>> list(islice_extended(iterable, -4, -1)) - ['e', 'f', 'g'] - - Slices with negative values require some caching of *iterable*, but this - function takes care to minimize the amount of memory required. - - For example, you can use a negative step with an infinite iterator: - - >>> from itertools import count - >>> list(islice_extended(count(), 110, 99, -2)) - [110, 108, 106, 104, 102, 100] - - You can also use slice notation directly: - - >>> iterable = map(str, count()) - >>> it = islice_extended(iterable)[10:20:2] - >>> list(it) - ['10', '12', '14', '16', '18'] - - """ - - def __init__(self, iterable, *args): - it = iter(iterable) - if args: - self._iterable = _islice_helper(it, slice(*args)) - else: - self._iterable = it - - def __iter__(self): - return self - - def __next__(self): - return next(self._iterable) - - def __getitem__(self, key): - if isinstance(key, slice): - return islice_extended(_islice_helper(self._iterable, key)) - - raise TypeError('islice_extended.__getitem__ argument must be a slice') - - -def _islice_helper(it, s): - start = s.start - stop = s.stop - if s.step == 0: - raise ValueError('step argument must be a non-zero integer or None.') - step = s.step or 1 - - if step > 0: - start = 0 if (start is None) else start - - if start < 0: - # Consume all but the last -start items - cache = deque(enumerate(it, 1), maxlen=-start) - len_iter = cache[-1][0] if cache else 0 - - # Adjust start to be positive - i = max(len_iter + start, 0) - - # Adjust stop to be positive - if stop is None: - j = len_iter - elif stop >= 0: - j = min(stop, len_iter) - else: - j = max(len_iter + stop, 0) - - # Slice the cache - n = j - i - if n <= 0: - return - - for index, item in islice(cache, 0, n, step): - yield item - elif (stop is not None) and (stop < 0): - # Advance to the start position - next(islice(it, start, start), None) - - # When stop is negative, we have to carry -stop items while - # iterating - cache = deque(islice(it, -stop), maxlen=-stop) - - for index, item in enumerate(it): - cached_item = cache.popleft() - if index % step == 0: - yield cached_item - cache.append(item) - else: - # When both start and stop are positive we have the normal case - yield from islice(it, start, stop, step) - else: - start = -1 if (start is None) else start - - if (stop is not None) and (stop < 0): - # Consume all but the last items - n = -stop - 1 - cache = deque(enumerate(it, 1), maxlen=n) - len_iter = cache[-1][0] if cache else 0 - - # If start and stop are both negative they are comparable and - # we can just slice. Otherwise we can adjust start to be negative - # and then slice. - if start < 0: - i, j = start, stop - else: - i, j = min(start - len_iter, -1), None - - for index, item in list(cache)[i:j:step]: - yield item - else: - # Advance to the stop position - if stop is not None: - m = stop + 1 - next(islice(it, m, m), None) - - # stop is positive, so if start is negative they are not comparable - # and we need the rest of the items. - if start < 0: - i = start - n = None - # stop is None and start is positive, so we just need items up to - # the start index. - elif stop is None: - i = None - n = start + 1 - # Both stop and start are positive, so they are comparable. - else: - i = None - n = start - stop - if n <= 0: - return - - cache = list(islice(it, n)) - - yield from cache[i::step] - - -def always_reversible(iterable): - """An extension of :func:`reversed` that supports all iterables, not - just those which implement the ``Reversible`` or ``Sequence`` protocols. - - >>> print(*always_reversible(x for x in range(3))) - 2 1 0 - - If the iterable is already reversible, this function returns the - result of :func:`reversed()`. If the iterable is not reversible, - this function will cache the remaining items in the iterable and - yield them in reverse order, which may require significant storage. - """ - try: - return reversed(iterable) - except TypeError: - return reversed(list(iterable)) - - -def consecutive_groups(iterable, ordering=lambda x: x): - """Yield groups of consecutive items using :func:`itertools.groupby`. - The *ordering* function determines whether two items are adjacent by - returning their position. - - By default, the ordering function is the identity function. This is - suitable for finding runs of numbers: - - >>> iterable = [1, 10, 11, 12, 20, 30, 31, 32, 33, 40] - >>> for group in consecutive_groups(iterable): - ... print(list(group)) - [1] - [10, 11, 12] - [20] - [30, 31, 32, 33] - [40] - - For finding runs of adjacent letters, try using the :meth:`index` method - of a string of letters: - - >>> from string import ascii_lowercase - >>> iterable = 'abcdfgilmnop' - >>> ordering = ascii_lowercase.index - >>> for group in consecutive_groups(iterable, ordering): - ... print(list(group)) - ['a', 'b', 'c', 'd'] - ['f', 'g'] - ['i'] - ['l', 'm', 'n', 'o', 'p'] - - Each group of consecutive items is an iterator that shares it source with - *iterable*. When an an output group is advanced, the previous group is - no longer available unless its elements are copied (e.g., into a ``list``). - - >>> iterable = [1, 2, 11, 12, 21, 22] - >>> saved_groups = [] - >>> for group in consecutive_groups(iterable): - ... saved_groups.append(list(group)) # Copy group elements - >>> saved_groups - [[1, 2], [11, 12], [21, 22]] - - """ - for k, g in groupby( - enumerate(iterable), key=lambda x: x[0] - ordering(x[1]) - ): - yield map(itemgetter(1), g) - - -def difference(iterable, func=sub, *, initial=None): - """This function is the inverse of :func:`itertools.accumulate`. By default - it will compute the first difference of *iterable* using - :func:`operator.sub`: - - >>> from itertools import accumulate - >>> iterable = accumulate([0, 1, 2, 3, 4]) # produces 0, 1, 3, 6, 10 - >>> list(difference(iterable)) - [0, 1, 2, 3, 4] - - *func* defaults to :func:`operator.sub`, but other functions can be - specified. They will be applied as follows:: - - A, B, C, D, ... --> A, func(B, A), func(C, B), func(D, C), ... - - For example, to do progressive division: - - >>> iterable = [1, 2, 6, 24, 120] - >>> func = lambda x, y: x // y - >>> list(difference(iterable, func)) - [1, 2, 3, 4, 5] - - If the *initial* keyword is set, the first element will be skipped when - computing successive differences. - - >>> it = [10, 11, 13, 16] # from accumulate([1, 2, 3], initial=10) - >>> list(difference(it, initial=10)) - [1, 2, 3] - - """ - a, b = tee(iterable) - try: - first = [next(b)] - except StopIteration: - return iter([]) - - if initial is not None: - first = [] - - return chain(first, starmap(func, zip(b, a))) - - -class SequenceView(Sequence): - """Return a read-only view of the sequence object *target*. - - :class:`SequenceView` objects are analogous to Python's built-in - "dictionary view" types. They provide a dynamic view of a sequence's items, - meaning that when the sequence updates, so does the view. - - >>> seq = ['0', '1', '2'] - >>> view = SequenceView(seq) - >>> view - SequenceView(['0', '1', '2']) - >>> seq.append('3') - >>> view - SequenceView(['0', '1', '2', '3']) - - Sequence views support indexing, slicing, and length queries. They act - like the underlying sequence, except they don't allow assignment: - - >>> view[1] - '1' - >>> view[1:-1] - ['1', '2'] - >>> len(view) - 4 - - Sequence views are useful as an alternative to copying, as they don't - require (much) extra storage. - - """ - - def __init__(self, target): - if not isinstance(target, Sequence): - raise TypeError - self._target = target - - def __getitem__(self, index): - return self._target[index] - - def __len__(self): - return len(self._target) - - def __repr__(self): - return '{}({})'.format(self.__class__.__name__, repr(self._target)) - - -class seekable: - """Wrap an iterator to allow for seeking backward and forward. This - progressively caches the items in the source iterable so they can be - re-visited. - - Call :meth:`seek` with an index to seek to that position in the source - iterable. - - To "reset" an iterator, seek to ``0``: - - >>> from itertools import count - >>> it = seekable((str(n) for n in count())) - >>> next(it), next(it), next(it) - ('0', '1', '2') - >>> it.seek(0) - >>> next(it), next(it), next(it) - ('0', '1', '2') - >>> next(it) - '3' - - You can also seek forward: - - >>> it = seekable((str(n) for n in range(20))) - >>> it.seek(10) - >>> next(it) - '10' - >>> it.seek(20) # Seeking past the end of the source isn't a problem - >>> list(it) - [] - >>> it.seek(0) # Resetting works even after hitting the end - >>> next(it), next(it), next(it) - ('0', '1', '2') - - Call :meth:`peek` to look ahead one item without advancing the iterator: - - >>> it = seekable('1234') - >>> it.peek() - '1' - >>> list(it) - ['1', '2', '3', '4'] - >>> it.peek(default='empty') - 'empty' - - Before the iterator is at its end, calling :func:`bool` on it will return - ``True``. After it will return ``False``: - - >>> it = seekable('5678') - >>> bool(it) - True - >>> list(it) - ['5', '6', '7', '8'] - >>> bool(it) - False - - You may view the contents of the cache with the :meth:`elements` method. - That returns a :class:`SequenceView`, a view that updates automatically: - - >>> it = seekable((str(n) for n in range(10))) - >>> next(it), next(it), next(it) - ('0', '1', '2') - >>> elements = it.elements() - >>> elements - SequenceView(['0', '1', '2']) - >>> next(it) - '3' - >>> elements - SequenceView(['0', '1', '2', '3']) - - By default, the cache grows as the source iterable progresses, so beware of - wrapping very large or infinite iterables. Supply *maxlen* to limit the - size of the cache (this of course limits how far back you can seek). - - >>> from itertools import count - >>> it = seekable((str(n) for n in count()), maxlen=2) - >>> next(it), next(it), next(it), next(it) - ('0', '1', '2', '3') - >>> list(it.elements()) - ['2', '3'] - >>> it.seek(0) - >>> next(it), next(it), next(it), next(it) - ('2', '3', '4', '5') - >>> next(it) - '6' - - """ - - def __init__(self, iterable, maxlen=None): - self._source = iter(iterable) - if maxlen is None: - self._cache = [] - else: - self._cache = deque([], maxlen) - self._index = None - - def __iter__(self): - return self - - def __next__(self): - if self._index is not None: - try: - item = self._cache[self._index] - except IndexError: - self._index = None - else: - self._index += 1 - return item - - item = next(self._source) - self._cache.append(item) - return item - - def __bool__(self): - try: - self.peek() - except StopIteration: - return False - return True - - def peek(self, default=_marker): - try: - peeked = next(self) - except StopIteration: - if default is _marker: - raise - return default - if self._index is None: - self._index = len(self._cache) - self._index -= 1 - return peeked - - def elements(self): - return SequenceView(self._cache) - - def seek(self, index): - self._index = index - remainder = index - len(self._cache) - if remainder > 0: - consume(self, remainder) - - -class run_length: - """ - :func:`run_length.encode` compresses an iterable with run-length encoding. - It yields groups of repeated items with the count of how many times they - were repeated: - - >>> uncompressed = 'abbcccdddd' - >>> list(run_length.encode(uncompressed)) - [('a', 1), ('b', 2), ('c', 3), ('d', 4)] - - :func:`run_length.decode` decompresses an iterable that was previously - compressed with run-length encoding. It yields the items of the - decompressed iterable: - - >>> compressed = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] - >>> list(run_length.decode(compressed)) - ['a', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd', 'd'] - - """ - - @staticmethod - def encode(iterable): - return ((k, ilen(g)) for k, g in groupby(iterable)) - - @staticmethod - def decode(iterable): - return chain.from_iterable(repeat(k, n) for k, n in iterable) - - -def exactly_n(iterable, n, predicate=bool): - """Return ``True`` if exactly ``n`` items in the iterable are ``True`` - according to the *predicate* function. - - >>> exactly_n([True, True, False], 2) - True - >>> exactly_n([True, True, False], 1) - False - >>> exactly_n([0, 1, 2, 3, 4, 5], 3, lambda x: x < 3) - True - - The iterable will be advanced until ``n + 1`` truthy items are encountered, - so avoid calling it on infinite iterables. - - """ - return len(take(n + 1, filter(predicate, iterable))) == n - - -def circular_shifts(iterable): - """Return a list of circular shifts of *iterable*. - - >>> circular_shifts(range(4)) - [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)] - """ - lst = list(iterable) - return take(len(lst), windowed(cycle(lst), len(lst))) - - -def make_decorator(wrapping_func, result_index=0): - """Return a decorator version of *wrapping_func*, which is a function that - modifies an iterable. *result_index* is the position in that function's - signature where the iterable goes. - - This lets you use itertools on the "production end," i.e. at function - definition. This can augment what the function returns without changing the - function's code. - - For example, to produce a decorator version of :func:`chunked`: - - >>> from more_itertools import chunked - >>> chunker = make_decorator(chunked, result_index=0) - >>> @chunker(3) - ... def iter_range(n): - ... return iter(range(n)) - ... - >>> list(iter_range(9)) - [[0, 1, 2], [3, 4, 5], [6, 7, 8]] - - To only allow truthy items to be returned: - - >>> truth_serum = make_decorator(filter, result_index=1) - >>> @truth_serum(bool) - ... def boolean_test(): - ... return [0, 1, '', ' ', False, True] - ... - >>> list(boolean_test()) - [1, ' ', True] - - The :func:`peekable` and :func:`seekable` wrappers make for practical - decorators: - - >>> from more_itertools import peekable - >>> peekable_function = make_decorator(peekable) - >>> @peekable_function() - ... def str_range(*args): - ... return (str(x) for x in range(*args)) - ... - >>> it = str_range(1, 20, 2) - >>> next(it), next(it), next(it) - ('1', '3', '5') - >>> it.peek() - '7' - >>> next(it) - '7' - - """ - # See https://sites.google.com/site/bbayles/index/decorator_factory for - # notes on how this works. - def decorator(*wrapping_args, **wrapping_kwargs): - def outer_wrapper(f): - def inner_wrapper(*args, **kwargs): - result = f(*args, **kwargs) - wrapping_args_ = list(wrapping_args) - wrapping_args_.insert(result_index, result) - return wrapping_func(*wrapping_args_, **wrapping_kwargs) - - return inner_wrapper - - return outer_wrapper - - return decorator - - -def map_reduce(iterable, keyfunc, valuefunc=None, reducefunc=None): - """Return a dictionary that maps the items in *iterable* to categories - defined by *keyfunc*, transforms them with *valuefunc*, and - then summarizes them by category with *reducefunc*. - - *valuefunc* defaults to the identity function if it is unspecified. - If *reducefunc* is unspecified, no summarization takes place: - - >>> keyfunc = lambda x: x.upper() - >>> result = map_reduce('abbccc', keyfunc) - >>> sorted(result.items()) - [('A', ['a']), ('B', ['b', 'b']), ('C', ['c', 'c', 'c'])] - - Specifying *valuefunc* transforms the categorized items: - - >>> keyfunc = lambda x: x.upper() - >>> valuefunc = lambda x: 1 - >>> result = map_reduce('abbccc', keyfunc, valuefunc) - >>> sorted(result.items()) - [('A', [1]), ('B', [1, 1]), ('C', [1, 1, 1])] - - Specifying *reducefunc* summarizes the categorized items: - - >>> keyfunc = lambda x: x.upper() - >>> valuefunc = lambda x: 1 - >>> reducefunc = sum - >>> result = map_reduce('abbccc', keyfunc, valuefunc, reducefunc) - >>> sorted(result.items()) - [('A', 1), ('B', 2), ('C', 3)] - - You may want to filter the input iterable before applying the map/reduce - procedure: - - >>> all_items = range(30) - >>> items = [x for x in all_items if 10 <= x <= 20] # Filter - >>> keyfunc = lambda x: x % 2 # Evens map to 0; odds to 1 - >>> categories = map_reduce(items, keyfunc=keyfunc) - >>> sorted(categories.items()) - [(0, [10, 12, 14, 16, 18, 20]), (1, [11, 13, 15, 17, 19])] - >>> summaries = map_reduce(items, keyfunc=keyfunc, reducefunc=sum) - >>> sorted(summaries.items()) - [(0, 90), (1, 75)] - - Note that all items in the iterable are gathered into a list before the - summarization step, which may require significant storage. - - The returned object is a :obj:`collections.defaultdict` with the - ``default_factory`` set to ``None``, such that it behaves like a normal - dictionary. - - """ - valuefunc = (lambda x: x) if (valuefunc is None) else valuefunc - - ret = defaultdict(list) - for item in iterable: - key = keyfunc(item) - value = valuefunc(item) - ret[key].append(value) - - if reducefunc is not None: - for key, value_list in ret.items(): - ret[key] = reducefunc(value_list) - - ret.default_factory = None - return ret - - -def rlocate(iterable, pred=bool, window_size=None): - """Yield the index of each item in *iterable* for which *pred* returns - ``True``, starting from the right and moving left. - - *pred* defaults to :func:`bool`, which will select truthy items: - - >>> list(rlocate([0, 1, 1, 0, 1, 0, 0])) # Truthy at 1, 2, and 4 - [4, 2, 1] - - Set *pred* to a custom function to, e.g., find the indexes for a particular - item: - - >>> iterable = iter('abcb') - >>> pred = lambda x: x == 'b' - >>> list(rlocate(iterable, pred)) - [3, 1] - - If *window_size* is given, then the *pred* function will be called with - that many items. This enables searching for sub-sequences: - - >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] - >>> pred = lambda *args: args == (1, 2, 3) - >>> list(rlocate(iterable, pred=pred, window_size=3)) - [9, 5, 1] - - Beware, this function won't return anything for infinite iterables. - If *iterable* is reversible, ``rlocate`` will reverse it and search from - the right. Otherwise, it will search from the left and return the results - in reverse order. - - See :func:`locate` to for other example applications. - - """ - if window_size is None: - try: - len_iter = len(iterable) - return (len_iter - i - 1 for i in locate(reversed(iterable), pred)) - except TypeError: - pass - - return reversed(list(locate(iterable, pred, window_size))) - - -def replace(iterable, pred, substitutes, count=None, window_size=1): - """Yield the items from *iterable*, replacing the items for which *pred* - returns ``True`` with the items from the iterable *substitutes*. - - >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1] - >>> pred = lambda x: x == 0 - >>> substitutes = (2, 3) - >>> list(replace(iterable, pred, substitutes)) - [1, 1, 2, 3, 1, 1, 2, 3, 1, 1] - - If *count* is given, the number of replacements will be limited: - - >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1, 0] - >>> pred = lambda x: x == 0 - >>> substitutes = [None] - >>> list(replace(iterable, pred, substitutes, count=2)) - [1, 1, None, 1, 1, None, 1, 1, 0] - - Use *window_size* to control the number of items passed as arguments to - *pred*. This allows for locating and replacing subsequences. - - >>> iterable = [0, 1, 2, 5, 0, 1, 2, 5] - >>> window_size = 3 - >>> pred = lambda *args: args == (0, 1, 2) # 3 items passed to pred - >>> substitutes = [3, 4] # Splice in these items - >>> list(replace(iterable, pred, substitutes, window_size=window_size)) - [3, 4, 5, 3, 4, 5] - - """ - if window_size < 1: - raise ValueError('window_size must be at least 1') - - # Save the substitutes iterable, since it's used more than once - substitutes = tuple(substitutes) - - # Add padding such that the number of windows matches the length of the - # iterable - it = chain(iterable, [_marker] * (window_size - 1)) - windows = windowed(it, window_size) - - n = 0 - for w in windows: - # If the current window matches our predicate (and we haven't hit - # our maximum number of replacements), splice in the substitutes - # and then consume the following windows that overlap with this one. - # For example, if the iterable is (0, 1, 2, 3, 4...) - # and the window size is 2, we have (0, 1), (1, 2), (2, 3)... - # If the predicate matches on (0, 1), we need to zap (0, 1) and (1, 2) - if pred(*w): - if (count is None) or (n < count): - n += 1 - yield from substitutes - consume(windows, window_size - 1) - continue - - # If there was no match (or we've reached the replacement limit), - # yield the first item from the window. - if w and (w[0] is not _marker): - yield w[0] - - -def partitions(iterable): - """Yield all possible order-preserving partitions of *iterable*. - - >>> iterable = 'abc' - >>> for part in partitions(iterable): - ... print([''.join(p) for p in part]) - ['abc'] - ['a', 'bc'] - ['ab', 'c'] - ['a', 'b', 'c'] - - This is unrelated to :func:`partition`. - - """ - sequence = list(iterable) - n = len(sequence) - for i in powerset(range(1, n)): - yield [sequence[i:j] for i, j in zip((0,) + i, i + (n,))] - - -def set_partitions(iterable, k=None): - """ - Yield the set partitions of *iterable* into *k* parts. Set partitions are - not order-preserving. - - >>> iterable = 'abc' - >>> for part in set_partitions(iterable, 2): - ... print([''.join(p) for p in part]) - ['a', 'bc'] - ['ab', 'c'] - ['b', 'ac'] - - - If *k* is not given, every set partition is generated. - - >>> iterable = 'abc' - >>> for part in set_partitions(iterable): - ... print([''.join(p) for p in part]) - ['abc'] - ['a', 'bc'] - ['ab', 'c'] - ['b', 'ac'] - ['a', 'b', 'c'] - - """ - L = list(iterable) - n = len(L) - if k is not None: - if k < 1: - raise ValueError( - "Can't partition in a negative or zero number of groups" - ) - elif k > n: - return - - def set_partitions_helper(L, k): - n = len(L) - if k == 1: - yield [L] - elif n == k: - yield [[s] for s in L] - else: - e, *M = L - for p in set_partitions_helper(M, k - 1): - yield [[e], *p] - for p in set_partitions_helper(M, k): - for i in range(len(p)): - yield p[:i] + [[e] + p[i]] + p[i + 1 :] - - if k is None: - for k in range(1, n + 1): - yield from set_partitions_helper(L, k) - else: - yield from set_partitions_helper(L, k) - - -class time_limited: - """ - Yield items from *iterable* until *limit_seconds* have passed. - If the time limit expires before all items have been yielded, the - ``timed_out`` parameter will be set to ``True``. - - >>> from time import sleep - >>> def generator(): - ... yield 1 - ... yield 2 - ... sleep(0.2) - ... yield 3 - >>> iterable = time_limited(0.1, generator()) - >>> list(iterable) - [1, 2] - >>> iterable.timed_out - True - - Note that the time is checked before each item is yielded, and iteration - stops if the time elapsed is greater than *limit_seconds*. If your time - limit is 1 second, but it takes 2 seconds to generate the first item from - the iterable, the function will run for 2 seconds and not yield anything. - - """ - - def __init__(self, limit_seconds, iterable): - if limit_seconds < 0: - raise ValueError('limit_seconds must be positive') - self.limit_seconds = limit_seconds - self._iterable = iter(iterable) - self._start_time = monotonic() - self.timed_out = False - - def __iter__(self): - return self - - def __next__(self): - item = next(self._iterable) - if monotonic() - self._start_time > self.limit_seconds: - self.timed_out = True - raise StopIteration - - return item - - -def only(iterable, default=None, too_long=None): - """If *iterable* has only one item, return it. - If it has zero items, return *default*. - If it has more than one item, raise the exception given by *too_long*, - which is ``ValueError`` by default. - - >>> only([], default='missing') - 'missing' - >>> only([1]) - 1 - >>> only([1, 2]) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - ValueError: Expected exactly one item in iterable, but got 1, 2, - and perhaps more.' - >>> only([1, 2], too_long=TypeError) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - TypeError - - Note that :func:`only` attempts to advance *iterable* twice to ensure there - is only one item. See :func:`spy` or :func:`peekable` to check - iterable contents less destructively. - """ - it = iter(iterable) - first_value = next(it, default) - - try: - second_value = next(it) - except StopIteration: - pass - else: - msg = ( - 'Expected exactly one item in iterable, but got {!r}, {!r}, ' - 'and perhaps more.'.format(first_value, second_value) - ) - raise too_long or ValueError(msg) - - return first_value - - -def ichunked(iterable, n): - """Break *iterable* into sub-iterables with *n* elements each. - :func:`ichunked` is like :func:`chunked`, but it yields iterables - instead of lists. - - If the sub-iterables are read in order, the elements of *iterable* - won't be stored in memory. - If they are read out of order, :func:`itertools.tee` is used to cache - elements as necessary. - - >>> from itertools import count - >>> all_chunks = ichunked(count(), 4) - >>> c_1, c_2, c_3 = next(all_chunks), next(all_chunks), next(all_chunks) - >>> list(c_2) # c_1's elements have been cached; c_3's haven't been - [4, 5, 6, 7] - >>> list(c_1) - [0, 1, 2, 3] - >>> list(c_3) - [8, 9, 10, 11] - - """ - source = iter(iterable) - - while True: - # Check to see whether we're at the end of the source iterable - item = next(source, _marker) - if item is _marker: - return - - # Clone the source and yield an n-length slice - source, it = tee(chain([item], source)) - yield islice(it, n) - - # Advance the source iterable - consume(source, n) - - -def distinct_combinations(iterable, r): - """Yield the distinct combinations of *r* items taken from *iterable*. - - >>> list(distinct_combinations([0, 0, 1], 2)) - [(0, 0), (0, 1)] - - Equivalent to ``set(combinations(iterable))``, except duplicates are not - generated and thrown away. For larger input sequences this is much more - efficient. - - """ - if r < 0: - raise ValueError('r must be non-negative') - elif r == 0: - yield () - return - pool = tuple(iterable) - generators = [unique_everseen(enumerate(pool), key=itemgetter(1))] - current_combo = [None] * r - level = 0 - while generators: - try: - cur_idx, p = next(generators[-1]) - except StopIteration: - generators.pop() - level -= 1 - continue - current_combo[level] = p - if level + 1 == r: - yield tuple(current_combo) - else: - generators.append( - unique_everseen( - enumerate(pool[cur_idx + 1 :], cur_idx + 1), - key=itemgetter(1), - ) - ) - level += 1 - - -def filter_except(validator, iterable, *exceptions): - """Yield the items from *iterable* for which the *validator* function does - not raise one of the specified *exceptions*. - - *validator* is called for each item in *iterable*. - It should be a function that accepts one argument and raises an exception - if that item is not valid. - - >>> iterable = ['1', '2', 'three', '4', None] - >>> list(filter_except(int, iterable, ValueError, TypeError)) - ['1', '2', '4'] - - If an exception other than one given by *exceptions* is raised by - *validator*, it is raised like normal. - """ - for item in iterable: - try: - validator(item) - except exceptions: - pass - else: - yield item - - -def map_except(function, iterable, *exceptions): - """Transform each item from *iterable* with *function* and yield the - result, unless *function* raises one of the specified *exceptions*. - - *function* is called to transform each item in *iterable*. - It should be a accept one argument. - - >>> iterable = ['1', '2', 'three', '4', None] - >>> list(map_except(int, iterable, ValueError, TypeError)) - [1, 2, 4] - - If an exception other than one given by *exceptions* is raised by - *function*, it is raised like normal. - """ - for item in iterable: - try: - yield function(item) - except exceptions: - pass - - -def _sample_unweighted(iterable, k): - # Implementation of "Algorithm L" from the 1994 paper by Kim-Hung Li: - # "Reservoir-Sampling Algorithms of Time Complexity O(n(1+log(N/n)))". - - # Fill up the reservoir (collection of samples) with the first `k` samples - reservoir = take(k, iterable) - - # Generate random number that's the largest in a sample of k U(0,1) numbers - # Largest order statistic: https://en.wikipedia.org/wiki/Order_statistic - W = exp(log(random()) / k) - - # The number of elements to skip before changing the reservoir is a random - # number with a geometric distribution. Sample it using random() and logs. - next_index = k + floor(log(random()) / log(1 - W)) - - for index, element in enumerate(iterable, k): - - if index == next_index: - reservoir[randrange(k)] = element - # The new W is the largest in a sample of k U(0, `old_W`) numbers - W *= exp(log(random()) / k) - next_index += floor(log(random()) / log(1 - W)) + 1 - - return reservoir - - -def _sample_weighted(iterable, k, weights): - # Implementation of "A-ExpJ" from the 2006 paper by Efraimidis et al. : - # "Weighted random sampling with a reservoir". - - # Log-transform for numerical stability for weights that are small/large - weight_keys = (log(random()) / weight for weight in weights) - - # Fill up the reservoir (collection of samples) with the first `k` - # weight-keys and elements, then heapify the list. - reservoir = take(k, zip(weight_keys, iterable)) - heapify(reservoir) - - # The number of jumps before changing the reservoir is a random variable - # with an exponential distribution. Sample it using random() and logs. - smallest_weight_key, _ = reservoir[0] - weights_to_skip = log(random()) / smallest_weight_key - - for weight, element in zip(weights, iterable): - if weight >= weights_to_skip: - # The notation here is consistent with the paper, but we store - # the weight-keys in log-space for better numerical stability. - smallest_weight_key, _ = reservoir[0] - t_w = exp(weight * smallest_weight_key) - r_2 = uniform(t_w, 1) # generate U(t_w, 1) - weight_key = log(r_2) / weight - heapreplace(reservoir, (weight_key, element)) - smallest_weight_key, _ = reservoir[0] - weights_to_skip = log(random()) / smallest_weight_key - else: - weights_to_skip -= weight - - # Equivalent to [element for weight_key, element in sorted(reservoir)] - return [heappop(reservoir)[1] for _ in range(k)] - - -def sample(iterable, k, weights=None): - """Return a *k*-length list of elements chosen (without replacement) - from the *iterable*. Like :func:`random.sample`, but works on iterables - of unknown length. - - >>> iterable = range(100) - >>> sample(iterable, 5) # doctest: +SKIP - [81, 60, 96, 16, 4] - - An iterable with *weights* may also be given: - - >>> iterable = range(100) - >>> weights = (i * i + 1 for i in range(100)) - >>> sampled = sample(iterable, 5, weights=weights) # doctest: +SKIP - [79, 67, 74, 66, 78] - - The algorithm can also be used to generate weighted random permutations. - The relative weight of each item determines the probability that it - appears late in the permutation. - - >>> data = "abcdefgh" - >>> weights = range(1, len(data) + 1) - >>> sample(data, k=len(data), weights=weights) # doctest: +SKIP - ['c', 'a', 'b', 'e', 'g', 'd', 'h', 'f'] - """ - if k == 0: - return [] - - iterable = iter(iterable) - if weights is None: - return _sample_unweighted(iterable, k) - else: - weights = iter(weights) - return _sample_weighted(iterable, k, weights) - - -def is_sorted(iterable, key=None, reverse=False): - """Returns ``True`` if the items of iterable are in sorted order, and - ``False`` otherwise. *key* and *reverse* have the same meaning that they do - in the built-in :func:`sorted` function. - - >>> is_sorted(['1', '2', '3', '4', '5'], key=int) - True - >>> is_sorted([5, 4, 3, 1, 2], reverse=True) - False - - The function returns ``False`` after encountering the first out-of-order - item. If there are no out-of-order items, the iterable is exhausted. - """ - - compare = lt if reverse else gt - it = iterable if (key is None) else map(key, iterable) - return not any(starmap(compare, pairwise(it))) - - -class AbortThread(BaseException): - pass - - -class callback_iter: - """Convert a function that uses callbacks to an iterator. - - Let *func* be a function that takes a `callback` keyword argument. - For example: - - >>> def func(callback=None): - ... for i, c in [(1, 'a'), (2, 'b'), (3, 'c')]: - ... if callback: - ... callback(i, c) - ... return 4 - - - Use ``with callback_iter(func)`` to get an iterator over the parameters - that are delivered to the callback. - - >>> with callback_iter(func) as it: - ... for args, kwargs in it: - ... print(args) - (1, 'a') - (2, 'b') - (3, 'c') - - The function will be called in a background thread. The ``done`` property - indicates whether it has completed execution. - - >>> it.done - True - - If it completes successfully, its return value will be available - in the ``result`` property. - - >>> it.result - 4 - - Notes: - - * If the function uses some keyword argument besides ``callback``, supply - *callback_kwd*. - * If it finished executing, but raised an exception, accessing the - ``result`` property will raise the same exception. - * If it hasn't finished executing, accessing the ``result`` - property from within the ``with`` block will raise ``RuntimeError``. - * If it hasn't finished executing, accessing the ``result`` property from - outside the ``with`` block will raise a - ``more_itertools.AbortThread`` exception. - * Provide *wait_seconds* to adjust how frequently the it is polled for - output. - - """ - - def __init__(self, func, callback_kwd='callback', wait_seconds=0.1): - self._func = func - self._callback_kwd = callback_kwd - self._aborted = False - self._future = None - self._wait_seconds = wait_seconds - self._executor = ThreadPoolExecutor(max_workers=1) - self._iterator = self._reader() - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - self._aborted = True - self._executor.shutdown() - - def __iter__(self): - return self - - def __next__(self): - return next(self._iterator) - - @property - def done(self): - if self._future is None: - return False - return self._future.done() - - @property - def result(self): - if not self.done: - raise RuntimeError('Function has not yet completed') - - return self._future.result() - - def _reader(self): - q = Queue() - - def callback(*args, **kwargs): - if self._aborted: - raise AbortThread('canceled by user') - - q.put((args, kwargs)) - - self._future = self._executor.submit( - self._func, **{self._callback_kwd: callback} - ) - - while True: - try: - item = q.get(timeout=self._wait_seconds) - except Empty: - pass - else: - q.task_done() - yield item - - if self._future.done(): - break - - remaining = [] - while True: - try: - item = q.get_nowait() - except Empty: - break - else: - q.task_done() - remaining.append(item) - q.join() - yield from remaining - - -def windowed_complete(iterable, n): - """ - Yield ``(beginning, middle, end)`` tuples, where: - - * Each ``middle`` has *n* items from *iterable* - * Each ``beginning`` has the items before the ones in ``middle`` - * Each ``end`` has the items after the ones in ``middle`` - - >>> iterable = range(7) - >>> n = 3 - >>> for beginning, middle, end in windowed_complete(iterable, n): - ... print(beginning, middle, end) - () (0, 1, 2) (3, 4, 5, 6) - (0,) (1, 2, 3) (4, 5, 6) - (0, 1) (2, 3, 4) (5, 6) - (0, 1, 2) (3, 4, 5) (6,) - (0, 1, 2, 3) (4, 5, 6) () - - Note that *n* must be at least 0 and most equal to the length of - *iterable*. - - This function will exhaust the iterable and may require significant - storage. - """ - if n < 0: - raise ValueError('n must be >= 0') - - seq = tuple(iterable) - size = len(seq) - - if n > size: - raise ValueError('n must be <= len(seq)') - - for i in range(size - n + 1): - beginning = seq[:i] - middle = seq[i : i + n] - end = seq[i + n :] - yield beginning, middle, end - - -def all_unique(iterable, key=None): - """ - Returns ``True`` if all the elements of *iterable* are unique (no two - elements are equal). - - >>> all_unique('ABCB') - False - - If a *key* function is specified, it will be used to make comparisons. - - >>> all_unique('ABCb') - True - >>> all_unique('ABCb', str.lower) - False - - The function returns as soon as the first non-unique element is - encountered. Iterables with a mix of hashable and unhashable items can - be used, but the function will be slower for unhashable items. - """ - seenset = set() - seenset_add = seenset.add - seenlist = [] - seenlist_add = seenlist.append - for element in map(key, iterable) if key else iterable: - try: - if element in seenset: - return False - seenset_add(element) - except TypeError: - if element in seenlist: - return False - seenlist_add(element) - return True - - -def nth_product(index, *args): - """Equivalent to ``list(product(*args))[index]``. - - The products of *args* can be ordered lexicographically. - :func:`nth_product` computes the product at sort position *index* without - computing the previous products. - - >>> nth_product(8, range(2), range(2), range(2), range(2)) - (1, 0, 0, 0) - - ``IndexError`` will be raised if the given *index* is invalid. - """ - pools = list(map(tuple, reversed(args))) - ns = list(map(len, pools)) - - c = reduce(mul, ns) - - if index < 0: - index += c - - if not 0 <= index < c: - raise IndexError - - result = [] - for pool, n in zip(pools, ns): - result.append(pool[index % n]) - index //= n - - return tuple(reversed(result)) - - -def nth_permutation(iterable, r, index): - """Equivalent to ``list(permutations(iterable, r))[index]``` - - The subsequences of *iterable* that are of length *r* where order is - important can be ordered lexicographically. :func:`nth_permutation` - computes the subsequence at sort position *index* directly, without - computing the previous subsequences. - - >>> nth_permutation('ghijk', 2, 5) - ('h', 'i') - - ``ValueError`` will be raised If *r* is negative or greater than the length - of *iterable*. - ``IndexError`` will be raised if the given *index* is invalid. - """ - pool = list(iterable) - n = len(pool) - - if r is None or r == n: - r, c = n, factorial(n) - elif not 0 <= r < n: - raise ValueError - else: - c = factorial(n) // factorial(n - r) - - if index < 0: - index += c - - if not 0 <= index < c: - raise IndexError - - if c == 0: - return tuple() - - result = [0] * r - q = index * factorial(n) // c if r < n else index - for d in range(1, n + 1): - q, i = divmod(q, d) - if 0 <= n - d < r: - result[n - d] = i - if q == 0: - break - - return tuple(map(pool.pop, result)) - - -def value_chain(*args): - """Yield all arguments passed to the function in the same order in which - they were passed. If an argument itself is iterable then iterate over its - values. - - >>> list(value_chain(1, 2, 3, [4, 5, 6])) - [1, 2, 3, 4, 5, 6] - - Binary and text strings are not considered iterable and are emitted - as-is: - - >>> list(value_chain('12', '34', ['56', '78'])) - ['12', '34', '56', '78'] - - - Multiple levels of nesting are not flattened. - - """ - for value in args: - if isinstance(value, (str, bytes)): - yield value - continue - try: - yield from value - except TypeError: - yield value - - -def product_index(element, *args): - """Equivalent to ``list(product(*args)).index(element)`` - - The products of *args* can be ordered lexicographically. - :func:`product_index` computes the first index of *element* without - computing the previous products. - - >>> product_index([8, 2], range(10), range(5)) - 42 - - ``ValueError`` will be raised if the given *element* isn't in the product - of *args*. - """ - index = 0 - - for x, pool in zip_longest(element, args, fillvalue=_marker): - if x is _marker or pool is _marker: - raise ValueError('element is not a product of args') - - pool = tuple(pool) - index = index * len(pool) + pool.index(x) - - return index - - -def combination_index(element, iterable): - """Equivalent to ``list(combinations(iterable, r)).index(element)`` - - The subsequences of *iterable* that are of length *r* can be ordered - lexicographically. :func:`combination_index` computes the index of the - first *element*, without computing the previous combinations. - - >>> combination_index('adf', 'abcdefg') - 10 - - ``ValueError`` will be raised if the given *element* isn't one of the - combinations of *iterable*. - """ - element = enumerate(element) - k, y = next(element, (None, None)) - if k is None: - return 0 - - indexes = [] - pool = enumerate(iterable) - for n, x in pool: - if x == y: - indexes.append(n) - tmp, y = next(element, (None, None)) - if tmp is None: - break - else: - k = tmp - else: - raise ValueError('element is not a combination of iterable') - - n, _ = last(pool, default=(n, None)) - - # Python versiosn below 3.8 don't have math.comb - index = 1 - for i, j in enumerate(reversed(indexes), start=1): - j = n - j - if i <= j: - index += factorial(j) // (factorial(i) * factorial(j - i)) - - return factorial(n + 1) // (factorial(k + 1) * factorial(n - k)) - index - - -def permutation_index(element, iterable): - """Equivalent to ``list(permutations(iterable, r)).index(element)``` - - The subsequences of *iterable* that are of length *r* where order is - important can be ordered lexicographically. :func:`permutation_index` - computes the index of the first *element* directly, without computing - the previous permutations. - - >>> permutation_index([1, 3, 2], range(5)) - 19 - - ``ValueError`` will be raised if the given *element* isn't one of the - permutations of *iterable*. - """ - index = 0 - pool = list(iterable) - for i, x in zip(range(len(pool), -1, -1), element): - r = pool.index(x) - index = index * i + r - del pool[r] - - return index - - -class countable: - """Wrap *iterable* and keep a count of how many items have been consumed. - - The ``items_seen`` attribute starts at ``0`` and increments as the iterable - is consumed: - - >>> iterable = map(str, range(10)) - >>> it = countable(iterable) - >>> it.items_seen - 0 - >>> next(it), next(it) - ('0', '1') - >>> list(it) - ['2', '3', '4', '5', '6', '7', '8', '9'] - >>> it.items_seen - 10 - """ - - def __init__(self, iterable): - self._it = iter(iterable) - self.items_seen = 0 - - def __iter__(self): - return self - - def __next__(self): - item = next(self._it) - self.items_seen += 1 - - return item diff --git a/_setuptools_vendored/more_itertools/more.pyi b/_setuptools_vendored/more_itertools/more.pyi deleted file mode 100644 index 2fba9cb300..0000000000 --- a/_setuptools_vendored/more_itertools/more.pyi +++ /dev/null @@ -1,480 +0,0 @@ -"""Stubs for more_itertools.more""" - -from typing import ( - Any, - Callable, - Container, - Dict, - Generic, - Hashable, - Iterable, - Iterator, - List, - Optional, - Reversible, - Sequence, - Sized, - Tuple, - Union, - TypeVar, - type_check_only, -) -from types import TracebackType -from typing_extensions import ContextManager, Protocol, Type, overload - -# Type and type variable definitions -_T = TypeVar('_T') -_U = TypeVar('_U') -_V = TypeVar('_V') -_W = TypeVar('_W') -_T_co = TypeVar('_T_co', covariant=True) -_GenFn = TypeVar('_GenFn', bound=Callable[..., Iterator[object]]) -_Raisable = Union[BaseException, 'Type[BaseException]'] - -@type_check_only -class _SizedIterable(Protocol[_T_co], Sized, Iterable[_T_co]): ... - -@type_check_only -class _SizedReversible(Protocol[_T_co], Sized, Reversible[_T_co]): ... - -def chunked( - iterable: Iterable[_T], n: int, strict: bool = ... -) -> Iterator[List[_T]]: ... -@overload -def first(iterable: Iterable[_T]) -> _T: ... -@overload -def first(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... -@overload -def last(iterable: Iterable[_T]) -> _T: ... -@overload -def last(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... -@overload -def nth_or_last(iterable: Iterable[_T], n: int) -> _T: ... -@overload -def nth_or_last( - iterable: Iterable[_T], n: int, default: _U -) -> Union[_T, _U]: ... - -class peekable(Generic[_T], Iterator[_T]): - def __init__(self, iterable: Iterable[_T]) -> None: ... - def __iter__(self) -> peekable[_T]: ... - def __bool__(self) -> bool: ... - @overload - def peek(self) -> _T: ... - @overload - def peek(self, default: _U) -> Union[_T, _U]: ... - def prepend(self, *items: _T) -> None: ... - def __next__(self) -> _T: ... - @overload - def __getitem__(self, index: int) -> _T: ... - @overload - def __getitem__(self, index: slice) -> List[_T]: ... - -def collate(*iterables: Iterable[_T], **kwargs: Any) -> Iterable[_T]: ... -def consumer(func: _GenFn) -> _GenFn: ... -def ilen(iterable: Iterable[object]) -> int: ... -def iterate(func: Callable[[_T], _T], start: _T) -> Iterator[_T]: ... -def with_iter( - context_manager: ContextManager[Iterable[_T]], -) -> Iterator[_T]: ... -def one( - iterable: Iterable[_T], - too_short: Optional[_Raisable] = ..., - too_long: Optional[_Raisable] = ..., -) -> _T: ... -def distinct_permutations( - iterable: Iterable[_T], r: Optional[int] = ... -) -> Iterator[Tuple[_T, ...]]: ... -def intersperse( - e: _U, iterable: Iterable[_T], n: int = ... -) -> Iterator[Union[_T, _U]]: ... -def unique_to_each(*iterables: Iterable[_T]) -> List[List[_T]]: ... -@overload -def windowed( - seq: Iterable[_T], n: int, *, step: int = ... -) -> Iterator[Tuple[Optional[_T], ...]]: ... -@overload -def windowed( - seq: Iterable[_T], n: int, fillvalue: _U, step: int = ... -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... -def substrings(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... -def substrings_indexes( - seq: Sequence[_T], reverse: bool = ... -) -> Iterator[Tuple[Sequence[_T], int, int]]: ... - -class bucket(Generic[_T, _U], Container[_U]): - def __init__( - self, - iterable: Iterable[_T], - key: Callable[[_T], _U], - validator: Optional[Callable[[object], object]] = ..., - ) -> None: ... - def __contains__(self, value: object) -> bool: ... - def __iter__(self) -> Iterator[_U]: ... - def __getitem__(self, value: object) -> Iterator[_T]: ... - -def spy( - iterable: Iterable[_T], n: int = ... -) -> Tuple[List[_T], Iterator[_T]]: ... -def interleave(*iterables: Iterable[_T]) -> Iterator[_T]: ... -def interleave_longest(*iterables: Iterable[_T]) -> Iterator[_T]: ... -def collapse( - iterable: Iterable[Any], - base_type: Optional[type] = ..., - levels: Optional[int] = ..., -) -> Iterator[Any]: ... -@overload -def side_effect( - func: Callable[[_T], object], - iterable: Iterable[_T], - chunk_size: None = ..., - before: Optional[Callable[[], object]] = ..., - after: Optional[Callable[[], object]] = ..., -) -> Iterator[_T]: ... -@overload -def side_effect( - func: Callable[[List[_T]], object], - iterable: Iterable[_T], - chunk_size: int, - before: Optional[Callable[[], object]] = ..., - after: Optional[Callable[[], object]] = ..., -) -> Iterator[_T]: ... -def sliced( - seq: Sequence[_T], n: int, strict: bool = ... -) -> Iterator[Sequence[_T]]: ... -def split_at( - iterable: Iterable[_T], - pred: Callable[[_T], object], - maxsplit: int = ..., - keep_separator: bool = ..., -) -> Iterator[List[_T]]: ... -def split_before( - iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... -) -> Iterator[List[_T]]: ... -def split_after( - iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... -) -> Iterator[List[_T]]: ... -def split_when( - iterable: Iterable[_T], - pred: Callable[[_T, _T], object], - maxsplit: int = ..., -) -> Iterator[List[_T]]: ... -def split_into( - iterable: Iterable[_T], sizes: Iterable[Optional[int]] -) -> Iterator[List[_T]]: ... -@overload -def padded( - iterable: Iterable[_T], - *, - n: Optional[int] = ..., - next_multiple: bool = ... -) -> Iterator[Optional[_T]]: ... -@overload -def padded( - iterable: Iterable[_T], - fillvalue: _U, - n: Optional[int] = ..., - next_multiple: bool = ..., -) -> Iterator[Union[_T, _U]]: ... -@overload -def repeat_last(iterable: Iterable[_T]) -> Iterator[_T]: ... -@overload -def repeat_last( - iterable: Iterable[_T], default: _U -) -> Iterator[Union[_T, _U]]: ... -def distribute(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... -@overload -def stagger( - iterable: Iterable[_T], - offsets: _SizedIterable[int] = ..., - longest: bool = ..., -) -> Iterator[Tuple[Optional[_T], ...]]: ... -@overload -def stagger( - iterable: Iterable[_T], - offsets: _SizedIterable[int] = ..., - longest: bool = ..., - fillvalue: _U = ..., -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... - -class UnequalIterablesError(ValueError): - def __init__( - self, details: Optional[Tuple[int, int, int]] = ... - ) -> None: ... - -def zip_equal(*iterables: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... -@overload -def zip_offset( - *iterables: Iterable[_T], offsets: _SizedIterable[int], longest: bool = ... -) -> Iterator[Tuple[Optional[_T], ...]]: ... -@overload -def zip_offset( - *iterables: Iterable[_T], - offsets: _SizedIterable[int], - longest: bool = ..., - fillvalue: _U -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... -def sort_together( - iterables: Iterable[Iterable[_T]], - key_list: Iterable[int] = ..., - key: Optional[Callable[..., Any]] = ..., - reverse: bool = ..., -) -> List[Tuple[_T, ...]]: ... -def unzip(iterable: Iterable[Sequence[_T]]) -> Tuple[Iterator[_T], ...]: ... -def divide(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... -def always_iterable( - obj: object, - base_type: Union[ - type, Tuple[Union[type, Tuple[Any, ...]], ...], None - ] = ..., -) -> Iterator[Any]: ... -def adjacent( - predicate: Callable[[_T], bool], - iterable: Iterable[_T], - distance: int = ..., -) -> Iterator[Tuple[bool, _T]]: ... -def groupby_transform( - iterable: Iterable[_T], - keyfunc: Optional[Callable[[_T], _U]] = ..., - valuefunc: Optional[Callable[[_T], _V]] = ..., - reducefunc: Optional[Callable[..., _W]] = ..., -) -> Iterator[Tuple[_T, _W]]: ... - -class numeric_range(Generic[_T, _U], Sequence[_T], Hashable, Reversible[_T]): - @overload - def __init__(self, __stop: _T) -> None: ... - @overload - def __init__(self, __start: _T, __stop: _T) -> None: ... - @overload - def __init__(self, __start: _T, __stop: _T, __step: _U) -> None: ... - def __bool__(self) -> bool: ... - def __contains__(self, elem: object) -> bool: ... - def __eq__(self, other: object) -> bool: ... - @overload - def __getitem__(self, key: int) -> _T: ... - @overload - def __getitem__(self, key: slice) -> numeric_range[_T, _U]: ... - def __hash__(self) -> int: ... - def __iter__(self) -> Iterator[_T]: ... - def __len__(self) -> int: ... - def __reduce__( - self, - ) -> Tuple[Type[numeric_range[_T, _U]], Tuple[_T, _T, _U]]: ... - def __repr__(self) -> str: ... - def __reversed__(self) -> Iterator[_T]: ... - def count(self, value: _T) -> int: ... - def index(self, value: _T) -> int: ... # type: ignore - -def count_cycle( - iterable: Iterable[_T], n: Optional[int] = ... -) -> Iterable[Tuple[int, _T]]: ... -def mark_ends( - iterable: Iterable[_T], -) -> Iterable[Tuple[bool, bool, _T]]: ... -def locate( - iterable: Iterable[object], - pred: Callable[..., Any] = ..., - window_size: Optional[int] = ..., -) -> Iterator[int]: ... -def lstrip( - iterable: Iterable[_T], pred: Callable[[_T], object] -) -> Iterator[_T]: ... -def rstrip( - iterable: Iterable[_T], pred: Callable[[_T], object] -) -> Iterator[_T]: ... -def strip( - iterable: Iterable[_T], pred: Callable[[_T], object] -) -> Iterator[_T]: ... - -class islice_extended(Generic[_T], Iterator[_T]): - def __init__( - self, iterable: Iterable[_T], *args: Optional[int] - ) -> None: ... - def __iter__(self) -> islice_extended[_T]: ... - def __next__(self) -> _T: ... - def __getitem__(self, index: slice) -> islice_extended[_T]: ... - -def always_reversible(iterable: Iterable[_T]) -> Iterator[_T]: ... -def consecutive_groups( - iterable: Iterable[_T], ordering: Callable[[_T], int] = ... -) -> Iterator[Iterator[_T]]: ... -@overload -def difference( - iterable: Iterable[_T], - func: Callable[[_T, _T], _U] = ..., - *, - initial: None = ... -) -> Iterator[Union[_T, _U]]: ... -@overload -def difference( - iterable: Iterable[_T], func: Callable[[_T, _T], _U] = ..., *, initial: _U -) -> Iterator[_U]: ... - -class SequenceView(Generic[_T], Sequence[_T]): - def __init__(self, target: Sequence[_T]) -> None: ... - @overload - def __getitem__(self, index: int) -> _T: ... - @overload - def __getitem__(self, index: slice) -> Sequence[_T]: ... - def __len__(self) -> int: ... - -class seekable(Generic[_T], Iterator[_T]): - def __init__( - self, iterable: Iterable[_T], maxlen: Optional[int] = ... - ) -> None: ... - def __iter__(self) -> seekable[_T]: ... - def __next__(self) -> _T: ... - def __bool__(self) -> bool: ... - @overload - def peek(self) -> _T: ... - @overload - def peek(self, default: _U) -> Union[_T, _U]: ... - def elements(self) -> SequenceView[_T]: ... - def seek(self, index: int) -> None: ... - -class run_length: - @staticmethod - def encode(iterable: Iterable[_T]) -> Iterator[Tuple[_T, int]]: ... - @staticmethod - def decode(iterable: Iterable[Tuple[_T, int]]) -> Iterator[_T]: ... - -def exactly_n( - iterable: Iterable[_T], n: int, predicate: Callable[[_T], object] = ... -) -> bool: ... -def circular_shifts(iterable: Iterable[_T]) -> List[Tuple[_T, ...]]: ... -def make_decorator( - wrapping_func: Callable[..., _U], result_index: int = ... -) -> Callable[..., Callable[[Callable[..., Any]], Callable[..., _U]]]: ... -@overload -def map_reduce( - iterable: Iterable[_T], - keyfunc: Callable[[_T], _U], - valuefunc: None = ..., - reducefunc: None = ..., -) -> Dict[_U, List[_T]]: ... -@overload -def map_reduce( - iterable: Iterable[_T], - keyfunc: Callable[[_T], _U], - valuefunc: Callable[[_T], _V], - reducefunc: None = ..., -) -> Dict[_U, List[_V]]: ... -@overload -def map_reduce( - iterable: Iterable[_T], - keyfunc: Callable[[_T], _U], - valuefunc: None = ..., - reducefunc: Callable[[List[_T]], _W] = ..., -) -> Dict[_U, _W]: ... -@overload -def map_reduce( - iterable: Iterable[_T], - keyfunc: Callable[[_T], _U], - valuefunc: Callable[[_T], _V], - reducefunc: Callable[[List[_V]], _W], -) -> Dict[_U, _W]: ... -def rlocate( - iterable: Iterable[_T], - pred: Callable[..., object] = ..., - window_size: Optional[int] = ..., -) -> Iterator[int]: ... -def replace( - iterable: Iterable[_T], - pred: Callable[..., object], - substitutes: Iterable[_U], - count: Optional[int] = ..., - window_size: int = ..., -) -> Iterator[Union[_T, _U]]: ... -def partitions(iterable: Iterable[_T]) -> Iterator[List[List[_T]]]: ... -def set_partitions( - iterable: Iterable[_T], k: Optional[int] = ... -) -> Iterator[List[List[_T]]]: ... - -class time_limited(Generic[_T], Iterator[_T]): - def __init__( - self, limit_seconds: float, iterable: Iterable[_T] - ) -> None: ... - def __iter__(self) -> islice_extended[_T]: ... - def __next__(self) -> _T: ... - -@overload -def only( - iterable: Iterable[_T], *, too_long: Optional[_Raisable] = ... -) -> Optional[_T]: ... -@overload -def only( - iterable: Iterable[_T], default: _U, too_long: Optional[_Raisable] = ... -) -> Union[_T, _U]: ... -def ichunked(iterable: Iterable[_T], n: int) -> Iterator[Iterator[_T]]: ... -def distinct_combinations( - iterable: Iterable[_T], r: int -) -> Iterator[Tuple[_T, ...]]: ... -def filter_except( - validator: Callable[[Any], object], - iterable: Iterable[_T], - *exceptions: Type[BaseException] -) -> Iterator[_T]: ... -def map_except( - function: Callable[[Any], _U], - iterable: Iterable[_T], - *exceptions: Type[BaseException] -) -> Iterator[_U]: ... -def sample( - iterable: Iterable[_T], - k: int, - weights: Optional[Iterable[float]] = ..., -) -> List[_T]: ... -def is_sorted( - iterable: Iterable[_T], - key: Optional[Callable[[_T], _U]] = ..., - reverse: bool = False, -) -> bool: ... - -class AbortThread(BaseException): - pass - -class callback_iter(Generic[_T], Iterator[_T]): - def __init__( - self, - func: Callable[..., Any], - callback_kwd: str = ..., - wait_seconds: float = ..., - ) -> None: ... - def __enter__(self) -> callback_iter[_T]: ... - def __exit__( - self, - exc_type: Optional[Type[BaseException]], - exc_value: Optional[BaseException], - traceback: Optional[TracebackType], - ) -> Optional[bool]: ... - def __iter__(self) -> callback_iter[_T]: ... - def __next__(self) -> _T: ... - def _reader(self) -> Iterator[_T]: ... - @property - def done(self) -> bool: ... - @property - def result(self) -> Any: ... - -def windowed_complete( - iterable: Iterable[_T], n: int -) -> Iterator[Tuple[_T, ...]]: ... -def all_unique( - iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... -) -> bool: ... -def nth_product(index: int, *args: Iterable[_T]) -> Tuple[_T, ...]: ... -def nth_permutation( - iterable: Iterable[_T], r: int, index: int -) -> Tuple[_T, ...]: ... -def value_chain(*args: Union[_T, Iterable[_T]]) -> Iterable[_T]: ... -def product_index(element: Iterable[_T], *args: Iterable[_T]) -> int: ... -def combination_index( - element: Iterable[_T], iterable: Iterable[_T] -) -> int: ... -def permutation_index( - element: Iterable[_T], iterable: Iterable[_T] -) -> int: ... - -class countable(Generic[_T], Iterator[_T]): - def __init__(self, iterable: Iterable[_T]) -> None: ... - def __iter__(self) -> countable[_T]: ... - def __next__(self) -> _T: ... diff --git a/_setuptools_vendored/more_itertools/py.typed b/_setuptools_vendored/more_itertools/py.typed deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/_setuptools_vendored/more_itertools/recipes.py b/_setuptools_vendored/more_itertools/recipes.py deleted file mode 100644 index 521abd7c2c..0000000000 --- a/_setuptools_vendored/more_itertools/recipes.py +++ /dev/null @@ -1,620 +0,0 @@ -"""Imported from the recipes section of the itertools documentation. - -All functions taken from the recipes section of the itertools library docs -[1]_. -Some backward-compatible usability improvements have been made. - -.. [1] http://docs.python.org/library/itertools.html#recipes - -""" -import warnings -from collections import deque -from itertools import ( - chain, - combinations, - count, - cycle, - groupby, - islice, - repeat, - starmap, - tee, - zip_longest, -) -import operator -from random import randrange, sample, choice - -__all__ = [ - 'all_equal', - 'consume', - 'convolve', - 'dotproduct', - 'first_true', - 'flatten', - 'grouper', - 'iter_except', - 'ncycles', - 'nth', - 'nth_combination', - 'padnone', - 'pad_none', - 'pairwise', - 'partition', - 'powerset', - 'prepend', - 'quantify', - 'random_combination_with_replacement', - 'random_combination', - 'random_permutation', - 'random_product', - 'repeatfunc', - 'roundrobin', - 'tabulate', - 'tail', - 'take', - 'unique_everseen', - 'unique_justseen', -] - - -def take(n, iterable): - """Return first *n* items of the iterable as a list. - - >>> take(3, range(10)) - [0, 1, 2] - - If there are fewer than *n* items in the iterable, all of them are - returned. - - >>> take(10, range(3)) - [0, 1, 2] - - """ - return list(islice(iterable, n)) - - -def tabulate(function, start=0): - """Return an iterator over the results of ``func(start)``, - ``func(start + 1)``, ``func(start + 2)``... - - *func* should be a function that accepts one integer argument. - - If *start* is not specified it defaults to 0. It will be incremented each - time the iterator is advanced. - - >>> square = lambda x: x ** 2 - >>> iterator = tabulate(square, -3) - >>> take(4, iterator) - [9, 4, 1, 0] - - """ - return map(function, count(start)) - - -def tail(n, iterable): - """Return an iterator over the last *n* items of *iterable*. - - >>> t = tail(3, 'ABCDEFG') - >>> list(t) - ['E', 'F', 'G'] - - """ - return iter(deque(iterable, maxlen=n)) - - -def consume(iterator, n=None): - """Advance *iterable* by *n* steps. If *n* is ``None``, consume it - entirely. - - Efficiently exhausts an iterator without returning values. Defaults to - consuming the whole iterator, but an optional second argument may be - provided to limit consumption. - - >>> i = (x for x in range(10)) - >>> next(i) - 0 - >>> consume(i, 3) - >>> next(i) - 4 - >>> consume(i) - >>> next(i) - Traceback (most recent call last): - File "", line 1, in - StopIteration - - If the iterator has fewer items remaining than the provided limit, the - whole iterator will be consumed. - - >>> i = (x for x in range(3)) - >>> consume(i, 5) - >>> next(i) - Traceback (most recent call last): - File "", line 1, in - StopIteration - - """ - # Use functions that consume iterators at C speed. - if n is None: - # feed the entire iterator into a zero-length deque - deque(iterator, maxlen=0) - else: - # advance to the empty slice starting at position n - next(islice(iterator, n, n), None) - - -def nth(iterable, n, default=None): - """Returns the nth item or a default value. - - >>> l = range(10) - >>> nth(l, 3) - 3 - >>> nth(l, 20, "zebra") - 'zebra' - - """ - return next(islice(iterable, n, None), default) - - -def all_equal(iterable): - """ - Returns ``True`` if all the elements are equal to each other. - - >>> all_equal('aaaa') - True - >>> all_equal('aaab') - False - - """ - g = groupby(iterable) - return next(g, True) and not next(g, False) - - -def quantify(iterable, pred=bool): - """Return the how many times the predicate is true. - - >>> quantify([True, False, True]) - 2 - - """ - return sum(map(pred, iterable)) - - -def pad_none(iterable): - """Returns the sequence of elements and then returns ``None`` indefinitely. - - >>> take(5, pad_none(range(3))) - [0, 1, 2, None, None] - - Useful for emulating the behavior of the built-in :func:`map` function. - - See also :func:`padded`. - - """ - return chain(iterable, repeat(None)) - - -padnone = pad_none - - -def ncycles(iterable, n): - """Returns the sequence elements *n* times - - >>> list(ncycles(["a", "b"], 3)) - ['a', 'b', 'a', 'b', 'a', 'b'] - - """ - return chain.from_iterable(repeat(tuple(iterable), n)) - - -def dotproduct(vec1, vec2): - """Returns the dot product of the two iterables. - - >>> dotproduct([10, 10], [20, 20]) - 400 - - """ - return sum(map(operator.mul, vec1, vec2)) - - -def flatten(listOfLists): - """Return an iterator flattening one level of nesting in a list of lists. - - >>> list(flatten([[0, 1], [2, 3]])) - [0, 1, 2, 3] - - See also :func:`collapse`, which can flatten multiple levels of nesting. - - """ - return chain.from_iterable(listOfLists) - - -def repeatfunc(func, times=None, *args): - """Call *func* with *args* repeatedly, returning an iterable over the - results. - - If *times* is specified, the iterable will terminate after that many - repetitions: - - >>> from operator import add - >>> times = 4 - >>> args = 3, 5 - >>> list(repeatfunc(add, times, *args)) - [8, 8, 8, 8] - - If *times* is ``None`` the iterable will not terminate: - - >>> from random import randrange - >>> times = None - >>> args = 1, 11 - >>> take(6, repeatfunc(randrange, times, *args)) # doctest:+SKIP - [2, 4, 8, 1, 8, 4] - - """ - if times is None: - return starmap(func, repeat(args)) - return starmap(func, repeat(args, times)) - - -def _pairwise(iterable): - """Returns an iterator of paired items, overlapping, from the original - - >>> take(4, pairwise(count())) - [(0, 1), (1, 2), (2, 3), (3, 4)] - - On Python 3.10 and above, this is an alias for :func:`itertools.pairwise`. - - """ - a, b = tee(iterable) - next(b, None) - yield from zip(a, b) - - -try: - from itertools import pairwise as itertools_pairwise -except ImportError: - pairwise = _pairwise -else: - - def pairwise(iterable): - yield from itertools_pairwise(iterable) - - pairwise.__doc__ = _pairwise.__doc__ - - -def grouper(iterable, n, fillvalue=None): - """Collect data into fixed-length chunks or blocks. - - >>> list(grouper('ABCDEFG', 3, 'x')) - [('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')] - - """ - if isinstance(iterable, int): - warnings.warn( - "grouper expects iterable as first parameter", DeprecationWarning - ) - n, iterable = iterable, n - args = [iter(iterable)] * n - return zip_longest(fillvalue=fillvalue, *args) - - -def roundrobin(*iterables): - """Yields an item from each iterable, alternating between them. - - >>> list(roundrobin('ABC', 'D', 'EF')) - ['A', 'D', 'E', 'B', 'F', 'C'] - - This function produces the same output as :func:`interleave_longest`, but - may perform better for some inputs (in particular when the number of - iterables is small). - - """ - # Recipe credited to George Sakkis - pending = len(iterables) - nexts = cycle(iter(it).__next__ for it in iterables) - while pending: - try: - for next in nexts: - yield next() - except StopIteration: - pending -= 1 - nexts = cycle(islice(nexts, pending)) - - -def partition(pred, iterable): - """ - Returns a 2-tuple of iterables derived from the input iterable. - The first yields the items that have ``pred(item) == False``. - The second yields the items that have ``pred(item) == True``. - - >>> is_odd = lambda x: x % 2 != 0 - >>> iterable = range(10) - >>> even_items, odd_items = partition(is_odd, iterable) - >>> list(even_items), list(odd_items) - ([0, 2, 4, 6, 8], [1, 3, 5, 7, 9]) - - If *pred* is None, :func:`bool` is used. - - >>> iterable = [0, 1, False, True, '', ' '] - >>> false_items, true_items = partition(None, iterable) - >>> list(false_items), list(true_items) - ([0, False, ''], [1, True, ' ']) - - """ - if pred is None: - pred = bool - - evaluations = ((pred(x), x) for x in iterable) - t1, t2 = tee(evaluations) - return ( - (x for (cond, x) in t1 if not cond), - (x for (cond, x) in t2 if cond), - ) - - -def powerset(iterable): - """Yields all possible subsets of the iterable. - - >>> list(powerset([1, 2, 3])) - [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] - - :func:`powerset` will operate on iterables that aren't :class:`set` - instances, so repeated elements in the input will produce repeated elements - in the output. Use :func:`unique_everseen` on the input to avoid generating - duplicates: - - >>> seq = [1, 1, 0] - >>> list(powerset(seq)) - [(), (1,), (1,), (0,), (1, 1), (1, 0), (1, 0), (1, 1, 0)] - >>> from more_itertools import unique_everseen - >>> list(powerset(unique_everseen(seq))) - [(), (1,), (0,), (1, 0)] - - """ - s = list(iterable) - return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1)) - - -def unique_everseen(iterable, key=None): - """ - Yield unique elements, preserving order. - - >>> list(unique_everseen('AAAABBBCCDAABBB')) - ['A', 'B', 'C', 'D'] - >>> list(unique_everseen('ABBCcAD', str.lower)) - ['A', 'B', 'C', 'D'] - - Sequences with a mix of hashable and unhashable items can be used. - The function will be slower (i.e., `O(n^2)`) for unhashable items. - - Remember that ``list`` objects are unhashable - you can use the *key* - parameter to transform the list to a tuple (which is hashable) to - avoid a slowdown. - - >>> iterable = ([1, 2], [2, 3], [1, 2]) - >>> list(unique_everseen(iterable)) # Slow - [[1, 2], [2, 3]] - >>> list(unique_everseen(iterable, key=tuple)) # Faster - [[1, 2], [2, 3]] - - Similary, you may want to convert unhashable ``set`` objects with - ``key=frozenset``. For ``dict`` objects, - ``key=lambda x: frozenset(x.items())`` can be used. - - """ - seenset = set() - seenset_add = seenset.add - seenlist = [] - seenlist_add = seenlist.append - use_key = key is not None - - for element in iterable: - k = key(element) if use_key else element - try: - if k not in seenset: - seenset_add(k) - yield element - except TypeError: - if k not in seenlist: - seenlist_add(k) - yield element - - -def unique_justseen(iterable, key=None): - """Yields elements in order, ignoring serial duplicates - - >>> list(unique_justseen('AAAABBBCCDAABBB')) - ['A', 'B', 'C', 'D', 'A', 'B'] - >>> list(unique_justseen('ABBCcAD', str.lower)) - ['A', 'B', 'C', 'A', 'D'] - - """ - return map(next, map(operator.itemgetter(1), groupby(iterable, key))) - - -def iter_except(func, exception, first=None): - """Yields results from a function repeatedly until an exception is raised. - - Converts a call-until-exception interface to an iterator interface. - Like ``iter(func, sentinel)``, but uses an exception instead of a sentinel - to end the loop. - - >>> l = [0, 1, 2] - >>> list(iter_except(l.pop, IndexError)) - [2, 1, 0] - - """ - try: - if first is not None: - yield first() - while 1: - yield func() - except exception: - pass - - -def first_true(iterable, default=None, pred=None): - """ - Returns the first true value in the iterable. - - If no true value is found, returns *default* - - If *pred* is not None, returns the first item for which - ``pred(item) == True`` . - - >>> first_true(range(10)) - 1 - >>> first_true(range(10), pred=lambda x: x > 5) - 6 - >>> first_true(range(10), default='missing', pred=lambda x: x > 9) - 'missing' - - """ - return next(filter(pred, iterable), default) - - -def random_product(*args, repeat=1): - """Draw an item at random from each of the input iterables. - - >>> random_product('abc', range(4), 'XYZ') # doctest:+SKIP - ('c', 3, 'Z') - - If *repeat* is provided as a keyword argument, that many items will be - drawn from each iterable. - - >>> random_product('abcd', range(4), repeat=2) # doctest:+SKIP - ('a', 2, 'd', 3) - - This equivalent to taking a random selection from - ``itertools.product(*args, **kwarg)``. - - """ - pools = [tuple(pool) for pool in args] * repeat - return tuple(choice(pool) for pool in pools) - - -def random_permutation(iterable, r=None): - """Return a random *r* length permutation of the elements in *iterable*. - - If *r* is not specified or is ``None``, then *r* defaults to the length of - *iterable*. - - >>> random_permutation(range(5)) # doctest:+SKIP - (3, 4, 0, 1, 2) - - This equivalent to taking a random selection from - ``itertools.permutations(iterable, r)``. - - """ - pool = tuple(iterable) - r = len(pool) if r is None else r - return tuple(sample(pool, r)) - - -def random_combination(iterable, r): - """Return a random *r* length subsequence of the elements in *iterable*. - - >>> random_combination(range(5), 3) # doctest:+SKIP - (2, 3, 4) - - This equivalent to taking a random selection from - ``itertools.combinations(iterable, r)``. - - """ - pool = tuple(iterable) - n = len(pool) - indices = sorted(sample(range(n), r)) - return tuple(pool[i] for i in indices) - - -def random_combination_with_replacement(iterable, r): - """Return a random *r* length subsequence of elements in *iterable*, - allowing individual elements to be repeated. - - >>> random_combination_with_replacement(range(3), 5) # doctest:+SKIP - (0, 0, 1, 2, 2) - - This equivalent to taking a random selection from - ``itertools.combinations_with_replacement(iterable, r)``. - - """ - pool = tuple(iterable) - n = len(pool) - indices = sorted(randrange(n) for i in range(r)) - return tuple(pool[i] for i in indices) - - -def nth_combination(iterable, r, index): - """Equivalent to ``list(combinations(iterable, r))[index]``. - - The subsequences of *iterable* that are of length *r* can be ordered - lexicographically. :func:`nth_combination` computes the subsequence at - sort position *index* directly, without computing the previous - subsequences. - - >>> nth_combination(range(5), 3, 5) - (0, 3, 4) - - ``ValueError`` will be raised If *r* is negative or greater than the length - of *iterable*. - ``IndexError`` will be raised if the given *index* is invalid. - """ - pool = tuple(iterable) - n = len(pool) - if (r < 0) or (r > n): - raise ValueError - - c = 1 - k = min(r, n - r) - for i in range(1, k + 1): - c = c * (n - k + i) // i - - if index < 0: - index += c - - if (index < 0) or (index >= c): - raise IndexError - - result = [] - while r: - c, n, r = c * r // n, n - 1, r - 1 - while index >= c: - index -= c - c, n = c * (n - r) // n, n - 1 - result.append(pool[-1 - n]) - - return tuple(result) - - -def prepend(value, iterator): - """Yield *value*, followed by the elements in *iterator*. - - >>> value = '0' - >>> iterator = ['1', '2', '3'] - >>> list(prepend(value, iterator)) - ['0', '1', '2', '3'] - - To prepend multiple values, see :func:`itertools.chain` - or :func:`value_chain`. - - """ - return chain([value], iterator) - - -def convolve(signal, kernel): - """Convolve the iterable *signal* with the iterable *kernel*. - - >>> signal = (1, 2, 3, 4, 5) - >>> kernel = [3, 2, 1] - >>> list(convolve(signal, kernel)) - [3, 8, 14, 20, 26, 14, 5] - - Note: the input arguments are not interchangeable, as the *kernel* - is immediately consumed and stored. - - """ - kernel = tuple(kernel)[::-1] - n = len(kernel) - window = deque([0], maxlen=n) * n - for x in chain(signal, repeat(0, n - 1)): - window.append(x) - yield sum(map(operator.mul, kernel, window)) diff --git a/_setuptools_vendored/more_itertools/recipes.pyi b/_setuptools_vendored/more_itertools/recipes.pyi deleted file mode 100644 index 5e39d96390..0000000000 --- a/_setuptools_vendored/more_itertools/recipes.pyi +++ /dev/null @@ -1,103 +0,0 @@ -"""Stubs for more_itertools.recipes""" -from typing import ( - Any, - Callable, - Iterable, - Iterator, - List, - Optional, - Tuple, - TypeVar, - Union, -) -from typing_extensions import overload, Type - -# Type and type variable definitions -_T = TypeVar('_T') -_U = TypeVar('_U') - -def take(n: int, iterable: Iterable[_T]) -> List[_T]: ... -def tabulate( - function: Callable[[int], _T], start: int = ... -) -> Iterator[_T]: ... -def tail(n: int, iterable: Iterable[_T]) -> Iterator[_T]: ... -def consume(iterator: Iterable[object], n: Optional[int] = ...) -> None: ... -@overload -def nth(iterable: Iterable[_T], n: int) -> Optional[_T]: ... -@overload -def nth(iterable: Iterable[_T], n: int, default: _U) -> Union[_T, _U]: ... -def all_equal(iterable: Iterable[object]) -> bool: ... -def quantify( - iterable: Iterable[_T], pred: Callable[[_T], bool] = ... -) -> int: ... -def pad_none(iterable: Iterable[_T]) -> Iterator[Optional[_T]]: ... -def padnone(iterable: Iterable[_T]) -> Iterator[Optional[_T]]: ... -def ncycles(iterable: Iterable[_T], n: int) -> Iterator[_T]: ... -def dotproduct(vec1: Iterable[object], vec2: Iterable[object]) -> object: ... -def flatten(listOfLists: Iterable[Iterable[_T]]) -> Iterator[_T]: ... -def repeatfunc( - func: Callable[..., _U], times: Optional[int] = ..., *args: Any -) -> Iterator[_U]: ... -def pairwise(iterable: Iterable[_T]) -> Iterator[Tuple[_T, _T]]: ... -@overload -def grouper( - iterable: Iterable[_T], n: int -) -> Iterator[Tuple[Optional[_T], ...]]: ... -@overload -def grouper( - iterable: Iterable[_T], n: int, fillvalue: _U -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... -@overload -def grouper( # Deprecated interface - iterable: int, n: Iterable[_T] -) -> Iterator[Tuple[Optional[_T], ...]]: ... -@overload -def grouper( # Deprecated interface - iterable: int, n: Iterable[_T], fillvalue: _U -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... -def roundrobin(*iterables: Iterable[_T]) -> Iterator[_T]: ... -def partition( - pred: Optional[Callable[[_T], object]], iterable: Iterable[_T] -) -> Tuple[Iterator[_T], Iterator[_T]]: ... -def powerset(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... -def unique_everseen( - iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... -) -> Iterator[_T]: ... -def unique_justseen( - iterable: Iterable[_T], key: Optional[Callable[[_T], object]] = ... -) -> Iterator[_T]: ... -@overload -def iter_except( - func: Callable[[], _T], exception: Type[BaseException], first: None = ... -) -> Iterator[_T]: ... -@overload -def iter_except( - func: Callable[[], _T], - exception: Type[BaseException], - first: Callable[[], _U], -) -> Iterator[Union[_T, _U]]: ... -@overload -def first_true( - iterable: Iterable[_T], *, pred: Optional[Callable[[_T], object]] = ... -) -> Optional[_T]: ... -@overload -def first_true( - iterable: Iterable[_T], - default: _U, - pred: Optional[Callable[[_T], object]] = ..., -) -> Union[_T, _U]: ... -def random_product( - *args: Iterable[_T], repeat: int = ... -) -> Tuple[_T, ...]: ... -def random_permutation( - iterable: Iterable[_T], r: Optional[int] = ... -) -> Tuple[_T, ...]: ... -def random_combination(iterable: Iterable[_T], r: int) -> Tuple[_T, ...]: ... -def random_combination_with_replacement( - iterable: Iterable[_T], r: int -) -> Tuple[_T, ...]: ... -def nth_combination( - iterable: Iterable[_T], r: int, index: int -) -> Tuple[_T, ...]: ... -def prepend(value: _T, iterator: Iterable[_U]) -> Iterator[Union[_T, _U]]: ... -def convolve(signal: Iterable[_T], kernel: Iterable[_T]) -> Iterator[_T]: ... diff --git a/_setuptools_vendored/ordered_set-3.1.1.dist-info/METADATA b/_setuptools_vendored/ordered_set-3.1.1.dist-info/METADATA deleted file mode 100644 index e124369c0d..0000000000 --- a/_setuptools_vendored/ordered_set-3.1.1.dist-info/METADATA +++ /dev/null @@ -1,156 +0,0 @@ -Metadata-Version: 2.1 -Name: ordered-set -Version: 3.1.1 -Summary: A MutableSet that remembers its order, so that every entry has an index. -Home-page: https://github.com/LuminosoInsight/ordered-set -Maintainer: Robyn Speer -Maintainer-email: rspeer@luminoso.com -License: MIT-LICENSE -Platform: any -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Requires-Python: >=2.7 -Description-Content-Type: text/markdown - -[![Travis](https://img.shields.io/travis/LuminosoInsight/ordered-set/master.svg?label=Travis%20CI)](https://travis-ci.org/LuminosoInsight/ordered-set) -[![Codecov](https://codecov.io/github/LuminosoInsight/ordered-set/badge.svg?branch=master&service=github)](https://codecov.io/github/LuminosoInsight/ordered-set?branch=master) -[![Pypi](https://img.shields.io/pypi/v/ordered-set.svg)](https://pypi.python.org/pypi/ordered-set) - -An OrderedSet is a mutable data structure that is a hybrid of a list and a set. -It remembers the order of its entries, and every entry has an index number that -can be looked up. - - -## Usage examples - -An OrderedSet is created and used like a set: - - >>> from ordered_set import OrderedSet - - >>> letters = OrderedSet('abracadabra') - - >>> letters - OrderedSet(['a', 'b', 'r', 'c', 'd']) - - >>> 'r' in letters - True - -It is efficient to find the index of an entry in an OrderedSet, or find an -entry by its index. To help with this use case, the `.add()` method returns -the index of the added item, whether it was already in the set or not. - - >>> letters.index('r') - 2 - - >>> letters[2] - 'r' - - >>> letters.add('r') - 2 - - >>> letters.add('x') - 5 - -OrderedSets implement the union (`|`), intersection (`&`), and difference (`-`) -operators like sets do. - - >>> letters |= OrderedSet('shazam') - - >>> letters - OrderedSet(['a', 'b', 'r', 'c', 'd', 'x', 's', 'h', 'z', 'm']) - - >>> letters & set('aeiou') - OrderedSet(['a']) - - >>> letters -= 'abcd' - - >>> letters - OrderedSet(['r', 'x', 's', 'h', 'z', 'm']) - -The `__getitem__()` and `index()` methods have been extended to accept any -iterable except a string, returning a list, to perform NumPy-like "fancy -indexing". - - >>> letters = OrderedSet('abracadabra') - - >>> letters[[0, 2, 3]] - ['a', 'r', 'c'] - - >>> letters.index(['a', 'r', 'c']) - [0, 2, 3] - -OrderedSet implements `__getstate__` and `__setstate__` so it can be pickled, -and implements the abstract base classes `collections.MutableSet` and -`collections.Sequence`. - - -## Interoperability with NumPy and Pandas - -An OrderedSet can be used as a bi-directional mapping between a sparse -vocabulary and dense index numbers. As of version 3.1, it accepts NumPy arrays -of index numbers as well as lists. - -This combination of features makes OrderedSet a simple implementation of many -of the things that `pandas.Index` is used for, and many of its operations are -faster than the equivalent pandas operations. - -For further compatibility with pandas.Index, `get_loc` (the pandas method for -looking up a single index) and `get_indexer` (the pandas method for fancy -indexing in reverse) are both aliases for `index` (which handles both cases -in OrderedSet). - - -## Type hinting -To use type hinting features install `ordered-set-stubs` package from -[PyPI](https://pypi.org/project/ordered-set-stubs/): - - $ pip install ordered-set-stubs - - -## Authors - -OrderedSet was implemented by Robyn Speer. Jon Crall contributed changes and -tests to make it fit the Python set API. - - -## Comparisons - -The original implementation of OrderedSet was a [recipe posted to ActiveState -Recipes][recipe] by Raymond Hettiger, released under the MIT license. - -[recipe]: https://code.activestate.com/recipes/576694-orderedset/ - -Hettiger's implementation kept its content in a doubly-linked list referenced by a -dict. As a result, looking up an item by its index was an O(N) operation, while -deletion was O(1). - -This version makes different trade-offs for the sake of efficient lookups. Its -content is a standard Python list instead of a doubly-linked list. This -provides O(1) lookups by index at the expense of O(N) deletion, as well as -slightly faster iteration. - -In Python 3.6 and later, the built-in `dict` type is inherently ordered. If you -ignore the dictionary values, that also gives you a simple ordered set, with -fast O(1) insertion, deletion, iteration and membership testing. However, `dict` -does not provide the list-like random access features of OrderedSet. You -would have to convert it to a list in O(N) to look up the index of an entry or -look up an entry by its index. - - -## Compatibility - -OrderedSet is automatically tested on Python 2.7, 3.4, 3.5, 3.6, and 3.7. -We've checked more informally that it works on PyPy and PyPy3. - - diff --git a/_setuptools_vendored/ordered_set-3.1.1.dist-info/MIT-LICENSE b/_setuptools_vendored/ordered_set-3.1.1.dist-info/MIT-LICENSE deleted file mode 100644 index 25117ef4f1..0000000000 --- a/_setuptools_vendored/ordered_set-3.1.1.dist-info/MIT-LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2018 Luminoso Technologies, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/_setuptools_vendored/ordered_set-3.1.1.dist-info/RECORD b/_setuptools_vendored/ordered_set-3.1.1.dist-info/RECORD deleted file mode 100644 index 55f40206ed..0000000000 --- a/_setuptools_vendored/ordered_set-3.1.1.dist-info/RECORD +++ /dev/null @@ -1,6 +0,0 @@ -ordered_set.py,sha256=dbaCcs27dyN9gnMWGF5nA_BrVn6Q-NrjKYJpV9_fgBs,15130 -ordered_set-3.1.1.dist-info/METADATA,sha256=uOwCSESslh9mHc_HHkWlFJND82Gk-1Oa3PoE0LQxxww,5333 -ordered_set-3.1.1.dist-info/MIT-LICENSE,sha256=TvRE7qUSUBcd0ols7wgNf3zDEEJWW7kv7WDRySrMBBE,1071 -ordered_set-3.1.1.dist-info/WHEEL,sha256=WzZ8cwjh8l0jtULNjYq1Hpr-WCqCRgPr--TX4P5I1Wo,110 -ordered_set-3.1.1.dist-info/top_level.txt,sha256=NTY2_aDi1Do9fl3Z9EmWPxasFkUeW2dzO2D3RDx5CfM,12 -ordered_set-3.1.1.dist-info/RECORD,, diff --git a/_setuptools_vendored/ordered_set-3.1.1.dist-info/WHEEL b/_setuptools_vendored/ordered_set-3.1.1.dist-info/WHEEL deleted file mode 100644 index b733a60d37..0000000000 --- a/_setuptools_vendored/ordered_set-3.1.1.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.37.0) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/_setuptools_vendored/ordered_set-3.1.1.dist-info/top_level.txt b/_setuptools_vendored/ordered_set-3.1.1.dist-info/top_level.txt deleted file mode 100644 index 1c191eef52..0000000000 --- a/_setuptools_vendored/ordered_set-3.1.1.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -ordered_set diff --git a/_setuptools_vendored/ordered_set.py b/_setuptools_vendored/ordered_set.py deleted file mode 100644 index 14876000de..0000000000 --- a/_setuptools_vendored/ordered_set.py +++ /dev/null @@ -1,488 +0,0 @@ -""" -An OrderedSet is a custom MutableSet that remembers its order, so that every -entry has an index that can be looked up. - -Based on a recipe originally posted to ActiveState Recipes by Raymond Hettiger, -and released under the MIT license. -""" -import itertools as it -from collections import deque - -try: - # Python 3 - from collections.abc import MutableSet, Sequence -except ImportError: - # Python 2.7 - from collections import MutableSet, Sequence - -SLICE_ALL = slice(None) -__version__ = "3.1" - - -def is_iterable(obj): - """ - Are we being asked to look up a list of things, instead of a single thing? - We check for the `__iter__` attribute so that this can cover types that - don't have to be known by this module, such as NumPy arrays. - - Strings, however, should be considered as atomic values to look up, not - iterables. The same goes for tuples, since they are immutable and therefore - valid entries. - - We don't need to check for the Python 2 `unicode` type, because it doesn't - have an `__iter__` attribute anyway. - """ - return ( - hasattr(obj, "__iter__") - and not isinstance(obj, str) - and not isinstance(obj, tuple) - ) - - -class OrderedSet(MutableSet, Sequence): - """ - An OrderedSet is a custom MutableSet that remembers its order, so that - every entry has an index that can be looked up. - - Example: - >>> OrderedSet([1, 1, 2, 3, 2]) - OrderedSet([1, 2, 3]) - """ - - def __init__(self, iterable=None): - self.items = [] - self.map = {} - if iterable is not None: - self |= iterable - - def __len__(self): - """ - Returns the number of unique elements in the ordered set - - Example: - >>> len(OrderedSet([])) - 0 - >>> len(OrderedSet([1, 2])) - 2 - """ - return len(self.items) - - def __getitem__(self, index): - """ - Get the item at a given index. - - If `index` is a slice, you will get back that slice of items, as a - new OrderedSet. - - If `index` is a list or a similar iterable, you'll get a list of - items corresponding to those indices. This is similar to NumPy's - "fancy indexing". The result is not an OrderedSet because you may ask - for duplicate indices, and the number of elements returned should be - the number of elements asked for. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset[1] - 2 - """ - if isinstance(index, slice) and index == SLICE_ALL: - return self.copy() - elif is_iterable(index): - return [self.items[i] for i in index] - elif hasattr(index, "__index__") or isinstance(index, slice): - result = self.items[index] - if isinstance(result, list): - return self.__class__(result) - else: - return result - else: - raise TypeError("Don't know how to index an OrderedSet by %r" % index) - - def copy(self): - """ - Return a shallow copy of this object. - - Example: - >>> this = OrderedSet([1, 2, 3]) - >>> other = this.copy() - >>> this == other - True - >>> this is other - False - """ - return self.__class__(self) - - def __getstate__(self): - if len(self) == 0: - # The state can't be an empty list. - # We need to return a truthy value, or else __setstate__ won't be run. - # - # This could have been done more gracefully by always putting the state - # in a tuple, but this way is backwards- and forwards- compatible with - # previous versions of OrderedSet. - return (None,) - else: - return list(self) - - def __setstate__(self, state): - if state == (None,): - self.__init__([]) - else: - self.__init__(state) - - def __contains__(self, key): - """ - Test if the item is in this ordered set - - Example: - >>> 1 in OrderedSet([1, 3, 2]) - True - >>> 5 in OrderedSet([1, 3, 2]) - False - """ - return key in self.map - - def add(self, key): - """ - Add `key` as an item to this OrderedSet, then return its index. - - If `key` is already in the OrderedSet, return the index it already - had. - - Example: - >>> oset = OrderedSet() - >>> oset.append(3) - 0 - >>> print(oset) - OrderedSet([3]) - """ - if key not in self.map: - self.map[key] = len(self.items) - self.items.append(key) - return self.map[key] - - append = add - - def update(self, sequence): - """ - Update the set with the given iterable sequence, then return the index - of the last element inserted. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.update([3, 1, 5, 1, 4]) - 4 - >>> print(oset) - OrderedSet([1, 2, 3, 5, 4]) - """ - item_index = None - try: - for item in sequence: - item_index = self.add(item) - except TypeError: - raise ValueError( - "Argument needs to be an iterable, got %s" % type(sequence) - ) - return item_index - - def index(self, key): - """ - Get the index of a given entry, raising an IndexError if it's not - present. - - `key` can be an iterable of entries that is not a string, in which case - this returns a list of indices. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.index(2) - 1 - """ - if is_iterable(key): - return [self.index(subkey) for subkey in key] - return self.map[key] - - # Provide some compatibility with pd.Index - get_loc = index - get_indexer = index - - def pop(self): - """ - Remove and return the last element from the set. - - Raises KeyError if the set is empty. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.pop() - 3 - """ - if not self.items: - raise KeyError("Set is empty") - - elem = self.items[-1] - del self.items[-1] - del self.map[elem] - return elem - - def discard(self, key): - """ - Remove an element. Do not raise an exception if absent. - - The MutableSet mixin uses this to implement the .remove() method, which - *does* raise an error when asked to remove a non-existent item. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.discard(2) - >>> print(oset) - OrderedSet([1, 3]) - >>> oset.discard(2) - >>> print(oset) - OrderedSet([1, 3]) - """ - if key in self: - i = self.map[key] - del self.items[i] - del self.map[key] - for k, v in self.map.items(): - if v >= i: - self.map[k] = v - 1 - - def clear(self): - """ - Remove all items from this OrderedSet. - """ - del self.items[:] - self.map.clear() - - def __iter__(self): - """ - Example: - >>> list(iter(OrderedSet([1, 2, 3]))) - [1, 2, 3] - """ - return iter(self.items) - - def __reversed__(self): - """ - Example: - >>> list(reversed(OrderedSet([1, 2, 3]))) - [3, 2, 1] - """ - return reversed(self.items) - - def __repr__(self): - if not self: - return "%s()" % (self.__class__.__name__,) - return "%s(%r)" % (self.__class__.__name__, list(self)) - - def __eq__(self, other): - """ - Returns true if the containers have the same items. If `other` is a - Sequence, then order is checked, otherwise it is ignored. - - Example: - >>> oset = OrderedSet([1, 3, 2]) - >>> oset == [1, 3, 2] - True - >>> oset == [1, 2, 3] - False - >>> oset == [2, 3] - False - >>> oset == OrderedSet([3, 2, 1]) - False - """ - # In Python 2 deque is not a Sequence, so treat it as one for - # consistent behavior with Python 3. - if isinstance(other, (Sequence, deque)): - # Check that this OrderedSet contains the same elements, in the - # same order, as the other object. - return list(self) == list(other) - try: - other_as_set = set(other) - except TypeError: - # If `other` can't be converted into a set, it's not equal. - return False - else: - return set(self) == other_as_set - - def union(self, *sets): - """ - Combines all unique items. - Each items order is defined by its first appearance. - - Example: - >>> oset = OrderedSet.union(OrderedSet([3, 1, 4, 1, 5]), [1, 3], [2, 0]) - >>> print(oset) - OrderedSet([3, 1, 4, 5, 2, 0]) - >>> oset.union([8, 9]) - OrderedSet([3, 1, 4, 5, 2, 0, 8, 9]) - >>> oset | {10} - OrderedSet([3, 1, 4, 5, 2, 0, 10]) - """ - cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet - containers = map(list, it.chain([self], sets)) - items = it.chain.from_iterable(containers) - return cls(items) - - def __and__(self, other): - # the parent implementation of this is backwards - return self.intersection(other) - - def intersection(self, *sets): - """ - Returns elements in common between all sets. Order is defined only - by the first set. - - Example: - >>> oset = OrderedSet.intersection(OrderedSet([0, 1, 2, 3]), [1, 2, 3]) - >>> print(oset) - OrderedSet([1, 2, 3]) - >>> oset.intersection([2, 4, 5], [1, 2, 3, 4]) - OrderedSet([2]) - >>> oset.intersection() - OrderedSet([1, 2, 3]) - """ - cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet - if sets: - common = set.intersection(*map(set, sets)) - items = (item for item in self if item in common) - else: - items = self - return cls(items) - - def difference(self, *sets): - """ - Returns all elements that are in this set but not the others. - - Example: - >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2])) - OrderedSet([1, 3]) - >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2]), OrderedSet([3])) - OrderedSet([1]) - >>> OrderedSet([1, 2, 3]) - OrderedSet([2]) - OrderedSet([1, 3]) - >>> OrderedSet([1, 2, 3]).difference() - OrderedSet([1, 2, 3]) - """ - cls = self.__class__ - if sets: - other = set.union(*map(set, sets)) - items = (item for item in self if item not in other) - else: - items = self - return cls(items) - - def issubset(self, other): - """ - Report whether another set contains this set. - - Example: - >>> OrderedSet([1, 2, 3]).issubset({1, 2}) - False - >>> OrderedSet([1, 2, 3]).issubset({1, 2, 3, 4}) - True - >>> OrderedSet([1, 2, 3]).issubset({1, 4, 3, 5}) - False - """ - if len(self) > len(other): # Fast check for obvious cases - return False - return all(item in other for item in self) - - def issuperset(self, other): - """ - Report whether this set contains another set. - - Example: - >>> OrderedSet([1, 2]).issuperset([1, 2, 3]) - False - >>> OrderedSet([1, 2, 3, 4]).issuperset({1, 2, 3}) - True - >>> OrderedSet([1, 4, 3, 5]).issuperset({1, 2, 3}) - False - """ - if len(self) < len(other): # Fast check for obvious cases - return False - return all(item in self for item in other) - - def symmetric_difference(self, other): - """ - Return the symmetric difference of two OrderedSets as a new set. - That is, the new set will contain all elements that are in exactly - one of the sets. - - Their order will be preserved, with elements from `self` preceding - elements from `other`. - - Example: - >>> this = OrderedSet([1, 4, 3, 5, 7]) - >>> other = OrderedSet([9, 7, 1, 3, 2]) - >>> this.symmetric_difference(other) - OrderedSet([4, 5, 9, 2]) - """ - cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet - diff1 = cls(self).difference(other) - diff2 = cls(other).difference(self) - return diff1.union(diff2) - - def _update_items(self, items): - """ - Replace the 'items' list of this OrderedSet with a new one, updating - self.map accordingly. - """ - self.items = items - self.map = {item: idx for (idx, item) in enumerate(items)} - - def difference_update(self, *sets): - """ - Update this OrderedSet to remove items from one or more other sets. - - Example: - >>> this = OrderedSet([1, 2, 3]) - >>> this.difference_update(OrderedSet([2, 4])) - >>> print(this) - OrderedSet([1, 3]) - - >>> this = OrderedSet([1, 2, 3, 4, 5]) - >>> this.difference_update(OrderedSet([2, 4]), OrderedSet([1, 4, 6])) - >>> print(this) - OrderedSet([3, 5]) - """ - items_to_remove = set() - for other in sets: - items_to_remove |= set(other) - self._update_items([item for item in self.items if item not in items_to_remove]) - - def intersection_update(self, other): - """ - Update this OrderedSet to keep only items in another set, preserving - their order in this set. - - Example: - >>> this = OrderedSet([1, 4, 3, 5, 7]) - >>> other = OrderedSet([9, 7, 1, 3, 2]) - >>> this.intersection_update(other) - >>> print(this) - OrderedSet([1, 3, 7]) - """ - other = set(other) - self._update_items([item for item in self.items if item in other]) - - def symmetric_difference_update(self, other): - """ - Update this OrderedSet to remove items from another set, then - add items from the other set that were not present in this set. - - Example: - >>> this = OrderedSet([1, 4, 3, 5, 7]) - >>> other = OrderedSet([9, 7, 1, 3, 2]) - >>> this.symmetric_difference_update(other) - >>> print(this) - OrderedSet([4, 5, 9, 2]) - """ - items_to_add = [item for item in other if item not in self] - items_to_remove = set(other) - self._update_items( - [item for item in self.items if item not in items_to_remove] + items_to_add - ) diff --git a/_setuptools_vendored/packaging-20.4.dist-info/LICENSE b/_setuptools_vendored/packaging-20.4.dist-info/LICENSE deleted file mode 100644 index 6f62d44e4e..0000000000 --- a/_setuptools_vendored/packaging-20.4.dist-info/LICENSE +++ /dev/null @@ -1,3 +0,0 @@ -This software is made available under the terms of *either* of the licenses -found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made -under the terms of *both* these licenses. diff --git a/_setuptools_vendored/packaging-20.4.dist-info/LICENSE.APACHE b/_setuptools_vendored/packaging-20.4.dist-info/LICENSE.APACHE deleted file mode 100644 index f433b1a53f..0000000000 --- a/_setuptools_vendored/packaging-20.4.dist-info/LICENSE.APACHE +++ /dev/null @@ -1,177 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/_setuptools_vendored/packaging-20.4.dist-info/LICENSE.BSD b/_setuptools_vendored/packaging-20.4.dist-info/LICENSE.BSD deleted file mode 100644 index 42ce7b75c9..0000000000 --- a/_setuptools_vendored/packaging-20.4.dist-info/LICENSE.BSD +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) Donald Stufft and individual contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/_setuptools_vendored/packaging-20.4.dist-info/METADATA b/_setuptools_vendored/packaging-20.4.dist-info/METADATA deleted file mode 100644 index 4f5fb30492..0000000000 --- a/_setuptools_vendored/packaging-20.4.dist-info/METADATA +++ /dev/null @@ -1,373 +0,0 @@ -Metadata-Version: 2.1 -Name: packaging -Version: 20.4 -Summary: Core utilities for Python packages -Home-page: https://github.com/pypa/packaging -Author: Donald Stufft and individual contributors -Author-email: donald@stufft.io -License: BSD-2-Clause or Apache-2.0 -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Apache Software License -Classifier: License :: OSI Approved :: BSD License -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* -Description-Content-Type: text/x-rst -Requires-Dist: pyparsing (>=2.0.2) -Requires-Dist: six - -packaging -========= - -Core utilities for Python packages. - -The ``packaging`` project includes the following: version handling, specifiers, -markers, requirements, tags, utilities. - -Documentation -------------- - -The `documentation`_ provides information and the API for the following: - -- Version Handling -- Specifiers -- Markers -- Requirements -- Tags -- Utilities - -Installation ------------- - -Use ``pip`` to install these utilities:: - - pip install packaging - -Discussion ----------- - -If you run into bugs, you can file them in our `issue tracker`_. - -You can also join ``#pypa`` on Freenode to ask questions or get involved. - - -.. _`documentation`: https://packaging.pypa.io/ -.. _`issue tracker`: https://github.com/pypa/packaging/issues - - -Code of Conduct ---------------- - -Everyone interacting in the packaging project's codebases, issue trackers, chat -rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_. - -.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ - -Contributing ------------- - -The ``CONTRIBUTING.rst`` file outlines how to contribute to this project as -well as how to report a potential security issue. The documentation for this -project also covers information about `project development`_ and `security`_. - -.. _`project development`: https://packaging.pypa.io/en/latest/development/ -.. _`security`: https://packaging.pypa.io/en/latest/security/ - -Project History ---------------- - -Please review the ``CHANGELOG.rst`` file or the `Changelog documentation`_ for -recent changes and project history. - -.. _`Changelog documentation`: https://packaging.pypa.io/en/latest/changelog/ - -Changelog ---------- - -20.4 - 2020-05-19 -~~~~~~~~~~~~~~~~~ - -* Canonicalize version before comparing specifiers. (`#282 `__) -* Change type hint for ``canonicalize_name`` to return - ``packaging.utils.NormalizedName``. - This enables the use of static typing tools (like mypy) to detect mixing of - normalized and un-normalized names. - -20.3 - 2020-03-05 -~~~~~~~~~~~~~~~~~ - -* Fix changelog for 20.2. - -20.2 - 2020-03-05 -~~~~~~~~~~~~~~~~~ - -* Fix a bug that caused a 32-bit OS that runs on a 64-bit ARM CPU (e.g. ARM-v8, - aarch64), to report the wrong bitness. - -20.1 - 2020-01-24 -~~~~~~~~~~~~~~~~~~~ - -* Fix a bug caused by reuse of an exhausted iterator. (`#257 `__) - -20.0 - 2020-01-06 -~~~~~~~~~~~~~~~~~ - -* Add type hints (`#191 `__) - -* Add proper trove classifiers for PyPy support (`#198 `__) - -* Scale back depending on ``ctypes`` for manylinux support detection (`#171 `__) - -* Use ``sys.implementation.name`` where appropriate for ``packaging.tags`` (`#193 `__) - -* Expand upon the API provded by ``packaging.tags``: ``interpreter_name()``, ``mac_platforms()``, ``compatible_tags()``, ``cpython_tags()``, ``generic_tags()`` (`#187 `__) - -* Officially support Python 3.8 (`#232 `__) - -* Add ``major``, ``minor``, and ``micro`` aliases to ``packaging.version.Version`` (`#226 `__) - -* Properly mark ``packaging`` has being fully typed by adding a `py.typed` file (`#226 `__) - -19.2 - 2019-09-18 -~~~~~~~~~~~~~~~~~ - -* Remove dependency on ``attrs`` (`#178 `__, `#179 `__) - -* Use appropriate fallbacks for CPython ABI tag (`#181 `__, `#185 `__) - -* Add manylinux2014 support (`#186 `__) - -* Improve ABI detection (`#181 `__) - -* Properly handle debug wheels for Python 3.8 (`#172 `__) - -* Improve detection of debug builds on Windows (`#194 `__) - -19.1 - 2019-07-30 -~~~~~~~~~~~~~~~~~ - -* Add the ``packaging.tags`` module. (`#156 `__) - -* Correctly handle two-digit versions in ``python_version`` (`#119 `__) - - -19.0 - 2019-01-20 -~~~~~~~~~~~~~~~~~ - -* Fix string representation of PEP 508 direct URL requirements with markers. - -* Better handling of file URLs - - This allows for using ``file:///absolute/path``, which was previously - prevented due to the missing ``netloc``. - - This allows for all file URLs that ``urlunparse`` turns back into the - original URL to be valid. - - -18.0 - 2018-09-26 -~~~~~~~~~~~~~~~~~ - -* Improve error messages when invalid requirements are given. (`#129 `__) - - -17.1 - 2017-02-28 -~~~~~~~~~~~~~~~~~ - -* Fix ``utils.canonicalize_version`` when supplying non PEP 440 versions. - - -17.0 - 2017-02-28 -~~~~~~~~~~~~~~~~~ - -* Drop support for python 2.6, 3.2, and 3.3. - -* Define minimal pyparsing version to 2.0.2 (`#91 `__). - -* Add ``epoch``, ``release``, ``pre``, ``dev``, and ``post`` attributes to - ``Version`` and ``LegacyVersion`` (`#34 `__). - -* Add ``Version().is_devrelease`` and ``LegacyVersion().is_devrelease`` to - make it easy to determine if a release is a development release. - -* Add ``utils.canonicalize_version`` to canonicalize version strings or - ``Version`` instances (`#121 `__). - - -16.8 - 2016-10-29 -~~~~~~~~~~~~~~~~~ - -* Fix markers that utilize ``in`` so that they render correctly. - -* Fix an erroneous test on Python RC releases. - - -16.7 - 2016-04-23 -~~~~~~~~~~~~~~~~~ - -* Add support for the deprecated ``python_implementation`` marker which was - an undocumented setuptools marker in addition to the newer markers. - - -16.6 - 2016-03-29 -~~~~~~~~~~~~~~~~~ - -* Add support for the deprecated, PEP 345 environment markers in addition to - the newer markers. - - -16.5 - 2016-02-26 -~~~~~~~~~~~~~~~~~ - -* Fix a regression in parsing requirements with whitespaces between the comma - separators. - - -16.4 - 2016-02-22 -~~~~~~~~~~~~~~~~~ - -* Fix a regression in parsing requirements like ``foo (==4)``. - - -16.3 - 2016-02-21 -~~~~~~~~~~~~~~~~~ - -* Fix a bug where ``packaging.requirements:Requirement`` was overly strict when - matching legacy requirements. - - -16.2 - 2016-02-09 -~~~~~~~~~~~~~~~~~ - -* Add a function that implements the name canonicalization from PEP 503. - - -16.1 - 2016-02-07 -~~~~~~~~~~~~~~~~~ - -* Implement requirement specifiers from PEP 508. - - -16.0 - 2016-01-19 -~~~~~~~~~~~~~~~~~ - -* Relicense so that packaging is available under *either* the Apache License, - Version 2.0 or a 2 Clause BSD license. - -* Support installation of packaging when only distutils is available. - -* Fix ``==`` comparison when there is a prefix and a local version in play. - (`#41 `__). - -* Implement environment markers from PEP 508. - - -15.3 - 2015-08-01 -~~~~~~~~~~~~~~~~~ - -* Normalize post-release spellings for rev/r prefixes. `#35 `__ - - -15.2 - 2015-05-13 -~~~~~~~~~~~~~~~~~ - -* Fix an error where the arbitary specifier (``===``) was not correctly - allowing pre-releases when it was being used. - -* Expose the specifier and version parts through properties on the - ``Specifier`` classes. - -* Allow iterating over the ``SpecifierSet`` to get access to all of the - ``Specifier`` instances. - -* Allow testing if a version is contained within a specifier via the ``in`` - operator. - - -15.1 - 2015-04-13 -~~~~~~~~~~~~~~~~~ - -* Fix a logic error that was causing inconsistent answers about whether or not - a pre-release was contained within a ``SpecifierSet`` or not. - - -15.0 - 2015-01-02 -~~~~~~~~~~~~~~~~~ - -* Add ``Version().is_postrelease`` and ``LegacyVersion().is_postrelease`` to - make it easy to determine if a release is a post release. - -* Add ``Version().base_version`` and ``LegacyVersion().base_version`` to make - it easy to get the public version without any pre or post release markers. - -* Support the update to PEP 440 which removed the implied ``!=V.*`` when using - either ``>V`` or ``V`` or ````) operator. - - -14.3 - 2014-11-19 -~~~~~~~~~~~~~~~~~ - -* **BACKWARDS INCOMPATIBLE** Refactor specifier support so that it can sanely - handle legacy specifiers as well as PEP 440 specifiers. - -* **BACKWARDS INCOMPATIBLE** Move the specifier support out of - ``packaging.version`` into ``packaging.specifiers``. - - -14.2 - 2014-09-10 -~~~~~~~~~~~~~~~~~ - -* Add prerelease support to ``Specifier``. -* Remove the ability to do ``item in Specifier()`` and replace it with - ``Specifier().contains(item)`` in order to allow flags that signal if a - prerelease should be accepted or not. -* Add a method ``Specifier().filter()`` which will take an iterable and returns - an iterable with items that do not match the specifier filtered out. - - -14.1 - 2014-09-08 -~~~~~~~~~~~~~~~~~ - -* Allow ``LegacyVersion`` and ``Version`` to be sorted together. -* Add ``packaging.version.parse()`` to enable easily parsing a version string - as either a ``Version`` or a ``LegacyVersion`` depending on it's PEP 440 - validity. - - -14.0 - 2014-09-05 -~~~~~~~~~~~~~~~~~ - -* Initial release. - - -.. _`master`: https://github.com/pypa/packaging/ - - diff --git a/_setuptools_vendored/packaging-20.4.dist-info/RECORD b/_setuptools_vendored/packaging-20.4.dist-info/RECORD deleted file mode 100644 index 1880a93d64..0000000000 --- a/_setuptools_vendored/packaging-20.4.dist-info/RECORD +++ /dev/null @@ -1,19 +0,0 @@ -packaging/__about__.py,sha256=PNMsaZn4UcCHyubgROH1bl6CluduPjI5kFrSp_Zgklo,736 -packaging/__init__.py,sha256=6enbp5XgRfjBjsI9-bn00HjHf5TH21PDMOKkJW8xw-w,562 -packaging/_compat.py,sha256=MXdsGpSE_W-ZrHoC87andI4LV2FAwU7HLL-eHe_CjhU,1128 -packaging/_structures.py,sha256=ozkCX8Q8f2qE1Eic3YiQ4buDVfgz2iYevY9e7R2y3iY,2022 -packaging/_typing.py,sha256=x59EhQ57TMT-kTRyLZV25HZvYGGwbucTo6iKh_O0tMw,1812 -packaging/markers.py,sha256=ynIh92WKxQTx663RE66zJ4r4MlNHPWTFYzP0Q1e-cLc,9455 -packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -packaging/requirements.py,sha256=nvrgDjjCJZbb4lylquBO5oXQbY5dIlcq6CyQxjYJzqM,4855 -packaging/specifiers.py,sha256=uYp9l13F0LcknS6d4N60ytiBgFmIhKideOq9AnsxTco,31944 -packaging/tags.py,sha256=NKMS37Zo_nWrZxgsD6zbXsXgc9edn9m160cBiLmHJdE,24067 -packaging/utils.py,sha256=RShlvnjO2CtYSD8uri32frMMFMTmB-3ihsq1-ghzLEw,1811 -packaging/version.py,sha256=Cnbm-OO9D_qd8ZTFxzFcjSavexSYFZmyeaoPvMsjgPc,15470 -packaging-20.4.dist-info/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197 -packaging-20.4.dist-info/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174 -packaging-20.4.dist-info/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344 -packaging-20.4.dist-info/METADATA,sha256=NPvtLUTeeRypc68HcgRkyEiIips2o9rChFrbj0t2MjE,11212 -packaging-20.4.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 -packaging-20.4.dist-info/top_level.txt,sha256=zFdHrhWnPslzsiP455HutQsqPB6v0KCtNUMtUtrefDw,10 -packaging-20.4.dist-info/RECORD,, diff --git a/_setuptools_vendored/packaging-20.4.dist-info/WHEEL b/_setuptools_vendored/packaging-20.4.dist-info/WHEEL deleted file mode 100644 index ef99c6cf32..0000000000 --- a/_setuptools_vendored/packaging-20.4.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.34.2) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/_setuptools_vendored/packaging-20.4.dist-info/top_level.txt b/_setuptools_vendored/packaging-20.4.dist-info/top_level.txt deleted file mode 100644 index 748809f75c..0000000000 --- a/_setuptools_vendored/packaging-20.4.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -packaging diff --git a/_setuptools_vendored/packaging/__about__.py b/_setuptools_vendored/packaging/__about__.py deleted file mode 100644 index 4d998578d7..0000000000 --- a/_setuptools_vendored/packaging/__about__.py +++ /dev/null @@ -1,27 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] - -__title__ = "packaging" -__summary__ = "Core utilities for Python packages" -__uri__ = "https://github.com/pypa/packaging" - -__version__ = "20.4" - -__author__ = "Donald Stufft and individual contributors" -__email__ = "donald@stufft.io" - -__license__ = "BSD-2-Clause or Apache-2.0" -__copyright__ = "Copyright 2014-2019 %s" % __author__ diff --git a/_setuptools_vendored/packaging/__init__.py b/_setuptools_vendored/packaging/__init__.py deleted file mode 100644 index a0cf67df52..0000000000 --- a/_setuptools_vendored/packaging/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -from .__about__ import ( - __author__, - __copyright__, - __email__, - __license__, - __summary__, - __title__, - __uri__, - __version__, -) - -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] diff --git a/_setuptools_vendored/packaging/_compat.py b/_setuptools_vendored/packaging/_compat.py deleted file mode 100644 index e54bd4ede8..0000000000 --- a/_setuptools_vendored/packaging/_compat.py +++ /dev/null @@ -1,38 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import sys - -from ._typing import TYPE_CHECKING - -if TYPE_CHECKING: # pragma: no cover - from typing import Any, Dict, Tuple, Type - - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -# flake8: noqa - -if PY3: - string_types = (str,) -else: - string_types = (basestring,) - - -def with_metaclass(meta, *bases): - # type: (Type[Any], Tuple[Type[Any], ...]) -> Any - """ - Create a base class with a metaclass. - """ - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): # type: ignore - def __new__(cls, name, this_bases, d): - # type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any - return meta(name, bases, d) - - return type.__new__(metaclass, "temporary_class", (), {}) diff --git a/_setuptools_vendored/packaging/_structures.py b/_setuptools_vendored/packaging/_structures.py deleted file mode 100644 index 800d5c5588..0000000000 --- a/_setuptools_vendored/packaging/_structures.py +++ /dev/null @@ -1,86 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - - -class InfinityType(object): - def __repr__(self): - # type: () -> str - return "Infinity" - - def __hash__(self): - # type: () -> int - return hash(repr(self)) - - def __lt__(self, other): - # type: (object) -> bool - return False - - def __le__(self, other): - # type: (object) -> bool - return False - - def __eq__(self, other): - # type: (object) -> bool - return isinstance(other, self.__class__) - - def __ne__(self, other): - # type: (object) -> bool - return not isinstance(other, self.__class__) - - def __gt__(self, other): - # type: (object) -> bool - return True - - def __ge__(self, other): - # type: (object) -> bool - return True - - def __neg__(self): - # type: (object) -> NegativeInfinityType - return NegativeInfinity - - -Infinity = InfinityType() - - -class NegativeInfinityType(object): - def __repr__(self): - # type: () -> str - return "-Infinity" - - def __hash__(self): - # type: () -> int - return hash(repr(self)) - - def __lt__(self, other): - # type: (object) -> bool - return True - - def __le__(self, other): - # type: (object) -> bool - return True - - def __eq__(self, other): - # type: (object) -> bool - return isinstance(other, self.__class__) - - def __ne__(self, other): - # type: (object) -> bool - return not isinstance(other, self.__class__) - - def __gt__(self, other): - # type: (object) -> bool - return False - - def __ge__(self, other): - # type: (object) -> bool - return False - - def __neg__(self): - # type: (object) -> InfinityType - return Infinity - - -NegativeInfinity = NegativeInfinityType() diff --git a/_setuptools_vendored/packaging/_typing.py b/_setuptools_vendored/packaging/_typing.py deleted file mode 100644 index 77a8b9185a..0000000000 --- a/_setuptools_vendored/packaging/_typing.py +++ /dev/null @@ -1,48 +0,0 @@ -"""For neatly implementing static typing in packaging. - -`mypy` - the static type analysis tool we use - uses the `typing` module, which -provides core functionality fundamental to mypy's functioning. - -Generally, `typing` would be imported at runtime and used in that fashion - -it acts as a no-op at runtime and does not have any run-time overhead by -design. - -As it turns out, `typing` is not vendorable - it uses separate sources for -Python 2/Python 3. Thus, this codebase can not expect it to be present. -To work around this, mypy allows the typing import to be behind a False-y -optional to prevent it from running at runtime and type-comments can be used -to remove the need for the types to be accessible directly during runtime. - -This module provides the False-y guard in a nicely named fashion so that a -curious maintainer can reach here to read this. - -In packaging, all static-typing related imports should be guarded as follows: - - from packaging._typing import TYPE_CHECKING - - if TYPE_CHECKING: - from typing import ... - -Ref: https://github.com/python/mypy/issues/3216 -""" - -__all__ = ["TYPE_CHECKING", "cast"] - -# The TYPE_CHECKING constant defined by the typing module is False at runtime -# but True while type checking. -if False: # pragma: no cover - from typing import TYPE_CHECKING -else: - TYPE_CHECKING = False - -# typing's cast syntax requires calling typing.cast at runtime, but we don't -# want to import typing at runtime. Here, we inform the type checkers that -# we're importing `typing.cast` as `cast` and re-implement typing.cast's -# runtime behavior in a block that is ignored by type checkers. -if TYPE_CHECKING: # pragma: no cover - # not executed at runtime - from typing import cast -else: - # executed at runtime - def cast(type_, value): # noqa - return value diff --git a/_setuptools_vendored/packaging/markers.py b/_setuptools_vendored/packaging/markers.py deleted file mode 100644 index 87cd3f9585..0000000000 --- a/_setuptools_vendored/packaging/markers.py +++ /dev/null @@ -1,328 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import operator -import os -import platform -import sys - -from pyparsing import ParseException, ParseResults, stringStart, stringEnd -from pyparsing import ZeroOrMore, Group, Forward, QuotedString -from pyparsing import Literal as L # noqa - -from ._compat import string_types -from ._typing import TYPE_CHECKING -from .specifiers import Specifier, InvalidSpecifier - -if TYPE_CHECKING: # pragma: no cover - from typing import Any, Callable, Dict, List, Optional, Tuple, Union - - Operator = Callable[[str, str], bool] - - -__all__ = [ - "InvalidMarker", - "UndefinedComparison", - "UndefinedEnvironmentName", - "Marker", - "default_environment", -] - - -class InvalidMarker(ValueError): - """ - An invalid marker was found, users should refer to PEP 508. - """ - - -class UndefinedComparison(ValueError): - """ - An invalid operation was attempted on a value that doesn't support it. - """ - - -class UndefinedEnvironmentName(ValueError): - """ - A name was attempted to be used that does not exist inside of the - environment. - """ - - -class Node(object): - def __init__(self, value): - # type: (Any) -> None - self.value = value - - def __str__(self): - # type: () -> str - return str(self.value) - - def __repr__(self): - # type: () -> str - return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) - - def serialize(self): - # type: () -> str - raise NotImplementedError - - -class Variable(Node): - def serialize(self): - # type: () -> str - return str(self) - - -class Value(Node): - def serialize(self): - # type: () -> str - return '"{0}"'.format(self) - - -class Op(Node): - def serialize(self): - # type: () -> str - return str(self) - - -VARIABLE = ( - L("implementation_version") - | L("platform_python_implementation") - | L("implementation_name") - | L("python_full_version") - | L("platform_release") - | L("platform_version") - | L("platform_machine") - | L("platform_system") - | L("python_version") - | L("sys_platform") - | L("os_name") - | L("os.name") # PEP-345 - | L("sys.platform") # PEP-345 - | L("platform.version") # PEP-345 - | L("platform.machine") # PEP-345 - | L("platform.python_implementation") # PEP-345 - | L("python_implementation") # undocumented setuptools legacy - | L("extra") # PEP-508 -) -ALIASES = { - "os.name": "os_name", - "sys.platform": "sys_platform", - "platform.version": "platform_version", - "platform.machine": "platform_machine", - "platform.python_implementation": "platform_python_implementation", - "python_implementation": "platform_python_implementation", -} -VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) - -VERSION_CMP = ( - L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") -) - -MARKER_OP = VERSION_CMP | L("not in") | L("in") -MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) - -MARKER_VALUE = QuotedString("'") | QuotedString('"') -MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) - -BOOLOP = L("and") | L("or") - -MARKER_VAR = VARIABLE | MARKER_VALUE - -MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) -MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) - -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() - -MARKER_EXPR = Forward() -MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) -MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) - -MARKER = stringStart + MARKER_EXPR + stringEnd - - -def _coerce_parse_result(results): - # type: (Union[ParseResults, List[Any]]) -> List[Any] - if isinstance(results, ParseResults): - return [_coerce_parse_result(i) for i in results] - else: - return results - - -def _format_marker(marker, first=True): - # type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str - - assert isinstance(marker, (list, tuple, string_types)) - - # Sometimes we have a structure like [[...]] which is a single item list - # where the single item is itself it's own list. In that case we want skip - # the rest of this function so that we don't get extraneous () on the - # outside. - if ( - isinstance(marker, list) - and len(marker) == 1 - and isinstance(marker[0], (list, tuple)) - ): - return _format_marker(marker[0]) - - if isinstance(marker, list): - inner = (_format_marker(m, first=False) for m in marker) - if first: - return " ".join(inner) - else: - return "(" + " ".join(inner) + ")" - elif isinstance(marker, tuple): - return " ".join([m.serialize() for m in marker]) - else: - return marker - - -_operators = { - "in": lambda lhs, rhs: lhs in rhs, - "not in": lambda lhs, rhs: lhs not in rhs, - "<": operator.lt, - "<=": operator.le, - "==": operator.eq, - "!=": operator.ne, - ">=": operator.ge, - ">": operator.gt, -} # type: Dict[str, Operator] - - -def _eval_op(lhs, op, rhs): - # type: (str, Op, str) -> bool - try: - spec = Specifier("".join([op.serialize(), rhs])) - except InvalidSpecifier: - pass - else: - return spec.contains(lhs) - - oper = _operators.get(op.serialize()) # type: Optional[Operator] - if oper is None: - raise UndefinedComparison( - "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) - ) - - return oper(lhs, rhs) - - -class Undefined(object): - pass - - -_undefined = Undefined() - - -def _get_env(environment, name): - # type: (Dict[str, str], str) -> str - value = environment.get(name, _undefined) # type: Union[str, Undefined] - - if isinstance(value, Undefined): - raise UndefinedEnvironmentName( - "{0!r} does not exist in evaluation environment.".format(name) - ) - - return value - - -def _evaluate_markers(markers, environment): - # type: (List[Any], Dict[str, str]) -> bool - groups = [[]] # type: List[List[bool]] - - for marker in markers: - assert isinstance(marker, (list, tuple, string_types)) - - if isinstance(marker, list): - groups[-1].append(_evaluate_markers(marker, environment)) - elif isinstance(marker, tuple): - lhs, op, rhs = marker - - if isinstance(lhs, Variable): - lhs_value = _get_env(environment, lhs.value) - rhs_value = rhs.value - else: - lhs_value = lhs.value - rhs_value = _get_env(environment, rhs.value) - - groups[-1].append(_eval_op(lhs_value, op, rhs_value)) - else: - assert marker in ["and", "or"] - if marker == "or": - groups.append([]) - - return any(all(item) for item in groups) - - -def format_full_version(info): - # type: (sys._version_info) -> str - version = "{0.major}.{0.minor}.{0.micro}".format(info) - kind = info.releaselevel - if kind != "final": - version += kind[0] + str(info.serial) - return version - - -def default_environment(): - # type: () -> Dict[str, str] - if hasattr(sys, "implementation"): - # Ignoring the `sys.implementation` reference for type checking due to - # mypy not liking that the attribute doesn't exist in Python 2.7 when - # run with the `--py27` flag. - iver = format_full_version(sys.implementation.version) # type: ignore - implementation_name = sys.implementation.name # type: ignore - else: - iver = "0" - implementation_name = "" - - return { - "implementation_name": implementation_name, - "implementation_version": iver, - "os_name": os.name, - "platform_machine": platform.machine(), - "platform_release": platform.release(), - "platform_system": platform.system(), - "platform_version": platform.version(), - "python_full_version": platform.python_version(), - "platform_python_implementation": platform.python_implementation(), - "python_version": ".".join(platform.python_version_tuple()[:2]), - "sys_platform": sys.platform, - } - - -class Marker(object): - def __init__(self, marker): - # type: (str) -> None - try: - self._markers = _coerce_parse_result(MARKER.parseString(marker)) - except ParseException as e: - err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( - marker, marker[e.loc : e.loc + 8] - ) - raise InvalidMarker(err_str) - - def __str__(self): - # type: () -> str - return _format_marker(self._markers) - - def __repr__(self): - # type: () -> str - return "".format(str(self)) - - def evaluate(self, environment=None): - # type: (Optional[Dict[str, str]]) -> bool - """Evaluate a marker. - - Return the boolean from evaluating the given marker against the - environment. environment is an optional argument to override all or - part of the determined environment. - - The environment is determined from the current Python process. - """ - current_environment = default_environment() - if environment is not None: - current_environment.update(environment) - - return _evaluate_markers(self._markers, current_environment) diff --git a/_setuptools_vendored/packaging/py.typed b/_setuptools_vendored/packaging/py.typed deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/_setuptools_vendored/packaging/requirements.py b/_setuptools_vendored/packaging/requirements.py deleted file mode 100644 index 91f81ede00..0000000000 --- a/_setuptools_vendored/packaging/requirements.py +++ /dev/null @@ -1,145 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import string -import re - -from pyparsing import stringStart, stringEnd, originalTextFor, ParseException -from pyparsing import ZeroOrMore, Word, Optional, Regex, Combine -from pyparsing import Literal as L # noqa -from six.moves.urllib import parse as urlparse - -from ._typing import TYPE_CHECKING -from .markers import MARKER_EXPR, Marker -from .specifiers import LegacySpecifier, Specifier, SpecifierSet - -if TYPE_CHECKING: # pragma: no cover - from typing import List - - -class InvalidRequirement(ValueError): - """ - An invalid requirement was found, users should refer to PEP 508. - """ - - -ALPHANUM = Word(string.ascii_letters + string.digits) - -LBRACKET = L("[").suppress() -RBRACKET = L("]").suppress() -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() -COMMA = L(",").suppress() -SEMICOLON = L(";").suppress() -AT = L("@").suppress() - -PUNCTUATION = Word("-_.") -IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) -IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) - -NAME = IDENTIFIER("name") -EXTRA = IDENTIFIER - -URI = Regex(r"[^ ]+")("url") -URL = AT + URI - -EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) -EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") - -VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) -VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) - -VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY -VERSION_MANY = Combine( - VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False -)("_raw_spec") -_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) -_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") - -VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") -VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) - -MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") -MARKER_EXPR.setParseAction( - lambda s, l, t: Marker(s[t._original_start : t._original_end]) -) -MARKER_SEPARATOR = SEMICOLON -MARKER = MARKER_SEPARATOR + MARKER_EXPR - -VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) -URL_AND_MARKER = URL + Optional(MARKER) - -NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) - -REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd -# pyparsing isn't thread safe during initialization, so we do it eagerly, see -# issue #104 -REQUIREMENT.parseString("x[]") - - -class Requirement(object): - """Parse a requirement. - - Parse a given requirement string into its parts, such as name, specifier, - URL, and extras. Raises InvalidRequirement on a badly-formed requirement - string. - """ - - # TODO: Can we test whether something is contained within a requirement? - # If so how do we do that? Do we need to test against the _name_ of - # the thing as well as the version? What about the markers? - # TODO: Can we normalize the name and extra name? - - def __init__(self, requirement_string): - # type: (str) -> None - try: - req = REQUIREMENT.parseString(requirement_string) - except ParseException as e: - raise InvalidRequirement( - 'Parse error at "{0!r}": {1}'.format( - requirement_string[e.loc : e.loc + 8], e.msg - ) - ) - - self.name = req.name - if req.url: - parsed_url = urlparse.urlparse(req.url) - if parsed_url.scheme == "file": - if urlparse.urlunparse(parsed_url) != req.url: - raise InvalidRequirement("Invalid URL given") - elif not (parsed_url.scheme and parsed_url.netloc) or ( - not parsed_url.scheme and not parsed_url.netloc - ): - raise InvalidRequirement("Invalid URL: {0}".format(req.url)) - self.url = req.url - else: - self.url = None - self.extras = set(req.extras.asList() if req.extras else []) - self.specifier = SpecifierSet(req.specifier) - self.marker = req.marker if req.marker else None - - def __str__(self): - # type: () -> str - parts = [self.name] # type: List[str] - - if self.extras: - parts.append("[{0}]".format(",".join(sorted(self.extras)))) - - if self.specifier: - parts.append(str(self.specifier)) - - if self.url: - parts.append("@ {0}".format(self.url)) - if self.marker: - parts.append(" ") - - if self.marker: - parts.append("; {0}".format(self.marker)) - - return "".join(parts) - - def __repr__(self): - # type: () -> str - return "".format(str(self)) diff --git a/_setuptools_vendored/packaging/specifiers.py b/_setuptools_vendored/packaging/specifiers.py deleted file mode 100644 index fe09bb1dbb..0000000000 --- a/_setuptools_vendored/packaging/specifiers.py +++ /dev/null @@ -1,863 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import abc -import functools -import itertools -import re - -from ._compat import string_types, with_metaclass -from ._typing import TYPE_CHECKING -from .utils import canonicalize_version -from .version import Version, LegacyVersion, parse - -if TYPE_CHECKING: # pragma: no cover - from typing import ( - List, - Dict, - Union, - Iterable, - Iterator, - Optional, - Callable, - Tuple, - FrozenSet, - ) - - ParsedVersion = Union[Version, LegacyVersion] - UnparsedVersion = Union[Version, LegacyVersion, str] - CallableOperator = Callable[[ParsedVersion, str], bool] - - -class InvalidSpecifier(ValueError): - """ - An invalid specifier was found, users should refer to PEP 440. - """ - - -class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore - @abc.abstractmethod - def __str__(self): - # type: () -> str - """ - Returns the str representation of this Specifier like object. This - should be representative of the Specifier itself. - """ - - @abc.abstractmethod - def __hash__(self): - # type: () -> int - """ - Returns a hash value for this Specifier like object. - """ - - @abc.abstractmethod - def __eq__(self, other): - # type: (object) -> bool - """ - Returns a boolean representing whether or not the two Specifier like - objects are equal. - """ - - @abc.abstractmethod - def __ne__(self, other): - # type: (object) -> bool - """ - Returns a boolean representing whether or not the two Specifier like - objects are not equal. - """ - - @abc.abstractproperty - def prereleases(self): - # type: () -> Optional[bool] - """ - Returns whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None - """ - Sets whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @abc.abstractmethod - def contains(self, item, prereleases=None): - # type: (str, Optional[bool]) -> bool - """ - Determines if the given item is contained within this specifier. - """ - - @abc.abstractmethod - def filter(self, iterable, prereleases=None): - # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] - """ - Takes an iterable of items and filters them so that only items which - are contained within this specifier are allowed in it. - """ - - -class _IndividualSpecifier(BaseSpecifier): - - _operators = {} # type: Dict[str, str] - - def __init__(self, spec="", prereleases=None): - # type: (str, Optional[bool]) -> None - match = self._regex.search(spec) - if not match: - raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) - - self._spec = ( - match.group("operator").strip(), - match.group("version").strip(), - ) # type: Tuple[str, str] - - # Store whether or not this Specifier should accept prereleases - self._prereleases = prereleases - - def __repr__(self): - # type: () -> str - pre = ( - ", prereleases={0!r}".format(self.prereleases) - if self._prereleases is not None - else "" - ) - - return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre) - - def __str__(self): - # type: () -> str - return "{0}{1}".format(*self._spec) - - @property - def _canonical_spec(self): - # type: () -> Tuple[str, Union[Version, str]] - return self._spec[0], canonicalize_version(self._spec[1]) - - def __hash__(self): - # type: () -> int - return hash(self._canonical_spec) - - def __eq__(self, other): - # type: (object) -> bool - if isinstance(other, string_types): - try: - other = self.__class__(str(other)) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._canonical_spec == other._canonical_spec - - def __ne__(self, other): - # type: (object) -> bool - if isinstance(other, string_types): - try: - other = self.__class__(str(other)) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._spec != other._spec - - def _get_operator(self, op): - # type: (str) -> CallableOperator - operator_callable = getattr( - self, "_compare_{0}".format(self._operators[op]) - ) # type: CallableOperator - return operator_callable - - def _coerce_version(self, version): - # type: (UnparsedVersion) -> ParsedVersion - if not isinstance(version, (LegacyVersion, Version)): - version = parse(version) - return version - - @property - def operator(self): - # type: () -> str - return self._spec[0] - - @property - def version(self): - # type: () -> str - return self._spec[1] - - @property - def prereleases(self): - # type: () -> Optional[bool] - return self._prereleases - - @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None - self._prereleases = value - - def __contains__(self, item): - # type: (str) -> bool - return self.contains(item) - - def contains(self, item, prereleases=None): - # type: (UnparsedVersion, Optional[bool]) -> bool - - # Determine if prereleases are to be allowed or not. - if prereleases is None: - prereleases = self.prereleases - - # Normalize item to a Version or LegacyVersion, this allows us to have - # a shortcut for ``"2.0" in Specifier(">=2") - normalized_item = self._coerce_version(item) - - # Determine if we should be supporting prereleases in this specifier - # or not, if we do not support prereleases than we can short circuit - # logic if this version is a prereleases. - if normalized_item.is_prerelease and not prereleases: - return False - - # Actually do the comparison to determine if this item is contained - # within this Specifier or not. - operator_callable = self._get_operator(self.operator) # type: CallableOperator - return operator_callable(normalized_item, self.version) - - def filter(self, iterable, prereleases=None): - # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] - - yielded = False - found_prereleases = [] - - kw = {"prereleases": prereleases if prereleases is not None else True} - - # Attempt to iterate over all the values in the iterable and if any of - # them match, yield them. - for version in iterable: - parsed_version = self._coerce_version(version) - - if self.contains(parsed_version, **kw): - # If our version is a prerelease, and we were not set to allow - # prereleases, then we'll store it for later incase nothing - # else matches this specifier. - if parsed_version.is_prerelease and not ( - prereleases or self.prereleases - ): - found_prereleases.append(version) - # Either this is not a prerelease, or we should have been - # accepting prereleases from the beginning. - else: - yielded = True - yield version - - # Now that we've iterated over everything, determine if we've yielded - # any values, and if we have not and we have any prereleases stored up - # then we will go ahead and yield the prereleases. - if not yielded and found_prereleases: - for version in found_prereleases: - yield version - - -class LegacySpecifier(_IndividualSpecifier): - - _regex_str = r""" - (?P(==|!=|<=|>=|<|>)) - \s* - (?P - [^,;\s)]* # Since this is a "legacy" specifier, and the version - # string can be just about anything, we match everything - # except for whitespace, a semi-colon for marker support, - # a closing paren since versions can be enclosed in - # them, and a comma since it's a version separator. - ) - """ - - _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) - - _operators = { - "==": "equal", - "!=": "not_equal", - "<=": "less_than_equal", - ">=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - } - - def _coerce_version(self, version): - # type: (Union[ParsedVersion, str]) -> LegacyVersion - if not isinstance(version, LegacyVersion): - version = LegacyVersion(str(version)) - return version - - def _compare_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective == self._coerce_version(spec) - - def _compare_not_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective != self._coerce_version(spec) - - def _compare_less_than_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective <= self._coerce_version(spec) - - def _compare_greater_than_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective >= self._coerce_version(spec) - - def _compare_less_than(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective < self._coerce_version(spec) - - def _compare_greater_than(self, prospective, spec): - # type: (LegacyVersion, str) -> bool - return prospective > self._coerce_version(spec) - - -def _require_version_compare( - fn # type: (Callable[[Specifier, ParsedVersion, str], bool]) -): - # type: (...) -> Callable[[Specifier, ParsedVersion, str], bool] - @functools.wraps(fn) - def wrapped(self, prospective, spec): - # type: (Specifier, ParsedVersion, str) -> bool - if not isinstance(prospective, Version): - return False - return fn(self, prospective, spec) - - return wrapped - - -class Specifier(_IndividualSpecifier): - - _regex_str = r""" - (?P(~=|==|!=|<=|>=|<|>|===)) - (?P - (?: - # The identity operators allow for an escape hatch that will - # do an exact string match of the version you wish to install. - # This will not be parsed by PEP 440 and we cannot determine - # any semantic meaning from it. This operator is discouraged - # but included entirely as an escape hatch. - (?<====) # Only match for the identity operator - \s* - [^\s]* # We just match everything, except for whitespace - # since we are only testing for strict identity. - ) - | - (?: - # The (non)equality operators allow for wild card and local - # versions to be specified so we have to define these two - # operators separately to enable that. - (?<===|!=) # Only match for equals and not equals - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)* # release - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - - # You cannot use a wild card and a dev or local version - # together so group them with a | and make them optional. - (?: - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local - | - \.\* # Wild card syntax of .* - )? - ) - | - (?: - # The compatible operator requires at least two digits in the - # release segment. - (?<=~=) # Only match for the compatible operator - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - ) - | - (?: - # All other operators only allow a sub set of what the - # (non)equality operators do. Specifically they do not allow - # local versions to be specified nor do they allow the prefix - # matching wild cards. - (?=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - "===": "arbitrary", - } - - @_require_version_compare - def _compare_compatible(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - - # Compatible releases have an equivalent combination of >= and ==. That - # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to - # implement this in terms of the other specifiers instead of - # implementing it ourselves. The only thing we need to do is construct - # the other specifiers. - - # We want everything but the last item in the version, but we want to - # ignore post and dev releases and we want to treat the pre-release as - # it's own separate segment. - prefix = ".".join( - list( - itertools.takewhile( - lambda x: (not x.startswith("post") and not x.startswith("dev")), - _version_split(spec), - ) - )[:-1] - ) - - # Add the prefix notation to the end of our string - prefix += ".*" - - return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( - prospective, prefix - ) - - @_require_version_compare - def _compare_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - - # We need special logic to handle prefix matching - if spec.endswith(".*"): - # In the case of prefix matching we want to ignore local segment. - prospective = Version(prospective.public) - # Split the spec out by dots, and pretend that there is an implicit - # dot in between a release segment and a pre-release segment. - split_spec = _version_split(spec[:-2]) # Remove the trailing .* - - # Split the prospective version out by dots, and pretend that there - # is an implicit dot in between a release segment and a pre-release - # segment. - split_prospective = _version_split(str(prospective)) - - # Shorten the prospective version to be the same length as the spec - # so that we can determine if the specifier is a prefix of the - # prospective version or not. - shortened_prospective = split_prospective[: len(split_spec)] - - # Pad out our two sides with zeros so that they both equal the same - # length. - padded_spec, padded_prospective = _pad_version( - split_spec, shortened_prospective - ) - - return padded_prospective == padded_spec - else: - # Convert our spec string into a Version - spec_version = Version(spec) - - # If the specifier does not have a local segment, then we want to - # act as if the prospective version also does not have a local - # segment. - if not spec_version.local: - prospective = Version(prospective.public) - - return prospective == spec_version - - @_require_version_compare - def _compare_not_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - return not self._compare_equal(prospective, spec) - - @_require_version_compare - def _compare_less_than_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return Version(prospective.public) <= Version(spec) - - @_require_version_compare - def _compare_greater_than_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return Version(prospective.public) >= Version(spec) - - @_require_version_compare - def _compare_less_than(self, prospective, spec_str): - # type: (ParsedVersion, str) -> bool - - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec_str) - - # Check to see if the prospective version is less than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective < spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a pre-release version, that we do not accept pre-release - # versions for the version mentioned in the specifier (e.g. <3.1 should - # not match 3.1.dev0, but should match 3.0.dev0). - if not spec.is_prerelease and prospective.is_prerelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # less than the spec version *and* it's not a pre-release of the same - # version in the spec. - return True - - @_require_version_compare - def _compare_greater_than(self, prospective, spec_str): - # type: (ParsedVersion, str) -> bool - - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec_str) - - # Check to see if the prospective version is greater than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective > spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a post-release version, that we do not accept - # post-release versions for the version mentioned in the specifier - # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). - if not spec.is_postrelease and prospective.is_postrelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # Ensure that we do not allow a local version of the version mentioned - # in the specifier, which is technically greater than, to match. - if prospective.local is not None: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # greater than the spec version *and* it's not a pre-release of the - # same version in the spec. - return True - - def _compare_arbitrary(self, prospective, spec): - # type: (Version, str) -> bool - return str(prospective).lower() == str(spec).lower() - - @property - def prereleases(self): - # type: () -> bool - - # If there is an explicit prereleases set for this, then we'll just - # blindly use that. - if self._prereleases is not None: - return self._prereleases - - # Look at all of our specifiers and determine if they are inclusive - # operators, and if they are if they are including an explicit - # prerelease. - operator, version = self._spec - if operator in ["==", ">=", "<=", "~=", "==="]: - # The == specifier can include a trailing .*, if it does we - # want to remove before parsing. - if operator == "==" and version.endswith(".*"): - version = version[:-2] - - # Parse the version, and if it is a pre-release than this - # specifier allows pre-releases. - if parse(version).is_prerelease: - return True - - return False - - @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None - self._prereleases = value - - -_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") - - -def _version_split(version): - # type: (str) -> List[str] - result = [] # type: List[str] - for item in version.split("."): - match = _prefix_regex.search(item) - if match: - result.extend(match.groups()) - else: - result.append(item) - return result - - -def _pad_version(left, right): - # type: (List[str], List[str]) -> Tuple[List[str], List[str]] - left_split, right_split = [], [] - - # Get the release segment of our versions - left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) - right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) - - # Get the rest of our versions - left_split.append(left[len(left_split[0]) :]) - right_split.append(right[len(right_split[0]) :]) - - # Insert our padding - left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) - right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) - - return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) - - -class SpecifierSet(BaseSpecifier): - def __init__(self, specifiers="", prereleases=None): - # type: (str, Optional[bool]) -> None - - # Split on , to break each individual specifier into it's own item, and - # strip each item to remove leading/trailing whitespace. - split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] - - # Parsed each individual specifier, attempting first to make it a - # Specifier and falling back to a LegacySpecifier. - parsed = set() - for specifier in split_specifiers: - try: - parsed.add(Specifier(specifier)) - except InvalidSpecifier: - parsed.add(LegacySpecifier(specifier)) - - # Turn our parsed specifiers into a frozen set and save them for later. - self._specs = frozenset(parsed) - - # Store our prereleases value so we can use it later to determine if - # we accept prereleases or not. - self._prereleases = prereleases - - def __repr__(self): - # type: () -> str - pre = ( - ", prereleases={0!r}".format(self.prereleases) - if self._prereleases is not None - else "" - ) - - return "".format(str(self), pre) - - def __str__(self): - # type: () -> str - return ",".join(sorted(str(s) for s in self._specs)) - - def __hash__(self): - # type: () -> int - return hash(self._specs) - - def __and__(self, other): - # type: (Union[SpecifierSet, str]) -> SpecifierSet - if isinstance(other, string_types): - other = SpecifierSet(other) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - specifier = SpecifierSet() - specifier._specs = frozenset(self._specs | other._specs) - - if self._prereleases is None and other._prereleases is not None: - specifier._prereleases = other._prereleases - elif self._prereleases is not None and other._prereleases is None: - specifier._prereleases = self._prereleases - elif self._prereleases == other._prereleases: - specifier._prereleases = self._prereleases - else: - raise ValueError( - "Cannot combine SpecifierSets with True and False prerelease " - "overrides." - ) - - return specifier - - def __eq__(self, other): - # type: (object) -> bool - if isinstance(other, (string_types, _IndividualSpecifier)): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs == other._specs - - def __ne__(self, other): - # type: (object) -> bool - if isinstance(other, (string_types, _IndividualSpecifier)): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs != other._specs - - def __len__(self): - # type: () -> int - return len(self._specs) - - def __iter__(self): - # type: () -> Iterator[FrozenSet[_IndividualSpecifier]] - return iter(self._specs) - - @property - def prereleases(self): - # type: () -> Optional[bool] - - # If we have been given an explicit prerelease modifier, then we'll - # pass that through here. - if self._prereleases is not None: - return self._prereleases - - # If we don't have any specifiers, and we don't have a forced value, - # then we'll just return None since we don't know if this should have - # pre-releases or not. - if not self._specs: - return None - - # Otherwise we'll see if any of the given specifiers accept - # prereleases, if any of them do we'll return True, otherwise False. - return any(s.prereleases for s in self._specs) - - @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None - self._prereleases = value - - def __contains__(self, item): - # type: (Union[ParsedVersion, str]) -> bool - return self.contains(item) - - def contains(self, item, prereleases=None): - # type: (Union[ParsedVersion, str], Optional[bool]) -> bool - - # Ensure that our item is a Version or LegacyVersion instance. - if not isinstance(item, (LegacyVersion, Version)): - item = parse(item) - - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # We can determine if we're going to allow pre-releases by looking to - # see if any of the underlying items supports them. If none of them do - # and this item is a pre-release then we do not allow it and we can - # short circuit that here. - # Note: This means that 1.0.dev1 would not be contained in something - # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 - if not prereleases and item.is_prerelease: - return False - - # We simply dispatch to the underlying specs here to make sure that the - # given version is contained within all of them. - # Note: This use of all() here means that an empty set of specifiers - # will always return True, this is an explicit design decision. - return all(s.contains(item, prereleases=prereleases) for s in self._specs) - - def filter( - self, - iterable, # type: Iterable[Union[ParsedVersion, str]] - prereleases=None, # type: Optional[bool] - ): - # type: (...) -> Iterable[Union[ParsedVersion, str]] - - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # If we have any specifiers, then we want to wrap our iterable in the - # filter method for each one, this will act as a logical AND amongst - # each specifier. - if self._specs: - for spec in self._specs: - iterable = spec.filter(iterable, prereleases=bool(prereleases)) - return iterable - # If we do not have any specifiers, then we need to have a rough filter - # which will filter out any pre-releases, unless there are no final - # releases, and which will filter out LegacyVersion in general. - else: - filtered = [] # type: List[Union[ParsedVersion, str]] - found_prereleases = [] # type: List[Union[ParsedVersion, str]] - - for item in iterable: - # Ensure that we some kind of Version class for this item. - if not isinstance(item, (LegacyVersion, Version)): - parsed_version = parse(item) - else: - parsed_version = item - - # Filter out any item which is parsed as a LegacyVersion - if isinstance(parsed_version, LegacyVersion): - continue - - # Store any item which is a pre-release for later unless we've - # already found a final version or we are accepting prereleases - if parsed_version.is_prerelease and not prereleases: - if not filtered: - found_prereleases.append(item) - else: - filtered.append(item) - - # If we've found no items except for pre-releases, then we'll go - # ahead and use the pre-releases - if not filtered and found_prereleases and prereleases is None: - return found_prereleases - - return filtered diff --git a/_setuptools_vendored/packaging/tags.py b/_setuptools_vendored/packaging/tags.py deleted file mode 100644 index 9064910b8b..0000000000 --- a/_setuptools_vendored/packaging/tags.py +++ /dev/null @@ -1,751 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import absolute_import - -import distutils.util - -try: - from importlib.machinery import EXTENSION_SUFFIXES -except ImportError: # pragma: no cover - import imp - - EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] - del imp -import logging -import os -import platform -import re -import struct -import sys -import sysconfig -import warnings - -from ._typing import TYPE_CHECKING, cast - -if TYPE_CHECKING: # pragma: no cover - from typing import ( - Dict, - FrozenSet, - IO, - Iterable, - Iterator, - List, - Optional, - Sequence, - Tuple, - Union, - ) - - PythonVersion = Sequence[int] - MacVersion = Tuple[int, int] - GlibcVersion = Tuple[int, int] - - -logger = logging.getLogger(__name__) - -INTERPRETER_SHORT_NAMES = { - "python": "py", # Generic. - "cpython": "cp", - "pypy": "pp", - "ironpython": "ip", - "jython": "jy", -} # type: Dict[str, str] - - -_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 - - -class Tag(object): - """ - A representation of the tag triple for a wheel. - - Instances are considered immutable and thus are hashable. Equality checking - is also supported. - """ - - __slots__ = ["_interpreter", "_abi", "_platform"] - - def __init__(self, interpreter, abi, platform): - # type: (str, str, str) -> None - self._interpreter = interpreter.lower() - self._abi = abi.lower() - self._platform = platform.lower() - - @property - def interpreter(self): - # type: () -> str - return self._interpreter - - @property - def abi(self): - # type: () -> str - return self._abi - - @property - def platform(self): - # type: () -> str - return self._platform - - def __eq__(self, other): - # type: (object) -> bool - if not isinstance(other, Tag): - return NotImplemented - - return ( - (self.platform == other.platform) - and (self.abi == other.abi) - and (self.interpreter == other.interpreter) - ) - - def __hash__(self): - # type: () -> int - return hash((self._interpreter, self._abi, self._platform)) - - def __str__(self): - # type: () -> str - return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) - - def __repr__(self): - # type: () -> str - return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) - - -def parse_tag(tag): - # type: (str) -> FrozenSet[Tag] - """ - Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. - - Returning a set is required due to the possibility that the tag is a - compressed tag set. - """ - tags = set() - interpreters, abis, platforms = tag.split("-") - for interpreter in interpreters.split("."): - for abi in abis.split("."): - for platform_ in platforms.split("."): - tags.add(Tag(interpreter, abi, platform_)) - return frozenset(tags) - - -def _warn_keyword_parameter(func_name, kwargs): - # type: (str, Dict[str, bool]) -> bool - """ - Backwards-compatibility with Python 2.7 to allow treating 'warn' as keyword-only. - """ - if not kwargs: - return False - elif len(kwargs) > 1 or "warn" not in kwargs: - kwargs.pop("warn", None) - arg = next(iter(kwargs.keys())) - raise TypeError( - "{}() got an unexpected keyword argument {!r}".format(func_name, arg) - ) - return kwargs["warn"] - - -def _get_config_var(name, warn=False): - # type: (str, bool) -> Union[int, str, None] - value = sysconfig.get_config_var(name) - if value is None and warn: - logger.debug( - "Config variable '%s' is unset, Python ABI tag may be incorrect", name - ) - return value - - -def _normalize_string(string): - # type: (str) -> str - return string.replace(".", "_").replace("-", "_") - - -def _abi3_applies(python_version): - # type: (PythonVersion) -> bool - """ - Determine if the Python version supports abi3. - - PEP 384 was first implemented in Python 3.2. - """ - return len(python_version) > 1 and tuple(python_version) >= (3, 2) - - -def _cpython_abis(py_version, warn=False): - # type: (PythonVersion, bool) -> List[str] - py_version = tuple(py_version) # To allow for version comparison. - abis = [] - version = _version_nodot(py_version[:2]) - debug = pymalloc = ucs4 = "" - with_debug = _get_config_var("Py_DEBUG", warn) - has_refcount = hasattr(sys, "gettotalrefcount") - # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled - # extension modules is the best option. - # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 - has_ext = "_d.pyd" in EXTENSION_SUFFIXES - if with_debug or (with_debug is None and (has_refcount or has_ext)): - debug = "d" - if py_version < (3, 8): - with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) - if with_pymalloc or with_pymalloc is None: - pymalloc = "m" - if py_version < (3, 3): - unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) - if unicode_size == 4 or ( - unicode_size is None and sys.maxunicode == 0x10FFFF - ): - ucs4 = "u" - elif debug: - # Debug builds can also load "normal" extension modules. - # We can also assume no UCS-4 or pymalloc requirement. - abis.append("cp{version}".format(version=version)) - abis.insert( - 0, - "cp{version}{debug}{pymalloc}{ucs4}".format( - version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4 - ), - ) - return abis - - -def cpython_tags( - python_version=None, # type: Optional[PythonVersion] - abis=None, # type: Optional[Iterable[str]] - platforms=None, # type: Optional[Iterable[str]] - **kwargs # type: bool -): - # type: (...) -> Iterator[Tag] - """ - Yields the tags for a CPython interpreter. - - The tags consist of: - - cp-- - - cp-abi3- - - cp-none- - - cp-abi3- # Older Python versions down to 3.2. - - If python_version only specifies a major version then user-provided ABIs and - the 'none' ABItag will be used. - - If 'abi3' or 'none' are specified in 'abis' then they will be yielded at - their normal position and not at the beginning. - """ - warn = _warn_keyword_parameter("cpython_tags", kwargs) - if not python_version: - python_version = sys.version_info[:2] - - interpreter = "cp{}".format(_version_nodot(python_version[:2])) - - if abis is None: - if len(python_version) > 1: - abis = _cpython_abis(python_version, warn) - else: - abis = [] - abis = list(abis) - # 'abi3' and 'none' are explicitly handled later. - for explicit_abi in ("abi3", "none"): - try: - abis.remove(explicit_abi) - except ValueError: - pass - - platforms = list(platforms or _platform_tags()) - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - if _abi3_applies(python_version): - for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): - yield tag - for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): - yield tag - - if _abi3_applies(python_version): - for minor_version in range(python_version[1] - 1, 1, -1): - for platform_ in platforms: - interpreter = "cp{version}".format( - version=_version_nodot((python_version[0], minor_version)) - ) - yield Tag(interpreter, "abi3", platform_) - - -def _generic_abi(): - # type: () -> Iterator[str] - abi = sysconfig.get_config_var("SOABI") - if abi: - yield _normalize_string(abi) - - -def generic_tags( - interpreter=None, # type: Optional[str] - abis=None, # type: Optional[Iterable[str]] - platforms=None, # type: Optional[Iterable[str]] - **kwargs # type: bool -): - # type: (...) -> Iterator[Tag] - """ - Yields the tags for a generic interpreter. - - The tags consist of: - - -- - - The "none" ABI will be added if it was not explicitly provided. - """ - warn = _warn_keyword_parameter("generic_tags", kwargs) - if not interpreter: - interp_name = interpreter_name() - interp_version = interpreter_version(warn=warn) - interpreter = "".join([interp_name, interp_version]) - if abis is None: - abis = _generic_abi() - platforms = list(platforms or _platform_tags()) - abis = list(abis) - if "none" not in abis: - abis.append("none") - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - - -def _py_interpreter_range(py_version): - # type: (PythonVersion) -> Iterator[str] - """ - Yields Python versions in descending order. - - After the latest version, the major-only version will be yielded, and then - all previous versions of that major version. - """ - if len(py_version) > 1: - yield "py{version}".format(version=_version_nodot(py_version[:2])) - yield "py{major}".format(major=py_version[0]) - if len(py_version) > 1: - for minor in range(py_version[1] - 1, -1, -1): - yield "py{version}".format(version=_version_nodot((py_version[0], minor))) - - -def compatible_tags( - python_version=None, # type: Optional[PythonVersion] - interpreter=None, # type: Optional[str] - platforms=None, # type: Optional[Iterable[str]] -): - # type: (...) -> Iterator[Tag] - """ - Yields the sequence of tags that are compatible with a specific version of Python. - - The tags consist of: - - py*-none- - - -none-any # ... if `interpreter` is provided. - - py*-none-any - """ - if not python_version: - python_version = sys.version_info[:2] - platforms = list(platforms or _platform_tags()) - for version in _py_interpreter_range(python_version): - for platform_ in platforms: - yield Tag(version, "none", platform_) - if interpreter: - yield Tag(interpreter, "none", "any") - for version in _py_interpreter_range(python_version): - yield Tag(version, "none", "any") - - -def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): - # type: (str, bool) -> str - if not is_32bit: - return arch - - if arch.startswith("ppc"): - return "ppc" - - return "i386" - - -def _mac_binary_formats(version, cpu_arch): - # type: (MacVersion, str) -> List[str] - formats = [cpu_arch] - if cpu_arch == "x86_64": - if version < (10, 4): - return [] - formats.extend(["intel", "fat64", "fat32"]) - - elif cpu_arch == "i386": - if version < (10, 4): - return [] - formats.extend(["intel", "fat32", "fat"]) - - elif cpu_arch == "ppc64": - # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? - if version > (10, 5) or version < (10, 4): - return [] - formats.append("fat64") - - elif cpu_arch == "ppc": - if version > (10, 6): - return [] - formats.extend(["fat32", "fat"]) - - formats.append("universal") - return formats - - -def mac_platforms(version=None, arch=None): - # type: (Optional[MacVersion], Optional[str]) -> Iterator[str] - """ - Yields the platform tags for a macOS system. - - The `version` parameter is a two-item tuple specifying the macOS version to - generate platform tags for. The `arch` parameter is the CPU architecture to - generate platform tags for. Both parameters default to the appropriate value - for the current system. - """ - version_str, _, cpu_arch = platform.mac_ver() # type: ignore - if version is None: - version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) - else: - version = version - if arch is None: - arch = _mac_arch(cpu_arch) - else: - arch = arch - for minor_version in range(version[1], -1, -1): - compat_version = version[0], minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) - - -# From PEP 513. -def _is_manylinux_compatible(name, glibc_version): - # type: (str, GlibcVersion) -> bool - # Check for presence of _manylinux module. - try: - import _manylinux # noqa - - return bool(getattr(_manylinux, name + "_compatible")) - except (ImportError, AttributeError): - # Fall through to heuristic check below. - pass - - return _have_compatible_glibc(*glibc_version) - - -def _glibc_version_string(): - # type: () -> Optional[str] - # Returns glibc version string, or None if not using glibc. - return _glibc_version_string_confstr() or _glibc_version_string_ctypes() - - -def _glibc_version_string_confstr(): - # type: () -> Optional[str] - """ - Primary implementation of glibc_version_string using os.confstr. - """ - # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely - # to be broken or missing. This strategy is used in the standard library - # platform module. - # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 - try: - # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". - version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821 - "CS_GNU_LIBC_VERSION" - ) - assert version_string is not None - _, version = version_string.split() # type: Tuple[str, str] - except (AssertionError, AttributeError, OSError, ValueError): - # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... - return None - return version - - -def _glibc_version_string_ctypes(): - # type: () -> Optional[str] - """ - Fallback implementation of glibc_version_string using ctypes. - """ - try: - import ctypes - except ImportError: - return None - - # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen - # manpage says, "If filename is NULL, then the returned handle is for the - # main program". This way we can let the linker do the work to figure out - # which libc our process is actually using. - # - # Note: typeshed is wrong here so we are ignoring this line. - process_namespace = ctypes.CDLL(None) # type: ignore - try: - gnu_get_libc_version = process_namespace.gnu_get_libc_version - except AttributeError: - # Symbol doesn't exist -> therefore, we are not linked to - # glibc. - return None - - # Call gnu_get_libc_version, which returns a string like "2.5" - gnu_get_libc_version.restype = ctypes.c_char_p - version_str = gnu_get_libc_version() # type: str - # py2 / py3 compatibility: - if not isinstance(version_str, str): - version_str = version_str.decode("ascii") - - return version_str - - -# Separated out from have_compatible_glibc for easier unit testing. -def _check_glibc_version(version_str, required_major, minimum_minor): - # type: (str, int, int) -> bool - # Parse string and check against requested version. - # - # We use a regexp instead of str.split because we want to discard any - # random junk that might come after the minor version -- this might happen - # in patched/forked versions of glibc (e.g. Linaro's version of glibc - # uses version strings like "2.20-2014.11"). See gh-3588. - m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) - if not m: - warnings.warn( - "Expected glibc version with 2 components major.minor," - " got: %s" % version_str, - RuntimeWarning, - ) - return False - return ( - int(m.group("major")) == required_major - and int(m.group("minor")) >= minimum_minor - ) - - -def _have_compatible_glibc(required_major, minimum_minor): - # type: (int, int) -> bool - version_str = _glibc_version_string() - if version_str is None: - return False - return _check_glibc_version(version_str, required_major, minimum_minor) - - -# Python does not provide platform information at sufficient granularity to -# identify the architecture of the running executable in some cases, so we -# determine it dynamically by reading the information from the running -# process. This only applies on Linux, which uses the ELF format. -class _ELFFileHeader(object): - # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header - class _InvalidELFFileHeader(ValueError): - """ - An invalid ELF file header was found. - """ - - ELF_MAGIC_NUMBER = 0x7F454C46 - ELFCLASS32 = 1 - ELFCLASS64 = 2 - ELFDATA2LSB = 1 - ELFDATA2MSB = 2 - EM_386 = 3 - EM_S390 = 22 - EM_ARM = 40 - EM_X86_64 = 62 - EF_ARM_ABIMASK = 0xFF000000 - EF_ARM_ABI_VER5 = 0x05000000 - EF_ARM_ABI_FLOAT_HARD = 0x00000400 - - def __init__(self, file): - # type: (IO[bytes]) -> None - def unpack(fmt): - # type: (str) -> int - try: - (result,) = struct.unpack( - fmt, file.read(struct.calcsize(fmt)) - ) # type: (int, ) - except struct.error: - raise _ELFFileHeader._InvalidELFFileHeader() - return result - - self.e_ident_magic = unpack(">I") - if self.e_ident_magic != self.ELF_MAGIC_NUMBER: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_class = unpack("B") - if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_data = unpack("B") - if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_version = unpack("B") - self.e_ident_osabi = unpack("B") - self.e_ident_abiversion = unpack("B") - self.e_ident_pad = file.read(7) - format_h = "H" - format_i = "I" - format_q = "Q" - format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q - self.e_type = unpack(format_h) - self.e_machine = unpack(format_h) - self.e_version = unpack(format_i) - self.e_entry = unpack(format_p) - self.e_phoff = unpack(format_p) - self.e_shoff = unpack(format_p) - self.e_flags = unpack(format_i) - self.e_ehsize = unpack(format_h) - self.e_phentsize = unpack(format_h) - self.e_phnum = unpack(format_h) - self.e_shentsize = unpack(format_h) - self.e_shnum = unpack(format_h) - self.e_shstrndx = unpack(format_h) - - -def _get_elf_header(): - # type: () -> Optional[_ELFFileHeader] - try: - with open(sys.executable, "rb") as f: - elf_header = _ELFFileHeader(f) - except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): - return None - return elf_header - - -def _is_linux_armhf(): - # type: () -> bool - # hard-float ABI can be detected from the ELF header of the running - # process - # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_ARM - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABIMASK - ) == elf_header.EF_ARM_ABI_VER5 - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD - ) == elf_header.EF_ARM_ABI_FLOAT_HARD - return result - - -def _is_linux_i686(): - # type: () -> bool - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_386 - return result - - -def _have_compatible_manylinux_abi(arch): - # type: (str) -> bool - if arch == "armv7l": - return _is_linux_armhf() - if arch == "i686": - return _is_linux_i686() - return True - - -def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): - # type: (bool) -> Iterator[str] - linux = _normalize_string(distutils.util.get_platform()) - if is_32bit: - if linux == "linux_x86_64": - linux = "linux_i686" - elif linux == "linux_aarch64": - linux = "linux_armv7l" - manylinux_support = [] - _, arch = linux.split("_", 1) - if _have_compatible_manylinux_abi(arch): - if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}: - manylinux_support.append( - ("manylinux2014", (2, 17)) - ) # CentOS 7 w/ glibc 2.17 (PEP 599) - if arch in {"x86_64", "i686"}: - manylinux_support.append( - ("manylinux2010", (2, 12)) - ) # CentOS 6 w/ glibc 2.12 (PEP 571) - manylinux_support.append( - ("manylinux1", (2, 5)) - ) # CentOS 5 w/ glibc 2.5 (PEP 513) - manylinux_support_iter = iter(manylinux_support) - for name, glibc_version in manylinux_support_iter: - if _is_manylinux_compatible(name, glibc_version): - yield linux.replace("linux", name) - break - # Support for a later manylinux implies support for an earlier version. - for name, _ in manylinux_support_iter: - yield linux.replace("linux", name) - yield linux - - -def _generic_platforms(): - # type: () -> Iterator[str] - yield _normalize_string(distutils.util.get_platform()) - - -def _platform_tags(): - # type: () -> Iterator[str] - """ - Provides the platform tags for this installation. - """ - if platform.system() == "Darwin": - return mac_platforms() - elif platform.system() == "Linux": - return _linux_platforms() - else: - return _generic_platforms() - - -def interpreter_name(): - # type: () -> str - """ - Returns the name of the running interpreter. - """ - try: - name = sys.implementation.name # type: ignore - except AttributeError: # pragma: no cover - # Python 2.7 compatibility. - name = platform.python_implementation().lower() - return INTERPRETER_SHORT_NAMES.get(name) or name - - -def interpreter_version(**kwargs): - # type: (bool) -> str - """ - Returns the version of the running interpreter. - """ - warn = _warn_keyword_parameter("interpreter_version", kwargs) - version = _get_config_var("py_version_nodot", warn=warn) - if version: - version = str(version) - else: - version = _version_nodot(sys.version_info[:2]) - return version - - -def _version_nodot(version): - # type: (PythonVersion) -> str - if any(v >= 10 for v in version): - sep = "_" - else: - sep = "" - return sep.join(map(str, version)) - - -def sys_tags(**kwargs): - # type: (bool) -> Iterator[Tag] - """ - Returns the sequence of tag triples for the running interpreter. - - The order of the sequence corresponds to priority order for the - interpreter, from most to least important. - """ - warn = _warn_keyword_parameter("sys_tags", kwargs) - - interp_name = interpreter_name() - if interp_name == "cp": - for tag in cpython_tags(warn=warn): - yield tag - else: - for tag in generic_tags(): - yield tag - - for tag in compatible_tags(): - yield tag diff --git a/_setuptools_vendored/packaging/utils.py b/_setuptools_vendored/packaging/utils.py deleted file mode 100644 index 19579c1a0f..0000000000 --- a/_setuptools_vendored/packaging/utils.py +++ /dev/null @@ -1,65 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import re - -from ._typing import TYPE_CHECKING, cast -from .version import InvalidVersion, Version - -if TYPE_CHECKING: # pragma: no cover - from typing import NewType, Union - - NormalizedName = NewType("NormalizedName", str) - -_canonicalize_regex = re.compile(r"[-_.]+") - - -def canonicalize_name(name): - # type: (str) -> NormalizedName - # This is taken from PEP 503. - value = _canonicalize_regex.sub("-", name).lower() - return cast("NormalizedName", value) - - -def canonicalize_version(_version): - # type: (str) -> Union[Version, str] - """ - This is very similar to Version.__str__, but has one subtle difference - with the way it handles the release segment. - """ - - try: - version = Version(_version) - except InvalidVersion: - # Legacy versions cannot be normalized - return _version - - parts = [] - - # Epoch - if version.epoch != 0: - parts.append("{0}!".format(version.epoch)) - - # Release segment - # NB: This strips trailing '.0's to normalize - parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release))) - - # Pre-release - if version.pre is not None: - parts.append("".join(str(x) for x in version.pre)) - - # Post-release - if version.post is not None: - parts.append(".post{0}".format(version.post)) - - # Development release - if version.dev is not None: - parts.append(".dev{0}".format(version.dev)) - - # Local version segment - if version.local is not None: - parts.append("+{0}".format(version.local)) - - return "".join(parts) diff --git a/_setuptools_vendored/packaging/version.py b/_setuptools_vendored/packaging/version.py deleted file mode 100644 index 00371e86a8..0000000000 --- a/_setuptools_vendored/packaging/version.py +++ /dev/null @@ -1,535 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import collections -import itertools -import re - -from ._structures import Infinity, NegativeInfinity -from ._typing import TYPE_CHECKING - -if TYPE_CHECKING: # pragma: no cover - from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union - - from ._structures import InfinityType, NegativeInfinityType - - InfiniteTypes = Union[InfinityType, NegativeInfinityType] - PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] - SubLocalType = Union[InfiniteTypes, int, str] - LocalType = Union[ - NegativeInfinityType, - Tuple[ - Union[ - SubLocalType, - Tuple[SubLocalType, str], - Tuple[NegativeInfinityType, SubLocalType], - ], - ..., - ], - ] - CmpKey = Tuple[ - int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType - ] - LegacyCmpKey = Tuple[int, Tuple[str, ...]] - VersionComparisonMethod = Callable[ - [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool - ] - -__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] - - -_Version = collections.namedtuple( - "_Version", ["epoch", "release", "dev", "pre", "post", "local"] -) - - -def parse(version): - # type: (str) -> Union[LegacyVersion, Version] - """ - Parse the given version string and return either a :class:`Version` object - or a :class:`LegacyVersion` object depending on if the given version is - a valid PEP 440 version or a legacy version. - """ - try: - return Version(version) - except InvalidVersion: - return LegacyVersion(version) - - -class InvalidVersion(ValueError): - """ - An invalid version was found, users should refer to PEP 440. - """ - - -class _BaseVersion(object): - _key = None # type: Union[CmpKey, LegacyCmpKey] - - def __hash__(self): - # type: () -> int - return hash(self._key) - - def __lt__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s < o) - - def __le__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s <= o) - - def __eq__(self, other): - # type: (object) -> bool - return self._compare(other, lambda s, o: s == o) - - def __ge__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s >= o) - - def __gt__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s > o) - - def __ne__(self, other): - # type: (object) -> bool - return self._compare(other, lambda s, o: s != o) - - def _compare(self, other, method): - # type: (object, VersionComparisonMethod) -> Union[bool, NotImplemented] - if not isinstance(other, _BaseVersion): - return NotImplemented - - return method(self._key, other._key) - - -class LegacyVersion(_BaseVersion): - def __init__(self, version): - # type: (str) -> None - self._version = str(version) - self._key = _legacy_cmpkey(self._version) - - def __str__(self): - # type: () -> str - return self._version - - def __repr__(self): - # type: () -> str - return "".format(repr(str(self))) - - @property - def public(self): - # type: () -> str - return self._version - - @property - def base_version(self): - # type: () -> str - return self._version - - @property - def epoch(self): - # type: () -> int - return -1 - - @property - def release(self): - # type: () -> None - return None - - @property - def pre(self): - # type: () -> None - return None - - @property - def post(self): - # type: () -> None - return None - - @property - def dev(self): - # type: () -> None - return None - - @property - def local(self): - # type: () -> None - return None - - @property - def is_prerelease(self): - # type: () -> bool - return False - - @property - def is_postrelease(self): - # type: () -> bool - return False - - @property - def is_devrelease(self): - # type: () -> bool - return False - - -_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) - -_legacy_version_replacement_map = { - "pre": "c", - "preview": "c", - "-": "final-", - "rc": "c", - "dev": "@", -} - - -def _parse_version_parts(s): - # type: (str) -> Iterator[str] - for part in _legacy_version_component_re.split(s): - part = _legacy_version_replacement_map.get(part, part) - - if not part or part == ".": - continue - - if part[:1] in "0123456789": - # pad for numeric comparison - yield part.zfill(8) - else: - yield "*" + part - - # ensure that alpha/beta/candidate are before final - yield "*final" - - -def _legacy_cmpkey(version): - # type: (str) -> LegacyCmpKey - - # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch - # greater than or equal to 0. This will effectively put the LegacyVersion, - # which uses the defacto standard originally implemented by setuptools, - # as before all PEP 440 versions. - epoch = -1 - - # This scheme is taken from pkg_resources.parse_version setuptools prior to - # it's adoption of the packaging library. - parts = [] # type: List[str] - for part in _parse_version_parts(version.lower()): - if part.startswith("*"): - # remove "-" before a prerelease tag - if part < "*final": - while parts and parts[-1] == "*final-": - parts.pop() - - # remove trailing zeros from each series of numeric parts - while parts and parts[-1] == "00000000": - parts.pop() - - parts.append(part) - - return epoch, tuple(parts) - - -# Deliberately not anchored to the start and end of the string, to make it -# easier for 3rd party code to reuse -VERSION_PATTERN = r""" - v? - (?: - (?:(?P[0-9]+)!)? # epoch - (?P[0-9]+(?:\.[0-9]+)*) # release segment - (?P
                                          # pre-release
-            [-_\.]?
-            (?P(a|b|c|rc|alpha|beta|pre|preview))
-            [-_\.]?
-            (?P[0-9]+)?
-        )?
-        (?P                                         # post release
-            (?:-(?P[0-9]+))
-            |
-            (?:
-                [-_\.]?
-                (?Ppost|rev|r)
-                [-_\.]?
-                (?P[0-9]+)?
-            )
-        )?
-        (?P                                          # dev release
-            [-_\.]?
-            (?Pdev)
-            [-_\.]?
-            (?P[0-9]+)?
-        )?
-    )
-    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
-"""
-
-
-class Version(_BaseVersion):
-
-    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
-
-    def __init__(self, version):
-        # type: (str) -> None
-
-        # Validate the version and parse it into pieces
-        match = self._regex.search(version)
-        if not match:
-            raise InvalidVersion("Invalid version: '{0}'".format(version))
-
-        # Store the parsed out pieces of the version
-        self._version = _Version(
-            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
-            release=tuple(int(i) for i in match.group("release").split(".")),
-            pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
-            post=_parse_letter_version(
-                match.group("post_l"), match.group("post_n1") or match.group("post_n2")
-            ),
-            dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
-            local=_parse_local_version(match.group("local")),
-        )
-
-        # Generate a key which will be used for sorting
-        self._key = _cmpkey(
-            self._version.epoch,
-            self._version.release,
-            self._version.pre,
-            self._version.post,
-            self._version.dev,
-            self._version.local,
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return "".format(repr(str(self)))
-
-    def __str__(self):
-        # type: () -> str
-        parts = []
-
-        # Epoch
-        if self.epoch != 0:
-            parts.append("{0}!".format(self.epoch))
-
-        # Release segment
-        parts.append(".".join(str(x) for x in self.release))
-
-        # Pre-release
-        if self.pre is not None:
-            parts.append("".join(str(x) for x in self.pre))
-
-        # Post-release
-        if self.post is not None:
-            parts.append(".post{0}".format(self.post))
-
-        # Development release
-        if self.dev is not None:
-            parts.append(".dev{0}".format(self.dev))
-
-        # Local version segment
-        if self.local is not None:
-            parts.append("+{0}".format(self.local))
-
-        return "".join(parts)
-
-    @property
-    def epoch(self):
-        # type: () -> int
-        _epoch = self._version.epoch  # type: int
-        return _epoch
-
-    @property
-    def release(self):
-        # type: () -> Tuple[int, ...]
-        _release = self._version.release  # type: Tuple[int, ...]
-        return _release
-
-    @property
-    def pre(self):
-        # type: () -> Optional[Tuple[str, int]]
-        _pre = self._version.pre  # type: Optional[Tuple[str, int]]
-        return _pre
-
-    @property
-    def post(self):
-        # type: () -> Optional[Tuple[str, int]]
-        return self._version.post[1] if self._version.post else None
-
-    @property
-    def dev(self):
-        # type: () -> Optional[Tuple[str, int]]
-        return self._version.dev[1] if self._version.dev else None
-
-    @property
-    def local(self):
-        # type: () -> Optional[str]
-        if self._version.local:
-            return ".".join(str(x) for x in self._version.local)
-        else:
-            return None
-
-    @property
-    def public(self):
-        # type: () -> str
-        return str(self).split("+", 1)[0]
-
-    @property
-    def base_version(self):
-        # type: () -> str
-        parts = []
-
-        # Epoch
-        if self.epoch != 0:
-            parts.append("{0}!".format(self.epoch))
-
-        # Release segment
-        parts.append(".".join(str(x) for x in self.release))
-
-        return "".join(parts)
-
-    @property
-    def is_prerelease(self):
-        # type: () -> bool
-        return self.dev is not None or self.pre is not None
-
-    @property
-    def is_postrelease(self):
-        # type: () -> bool
-        return self.post is not None
-
-    @property
-    def is_devrelease(self):
-        # type: () -> bool
-        return self.dev is not None
-
-    @property
-    def major(self):
-        # type: () -> int
-        return self.release[0] if len(self.release) >= 1 else 0
-
-    @property
-    def minor(self):
-        # type: () -> int
-        return self.release[1] if len(self.release) >= 2 else 0
-
-    @property
-    def micro(self):
-        # type: () -> int
-        return self.release[2] if len(self.release) >= 3 else 0
-
-
-def _parse_letter_version(
-    letter,  # type: str
-    number,  # type: Union[str, bytes, SupportsInt]
-):
-    # type: (...) -> Optional[Tuple[str, int]]
-
-    if letter:
-        # We consider there to be an implicit 0 in a pre-release if there is
-        # not a numeral associated with it.
-        if number is None:
-            number = 0
-
-        # We normalize any letters to their lower case form
-        letter = letter.lower()
-
-        # We consider some words to be alternate spellings of other words and
-        # in those cases we want to normalize the spellings to our preferred
-        # spelling.
-        if letter == "alpha":
-            letter = "a"
-        elif letter == "beta":
-            letter = "b"
-        elif letter in ["c", "pre", "preview"]:
-            letter = "rc"
-        elif letter in ["rev", "r"]:
-            letter = "post"
-
-        return letter, int(number)
-    if not letter and number:
-        # We assume if we are given a number, but we are not given a letter
-        # then this is using the implicit post release syntax (e.g. 1.0-1)
-        letter = "post"
-
-        return letter, int(number)
-
-    return None
-
-
-_local_version_separators = re.compile(r"[\._-]")
-
-
-def _parse_local_version(local):
-    # type: (str) -> Optional[LocalType]
-    """
-    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
-    """
-    if local is not None:
-        return tuple(
-            part.lower() if not part.isdigit() else int(part)
-            for part in _local_version_separators.split(local)
-        )
-    return None
-
-
-def _cmpkey(
-    epoch,  # type: int
-    release,  # type: Tuple[int, ...]
-    pre,  # type: Optional[Tuple[str, int]]
-    post,  # type: Optional[Tuple[str, int]]
-    dev,  # type: Optional[Tuple[str, int]]
-    local,  # type: Optional[Tuple[SubLocalType]]
-):
-    # type: (...) -> CmpKey
-
-    # When we compare a release version, we want to compare it with all of the
-    # trailing zeros removed. So we'll use a reverse the list, drop all the now
-    # leading zeros until we come to something non zero, then take the rest
-    # re-reverse it back into the correct order and make it a tuple and use
-    # that for our sorting key.
-    _release = tuple(
-        reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
-    )
-
-    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
-    # We'll do this by abusing the pre segment, but we _only_ want to do this
-    # if there is not a pre or a post segment. If we have one of those then
-    # the normal sorting rules will handle this case correctly.
-    if pre is None and post is None and dev is not None:
-        _pre = NegativeInfinity  # type: PrePostDevType
-    # Versions without a pre-release (except as noted above) should sort after
-    # those with one.
-    elif pre is None:
-        _pre = Infinity
-    else:
-        _pre = pre
-
-    # Versions without a post segment should sort before those with one.
-    if post is None:
-        _post = NegativeInfinity  # type: PrePostDevType
-
-    else:
-        _post = post
-
-    # Versions without a development segment should sort after those with one.
-    if dev is None:
-        _dev = Infinity  # type: PrePostDevType
-
-    else:
-        _dev = dev
-
-    if local is None:
-        # Versions without a local segment should sort before those with one.
-        _local = NegativeInfinity  # type: LocalType
-    else:
-        # Versions with a local segment need that segment parsed to implement
-        # the sorting rules in PEP440.
-        # - Alpha numeric segments sort before numeric segments
-        # - Alpha numeric segments sort lexicographically
-        # - Numeric segments sort numerically
-        # - Shorter versions sort before longer versions when the prefixes
-        #   match exactly
-        _local = tuple(
-            (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local
-        )
-
-    return epoch, _release, _pre, _post, _dev, _local
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/DESCRIPTION.rst b/_setuptools_vendored/pyparsing-2.2.1.dist-info/DESCRIPTION.rst
deleted file mode 100644
index e1187231a3..0000000000
--- a/_setuptools_vendored/pyparsing-2.2.1.dist-info/DESCRIPTION.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-UNKNOWN
-
-
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/LICENSE.txt b/_setuptools_vendored/pyparsing-2.2.1.dist-info/LICENSE.txt
deleted file mode 100644
index bbc959e0d6..0000000000
--- a/_setuptools_vendored/pyparsing-2.2.1.dist-info/LICENSE.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/METADATA b/_setuptools_vendored/pyparsing-2.2.1.dist-info/METADATA
deleted file mode 100644
index a15c350e36..0000000000
--- a/_setuptools_vendored/pyparsing-2.2.1.dist-info/METADATA
+++ /dev/null
@@ -1,30 +0,0 @@
-Metadata-Version: 2.0
-Name: pyparsing
-Version: 2.2.1
-Summary: Python parsing module
-Home-page: https://github.com/pyparsing/pyparsing/
-Author: Paul McGuire
-Author-email: ptmcg@users.sourceforge.net
-License: MIT License
-Download-URL: https://pypi.org/project/pyparsing/
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: Intended Audience :: Information Technology
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*
-
-UNKNOWN
-
-
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/RECORD b/_setuptools_vendored/pyparsing-2.2.1.dist-info/RECORD
deleted file mode 100644
index 74cd1d53f6..0000000000
--- a/_setuptools_vendored/pyparsing-2.2.1.dist-info/RECORD
+++ /dev/null
@@ -1,8 +0,0 @@
-pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055
-pyparsing-2.2.1.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10
-pyparsing-2.2.1.dist-info/LICENSE.txt,sha256=081Pq74Spe1XdwrGkewNKSqa078kLIh7UWI-wVjdj8I,1041
-pyparsing-2.2.1.dist-info/METADATA,sha256=I0jhx9vpUYlQXjn4gVDnFFoAt3nNrxwR4iuqA_pknYs,1091
-pyparsing-2.2.1.dist-info/RECORD,,
-pyparsing-2.2.1.dist-info/WHEEL,sha256=kdsN-5OJAZIiHN-iO4Rhl82KyS0bDWf4uBwMbkNafr8,110
-pyparsing-2.2.1.dist-info/metadata.json,sha256=v1_77-dSdajUZSItSJg8Ov9M713STY3PzhyrRvs1ax4,1185
-pyparsing-2.2.1.dist-info/top_level.txt,sha256=eUOjGzJVhlQ3WS2rFAy2mN3LX_7FKTM5GSJ04jfnLmU,10
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/WHEEL b/_setuptools_vendored/pyparsing-2.2.1.dist-info/WHEEL
deleted file mode 100644
index 7332a419cd..0000000000
--- a/_setuptools_vendored/pyparsing-2.2.1.dist-info/WHEEL
+++ /dev/null
@@ -1,6 +0,0 @@
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.30.0)
-Root-Is-Purelib: true
-Tag: py2-none-any
-Tag: py3-none-any
-
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/metadata.json b/_setuptools_vendored/pyparsing-2.2.1.dist-info/metadata.json
deleted file mode 100644
index b760b766b0..0000000000
--- a/_setuptools_vendored/pyparsing-2.2.1.dist-info/metadata.json
+++ /dev/null
@@ -1 +0,0 @@
-{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7"], "download_url": "https://pypi.org/project/pyparsing/", "extensions": {"python.details": {"contacts": [{"email": "ptmcg@users.sourceforge.net", "name": "Paul McGuire", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "project_urls": {"Home": "https://github.com/pyparsing/pyparsing/"}}}, "generator": "bdist_wheel (0.30.0)", "license": "MIT License", "metadata_version": "2.0", "name": "pyparsing", "requires_python": ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*", "summary": "Python parsing module", "version": "2.2.1"}
\ No newline at end of file
diff --git a/_setuptools_vendored/pyparsing-2.2.1.dist-info/top_level.txt b/_setuptools_vendored/pyparsing-2.2.1.dist-info/top_level.txt
deleted file mode 100644
index 210dfec50b..0000000000
--- a/_setuptools_vendored/pyparsing-2.2.1.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-pyparsing
diff --git a/_setuptools_vendored/pyparsing.py b/_setuptools_vendored/pyparsing.py
deleted file mode 100644
index cf75e1e5fc..0000000000
--- a/_setuptools_vendored/pyparsing.py
+++ /dev/null
@@ -1,5742 +0,0 @@
-# module pyparsing.py
-#
-# Copyright (c) 2003-2018  Paul T. McGuire
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__doc__ = \
-"""
-pyparsing module - Classes and methods to define and execute parsing grammars
-=============================================================================
-
-The pyparsing module is an alternative approach to creating and executing simple grammars,
-vs. the traditional lex/yacc approach, or the use of regular expressions.  With pyparsing, you
-don't need to learn a new syntax for defining grammars or matching expressions - the parsing module
-provides a library of classes that you use to construct the grammar directly in Python.
-
-Here is a program to parse "Hello, World!" (or any greeting of the form 
-C{", !"}), built up using L{Word}, L{Literal}, and L{And} elements 
-(L{'+'} operator gives L{And} expressions, strings are auto-converted to
-L{Literal} expressions)::
-
-    from pyparsing import Word, alphas
-
-    # define grammar of a greeting
-    greet = Word(alphas) + "," + Word(alphas) + "!"
-
-    hello = "Hello, World!"
-    print (hello, "->", greet.parseString(hello))
-
-The program outputs the following::
-
-    Hello, World! -> ['Hello', ',', 'World', '!']
-
-The Python representation of the grammar is quite readable, owing to the self-explanatory
-class names, and the use of '+', '|' and '^' operators.
-
-The L{ParseResults} object returned from L{ParserElement.parseString} can be accessed as a nested list, a dictionary, or an
-object with named attributes.
-
-The pyparsing module handles some of the problems that are typically vexing when writing text parsers:
- - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello  ,  World  !", etc.)
- - quoted strings
- - embedded comments
-
-
-Getting Started -
------------------
-Visit the classes L{ParserElement} and L{ParseResults} to see the base classes that most other pyparsing
-classes inherit from. Use the docstrings for examples of how to:
- - construct literal match expressions from L{Literal} and L{CaselessLiteral} classes
- - construct character word-group expressions using the L{Word} class
- - see how to create repetitive expressions using L{ZeroOrMore} and L{OneOrMore} classes
- - use L{'+'}, L{'|'}, L{'^'}, and L{'&'} operators to combine simple expressions into more complex ones
- - associate names with your parsed results using L{ParserElement.setResultsName}
- - find some helpful expression short-cuts like L{delimitedList} and L{oneOf}
- - find more useful common expressions in the L{pyparsing_common} namespace class
-"""
-
-__version__ = "2.2.1"
-__versionTime__ = "18 Sep 2018 00:49 UTC"
-__author__ = "Paul McGuire "
-
-import string
-from weakref import ref as wkref
-import copy
-import sys
-import warnings
-import re
-import sre_constants
-import collections
-import pprint
-import traceback
-import types
-from datetime import datetime
-
-try:
-    from _thread import RLock
-except ImportError:
-    from threading import RLock
-
-try:
-    # Python 3
-    from collections.abc import Iterable
-    from collections.abc import MutableMapping
-except ImportError:
-    # Python 2.7
-    from collections import Iterable
-    from collections import MutableMapping
-
-try:
-    from collections import OrderedDict as _OrderedDict
-except ImportError:
-    try:
-        from ordereddict import OrderedDict as _OrderedDict
-    except ImportError:
-        _OrderedDict = None
-
-#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) )
-
-__all__ = [
-'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty',
-'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal',
-'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or',
-'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException',
-'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException',
-'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 
-'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore',
-'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col',
-'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString',
-'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums',
-'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno',
-'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral',
-'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables',
-'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', 
-'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd',
-'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute',
-'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass',
-'CloseMatch', 'tokenMap', 'pyparsing_common',
-]
-
-system_version = tuple(sys.version_info)[:3]
-PY_3 = system_version[0] == 3
-if PY_3:
-    _MAX_INT = sys.maxsize
-    basestring = str
-    unichr = chr
-    _ustr = str
-
-    # build list of single arg builtins, that can be used as parse actions
-    singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max]
-
-else:
-    _MAX_INT = sys.maxint
-    range = xrange
-
-    def _ustr(obj):
-        """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries
-           str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It
-           then < returns the unicode object | encodes it with the default encoding | ... >.
-        """
-        if isinstance(obj,unicode):
-            return obj
-
-        try:
-            # If this works, then _ustr(obj) has the same behaviour as str(obj), so
-            # it won't break any existing code.
-            return str(obj)
-
-        except UnicodeEncodeError:
-            # Else encode it
-            ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace')
-            xmlcharref = Regex(r'&#\d+;')
-            xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:])
-            return xmlcharref.transformString(ret)
-
-    # build list of single arg builtins, tolerant of Python version, that can be used as parse actions
-    singleArgBuiltins = []
-    import __builtin__
-    for fname in "sum len sorted reversed list tuple set any all min max".split():
-        try:
-            singleArgBuiltins.append(getattr(__builtin__,fname))
-        except AttributeError:
-            continue
-            
-_generatorType = type((y for y in range(1)))
- 
-def _xml_escape(data):
-    """Escape &, <, >, ", ', etc. in a string of data."""
-
-    # ampersand must be replaced first
-    from_symbols = '&><"\''
-    to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split())
-    for from_,to_ in zip(from_symbols, to_symbols):
-        data = data.replace(from_, to_)
-    return data
-
-class _Constants(object):
-    pass
-
-alphas     = string.ascii_uppercase + string.ascii_lowercase
-nums       = "0123456789"
-hexnums    = nums + "ABCDEFabcdef"
-alphanums  = alphas + nums
-_bslash    = chr(92)
-printables = "".join(c for c in string.printable if c not in string.whitespace)
-
-class ParseBaseException(Exception):
-    """base exception class for all parsing runtime exceptions"""
-    # Performance tuning: we construct a *lot* of these, so keep this
-    # constructor as small and fast as possible
-    def __init__( self, pstr, loc=0, msg=None, elem=None ):
-        self.loc = loc
-        if msg is None:
-            self.msg = pstr
-            self.pstr = ""
-        else:
-            self.msg = msg
-            self.pstr = pstr
-        self.parserElement = elem
-        self.args = (pstr, loc, msg)
-
-    @classmethod
-    def _from_exception(cls, pe):
-        """
-        internal factory method to simplify creating one type of ParseException 
-        from another - avoids having __init__ signature conflicts among subclasses
-        """
-        return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement)
-
-    def __getattr__( self, aname ):
-        """supported attributes by name are:
-            - lineno - returns the line number of the exception text
-            - col - returns the column number of the exception text
-            - line - returns the line containing the exception text
-        """
-        if( aname == "lineno" ):
-            return lineno( self.loc, self.pstr )
-        elif( aname in ("col", "column") ):
-            return col( self.loc, self.pstr )
-        elif( aname == "line" ):
-            return line( self.loc, self.pstr )
-        else:
-            raise AttributeError(aname)
-
-    def __str__( self ):
-        return "%s (at char %d), (line:%d, col:%d)" % \
-                ( self.msg, self.loc, self.lineno, self.column )
-    def __repr__( self ):
-        return _ustr(self)
-    def markInputline( self, markerString = ">!<" ):
-        """Extracts the exception line from the input string, and marks
-           the location of the exception with a special symbol.
-        """
-        line_str = self.line
-        line_column = self.column - 1
-        if markerString:
-            line_str = "".join((line_str[:line_column],
-                                markerString, line_str[line_column:]))
-        return line_str.strip()
-    def __dir__(self):
-        return "lineno col line".split() + dir(type(self))
-
-class ParseException(ParseBaseException):
-    """
-    Exception thrown when parse expressions don't match class;
-    supported attributes by name are:
-     - lineno - returns the line number of the exception text
-     - col - returns the column number of the exception text
-     - line - returns the line containing the exception text
-        
-    Example::
-        try:
-            Word(nums).setName("integer").parseString("ABC")
-        except ParseException as pe:
-            print(pe)
-            print("column: {}".format(pe.col))
-            
-    prints::
-       Expected integer (at char 0), (line:1, col:1)
-        column: 1
-    """
-    pass
-
-class ParseFatalException(ParseBaseException):
-    """user-throwable exception thrown when inconsistent parse content
-       is found; stops all parsing immediately"""
-    pass
-
-class ParseSyntaxException(ParseFatalException):
-    """just like L{ParseFatalException}, but thrown internally when an
-       L{ErrorStop} ('-' operator) indicates that parsing is to stop 
-       immediately because an unbacktrackable syntax error has been found"""
-    pass
-
-#~ class ReparseException(ParseBaseException):
-    #~ """Experimental class - parse actions can raise this exception to cause
-       #~ pyparsing to reparse the input string:
-        #~ - with a modified input string, and/or
-        #~ - with a modified start location
-       #~ Set the values of the ReparseException in the constructor, and raise the
-       #~ exception in a parse action to cause pyparsing to use the new string/location.
-       #~ Setting the values as None causes no change to be made.
-       #~ """
-    #~ def __init_( self, newstring, restartLoc ):
-        #~ self.newParseText = newstring
-        #~ self.reparseLoc = restartLoc
-
-class RecursiveGrammarException(Exception):
-    """exception thrown by L{ParserElement.validate} if the grammar could be improperly recursive"""
-    def __init__( self, parseElementList ):
-        self.parseElementTrace = parseElementList
-
-    def __str__( self ):
-        return "RecursiveGrammarException: %s" % self.parseElementTrace
-
-class _ParseResultsWithOffset(object):
-    def __init__(self,p1,p2):
-        self.tup = (p1,p2)
-    def __getitem__(self,i):
-        return self.tup[i]
-    def __repr__(self):
-        return repr(self.tup[0])
-    def setOffset(self,i):
-        self.tup = (self.tup[0],i)
-
-class ParseResults(object):
-    """
-    Structured parse results, to provide multiple means of access to the parsed data:
-       - as a list (C{len(results)})
-       - by list index (C{results[0], results[1]}, etc.)
-       - by attribute (C{results.} - see L{ParserElement.setResultsName})
-
-    Example::
-        integer = Word(nums)
-        date_str = (integer.setResultsName("year") + '/' 
-                        + integer.setResultsName("month") + '/' 
-                        + integer.setResultsName("day"))
-        # equivalent form:
-        # date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-
-        # parseString returns a ParseResults object
-        result = date_str.parseString("1999/12/31")
-
-        def test(s, fn=repr):
-            print("%s -> %s" % (s, fn(eval(s))))
-        test("list(result)")
-        test("result[0]")
-        test("result['month']")
-        test("result.day")
-        test("'month' in result")
-        test("'minutes' in result")
-        test("result.dump()", str)
-    prints::
-        list(result) -> ['1999', '/', '12', '/', '31']
-        result[0] -> '1999'
-        result['month'] -> '12'
-        result.day -> '31'
-        'month' in result -> True
-        'minutes' in result -> False
-        result.dump() -> ['1999', '/', '12', '/', '31']
-        - day: 31
-        - month: 12
-        - year: 1999
-    """
-    def __new__(cls, toklist=None, name=None, asList=True, modal=True ):
-        if isinstance(toklist, cls):
-            return toklist
-        retobj = object.__new__(cls)
-        retobj.__doinit = True
-        return retobj
-
-    # Performance tuning: we construct a *lot* of these, so keep this
-    # constructor as small and fast as possible
-    def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ):
-        if self.__doinit:
-            self.__doinit = False
-            self.__name = None
-            self.__parent = None
-            self.__accumNames = {}
-            self.__asList = asList
-            self.__modal = modal
-            if toklist is None:
-                toklist = []
-            if isinstance(toklist, list):
-                self.__toklist = toklist[:]
-            elif isinstance(toklist, _generatorType):
-                self.__toklist = list(toklist)
-            else:
-                self.__toklist = [toklist]
-            self.__tokdict = dict()
-
-        if name is not None and name:
-            if not modal:
-                self.__accumNames[name] = 0
-            if isinstance(name,int):
-                name = _ustr(name) # will always return a str, but use _ustr for consistency
-            self.__name = name
-            if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])):
-                if isinstance(toklist,basestring):
-                    toklist = [ toklist ]
-                if asList:
-                    if isinstance(toklist,ParseResults):
-                        self[name] = _ParseResultsWithOffset(toklist.copy(),0)
-                    else:
-                        self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0)
-                    self[name].__name = name
-                else:
-                    try:
-                        self[name] = toklist[0]
-                    except (KeyError,TypeError,IndexError):
-                        self[name] = toklist
-
-    def __getitem__( self, i ):
-        if isinstance( i, (int,slice) ):
-            return self.__toklist[i]
-        else:
-            if i not in self.__accumNames:
-                return self.__tokdict[i][-1][0]
-            else:
-                return ParseResults([ v[0] for v in self.__tokdict[i] ])
-
-    def __setitem__( self, k, v, isinstance=isinstance ):
-        if isinstance(v,_ParseResultsWithOffset):
-            self.__tokdict[k] = self.__tokdict.get(k,list()) + [v]
-            sub = v[0]
-        elif isinstance(k,(int,slice)):
-            self.__toklist[k] = v
-            sub = v
-        else:
-            self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)]
-            sub = v
-        if isinstance(sub,ParseResults):
-            sub.__parent = wkref(self)
-
-    def __delitem__( self, i ):
-        if isinstance(i,(int,slice)):
-            mylen = len( self.__toklist )
-            del self.__toklist[i]
-
-            # convert int to slice
-            if isinstance(i, int):
-                if i < 0:
-                    i += mylen
-                i = slice(i, i+1)
-            # get removed indices
-            removed = list(range(*i.indices(mylen)))
-            removed.reverse()
-            # fixup indices in token dictionary
-            for name,occurrences in self.__tokdict.items():
-                for j in removed:
-                    for k, (value, position) in enumerate(occurrences):
-                        occurrences[k] = _ParseResultsWithOffset(value, position - (position > j))
-        else:
-            del self.__tokdict[i]
-
-    def __contains__( self, k ):
-        return k in self.__tokdict
-
-    def __len__( self ): return len( self.__toklist )
-    def __bool__(self): return ( not not self.__toklist )
-    __nonzero__ = __bool__
-    def __iter__( self ): return iter( self.__toklist )
-    def __reversed__( self ): return iter( self.__toklist[::-1] )
-    def _iterkeys( self ):
-        if hasattr(self.__tokdict, "iterkeys"):
-            return self.__tokdict.iterkeys()
-        else:
-            return iter(self.__tokdict)
-
-    def _itervalues( self ):
-        return (self[k] for k in self._iterkeys())
-            
-    def _iteritems( self ):
-        return ((k, self[k]) for k in self._iterkeys())
-
-    if PY_3:
-        keys = _iterkeys       
-        """Returns an iterator of all named result keys (Python 3.x only)."""
-
-        values = _itervalues
-        """Returns an iterator of all named result values (Python 3.x only)."""
-
-        items = _iteritems
-        """Returns an iterator of all named result key-value tuples (Python 3.x only)."""
-
-    else:
-        iterkeys = _iterkeys
-        """Returns an iterator of all named result keys (Python 2.x only)."""
-
-        itervalues = _itervalues
-        """Returns an iterator of all named result values (Python 2.x only)."""
-
-        iteritems = _iteritems
-        """Returns an iterator of all named result key-value tuples (Python 2.x only)."""
-
-        def keys( self ):
-            """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.iterkeys())
-
-        def values( self ):
-            """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.itervalues())
-                
-        def items( self ):
-            """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.iteritems())
-
-    def haskeys( self ):
-        """Since keys() returns an iterator, this method is helpful in bypassing
-           code that looks for the existence of any defined results names."""
-        return bool(self.__tokdict)
-        
-    def pop( self, *args, **kwargs):
-        """
-        Removes and returns item at specified index (default=C{last}).
-        Supports both C{list} and C{dict} semantics for C{pop()}. If passed no
-        argument or an integer argument, it will use C{list} semantics
-        and pop tokens from the list of parsed tokens. If passed a 
-        non-integer argument (most likely a string), it will use C{dict}
-        semantics and pop the corresponding value from any defined 
-        results names. A second default return value argument is 
-        supported, just as in C{dict.pop()}.
-
-        Example::
-            def remove_first(tokens):
-                tokens.pop(0)
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-            print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321']
-
-            label = Word(alphas)
-            patt = label("LABEL") + OneOrMore(Word(nums))
-            print(patt.parseString("AAB 123 321").dump())
-
-            # Use pop() in a parse action to remove named result (note that corresponding value is not
-            # removed from list form of results)
-            def remove_LABEL(tokens):
-                tokens.pop("LABEL")
-                return tokens
-            patt.addParseAction(remove_LABEL)
-            print(patt.parseString("AAB 123 321").dump())
-        prints::
-            ['AAB', '123', '321']
-            - LABEL: AAB
-
-            ['AAB', '123', '321']
-        """
-        if not args:
-            args = [-1]
-        for k,v in kwargs.items():
-            if k == 'default':
-                args = (args[0], v)
-            else:
-                raise TypeError("pop() got an unexpected keyword argument '%s'" % k)
-        if (isinstance(args[0], int) or 
-                        len(args) == 1 or 
-                        args[0] in self):
-            index = args[0]
-            ret = self[index]
-            del self[index]
-            return ret
-        else:
-            defaultvalue = args[1]
-            return defaultvalue
-
-    def get(self, key, defaultValue=None):
-        """
-        Returns named result matching the given key, or if there is no
-        such name, then returns the given C{defaultValue} or C{None} if no
-        C{defaultValue} is specified.
-
-        Similar to C{dict.get()}.
-        
-        Example::
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
-
-            result = date_str.parseString("1999/12/31")
-            print(result.get("year")) # -> '1999'
-            print(result.get("hour", "not specified")) # -> 'not specified'
-            print(result.get("hour")) # -> None
-        """
-        if key in self:
-            return self[key]
-        else:
-            return defaultValue
-
-    def insert( self, index, insStr ):
-        """
-        Inserts new element at location index in the list of parsed tokens.
-        
-        Similar to C{list.insert()}.
-
-        Example::
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-
-            # use a parse action to insert the parse location in the front of the parsed results
-            def insert_locn(locn, tokens):
-                tokens.insert(0, locn)
-            print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321']
-        """
-        self.__toklist.insert(index, insStr)
-        # fixup indices in token dictionary
-        for name,occurrences in self.__tokdict.items():
-            for k, (value, position) in enumerate(occurrences):
-                occurrences[k] = _ParseResultsWithOffset(value, position + (position > index))
-
-    def append( self, item ):
-        """
-        Add single element to end of ParseResults list of elements.
-
-        Example::
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-            
-            # use a parse action to compute the sum of the parsed integers, and add it to the end
-            def append_sum(tokens):
-                tokens.append(sum(map(int, tokens)))
-            print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444]
-        """
-        self.__toklist.append(item)
-
-    def extend( self, itemseq ):
-        """
-        Add sequence of elements to end of ParseResults list of elements.
-
-        Example::
-            patt = OneOrMore(Word(alphas))
-            
-            # use a parse action to append the reverse of the matched strings, to make a palindrome
-            def make_palindrome(tokens):
-                tokens.extend(reversed([t[::-1] for t in tokens]))
-                return ''.join(tokens)
-            print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl'
-        """
-        if isinstance(itemseq, ParseResults):
-            self += itemseq
-        else:
-            self.__toklist.extend(itemseq)
-
-    def clear( self ):
-        """
-        Clear all elements and results names.
-        """
-        del self.__toklist[:]
-        self.__tokdict.clear()
-
-    def __getattr__( self, name ):
-        try:
-            return self[name]
-        except KeyError:
-            return ""
-            
-        if name in self.__tokdict:
-            if name not in self.__accumNames:
-                return self.__tokdict[name][-1][0]
-            else:
-                return ParseResults([ v[0] for v in self.__tokdict[name] ])
-        else:
-            return ""
-
-    def __add__( self, other ):
-        ret = self.copy()
-        ret += other
-        return ret
-
-    def __iadd__( self, other ):
-        if other.__tokdict:
-            offset = len(self.__toklist)
-            addoffset = lambda a: offset if a<0 else a+offset
-            otheritems = other.__tokdict.items()
-            otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) )
-                                for (k,vlist) in otheritems for v in vlist]
-            for k,v in otherdictitems:
-                self[k] = v
-                if isinstance(v[0],ParseResults):
-                    v[0].__parent = wkref(self)
-            
-        self.__toklist += other.__toklist
-        self.__accumNames.update( other.__accumNames )
-        return self
-
-    def __radd__(self, other):
-        if isinstance(other,int) and other == 0:
-            # useful for merging many ParseResults using sum() builtin
-            return self.copy()
-        else:
-            # this may raise a TypeError - so be it
-            return other + self
-        
-    def __repr__( self ):
-        return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) )
-
-    def __str__( self ):
-        return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']'
-
-    def _asStringList( self, sep='' ):
-        out = []
-        for item in self.__toklist:
-            if out and sep:
-                out.append(sep)
-            if isinstance( item, ParseResults ):
-                out += item._asStringList()
-            else:
-                out.append( _ustr(item) )
-        return out
-
-    def asList( self ):
-        """
-        Returns the parse results as a nested list of matching tokens, all converted to strings.
-
-        Example::
-            patt = OneOrMore(Word(alphas))
-            result = patt.parseString("sldkj lsdkj sldkj")
-            # even though the result prints in string-like form, it is actually a pyparsing ParseResults
-            print(type(result), result) # ->  ['sldkj', 'lsdkj', 'sldkj']
-            
-            # Use asList() to create an actual list
-            result_list = result.asList()
-            print(type(result_list), result_list) # ->  ['sldkj', 'lsdkj', 'sldkj']
-        """
-        return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist]
-
-    def asDict( self ):
-        """
-        Returns the named parse results as a nested dictionary.
-
-        Example::
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-            
-            result = date_str.parseString('12/31/1999')
-            print(type(result), repr(result)) # ->  (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
-            
-            result_dict = result.asDict()
-            print(type(result_dict), repr(result_dict)) # ->  {'day': '1999', 'year': '12', 'month': '31'}
-
-            # even though a ParseResults supports dict-like access, sometime you just need to have a dict
-            import json
-            print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable
-            print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"}
-        """
-        if PY_3:
-            item_fn = self.items
-        else:
-            item_fn = self.iteritems
-            
-        def toItem(obj):
-            if isinstance(obj, ParseResults):
-                if obj.haskeys():
-                    return obj.asDict()
-                else:
-                    return [toItem(v) for v in obj]
-            else:
-                return obj
-                
-        return dict((k,toItem(v)) for k,v in item_fn())
-
-    def copy( self ):
-        """
-        Returns a new copy of a C{ParseResults} object.
-        """
-        ret = ParseResults( self.__toklist )
-        ret.__tokdict = self.__tokdict.copy()
-        ret.__parent = self.__parent
-        ret.__accumNames.update( self.__accumNames )
-        ret.__name = self.__name
-        return ret
-
-    def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ):
-        """
-        (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.
-        """
-        nl = "\n"
-        out = []
-        namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items()
-                                                            for v in vlist)
-        nextLevelIndent = indent + "  "
-
-        # collapse out indents if formatting is not desired
-        if not formatted:
-            indent = ""
-            nextLevelIndent = ""
-            nl = ""
-
-        selfTag = None
-        if doctag is not None:
-            selfTag = doctag
-        else:
-            if self.__name:
-                selfTag = self.__name
-
-        if not selfTag:
-            if namedItemsOnly:
-                return ""
-            else:
-                selfTag = "ITEM"
-
-        out += [ nl, indent, "<", selfTag, ">" ]
-
-        for i,res in enumerate(self.__toklist):
-            if isinstance(res,ParseResults):
-                if i in namedItems:
-                    out += [ res.asXML(namedItems[i],
-                                        namedItemsOnly and doctag is None,
-                                        nextLevelIndent,
-                                        formatted)]
-                else:
-                    out += [ res.asXML(None,
-                                        namedItemsOnly and doctag is None,
-                                        nextLevelIndent,
-                                        formatted)]
-            else:
-                # individual token, see if there is a name for it
-                resTag = None
-                if i in namedItems:
-                    resTag = namedItems[i]
-                if not resTag:
-                    if namedItemsOnly:
-                        continue
-                    else:
-                        resTag = "ITEM"
-                xmlBodyText = _xml_escape(_ustr(res))
-                out += [ nl, nextLevelIndent, "<", resTag, ">",
-                                                xmlBodyText,
-                                                "" ]
-
-        out += [ nl, indent, "" ]
-        return "".join(out)
-
-    def __lookup(self,sub):
-        for k,vlist in self.__tokdict.items():
-            for v,loc in vlist:
-                if sub is v:
-                    return k
-        return None
-
-    def getName(self):
-        r"""
-        Returns the results name for this token expression. Useful when several 
-        different expressions might match at a particular location.
-
-        Example::
-            integer = Word(nums)
-            ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d")
-            house_number_expr = Suppress('#') + Word(nums, alphanums)
-            user_data = (Group(house_number_expr)("house_number") 
-                        | Group(ssn_expr)("ssn")
-                        | Group(integer)("age"))
-            user_info = OneOrMore(user_data)
-            
-            result = user_info.parseString("22 111-22-3333 #221B")
-            for item in result:
-                print(item.getName(), ':', item[0])
-        prints::
-            age : 22
-            ssn : 111-22-3333
-            house_number : 221B
-        """
-        if self.__name:
-            return self.__name
-        elif self.__parent:
-            par = self.__parent()
-            if par:
-                return par.__lookup(self)
-            else:
-                return None
-        elif (len(self) == 1 and
-               len(self.__tokdict) == 1 and
-               next(iter(self.__tokdict.values()))[0][1] in (0,-1)):
-            return next(iter(self.__tokdict.keys()))
-        else:
-            return None
-
-    def dump(self, indent='', depth=0, full=True):
-        """
-        Diagnostic method for listing out the contents of a C{ParseResults}.
-        Accepts an optional C{indent} argument so that this string can be embedded
-        in a nested display of other data.
-
-        Example::
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-            
-            result = date_str.parseString('12/31/1999')
-            print(result.dump())
-        prints::
-            ['12', '/', '31', '/', '1999']
-            - day: 1999
-            - month: 31
-            - year: 12
-        """
-        out = []
-        NL = '\n'
-        out.append( indent+_ustr(self.asList()) )
-        if full:
-            if self.haskeys():
-                items = sorted((str(k), v) for k,v in self.items())
-                for k,v in items:
-                    if out:
-                        out.append(NL)
-                    out.append( "%s%s- %s: " % (indent,('  '*depth), k) )
-                    if isinstance(v,ParseResults):
-                        if v:
-                            out.append( v.dump(indent,depth+1) )
-                        else:
-                            out.append(_ustr(v))
-                    else:
-                        out.append(repr(v))
-            elif any(isinstance(vv,ParseResults) for vv in self):
-                v = self
-                for i,vv in enumerate(v):
-                    if isinstance(vv,ParseResults):
-                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),vv.dump(indent,depth+1) ))
-                    else:
-                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),_ustr(vv)))
-            
-        return "".join(out)
-
-    def pprint(self, *args, **kwargs):
-        """
-        Pretty-printer for parsed results as a list, using the C{pprint} module.
-        Accepts additional positional or keyword args as defined for the 
-        C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint})
-
-        Example::
-            ident = Word(alphas, alphanums)
-            num = Word(nums)
-            func = Forward()
-            term = ident | num | Group('(' + func + ')')
-            func <<= ident + Group(Optional(delimitedList(term)))
-            result = func.parseString("fna a,b,(fnb c,d,200),100")
-            result.pprint(width=40)
-        prints::
-            ['fna',
-             ['a',
-              'b',
-              ['(', 'fnb', ['c', 'd', '200'], ')'],
-              '100']]
-        """
-        pprint.pprint(self.asList(), *args, **kwargs)
-
-    # add support for pickle protocol
-    def __getstate__(self):
-        return ( self.__toklist,
-                 ( self.__tokdict.copy(),
-                   self.__parent is not None and self.__parent() or None,
-                   self.__accumNames,
-                   self.__name ) )
-
-    def __setstate__(self,state):
-        self.__toklist = state[0]
-        (self.__tokdict,
-         par,
-         inAccumNames,
-         self.__name) = state[1]
-        self.__accumNames = {}
-        self.__accumNames.update(inAccumNames)
-        if par is not None:
-            self.__parent = wkref(par)
-        else:
-            self.__parent = None
-
-    def __getnewargs__(self):
-        return self.__toklist, self.__name, self.__asList, self.__modal
-
-    def __dir__(self):
-        return (dir(type(self)) + list(self.keys()))
-
-MutableMapping.register(ParseResults)
-
-def col (loc,strg):
-    """Returns current column within a string, counting newlines as line separators.
-   The first column is number 1.
-
-   Note: the default parsing behavior is to expand tabs in the input string
-   before starting the parsing process.  See L{I{ParserElement.parseString}} for more information
-   on parsing strings containing C{}s, and suggested methods to maintain a
-   consistent view of the parsed string, the parse location, and line and column
-   positions within the parsed string.
-   """
-    s = strg
-    return 1 if 0} for more information
-   on parsing strings containing C{}s, and suggested methods to maintain a
-   consistent view of the parsed string, the parse location, and line and column
-   positions within the parsed string.
-   """
-    return strg.count("\n",0,loc) + 1
-
-def line( loc, strg ):
-    """Returns the line of text containing loc within a string, counting newlines as line separators.
-       """
-    lastCR = strg.rfind("\n", 0, loc)
-    nextCR = strg.find("\n", loc)
-    if nextCR >= 0:
-        return strg[lastCR+1:nextCR]
-    else:
-        return strg[lastCR+1:]
-
-def _defaultStartDebugAction( instring, loc, expr ):
-    print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )))
-
-def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ):
-    print ("Matched " + _ustr(expr) + " -> " + str(toks.asList()))
-
-def _defaultExceptionDebugAction( instring, loc, expr, exc ):
-    print ("Exception raised:" + _ustr(exc))
-
-def nullDebugAction(*args):
-    """'Do-nothing' debug action, to suppress debugging output during parsing."""
-    pass
-
-# Only works on Python 3.x - nonlocal is toxic to Python 2 installs
-#~ 'decorator to trim function calls to match the arity of the target'
-#~ def _trim_arity(func, maxargs=3):
-    #~ if func in singleArgBuiltins:
-        #~ return lambda s,l,t: func(t)
-    #~ limit = 0
-    #~ foundArity = False
-    #~ def wrapper(*args):
-        #~ nonlocal limit,foundArity
-        #~ while 1:
-            #~ try:
-                #~ ret = func(*args[limit:])
-                #~ foundArity = True
-                #~ return ret
-            #~ except TypeError:
-                #~ if limit == maxargs or foundArity:
-                    #~ raise
-                #~ limit += 1
-                #~ continue
-    #~ return wrapper
-
-# this version is Python 2.x-3.x cross-compatible
-'decorator to trim function calls to match the arity of the target'
-def _trim_arity(func, maxargs=2):
-    if func in singleArgBuiltins:
-        return lambda s,l,t: func(t)
-    limit = [0]
-    foundArity = [False]
-    
-    # traceback return data structure changed in Py3.5 - normalize back to plain tuples
-    if system_version[:2] >= (3,5):
-        def extract_stack(limit=0):
-            # special handling for Python 3.5.0 - extra deep call stack by 1
-            offset = -3 if system_version == (3,5,0) else -2
-            frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset]
-            return [frame_summary[:2]]
-        def extract_tb(tb, limit=0):
-            frames = traceback.extract_tb(tb, limit=limit)
-            frame_summary = frames[-1]
-            return [frame_summary[:2]]
-    else:
-        extract_stack = traceback.extract_stack
-        extract_tb = traceback.extract_tb
-    
-    # synthesize what would be returned by traceback.extract_stack at the call to 
-    # user's parse action 'func', so that we don't incur call penalty at parse time
-    
-    LINE_DIFF = 6
-    # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND 
-    # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!!
-    this_line = extract_stack(limit=2)[-1]
-    pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF)
-
-    def wrapper(*args):
-        while 1:
-            try:
-                ret = func(*args[limit[0]:])
-                foundArity[0] = True
-                return ret
-            except TypeError:
-                # re-raise TypeErrors if they did not come from our arity testing
-                if foundArity[0]:
-                    raise
-                else:
-                    try:
-                        tb = sys.exc_info()[-1]
-                        if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth:
-                            raise
-                    finally:
-                        del tb
-
-                if limit[0] <= maxargs:
-                    limit[0] += 1
-                    continue
-                raise
-
-    # copy func name to wrapper for sensible debug output
-    func_name = ""
-    try:
-        func_name = getattr(func, '__name__', 
-                            getattr(func, '__class__').__name__)
-    except Exception:
-        func_name = str(func)
-    wrapper.__name__ = func_name
-
-    return wrapper
-
-class ParserElement(object):
-    """Abstract base level parser element class."""
-    DEFAULT_WHITE_CHARS = " \n\t\r"
-    verbose_stacktrace = False
-
-    @staticmethod
-    def setDefaultWhitespaceChars( chars ):
-        r"""
-        Overrides the default whitespace chars
-
-        Example::
-            # default whitespace chars are space,  and newline
-            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def', 'ghi', 'jkl']
-            
-            # change to just treat newline as significant
-            ParserElement.setDefaultWhitespaceChars(" \t")
-            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def']
-        """
-        ParserElement.DEFAULT_WHITE_CHARS = chars
-
-    @staticmethod
-    def inlineLiteralsUsing(cls):
-        """
-        Set class to be used for inclusion of string literals into a parser.
-        
-        Example::
-            # default literal class used is Literal
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
-
-
-            # change to Suppress
-            ParserElement.inlineLiteralsUsing(Suppress)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '12', '31']
-        """
-        ParserElement._literalStringClass = cls
-
-    def __init__( self, savelist=False ):
-        self.parseAction = list()
-        self.failAction = None
-        #~ self.name = ""  # don't define self.name, let subclasses try/except upcall
-        self.strRepr = None
-        self.resultsName = None
-        self.saveAsList = savelist
-        self.skipWhitespace = True
-        self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
-        self.copyDefaultWhiteChars = True
-        self.mayReturnEmpty = False # used when checking for left-recursion
-        self.keepTabs = False
-        self.ignoreExprs = list()
-        self.debug = False
-        self.streamlined = False
-        self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index
-        self.errmsg = ""
-        self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all)
-        self.debugActions = ( None, None, None ) #custom debug actions
-        self.re = None
-        self.callPreparse = True # used to avoid redundant calls to preParse
-        self.callDuringTry = False
-
-    def copy( self ):
-        """
-        Make a copy of this C{ParserElement}.  Useful for defining different parse actions
-        for the same parsing pattern, using copies of the original parse element.
-        
-        Example::
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K")
-            integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
-            
-            print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M"))
-        prints::
-            [5120, 100, 655360, 268435456]
-        Equivalent form of C{expr.copy()} is just C{expr()}::
-            integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
-        """
-        cpy = copy.copy( self )
-        cpy.parseAction = self.parseAction[:]
-        cpy.ignoreExprs = self.ignoreExprs[:]
-        if self.copyDefaultWhiteChars:
-            cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
-        return cpy
-
-    def setName( self, name ):
-        """
-        Define name for this expression, makes debugging and exception messages clearer.
-        
-        Example::
-            Word(nums).parseString("ABC")  # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1)
-            Word(nums).setName("integer").parseString("ABC")  # -> Exception: Expected integer (at char 0), (line:1, col:1)
-        """
-        self.name = name
-        self.errmsg = "Expected " + self.name
-        if hasattr(self,"exception"):
-            self.exception.msg = self.errmsg
-        return self
-
-    def setResultsName( self, name, listAllMatches=False ):
-        """
-        Define name for referencing matching tokens as a nested attribute
-        of the returned parse results.
-        NOTE: this returns a *copy* of the original C{ParserElement} object;
-        this is so that the client can define a basic element, such as an
-        integer, and reference it in multiple places with different names.
-
-        You can also set results names using the abbreviated syntax,
-        C{expr("name")} in place of C{expr.setResultsName("name")} - 
-        see L{I{__call__}<__call__>}.
-
-        Example::
-            date_str = (integer.setResultsName("year") + '/' 
-                        + integer.setResultsName("month") + '/' 
-                        + integer.setResultsName("day"))
-
-            # equivalent form:
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-        """
-        newself = self.copy()
-        if name.endswith("*"):
-            name = name[:-1]
-            listAllMatches=True
-        newself.resultsName = name
-        newself.modalResults = not listAllMatches
-        return newself
-
-    def setBreak(self,breakFlag = True):
-        """Method to invoke the Python pdb debugger when this element is
-           about to be parsed. Set C{breakFlag} to True to enable, False to
-           disable.
-        """
-        if breakFlag:
-            _parseMethod = self._parse
-            def breaker(instring, loc, doActions=True, callPreParse=True):
-                import pdb
-                pdb.set_trace()
-                return _parseMethod( instring, loc, doActions, callPreParse )
-            breaker._originalParseMethod = _parseMethod
-            self._parse = breaker
-        else:
-            if hasattr(self._parse,"_originalParseMethod"):
-                self._parse = self._parse._originalParseMethod
-        return self
-
-    def setParseAction( self, *fns, **kwargs ):
-        """
-        Define one or more actions to perform when successfully matching parse element definition.
-        Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)},
-        C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where:
-         - s   = the original string being parsed (see note below)
-         - loc = the location of the matching substring
-         - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object
-        If the functions in fns modify the tokens, they can return them as the return
-        value from fn, and the modified list of tokens will replace the original.
-        Otherwise, fn does not need to return any value.
-
-        Optional keyword arguments:
-         - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing
-
-        Note: the default parsing behavior is to expand tabs in the input string
-        before starting the parsing process.  See L{I{parseString}} for more information
-        on parsing strings containing C{}s, and suggested methods to maintain a
-        consistent view of the parsed string, the parse location, and line and column
-        positions within the parsed string.
-        
-        Example::
-            integer = Word(nums)
-            date_str = integer + '/' + integer + '/' + integer
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
-
-            # use parse action to convert to ints at parse time
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            date_str = integer + '/' + integer + '/' + integer
-
-            # note that integer fields are now ints, not strings
-            date_str.parseString("1999/12/31")  # -> [1999, '/', 12, '/', 31]
-        """
-        self.parseAction = list(map(_trim_arity, list(fns)))
-        self.callDuringTry = kwargs.get("callDuringTry", False)
-        return self
-
-    def addParseAction( self, *fns, **kwargs ):
-        """
-        Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}}.
-        
-        See examples in L{I{copy}}.
-        """
-        self.parseAction += list(map(_trim_arity, list(fns)))
-        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
-        return self
-
-    def addCondition(self, *fns, **kwargs):
-        """Add a boolean predicate function to expression's list of parse actions. See 
-        L{I{setParseAction}} for function call signatures. Unlike C{setParseAction}, 
-        functions passed to C{addCondition} need to return boolean success/fail of the condition.
-
-        Optional keyword arguments:
-         - message = define a custom message to be used in the raised exception
-         - fatal   = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException
-         
-        Example::
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            year_int = integer.copy()
-            year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later")
-            date_str = year_int + '/' + integer + '/' + integer
-
-            result = date_str.parseString("1999/12/31")  # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1)
-        """
-        msg = kwargs.get("message", "failed user-defined condition")
-        exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException
-        for fn in fns:
-            def pa(s,l,t):
-                if not bool(_trim_arity(fn)(s,l,t)):
-                    raise exc_type(s,l,msg)
-            self.parseAction.append(pa)
-        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
-        return self
-
-    def setFailAction( self, fn ):
-        """Define action to perform if parsing fails at this expression.
-           Fail acton fn is a callable function that takes the arguments
-           C{fn(s,loc,expr,err)} where:
-            - s = string being parsed
-            - loc = location where expression match was attempted and failed
-            - expr = the parse expression that failed
-            - err = the exception thrown
-           The function returns no value.  It may throw C{L{ParseFatalException}}
-           if it is desired to stop parsing immediately."""
-        self.failAction = fn
-        return self
-
-    def _skipIgnorables( self, instring, loc ):
-        exprsFound = True
-        while exprsFound:
-            exprsFound = False
-            for e in self.ignoreExprs:
-                try:
-                    while 1:
-                        loc,dummy = e._parse( instring, loc )
-                        exprsFound = True
-                except ParseException:
-                    pass
-        return loc
-
-    def preParse( self, instring, loc ):
-        if self.ignoreExprs:
-            loc = self._skipIgnorables( instring, loc )
-
-        if self.skipWhitespace:
-            wt = self.whiteChars
-            instrlen = len(instring)
-            while loc < instrlen and instring[loc] in wt:
-                loc += 1
-
-        return loc
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        return loc, []
-
-    def postParse( self, instring, loc, tokenlist ):
-        return tokenlist
-
-    #~ @profile
-    def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ):
-        debugging = ( self.debug ) #and doActions )
-
-        if debugging or self.failAction:
-            #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))
-            if (self.debugActions[0] ):
-                self.debugActions[0]( instring, loc, self )
-            if callPreParse and self.callPreparse:
-                preloc = self.preParse( instring, loc )
-            else:
-                preloc = loc
-            tokensStart = preloc
-            try:
-                try:
-                    loc,tokens = self.parseImpl( instring, preloc, doActions )
-                except IndexError:
-                    raise ParseException( instring, len(instring), self.errmsg, self )
-            except ParseBaseException as err:
-                #~ print ("Exception raised:", err)
-                if self.debugActions[2]:
-                    self.debugActions[2]( instring, tokensStart, self, err )
-                if self.failAction:
-                    self.failAction( instring, tokensStart, self, err )
-                raise
-        else:
-            if callPreParse and self.callPreparse:
-                preloc = self.preParse( instring, loc )
-            else:
-                preloc = loc
-            tokensStart = preloc
-            if self.mayIndexError or preloc >= len(instring):
-                try:
-                    loc,tokens = self.parseImpl( instring, preloc, doActions )
-                except IndexError:
-                    raise ParseException( instring, len(instring), self.errmsg, self )
-            else:
-                loc,tokens = self.parseImpl( instring, preloc, doActions )
-
-        tokens = self.postParse( instring, loc, tokens )
-
-        retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults )
-        if self.parseAction and (doActions or self.callDuringTry):
-            if debugging:
-                try:
-                    for fn in self.parseAction:
-                        tokens = fn( instring, tokensStart, retTokens )
-                        if tokens is not None:
-                            retTokens = ParseResults( tokens,
-                                                      self.resultsName,
-                                                      asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
-                                                      modal=self.modalResults )
-                except ParseBaseException as err:
-                    #~ print "Exception raised in user parse action:", err
-                    if (self.debugActions[2] ):
-                        self.debugActions[2]( instring, tokensStart, self, err )
-                    raise
-            else:
-                for fn in self.parseAction:
-                    tokens = fn( instring, tokensStart, retTokens )
-                    if tokens is not None:
-                        retTokens = ParseResults( tokens,
-                                                  self.resultsName,
-                                                  asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
-                                                  modal=self.modalResults )
-        if debugging:
-            #~ print ("Matched",self,"->",retTokens.asList())
-            if (self.debugActions[1] ):
-                self.debugActions[1]( instring, tokensStart, loc, self, retTokens )
-
-        return loc, retTokens
-
-    def tryParse( self, instring, loc ):
-        try:
-            return self._parse( instring, loc, doActions=False )[0]
-        except ParseFatalException:
-            raise ParseException( instring, loc, self.errmsg, self)
-    
-    def canParseNext(self, instring, loc):
-        try:
-            self.tryParse(instring, loc)
-        except (ParseException, IndexError):
-            return False
-        else:
-            return True
-
-    class _UnboundedCache(object):
-        def __init__(self):
-            cache = {}
-            self.not_in_cache = not_in_cache = object()
-
-            def get(self, key):
-                return cache.get(key, not_in_cache)
-
-            def set(self, key, value):
-                cache[key] = value
-
-            def clear(self):
-                cache.clear()
-                
-            def cache_len(self):
-                return len(cache)
-
-            self.get = types.MethodType(get, self)
-            self.set = types.MethodType(set, self)
-            self.clear = types.MethodType(clear, self)
-            self.__len__ = types.MethodType(cache_len, self)
-
-    if _OrderedDict is not None:
-        class _FifoCache(object):
-            def __init__(self, size):
-                self.not_in_cache = not_in_cache = object()
-
-                cache = _OrderedDict()
-
-                def get(self, key):
-                    return cache.get(key, not_in_cache)
-
-                def set(self, key, value):
-                    cache[key] = value
-                    while len(cache) > size:
-                        try:
-                            cache.popitem(False)
-                        except KeyError:
-                            pass
-
-                def clear(self):
-                    cache.clear()
-
-                def cache_len(self):
-                    return len(cache)
-
-                self.get = types.MethodType(get, self)
-                self.set = types.MethodType(set, self)
-                self.clear = types.MethodType(clear, self)
-                self.__len__ = types.MethodType(cache_len, self)
-
-    else:
-        class _FifoCache(object):
-            def __init__(self, size):
-                self.not_in_cache = not_in_cache = object()
-
-                cache = {}
-                key_fifo = collections.deque([], size)
-
-                def get(self, key):
-                    return cache.get(key, not_in_cache)
-
-                def set(self, key, value):
-                    cache[key] = value
-                    while len(key_fifo) > size:
-                        cache.pop(key_fifo.popleft(), None)
-                    key_fifo.append(key)
-
-                def clear(self):
-                    cache.clear()
-                    key_fifo.clear()
-
-                def cache_len(self):
-                    return len(cache)
-
-                self.get = types.MethodType(get, self)
-                self.set = types.MethodType(set, self)
-                self.clear = types.MethodType(clear, self)
-                self.__len__ = types.MethodType(cache_len, self)
-
-    # argument cache for optimizing repeated calls when backtracking through recursive expressions
-    packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail
-    packrat_cache_lock = RLock()
-    packrat_cache_stats = [0, 0]
-
-    # this method gets repeatedly called during backtracking with the same arguments -
-    # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression
-    def _parseCache( self, instring, loc, doActions=True, callPreParse=True ):
-        HIT, MISS = 0, 1
-        lookup = (self, instring, loc, callPreParse, doActions)
-        with ParserElement.packrat_cache_lock:
-            cache = ParserElement.packrat_cache
-            value = cache.get(lookup)
-            if value is cache.not_in_cache:
-                ParserElement.packrat_cache_stats[MISS] += 1
-                try:
-                    value = self._parseNoCache(instring, loc, doActions, callPreParse)
-                except ParseBaseException as pe:
-                    # cache a copy of the exception, without the traceback
-                    cache.set(lookup, pe.__class__(*pe.args))
-                    raise
-                else:
-                    cache.set(lookup, (value[0], value[1].copy()))
-                    return value
-            else:
-                ParserElement.packrat_cache_stats[HIT] += 1
-                if isinstance(value, Exception):
-                    raise value
-                return (value[0], value[1].copy())
-
-    _parse = _parseNoCache
-
-    @staticmethod
-    def resetCache():
-        ParserElement.packrat_cache.clear()
-        ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats)
-
-    _packratEnabled = False
-    @staticmethod
-    def enablePackrat(cache_size_limit=128):
-        """Enables "packrat" parsing, which adds memoizing to the parsing logic.
-           Repeated parse attempts at the same string location (which happens
-           often in many complex grammars) can immediately return a cached value,
-           instead of re-executing parsing/validating code.  Memoizing is done of
-           both valid results and parsing exceptions.
-           
-           Parameters:
-            - cache_size_limit - (default=C{128}) - if an integer value is provided
-              will limit the size of the packrat cache; if None is passed, then
-              the cache size will be unbounded; if 0 is passed, the cache will
-              be effectively disabled.
-            
-           This speedup may break existing programs that use parse actions that
-           have side-effects.  For this reason, packrat parsing is disabled when
-           you first import pyparsing.  To activate the packrat feature, your
-           program must call the class method C{ParserElement.enablePackrat()}.  If
-           your program uses C{psyco} to "compile as you go", you must call
-           C{enablePackrat} before calling C{psyco.full()}.  If you do not do this,
-           Python will crash.  For best results, call C{enablePackrat()} immediately
-           after importing pyparsing.
-           
-           Example::
-               import pyparsing
-               pyparsing.ParserElement.enablePackrat()
-        """
-        if not ParserElement._packratEnabled:
-            ParserElement._packratEnabled = True
-            if cache_size_limit is None:
-                ParserElement.packrat_cache = ParserElement._UnboundedCache()
-            else:
-                ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit)
-            ParserElement._parse = ParserElement._parseCache
-
-    def parseString( self, instring, parseAll=False ):
-        """
-        Execute the parse expression with the given string.
-        This is the main interface to the client code, once the complete
-        expression has been built.
-
-        If you want the grammar to require that the entire input string be
-        successfully parsed, then set C{parseAll} to True (equivalent to ending
-        the grammar with C{L{StringEnd()}}).
-
-        Note: C{parseString} implicitly calls C{expandtabs()} on the input string,
-        in order to report proper column numbers in parse actions.
-        If the input string contains tabs and
-        the grammar uses parse actions that use the C{loc} argument to index into the
-        string being parsed, you can ensure you have a consistent view of the input
-        string by:
-         - calling C{parseWithTabs} on your grammar before calling C{parseString}
-           (see L{I{parseWithTabs}})
-         - define your parse action using the full C{(s,loc,toks)} signature, and
-           reference the input string using the parse action's C{s} argument
-         - explictly expand the tabs in your input string before calling
-           C{parseString}
-        
-        Example::
-            Word('a').parseString('aaaaabaaa')  # -> ['aaaaa']
-            Word('a').parseString('aaaaabaaa', parseAll=True)  # -> Exception: Expected end of text
-        """
-        ParserElement.resetCache()
-        if not self.streamlined:
-            self.streamline()
-            #~ self.saveAsList = True
-        for e in self.ignoreExprs:
-            e.streamline()
-        if not self.keepTabs:
-            instring = instring.expandtabs()
-        try:
-            loc, tokens = self._parse( instring, 0 )
-            if parseAll:
-                loc = self.preParse( instring, loc )
-                se = Empty() + StringEnd()
-                se._parse( instring, loc )
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-        else:
-            return tokens
-
-    def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ):
-        """
-        Scan the input string for expression matches.  Each match will return the
-        matching tokens, start location, and end location.  May be called with optional
-        C{maxMatches} argument, to clip scanning after 'n' matches are found.  If
-        C{overlap} is specified, then overlapping matches will be reported.
-
-        Note that the start and end locations are reported relative to the string
-        being parsed.  See L{I{parseString}} for more information on parsing
-        strings with embedded tabs.
-
-        Example::
-            source = "sldjf123lsdjjkf345sldkjf879lkjsfd987"
-            print(source)
-            for tokens,start,end in Word(alphas).scanString(source):
-                print(' '*start + '^'*(end-start))
-                print(' '*start + tokens[0])
-        
-        prints::
-        
-            sldjf123lsdjjkf345sldkjf879lkjsfd987
-            ^^^^^
-            sldjf
-                    ^^^^^^^
-                    lsdjjkf
-                              ^^^^^^
-                              sldkjf
-                                       ^^^^^^
-                                       lkjsfd
-        """
-        if not self.streamlined:
-            self.streamline()
-        for e in self.ignoreExprs:
-            e.streamline()
-
-        if not self.keepTabs:
-            instring = _ustr(instring).expandtabs()
-        instrlen = len(instring)
-        loc = 0
-        preparseFn = self.preParse
-        parseFn = self._parse
-        ParserElement.resetCache()
-        matches = 0
-        try:
-            while loc <= instrlen and matches < maxMatches:
-                try:
-                    preloc = preparseFn( instring, loc )
-                    nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
-                except ParseException:
-                    loc = preloc+1
-                else:
-                    if nextLoc > loc:
-                        matches += 1
-                        yield tokens, preloc, nextLoc
-                        if overlap:
-                            nextloc = preparseFn( instring, loc )
-                            if nextloc > loc:
-                                loc = nextLoc
-                            else:
-                                loc += 1
-                        else:
-                            loc = nextLoc
-                    else:
-                        loc = preloc+1
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def transformString( self, instring ):
-        """
-        Extension to C{L{scanString}}, to modify matching text with modified tokens that may
-        be returned from a parse action.  To use C{transformString}, define a grammar and
-        attach a parse action to it that modifies the returned token list.
-        Invoking C{transformString()} on a target string will then scan for matches,
-        and replace the matched text patterns according to the logic in the parse
-        action.  C{transformString()} returns the resulting transformed string.
-        
-        Example::
-            wd = Word(alphas)
-            wd.setParseAction(lambda toks: toks[0].title())
-            
-            print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york."))
-        Prints::
-            Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York.
-        """
-        out = []
-        lastE = 0
-        # force preservation of s, to minimize unwanted transformation of string, and to
-        # keep string locs straight between transformString and scanString
-        self.keepTabs = True
-        try:
-            for t,s,e in self.scanString( instring ):
-                out.append( instring[lastE:s] )
-                if t:
-                    if isinstance(t,ParseResults):
-                        out += t.asList()
-                    elif isinstance(t,list):
-                        out += t
-                    else:
-                        out.append(t)
-                lastE = e
-            out.append(instring[lastE:])
-            out = [o for o in out if o]
-            return "".join(map(_ustr,_flatten(out)))
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def searchString( self, instring, maxMatches=_MAX_INT ):
-        """
-        Another extension to C{L{scanString}}, simplifying the access to the tokens found
-        to match the given parse expression.  May be called with optional
-        C{maxMatches} argument, to clip searching after 'n' matches are found.
-        
-        Example::
-            # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters
-            cap_word = Word(alphas.upper(), alphas.lower())
-            
-            print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))
-
-            # the sum() builtin can be used to merge results into a single ParseResults object
-            print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")))
-        prints::
-            [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']]
-            ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity']
-        """
-        try:
-            return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ])
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False):
-        """
-        Generator method to split a string using the given expression as a separator.
-        May be called with optional C{maxsplit} argument, to limit the number of splits;
-        and the optional C{includeSeparators} argument (default=C{False}), if the separating
-        matching text should be included in the split results.
-        
-        Example::        
-            punc = oneOf(list(".,;:/-!?"))
-            print(list(punc.split("This, this?, this sentence, is badly punctuated!")))
-        prints::
-            ['This', ' this', '', ' this sentence', ' is badly punctuated', '']
-        """
-        splits = 0
-        last = 0
-        for t,s,e in self.scanString(instring, maxMatches=maxsplit):
-            yield instring[last:s]
-            if includeSeparators:
-                yield t[0]
-            last = e
-        yield instring[last:]
-
-    def __add__(self, other ):
-        """
-        Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement
-        converts them to L{Literal}s by default.
-        
-        Example::
-            greet = Word(alphas) + "," + Word(alphas) + "!"
-            hello = "Hello, World!"
-            print (hello, "->", greet.parseString(hello))
-        Prints::
-            Hello, World! -> ['Hello', ',', 'World', '!']
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return And( [ self, other ] )
-
-    def __radd__(self, other ):
-        """
-        Implementation of + operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other + self
-
-    def __sub__(self, other):
-        """
-        Implementation of - operator, returns C{L{And}} with error stop
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return self + And._ErrorStop() + other
-
-    def __rsub__(self, other ):
-        """
-        Implementation of - operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other - self
-
-    def __mul__(self,other):
-        """
-        Implementation of * operator, allows use of C{expr * 3} in place of
-        C{expr + expr + expr}.  Expressions may also me multiplied by a 2-integer
-        tuple, similar to C{{min,max}} multipliers in regular expressions.  Tuples
-        may also include C{None} as in:
-         - C{expr*(n,None)} or C{expr*(n,)} is equivalent
-              to C{expr*n + L{ZeroOrMore}(expr)}
-              (read as "at least n instances of C{expr}")
-         - C{expr*(None,n)} is equivalent to C{expr*(0,n)}
-              (read as "0 to n instances of C{expr}")
-         - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)}
-         - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)}
-
-        Note that C{expr*(None,n)} does not raise an exception if
-        more than n exprs exist in the input stream; that is,
-        C{expr*(None,n)} does not enforce a maximum number of expr
-        occurrences.  If this behavior is desired, then write
-        C{expr*(None,n) + ~expr}
-        """
-        if isinstance(other,int):
-            minElements, optElements = other,0
-        elif isinstance(other,tuple):
-            other = (other + (None, None))[:2]
-            if other[0] is None:
-                other = (0, other[1])
-            if isinstance(other[0],int) and other[1] is None:
-                if other[0] == 0:
-                    return ZeroOrMore(self)
-                if other[0] == 1:
-                    return OneOrMore(self)
-                else:
-                    return self*other[0] + ZeroOrMore(self)
-            elif isinstance(other[0],int) and isinstance(other[1],int):
-                minElements, optElements = other
-                optElements -= minElements
-            else:
-                raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1]))
-        else:
-            raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other))
-
-        if minElements < 0:
-            raise ValueError("cannot multiply ParserElement by negative value")
-        if optElements < 0:
-            raise ValueError("second tuple value must be greater or equal to first tuple value")
-        if minElements == optElements == 0:
-            raise ValueError("cannot multiply ParserElement by 0 or (0,0)")
-
-        if (optElements):
-            def makeOptionalList(n):
-                if n>1:
-                    return Optional(self + makeOptionalList(n-1))
-                else:
-                    return Optional(self)
-            if minElements:
-                if minElements == 1:
-                    ret = self + makeOptionalList(optElements)
-                else:
-                    ret = And([self]*minElements) + makeOptionalList(optElements)
-            else:
-                ret = makeOptionalList(optElements)
-        else:
-            if minElements == 1:
-                ret = self
-            else:
-                ret = And([self]*minElements)
-        return ret
-
-    def __rmul__(self, other):
-        return self.__mul__(other)
-
-    def __or__(self, other ):
-        """
-        Implementation of | operator - returns C{L{MatchFirst}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return MatchFirst( [ self, other ] )
-
-    def __ror__(self, other ):
-        """
-        Implementation of | operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other | self
-
-    def __xor__(self, other ):
-        """
-        Implementation of ^ operator - returns C{L{Or}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return Or( [ self, other ] )
-
-    def __rxor__(self, other ):
-        """
-        Implementation of ^ operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other ^ self
-
-    def __and__(self, other ):
-        """
-        Implementation of & operator - returns C{L{Each}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return Each( [ self, other ] )
-
-    def __rand__(self, other ):
-        """
-        Implementation of & operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other & self
-
-    def __invert__( self ):
-        """
-        Implementation of ~ operator - returns C{L{NotAny}}
-        """
-        return NotAny( self )
-
-    def __call__(self, name=None):
-        """
-        Shortcut for C{L{setResultsName}}, with C{listAllMatches=False}.
-        
-        If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be
-        passed as C{True}.
-           
-        If C{name} is omitted, same as calling C{L{copy}}.
-
-        Example::
-            # these are equivalent
-            userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno")
-            userdata = Word(alphas)("name") + Word(nums+"-")("socsecno")             
-        """
-        if name is not None:
-            return self.setResultsName(name)
-        else:
-            return self.copy()
-
-    def suppress( self ):
-        """
-        Suppresses the output of this C{ParserElement}; useful to keep punctuation from
-        cluttering up returned output.
-        """
-        return Suppress( self )
-
-    def leaveWhitespace( self ):
-        """
-        Disables the skipping of whitespace before matching the characters in the
-        C{ParserElement}'s defined pattern.  This is normally only used internally by
-        the pyparsing module, but may be needed in some whitespace-sensitive grammars.
-        """
-        self.skipWhitespace = False
-        return self
-
-    def setWhitespaceChars( self, chars ):
-        """
-        Overrides the default whitespace chars
-        """
-        self.skipWhitespace = True
-        self.whiteChars = chars
-        self.copyDefaultWhiteChars = False
-        return self
-
-    def parseWithTabs( self ):
-        """
-        Overrides default behavior to expand C{}s to spaces before parsing the input string.
-        Must be called before C{parseString} when the input grammar contains elements that
-        match C{} characters.
-        """
-        self.keepTabs = True
-        return self
-
-    def ignore( self, other ):
-        """
-        Define expression to be ignored (e.g., comments) while doing pattern
-        matching; may be called repeatedly, to define multiple comment or other
-        ignorable patterns.
-        
-        Example::
-            patt = OneOrMore(Word(alphas))
-            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj']
-            
-            patt.ignore(cStyleComment)
-            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd']
-        """
-        if isinstance(other, basestring):
-            other = Suppress(other)
-
-        if isinstance( other, Suppress ):
-            if other not in self.ignoreExprs:
-                self.ignoreExprs.append(other)
-        else:
-            self.ignoreExprs.append( Suppress( other.copy() ) )
-        return self
-
-    def setDebugActions( self, startAction, successAction, exceptionAction ):
-        """
-        Enable display of debugging messages while doing pattern matching.
-        """
-        self.debugActions = (startAction or _defaultStartDebugAction,
-                             successAction or _defaultSuccessDebugAction,
-                             exceptionAction or _defaultExceptionDebugAction)
-        self.debug = True
-        return self
-
-    def setDebug( self, flag=True ):
-        """
-        Enable display of debugging messages while doing pattern matching.
-        Set C{flag} to True to enable, False to disable.
-
-        Example::
-            wd = Word(alphas).setName("alphaword")
-            integer = Word(nums).setName("numword")
-            term = wd | integer
-            
-            # turn on debugging for wd
-            wd.setDebug()
-
-            OneOrMore(term).parseString("abc 123 xyz 890")
-        
-        prints::
-            Match alphaword at loc 0(1,1)
-            Matched alphaword -> ['abc']
-            Match alphaword at loc 3(1,4)
-            Exception raised:Expected alphaword (at char 4), (line:1, col:5)
-            Match alphaword at loc 7(1,8)
-            Matched alphaword -> ['xyz']
-            Match alphaword at loc 11(1,12)
-            Exception raised:Expected alphaword (at char 12), (line:1, col:13)
-            Match alphaword at loc 15(1,16)
-            Exception raised:Expected alphaword (at char 15), (line:1, col:16)
-
-        The output shown is that produced by the default debug actions - custom debug actions can be
-        specified using L{setDebugActions}. Prior to attempting
-        to match the C{wd} expression, the debugging message C{"Match  at loc (,)"}
-        is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"}
-        message is shown. Also note the use of L{setName} to assign a human-readable name to the expression,
-        which makes debugging and exception messages easier to understand - for instance, the default
-        name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}.
-        """
-        if flag:
-            self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction )
-        else:
-            self.debug = False
-        return self
-
-    def __str__( self ):
-        return self.name
-
-    def __repr__( self ):
-        return _ustr(self)
-
-    def streamline( self ):
-        self.streamlined = True
-        self.strRepr = None
-        return self
-
-    def checkRecursion( self, parseElementList ):
-        pass
-
-    def validate( self, validateTrace=[] ):
-        """
-        Check defined expressions for valid structure, check for infinite recursive definitions.
-        """
-        self.checkRecursion( [] )
-
-    def parseFile( self, file_or_filename, parseAll=False ):
-        """
-        Execute the parse expression on the given file or filename.
-        If a filename is specified (instead of a file object),
-        the entire file is opened, read, and closed before parsing.
-        """
-        try:
-            file_contents = file_or_filename.read()
-        except AttributeError:
-            with open(file_or_filename, "r") as f:
-                file_contents = f.read()
-        try:
-            return self.parseString(file_contents, parseAll)
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def __eq__(self,other):
-        if isinstance(other, ParserElement):
-            return self is other or vars(self) == vars(other)
-        elif isinstance(other, basestring):
-            return self.matches(other)
-        else:
-            return super(ParserElement,self)==other
-
-    def __ne__(self,other):
-        return not (self == other)
-
-    def __hash__(self):
-        return hash(id(self))
-
-    def __req__(self,other):
-        return self == other
-
-    def __rne__(self,other):
-        return not (self == other)
-
-    def matches(self, testString, parseAll=True):
-        """
-        Method for quick testing of a parser against a test string. Good for simple 
-        inline microtests of sub expressions while building up larger parser.
-           
-        Parameters:
-         - testString - to test against this expression for a match
-         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests
-            
-        Example::
-            expr = Word(nums)
-            assert expr.matches("100")
-        """
-        try:
-            self.parseString(_ustr(testString), parseAll=parseAll)
-            return True
-        except ParseBaseException:
-            return False
-                
-    def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False):
-        """
-        Execute the parse expression on a series of test strings, showing each
-        test, the parsed results or where the parse failed. Quick and easy way to
-        run a parse expression against a list of sample strings.
-           
-        Parameters:
-         - tests - a list of separate test strings, or a multiline string of test strings
-         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests           
-         - comment - (default=C{'#'}) - expression for indicating embedded comments in the test 
-              string; pass None to disable comment filtering
-         - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline;
-              if False, only dump nested list
-         - printResults - (default=C{True}) prints test output to stdout
-         - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing
-
-        Returns: a (success, results) tuple, where success indicates that all tests succeeded
-        (or failed if C{failureTests} is True), and the results contain a list of lines of each 
-        test's output
-        
-        Example::
-            number_expr = pyparsing_common.number.copy()
-
-            result = number_expr.runTests('''
-                # unsigned integer
-                100
-                # negative integer
-                -100
-                # float with scientific notation
-                6.02e23
-                # integer with scientific notation
-                1e-12
-                ''')
-            print("Success" if result[0] else "Failed!")
-
-            result = number_expr.runTests('''
-                # stray character
-                100Z
-                # missing leading digit before '.'
-                -.100
-                # too many '.'
-                3.14.159
-                ''', failureTests=True)
-            print("Success" if result[0] else "Failed!")
-        prints::
-            # unsigned integer
-            100
-            [100]
-
-            # negative integer
-            -100
-            [-100]
-
-            # float with scientific notation
-            6.02e23
-            [6.02e+23]
-
-            # integer with scientific notation
-            1e-12
-            [1e-12]
-
-            Success
-            
-            # stray character
-            100Z
-               ^
-            FAIL: Expected end of text (at char 3), (line:1, col:4)
-
-            # missing leading digit before '.'
-            -.100
-            ^
-            FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1)
-
-            # too many '.'
-            3.14.159
-                ^
-            FAIL: Expected end of text (at char 4), (line:1, col:5)
-
-            Success
-
-        Each test string must be on a single line. If you want to test a string that spans multiple
-        lines, create a test like this::
-
-            expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines")
-        
-        (Note that this is a raw string literal, you must include the leading 'r'.)
-        """
-        if isinstance(tests, basestring):
-            tests = list(map(str.strip, tests.rstrip().splitlines()))
-        if isinstance(comment, basestring):
-            comment = Literal(comment)
-        allResults = []
-        comments = []
-        success = True
-        for t in tests:
-            if comment is not None and comment.matches(t, False) or comments and not t:
-                comments.append(t)
-                continue
-            if not t:
-                continue
-            out = ['\n'.join(comments), t]
-            comments = []
-            try:
-                t = t.replace(r'\n','\n')
-                result = self.parseString(t, parseAll=parseAll)
-                out.append(result.dump(full=fullDump))
-                success = success and not failureTests
-            except ParseBaseException as pe:
-                fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else ""
-                if '\n' in t:
-                    out.append(line(pe.loc, t))
-                    out.append(' '*(col(pe.loc,t)-1) + '^' + fatal)
-                else:
-                    out.append(' '*pe.loc + '^' + fatal)
-                out.append("FAIL: " + str(pe))
-                success = success and failureTests
-                result = pe
-            except Exception as exc:
-                out.append("FAIL-EXCEPTION: " + str(exc))
-                success = success and failureTests
-                result = exc
-
-            if printResults:
-                if fullDump:
-                    out.append('')
-                print('\n'.join(out))
-
-            allResults.append((t, result))
-        
-        return success, allResults
-
-        
-class Token(ParserElement):
-    """
-    Abstract C{ParserElement} subclass, for defining atomic matching patterns.
-    """
-    def __init__( self ):
-        super(Token,self).__init__( savelist=False )
-
-
-class Empty(Token):
-    """
-    An empty token, will always match.
-    """
-    def __init__( self ):
-        super(Empty,self).__init__()
-        self.name = "Empty"
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-
-
-class NoMatch(Token):
-    """
-    A token that will never match.
-    """
-    def __init__( self ):
-        super(NoMatch,self).__init__()
-        self.name = "NoMatch"
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-        self.errmsg = "Unmatchable token"
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        raise ParseException(instring, loc, self.errmsg, self)
-
-
-class Literal(Token):
-    """
-    Token to exactly match a specified string.
-    
-    Example::
-        Literal('blah').parseString('blah')  # -> ['blah']
-        Literal('blah').parseString('blahfooblah')  # -> ['blah']
-        Literal('blah').parseString('bla')  # -> Exception: Expected "blah"
-    
-    For case-insensitive matching, use L{CaselessLiteral}.
-    
-    For keyword matching (force word break before and after the matched string),
-    use L{Keyword} or L{CaselessKeyword}.
-    """
-    def __init__( self, matchString ):
-        super(Literal,self).__init__()
-        self.match = matchString
-        self.matchLen = len(matchString)
-        try:
-            self.firstMatchChar = matchString[0]
-        except IndexError:
-            warnings.warn("null string passed to Literal; use Empty() instead",
-                            SyntaxWarning, stacklevel=2)
-            self.__class__ = Empty
-        self.name = '"%s"' % _ustr(self.match)
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = False
-        self.mayIndexError = False
-
-    # Performance tuning: this routine gets called a *lot*
-    # if this is a single character match string  and the first character matches,
-    # short-circuit as quickly as possible, and avoid calling startswith
-    #~ @profile
-    def parseImpl( self, instring, loc, doActions=True ):
-        if (instring[loc] == self.firstMatchChar and
-            (self.matchLen==1 or instring.startswith(self.match,loc)) ):
-            return loc+self.matchLen, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-_L = Literal
-ParserElement._literalStringClass = Literal
-
-class Keyword(Token):
-    """
-    Token to exactly match a specified string as a keyword, that is, it must be
-    immediately followed by a non-keyword character.  Compare with C{L{Literal}}:
-     - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}.
-     - C{Keyword("if")} will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'}
-    Accepts two optional constructor arguments in addition to the keyword string:
-     - C{identChars} is a string of characters that would be valid identifier characters,
-          defaulting to all alphanumerics + "_" and "$"
-     - C{caseless} allows case-insensitive matching, default is C{False}.
-       
-    Example::
-        Keyword("start").parseString("start")  # -> ['start']
-        Keyword("start").parseString("starting")  # -> Exception
-
-    For case-insensitive matching, use L{CaselessKeyword}.
-    """
-    DEFAULT_KEYWORD_CHARS = alphanums+"_$"
-
-    def __init__( self, matchString, identChars=None, caseless=False ):
-        super(Keyword,self).__init__()
-        if identChars is None:
-            identChars = Keyword.DEFAULT_KEYWORD_CHARS
-        self.match = matchString
-        self.matchLen = len(matchString)
-        try:
-            self.firstMatchChar = matchString[0]
-        except IndexError:
-            warnings.warn("null string passed to Keyword; use Empty() instead",
-                            SyntaxWarning, stacklevel=2)
-        self.name = '"%s"' % self.match
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = False
-        self.mayIndexError = False
-        self.caseless = caseless
-        if caseless:
-            self.caselessmatch = matchString.upper()
-            identChars = identChars.upper()
-        self.identChars = set(identChars)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.caseless:
-            if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
-                 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and
-                 (loc == 0 or instring[loc-1].upper() not in self.identChars) ):
-                return loc+self.matchLen, self.match
-        else:
-            if (instring[loc] == self.firstMatchChar and
-                (self.matchLen==1 or instring.startswith(self.match,loc)) and
-                (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and
-                (loc == 0 or instring[loc-1] not in self.identChars) ):
-                return loc+self.matchLen, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-
-    def copy(self):
-        c = super(Keyword,self).copy()
-        c.identChars = Keyword.DEFAULT_KEYWORD_CHARS
-        return c
-
-    @staticmethod
-    def setDefaultKeywordChars( chars ):
-        """Overrides the default Keyword chars
-        """
-        Keyword.DEFAULT_KEYWORD_CHARS = chars
-
-class CaselessLiteral(Literal):
-    """
-    Token to match a specified string, ignoring case of letters.
-    Note: the matched results will always be in the case of the given
-    match string, NOT the case of the input text.
-
-    Example::
-        OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD']
-        
-    (Contrast with example for L{CaselessKeyword}.)
-    """
-    def __init__( self, matchString ):
-        super(CaselessLiteral,self).__init__( matchString.upper() )
-        # Preserve the defining literal.
-        self.returnString = matchString
-        self.name = "'%s'" % self.returnString
-        self.errmsg = "Expected " + self.name
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if instring[ loc:loc+self.matchLen ].upper() == self.match:
-            return loc+self.matchLen, self.returnString
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class CaselessKeyword(Keyword):
-    """
-    Caseless version of L{Keyword}.
-
-    Example::
-        OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD']
-        
-    (Contrast with example for L{CaselessLiteral}.)
-    """
-    def __init__( self, matchString, identChars=None ):
-        super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True )
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
-             (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ):
-            return loc+self.matchLen, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class CloseMatch(Token):
-    """
-    A variation on L{Literal} which matches "close" matches, that is, 
-    strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters:
-     - C{match_string} - string to be matched
-     - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match
-    
-    The results from a successful parse will contain the matched text from the input string and the following named results:
-     - C{mismatches} - a list of the positions within the match_string where mismatches were found
-     - C{original} - the original match_string used to compare against the input string
-    
-    If C{mismatches} is an empty list, then the match was an exact match.
-    
-    Example::
-        patt = CloseMatch("ATCATCGAATGGA")
-        patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']})
-        patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1)
-
-        # exact match
-        patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']})
-
-        # close match allowing up to 2 mismatches
-        patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2)
-        patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']})
-    """
-    def __init__(self, match_string, maxMismatches=1):
-        super(CloseMatch,self).__init__()
-        self.name = match_string
-        self.match_string = match_string
-        self.maxMismatches = maxMismatches
-        self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches)
-        self.mayIndexError = False
-        self.mayReturnEmpty = False
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        start = loc
-        instrlen = len(instring)
-        maxloc = start + len(self.match_string)
-
-        if maxloc <= instrlen:
-            match_string = self.match_string
-            match_stringloc = 0
-            mismatches = []
-            maxMismatches = self.maxMismatches
-
-            for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)):
-                src,mat = s_m
-                if src != mat:
-                    mismatches.append(match_stringloc)
-                    if len(mismatches) > maxMismatches:
-                        break
-            else:
-                loc = match_stringloc + 1
-                results = ParseResults([instring[start:loc]])
-                results['original'] = self.match_string
-                results['mismatches'] = mismatches
-                return loc, results
-
-        raise ParseException(instring, loc, self.errmsg, self)
-
-
-class Word(Token):
-    """
-    Token for matching words composed of allowed character sets.
-    Defined with string containing all allowed initial characters,
-    an optional string containing allowed body characters (if omitted,
-    defaults to the initial character set), and an optional minimum,
-    maximum, and/or exact length.  The default value for C{min} is 1 (a
-    minimum value < 1 is not valid); the default values for C{max} and C{exact}
-    are 0, meaning no maximum or exact length restriction. An optional
-    C{excludeChars} parameter can list characters that might be found in 
-    the input C{bodyChars} string; useful to define a word of all printables
-    except for one or two characters, for instance.
-    
-    L{srange} is useful for defining custom character set strings for defining 
-    C{Word} expressions, using range notation from regular expression character sets.
-    
-    A common mistake is to use C{Word} to match a specific literal string, as in 
-    C{Word("Address")}. Remember that C{Word} uses the string argument to define
-    I{sets} of matchable characters. This expression would match "Add", "AAA",
-    "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'.
-    To match an exact literal string, use L{Literal} or L{Keyword}.
-
-    pyparsing includes helper strings for building Words:
-     - L{alphas}
-     - L{nums}
-     - L{alphanums}
-     - L{hexnums}
-     - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.)
-     - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.)
-     - L{printables} (any non-whitespace character)
-
-    Example::
-        # a word composed of digits
-        integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9"))
-        
-        # a word with a leading capital, and zero or more lowercase
-        capital_word = Word(alphas.upper(), alphas.lower())
-
-        # hostnames are alphanumeric, with leading alpha, and '-'
-        hostname = Word(alphas, alphanums+'-')
-        
-        # roman numeral (not a strict parser, accepts invalid mix of characters)
-        roman = Word("IVXLCDM")
-        
-        # any string of non-whitespace characters, except for ','
-        csv_value = Word(printables, excludeChars=",")
-    """
-    def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ):
-        super(Word,self).__init__()
-        if excludeChars:
-            initChars = ''.join(c for c in initChars if c not in excludeChars)
-            if bodyChars:
-                bodyChars = ''.join(c for c in bodyChars if c not in excludeChars)
-        self.initCharsOrig = initChars
-        self.initChars = set(initChars)
-        if bodyChars :
-            self.bodyCharsOrig = bodyChars
-            self.bodyChars = set(bodyChars)
-        else:
-            self.bodyCharsOrig = initChars
-            self.bodyChars = set(initChars)
-
-        self.maxSpecified = max > 0
-
-        if min < 1:
-            raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted")
-
-        self.minLen = min
-
-        if max > 0:
-            self.maxLen = max
-        else:
-            self.maxLen = _MAX_INT
-
-        if exact > 0:
-            self.maxLen = exact
-            self.minLen = exact
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.asKeyword = asKeyword
-
-        if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0):
-            if self.bodyCharsOrig == self.initCharsOrig:
-                self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig)
-            elif len(self.initCharsOrig) == 1:
-                self.reString = "%s[%s]*" % \
-                                      (re.escape(self.initCharsOrig),
-                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
-            else:
-                self.reString = "[%s][%s]*" % \
-                                      (_escapeRegexRangeChars(self.initCharsOrig),
-                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
-            if self.asKeyword:
-                self.reString = r"\b"+self.reString+r"\b"
-            try:
-                self.re = re.compile( self.reString )
-            except Exception:
-                self.re = None
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.re:
-            result = self.re.match(instring,loc)
-            if not result:
-                raise ParseException(instring, loc, self.errmsg, self)
-
-            loc = result.end()
-            return loc, result.group()
-
-        if not(instring[ loc ] in self.initChars):
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        start = loc
-        loc += 1
-        instrlen = len(instring)
-        bodychars = self.bodyChars
-        maxloc = start + self.maxLen
-        maxloc = min( maxloc, instrlen )
-        while loc < maxloc and instring[loc] in bodychars:
-            loc += 1
-
-        throwException = False
-        if loc - start < self.minLen:
-            throwException = True
-        if self.maxSpecified and loc < instrlen and instring[loc] in bodychars:
-            throwException = True
-        if self.asKeyword:
-            if (start>0 and instring[start-1] in bodychars) or (loc4:
-                    return s[:4]+"..."
-                else:
-                    return s
-
-            if ( self.initCharsOrig != self.bodyCharsOrig ):
-                self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) )
-            else:
-                self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig)
-
-        return self.strRepr
-
-
-class Regex(Token):
-    r"""
-    Token for matching strings that match a given regular expression.
-    Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module.
-    If the given regex contains named groups (defined using C{(?P...)}), these will be preserved as 
-    named parse results.
-
-    Example::
-        realnum = Regex(r"[+-]?\d+\.\d*")
-        date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\d\d?)')
-        # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression
-        roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})")
-    """
-    compiledREtype = type(re.compile("[A-Z]"))
-    def __init__( self, pattern, flags=0):
-        """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags."""
-        super(Regex,self).__init__()
-
-        if isinstance(pattern, basestring):
-            if not pattern:
-                warnings.warn("null string passed to Regex; use Empty() instead",
-                        SyntaxWarning, stacklevel=2)
-
-            self.pattern = pattern
-            self.flags = flags
-
-            try:
-                self.re = re.compile(self.pattern, self.flags)
-                self.reString = self.pattern
-            except sre_constants.error:
-                warnings.warn("invalid pattern (%s) passed to Regex" % pattern,
-                    SyntaxWarning, stacklevel=2)
-                raise
-
-        elif isinstance(pattern, Regex.compiledREtype):
-            self.re = pattern
-            self.pattern = \
-            self.reString = str(pattern)
-            self.flags = flags
-            
-        else:
-            raise ValueError("Regex may only be constructed with a string or a compiled RE object")
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        result = self.re.match(instring,loc)
-        if not result:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        loc = result.end()
-        d = result.groupdict()
-        ret = ParseResults(result.group())
-        if d:
-            for k in d:
-                ret[k] = d[k]
-        return loc,ret
-
-    def __str__( self ):
-        try:
-            return super(Regex,self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-            self.strRepr = "Re:(%s)" % repr(self.pattern)
-
-        return self.strRepr
-
-
-class QuotedString(Token):
-    r"""
-    Token for matching strings that are delimited by quoting characters.
-    
-    Defined with the following parameters:
-        - quoteChar - string of one or more characters defining the quote delimiting string
-        - escChar - character to escape quotes, typically backslash (default=C{None})
-        - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{None})
-        - multiline - boolean indicating whether quotes can span multiple lines (default=C{False})
-        - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True})
-        - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar)
-        - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True})
-
-    Example::
-        qs = QuotedString('"')
-        print(qs.searchString('lsjdf "This is the quote" sldjf'))
-        complex_qs = QuotedString('{{', endQuoteChar='}}')
-        print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf'))
-        sql_qs = QuotedString('"', escQuote='""')
-        print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf'))
-    prints::
-        [['This is the quote']]
-        [['This is the "quote"']]
-        [['This is the quote with "embedded" quotes']]
-    """
-    def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True):
-        super(QuotedString,self).__init__()
-
-        # remove white space from quote chars - wont work anyway
-        quoteChar = quoteChar.strip()
-        if not quoteChar:
-            warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
-            raise SyntaxError()
-
-        if endQuoteChar is None:
-            endQuoteChar = quoteChar
-        else:
-            endQuoteChar = endQuoteChar.strip()
-            if not endQuoteChar:
-                warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
-                raise SyntaxError()
-
-        self.quoteChar = quoteChar
-        self.quoteCharLen = len(quoteChar)
-        self.firstQuoteChar = quoteChar[0]
-        self.endQuoteChar = endQuoteChar
-        self.endQuoteCharLen = len(endQuoteChar)
-        self.escChar = escChar
-        self.escQuote = escQuote
-        self.unquoteResults = unquoteResults
-        self.convertWhitespaceEscapes = convertWhitespaceEscapes
-
-        if multiline:
-            self.flags = re.MULTILINE | re.DOTALL
-            self.pattern = r'%s(?:[^%s%s]' % \
-                ( re.escape(self.quoteChar),
-                  _escapeRegexRangeChars(self.endQuoteChar[0]),
-                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
-        else:
-            self.flags = 0
-            self.pattern = r'%s(?:[^%s\n\r%s]' % \
-                ( re.escape(self.quoteChar),
-                  _escapeRegexRangeChars(self.endQuoteChar[0]),
-                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
-        if len(self.endQuoteChar) > 1:
-            self.pattern += (
-                '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]),
-                                               _escapeRegexRangeChars(self.endQuoteChar[i]))
-                                    for i in range(len(self.endQuoteChar)-1,0,-1)) + ')'
-                )
-        if escQuote:
-            self.pattern += (r'|(?:%s)' % re.escape(escQuote))
-        if escChar:
-            self.pattern += (r'|(?:%s.)' % re.escape(escChar))
-            self.escCharReplacePattern = re.escape(self.escChar)+"(.)"
-        self.pattern += (r')*%s' % re.escape(self.endQuoteChar))
-
-        try:
-            self.re = re.compile(self.pattern, self.flags)
-            self.reString = self.pattern
-        except sre_constants.error:
-            warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern,
-                SyntaxWarning, stacklevel=2)
-            raise
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None
-        if not result:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        loc = result.end()
-        ret = result.group()
-
-        if self.unquoteResults:
-
-            # strip off quotes
-            ret = ret[self.quoteCharLen:-self.endQuoteCharLen]
-
-            if isinstance(ret,basestring):
-                # replace escaped whitespace
-                if '\\' in ret and self.convertWhitespaceEscapes:
-                    ws_map = {
-                        r'\t' : '\t',
-                        r'\n' : '\n',
-                        r'\f' : '\f',
-                        r'\r' : '\r',
-                    }
-                    for wslit,wschar in ws_map.items():
-                        ret = ret.replace(wslit, wschar)
-
-                # replace escaped characters
-                if self.escChar:
-                    ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret)
-
-                # replace escaped quotes
-                if self.escQuote:
-                    ret = ret.replace(self.escQuote, self.endQuoteChar)
-
-        return loc, ret
-
-    def __str__( self ):
-        try:
-            return super(QuotedString,self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-            self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar)
-
-        return self.strRepr
-
-
-class CharsNotIn(Token):
-    """
-    Token for matching words composed of characters I{not} in a given set (will
-    include whitespace in matched characters if not listed in the provided exclusion set - see example).
-    Defined with string containing all disallowed characters, and an optional
-    minimum, maximum, and/or exact length.  The default value for C{min} is 1 (a
-    minimum value < 1 is not valid); the default values for C{max} and C{exact}
-    are 0, meaning no maximum or exact length restriction.
-
-    Example::
-        # define a comma-separated-value as anything that is not a ','
-        csv_value = CharsNotIn(',')
-        print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213"))
-    prints::
-        ['dkls', 'lsdkjf', 's12 34', '@!#', '213']
-    """
-    def __init__( self, notChars, min=1, max=0, exact=0 ):
-        super(CharsNotIn,self).__init__()
-        self.skipWhitespace = False
-        self.notChars = notChars
-
-        if min < 1:
-            raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted")
-
-        self.minLen = min
-
-        if max > 0:
-            self.maxLen = max
-        else:
-            self.maxLen = _MAX_INT
-
-        if exact > 0:
-            self.maxLen = exact
-            self.minLen = exact
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = ( self.minLen == 0 )
-        self.mayIndexError = False
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if instring[loc] in self.notChars:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        start = loc
-        loc += 1
-        notchars = self.notChars
-        maxlen = min( start+self.maxLen, len(instring) )
-        while loc < maxlen and \
-              (instring[loc] not in notchars):
-            loc += 1
-
-        if loc - start < self.minLen:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        return loc, instring[start:loc]
-
-    def __str__( self ):
-        try:
-            return super(CharsNotIn, self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-            if len(self.notChars) > 4:
-                self.strRepr = "!W:(%s...)" % self.notChars[:4]
-            else:
-                self.strRepr = "!W:(%s)" % self.notChars
-
-        return self.strRepr
-
-class White(Token):
-    """
-    Special matching class for matching whitespace.  Normally, whitespace is ignored
-    by pyparsing grammars.  This class is included when some whitespace structures
-    are significant.  Define with a string containing the whitespace characters to be
-    matched; default is C{" \\t\\r\\n"}.  Also takes optional C{min}, C{max}, and C{exact} arguments,
-    as defined for the C{L{Word}} class.
-    """
-    whiteStrs = {
-        " " : "",
-        "\t": "",
-        "\n": "",
-        "\r": "",
-        "\f": "",
-        }
-    def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0):
-        super(White,self).__init__()
-        self.matchWhite = ws
-        self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) )
-        #~ self.leaveWhitespace()
-        self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite))
-        self.mayReturnEmpty = True
-        self.errmsg = "Expected " + self.name
-
-        self.minLen = min
-
-        if max > 0:
-            self.maxLen = max
-        else:
-            self.maxLen = _MAX_INT
-
-        if exact > 0:
-            self.maxLen = exact
-            self.minLen = exact
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if not(instring[ loc ] in self.matchWhite):
-            raise ParseException(instring, loc, self.errmsg, self)
-        start = loc
-        loc += 1
-        maxloc = start + self.maxLen
-        maxloc = min( maxloc, len(instring) )
-        while loc < maxloc and instring[loc] in self.matchWhite:
-            loc += 1
-
-        if loc - start < self.minLen:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        return loc, instring[start:loc]
-
-
-class _PositionToken(Token):
-    def __init__( self ):
-        super(_PositionToken,self).__init__()
-        self.name=self.__class__.__name__
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-
-class GoToColumn(_PositionToken):
-    """
-    Token to advance to a specific column of input text; useful for tabular report scraping.
-    """
-    def __init__( self, colno ):
-        super(GoToColumn,self).__init__()
-        self.col = colno
-
-    def preParse( self, instring, loc ):
-        if col(loc,instring) != self.col:
-            instrlen = len(instring)
-            if self.ignoreExprs:
-                loc = self._skipIgnorables( instring, loc )
-            while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col :
-                loc += 1
-        return loc
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        thiscol = col( loc, instring )
-        if thiscol > self.col:
-            raise ParseException( instring, loc, "Text not in expected column", self )
-        newloc = loc + self.col - thiscol
-        ret = instring[ loc: newloc ]
-        return newloc, ret
-
-
-class LineStart(_PositionToken):
-    """
-    Matches if current position is at the beginning of a line within the parse string
-    
-    Example::
-    
-        test = '''\
-        AAA this line
-        AAA and this line
-          AAA but not this one
-        B AAA and definitely not this one
-        '''
-
-        for t in (LineStart() + 'AAA' + restOfLine).searchString(test):
-            print(t)
-    
-    Prints::
-        ['AAA', ' this line']
-        ['AAA', ' and this line']    
-
-    """
-    def __init__( self ):
-        super(LineStart,self).__init__()
-        self.errmsg = "Expected start of line"
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if col(loc, instring) == 1:
-            return loc, []
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class LineEnd(_PositionToken):
-    """
-    Matches if current position is at the end of a line within the parse string
-    """
-    def __init__( self ):
-        super(LineEnd,self).__init__()
-        self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") )
-        self.errmsg = "Expected end of line"
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if loc len(instring):
-            return loc, []
-        else:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-class WordStart(_PositionToken):
-    """
-    Matches if the current position is at the beginning of a Word, and
-    is not preceded by any character in a given set of C{wordChars}
-    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
-    use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of
-    the string being parsed, or at the beginning of a line.
-    """
-    def __init__(self, wordChars = printables):
-        super(WordStart,self).__init__()
-        self.wordChars = set(wordChars)
-        self.errmsg = "Not at the start of a word"
-
-    def parseImpl(self, instring, loc, doActions=True ):
-        if loc != 0:
-            if (instring[loc-1] in self.wordChars or
-                instring[loc] not in self.wordChars):
-                raise ParseException(instring, loc, self.errmsg, self)
-        return loc, []
-
-class WordEnd(_PositionToken):
-    """
-    Matches if the current position is at the end of a Word, and
-    is not followed by any character in a given set of C{wordChars}
-    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
-    use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of
-    the string being parsed, or at the end of a line.
-    """
-    def __init__(self, wordChars = printables):
-        super(WordEnd,self).__init__()
-        self.wordChars = set(wordChars)
-        self.skipWhitespace = False
-        self.errmsg = "Not at the end of a word"
-
-    def parseImpl(self, instring, loc, doActions=True ):
-        instrlen = len(instring)
-        if instrlen>0 and loc maxExcLoc:
-                    maxException = err
-                    maxExcLoc = err.loc
-            except IndexError:
-                if len(instring) > maxExcLoc:
-                    maxException = ParseException(instring,len(instring),e.errmsg,self)
-                    maxExcLoc = len(instring)
-            else:
-                # save match among all matches, to retry longest to shortest
-                matches.append((loc2, e))
-
-        if matches:
-            matches.sort(key=lambda x: -x[0])
-            for _,e in matches:
-                try:
-                    return e._parse( instring, loc, doActions )
-                except ParseException as err:
-                    err.__traceback__ = None
-                    if err.loc > maxExcLoc:
-                        maxException = err
-                        maxExcLoc = err.loc
-
-        if maxException is not None:
-            maxException.msg = self.errmsg
-            raise maxException
-        else:
-            raise ParseException(instring, loc, "no defined alternatives to match", self)
-
-
-    def __ixor__(self, other ):
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        return self.append( other ) #Or( [ self, other ] )
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}"
-
-        return self.strRepr
-
-    def checkRecursion( self, parseElementList ):
-        subRecCheckList = parseElementList[:] + [ self ]
-        for e in self.exprs:
-            e.checkRecursion( subRecCheckList )
-
-
-class MatchFirst(ParseExpression):
-    """
-    Requires that at least one C{ParseExpression} is found.
-    If two expressions match, the first one listed is the one that will match.
-    May be constructed using the C{'|'} operator.
-
-    Example::
-        # construct MatchFirst using '|' operator
-        
-        # watch the order of expressions to match
-        number = Word(nums) | Combine(Word(nums) + '.' + Word(nums))
-        print(number.searchString("123 3.1416 789")) #  Fail! -> [['123'], ['3'], ['1416'], ['789']]
-
-        # put more selective expression first
-        number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums)
-        print(number.searchString("123 3.1416 789")) #  Better -> [['123'], ['3.1416'], ['789']]
-    """
-    def __init__( self, exprs, savelist = False ):
-        super(MatchFirst,self).__init__(exprs, savelist)
-        if self.exprs:
-            self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
-        else:
-            self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        maxExcLoc = -1
-        maxException = None
-        for e in self.exprs:
-            try:
-                ret = e._parse( instring, loc, doActions )
-                return ret
-            except ParseException as err:
-                if err.loc > maxExcLoc:
-                    maxException = err
-                    maxExcLoc = err.loc
-            except IndexError:
-                if len(instring) > maxExcLoc:
-                    maxException = ParseException(instring,len(instring),e.errmsg,self)
-                    maxExcLoc = len(instring)
-
-        # only got here if no expression matched, raise exception for match that made it the furthest
-        else:
-            if maxException is not None:
-                maxException.msg = self.errmsg
-                raise maxException
-            else:
-                raise ParseException(instring, loc, "no defined alternatives to match", self)
-
-    def __ior__(self, other ):
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        return self.append( other ) #MatchFirst( [ self, other ] )
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}"
-
-        return self.strRepr
-
-    def checkRecursion( self, parseElementList ):
-        subRecCheckList = parseElementList[:] + [ self ]
-        for e in self.exprs:
-            e.checkRecursion( subRecCheckList )
-
-
-class Each(ParseExpression):
-    """
-    Requires all given C{ParseExpression}s to be found, but in any order.
-    Expressions may be separated by whitespace.
-    May be constructed using the C{'&'} operator.
-
-    Example::
-        color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN")
-        shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON")
-        integer = Word(nums)
-        shape_attr = "shape:" + shape_type("shape")
-        posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn")
-        color_attr = "color:" + color("color")
-        size_attr = "size:" + integer("size")
-
-        # use Each (using operator '&') to accept attributes in any order 
-        # (shape and posn are required, color and size are optional)
-        shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr)
-
-        shape_spec.runTests('''
-            shape: SQUARE color: BLACK posn: 100, 120
-            shape: CIRCLE size: 50 color: BLUE posn: 50,80
-            color:GREEN size:20 shape:TRIANGLE posn:20,40
-            '''
-            )
-    prints::
-        shape: SQUARE color: BLACK posn: 100, 120
-        ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']]
-        - color: BLACK
-        - posn: ['100', ',', '120']
-          - x: 100
-          - y: 120
-        - shape: SQUARE
-
-
-        shape: CIRCLE size: 50 color: BLUE posn: 50,80
-        ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']]
-        - color: BLUE
-        - posn: ['50', ',', '80']
-          - x: 50
-          - y: 80
-        - shape: CIRCLE
-        - size: 50
-
-
-        color: GREEN size: 20 shape: TRIANGLE posn: 20,40
-        ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']]
-        - color: GREEN
-        - posn: ['20', ',', '40']
-          - x: 20
-          - y: 40
-        - shape: TRIANGLE
-        - size: 20
-    """
-    def __init__( self, exprs, savelist = True ):
-        super(Each,self).__init__(exprs, savelist)
-        self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
-        self.skipWhitespace = True
-        self.initExprGroups = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.initExprGroups:
-            self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional))
-            opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ]
-            opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)]
-            self.optionals = opt1 + opt2
-            self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ]
-            self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ]
-            self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ]
-            self.required += self.multirequired
-            self.initExprGroups = False
-        tmpLoc = loc
-        tmpReqd = self.required[:]
-        tmpOpt  = self.optionals[:]
-        matchOrder = []
-
-        keepMatching = True
-        while keepMatching:
-            tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired
-            failed = []
-            for e in tmpExprs:
-                try:
-                    tmpLoc = e.tryParse( instring, tmpLoc )
-                except ParseException:
-                    failed.append(e)
-                else:
-                    matchOrder.append(self.opt1map.get(id(e),e))
-                    if e in tmpReqd:
-                        tmpReqd.remove(e)
-                    elif e in tmpOpt:
-                        tmpOpt.remove(e)
-            if len(failed) == len(tmpExprs):
-                keepMatching = False
-
-        if tmpReqd:
-            missing = ", ".join(_ustr(e) for e in tmpReqd)
-            raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing )
-
-        # add any unmatched Optionals, in case they have default values defined
-        matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt]
-
-        resultlist = []
-        for e in matchOrder:
-            loc,results = e._parse(instring,loc,doActions)
-            resultlist.append(results)
-
-        finalResults = sum(resultlist, ParseResults([]))
-        return loc, finalResults
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}"
-
-        return self.strRepr
-
-    def checkRecursion( self, parseElementList ):
-        subRecCheckList = parseElementList[:] + [ self ]
-        for e in self.exprs:
-            e.checkRecursion( subRecCheckList )
-
-
-class ParseElementEnhance(ParserElement):
-    """
-    Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens.
-    """
-    def __init__( self, expr, savelist=False ):
-        super(ParseElementEnhance,self).__init__(savelist)
-        if isinstance( expr, basestring ):
-            if issubclass(ParserElement._literalStringClass, Token):
-                expr = ParserElement._literalStringClass(expr)
-            else:
-                expr = ParserElement._literalStringClass(Literal(expr))
-        self.expr = expr
-        self.strRepr = None
-        if expr is not None:
-            self.mayIndexError = expr.mayIndexError
-            self.mayReturnEmpty = expr.mayReturnEmpty
-            self.setWhitespaceChars( expr.whiteChars )
-            self.skipWhitespace = expr.skipWhitespace
-            self.saveAsList = expr.saveAsList
-            self.callPreparse = expr.callPreparse
-            self.ignoreExprs.extend(expr.ignoreExprs)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.expr is not None:
-            return self.expr._parse( instring, loc, doActions, callPreParse=False )
-        else:
-            raise ParseException("",loc,self.errmsg,self)
-
-    def leaveWhitespace( self ):
-        self.skipWhitespace = False
-        self.expr = self.expr.copy()
-        if self.expr is not None:
-            self.expr.leaveWhitespace()
-        return self
-
-    def ignore( self, other ):
-        if isinstance( other, Suppress ):
-            if other not in self.ignoreExprs:
-                super( ParseElementEnhance, self).ignore( other )
-                if self.expr is not None:
-                    self.expr.ignore( self.ignoreExprs[-1] )
-        else:
-            super( ParseElementEnhance, self).ignore( other )
-            if self.expr is not None:
-                self.expr.ignore( self.ignoreExprs[-1] )
-        return self
-
-    def streamline( self ):
-        super(ParseElementEnhance,self).streamline()
-        if self.expr is not None:
-            self.expr.streamline()
-        return self
-
-    def checkRecursion( self, parseElementList ):
-        if self in parseElementList:
-            raise RecursiveGrammarException( parseElementList+[self] )
-        subRecCheckList = parseElementList[:] + [ self ]
-        if self.expr is not None:
-            self.expr.checkRecursion( subRecCheckList )
-
-    def validate( self, validateTrace=[] ):
-        tmp = validateTrace[:]+[self]
-        if self.expr is not None:
-            self.expr.validate(tmp)
-        self.checkRecursion( [] )
-
-    def __str__( self ):
-        try:
-            return super(ParseElementEnhance,self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None and self.expr is not None:
-            self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) )
-        return self.strRepr
-
-
-class FollowedBy(ParseElementEnhance):
-    """
-    Lookahead matching of the given parse expression.  C{FollowedBy}
-    does I{not} advance the parsing position within the input string, it only
-    verifies that the specified parse expression matches at the current
-    position.  C{FollowedBy} always returns a null token list.
-
-    Example::
-        # use FollowedBy to match a label only if it is followed by a ':'
-        data_word = Word(alphas)
-        label = data_word + FollowedBy(':')
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        
-        OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint()
-    prints::
-        [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']]
-    """
-    def __init__( self, expr ):
-        super(FollowedBy,self).__init__(expr)
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        self.expr.tryParse( instring, loc )
-        return loc, []
-
-
-class NotAny(ParseElementEnhance):
-    """
-    Lookahead to disallow matching with the given parse expression.  C{NotAny}
-    does I{not} advance the parsing position within the input string, it only
-    verifies that the specified parse expression does I{not} match at the current
-    position.  Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny}
-    always returns a null token list.  May be constructed using the '~' operator.
-
-    Example::
-        
-    """
-    def __init__( self, expr ):
-        super(NotAny,self).__init__(expr)
-        #~ self.leaveWhitespace()
-        self.skipWhitespace = False  # do NOT use self.leaveWhitespace(), don't want to propagate to exprs
-        self.mayReturnEmpty = True
-        self.errmsg = "Found unwanted token, "+_ustr(self.expr)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.expr.canParseNext(instring, loc):
-            raise ParseException(instring, loc, self.errmsg, self)
-        return loc, []
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "~{" + _ustr(self.expr) + "}"
-
-        return self.strRepr
-
-class _MultipleMatch(ParseElementEnhance):
-    def __init__( self, expr, stopOn=None):
-        super(_MultipleMatch, self).__init__(expr)
-        self.saveAsList = True
-        ender = stopOn
-        if isinstance(ender, basestring):
-            ender = ParserElement._literalStringClass(ender)
-        self.not_ender = ~ender if ender is not None else None
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        self_expr_parse = self.expr._parse
-        self_skip_ignorables = self._skipIgnorables
-        check_ender = self.not_ender is not None
-        if check_ender:
-            try_not_ender = self.not_ender.tryParse
-        
-        # must be at least one (but first see if we are the stopOn sentinel;
-        # if so, fail)
-        if check_ender:
-            try_not_ender(instring, loc)
-        loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False )
-        try:
-            hasIgnoreExprs = (not not self.ignoreExprs)
-            while 1:
-                if check_ender:
-                    try_not_ender(instring, loc)
-                if hasIgnoreExprs:
-                    preloc = self_skip_ignorables( instring, loc )
-                else:
-                    preloc = loc
-                loc, tmptokens = self_expr_parse( instring, preloc, doActions )
-                if tmptokens or tmptokens.haskeys():
-                    tokens += tmptokens
-        except (ParseException,IndexError):
-            pass
-
-        return loc, tokens
-        
-class OneOrMore(_MultipleMatch):
-    """
-    Repetition of one or more of the given expression.
-    
-    Parameters:
-     - expr - expression that must match one or more times
-     - stopOn - (default=C{None}) - expression for a terminating sentinel
-          (only required if the sentinel would ordinarily match the repetition 
-          expression)          
-
-    Example::
-        data_word = Word(alphas)
-        label = data_word + FollowedBy(':')
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
-
-        text = "shape: SQUARE posn: upper left color: BLACK"
-        OneOrMore(attr_expr).parseString(text).pprint()  # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']]
-
-        # use stopOn attribute for OneOrMore to avoid reading label string as part of the data
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']]
-        
-        # could also be written as
-        (attr_expr * (1,)).parseString(text).pprint()
-    """
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + _ustr(self.expr) + "}..."
-
-        return self.strRepr
-
-class ZeroOrMore(_MultipleMatch):
-    """
-    Optional repetition of zero or more of the given expression.
-    
-    Parameters:
-     - expr - expression that must match zero or more times
-     - stopOn - (default=C{None}) - expression for a terminating sentinel
-          (only required if the sentinel would ordinarily match the repetition 
-          expression)          
-
-    Example: similar to L{OneOrMore}
-    """
-    def __init__( self, expr, stopOn=None):
-        super(ZeroOrMore,self).__init__(expr, stopOn=stopOn)
-        self.mayReturnEmpty = True
-        
-    def parseImpl( self, instring, loc, doActions=True ):
-        try:
-            return super(ZeroOrMore, self).parseImpl(instring, loc, doActions)
-        except (ParseException,IndexError):
-            return loc, []
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "[" + _ustr(self.expr) + "]..."
-
-        return self.strRepr
-
-class _NullToken(object):
-    def __bool__(self):
-        return False
-    __nonzero__ = __bool__
-    def __str__(self):
-        return ""
-
-_optionalNotMatched = _NullToken()
-class Optional(ParseElementEnhance):
-    """
-    Optional matching of the given expression.
-
-    Parameters:
-     - expr - expression that must match zero or more times
-     - default (optional) - value to be returned if the optional expression is not found.
-
-    Example::
-        # US postal code can be a 5-digit zip, plus optional 4-digit qualifier
-        zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4)))
-        zip.runTests('''
-            # traditional ZIP code
-            12345
-            
-            # ZIP+4 form
-            12101-0001
-            
-            # invalid ZIP
-            98765-
-            ''')
-    prints::
-        # traditional ZIP code
-        12345
-        ['12345']
-
-        # ZIP+4 form
-        12101-0001
-        ['12101-0001']
-
-        # invalid ZIP
-        98765-
-             ^
-        FAIL: Expected end of text (at char 5), (line:1, col:6)
-    """
-    def __init__( self, expr, default=_optionalNotMatched ):
-        super(Optional,self).__init__( expr, savelist=False )
-        self.saveAsList = self.expr.saveAsList
-        self.defaultValue = default
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        try:
-            loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False )
-        except (ParseException,IndexError):
-            if self.defaultValue is not _optionalNotMatched:
-                if self.expr.resultsName:
-                    tokens = ParseResults([ self.defaultValue ])
-                    tokens[self.expr.resultsName] = self.defaultValue
-                else:
-                    tokens = [ self.defaultValue ]
-            else:
-                tokens = []
-        return loc, tokens
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "[" + _ustr(self.expr) + "]"
-
-        return self.strRepr
-
-class SkipTo(ParseElementEnhance):
-    """
-    Token for skipping over all undefined text until the matched expression is found.
-
-    Parameters:
-     - expr - target expression marking the end of the data to be skipped
-     - include - (default=C{False}) if True, the target expression is also parsed 
-          (the skipped text and target expression are returned as a 2-element list).
-     - ignore - (default=C{None}) used to define grammars (typically quoted strings and 
-          comments) that might contain false matches to the target expression
-     - failOn - (default=C{None}) define expressions that are not allowed to be 
-          included in the skipped test; if found before the target expression is found, 
-          the SkipTo is not a match
-
-    Example::
-        report = '''
-            Outstanding Issues Report - 1 Jan 2000
-
-               # | Severity | Description                               |  Days Open
-            -----+----------+-------------------------------------------+-----------
-             101 | Critical | Intermittent system crash                 |          6
-              94 | Cosmetic | Spelling error on Login ('log|n')         |         14
-              79 | Minor    | System slow when running too many reports |         47
-            '''
-        integer = Word(nums)
-        SEP = Suppress('|')
-        # use SkipTo to simply match everything up until the next SEP
-        # - ignore quoted strings, so that a '|' character inside a quoted string does not match
-        # - parse action will call token.strip() for each matched token, i.e., the description body
-        string_data = SkipTo(SEP, ignore=quotedString)
-        string_data.setParseAction(tokenMap(str.strip))
-        ticket_expr = (integer("issue_num") + SEP 
-                      + string_data("sev") + SEP 
-                      + string_data("desc") + SEP 
-                      + integer("days_open"))
-        
-        for tkt in ticket_expr.searchString(report):
-            print tkt.dump()
-    prints::
-        ['101', 'Critical', 'Intermittent system crash', '6']
-        - days_open: 6
-        - desc: Intermittent system crash
-        - issue_num: 101
-        - sev: Critical
-        ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14']
-        - days_open: 14
-        - desc: Spelling error on Login ('log|n')
-        - issue_num: 94
-        - sev: Cosmetic
-        ['79', 'Minor', 'System slow when running too many reports', '47']
-        - days_open: 47
-        - desc: System slow when running too many reports
-        - issue_num: 79
-        - sev: Minor
-    """
-    def __init__( self, other, include=False, ignore=None, failOn=None ):
-        super( SkipTo, self ).__init__( other )
-        self.ignoreExpr = ignore
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-        self.includeMatch = include
-        self.asList = False
-        if isinstance(failOn, basestring):
-            self.failOn = ParserElement._literalStringClass(failOn)
-        else:
-            self.failOn = failOn
-        self.errmsg = "No match found for "+_ustr(self.expr)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        startloc = loc
-        instrlen = len(instring)
-        expr = self.expr
-        expr_parse = self.expr._parse
-        self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None
-        self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None
-        
-        tmploc = loc
-        while tmploc <= instrlen:
-            if self_failOn_canParseNext is not None:
-                # break if failOn expression matches
-                if self_failOn_canParseNext(instring, tmploc):
-                    break
-                    
-            if self_ignoreExpr_tryParse is not None:
-                # advance past ignore expressions
-                while 1:
-                    try:
-                        tmploc = self_ignoreExpr_tryParse(instring, tmploc)
-                    except ParseBaseException:
-                        break
-            
-            try:
-                expr_parse(instring, tmploc, doActions=False, callPreParse=False)
-            except (ParseException, IndexError):
-                # no match, advance loc in string
-                tmploc += 1
-            else:
-                # matched skipto expr, done
-                break
-
-        else:
-            # ran off the end of the input string without matching skipto expr, fail
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        # build up return values
-        loc = tmploc
-        skiptext = instring[startloc:loc]
-        skipresult = ParseResults(skiptext)
-        
-        if self.includeMatch:
-            loc, mat = expr_parse(instring,loc,doActions,callPreParse=False)
-            skipresult += mat
-
-        return loc, skipresult
-
-class Forward(ParseElementEnhance):
-    """
-    Forward declaration of an expression to be defined later -
-    used for recursive grammars, such as algebraic infix notation.
-    When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator.
-
-    Note: take care when assigning to C{Forward} not to overlook precedence of operators.
-    Specifically, '|' has a lower precedence than '<<', so that::
-        fwdExpr << a | b | c
-    will actually be evaluated as::
-        (fwdExpr << a) | b | c
-    thereby leaving b and c out as parseable alternatives.  It is recommended that you
-    explicitly group the values inserted into the C{Forward}::
-        fwdExpr << (a | b | c)
-    Converting to use the '<<=' operator instead will avoid this problem.
-
-    See L{ParseResults.pprint} for an example of a recursive parser created using
-    C{Forward}.
-    """
-    def __init__( self, other=None ):
-        super(Forward,self).__init__( other, savelist=False )
-
-    def __lshift__( self, other ):
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass(other)
-        self.expr = other
-        self.strRepr = None
-        self.mayIndexError = self.expr.mayIndexError
-        self.mayReturnEmpty = self.expr.mayReturnEmpty
-        self.setWhitespaceChars( self.expr.whiteChars )
-        self.skipWhitespace = self.expr.skipWhitespace
-        self.saveAsList = self.expr.saveAsList
-        self.ignoreExprs.extend(self.expr.ignoreExprs)
-        return self
-        
-    def __ilshift__(self, other):
-        return self << other
-    
-    def leaveWhitespace( self ):
-        self.skipWhitespace = False
-        return self
-
-    def streamline( self ):
-        if not self.streamlined:
-            self.streamlined = True
-            if self.expr is not None:
-                self.expr.streamline()
-        return self
-
-    def validate( self, validateTrace=[] ):
-        if self not in validateTrace:
-            tmp = validateTrace[:]+[self]
-            if self.expr is not None:
-                self.expr.validate(tmp)
-        self.checkRecursion([])
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-        return self.__class__.__name__ + ": ..."
-
-        # stubbed out for now - creates awful memory and perf issues
-        self._revertClass = self.__class__
-        self.__class__ = _ForwardNoRecurse
-        try:
-            if self.expr is not None:
-                retString = _ustr(self.expr)
-            else:
-                retString = "None"
-        finally:
-            self.__class__ = self._revertClass
-        return self.__class__.__name__ + ": " + retString
-
-    def copy(self):
-        if self.expr is not None:
-            return super(Forward,self).copy()
-        else:
-            ret = Forward()
-            ret <<= self
-            return ret
-
-class _ForwardNoRecurse(Forward):
-    def __str__( self ):
-        return "..."
-
-class TokenConverter(ParseElementEnhance):
-    """
-    Abstract subclass of C{ParseExpression}, for converting parsed results.
-    """
-    def __init__( self, expr, savelist=False ):
-        super(TokenConverter,self).__init__( expr )#, savelist )
-        self.saveAsList = False
-
-class Combine(TokenConverter):
-    """
-    Converter to concatenate all matching tokens to a single string.
-    By default, the matching patterns must also be contiguous in the input string;
-    this can be disabled by specifying C{'adjacent=False'} in the constructor.
-
-    Example::
-        real = Word(nums) + '.' + Word(nums)
-        print(real.parseString('3.1416')) # -> ['3', '.', '1416']
-        # will also erroneously match the following
-        print(real.parseString('3. 1416')) # -> ['3', '.', '1416']
-
-        real = Combine(Word(nums) + '.' + Word(nums))
-        print(real.parseString('3.1416')) # -> ['3.1416']
-        # no match when there are internal spaces
-        print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...)
-    """
-    def __init__( self, expr, joinString="", adjacent=True ):
-        super(Combine,self).__init__( expr )
-        # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself
-        if adjacent:
-            self.leaveWhitespace()
-        self.adjacent = adjacent
-        self.skipWhitespace = True
-        self.joinString = joinString
-        self.callPreparse = True
-
-    def ignore( self, other ):
-        if self.adjacent:
-            ParserElement.ignore(self, other)
-        else:
-            super( Combine, self).ignore( other )
-        return self
-
-    def postParse( self, instring, loc, tokenlist ):
-        retToks = tokenlist.copy()
-        del retToks[:]
-        retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults)
-
-        if self.resultsName and retToks.haskeys():
-            return [ retToks ]
-        else:
-            return retToks
-
-class Group(TokenConverter):
-    """
-    Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions.
-
-    Example::
-        ident = Word(alphas)
-        num = Word(nums)
-        term = ident | num
-        func = ident + Optional(delimitedList(term))
-        print(func.parseString("fn a,b,100"))  # -> ['fn', 'a', 'b', '100']
-
-        func = ident + Group(Optional(delimitedList(term)))
-        print(func.parseString("fn a,b,100"))  # -> ['fn', ['a', 'b', '100']]
-    """
-    def __init__( self, expr ):
-        super(Group,self).__init__( expr )
-        self.saveAsList = True
-
-    def postParse( self, instring, loc, tokenlist ):
-        return [ tokenlist ]
-
-class Dict(TokenConverter):
-    """
-    Converter to return a repetitive expression as a list, but also as a dictionary.
-    Each element can also be referenced using the first token in the expression as its key.
-    Useful for tabular report scraping when the first column can be used as a item key.
-
-    Example::
-        data_word = Word(alphas)
-        label = data_word + FollowedBy(':')
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
-
-        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
-        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        
-        # print attributes as plain groups
-        print(OneOrMore(attr_expr).parseString(text).dump())
-        
-        # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names
-        result = Dict(OneOrMore(Group(attr_expr))).parseString(text)
-        print(result.dump())
-        
-        # access named fields as dict entries, or output as dict
-        print(result['shape'])        
-        print(result.asDict())
-    prints::
-        ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap']
-
-        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
-        - color: light blue
-        - posn: upper left
-        - shape: SQUARE
-        - texture: burlap
-        SQUARE
-        {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'}
-    See more examples at L{ParseResults} of accessing fields by results name.
-    """
-    def __init__( self, expr ):
-        super(Dict,self).__init__( expr )
-        self.saveAsList = True
-
-    def postParse( self, instring, loc, tokenlist ):
-        for i,tok in enumerate(tokenlist):
-            if len(tok) == 0:
-                continue
-            ikey = tok[0]
-            if isinstance(ikey,int):
-                ikey = _ustr(tok[0]).strip()
-            if len(tok)==1:
-                tokenlist[ikey] = _ParseResultsWithOffset("",i)
-            elif len(tok)==2 and not isinstance(tok[1],ParseResults):
-                tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i)
-            else:
-                dictvalue = tok.copy() #ParseResults(i)
-                del dictvalue[0]
-                if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()):
-                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i)
-                else:
-                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i)
-
-        if self.resultsName:
-            return [ tokenlist ]
-        else:
-            return tokenlist
-
-
-class Suppress(TokenConverter):
-    """
-    Converter for ignoring the results of a parsed expression.
-
-    Example::
-        source = "a, b, c,d"
-        wd = Word(alphas)
-        wd_list1 = wd + ZeroOrMore(',' + wd)
-        print(wd_list1.parseString(source))
-
-        # often, delimiters that are useful during parsing are just in the
-        # way afterward - use Suppress to keep them out of the parsed output
-        wd_list2 = wd + ZeroOrMore(Suppress(',') + wd)
-        print(wd_list2.parseString(source))
-    prints::
-        ['a', ',', 'b', ',', 'c', ',', 'd']
-        ['a', 'b', 'c', 'd']
-    (See also L{delimitedList}.)
-    """
-    def postParse( self, instring, loc, tokenlist ):
-        return []
-
-    def suppress( self ):
-        return self
-
-
-class OnlyOnce(object):
-    """
-    Wrapper for parse actions, to ensure they are only called once.
-    """
-    def __init__(self, methodCall):
-        self.callable = _trim_arity(methodCall)
-        self.called = False
-    def __call__(self,s,l,t):
-        if not self.called:
-            results = self.callable(s,l,t)
-            self.called = True
-            return results
-        raise ParseException(s,l,"")
-    def reset(self):
-        self.called = False
-
-def traceParseAction(f):
-    """
-    Decorator for debugging parse actions. 
-    
-    When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".}
-    When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised.
-
-    Example::
-        wd = Word(alphas)
-
-        @traceParseAction
-        def remove_duplicate_chars(tokens):
-            return ''.join(sorted(set(''.join(tokens))))
-
-        wds = OneOrMore(wd).setParseAction(remove_duplicate_chars)
-        print(wds.parseString("slkdjs sld sldd sdlf sdljf"))
-    prints::
-        >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {}))
-        <3:
-            thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc
-        sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\n" % (thisFunc,line(l,s),l,t) )
-        try:
-            ret = f(*paArgs)
-        except Exception as exc:
-            sys.stderr.write( "< ['aa', 'bb', 'cc']
-        delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE']
-    """
-    dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..."
-    if combine:
-        return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName)
-    else:
-        return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName)
-
-def countedArray( expr, intExpr=None ):
-    """
-    Helper to define a counted list of expressions.
-    This helper defines a pattern of the form::
-        integer expr expr expr...
-    where the leading integer tells how many expr expressions follow.
-    The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed.
-    
-    If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value.
-
-    Example::
-        countedArray(Word(alphas)).parseString('2 ab cd ef')  # -> ['ab', 'cd']
-
-        # in this parser, the leading integer value is given in binary,
-        # '10' indicating that 2 values are in the array
-        binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2))
-        countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef')  # -> ['ab', 'cd']
-    """
-    arrayExpr = Forward()
-    def countFieldParseAction(s,l,t):
-        n = t[0]
-        arrayExpr << (n and Group(And([expr]*n)) or Group(empty))
-        return []
-    if intExpr is None:
-        intExpr = Word(nums).setParseAction(lambda t:int(t[0]))
-    else:
-        intExpr = intExpr.copy()
-    intExpr.setName("arrayLen")
-    intExpr.addParseAction(countFieldParseAction, callDuringTry=True)
-    return ( intExpr + arrayExpr ).setName('(len) ' + _ustr(expr) + '...')
-
-def _flatten(L):
-    ret = []
-    for i in L:
-        if isinstance(i,list):
-            ret.extend(_flatten(i))
-        else:
-            ret.append(i)
-    return ret
-
-def matchPreviousLiteral(expr):
-    """
-    Helper to define an expression that is indirectly defined from
-    the tokens matched in a previous expression, that is, it looks
-    for a 'repeat' of a previous expression.  For example::
-        first = Word(nums)
-        second = matchPreviousLiteral(first)
-        matchExpr = first + ":" + second
-    will match C{"1:1"}, but not C{"1:2"}.  Because this matches a
-    previous literal, will also match the leading C{"1:1"} in C{"1:10"}.
-    If this is not desired, use C{matchPreviousExpr}.
-    Do I{not} use with packrat parsing enabled.
-    """
-    rep = Forward()
-    def copyTokenToRepeater(s,l,t):
-        if t:
-            if len(t) == 1:
-                rep << t[0]
-            else:
-                # flatten t tokens
-                tflat = _flatten(t.asList())
-                rep << And(Literal(tt) for tt in tflat)
-        else:
-            rep << Empty()
-    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
-    rep.setName('(prev) ' + _ustr(expr))
-    return rep
-
-def matchPreviousExpr(expr):
-    """
-    Helper to define an expression that is indirectly defined from
-    the tokens matched in a previous expression, that is, it looks
-    for a 'repeat' of a previous expression.  For example::
-        first = Word(nums)
-        second = matchPreviousExpr(first)
-        matchExpr = first + ":" + second
-    will match C{"1:1"}, but not C{"1:2"}.  Because this matches by
-    expressions, will I{not} match the leading C{"1:1"} in C{"1:10"};
-    the expressions are evaluated first, and then compared, so
-    C{"1"} is compared with C{"10"}.
-    Do I{not} use with packrat parsing enabled.
-    """
-    rep = Forward()
-    e2 = expr.copy()
-    rep <<= e2
-    def copyTokenToRepeater(s,l,t):
-        matchTokens = _flatten(t.asList())
-        def mustMatchTheseTokens(s,l,t):
-            theseTokens = _flatten(t.asList())
-            if  theseTokens != matchTokens:
-                raise ParseException("",0,"")
-        rep.setParseAction( mustMatchTheseTokens, callDuringTry=True )
-    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
-    rep.setName('(prev) ' + _ustr(expr))
-    return rep
-
-def _escapeRegexRangeChars(s):
-    #~  escape these chars: ^-]
-    for c in r"\^-]":
-        s = s.replace(c,_bslash+c)
-    s = s.replace("\n",r"\n")
-    s = s.replace("\t",r"\t")
-    return _ustr(s)
-
-def oneOf( strs, caseless=False, useRegex=True ):
-    """
-    Helper to quickly define a set of alternative Literals, and makes sure to do
-    longest-first testing when there is a conflict, regardless of the input order,
-    but returns a C{L{MatchFirst}} for best performance.
-
-    Parameters:
-     - strs - a string of space-delimited literals, or a collection of string literals
-     - caseless - (default=C{False}) - treat all literals as caseless
-     - useRegex - (default=C{True}) - as an optimization, will generate a Regex
-          object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or
-          if creating a C{Regex} raises an exception)
-
-    Example::
-        comp_oper = oneOf("< = > <= >= !=")
-        var = Word(alphas)
-        number = Word(nums)
-        term = var | number
-        comparison_expr = term + comp_oper + term
-        print(comparison_expr.searchString("B = 12  AA=23 B<=AA AA>12"))
-    prints::
-        [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']]
-    """
-    if caseless:
-        isequal = ( lambda a,b: a.upper() == b.upper() )
-        masks = ( lambda a,b: b.upper().startswith(a.upper()) )
-        parseElementClass = CaselessLiteral
-    else:
-        isequal = ( lambda a,b: a == b )
-        masks = ( lambda a,b: b.startswith(a) )
-        parseElementClass = Literal
-
-    symbols = []
-    if isinstance(strs,basestring):
-        symbols = strs.split()
-    elif isinstance(strs, Iterable):
-        symbols = list(strs)
-    else:
-        warnings.warn("Invalid argument to oneOf, expected string or iterable",
-                SyntaxWarning, stacklevel=2)
-    if not symbols:
-        return NoMatch()
-
-    i = 0
-    while i < len(symbols)-1:
-        cur = symbols[i]
-        for j,other in enumerate(symbols[i+1:]):
-            if ( isequal(other, cur) ):
-                del symbols[i+j+1]
-                break
-            elif ( masks(cur, other) ):
-                del symbols[i+j+1]
-                symbols.insert(i,other)
-                cur = other
-                break
-        else:
-            i += 1
-
-    if not caseless and useRegex:
-        #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] ))
-        try:
-            if len(symbols)==len("".join(symbols)):
-                return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols))
-            else:
-                return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols))
-        except Exception:
-            warnings.warn("Exception creating Regex for oneOf, building MatchFirst",
-                    SyntaxWarning, stacklevel=2)
-
-
-    # last resort, just use MatchFirst
-    return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols))
-
-def dictOf( key, value ):
-    """
-    Helper to easily and clearly define a dictionary by specifying the respective patterns
-    for the key and value.  Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens
-    in the proper order.  The key pattern can include delimiting markers or punctuation,
-    as long as they are suppressed, thereby leaving the significant key text.  The value
-    pattern can include named results, so that the C{Dict} results can include named token
-    fields.
-
-    Example::
-        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
-        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        print(OneOrMore(attr_expr).parseString(text).dump())
-        
-        attr_label = label
-        attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)
-
-        # similar to Dict, but simpler call format
-        result = dictOf(attr_label, attr_value).parseString(text)
-        print(result.dump())
-        print(result['shape'])
-        print(result.shape)  # object attribute access works too
-        print(result.asDict())
-    prints::
-        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
-        - color: light blue
-        - posn: upper left
-        - shape: SQUARE
-        - texture: burlap
-        SQUARE
-        SQUARE
-        {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'}
-    """
-    return Dict( ZeroOrMore( Group ( key + value ) ) )
-
-def originalTextFor(expr, asString=True):
-    """
-    Helper to return the original, untokenized text for a given expression.  Useful to
-    restore the parsed fields of an HTML start tag into the raw tag text itself, or to
-    revert separate tokens with intervening whitespace back to the original matching
-    input text. By default, returns astring containing the original parsed text.  
-       
-    If the optional C{asString} argument is passed as C{False}, then the return value is a 
-    C{L{ParseResults}} containing any results names that were originally matched, and a 
-    single token containing the original matched text from the input string.  So if 
-    the expression passed to C{L{originalTextFor}} contains expressions with defined
-    results names, you must set C{asString} to C{False} if you want to preserve those
-    results name values.
-
-    Example::
-        src = "this is test  bold text  normal text "
-        for tag in ("b","i"):
-            opener,closer = makeHTMLTags(tag)
-            patt = originalTextFor(opener + SkipTo(closer) + closer)
-            print(patt.searchString(src)[0])
-    prints::
-        [' bold text ']
-        ['text']
-    """
-    locMarker = Empty().setParseAction(lambda s,loc,t: loc)
-    endlocMarker = locMarker.copy()
-    endlocMarker.callPreparse = False
-    matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end")
-    if asString:
-        extractText = lambda s,l,t: s[t._original_start:t._original_end]
-    else:
-        def extractText(s,l,t):
-            t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]]
-    matchExpr.setParseAction(extractText)
-    matchExpr.ignoreExprs = expr.ignoreExprs
-    return matchExpr
-
-def ungroup(expr): 
-    """
-    Helper to undo pyparsing's default grouping of And expressions, even
-    if all but one are non-empty.
-    """
-    return TokenConverter(expr).setParseAction(lambda t:t[0])
-
-def locatedExpr(expr):
-    """
-    Helper to decorate a returned token with its starting and ending locations in the input string.
-    This helper adds the following results names:
-     - locn_start = location where matched expression begins
-     - locn_end = location where matched expression ends
-     - value = the actual parsed results
-
-    Be careful if the input text contains C{} characters, you may want to call
-    C{L{ParserElement.parseWithTabs}}
-
-    Example::
-        wd = Word(alphas)
-        for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"):
-            print(match)
-    prints::
-        [[0, 'ljsdf', 5]]
-        [[8, 'lksdjjf', 15]]
-        [[18, 'lkkjj', 23]]
-    """
-    locator = Empty().setParseAction(lambda s,l,t: l)
-    return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end"))
-
-
-# convenience constants for positional expressions
-empty       = Empty().setName("empty")
-lineStart   = LineStart().setName("lineStart")
-lineEnd     = LineEnd().setName("lineEnd")
-stringStart = StringStart().setName("stringStart")
-stringEnd   = StringEnd().setName("stringEnd")
-
-_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1])
-_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16)))
-_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8)))
-_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | CharsNotIn(r'\]', exact=1)
-_charRange = Group(_singleChar + Suppress("-") + _singleChar)
-_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]"
-
-def srange(s):
-    r"""
-    Helper to easily define string ranges for use in Word construction.  Borrows
-    syntax from regexp '[]' string range definitions::
-        srange("[0-9]")   -> "0123456789"
-        srange("[a-z]")   -> "abcdefghijklmnopqrstuvwxyz"
-        srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_"
-    The input string must be enclosed in []'s, and the returned string is the expanded
-    character set joined into a single string.
-    The values enclosed in the []'s may be:
-     - a single character
-     - an escaped character with a leading backslash (such as C{\-} or C{\]})
-     - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character) 
-         (C{\0x##} is also supported for backwards compatibility) 
-     - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character)
-     - a range of any of the above, separated by a dash (C{'a-z'}, etc.)
-     - any combination of the above (C{'aeiouy'}, C{'a-zA-Z0-9_$'}, etc.)
-    """
-    _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1))
-    try:
-        return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body)
-    except Exception:
-        return ""
-
-def matchOnlyAtCol(n):
-    """
-    Helper method for defining parse actions that require matching at a specific
-    column in the input text.
-    """
-    def verifyCol(strg,locn,toks):
-        if col(locn,strg) != n:
-            raise ParseException(strg,locn,"matched token not at column %d" % n)
-    return verifyCol
-
-def replaceWith(replStr):
-    """
-    Helper method for common parse actions that simply return a literal value.  Especially
-    useful when used with C{L{transformString}()}.
-
-    Example::
-        num = Word(nums).setParseAction(lambda toks: int(toks[0]))
-        na = oneOf("N/A NA").setParseAction(replaceWith(math.nan))
-        term = na | num
-        
-        OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234]
-    """
-    return lambda s,l,t: [replStr]
-
-def removeQuotes(s,l,t):
-    """
-    Helper parse action for removing quotation marks from parsed quoted strings.
-
-    Example::
-        # by default, quotation marks are included in parsed results
-        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"]
-
-        # use removeQuotes to strip quotation marks from parsed results
-        quotedString.setParseAction(removeQuotes)
-        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"]
-    """
-    return t[0][1:-1]
-
-def tokenMap(func, *args):
-    """
-    Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional 
-    args are passed, they are forwarded to the given function as additional arguments after
-    the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the
-    parsed data to an integer using base 16.
-
-    Example (compare the last to example in L{ParserElement.transformString}::
-        hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16))
-        hex_ints.runTests('''
-            00 11 22 aa FF 0a 0d 1a
-            ''')
-        
-        upperword = Word(alphas).setParseAction(tokenMap(str.upper))
-        OneOrMore(upperword).runTests('''
-            my kingdom for a horse
-            ''')
-
-        wd = Word(alphas).setParseAction(tokenMap(str.title))
-        OneOrMore(wd).setParseAction(' '.join).runTests('''
-            now is the winter of our discontent made glorious summer by this sun of york
-            ''')
-    prints::
-        00 11 22 aa FF 0a 0d 1a
-        [0, 17, 34, 170, 255, 10, 13, 26]
-
-        my kingdom for a horse
-        ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE']
-
-        now is the winter of our discontent made glorious summer by this sun of york
-        ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York']
-    """
-    def pa(s,l,t):
-        return [func(tokn, *args) for tokn in t]
-
-    try:
-        func_name = getattr(func, '__name__', 
-                            getattr(func, '__class__').__name__)
-    except Exception:
-        func_name = str(func)
-    pa.__name__ = func_name
-
-    return pa
-
-upcaseTokens = tokenMap(lambda t: _ustr(t).upper())
-"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}"""
-
-downcaseTokens = tokenMap(lambda t: _ustr(t).lower())
-"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}"""
-    
-def _makeTags(tagStr, xml):
-    """Internal helper to construct opening and closing tag expressions, given a tag name"""
-    if isinstance(tagStr,basestring):
-        resname = tagStr
-        tagStr = Keyword(tagStr, caseless=not xml)
-    else:
-        resname = tagStr.name
-
-    tagAttrName = Word(alphas,alphanums+"_-:")
-    if (xml):
-        tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes )
-        openTag = Suppress("<") + tagStr("tag") + \
-                Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \
-                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
-    else:
-        printablesLessRAbrack = "".join(c for c in printables if c not in ">")
-        tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack)
-        openTag = Suppress("<") + tagStr("tag") + \
-                Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \
-                Optional( Suppress("=") + tagAttrValue ) ))) + \
-                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
-    closeTag = Combine(_L("")
-
-    openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname)
-    closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % resname)
-    openTag.tag = resname
-    closeTag.tag = resname
-    return openTag, closeTag
-
-def makeHTMLTags(tagStr):
-    """
-    Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches
-    tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values.
-
-    Example::
-        text = 'More info at the pyparsing wiki page'
-        # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple
-        a,a_end = makeHTMLTags("A")
-        link_expr = a + SkipTo(a_end)("link_text") + a_end
-        
-        for link in link_expr.searchString(text):
-            # attributes in the  tag (like "href" shown here) are also accessible as named results
-            print(link.link_text, '->', link.href)
-    prints::
-        pyparsing -> http://pyparsing.wikispaces.com
-    """
-    return _makeTags( tagStr, False )
-
-def makeXMLTags(tagStr):
-    """
-    Helper to construct opening and closing tag expressions for XML, given a tag name. Matches
-    tags only in the given upper/lower case.
-
-    Example: similar to L{makeHTMLTags}
-    """
-    return _makeTags( tagStr, True )
-
-def withAttribute(*args,**attrDict):
-    """
-    Helper to create a validating parse action to be used with start tags created
-    with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag
-    with a required attribute value, to avoid false matches on common tags such as
-    C{} or C{
}. - - Call C{withAttribute} with a series of attribute names and values. Specify the list - of filter attributes names and values as: - - keyword arguments, as in C{(align="right")}, or - - as an explicit dict with C{**} operator, when an attribute name is also a Python - reserved word, as in C{**{"class":"Customer", "align":"right"}} - - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) - For attribute names with a namespace prefix, you must use the second form. Attribute - names are matched insensitive to upper/lower case. - - If just testing for C{class} (with or without a namespace), use C{L{withClass}}. - - To verify that the attribute exists, but without specifying a value, pass - C{withAttribute.ANY_VALUE} as the value. - - Example:: - html = ''' -
- Some text -
1 4 0 1 0
-
1,3 2,3 1,1
-
this has no type
-
- - ''' - div,div_end = makeHTMLTags("div") - - # only match div tag having a type attribute with value "grid" - div_grid = div().setParseAction(withAttribute(type="grid")) - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.searchString(html): - print(grid_header.body) - - # construct a match with any div tag having a type attribute, regardless of the value - div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.searchString(html): - print(div_header.body) - prints:: - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - if args: - attrs = args[:] - else: - attrs = attrDict.items() - attrs = [(k,v) for k,v in attrs] - def pa(s,l,tokens): - for attrName,attrValue in attrs: - if attrName not in tokens: - raise ParseException(s,l,"no matching attribute " + attrName) - if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: - raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % - (attrName, tokens[attrName], attrValue)) - return pa -withAttribute.ANY_VALUE = object() - -def withClass(classname, namespace=''): - """ - Simplified version of C{L{withAttribute}} when matching on a div class - made - difficult because C{class} is a reserved word in Python. - - Example:: - html = ''' -
- Some text -
1 4 0 1 0
-
1,3 2,3 1,1
-
this <div> has no class
-
- - ''' - div,div_end = makeHTMLTags("div") - div_grid = div().setParseAction(withClass("grid")) - - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.searchString(html): - print(grid_header.body) - - div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.searchString(html): - print(div_header.body) - prints:: - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - classattr = "%s:class" % namespace if namespace else "class" - return withAttribute(**{classattr : classname}) - -opAssoc = _Constants() -opAssoc.LEFT = object() -opAssoc.RIGHT = object() - -def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): - """ - Helper method for constructing grammars of expressions made up of - operators working in a precedence hierarchy. Operators may be unary or - binary, left- or right-associative. Parse actions can also be attached - to operator expressions. The generated parser will also recognize the use - of parentheses to override operator precedences (see example below). - - Note: if you define a deep operator list, you may see performance issues - when using infixNotation. See L{ParserElement.enablePackrat} for a - mechanism to potentially improve your parser performance. - - Parameters: - - baseExpr - expression representing the most basic element for the nested - - opList - list of tuples, one for each operator precedence level in the - expression grammar; each tuple is of the form - (opExpr, numTerms, rightLeftAssoc, parseAction), where: - - opExpr is the pyparsing expression for the operator; - may also be a string, which will be converted to a Literal; - if numTerms is 3, opExpr is a tuple of two expressions, for the - two operators separating the 3 terms - - numTerms is the number of terms for this operator (must - be 1, 2, or 3) - - rightLeftAssoc is the indicator whether the operator is - right or left associative, using the pyparsing-defined - constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. - - parseAction is the parse action to be associated with - expressions matching this operator expression (the - parse action tuple member may be omitted); if the parse action - is passed a tuple or list of functions, this is equivalent to - calling C{setParseAction(*fn)} (L{ParserElement.setParseAction}) - - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) - - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) - - Example:: - # simple example of four-function arithmetic with ints and variable names - integer = pyparsing_common.signed_integer - varname = pyparsing_common.identifier - - arith_expr = infixNotation(integer | varname, - [ - ('-', 1, opAssoc.RIGHT), - (oneOf('* /'), 2, opAssoc.LEFT), - (oneOf('+ -'), 2, opAssoc.LEFT), - ]) - - arith_expr.runTests(''' - 5+3*6 - (5+3)*6 - -2--11 - ''', fullDump=False) - prints:: - 5+3*6 - [[5, '+', [3, '*', 6]]] - - (5+3)*6 - [[[5, '+', 3], '*', 6]] - - -2--11 - [[['-', 2], '-', ['-', 11]]] - """ - ret = Forward() - lastExpr = baseExpr | ( lpar + ret + rpar ) - for i,operDef in enumerate(opList): - opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] - termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr - if arity == 3: - if opExpr is None or len(opExpr) != 2: - raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") - opExpr1, opExpr2 = opExpr - thisExpr = Forward().setName(termName) - if rightLeftAssoc == opAssoc.LEFT: - if arity == 1: - matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) - elif arity == 2: - if opExpr is not None: - matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) - else: - matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) - elif arity == 3: - matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ - Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) - else: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - elif rightLeftAssoc == opAssoc.RIGHT: - if arity == 1: - # try to avoid LR with this extra test - if not isinstance(opExpr, Optional): - opExpr = Optional(opExpr) - matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) - elif arity == 2: - if opExpr is not None: - matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) - else: - matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) - elif arity == 3: - matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ - Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) - else: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - else: - raise ValueError("operator must indicate right or left associativity") - if pa: - if isinstance(pa, (tuple, list)): - matchExpr.setParseAction(*pa) - else: - matchExpr.setParseAction(pa) - thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) - lastExpr = thisExpr - ret <<= lastExpr - return ret - -operatorPrecedence = infixNotation -"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" - -dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") -sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") -quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| - Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") -unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") - -def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): - """ - Helper method for defining nested lists enclosed in opening and closing - delimiters ("(" and ")" are the default). - - Parameters: - - opener - opening character for a nested list (default=C{"("}); can also be a pyparsing expression - - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression - - content - expression for items within the nested lists (default=C{None}) - - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{quotedString}) - - If an expression is not provided for the content argument, the nested - expression will capture all whitespace-delimited content between delimiters - as a list of separate values. - - Use the C{ignoreExpr} argument to define expressions that may contain - opening or closing characters that should not be treated as opening - or closing characters for nesting, such as quotedString or a comment - expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. - The default is L{quotedString}, but if no expressions are to be ignored, - then pass C{None} for this argument. - - Example:: - data_type = oneOf("void int short long char float double") - decl_data_type = Combine(data_type + Optional(Word('*'))) - ident = Word(alphas+'_', alphanums+'_') - number = pyparsing_common.number - arg = Group(decl_data_type + ident) - LPAR,RPAR = map(Suppress, "()") - - code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) - - c_function = (decl_data_type("type") - + ident("name") - + LPAR + Optional(delimitedList(arg), [])("args") + RPAR - + code_body("body")) - c_function.ignore(cStyleComment) - - source_code = ''' - int is_odd(int x) { - return (x%2); - } - - int dec_to_hex(char hchar) { - if (hchar >= '0' && hchar <= '9') { - return (ord(hchar)-ord('0')); - } else { - return (10+ord(hchar)-ord('A')); - } - } - ''' - for func in c_function.searchString(source_code): - print("%(name)s (%(type)s) args: %(args)s" % func) - - prints:: - is_odd (int) args: [['int', 'x']] - dec_to_hex (int) args: [['char', 'hchar']] - """ - if opener == closer: - raise ValueError("opening and closing strings cannot be the same") - if content is None: - if isinstance(opener,basestring) and isinstance(closer,basestring): - if len(opener) == 1 and len(closer)==1: - if ignoreExpr is not None: - content = (Combine(OneOrMore(~ignoreExpr + - CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS - ).setParseAction(lambda t:t[0].strip())) - else: - if ignoreExpr is not None: - content = (Combine(OneOrMore(~ignoreExpr + - ~Literal(opener) + ~Literal(closer) + - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - raise ValueError("opening and closing arguments must be strings if no content expression is given") - ret = Forward() - if ignoreExpr is not None: - ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) - else: - ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) - ret.setName('nested %s%s expression' % (opener,closer)) - return ret - -def indentedBlock(blockStatementExpr, indentStack, indent=True): - """ - Helper method for defining space-delimited indentation blocks, such as - those used to define block statements in Python source code. - - Parameters: - - blockStatementExpr - expression defining syntax of statement that - is repeated within the indented block - - indentStack - list created by caller to manage indentation stack - (multiple statementWithIndentedBlock expressions within a single grammar - should share a common indentStack) - - indent - boolean indicating whether block must be indented beyond the - the current level; set to False for block of left-most statements - (default=C{True}) - - A valid block must contain at least one C{blockStatement}. - - Example:: - data = ''' - def A(z): - A1 - B = 100 - G = A2 - A2 - A3 - B - def BB(a,b,c): - BB1 - def BBA(): - bba1 - bba2 - bba3 - C - D - def spam(x,y): - def eggs(z): - pass - ''' - - - indentStack = [1] - stmt = Forward() - - identifier = Word(alphas, alphanums) - funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") - func_body = indentedBlock(stmt, indentStack) - funcDef = Group( funcDecl + func_body ) - - rvalue = Forward() - funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") - rvalue << (funcCall | identifier | Word(nums)) - assignment = Group(identifier + "=" + rvalue) - stmt << ( funcDef | assignment | identifier ) - - module_body = OneOrMore(stmt) - - parseTree = module_body.parseString(data) - parseTree.pprint() - prints:: - [['def', - 'A', - ['(', 'z', ')'], - ':', - [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], - 'B', - ['def', - 'BB', - ['(', 'a', 'b', 'c', ')'], - ':', - [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], - 'C', - 'D', - ['def', - 'spam', - ['(', 'x', 'y', ')'], - ':', - [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] - """ - def checkPeerIndent(s,l,t): - if l >= len(s): return - curCol = col(l,s) - if curCol != indentStack[-1]: - if curCol > indentStack[-1]: - raise ParseFatalException(s,l,"illegal nesting") - raise ParseException(s,l,"not a peer entry") - - def checkSubIndent(s,l,t): - curCol = col(l,s) - if curCol > indentStack[-1]: - indentStack.append( curCol ) - else: - raise ParseException(s,l,"not a subentry") - - def checkUnindent(s,l,t): - if l >= len(s): return - curCol = col(l,s) - if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): - raise ParseException(s,l,"not an unindent") - indentStack.pop() - - NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) - INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') - PEER = Empty().setParseAction(checkPeerIndent).setName('') - UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') - if indent: - smExpr = Group( Optional(NL) + - #~ FollowedBy(blockStatementExpr) + - INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) - else: - smExpr = Group( Optional(NL) + - (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) - blockStatementExpr.ignore(_bslash + LineEnd()) - return smExpr.setName('indented block') - -alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") -punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") - -anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) -_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) -commonHTMLEntity = Regex('&(?P' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") -def replaceHTMLEntity(t): - """Helper parser action to replace common HTML entities with their special characters""" - return _htmlEntityMap.get(t.entity) - -# it's easy to get these comment structures wrong - they're very common, so may as well make them available -cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") -"Comment of the form C{/* ... */}" - -htmlComment = Regex(r"").setName("HTML comment") -"Comment of the form C{}" - -restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") -dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") -"Comment of the form C{// ... (to end of line)}" - -cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") -"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" - -javaStyleComment = cppStyleComment -"Same as C{L{cppStyleComment}}" - -pythonStyleComment = Regex(r"#.*").setName("Python style comment") -"Comment of the form C{# ... (to end of line)}" - -_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + - Optional( Word(" \t") + - ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") -commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") -"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. - This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" - -# some other useful expressions - using lower-case class name since we are really using this as a namespace -class pyparsing_common: - """ - Here are some common low-level expressions that may be useful in jump-starting parser development: - - numeric forms (L{integers}, L{reals}, L{scientific notation}) - - common L{programming identifiers} - - network addresses (L{MAC}, L{IPv4}, L{IPv6}) - - ISO8601 L{dates} and L{datetime} - - L{UUID} - - L{comma-separated list} - Parse actions: - - C{L{convertToInteger}} - - C{L{convertToFloat}} - - C{L{convertToDate}} - - C{L{convertToDatetime}} - - C{L{stripHTMLTags}} - - C{L{upcaseTokens}} - - C{L{downcaseTokens}} - - Example:: - pyparsing_common.number.runTests(''' - # any int or real number, returned as the appropriate type - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.fnumber.runTests(''' - # any int or real number, returned as float - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.hex_integer.runTests(''' - # hex numbers - 100 - FF - ''') - - pyparsing_common.fraction.runTests(''' - # fractions - 1/2 - -3/4 - ''') - - pyparsing_common.mixed_integer.runTests(''' - # mixed fractions - 1 - 1/2 - -3/4 - 1-3/4 - ''') - - import uuid - pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) - pyparsing_common.uuid.runTests(''' - # uuid - 12345678-1234-5678-1234-567812345678 - ''') - prints:: - # any int or real number, returned as the appropriate type - 100 - [100] - - -100 - [-100] - - +100 - [100] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # any int or real number, returned as float - 100 - [100.0] - - -100 - [-100.0] - - +100 - [100.0] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # hex numbers - 100 - [256] - - FF - [255] - - # fractions - 1/2 - [0.5] - - -3/4 - [-0.75] - - # mixed fractions - 1 - [1] - - 1/2 - [0.5] - - -3/4 - [-0.75] - - 1-3/4 - [1.75] - - # uuid - 12345678-1234-5678-1234-567812345678 - [UUID('12345678-1234-5678-1234-567812345678')] - """ - - convertToInteger = tokenMap(int) - """ - Parse action for converting parsed integers to Python int - """ - - convertToFloat = tokenMap(float) - """ - Parse action for converting parsed numbers to Python float - """ - - integer = Word(nums).setName("integer").setParseAction(convertToInteger) - """expression that parses an unsigned integer, returns an int""" - - hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) - """expression that parses a hexadecimal integer, returns an int""" - - signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) - """expression that parses an integer with optional leading sign, returns an int""" - - fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") - """fractional expression of an integer divided by an integer, returns a float""" - fraction.addParseAction(lambda t: t[0]/t[-1]) - - mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") - """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" - mixed_integer.addParseAction(sum) - - real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) - """expression that parses a floating point number and returns a float""" - - sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) - """expression that parses a floating point number with optional scientific notation and returns a float""" - - # streamlining this expression makes the docs nicer-looking - number = (sci_real | real | signed_integer).streamline() - """any numeric expression, returns the corresponding Python type""" - - fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) - """any int or real number, returned as float""" - - identifier = Word(alphas+'_', alphanums+'_').setName("identifier") - """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" - - ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") - "IPv4 address (C{0.0.0.0 - 255.255.255.255})" - - _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") - _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") - _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") - _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) - _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") - ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") - "IPv6 address (long, short, or mixed form)" - - mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") - "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" - - @staticmethod - def convertToDate(fmt="%Y-%m-%d"): - """ - Helper to create a parse action for converting parsed date string to Python datetime.date - - Params - - - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) - - Example:: - date_expr = pyparsing_common.iso8601_date.copy() - date_expr.setParseAction(pyparsing_common.convertToDate()) - print(date_expr.parseString("1999-12-31")) - prints:: - [datetime.date(1999, 12, 31)] - """ - def cvt_fn(s,l,t): - try: - return datetime.strptime(t[0], fmt).date() - except ValueError as ve: - raise ParseException(s, l, str(ve)) - return cvt_fn - - @staticmethod - def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): - """ - Helper to create a parse action for converting parsed datetime string to Python datetime.datetime - - Params - - - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) - - Example:: - dt_expr = pyparsing_common.iso8601_datetime.copy() - dt_expr.setParseAction(pyparsing_common.convertToDatetime()) - print(dt_expr.parseString("1999-12-31T23:59:59.999")) - prints:: - [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] - """ - def cvt_fn(s,l,t): - try: - return datetime.strptime(t[0], fmt) - except ValueError as ve: - raise ParseException(s, l, str(ve)) - return cvt_fn - - iso8601_date = Regex(r'(?P\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?').setName("ISO8601 date") - "ISO8601 date (C{yyyy-mm-dd})" - - iso8601_datetime = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") - "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" - - uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") - "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" - - _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() - @staticmethod - def stripHTMLTags(s, l, tokens): - """ - Parse action to remove HTML tags from web page HTML source - - Example:: - # strip HTML links from normal text - text = 'More info at the
pyparsing wiki page' - td,td_end = makeHTMLTags("TD") - table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end - - print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' - """ - return pyparsing_common._html_stripper.transformString(tokens[0]) - - _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') - + Optional( White(" \t") ) ) ).streamline().setName("commaItem") - comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") - """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" - - upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) - """Parse action to convert tokens to upper case.""" - - downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) - """Parse action to convert tokens to lower case.""" - - -if __name__ == "__main__": - - selectToken = CaselessLiteral("select") - fromToken = CaselessLiteral("from") - - ident = Word(alphas, alphanums + "_$") - - columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) - columnNameList = Group(delimitedList(columnName)).setName("columns") - columnSpec = ('*' | columnNameList) - - tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) - tableNameList = Group(delimitedList(tableName)).setName("tables") - - simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") - - # demo runTests method, including embedded comments in test string - simpleSQL.runTests(""" - # '*' as column list and dotted table name - select * from SYS.XYZZY - - # caseless match on "SELECT", and casts back to "select" - SELECT * from XYZZY, ABC - - # list of column names, and mixed case SELECT keyword - Select AA,BB,CC from Sys.dual - - # multiple tables - Select A, B, C from Sys.dual, Table2 - - # invalid SELECT keyword - should fail - Xelect A, B, C from Sys.dual - - # incomplete command - should fail - Select - - # invalid column name - should fail - Select ^^^ frox Sys.dual - - """) - - pyparsing_common.number.runTests(""" - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - """) - - # any int or real number, returned as float - pyparsing_common.fnumber.runTests(""" - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - """) - - pyparsing_common.hex_integer.runTests(""" - 100 - FF - """) - - import uuid - pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) - pyparsing_common.uuid.runTests(""" - 12345678-1234-5678-1234-567812345678 - """) diff --git a/_setuptools_vendored/six-1.16.0.dist-info/LICENSE b/_setuptools_vendored/six-1.16.0.dist-info/LICENSE deleted file mode 100644 index de6633112c..0000000000 --- a/_setuptools_vendored/six-1.16.0.dist-info/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright (c) 2010-2020 Benjamin Peterson - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/_setuptools_vendored/six-1.16.0.dist-info/METADATA b/_setuptools_vendored/six-1.16.0.dist-info/METADATA deleted file mode 100644 index 6d7525c2eb..0000000000 --- a/_setuptools_vendored/six-1.16.0.dist-info/METADATA +++ /dev/null @@ -1,49 +0,0 @@ -Metadata-Version: 2.1 -Name: six -Version: 1.16.0 -Summary: Python 2 and 3 compatibility utilities -Home-page: https://github.com/benjaminp/six -Author: Benjamin Peterson -Author-email: benjamin@python.org -License: MIT -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 3 -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Topic :: Software Development :: Libraries -Classifier: Topic :: Utilities -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.* - -.. image:: https://img.shields.io/pypi/v/six.svg - :target: https://pypi.org/project/six/ - :alt: six on PyPI - -.. image:: https://travis-ci.org/benjaminp/six.svg?branch=master - :target: https://travis-ci.org/benjaminp/six - :alt: six on TravisCI - -.. image:: https://readthedocs.org/projects/six/badge/?version=latest - :target: https://six.readthedocs.io/ - :alt: six's documentation on Read the Docs - -.. image:: https://img.shields.io/badge/license-MIT-green.svg - :target: https://github.com/benjaminp/six/blob/master/LICENSE - :alt: MIT License badge - -Six is a Python 2 and 3 compatibility library. It provides utility functions -for smoothing over the differences between the Python versions with the goal of -writing Python code that is compatible on both Python versions. See the -documentation for more information on what is provided. - -Six supports Python 2.7 and 3.3+. It is contained in only one Python -file, so it can be easily copied into your project. (The copyright and license -notice must be retained.) - -Online documentation is at https://six.readthedocs.io/. - -Bugs can be reported to https://github.com/benjaminp/six. The code can also -be found there. - - diff --git a/_setuptools_vendored/six-1.16.0.dist-info/RECORD b/_setuptools_vendored/six-1.16.0.dist-info/RECORD deleted file mode 100644 index 8de4af79fa..0000000000 --- a/_setuptools_vendored/six-1.16.0.dist-info/RECORD +++ /dev/null @@ -1,6 +0,0 @@ -six.py,sha256=TOOfQi7nFGfMrIvtdr6wX4wyHH8M7aknmuLfo2cBBrM,34549 -six-1.16.0.dist-info/LICENSE,sha256=i7hQxWWqOJ_cFvOkaWWtI9gq3_YPI5P8J2K2MYXo5sk,1066 -six-1.16.0.dist-info/METADATA,sha256=VQcGIFCAEmfZcl77E5riPCN4v2TIsc_qtacnjxKHJoI,1795 -six-1.16.0.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 -six-1.16.0.dist-info/top_level.txt,sha256=_iVH_iYEtEXnD8nYGQYpYFUvkUW9sEO1GYbkeKSAais,4 -six-1.16.0.dist-info/RECORD,, diff --git a/_setuptools_vendored/six-1.16.0.dist-info/WHEEL b/_setuptools_vendored/six-1.16.0.dist-info/WHEEL deleted file mode 100644 index 01b8fc7d4a..0000000000 --- a/_setuptools_vendored/six-1.16.0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.36.2) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/_setuptools_vendored/six-1.16.0.dist-info/top_level.txt b/_setuptools_vendored/six-1.16.0.dist-info/top_level.txt deleted file mode 100644 index ffe2fce498..0000000000 --- a/_setuptools_vendored/six-1.16.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -six diff --git a/_setuptools_vendored/six.py b/_setuptools_vendored/six.py deleted file mode 100644 index 4e15675d8b..0000000000 --- a/_setuptools_vendored/six.py +++ /dev/null @@ -1,998 +0,0 @@ -# Copyright (c) 2010-2020 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -"""Utilities for writing code that runs on Python 2 and 3""" - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.16.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - -if PY34: - from importlib.util import spec_from_loader -else: - spec_from_loader = None - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def find_spec(self, fullname, path, target=None): - if fullname in self.known_modules: - return spec_from_loader(fullname, self) - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - - def create_module(self, spec): - return self.load_module(spec.name) - - def exec_module(self, module): - pass - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("getoutput", "commands", "subprocess"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("splitvalue", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), - MovedAttribute("parse_http_list", "urllib2", "urllib.request"), - MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - del io - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - _assertNotRegex = "assertNotRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" - _assertNotRegex = "assertNotRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - _assertNotRegex = "assertNotRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -def assertNotRegex(self, *args, **kwargs): - return getattr(self, _assertNotRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - try: - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - finally: - value = None - tb = None - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - try: - raise tp, value, tb - finally: - tb = None -""") - - -if sys.version_info[:2] > (3,): - exec_("""def raise_from(value, from_value): - try: - raise value from from_value - finally: - value = None -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - # This does exactly the same what the :func:`py3:functools.update_wrapper` - # function does on Python versions after 3.2. It sets the ``__wrapped__`` - # attribute on ``wrapper`` object and it doesn't raise an error if any of - # the attributes mentioned in ``assigned`` and ``updated`` are missing on - # ``wrapped`` object. - def _update_wrapper(wrapper, wrapped, - assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - for attr in assigned: - try: - value = getattr(wrapped, attr) - except AttributeError: - continue - else: - setattr(wrapper, attr, value) - for attr in updated: - getattr(wrapper, attr).update(getattr(wrapped, attr, {})) - wrapper.__wrapped__ = wrapped - return wrapper - _update_wrapper.__doc__ = functools.update_wrapper.__doc__ - - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - return functools.partial(_update_wrapper, wrapped=wrapped, - assigned=assigned, updated=updated) - wraps.__doc__ = functools.wraps.__doc__ - -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(type): - - def __new__(cls, name, this_bases, d): - if sys.version_info[:2] >= (3, 7): - # This version introduced PEP 560 that requires a bit - # of extra care (we mimic what is done by __build_class__). - resolved_bases = types.resolve_bases(bases) - if resolved_bases is not bases: - d['__orig_bases__'] = bases - else: - resolved_bases = bases - return meta(name, resolved_bases, d) - - @classmethod - def __prepare__(cls, name, this_bases): - return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - if hasattr(cls, '__qualname__'): - orig_vars['__qualname__'] = cls.__qualname__ - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def ensure_binary(s, encoding='utf-8', errors='strict'): - """Coerce **s** to six.binary_type. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> encoded to `bytes` - - `bytes` -> `bytes` - """ - if isinstance(s, binary_type): - return s - if isinstance(s, text_type): - return s.encode(encoding, errors) - raise TypeError("not expecting type '%s'" % type(s)) - - -def ensure_str(s, encoding='utf-8', errors='strict'): - """Coerce *s* to `str`. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - # Optimization: Fast return for the common case. - if type(s) is str: - return s - if PY2 and isinstance(s, text_type): - return s.encode(encoding, errors) - elif PY3 and isinstance(s, binary_type): - return s.decode(encoding, errors) - elif not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) - return s - - -def ensure_text(s, encoding='utf-8', errors='strict'): - """Coerce *s* to six.text_type. - - For Python 2: - - `unicode` -> `unicode` - - `str` -> `unicode` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - if isinstance(s, binary_type): - return s.decode(encoding, errors) - elif isinstance(s, text_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) - - -def python_2_unicode_compatible(klass): - """ - A class decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/conftest.py b/conftest.py index 9ad9c836d5..c23e79eff5 100644 --- a/conftest.py +++ b/conftest.py @@ -17,7 +17,6 @@ def pytest_addoption(parser): 'setuptools/_distutils', '_distutils_hack', 'pkg_resources/tests/data', - '_setuptools_vendored', ] diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index d89465e909..284e963e50 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -71,8 +71,6 @@ except ImportError: importlib_machinery = None -import _setuptools_vendored # noqa: F401 - import appdirs import packaging.version import packaging.specifiers diff --git a/pyproject.toml b/pyproject.toml index 70e3473d44..35a7d50aee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,13 @@ [build-system] requires = [ "wheel", + + # kept in sync with setup.cfg + "packaging==20.4", + "pyparsing>=2.2.1", + "ordered-set>=3.1.1", + "more_itertools>=8.8.0", + "appdirs>=1.4.3", ] build-backend = "setuptools.build_meta" backend-path = ["."] diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 974a5d8e80..9d6f0bc0dd 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -13,7 +13,6 @@ from ._deprecation_warning import SetuptoolsDeprecationWarning -import _setuptools_vendored # noqa: F401 import setuptools.version from setuptools.extension import Extension from setuptools.dist import Distribution From aa1f7dcf1ad6013886c15f3b2d8bafaae20bdce1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 29 Oct 2021 19:55:57 -0400 Subject: [PATCH 9/9] Adapt test to rely on build rather than setup.py --- setuptools/tests/test_virtualenv.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/setuptools/tests/test_virtualenv.py b/setuptools/tests/test_virtualenv.py index 8e6d4eaf00..3bc59db822 100644 --- a/setuptools/tests/test_virtualenv.py +++ b/setuptools/tests/test_virtualenv.py @@ -113,15 +113,12 @@ def test_pip_upgrade_from_source(pip_version, tmp_src, virtualenv): upgrade_pip = ('python -m pip install -U {pip_version} --retries=1',) virtualenv.run(' && '.join(( 'pip uninstall -y setuptools', - 'pip install -U wheel', + 'pip install build', ) + upgrade_pip).format(pip_version=pip_version)) dist_dir = virtualenv.workspace # Generate source distribution / wheel. - virtualenv.run(' && '.join(( - 'python setup.py -q sdist -d {dist}', - 'python setup.py -q bdist_wheel -d {dist}', - )).format(dist=dist_dir), cd=tmp_src) - sdist = glob.glob(os.path.join(dist_dir, '*.zip'))[0] + virtualenv.run(f'python -m build --outdir {dist_dir}', cd=tmp_src) + sdist = glob.glob(os.path.join(dist_dir, '*.tar.gz'))[0] wheel = glob.glob(os.path.join(dist_dir, '*.whl'))[0] # Then update from wheel. virtualenv.run('pip install ' + wheel)