From 57bc4222abb8d64e57b75c71e5d70388be08405a Mon Sep 17 00:00:00 2001 From: shim_ <> Date: Thu, 10 May 2018 17:50:30 +0200 Subject: [PATCH] added deps --- modules/_salsa20.so | Bin 0 -> 55640 bytes modules/_scrypt.so | Bin 0 -> 174152 bytes modules/bson/__init__.py | 69 + modules/bson/codec.py | 396 ++++ modules/bson/network.py | 67 + modules/bson/objectid.py | 295 +++ modules/bson/py3compat.py | 88 + modules/bson/tz_util.py | 52 + modules/dateutil/__init__.py | 8 + modules/dateutil/_common.py | 43 + modules/dateutil/_version.py | 4 + modules/dateutil/easter.py | 89 + modules/dateutil/parser/__init__.py | 60 + modules/dateutil/parser/_parser.py | 1578 +++++++++++++++ modules/dateutil/parser/isoparser.py | 406 ++++ modules/dateutil/relativedelta.py | 590 ++++++ modules/dateutil/rrule.py | 1672 +++++++++++++++ modules/dateutil/tz/__init__.py | 17 + modules/dateutil/tz/_common.py | 415 ++++ modules/dateutil/tz/_factories.py | 49 + modules/dateutil/tz/tz.py | 1785 +++++++++++++++++ modules/dateutil/tz/win.py | 331 +++ modules/dateutil/tzwin.py | 2 + modules/dateutil/utils.py | 71 + modules/dateutil/zoneinfo/__init__.py | 167 ++ .../zoneinfo/dateutil-zoneinfo.tar.gz | Bin 0 -> 139130 bytes modules/dateutil/zoneinfo/rebuild.py | 53 + modules/salsa20.py | 133 ++ modules/scrypt/__init__.py | 1 + modules/scrypt/scrypt.py | 232 +++ modules/six.py | 891 ++++++++ 31 files changed, 9564 insertions(+) create mode 100755 modules/_salsa20.so create mode 100755 modules/_scrypt.so create mode 100644 modules/bson/__init__.py create mode 100644 modules/bson/codec.py create mode 100644 modules/bson/network.py create mode 100644 modules/bson/objectid.py create mode 100644 modules/bson/py3compat.py create mode 100644 modules/bson/tz_util.py create mode 100644 modules/dateutil/__init__.py create mode 100644 modules/dateutil/_common.py create mode 100644 modules/dateutil/_version.py create mode 100644 modules/dateutil/easter.py create mode 100644 modules/dateutil/parser/__init__.py create mode 100644 modules/dateutil/parser/_parser.py create mode 100644 modules/dateutil/parser/isoparser.py create mode 100644 modules/dateutil/relativedelta.py create mode 100644 modules/dateutil/rrule.py create mode 100644 modules/dateutil/tz/__init__.py create mode 100644 modules/dateutil/tz/_common.py create mode 100644 modules/dateutil/tz/_factories.py create mode 100644 modules/dateutil/tz/tz.py create mode 100644 modules/dateutil/tz/win.py create mode 100644 modules/dateutil/tzwin.py create mode 100644 modules/dateutil/utils.py create mode 100644 modules/dateutil/zoneinfo/__init__.py create mode 100644 modules/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz create mode 100644 modules/dateutil/zoneinfo/rebuild.py create mode 100644 modules/salsa20.py create mode 100644 modules/scrypt/__init__.py create mode 100644 modules/scrypt/scrypt.py create mode 100644 modules/six.py diff --git a/modules/_salsa20.so b/modules/_salsa20.so new file mode 100755 index 0000000000000000000000000000000000000000..0ec825468873f5b296db8810eabb9cb324e9a25b GIT binary patch literal 55640 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
!F5cKfx*xj#FpVY1Y)~@ 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%0ANV694@#%S`$?-57i&E3$!CRG}dJ_v6 zV9G%=S1F|h3~6xxf)XZpwLm;fe>`Z72y}@uxZ2$RZNY6*fe5>DyGIDY#K2`6;opbHjS8}im5RYn?_P|IW?W)wOhng+D=sm$CSlczB`z<3k3L{vSO(R= z$Pfx+ZeoL&%giuwLp_Xh7%Irf5DJk+kT9d(LXBi(2!S#G!i56hOt^(Xj8G=bY-Wau z4fP0fAuO2bI#9)o41q8v%x%mJ6DMGCXduJ@1PL?hC)`y*FeZ`{7T|OOEPxmpB4EsD z@EPw642%qMFlHV+q+(&rVyF-(cSOvG@g_k<85!bWOqh=t8De2fn9CR$A`m`-us%Wk z0dve?ao0DfyOQC76Axn=!2KKsW5NQ7gQ1pT;*5GWrTH)sSU_ z4`adtjFBM>#zcf&JS4;rqyjWKX+mj6h7cGNrkj~z;*UBQ2VqnQ!Vm~64Qd1|8kiUi z=EK-Z&>&!DnAlOT4&~TFX-0+s7}F0b1Xc$NEHQXs1;d!qP$6c9;=1`T4lE=Y8G_Nn z2W%c@2nC~i3*=^qZxAHR88e}7lVhl3nZm;J|NsC0QXC2@bqp|$(!`E>HmUi?q3Yg2 zX%&WvE9%*lk@bKC&~(8Zw+8AnLbj}hs)PB!fT0YMY&dLWU`BH&sK5jr;DSmMC)BeU z%1B|_IABb8DH zrTH)sm?azxHT4h~h)x6vOHP-d&S3_586gc}p(|o!h=WKVNOUC}(7=N3ypeMngt6Y^gvkXIvE+_(e*JhV3bFgy28+P34_WIHf6?% z4fSkt<|-2xfXYgUod^4Va6hq#3CdiESM6cgbqtJutI|oc4!_%2Z)5}g1HXjD&pL`9qLC$ zh9DU85L5_a49IgZH^53;hz!UAkoCwI<`kG8amFc3AuQZLkp*MJG-FBdub^&Yg61i3 z{Gk`_U_oS8f!HveuzH4#p@5-?L1{jW4>OUGAq3q#8)$i_L~Lb&H?P4Qg0L_Q-9ko& zFp!gxF-#XCWssr^W*0m<&~1cEAlnII!|a6z6N3SkAPYn{4-}ms?a0^}8fTGEnvo#@ zSsKKKDRGA?;Rbak*lHPMDj1aJ!vy1@lDrHR47Ch06F1beDVbo_MMRmELY!F_m_V*$ z0N>6CVlgs=f-Y(VF;798t)Nyq<|q$L<1MHGj0_<#Cd>dvhBz1#*(}U%EzB&KYDR`Y z7;`t&5sVDMFeb8Dm_1#XS;BCQK`*FybPZigUnSvGlqhtK>>=4(M_Vn%d|9$av#2fh8ZY4VQe;NSYyd3uyFK-Dq&{e zaKLE9U@EF7tO!f124)T{Y2i__4DOzKhxsrLOdktF{ee1yu4H1cz^TRvYCI^EU~E_r zUk7|e2P zfd{iN6zY0V;KA5AP%$il2a{^Tt)vSowFpXM4PSIcSi%=mNen2YkTFaNEVLOJVqi>| z-wgE3*&fXoV+594*f6^6i=FiWvC{xEF} zgxQ!3Hz)?igxLYIF%WKJIb2~NjES%jBfDYR7zDF16>d;0jEQVx5Zp$X#~2xcU`&LK zut>!dsu3_7)8RJ8!I;Q4Mqsfq0@Fr}VGT?_M#5~&gxeSoVoCto zLK%z#n2UtsS6{u!>lLGE&*r=z`Tg$TUhXMF??o>gp9Ug z2~vy!D_5wAF!wMr#K4#^6S)~C!pCgkM#8+v$bb<>DC%JWw+|73FeWU1P}D=61E-ck zLw6sP2Gt8N_HC#bBSRdF`4B3^%urW9AI5nN6~r>K`GFT=lLH@wW@HG3G5z5}K`>?) zTqqdEoB|aBC9=@@Fy0SnL}6)cz--ilYGh;xfiYpaSQsXL_+1Tg5Q2nh`heH&? zcrdp!GQ`4|$lk#02E)7oOB##}5ilmq?Tid@Feb7$BH-STgL@+m#)LT>zqt?lU81C1eW?;A>Etm0{wCzt!_$+^v20CO6bM%nW4;^97(Bm|K|{I214vJIFwotFJ>fFfxR}n6R7+HW0>v`3IyI z;&cSL9O|f3P@0h;0A{>B+<1%uNmyczK3;{4E%swmw>ReFk^q@4BP8KZs zVKpQEN-_YRXfYZaFoR%9m>3K(MtU&Sm=jckrKz|c>WZUK8cR)#t^}KF(A6+AaA4+Q zbVaNT6Sx1X#&9((7GVhyi<8ima5C^HFfcNdF<_2vqbtScdUQ2B3=V7U{VD-r1u!h$J++ks%bu z4228D!Iv>V+4{Z=)$tf`7WuY1w8A4#pK)6sWj0rOkWPAt~<1u;!FymF= zW(C5SFh_yR!fa7tY7D}r5wi`0sWAeZM$F~~rp82cjkqduOsz>+wPIO&hN(3YT`MSn zF!QGrG(`QOG$TU{j0uY+R)&ca{#9d%CXDXhN2np3@PrZoV;aDPg1w>4P$H?uponl7nW>?u7rib!A1q6a79;z ztvEzigQeOy1PvNkuwX5X&=p}R)fl)SMUW|!W@HG3F_WP}ptKq~AI94a6=h@yg)whJ zi!(-sFc|Y5R0yIMVg(WjH3Mc7BSQ#`39|;IF$8XD3seImLkNs13blceAsEKYhYB$> z6f4Y!abPauV3_!$hE2JFaXw50*^QWUn3w?*h|P_Ga5v6{yD<>PgxSQ%5C>x-TZ&mi zVp9lgVN94!j0{0ACbFfN4DOqv6#B~Fdi(qF*3x! zm@oq&3L!2kS9tVJHWA}mFo0SCmqY$(mh5DH`Vz=h&q%z1z=c9!%&Sl#h+Y`)6I7IuAr8j;4i$nZgcyw=VGeGDI)#xT1jgJ17m9^3VNL{@ z6#{oK%myrrvS7x$!OaSUG1tO{f?-T#;{&l6kI|Qc8Se=afHTMr*MPY6dLXGcp9g%#eg8PDX|R z81pV%DC`lG`4&oJX{#terSzdRBSQd;nF$w)hcR>ELZPr61G9sXAs)tr7?Y795XMB(_XDTC zpK$wvU`!-^4K)y-AxKR7BCy!^0H=N8aCaucm`L_bAi};REcVSHLSG~neLWcZ4nV`> zFO=qFn0TXl3M0pV%v}fYMbuC)$uX2LaQv6DP^kk^9x6)3AlgAisTM>l&1Z+IgSkV2 zp`HtDGEN;Zm203n6&WUW*WlNJh$#rm1gg~>N-HoFfm~yuQVOCxRFuGOc2H5O=LZ|C zG#{oCX1NkfM?DW%1vWh}wXmqvVwl(obLhl3knr<~V z06W9P57lhS3K%&XrVy45nHfqs=EFF!$YExv=O9fdEDM8m!Z@%T%m&&&tjvLtqrr+H zA&wwtK*Joi$d-v=B7-}$U`T?}j0}M=<`1Y4NEth{q)>&@ATfl&5Ei=e%nTEIYGCHW z3n zfgrIMHvwBDO{l?Q9AcuxyO1fa+?4(pXw90??%dnoydNAr!_efC~k~ zm>p0dNQDgJwL;4nMuuP*vkNK&Q3&w^f`nOW0X2@1Aq2*ZhYN+lm@sQVW`)d$@nEab zkQSyDLf1GkGn6FEhjBJQ1tGd&JeWHf8Ny&pWG7<|^T3=8TO7m45C~(!+{wrg3uD3@ z39>X0?&SCIB_pvgCd@q$g)kn>NsJ7!Feb9=Fvo*1T^9s%T`N35<6ul=*9E~{2eSdo zq7qEwBVfkEbb&m8*>}R!n24?sSAPdnYZ6Q=ER;c;I%{AYn5V$z3F*SURSgYvuwobo z7SJ%oFupxB*@6|rI9^aem|_?o7ByhSFb*s{V2Tlm9>T)(RwTN&7#U*FJp~HgNVt~@ zp@nAk+WQc(=VUC9=gz;dmXJm+hF<~BoD10o-ssO0iHl$Wg}QIi~}nW85u%gOqdfv?Ji+x~mh%E>bi+!_j*asHJVjox> zi+x~mgiR0@%sxc)!OL4%5Q7Uk^za1>qFW9YM7J3%2(b)7Vpmi&8;Ei4IFT zFmZItVdCia!^9DmLs*zjfY)v?Cx8mU2zd5_IRGMxZaqX4-ExR1#6kqQ8=AY{Lup2a zB$#m`&@z#cAqi#{th``kNJ1C^VZjO_m_Y&zUxjqP=tBEF3+(0Q!(?EkG?G=YEWyc8 z$Y7=V-Q+vwkvFij0bok8IAIpl4_BcyBSQep;lXgfU@S*E11$$0Lun=kGmMTVx*9G9 zX2xuYzX__vGJK{3EpK2U&CF2DgJF&;R1r)uw$dM64KqVAW-l402+1}~MLtm5U`z6u z7%=z!psT?$LXWP5nW5MK!)LuvyH-PKtfPnMim+IQu7ri5_y7ZjZIEbUU=W5f7#Tug z%!hEHFc|Y4R0xz?Lg&MHtnd_tv56R}9=-#Jks$=O8R#igj*%f2#)KINGCl;0@fb&% zK#f2$J`iU7OQ;-Zof?#hY!OdV;2A4!nC_D^?pBY18?Lc&^QM97_LkB950;L%lf?-UUErJZS4CX2j)5{n* z{!6LMhe^Sbln|1V&y1m9H844tbA%Z_Glsy-fhmGXB7zBHM$8xLGFUJ(G9<&8cc4=NP`MO3AI3xMNWs|X1q*RlV@-;omH||6 zf6?WbdQI&Gx1OM#Tn$)6Y5shu;qK5H7N1%lsG1BYjZZDqXK-pal;sDt542<%=9TS8 zVlcKH)DA|5c$l?O2qCCpa0+HR%;#(jpZ0*#G|UE=FJaP2V4lU& zR3JhL)O-dZD1(t96vl)tg=S<3gE1?hsvt29=n31c3OfD^YAkHCDkDQU%%FCt6eB}8l!ZabKt&iCLSal7 zxKJ33SpXFRxifS=jJFw@N*Ec!V9Ya6A&5d4j~Sj$!eC5Rs1QUUjOPy(Wn>70F+-q2 z5QR{8U{ID&5k`hY81oy{DWC!BIvD2%R1j8=!uT*-z>1+}VNfuW85u%g%uJ{vMur#| z6Bark2ZzAJ3T6S8Lc0xWhBee}JPgIq`m>%vZaypwKVX$rfQF71lt$GHH5g8rL(PDh z$;`m{e?H7$m{DLsn0c^R01Lv+fihsmVRv`{)UpXsnvo$6W)&>dK?jdO9gZknFbdC= zPy=9TjFBM##ykv_Qe`M(5K?Y{0Y(Q06)_PczP1JMOcFTgBZjXUvUV{$Pfx+hQWnGV9XuRVhWVKFi*RInZ<_C2xGzwU}Ok{F_F!} zoI-$^#SAwq5XQ`sfH^e`#%x1!SKxdYuMaB9$Pfl&B3p_%GXS&H9d2n5j9CP?G!Dju zSpy1;Ah@N|;FiY0n8=o5F290V>IJtn0>+#OwG`A#(a3J&< z%!jeFr6KCr877`6V>6VQUkDYbh0@F*2{mXa=|gF_>SCxs1(b$MV7d!)kl?K(#O|L^ znu)<^K8&pfwI0iuz*i}Vw(n3H)CPpHdEwduU`#=%5HmvoBgV)9%o18G>O9?V6K=&G=Ed|*l@ zK+6b-L9o06E5jh7uo8ANG^;^GA+e7jVX2G{>O*kxiI9e{&=uh~ih7Q_4=pjDKxr(& zkFEskz%9BWEKN5|B^bl1FeR`u7>g+|DOjb%$PkYnIgAYP=rMz(`3f`085+@Cpb8pX z&@0V{i6JZrf?47QjcZ1RAXu3VOB9R@L74Vp99{~GyX8=OnHe}PU?gXBMW9u*Wf-bp zMr_At6uKf725f~D%!s4-j6zq0CHuhiUxu2)!oa0~S;wNQ!jkG>20Vlsgmq~%x*{yS zQkeet2y-wOBcdz9lI~#o|Kc+TT@jXKCjjjwxIt+~hENzY9WE3CW4?k4ff~k`y#$z9 znow1sQ4}Z>W&k5YAdHD@7G^I2W|lJ407iyD81o5SC?3Xq3KasmD{wxH2eSc>w*#OW z85x3L%xQ3;SQry#5XgAUWF}wv+0tF+yfeVGgn6XeH>;VNc-Vmyaks$=e z42BA^FciWZnaRiy2xB4}k2xZN zX?zgO_%OKfF)${wS(qh|5mbXKl*W>{(UoA0S#(8M>Mu+s7=!6BB`~)$G7#wv18Ay? zg3^o(p)e*aYhX_gm>NT{X~c|UOpSrqG-Ae{H`IyIP#Q~Kf+>L+$jAUU8>S278{B3i zG{Sv@tP#^Um^Q-04_PB-!Go!B2ssrVILK)PGjL!bkPnS9>}4dnDlG9j0eU~y1t`tP z5DH_e!p+8ZF&w7G5NsMTosOw75SvEKlynj5#HUaiyPwfjVevD}fKO0^u$CU^im;R( zF#Z1#=3uVyL05#OG;4qsbnBrsBSR>R=?k59WMqhkF$1AOEDWVH>gU6FL2yw}WWjie zP*FyPP#E(&TnMAdgz1hDm^&`OofZdU!t4NL`;hrC9?Tj>h7cHYG2F&D7;`&R2%;Cp zLpTki*@)@1Kuo8FVLBoZ=7?gbjf@OoFlISiC=kZ%h6;h)6*wQpgSii!PGB5_V=+3~ zn2rsCIo2I&0V6{Uj0uZDMus356Xp+)GlH-LZ4k^?2uHxyeLz|&3=9mIj);Ic!UgV# zU>Fl-Gia$5R2JD05pYK!Y{aN3F>Q>5*@$dbB-|{RFBlmjVN6))f~E)RVH}v9py0q9 zrSF3#O_*Jb4A@dF%mpxukkThCni(15U`&|JSe=2T|9T4QhD%TyG(-+#|AvY&G6cv% zgVqpAGcp8hf-+%#1nGpwA(XKJY68p_@IKwiP>nDHz=BW>a0-h~v-wzbVlK}_*U88b zjIIw$OZg?#rGKC_BSQ=4#sSS3W2;5Iv>XCf{J2sI?Q+}xRMYU(+w^Z z17pGr0vV6lV8t{m5N1{r+^kp_6XrOOS%FxbiZPZY4K>3MN@HmtVk*I??lF~Mw4pGS zU{v8SC9rU3WFS8Z?4kaF-E7Rvz=4^8&=p}RZeaRhH~C_3_M@x95{e1XB-ah485u%h z%*#+AP^4jYC^0pLz%-tPYGh=HficfPg+OM7%!lz{HekusJx~n`p)}TjLRW;v9GL#~ zP;;;c6uK%bA>IHz3GWq@W@HG3G0#Cyo?~PPhB2=}g+Trdoe$%EzQXkewk|?8I!6L+x4&r5PClVa&@=3m6$h z2$W`I2!t_DK!uRZyb2Y145hK$3bPaH?h{a&je*0VkU?fXjQ<5H&d3k|3n17@?u-lp zFy|bDTMz(a!kojyz~KN363hc`Vb)7P1B8o#xgiHq$HBu77Ba|kFpFV6M3#eSN02Z( zU{M9Sl(djRNm*k)OavBreDKIC1IxpF42w2|PM8QRBGGli+y{#sgie?UEPT*)BHRsO zX+lHB4N5aJaA4k?g{}x&Hwaw~GeemMMvKe~YD+(qW@HF}F(<=?U>hzV4rE|pfa#hB zRl>qh_5gF+6HFDXXo9GM@nB^WGeeC9MnMH?K!zw|9v=lu8tTyW$Hu^60ZP3v{tBo# zBSR=Gr69@)j13tu(_oQ@oT$*XBg?_V0m^`x1Ix;Y$Vbo72pO39u&j!Zff@>@U_}-z zae{|c;IdE#Oc5+}Nvjc{hQKM9X|OsAXY~cw0cF56E`}xyP`ZS%VeZGWT6GOn`5q{Z zrQCukISf_8!BD_pq*9FKm@b&g;}|MoA~0`pFcj27%z%l&+{g)*0o`ASkz-&RwzQzl z2GFIEP$sP51FB{*n?;x!LtthgYs73&V`>bX1`vzGfrf)E9427A2tPwN(Fg3z$#2*v<3`Qy?3`+B1A~64PFjUk-WS~mn6wG*7h+s{In2ILufbFBeX%d!R60G`M54}eK zG}!`W{)N(v48bra4|G>6C~1Yxhw=QOqKpioFlHQ72%-?iy9^a&WC(^a??Qzj3SqoY zP*FyPP#BX5nkg9>f?-TuicAiEAT8^T;S9qyDs7;^z!C=ABj02KmR8aN-udkPh0WC(*fALdF% zhCmn-7Wfdom=4BB`Y@->f*T(MV^_3WT222Q~O_y$xdBSSol zc?T{O0%P8V3NbU(JIsf1U{`H0GQ?y0ARg1X7(VEWj>O0;_~40w@C(*s#=zGiO27!zoOS@G=DEL{L`1EE-_vhrl*R zGBSk1n6QoMj10jrW)ajvMut!r6SkEcWPIp+7_SPU5Xyp62~buRlxAk&_=3?ehAD!% z1<6@3eOLz19H2(RcEvI>guNT{X~YaZOpSrqG~#Z}K$|O|rWlL~ z+u_K_5CdbHL0yX7^-xJz)1HBmAq3hwWiW=yF*1a~n8;>fdI-8&ofHcDY;!BjIcM8KHH z8ZkHN!E7{#h9M(EFpOyd6=G(X=rA9~ftiLS2k}AGD?w>y1`f<|9&|-m%vk^pkDXAO zks%bugx$@_$Pf%;9zv*sF<~2rA?X3eJB3gPO^k4A15|1kl*a0Em?D^4kemh6hs86n zOkNAMjFBM}#w>)|#>fx`W6psJL0k^i0;e#|3W1r0tPwNSVrmS;rV%rVVrmS+rV%qe z!YX%|YEZ8P#_oc8h@GJrbQX)ue3-yes00T{SOGMo0y`xH7Cvw-P?c~BW;RR%BSSol zxe2O>m7##aUTQv!3o{bSviu!T9si&-BSQ#`$pnocMutEblMO1w#83u3;R~vbfq?;L z5=;%2zC26`!aW%4-C?>#p*DehVxl@9#+88zGcp9inBh<%s7e?&8ZKPOV4{jqal-6@ zooL9yFmXjaPE{}mV^;+W1Ly@JpgIc1G=R!6GK9gHhEO5w=^iSH)O&{?yP*%2V`K=1 zF_F!}%nApfK>#~5f{`H<#ykQqwW6hm0bLQ6J`}nV4$!^lY-LzR zX=gx#X9bkTQifwH!I;KJSAyF<2WXtZ(h^o5Vk(-5dHs3<)T9_ERB)-VV2FpXDPZOY-g#WtKni`R={QHe5hrIpfocB2j(t$bVXPq66_@g z1_lc#gOMQ=#@q@m7Z@4hV9ae$AxNPF$CyoSfx4#+N@I<0bVXS522B5CggJ^B@rV^Rcnk4VX{ycYC&m6hAD-!d$Nd7m9%~VIE>+2!Jso5ULWPOxRKPSW+>} zH?UDbMuq^`&=jKM4|nHpga@H4I0ftO!a@vN35l+Ti-Fk!I~~fM}SyN~k(4Q#|!hDOd}Fi-CC_uA~K1I~}SAGzbE+ z1{uT5Sq)W#rOSb-1S9#ul&pf9g3TtF4LhJ}K&MM#3j~@VtB_DD&+;Gam5!*ohMWsmiY3AN@YN4eDMWSn+{cr&F3(4 zs-bGI#22O#3=hJTR6$L_W>Y&<4mQ5R#lXA}S4xAag`K&G%{)j8k%2)Qn*10ULSanU zI3{SVJyaHUekwCVas7N42Uf~4GZbTW>O7#FI4BJkgmJQOI5Ln$RC}$6p1`EPC2cd$Xl~SR~jPqgqOHgsJ zVi@N-RFIJ&5*A-D8a>`XRRk;!yP$Ce>PNuXu-pzlIPWx6!8It&$Pfx+%0UAaq!-49 z#VHem@q8E?5pSIT=fgNt;hK%-!`O@9VkYxpY*_pUUDSiFK<4P(Qi4{RWe1B*70 zW>|c|Xjr_#G8aeW5U7)qzo1&zu@6yIR6in%?b|_ z!};t`wkVVa>6L=AVPOaogRx=3#mEo@W5PlPqzuM}g$yG@5R8d0JYeCX24yfZgu@LYc5`6e9z?p{?!kCs&Z!$8(!kAW2AxJpDcp*?xEF)h~QyE~3RTvpUU`$iEt}qxA zW+2G;kohnk!g!1t8`jT)4cUTfH>k1BP#R0mVI$O4+n_X-)&fikqMH{1V{LhXQ4KcFlLxce}Msxg&d*o3Jh7-}w@f+-P)TLhbGhAVjiEdXGJ!v`pz8JgkY z3ZaZ5=&ctuP#SiD&U7e$1C+i5rJ>s3loKn2nE|DbL+QIv8X}7z8=>MYP`VvTZ-UYY zl@Qh=sQ42o{Q*idu|ed~$pWZ9*`e;$fYNnP8eKiKp~nC_JsNhKDeN5DPN)W0LmbM5 zQ)nfYI|!^IPlKw1T{;3+2xY)7_?QM&2OEEdT^s^c2B(%l!*?x|J`bgDKxx=zu&_%- zS3=X<1}MD+N^ghKd!Y1DD18Y^-+|I_i=m9WPjgwn7}r=iN=6zp#2 z<Lg`~r`XrRT38f*b5u^ch z5xXmt_JGpfP#U2U%7SiyfbH4gg!1{Iv;>q^fYPc^S_?|sLTLvm?G2>^pfuDLI0aji z2wMOMTST@Ty5I}05X$%m6=#F4vw|)0l7RA|%HY%%sQ7Luy%$Qu)@dDu@~=Z_xJD=g zwm=8A?iO~|KkQ(Cs4_S;8_I%RRIv=ohs|okno75z;&-7mtWgNNiUHQyhqb@&LCftY zP#U%r2C5xS;W8I?ugPbaN~k$7E{sMWse`RXf*rjMTjT|+e_$i`u;wbv92gBdq8(iy z?C5jY0rv*5@)@QPO2gE_?13$qfgOzwRR*V^#T5grSb!dm3ty}aR|sXmmN~=PGA&T? z4k!(aN7xz%Sd;7^RQ@8AhOMPW2Yt{4Gzm&CfYPvyE)Ssj5Vj^3UH?9)`lC=9w#^Q< z{|%-emM>vjkzm_{(9MJGsS1GF8w{nxp>!;io(ZMV^~*x@u?m!iZJa{47ngrvi$xot z`B(s&?>wM1`kGL5`_WfqCg9KqTc?Yz-v_E5wvId!%7>YQu08>}1Uv&uqwlytR}VYK zV+U0IZYT{qOJoN0w1EXs8n%)D0F;kz9_$nh*a;snb+F?-(AC3^nBak$#|Nbqpfv2f z3E0UP==x2e>YSl8>=dL}C?8!t>_CD%s66bb1lTbL=;~)dGvp#Dy&Ot!fzltK^lvB) zJHZ%sqB6R9q?&`SA9iqV6Z9lh*vYuCQ;^Zs!$vS+vpTRvtFRTS=;~qRJ#38*7qr~v zgVN~gmqNwYKxx?OHQ100x_S<1xycWuMWD1Kl!mS6fL&Y^02SX4rD2Ofjzjsd_B(7c z1>HVe_Q1+S2>m%1{Z|7!GWf z5Go9(OrYTmt6yL<39uOlxI!o+1sYE2P#QK<1e;NUDuYw7dJlH%&qS!qTqup+e}@Xg zsYg&2tp0{|v00$*7KYNWzCBbq@f0%yBf|-(A;`Lz8JHNbRkF+s%nYzdK~>Mfa00ee z3`v5Sft8^LTG%5AGcYr-F&ID#UsQ2+h6h;1IT$3Mg$$~CPKF3rs|87dnSqO;0IN7R z0|PX(BPnNKX5e8Ez$(tma05&3W@g}HV1V9khH4H!LrOlf2!zedAi$td2w@|W%nX7I zJGx7Jt#6TFo47tpoyDS?oBL;g)r&JMfW{XrHkad24{EexGv^xAUk=bHH~}r(9zewfph5Zz zg7#0=xE64=R2@10r4ptt9NA;ttSjDq({!pxgDZnV$qzzX7_s3^p8|&j?CG;tVIC z?uT_UK(n^k%xPuB9$$S>^BZg-?u2bv1x<7^GVn1RfbR1Jg%?Q0OBRTK89;YdgYLV6 zhUXHnd-xb&Cttvpd#`|+U*H4M&A=cHRsRHP&ID+02R4lhT8j^|7q-6`mVWZtq4q*o zMU_JnH0WL`Z2rBE!@qBEi2uPM4%+$v$`=gKHFU5=b)ZHKDE+`zH^QbmK#d%b_ye?f z0o`YZ&7EpY*yF{L3F6-d=q0PLk_B`b7&ddlq3Rbv)x*Nghy#*NJfI6AVd*@E2_hZ< z6^ErC(4~pk%rC*=PEa&pQx95S!pI=N&;wge4~c#-iA{YkI9*`YQwzZE#H=s2LH&CG zS`Wf%g1un%VhkD3@CRGSz`$@4ERI=ET?30_)>EKCT_FY@h6LyeFxd3V7bXVym5?yA zL9_rfc5!7K;-)ynJ;35T3<}V44_4EF)@g&>GXdJl2bI?#dqIP}AaMbxdf1p{1=t*r z3RDc5sE4HuWEo)=ko$QU9zY8`*m}T4U~_mF8d4#uVRN)=z~Y$gkWFB5h+1TF9~;Pg zWDyAaEDrS#!Qwm&4N(wvFn|4mihqFCQ!sHJsQV8#WVo3uCv7DqM#!oCR>=V1_ljvm1#XTF2QA!?9GPF7I7 z@GvZZj;e7$>nRDaII>CzTL&!8!@v*?;m1Jz<;cpw0Cgfn0!l`M)k8T*R6Y*zZdL{c zB_wq)=5ieBPvH;;O@4vmeF8N7Xh9A70ank$Z~!W92o>iBr%xV+1n4@VRZwvyusD>1 zL|Nbv_rW0^4;JTPNPy;Fn0tz#;tkMgNoe+C=mCo(SqNn=1dH=9On}BOY;biiSRASd ziMoJ8T!I}E?;D^202|B&O&Wp1?E!RD8MYkeCDqD5MU6;D9=;C>UkIhp!fewg__d{7Kd_>sJUQq9tH!@ zWtI#K46ro~>!9XGKK;yerwpzSeO zUY-OMcYx*>*jnioQ1Jrj{`eEn2xbMR6D4WdL?eTE3x=ee{N!w~a*&V(s&bG@kX%}6UNS>T zYH>+&VsR=1#FEs!WKfV~78m3sR>l`37A58uBc$SsQ%gzxBR?RnCmjDQd1Z*N^=wQiZiQH^^zG9iwlY}^GZO0 z6CdvuPG)+ZgM+(|r;~HMk)DyBDJX)9Q&EKiD&sx#GE02(Q%ZAEP2$Z=7!s56i%L)x zfnoxhJdxrdxuhsQsWgotwW1&%B?*I)KO_+1lQXgrMj7jYGE;hPejX@Ji%R0-5h2F_ z&QJ`FuEFuCd0^8V9Q*@Z{epvi;tk^s;|&;c6LWI%lM$wYJp{@}`Je;=(-jZS6JR&N zlRDUyU^DZV^E-A`MMNb>>pajKhaY<2XVlG2G$fdAy6UCir zsYONkMW}%gA77S+)6d|5O#`_vvkJ|1phz!HErCa2c4{Sh!CnJEllUPfYZ27_KoWnOV*E`%;Ag2=oix~8ZQgaeP8lbF#oDv2- za7NK9$SKiF&o5!nD@m;=VbBA$a~SlB^1(r;mzn`>*<_>?F~E5ct&k=TL=$^F=CXCd0< z=DT3@29PGG`$0~D^>3iU44`2YkT}dd*t{5whArZR*$>kX^FL_uC`i8()Iiw$8H{#- z_9H<0K?|JF^;dxB7Z?~|Ygu9QY%m&qdJSY3)G9Cq>bHWz6s8|G9|xmhn;gK389;qB z2n#m-2Re`eSwDR02$V1x5b+Jsh2X>d0h)A0)(@NCgV86T7f20k zKJx&P`cFgM57Q5u_dG#BKgcc+zQF)VyD@OuDVC9edXCxqz$sRglNG-#6vj13ZpVWyK1QOUJ15eN+tg|I=S0}l5~mq8L< z0Vs2U6hbk&eh?p`5~=`7O=yLPet_x+1tVyv6qMFLN?`Q|sC= value > 0x7fffffff: + buf.write(encode_int64_element(name, value)) + elif value > 0x7FFFFFFFFFFFFFFF: + if value > 0xFFFFFFFFFFFFFFFF: + raise Exception("BSON format supports only int value < %s" % 0xFFFFFFFFFFFFFFFF) + buf.write(encode_uint64_element(name, value)) + else: + buf.write(encode_int32_element(name, value)) + elif isinstance(value, float): + buf.write(encode_double_element(name, value)) + elif _is_string(value): + buf.write(encode_string_element(name, value)) + elif isinstance(value, str) or isinstance(value, bytes): + buf.write(encode_binary_element(name, value)) + elif isinstance(value, UUID): + buf.write(encode_binary_element(name, value.bytes, binary_subtype=4)) + elif isinstance(value, datetime): + buf.write(encode_utc_datetime_element(name, value)) + elif value is None: + buf.write(encode_none_element(name, value)) + elif isinstance(value, dict): + buf.write(encode_document_element(name, value, traversal_stack, + generator_func, on_unknown)) + elif isinstance(value, list) or isinstance(value, tuple): + buf.write(encode_array_element(name, value, traversal_stack, + generator_func, on_unknown)) + elif isinstance(value, BSONCoding): + buf.write(encode_object_element(name, value, traversal_stack, + generator_func, on_unknown)) + elif isinstance(value, Decimal): + buf.write(encode_double_element(name, float(value))) + else: + if on_unknown is not None: + encode_value(name, on_unknown(value), buf, traversal_stack, + generator_func, on_unknown) + else: + raise UnknownSerializerError() + + +def encode_document(obj, traversal_stack, traversal_parent=None, + generator_func=None, on_unknown=None): + buf = StringIO() + key_iter = iterkeys(obj) + if generator_func is not None: + key_iter = generator_func(obj, traversal_stack) + for name in key_iter: + value = obj[name] + traversal_stack.append(TraversalStep(traversal_parent or obj, name)) + encode_value(name, value, buf, traversal_stack, + generator_func, on_unknown) + traversal_stack.pop() + e_list = buf.getvalue() + e_list_length = len(e_list) + return struct.pack("`_. +""" + +import binascii +import calendar +import datetime +import hashlib +import os +import random +import socket +import struct +import threading +import time + +from bson.py3compat import PY3, bytes_from_hex, string_type, text_type +from bson.tz_util import utc + + +def _machine_bytes(): + """Get the machine portion of an ObjectId. + """ + machine_hash = hashlib.md5() + if PY3: + # gethostname() returns a unicode string in python 3.x + # while update() requires a byte string. + machine_hash.update(socket.gethostname().encode()) + else: + # Calling encode() here will fail with non-ascii hostnames + machine_hash.update(socket.gethostname()) + return machine_hash.digest()[0:3] + + +class InvalidId(ValueError): + """Raised when trying to create an ObjectId from invalid data. + """ + +def _raise_invalid_id(oid): + raise InvalidId( + "%r is not a valid ObjectId, it must be a 12-byte input" + " or a 24-character hex string" % oid) + + +class ObjectId(object): + """A MongoDB ObjectId. + """ + + _inc = random.randint(0, 0xFFFFFF) + _inc_lock = threading.Lock() + + _machine_bytes = _machine_bytes() + + __slots__ = ('__id') + + _type_marker = 7 + + def __init__(self, oid=None): + """Initialize a new ObjectId. + + An ObjectId is a 12-byte unique identifier consisting of: + + - a 4-byte value representing the seconds since the Unix epoch, + - a 3-byte machine identifier, + - a 2-byte process id, and + - a 3-byte counter, starting with a random value. + + By default, ``ObjectId()`` creates a new unique identifier. The + optional parameter `oid` can be an :class:`ObjectId`, or any 12 + :class:`bytes` or, in Python 2, any 12-character :class:`str`. + + For example, the 12 bytes b'foo-bar-quux' do not follow the ObjectId + specification but they are acceptable input:: + + >>> ObjectId(b'foo-bar-quux') + ObjectId('666f6f2d6261722d71757578') + + `oid` can also be a :class:`unicode` or :class:`str` of 24 hex digits:: + + >>> ObjectId('0123456789ab0123456789ab') + ObjectId('0123456789ab0123456789ab') + >>> + >>> # A u-prefixed unicode literal: + >>> ObjectId(u'0123456789ab0123456789ab') + ObjectId('0123456789ab0123456789ab') + + Raises :class:`~bson.errors.InvalidId` if `oid` is not 12 bytes nor + 24 hex digits, or :class:`TypeError` if `oid` is not an accepted type. + + :Parameters: + - `oid` (optional): a valid ObjectId. + + .. mongodoc:: objectids + """ + if oid is None: + self.__generate() + elif isinstance(oid, bytes) and len(oid) == 12: + self.__id = oid + else: + self.__validate(oid) + + @classmethod + def from_datetime(cls, generation_time): + """Create a dummy ObjectId instance with a specific generation time. + + This method is useful for doing range queries on a field + containing :class:`ObjectId` instances. + + .. warning:: + It is not safe to insert a document containing an ObjectId + generated using this method. This method deliberately + eliminates the uniqueness guarantee that ObjectIds + generally provide. ObjectIds generated with this method + should be used exclusively in queries. + + `generation_time` will be converted to UTC. Naive datetime + instances will be treated as though they already contain UTC. + + An example using this helper to get documents where ``"_id"`` + was generated before January 1, 2010 would be: + + >>> gen_time = datetime.datetime(2010, 1, 1) + >>> dummy_id = ObjectId.from_datetime(gen_time) + >>> result = collection.find({"_id": {"$lt": dummy_id}}) + + :Parameters: + - `generation_time`: :class:`~datetime.datetime` to be used + as the generation time for the resulting ObjectId. + """ + if generation_time.utcoffset() is not None: + generation_time = generation_time - generation_time.utcoffset() + timestamp = calendar.timegm(generation_time.timetuple()) + oid = struct.pack( + ">i", int(timestamp)) + b"\x00\x00\x00\x00\x00\x00\x00\x00" + return cls(oid) + + @classmethod + def is_valid(cls, oid): + """Checks if a `oid` string is valid or not. + + :Parameters: + - `oid`: the object id to validate + + .. versionadded:: 2.3 + """ + if not oid: + return False + + try: + ObjectId(oid) + return True + except (InvalidId, TypeError): + return False + + def __generate(self): + """Generate a new value for this ObjectId. + """ + + # 4 bytes current time + oid = struct.pack(">i", int(time.time())) + + # 3 bytes machine + oid += ObjectId._machine_bytes + + # 2 bytes pid + oid += struct.pack(">H", os.getpid() % 0xFFFF) + + # 3 bytes inc + with ObjectId._inc_lock: + oid += struct.pack(">i", ObjectId._inc)[1:4] + ObjectId._inc = (ObjectId._inc + 1) % 0xFFFFFF + + self.__id = oid + + def __validate(self, oid): + """Validate and use the given id for this ObjectId. + + Raises TypeError if id is not an instance of + (:class:`basestring` (:class:`str` or :class:`bytes` + in python 3), ObjectId) and InvalidId if it is not a + valid ObjectId. + + :Parameters: + - `oid`: a valid ObjectId + """ + if isinstance(oid, ObjectId): + self.__id = oid.binary + # bytes or unicode in python 2, str in python 3 + elif isinstance(oid, string_type): + if len(oid) == 24: + try: + self.__id = bytes_from_hex(oid) + except (TypeError, ValueError): + _raise_invalid_id(oid) + else: + _raise_invalid_id(oid) + else: + raise TypeError("id must be an instance of (bytes, %s, ObjectId), " + "not %s" % (text_type.__name__, type(oid))) + + @property + def binary(self): + """12-byte binary representation of this ObjectId. + """ + return self.__id + + @property + def generation_time(self): + """A :class:`datetime.datetime` instance representing the time of + generation for this :class:`ObjectId`. + + The :class:`datetime.datetime` is timezone aware, and + represents the generation time in UTC. It is precise to the + second. + """ + timestamp = struct.unpack(">i", self.__id[0:4])[0] + return datetime.datetime.fromtimestamp(timestamp, utc) + + def __getstate__(self): + """return value of object for pickling. + needed explicitly because __slots__() defined. + """ + return self.__id + + def __setstate__(self, value): + """explicit state set from pickling + """ + # Provide backwards compatability with OIDs + # pickled with pymongo-1.9 or older. + if isinstance(value, dict): + oid = value["_ObjectId__id"] + else: + oid = value + # ObjectIds pickled in python 2.x used `str` for __id. + # In python 3.x this has to be converted to `bytes` + # by encoding latin-1. + if PY3 and isinstance(oid, text_type): + self.__id = oid.encode('latin-1') + else: + self.__id = oid + + def __str__(self): + if PY3: + return binascii.hexlify(self.__id).decode() + return binascii.hexlify(self.__id) + + def __repr__(self): + return "ObjectId('%s')" % (str(self),) + + def __eq__(self, other): + if isinstance(other, ObjectId): + return self.__id == other.binary + return NotImplemented + + def __ne__(self, other): + if isinstance(other, ObjectId): + return self.__id != other.binary + return NotImplemented + + def __lt__(self, other): + if isinstance(other, ObjectId): + return self.__id < other.binary + return NotImplemented + + def __le__(self, other): + if isinstance(other, ObjectId): + return self.__id <= other.binary + return NotImplemented + + def __gt__(self, other): + if isinstance(other, ObjectId): + return self.__id > other.binary + return NotImplemented + + def __ge__(self, other): + if isinstance(other, ObjectId): + return self.__id >= other.binary + return NotImplemented + + def __hash__(self): + """Get a hash value for this :class:`ObjectId`.""" + return hash(self.__id) diff --git a/modules/bson/py3compat.py b/modules/bson/py3compat.py new file mode 100644 index 0000000..0a2aa32 --- /dev/null +++ b/modules/bson/py3compat.py @@ -0,0 +1,88 @@ +# Copyright 2009-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Utility functions and definitions for python3 compatibility.""" + +import sys + +PY3 = sys.version_info[0] == 3 + +if PY3: + import codecs + import _thread as thread + from io import BytesIO as StringIO + MAXSIZE = sys.maxsize + + imap = map + + def b(s): + # BSON and socket operations deal in binary data. In + # python 3 that means instances of `bytes`. In python + # 2.6 and 2.7 you can create an alias for `bytes` using + # the b prefix (e.g. b'foo'). + # See http://python3porting.com/problems.html#nicer-solutions + return codecs.latin_1_encode(s)[0] + + def bytes_from_hex(h): + return bytes.fromhex(h) + + def iteritems(d): + return iter(d.items()) + + def itervalues(d): + return iter(d.values()) + + def reraise(exctype, value, trace=None): + raise exctype(str(value)).with_traceback(trace) + + def _unicode(s): + return s + + text_type = str + string_type = str + integer_types = int +else: + import thread + + from itertools import imap + try: + from cStringIO import StringIO + except ImportError: + from StringIO import StringIO + + MAXSIZE = sys.maxint + + def b(s): + # See comments above. In python 2.x b('foo') is just 'foo'. + return s + + def bytes_from_hex(h): + return h.decode('hex') + + def iteritems(d): + return d.iteritems() + + def itervalues(d): + return d.itervalues() + + # "raise x, y, z" raises SyntaxError in Python 3 + exec("""def reraise(exctype, value, trace=None): + raise exctype, str(value), trace +""") + + _unicode = unicode + + string_type = basestring + text_type = unicode + integer_types = (int, long) diff --git a/modules/bson/tz_util.py b/modules/bson/tz_util.py new file mode 100644 index 0000000..6ec918f --- /dev/null +++ b/modules/bson/tz_util.py @@ -0,0 +1,52 @@ +# Copyright 2010-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Timezone related utilities for BSON.""" + +from datetime import (timedelta, + tzinfo) + +ZERO = timedelta(0) + + +class FixedOffset(tzinfo): + """Fixed offset timezone, in minutes east from UTC. + + Implementation based from the Python `standard library documentation + `_. + Defining __getinitargs__ enables pickling / copying. + """ + + def __init__(self, offset, name): + if isinstance(offset, timedelta): + self.__offset = offset + else: + self.__offset = timedelta(minutes=offset) + self.__name = name + + def __getinitargs__(self): + return self.__offset, self.__name + + def utcoffset(self, dt): + return self.__offset + + def tzname(self, dt): + return self.__name + + def dst(self, dt): + return ZERO + + +utc = FixedOffset(0, "UTC") +"""Fixed offset timezone representing UTC.""" diff --git a/modules/dateutil/__init__.py b/modules/dateutil/__init__.py new file mode 100644 index 0000000..0defb82 --- /dev/null +++ b/modules/dateutil/__init__.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +try: + from ._version import version as __version__ +except ImportError: + __version__ = 'unknown' + +__all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz', + 'utils', 'zoneinfo'] diff --git a/modules/dateutil/_common.py b/modules/dateutil/_common.py new file mode 100644 index 0000000..4eb2659 --- /dev/null +++ b/modules/dateutil/_common.py @@ -0,0 +1,43 @@ +""" +Common code used in multiple modules. +""" + + +class weekday(object): + __slots__ = ["weekday", "n"] + + def __init__(self, weekday, n=None): + self.weekday = weekday + self.n = n + + def __call__(self, n): + if n == self.n: + return self + else: + return self.__class__(self.weekday, n) + + def __eq__(self, other): + try: + if self.weekday != other.weekday or self.n != other.n: + return False + except AttributeError: + return False + return True + + def __hash__(self): + return hash(( + self.weekday, + self.n, + )) + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday] + if not self.n: + return s + else: + return "%s(%+d)" % (s, self.n) + +# vim:ts=4:sw=4:et diff --git a/modules/dateutil/_version.py b/modules/dateutil/_version.py new file mode 100644 index 0000000..713fe0d --- /dev/null +++ b/modules/dateutil/_version.py @@ -0,0 +1,4 @@ +# coding: utf-8 +# file generated by setuptools_scm +# don't change, don't track in version control +version = '2.7.3' diff --git a/modules/dateutil/easter.py b/modules/dateutil/easter.py new file mode 100644 index 0000000..53b7c78 --- /dev/null +++ b/modules/dateutil/easter.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +""" +This module offers a generic easter computing method for any given year, using +Western, Orthodox or Julian algorithms. +""" + +import datetime + +__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"] + +EASTER_JULIAN = 1 +EASTER_ORTHODOX = 2 +EASTER_WESTERN = 3 + + +def easter(year, method=EASTER_WESTERN): + """ + This method was ported from the work done by GM Arts, + on top of the algorithm by Claus Tondering, which was + based in part on the algorithm of Ouding (1940), as + quoted in "Explanatory Supplement to the Astronomical + Almanac", P. Kenneth Seidelmann, editor. + + This algorithm implements three different easter + calculation methods: + + 1 - Original calculation in Julian calendar, valid in + dates after 326 AD + 2 - Original method, with date converted to Gregorian + calendar, valid in years 1583 to 4099 + 3 - Revised method, in Gregorian calendar, valid in + years 1583 to 4099 as well + + These methods are represented by the constants: + + * ``EASTER_JULIAN = 1`` + * ``EASTER_ORTHODOX = 2`` + * ``EASTER_WESTERN = 3`` + + The default method is method 3. + + More about the algorithm may be found at: + + `GM Arts: Easter Algorithms `_ + + and + + `The Calendar FAQ: Easter `_ + + """ + + if not (1 <= method <= 3): + raise ValueError("invalid method") + + # g - Golden year - 1 + # c - Century + # h - (23 - Epact) mod 30 + # i - Number of days from March 21 to Paschal Full Moon + # j - Weekday for PFM (0=Sunday, etc) + # p - Number of days from March 21 to Sunday on or before PFM + # (-6 to 28 methods 1 & 3, to 56 for method 2) + # e - Extra days to add for method 2 (converting Julian + # date to Gregorian date) + + y = year + g = y % 19 + e = 0 + if method < 3: + # Old method + i = (19*g + 15) % 30 + j = (y + y//4 + i) % 7 + if method == 2: + # Extra dates to convert Julian to Gregorian date + e = 10 + if y > 1600: + e = e + y//100 - 16 - (y//100 - 16)//4 + else: + # New method + c = y//100 + h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30 + i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11)) + j = (y + y//4 + i + 2 - c + c//4) % 7 + + # p can be from -6 to 56 corresponding to dates 22 March to 23 May + # (later dates apply to method 2, although 23 May never actually occurs) + p = i - j + e + d = 1 + (p + 27 + (p + 6)//40) % 31 + m = 3 + (p + 26)//30 + return datetime.date(int(y), int(m), int(d)) diff --git a/modules/dateutil/parser/__init__.py b/modules/dateutil/parser/__init__.py new file mode 100644 index 0000000..216762c --- /dev/null +++ b/modules/dateutil/parser/__init__.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +from ._parser import parse, parser, parserinfo +from ._parser import DEFAULTPARSER, DEFAULTTZPARSER +from ._parser import UnknownTimezoneWarning + +from ._parser import __doc__ + +from .isoparser import isoparser, isoparse + +__all__ = ['parse', 'parser', 'parserinfo', + 'isoparse', 'isoparser', + 'UnknownTimezoneWarning'] + + +### +# Deprecate portions of the private interface so that downstream code that +# is improperly relying on it is given *some* notice. + + +def __deprecated_private_func(f): + from functools import wraps + import warnings + + msg = ('{name} is a private function and may break without warning, ' + 'it will be moved and or renamed in future versions.') + msg = msg.format(name=f.__name__) + + @wraps(f) + def deprecated_func(*args, **kwargs): + warnings.warn(msg, DeprecationWarning) + return f(*args, **kwargs) + + return deprecated_func + +def __deprecate_private_class(c): + import warnings + + msg = ('{name} is a private class and may break without warning, ' + 'it will be moved and or renamed in future versions.') + msg = msg.format(name=c.__name__) + + class private_class(c): + __doc__ = c.__doc__ + + def __init__(self, *args, **kwargs): + warnings.warn(msg, DeprecationWarning) + super(private_class, self).__init__(*args, **kwargs) + + private_class.__name__ = c.__name__ + + return private_class + + +from ._parser import _timelex, _resultbase +from ._parser import _tzparser, _parsetz + +_timelex = __deprecate_private_class(_timelex) +_tzparser = __deprecate_private_class(_tzparser) +_resultbase = __deprecate_private_class(_resultbase) +_parsetz = __deprecated_private_func(_parsetz) diff --git a/modules/dateutil/parser/_parser.py b/modules/dateutil/parser/_parser.py new file mode 100644 index 0000000..9d2bb79 --- /dev/null +++ b/modules/dateutil/parser/_parser.py @@ -0,0 +1,1578 @@ +# -*- coding: utf-8 -*- +""" +This module offers a generic date/time string parser which is able to parse +most known formats to represent a date and/or time. + +This module attempts to be forgiving with regards to unlikely input formats, +returning a datetime object even for dates which are ambiguous. If an element +of a date/time stamp is omitted, the following rules are applied: + +- If AM or PM is left unspecified, a 24-hour clock is assumed, however, an hour + on a 12-hour clock (``0 <= hour <= 12``) *must* be specified if AM or PM is + specified. +- If a time zone is omitted, a timezone-naive datetime is returned. + +If any other elements are missing, they are taken from the +:class:`datetime.datetime` object passed to the parameter ``default``. If this +results in a day number exceeding the valid number of days per month, the +value falls back to the end of the month. + +Additional resources about date/time string formats can be found below: + +- `A summary of the international standard date and time notation + `_ +- `W3C Date and Time Formats `_ +- `Time Formats (Planetary Rings Node) `_ +- `CPAN ParseDate module + `_ +- `Java SimpleDateFormat Class + `_ +""" +from __future__ import unicode_literals + +import datetime +import re +import string +import time +import warnings + +from calendar import monthrange +from io import StringIO + +import six +from six import binary_type, integer_types, text_type + +from decimal import Decimal + +from warnings import warn + +from .. import relativedelta +from .. import tz + +__all__ = ["parse", "parserinfo"] + + +# TODO: pandas.core.tools.datetimes imports this explicitly. Might be worth +# making public and/or figuring out if there is something we can +# take off their plate. +class _timelex(object): + # Fractional seconds are sometimes split by a comma + _split_decimal = re.compile("([.,])") + + def __init__(self, instream): + if six.PY2: + # In Python 2, we can't duck type properly because unicode has + # a 'decode' function, and we'd be double-decoding + if isinstance(instream, (binary_type, bytearray)): + instream = instream.decode() + else: + if getattr(instream, 'decode', None) is not None: + instream = instream.decode() + + if isinstance(instream, text_type): + instream = StringIO(instream) + elif getattr(instream, 'read', None) is None: + raise TypeError('Parser must be a string or character stream, not ' + '{itype}'.format(itype=instream.__class__.__name__)) + + self.instream = instream + self.charstack = [] + self.tokenstack = [] + self.eof = False + + def get_token(self): + """ + This function breaks the time string into lexical units (tokens), which + can be parsed by the parser. Lexical units are demarcated by changes in + the character set, so any continuous string of letters is considered + one unit, any continuous string of numbers is considered one unit. + + The main complication arises from the fact that dots ('.') can be used + both as separators (e.g. "Sep.20.2009") or decimal points (e.g. + "4:30:21.447"). As such, it is necessary to read the full context of + any dot-separated strings before breaking it into tokens; as such, this + function maintains a "token stack", for when the ambiguous context + demands that multiple tokens be parsed at once. + """ + if self.tokenstack: + return self.tokenstack.pop(0) + + seenletters = False + token = None + state = None + + while not self.eof: + # We only realize that we've reached the end of a token when we + # find a character that's not part of the current token - since + # that character may be part of the next token, it's stored in the + # charstack. + if self.charstack: + nextchar = self.charstack.pop(0) + else: + nextchar = self.instream.read(1) + while nextchar == '\x00': + nextchar = self.instream.read(1) + + if not nextchar: + self.eof = True + break + elif not state: + # First character of the token - determines if we're starting + # to parse a word, a number or something else. + token = nextchar + if self.isword(nextchar): + state = 'a' + elif self.isnum(nextchar): + state = '0' + elif self.isspace(nextchar): + token = ' ' + break # emit token + else: + break # emit token + elif state == 'a': + # If we've already started reading a word, we keep reading + # letters until we find something that's not part of a word. + seenletters = True + if self.isword(nextchar): + token += nextchar + elif nextchar == '.': + token += nextchar + state = 'a.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == '0': + # If we've already started reading a number, we keep reading + # numbers until we find something that doesn't fit. + if self.isnum(nextchar): + token += nextchar + elif nextchar == '.' or (nextchar == ',' and len(token) >= 2): + token += nextchar + state = '0.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == 'a.': + # If we've seen some letters and a dot separator, continue + # parsing, and the tokens will be broken up later. + seenletters = True + if nextchar == '.' or self.isword(nextchar): + token += nextchar + elif self.isnum(nextchar) and token[-1] == '.': + token += nextchar + state = '0.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == '0.': + # If we've seen at least one dot separator, keep going, we'll + # break up the tokens later. + if nextchar == '.' or self.isnum(nextchar): + token += nextchar + elif self.isword(nextchar) and token[-1] == '.': + token += nextchar + state = 'a.' + else: + self.charstack.append(nextchar) + break # emit token + + if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or + token[-1] in '.,')): + l = self._split_decimal.split(token) + token = l[0] + for tok in l[1:]: + if tok: + self.tokenstack.append(tok) + + if state == '0.' and token.count('.') == 0: + token = token.replace(',', '.') + + return token + + def __iter__(self): + return self + + def __next__(self): + token = self.get_token() + if token is None: + raise StopIteration + + return token + + def next(self): + return self.__next__() # Python 2.x support + + @classmethod + def split(cls, s): + return list(cls(s)) + + @classmethod + def isword(cls, nextchar): + """ Whether or not the next character is part of a word """ + return nextchar.isalpha() + + @classmethod + def isnum(cls, nextchar): + """ Whether the next character is part of a number """ + return nextchar.isdigit() + + @classmethod + def isspace(cls, nextchar): + """ Whether the next character is whitespace """ + return nextchar.isspace() + + +class _resultbase(object): + + def __init__(self): + for attr in self.__slots__: + setattr(self, attr, None) + + def _repr(self, classname): + l = [] + for attr in self.__slots__: + value = getattr(self, attr) + if value is not None: + l.append("%s=%s" % (attr, repr(value))) + return "%s(%s)" % (classname, ", ".join(l)) + + def __len__(self): + return (sum(getattr(self, attr) is not None + for attr in self.__slots__)) + + def __repr__(self): + return self._repr(self.__class__.__name__) + + +class parserinfo(object): + """ + Class which handles what inputs are accepted. Subclass this to customize + the language and acceptable values for each parameter. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM + and YMD. Default is ``False``. + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken + to be the year, otherwise the last number is taken to be the year. + Default is ``False``. + """ + + # m from a.m/p.m, t from ISO T separator + JUMP = [" ", ".", ",", ";", "-", "/", "'", + "at", "on", "and", "ad", "m", "t", "of", + "st", "nd", "rd", "th"] + + WEEKDAYS = [("Mon", "Monday"), + ("Tue", "Tuesday"), # TODO: "Tues" + ("Wed", "Wednesday"), + ("Thu", "Thursday"), # TODO: "Thurs" + ("Fri", "Friday"), + ("Sat", "Saturday"), + ("Sun", "Sunday")] + MONTHS = [("Jan", "January"), + ("Feb", "February"), # TODO: "Febr" + ("Mar", "March"), + ("Apr", "April"), + ("May", "May"), + ("Jun", "June"), + ("Jul", "July"), + ("Aug", "August"), + ("Sep", "Sept", "September"), + ("Oct", "October"), + ("Nov", "November"), + ("Dec", "December")] + HMS = [("h", "hour", "hours"), + ("m", "minute", "minutes"), + ("s", "second", "seconds")] + AMPM = [("am", "a"), + ("pm", "p")] + UTCZONE = ["UTC", "GMT", "Z"] + PERTAIN = ["of"] + TZOFFSET = {} + # TODO: ERA = ["AD", "BC", "CE", "BCE", "Stardate", + # "Anno Domini", "Year of Our Lord"] + + def __init__(self, dayfirst=False, yearfirst=False): + self._jump = self._convert(self.JUMP) + self._weekdays = self._convert(self.WEEKDAYS) + self._months = self._convert(self.MONTHS) + self._hms = self._convert(self.HMS) + self._ampm = self._convert(self.AMPM) + self._utczone = self._convert(self.UTCZONE) + self._pertain = self._convert(self.PERTAIN) + + self.dayfirst = dayfirst + self.yearfirst = yearfirst + + self._year = time.localtime().tm_year + self._century = self._year // 100 * 100 + + def _convert(self, lst): + dct = {} + for i, v in enumerate(lst): + if isinstance(v, tuple): + for v in v: + dct[v.lower()] = i + else: + dct[v.lower()] = i + return dct + + def jump(self, name): + return name.lower() in self._jump + + def weekday(self, name): + try: + return self._weekdays[name.lower()] + except KeyError: + pass + return None + + def month(self, name): + try: + return self._months[name.lower()] + 1 + except KeyError: + pass + return None + + def hms(self, name): + try: + return self._hms[name.lower()] + except KeyError: + return None + + def ampm(self, name): + try: + return self._ampm[name.lower()] + except KeyError: + return None + + def pertain(self, name): + return name.lower() in self._pertain + + def utczone(self, name): + return name.lower() in self._utczone + + def tzoffset(self, name): + if name in self._utczone: + return 0 + + return self.TZOFFSET.get(name) + + def convertyear(self, year, century_specified=False): + """ + Converts two-digit years to year within [-50, 49] + range of self._year (current local time) + """ + + # Function contract is that the year is always positive + assert year >= 0 + + if year < 100 and not century_specified: + # assume current century to start + year += self._century + + if year >= self._year + 50: # if too far in future + year -= 100 + elif year < self._year - 50: # if too far in past + year += 100 + + return year + + def validate(self, res): + # move to info + if res.year is not None: + res.year = self.convertyear(res.year, res.century_specified) + + if res.tzoffset == 0 and not res.tzname or res.tzname == 'Z': + res.tzname = "UTC" + res.tzoffset = 0 + elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname): + res.tzoffset = 0 + return True + + +class _ymd(list): + def __init__(self, *args, **kwargs): + super(self.__class__, self).__init__(*args, **kwargs) + self.century_specified = False + self.dstridx = None + self.mstridx = None + self.ystridx = None + + @property + def has_year(self): + return self.ystridx is not None + + @property + def has_month(self): + return self.mstridx is not None + + @property + def has_day(self): + return self.dstridx is not None + + def could_be_day(self, value): + if self.has_day: + return False + elif not self.has_month: + return 1 <= value <= 31 + elif not self.has_year: + # Be permissive, assume leapyear + month = self[self.mstridx] + return 1 <= value <= monthrange(2000, month)[1] + else: + month = self[self.mstridx] + year = self[self.ystridx] + return 1 <= value <= monthrange(year, month)[1] + + def append(self, val, label=None): + if hasattr(val, '__len__'): + if val.isdigit() and len(val) > 2: + self.century_specified = True + if label not in [None, 'Y']: # pragma: no cover + raise ValueError(label) + label = 'Y' + elif val > 100: + self.century_specified = True + if label not in [None, 'Y']: # pragma: no cover + raise ValueError(label) + label = 'Y' + + super(self.__class__, self).append(int(val)) + + if label == 'M': + if self.has_month: + raise ValueError('Month is already set') + self.mstridx = len(self) - 1 + elif label == 'D': + if self.has_day: + raise ValueError('Day is already set') + self.dstridx = len(self) - 1 + elif label == 'Y': + if self.has_year: + raise ValueError('Year is already set') + self.ystridx = len(self) - 1 + + def _resolve_from_stridxs(self, strids): + """ + Try to resolve the identities of year/month/day elements using + ystridx, mstridx, and dstridx, if enough of these are specified. + """ + if len(self) == 3 and len(strids) == 2: + # we can back out the remaining stridx value + missing = [x for x in range(3) if x not in strids.values()] + key = [x for x in ['y', 'm', 'd'] if x not in strids] + assert len(missing) == len(key) == 1 + key = key[0] + val = missing[0] + strids[key] = val + + assert len(self) == len(strids) # otherwise this should not be called + out = {key: self[strids[key]] for key in strids} + return (out.get('y'), out.get('m'), out.get('d')) + + def resolve_ymd(self, yearfirst, dayfirst): + len_ymd = len(self) + year, month, day = (None, None, None) + + strids = (('y', self.ystridx), + ('m', self.mstridx), + ('d', self.dstridx)) + + strids = {key: val for key, val in strids if val is not None} + if (len(self) == len(strids) > 0 or + (len(self) == 3 and len(strids) == 2)): + return self._resolve_from_stridxs(strids) + + mstridx = self.mstridx + + if len_ymd > 3: + raise ValueError("More than three YMD values") + elif len_ymd == 1 or (mstridx is not None and len_ymd == 2): + # One member, or two members with a month string + if mstridx is not None: + month = self[mstridx] + # since mstridx is 0 or 1, self[mstridx-1] always + # looks up the other element + other = self[mstridx - 1] + else: + other = self[0] + + if len_ymd > 1 or mstridx is None: + if other > 31: + year = other + else: + day = other + + elif len_ymd == 2: + # Two members with numbers + if self[0] > 31: + # 99-01 + year, month = self + elif self[1] > 31: + # 01-99 + month, year = self + elif dayfirst and self[1] <= 12: + # 13-01 + day, month = self + else: + # 01-13 + month, day = self + + elif len_ymd == 3: + # Three members + if mstridx == 0: + if self[1] > 31: + # Apr-2003-25 + month, year, day = self + else: + month, day, year = self + elif mstridx == 1: + if self[0] > 31 or (yearfirst and self[2] <= 31): + # 99-Jan-01 + year, month, day = self + else: + # 01-Jan-01 + # Give precendence to day-first, since + # two-digit years is usually hand-written. + day, month, year = self + + elif mstridx == 2: + # WTF!? + if self[1] > 31: + # 01-99-Jan + day, year, month = self + else: + # 99-01-Jan + year, day, month = self + + else: + if (self[0] > 31 or + self.ystridx == 0 or + (yearfirst and self[1] <= 12 and self[2] <= 31)): + # 99-01-01 + if dayfirst and self[2] <= 12: + year, day, month = self + else: + year, month, day = self + elif self[0] > 12 or (dayfirst and self[1] <= 12): + # 13-01-01 + day, month, year = self + else: + # 01-13-01 + month, day, year = self + + return year, month, day + + +class parser(object): + def __init__(self, info=None): + self.info = info or parserinfo() + + def parse(self, timestr, default=None, + ignoretz=False, tzinfos=None, **kwargs): + """ + Parse the date/time string into a :class:`datetime.datetime` object. + + :param timestr: + Any date/time string using the supported formats. + + :param default: + The default datetime object, if this is a datetime object and not + ``None``, elements specified in ``timestr`` replace elements in the + default object. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a + naive :class:`datetime.datetime` object is returned. + + :param tzinfos: + Additional time zone names / aliases which may be present in the + string. This argument maps time zone names (and optionally offsets + from those time zones) to time zones. This parameter can be a + dictionary with timezone aliases mapping time zone names to time + zones or a function taking two parameters (``tzname`` and + ``tzoffset``) and returning a time zone. + + The timezones to which the names are mapped can be an integer + offset from UTC in seconds or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} + >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) + >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, + tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) + + This parameter is ignored if ``ignoretz`` is set. + + :param \\*\\*kwargs: + Keyword arguments as passed to ``_parse()``. + + :return: + Returns a :class:`datetime.datetime` object or, if the + ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the + first element being a :class:`datetime.datetime` object, the second + a tuple containing the fuzzy tokens. + + :raises ValueError: + Raised for invalid or unknown string format, if the provided + :class:`tzinfo` is not in a valid format, or if an invalid date + would be created. + + :raises TypeError: + Raised for non-string or character stream input. + + :raises OverflowError: + Raised if the parsed date exceeds the largest valid C integer on + your system. + """ + + if default is None: + default = datetime.datetime.now().replace(hour=0, minute=0, + second=0, microsecond=0) + + res, skipped_tokens = self._parse(timestr, **kwargs) + + if res is None: + raise ValueError("Unknown string format:", timestr) + + if len(res) == 0: + raise ValueError("String does not contain a date:", timestr) + + ret = self._build_naive(res, default) + + if not ignoretz: + ret = self._build_tzaware(ret, res, tzinfos) + + if kwargs.get('fuzzy_with_tokens', False): + return ret, skipped_tokens + else: + return ret + + class _result(_resultbase): + __slots__ = ["year", "month", "day", "weekday", + "hour", "minute", "second", "microsecond", + "tzname", "tzoffset", "ampm","any_unused_tokens"] + + def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False, + fuzzy_with_tokens=False): + """ + Private method which performs the heavy lifting of parsing, called from + ``parse()``, which passes on its ``kwargs`` to this function. + + :param timestr: + The string to parse. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM + and YMD. If set to ``None``, this value is retrieved from the + current :class:`parserinfo` object (which itself defaults to + ``False``). + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken + to be the year, otherwise the last number is taken to be the year. + If this is set to ``None``, the value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param fuzzy: + Whether to allow fuzzy parsing, allowing for string like "Today is + January 1, 2047 at 8:21:00AM". + + :param fuzzy_with_tokens: + If ``True``, ``fuzzy`` is automatically set to True, and the parser + will return a tuple where the first element is the parsed + :class:`datetime.datetime` datetimestamp and the second element is + a tuple containing the portions of the string which were ignored: + + .. doctest:: + + >>> from dateutil.parser import parse + >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) + (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) + + """ + if fuzzy_with_tokens: + fuzzy = True + + info = self.info + + if dayfirst is None: + dayfirst = info.dayfirst + + if yearfirst is None: + yearfirst = info.yearfirst + + res = self._result() + l = _timelex.split(timestr) # Splits the timestr into tokens + + skipped_idxs = [] + + # year/month/day list + ymd = _ymd() + + len_l = len(l) + i = 0 + try: + while i < len_l: + + # Check if it's a number + value_repr = l[i] + try: + value = float(value_repr) + except ValueError: + value = None + + if value is not None: + # Numeric token + i = self._parse_numeric_token(l, i, info, ymd, res, fuzzy) + + # Check weekday + elif info.weekday(l[i]) is not None: + value = info.weekday(l[i]) + res.weekday = value + + # Check month name + elif info.month(l[i]) is not None: + value = info.month(l[i]) + ymd.append(value, 'M') + + if i + 1 < len_l: + if l[i + 1] in ('-', '/'): + # Jan-01[-99] + sep = l[i + 1] + ymd.append(l[i + 2]) + + if i + 3 < len_l and l[i + 3] == sep: + # Jan-01-99 + ymd.append(l[i + 4]) + i += 2 + + i += 2 + + elif (i + 4 < len_l and l[i + 1] == l[i + 3] == ' ' and + info.pertain(l[i + 2])): + # Jan of 01 + # In this case, 01 is clearly year + if l[i + 4].isdigit(): + # Convert it here to become unambiguous + value = int(l[i + 4]) + year = str(info.convertyear(value)) + ymd.append(year, 'Y') + else: + # Wrong guess + pass + # TODO: not hit in tests + i += 4 + + # Check am/pm + elif info.ampm(l[i]) is not None: + value = info.ampm(l[i]) + val_is_ampm = self._ampm_valid(res.hour, res.ampm, fuzzy) + + if val_is_ampm: + res.hour = self._adjust_ampm(res.hour, value) + res.ampm = value + + elif fuzzy: + skipped_idxs.append(i) + + # Check for a timezone name + elif self._could_be_tzname(res.hour, res.tzname, res.tzoffset, l[i]): + res.tzname = l[i] + res.tzoffset = info.tzoffset(res.tzname) + + # Check for something like GMT+3, or BRST+3. Notice + # that it doesn't mean "I am 3 hours after GMT", but + # "my time +3 is GMT". If found, we reverse the + # logic so that timezone parsing code will get it + # right. + if i + 1 < len_l and l[i + 1] in ('+', '-'): + l[i + 1] = ('+', '-')[l[i + 1] == '+'] + res.tzoffset = None + if info.utczone(res.tzname): + # With something like GMT+3, the timezone + # is *not* GMT. + res.tzname = None + + # Check for a numbered timezone + elif res.hour is not None and l[i] in ('+', '-'): + signal = (-1, 1)[l[i] == '+'] + len_li = len(l[i + 1]) + + # TODO: check that l[i + 1] is integer? + if len_li == 4: + # -0300 + hour_offset = int(l[i + 1][:2]) + min_offset = int(l[i + 1][2:]) + elif i + 2 < len_l and l[i + 2] == ':': + # -03:00 + hour_offset = int(l[i + 1]) + min_offset = int(l[i + 3]) # TODO: Check that l[i+3] is minute-like? + i += 2 + elif len_li <= 2: + # -[0]3 + hour_offset = int(l[i + 1][:2]) + min_offset = 0 + else: + raise ValueError(timestr) + + res.tzoffset = signal * (hour_offset * 3600 + min_offset * 60) + + # Look for a timezone name between parenthesis + if (i + 5 < len_l and + info.jump(l[i + 2]) and l[i + 3] == '(' and + l[i + 5] == ')' and + 3 <= len(l[i + 4]) and + self._could_be_tzname(res.hour, res.tzname, + None, l[i + 4])): + # -0300 (BRST) + res.tzname = l[i + 4] + i += 4 + + i += 1 + + # Check jumps + elif not (info.jump(l[i]) or fuzzy): + raise ValueError(timestr) + + else: + skipped_idxs.append(i) + i += 1 + + # Process year/month/day + year, month, day = ymd.resolve_ymd(yearfirst, dayfirst) + + res.century_specified = ymd.century_specified + res.year = year + res.month = month + res.day = day + + except (IndexError, ValueError): + return None, None + + if not info.validate(res): + return None, None + + if fuzzy_with_tokens: + skipped_tokens = self._recombine_skipped(l, skipped_idxs) + return res, tuple(skipped_tokens) + else: + return res, None + + def _parse_numeric_token(self, tokens, idx, info, ymd, res, fuzzy): + # Token is a number + value_repr = tokens[idx] + try: + value = self._to_decimal(value_repr) + except Exception as e: + six.raise_from(ValueError('Unknown numeric token'), e) + + len_li = len(value_repr) + + len_l = len(tokens) + + if (len(ymd) == 3 and len_li in (2, 4) and + res.hour is None and + (idx + 1 >= len_l or + (tokens[idx + 1] != ':' and + info.hms(tokens[idx + 1]) is None))): + # 19990101T23[59] + s = tokens[idx] + res.hour = int(s[:2]) + + if len_li == 4: + res.minute = int(s[2:]) + + elif len_li == 6 or (len_li > 6 and tokens[idx].find('.') == 6): + # YYMMDD or HHMMSS[.ss] + s = tokens[idx] + + if not ymd and '.' not in tokens[idx]: + ymd.append(s[:2]) + ymd.append(s[2:4]) + ymd.append(s[4:]) + else: + # 19990101T235959[.59] + + # TODO: Check if res attributes already set. + res.hour = int(s[:2]) + res.minute = int(s[2:4]) + res.second, res.microsecond = self._parsems(s[4:]) + + elif len_li in (8, 12, 14): + # YYYYMMDD + s = tokens[idx] + ymd.append(s[:4], 'Y') + ymd.append(s[4:6]) + ymd.append(s[6:8]) + + if len_li > 8: + res.hour = int(s[8:10]) + res.minute = int(s[10:12]) + + if len_li > 12: + res.second = int(s[12:]) + + elif self._find_hms_idx(idx, tokens, info, allow_jump=True) is not None: + # HH[ ]h or MM[ ]m or SS[.ss][ ]s + hms_idx = self._find_hms_idx(idx, tokens, info, allow_jump=True) + (idx, hms) = self._parse_hms(idx, tokens, info, hms_idx) + if hms is not None: + # TODO: checking that hour/minute/second are not + # already set? + self._assign_hms(res, value_repr, hms) + + elif idx + 2 < len_l and tokens[idx + 1] == ':': + # HH:MM[:SS[.ss]] + res.hour = int(value) + value = self._to_decimal(tokens[idx + 2]) # TODO: try/except for this? + (res.minute, res.second) = self._parse_min_sec(value) + + if idx + 4 < len_l and tokens[idx + 3] == ':': + res.second, res.microsecond = self._parsems(tokens[idx + 4]) + + idx += 2 + + idx += 2 + + elif idx + 1 < len_l and tokens[idx + 1] in ('-', '/', '.'): + sep = tokens[idx + 1] + ymd.append(value_repr) + + if idx + 2 < len_l and not info.jump(tokens[idx + 2]): + if tokens[idx + 2].isdigit(): + # 01-01[-01] + ymd.append(tokens[idx + 2]) + else: + # 01-Jan[-01] + value = info.month(tokens[idx + 2]) + + if value is not None: + ymd.append(value, 'M') + else: + raise ValueError() + + if idx + 3 < len_l and tokens[idx + 3] == sep: + # We have three members + value = info.month(tokens[idx + 4]) + + if value is not None: + ymd.append(value, 'M') + else: + ymd.append(tokens[idx + 4]) + idx += 2 + + idx += 1 + idx += 1 + + elif idx + 1 >= len_l or info.jump(tokens[idx + 1]): + if idx + 2 < len_l and info.ampm(tokens[idx + 2]) is not None: + # 12 am + hour = int(value) + res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 2])) + idx += 1 + else: + # Year, month or day + ymd.append(value) + idx += 1 + + elif info.ampm(tokens[idx + 1]) is not None and (0 <= value < 24): + # 12am + hour = int(value) + res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 1])) + idx += 1 + + elif ymd.could_be_day(value): + ymd.append(value) + + elif not fuzzy: + raise ValueError() + + return idx + + def _find_hms_idx(self, idx, tokens, info, allow_jump): + len_l = len(tokens) + + if idx+1 < len_l and info.hms(tokens[idx+1]) is not None: + # There is an "h", "m", or "s" label following this token. We take + # assign the upcoming label to the current token. + # e.g. the "12" in 12h" + hms_idx = idx + 1 + + elif (allow_jump and idx+2 < len_l and tokens[idx+1] == ' ' and + info.hms(tokens[idx+2]) is not None): + # There is a space and then an "h", "m", or "s" label. + # e.g. the "12" in "12 h" + hms_idx = idx + 2 + + elif idx > 0 and info.hms(tokens[idx-1]) is not None: + # There is a "h", "m", or "s" preceeding this token. Since neither + # of the previous cases was hit, there is no label following this + # token, so we use the previous label. + # e.g. the "04" in "12h04" + hms_idx = idx-1 + + elif (1 < idx == len_l-1 and tokens[idx-1] == ' ' and + info.hms(tokens[idx-2]) is not None): + # If we are looking at the final token, we allow for a + # backward-looking check to skip over a space. + # TODO: Are we sure this is the right condition here? + hms_idx = idx - 2 + + else: + hms_idx = None + + return hms_idx + + def _assign_hms(self, res, value_repr, hms): + # See GH issue #427, fixing float rounding + value = self._to_decimal(value_repr) + + if hms == 0: + # Hour + res.hour = int(value) + if value % 1: + res.minute = int(60*(value % 1)) + + elif hms == 1: + (res.minute, res.second) = self._parse_min_sec(value) + + elif hms == 2: + (res.second, res.microsecond) = self._parsems(value_repr) + + def _could_be_tzname(self, hour, tzname, tzoffset, token): + return (hour is not None and + tzname is None and + tzoffset is None and + len(token) <= 5 and + all(x in string.ascii_uppercase for x in token)) + + def _ampm_valid(self, hour, ampm, fuzzy): + """ + For fuzzy parsing, 'a' or 'am' (both valid English words) + may erroneously trigger the AM/PM flag. Deal with that + here. + """ + val_is_ampm = True + + # If there's already an AM/PM flag, this one isn't one. + if fuzzy and ampm is not None: + val_is_ampm = False + + # If AM/PM is found and hour is not, raise a ValueError + if hour is None: + if fuzzy: + val_is_ampm = False + else: + raise ValueError('No hour specified with AM or PM flag.') + elif not 0 <= hour <= 12: + # If AM/PM is found, it's a 12 hour clock, so raise + # an error for invalid range + if fuzzy: + val_is_ampm = False + else: + raise ValueError('Invalid hour specified for 12-hour clock.') + + return val_is_ampm + + def _adjust_ampm(self, hour, ampm): + if hour < 12 and ampm == 1: + hour += 12 + elif hour == 12 and ampm == 0: + hour = 0 + return hour + + def _parse_min_sec(self, value): + # TODO: Every usage of this function sets res.second to the return + # value. Are there any cases where second will be returned as None and + # we *dont* want to set res.second = None? + minute = int(value) + second = None + + sec_remainder = value % 1 + if sec_remainder: + second = int(60 * sec_remainder) + return (minute, second) + + def _parsems(self, value): + """Parse a I[.F] seconds value into (seconds, microseconds).""" + if "." not in value: + return int(value), 0 + else: + i, f = value.split(".") + return int(i), int(f.ljust(6, "0")[:6]) + + def _parse_hms(self, idx, tokens, info, hms_idx): + # TODO: Is this going to admit a lot of false-positives for when we + # just happen to have digits and "h", "m" or "s" characters in non-date + # text? I guess hex hashes won't have that problem, but there's plenty + # of random junk out there. + if hms_idx is None: + hms = None + new_idx = idx + elif hms_idx > idx: + hms = info.hms(tokens[hms_idx]) + new_idx = hms_idx + else: + # Looking backwards, increment one. + hms = info.hms(tokens[hms_idx]) + 1 + new_idx = idx + + return (new_idx, hms) + + def _recombine_skipped(self, tokens, skipped_idxs): + """ + >>> tokens = ["foo", " ", "bar", " ", "19June2000", "baz"] + >>> skipped_idxs = [0, 1, 2, 5] + >>> _recombine_skipped(tokens, skipped_idxs) + ["foo bar", "baz"] + """ + skipped_tokens = [] + for i, idx in enumerate(sorted(skipped_idxs)): + if i > 0 and idx - 1 == skipped_idxs[i - 1]: + skipped_tokens[-1] = skipped_tokens[-1] + tokens[idx] + else: + skipped_tokens.append(tokens[idx]) + + return skipped_tokens + + def _build_tzinfo(self, tzinfos, tzname, tzoffset): + if callable(tzinfos): + tzdata = tzinfos(tzname, tzoffset) + else: + tzdata = tzinfos.get(tzname) + # handle case where tzinfo is paased an options that returns None + # eg tzinfos = {'BRST' : None} + if isinstance(tzdata, datetime.tzinfo) or tzdata is None: + tzinfo = tzdata + elif isinstance(tzdata, text_type): + tzinfo = tz.tzstr(tzdata) + elif isinstance(tzdata, integer_types): + tzinfo = tz.tzoffset(tzname, tzdata) + return tzinfo + + def _build_tzaware(self, naive, res, tzinfos): + if (callable(tzinfos) or (tzinfos and res.tzname in tzinfos)): + tzinfo = self._build_tzinfo(tzinfos, res.tzname, res.tzoffset) + aware = naive.replace(tzinfo=tzinfo) + aware = self._assign_tzname(aware, res.tzname) + + elif res.tzname and res.tzname in time.tzname: + aware = naive.replace(tzinfo=tz.tzlocal()) + + # Handle ambiguous local datetime + aware = self._assign_tzname(aware, res.tzname) + + # This is mostly relevant for winter GMT zones parsed in the UK + if (aware.tzname() != res.tzname and + res.tzname in self.info.UTCZONE): + aware = aware.replace(tzinfo=tz.tzutc()) + + elif res.tzoffset == 0: + aware = naive.replace(tzinfo=tz.tzutc()) + + elif res.tzoffset: + aware = naive.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) + + elif not res.tzname and not res.tzoffset: + # i.e. no timezone information was found. + aware = naive + + elif res.tzname: + # tz-like string was parsed but we don't know what to do + # with it + warnings.warn("tzname {tzname} identified but not understood. " + "Pass `tzinfos` argument in order to correctly " + "return a timezone-aware datetime. In a future " + "version, this will raise an " + "exception.".format(tzname=res.tzname), + category=UnknownTimezoneWarning) + aware = naive + + return aware + + def _build_naive(self, res, default): + repl = {} + for attr in ("year", "month", "day", "hour", + "minute", "second", "microsecond"): + value = getattr(res, attr) + if value is not None: + repl[attr] = value + + if 'day' not in repl: + # If the default day exceeds the last day of the month, fall back + # to the end of the month. + cyear = default.year if res.year is None else res.year + cmonth = default.month if res.month is None else res.month + cday = default.day if res.day is None else res.day + + if cday > monthrange(cyear, cmonth)[1]: + repl['day'] = monthrange(cyear, cmonth)[1] + + naive = default.replace(**repl) + + if res.weekday is not None and not res.day: + naive = naive + relativedelta.relativedelta(weekday=res.weekday) + + return naive + + def _assign_tzname(self, dt, tzname): + if dt.tzname() != tzname: + new_dt = tz.enfold(dt, fold=1) + if new_dt.tzname() == tzname: + return new_dt + + return dt + + def _to_decimal(self, val): + try: + decimal_value = Decimal(val) + # See GH 662, edge case, infinite value should not be converted via `_to_decimal` + if not decimal_value.is_finite(): + raise ValueError("Converted decimal value is infinite or NaN") + except Exception as e: + msg = "Could not convert %s to decimal" % val + six.raise_from(ValueError(msg), e) + else: + return decimal_value + + +DEFAULTPARSER = parser() + + +def parse(timestr, parserinfo=None, **kwargs): + """ + + Parse a string in one of the supported formats, using the + ``parserinfo`` parameters. + + :param timestr: + A string containing a date/time stamp. + + :param parserinfo: + A :class:`parserinfo` object containing parameters for the parser. + If ``None``, the default arguments to the :class:`parserinfo` + constructor are used. + + The ``**kwargs`` parameter takes the following keyword arguments: + + :param default: + The default datetime object, if this is a datetime object and not + ``None``, elements specified in ``timestr`` replace elements in the + default object. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a naive + :class:`datetime` object is returned. + + :param tzinfos: + Additional time zone names / aliases which may be present in the + string. This argument maps time zone names (and optionally offsets + from those time zones) to time zones. This parameter can be a + dictionary with timezone aliases mapping time zone names to time + zones or a function taking two parameters (``tzname`` and + ``tzoffset``) and returning a time zone. + + The timezones to which the names are mapped can be an integer + offset from UTC in seconds or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} + >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) + >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, + tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) + + This parameter is ignored if ``ignoretz`` is set. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM and + YMD. If set to ``None``, this value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken to + be the year, otherwise the last number is taken to be the year. If + this is set to ``None``, the value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param fuzzy: + Whether to allow fuzzy parsing, allowing for string like "Today is + January 1, 2047 at 8:21:00AM". + + :param fuzzy_with_tokens: + If ``True``, ``fuzzy`` is automatically set to True, and the parser + will return a tuple where the first element is the parsed + :class:`datetime.datetime` datetimestamp and the second element is + a tuple containing the portions of the string which were ignored: + + .. doctest:: + + >>> from dateutil.parser import parse + >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) + (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) + + :return: + Returns a :class:`datetime.datetime` object or, if the + ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the + first element being a :class:`datetime.datetime` object, the second + a tuple containing the fuzzy tokens. + + :raises ValueError: + Raised for invalid or unknown string format, if the provided + :class:`tzinfo` is not in a valid format, or if an invalid date + would be created. + + :raises OverflowError: + Raised if the parsed date exceeds the largest valid C integer on + your system. + """ + if parserinfo: + return parser(parserinfo).parse(timestr, **kwargs) + else: + return DEFAULTPARSER.parse(timestr, **kwargs) + + +class _tzparser(object): + + class _result(_resultbase): + + __slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset", + "start", "end"] + + class _attr(_resultbase): + __slots__ = ["month", "week", "weekday", + "yday", "jyday", "day", "time"] + + def __repr__(self): + return self._repr("") + + def __init__(self): + _resultbase.__init__(self) + self.start = self._attr() + self.end = self._attr() + + def parse(self, tzstr): + res = self._result() + l = [x for x in re.split(r'([,:.]|[a-zA-Z]+|[0-9]+)',tzstr) if x] + used_idxs = list() + try: + + len_l = len(l) + + i = 0 + while i < len_l: + # BRST+3[BRDT[+2]] + j = i + while j < len_l and not [x for x in l[j] + if x in "0123456789:,-+"]: + j += 1 + if j != i: + if not res.stdabbr: + offattr = "stdoffset" + res.stdabbr = "".join(l[i:j]) + else: + offattr = "dstoffset" + res.dstabbr = "".join(l[i:j]) + + for ii in range(j): + used_idxs.append(ii) + i = j + if (i < len_l and (l[i] in ('+', '-') or l[i][0] in + "0123456789")): + if l[i] in ('+', '-'): + # Yes, that's right. See the TZ variable + # documentation. + signal = (1, -1)[l[i] == '+'] + used_idxs.append(i) + i += 1 + else: + signal = -1 + len_li = len(l[i]) + if len_li == 4: + # -0300 + setattr(res, offattr, (int(l[i][:2]) * 3600 + + int(l[i][2:]) * 60) * signal) + elif i + 1 < len_l and l[i + 1] == ':': + # -03:00 + setattr(res, offattr, + (int(l[i]) * 3600 + + int(l[i + 2]) * 60) * signal) + used_idxs.append(i) + i += 2 + elif len_li <= 2: + # -[0]3 + setattr(res, offattr, + int(l[i][:2]) * 3600 * signal) + else: + return None + used_idxs.append(i) + i += 1 + if res.dstabbr: + break + else: + break + + + if i < len_l: + for j in range(i, len_l): + if l[j] == ';': + l[j] = ',' + + assert l[i] == ',' + + i += 1 + + if i >= len_l: + pass + elif (8 <= l.count(',') <= 9 and + not [y for x in l[i:] if x != ',' + for y in x if y not in "0123456789+-"]): + # GMT0BST,3,0,30,3600,10,0,26,7200[,3600] + for x in (res.start, res.end): + x.month = int(l[i]) + used_idxs.append(i) + i += 2 + if l[i] == '-': + value = int(l[i + 1]) * -1 + used_idxs.append(i) + i += 1 + else: + value = int(l[i]) + used_idxs.append(i) + i += 2 + if value: + x.week = value + x.weekday = (int(l[i]) - 1) % 7 + else: + x.day = int(l[i]) + used_idxs.append(i) + i += 2 + x.time = int(l[i]) + used_idxs.append(i) + i += 2 + if i < len_l: + if l[i] in ('-', '+'): + signal = (-1, 1)[l[i] == "+"] + used_idxs.append(i) + i += 1 + else: + signal = 1 + used_idxs.append(i) + res.dstoffset = (res.stdoffset + int(l[i]) * signal) + + # This was a made-up format that is not in normal use + warn(('Parsed time zone "%s"' % tzstr) + + 'is in a non-standard dateutil-specific format, which ' + + 'is now deprecated; support for parsing this format ' + + 'will be removed in future versions. It is recommended ' + + 'that you switch to a standard format like the GNU ' + + 'TZ variable format.', tz.DeprecatedTzFormatWarning) + elif (l.count(',') == 2 and l[i:].count('/') <= 2 and + not [y for x in l[i:] if x not in (',', '/', 'J', 'M', + '.', '-', ':') + for y in x if y not in "0123456789"]): + for x in (res.start, res.end): + if l[i] == 'J': + # non-leap year day (1 based) + used_idxs.append(i) + i += 1 + x.jyday = int(l[i]) + elif l[i] == 'M': + # month[-.]week[-.]weekday + used_idxs.append(i) + i += 1 + x.month = int(l[i]) + used_idxs.append(i) + i += 1 + assert l[i] in ('-', '.') + used_idxs.append(i) + i += 1 + x.week = int(l[i]) + if x.week == 5: + x.week = -1 + used_idxs.append(i) + i += 1 + assert l[i] in ('-', '.') + used_idxs.append(i) + i += 1 + x.weekday = (int(l[i]) - 1) % 7 + else: + # year day (zero based) + x.yday = int(l[i]) + 1 + + used_idxs.append(i) + i += 1 + + if i < len_l and l[i] == '/': + used_idxs.append(i) + i += 1 + # start time + len_li = len(l[i]) + if len_li == 4: + # -0300 + x.time = (int(l[i][:2]) * 3600 + + int(l[i][2:]) * 60) + elif i + 1 < len_l and l[i + 1] == ':': + # -03:00 + x.time = int(l[i]) * 3600 + int(l[i + 2]) * 60 + used_idxs.append(i) + i += 2 + if i + 1 < len_l and l[i + 1] == ':': + used_idxs.append(i) + i += 2 + x.time += int(l[i]) + elif len_li <= 2: + # -[0]3 + x.time = (int(l[i][:2]) * 3600) + else: + return None + used_idxs.append(i) + i += 1 + + assert i == len_l or l[i] == ',' + + i += 1 + + assert i >= len_l + + except (IndexError, ValueError, AssertionError): + return None + + unused_idxs = set(range(len_l)).difference(used_idxs) + res.any_unused_tokens = not {l[n] for n in unused_idxs}.issubset({",",":"}) + return res + + +DEFAULTTZPARSER = _tzparser() + + +def _parsetz(tzstr): + return DEFAULTTZPARSER.parse(tzstr) + +class UnknownTimezoneWarning(RuntimeWarning): + """Raised when the parser finds a timezone it cannot parse into a tzinfo""" +# vim:ts=4:sw=4:et diff --git a/modules/dateutil/parser/isoparser.py b/modules/dateutil/parser/isoparser.py new file mode 100644 index 0000000..cd27f93 --- /dev/null +++ b/modules/dateutil/parser/isoparser.py @@ -0,0 +1,406 @@ +# -*- coding: utf-8 -*- +""" +This module offers a parser for ISO-8601 strings + +It is intended to support all valid date, time and datetime formats per the +ISO-8601 specification. + +..versionadded:: 2.7.0 +""" +from datetime import datetime, timedelta, time, date +import calendar +from dateutil import tz + +from functools import wraps + +import re +import six + +__all__ = ["isoparse", "isoparser"] + + +def _takes_ascii(f): + @wraps(f) + def func(self, str_in, *args, **kwargs): + # If it's a stream, read the whole thing + str_in = getattr(str_in, 'read', lambda: str_in)() + + # If it's unicode, turn it into bytes, since ISO-8601 only covers ASCII + if isinstance(str_in, six.text_type): + # ASCII is the same in UTF-8 + try: + str_in = str_in.encode('ascii') + except UnicodeEncodeError as e: + msg = 'ISO-8601 strings should contain only ASCII characters' + six.raise_from(ValueError(msg), e) + + return f(self, str_in, *args, **kwargs) + + return func + + +class isoparser(object): + def __init__(self, sep=None): + """ + :param sep: + A single character that separates date and time portions. If + ``None``, the parser will accept any single character. + For strict ISO-8601 adherence, pass ``'T'``. + """ + if sep is not None: + if (len(sep) != 1 or ord(sep) >= 128 or sep in '0123456789'): + raise ValueError('Separator must be a single, non-numeric ' + + 'ASCII character') + + sep = sep.encode('ascii') + + self._sep = sep + + @_takes_ascii + def isoparse(self, dt_str): + """ + Parse an ISO-8601 datetime string into a :class:`datetime.datetime`. + + An ISO-8601 datetime string consists of a date portion, followed + optionally by a time portion - the date and time portions are separated + by a single character separator, which is ``T`` in the official + standard. Incomplete date formats (such as ``YYYY-MM``) may *not* be + combined with a time portion. + + Supported date formats are: + + Common: + + - ``YYYY`` + - ``YYYY-MM`` or ``YYYYMM`` + - ``YYYY-MM-DD`` or ``YYYYMMDD`` + + Uncommon: + + - ``YYYY-Www`` or ``YYYYWww`` - ISO week (day defaults to 0) + - ``YYYY-Www-D`` or ``YYYYWwwD`` - ISO week and day + + The ISO week and day numbering follows the same logic as + :func:`datetime.date.isocalendar`. + + Supported time formats are: + + - ``hh`` + - ``hh:mm`` or ``hhmm`` + - ``hh:mm:ss`` or ``hhmmss`` + - ``hh:mm:ss.sss`` or ``hh:mm:ss.ssssss`` (3-6 sub-second digits) + + Midnight is a special case for `hh`, as the standard supports both + 00:00 and 24:00 as a representation. + + .. caution:: + + Support for fractional components other than seconds is part of the + ISO-8601 standard, but is not currently implemented in this parser. + + Supported time zone offset formats are: + + - `Z` (UTC) + - `±HH:MM` + - `±HHMM` + - `±HH` + + Offsets will be represented as :class:`dateutil.tz.tzoffset` objects, + with the exception of UTC, which will be represented as + :class:`dateutil.tz.tzutc`. Time zone offsets equivalent to UTC (such + as `+00:00`) will also be represented as :class:`dateutil.tz.tzutc`. + + :param dt_str: + A string or stream containing only an ISO-8601 datetime string + + :return: + Returns a :class:`datetime.datetime` representing the string. + Unspecified components default to their lowest value. + + .. warning:: + + As of version 2.7.0, the strictness of the parser should not be + considered a stable part of the contract. Any valid ISO-8601 string + that parses correctly with the default settings will continue to + parse correctly in future versions, but invalid strings that + currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not + guaranteed to continue failing in future versions if they encode + a valid date. + + .. versionadded:: 2.7.0 + """ + components, pos = self._parse_isodate(dt_str) + + if len(dt_str) > pos: + if self._sep is None or dt_str[pos:pos + 1] == self._sep: + components += self._parse_isotime(dt_str[pos + 1:]) + else: + raise ValueError('String contains unknown ISO components') + + return datetime(*components) + + @_takes_ascii + def parse_isodate(self, datestr): + """ + Parse the date portion of an ISO string. + + :param datestr: + The string portion of an ISO string, without a separator + + :return: + Returns a :class:`datetime.date` object + """ + components, pos = self._parse_isodate(datestr) + if pos < len(datestr): + raise ValueError('String contains unknown ISO ' + + 'components: {}'.format(datestr)) + return date(*components) + + @_takes_ascii + def parse_isotime(self, timestr): + """ + Parse the time portion of an ISO string. + + :param timestr: + The time portion of an ISO string, without a separator + + :return: + Returns a :class:`datetime.time` object + """ + return time(*self._parse_isotime(timestr)) + + @_takes_ascii + def parse_tzstr(self, tzstr, zero_as_utc=True): + """ + Parse a valid ISO time zone string. + + See :func:`isoparser.isoparse` for details on supported formats. + + :param tzstr: + A string representing an ISO time zone offset + + :param zero_as_utc: + Whether to return :class:`dateutil.tz.tzutc` for zero-offset zones + + :return: + Returns :class:`dateutil.tz.tzoffset` for offsets and + :class:`dateutil.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is + specified) offsets equivalent to UTC. + """ + return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc) + + # Constants + _MICROSECOND_END_REGEX = re.compile(b'[-+Z]+') + _DATE_SEP = b'-' + _TIME_SEP = b':' + _MICRO_SEP = b'.' + + def _parse_isodate(self, dt_str): + try: + return self._parse_isodate_common(dt_str) + except ValueError: + return self._parse_isodate_uncommon(dt_str) + + def _parse_isodate_common(self, dt_str): + len_str = len(dt_str) + components = [1, 1, 1] + + if len_str < 4: + raise ValueError('ISO string too short') + + # Year + components[0] = int(dt_str[0:4]) + pos = 4 + if pos >= len_str: + return components, pos + + has_sep = dt_str[pos:pos + 1] == self._DATE_SEP + if has_sep: + pos += 1 + + # Month + if len_str - pos < 2: + raise ValueError('Invalid common month') + + components[1] = int(dt_str[pos:pos + 2]) + pos += 2 + + if pos >= len_str: + if has_sep: + return components, pos + else: + raise ValueError('Invalid ISO format') + + if has_sep: + if dt_str[pos:pos + 1] != self._DATE_SEP: + raise ValueError('Invalid separator in ISO string') + pos += 1 + + # Day + if len_str - pos < 2: + raise ValueError('Invalid common day') + components[2] = int(dt_str[pos:pos + 2]) + return components, pos + 2 + + def _parse_isodate_uncommon(self, dt_str): + if len(dt_str) < 4: + raise ValueError('ISO string too short') + + # All ISO formats start with the year + year = int(dt_str[0:4]) + + has_sep = dt_str[4:5] == self._DATE_SEP + + pos = 4 + has_sep # Skip '-' if it's there + if dt_str[pos:pos + 1] == b'W': + # YYYY-?Www-?D? + pos += 1 + weekno = int(dt_str[pos:pos + 2]) + pos += 2 + + dayno = 1 + if len(dt_str) > pos: + if (dt_str[pos:pos + 1] == self._DATE_SEP) != has_sep: + raise ValueError('Inconsistent use of dash separator') + + pos += has_sep + + dayno = int(dt_str[pos:pos + 1]) + pos += 1 + + base_date = self._calculate_weekdate(year, weekno, dayno) + else: + # YYYYDDD or YYYY-DDD + if len(dt_str) - pos < 3: + raise ValueError('Invalid ordinal day') + + ordinal_day = int(dt_str[pos:pos + 3]) + pos += 3 + + if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)): + raise ValueError('Invalid ordinal day' + + ' {} for year {}'.format(ordinal_day, year)) + + base_date = date(year, 1, 1) + timedelta(days=ordinal_day - 1) + + components = [base_date.year, base_date.month, base_date.day] + return components, pos + + def _calculate_weekdate(self, year, week, day): + """ + Calculate the day of corresponding to the ISO year-week-day calendar. + + This function is effectively the inverse of + :func:`datetime.date.isocalendar`. + + :param year: + The year in the ISO calendar + + :param week: + The week in the ISO calendar - range is [1, 53] + + :param day: + The day in the ISO calendar - range is [1 (MON), 7 (SUN)] + + :return: + Returns a :class:`datetime.date` + """ + if not 0 < week < 54: + raise ValueError('Invalid week: {}'.format(week)) + + if not 0 < day < 8: # Range is 1-7 + raise ValueError('Invalid weekday: {}'.format(day)) + + # Get week 1 for the specific year: + jan_4 = date(year, 1, 4) # Week 1 always has January 4th in it + week_1 = jan_4 - timedelta(days=jan_4.isocalendar()[2] - 1) + + # Now add the specific number of weeks and days to get what we want + week_offset = (week - 1) * 7 + (day - 1) + return week_1 + timedelta(days=week_offset) + + def _parse_isotime(self, timestr): + len_str = len(timestr) + components = [0, 0, 0, 0, None] + pos = 0 + comp = -1 + + if len(timestr) < 2: + raise ValueError('ISO time too short') + + has_sep = len_str >= 3 and timestr[2:3] == self._TIME_SEP + + while pos < len_str and comp < 5: + comp += 1 + + if timestr[pos:pos + 1] in b'-+Z': + # Detect time zone boundary + components[-1] = self._parse_tzstr(timestr[pos:]) + pos = len_str + break + + if comp < 3: + # Hour, minute, second + components[comp] = int(timestr[pos:pos + 2]) + pos += 2 + if (has_sep and pos < len_str and + timestr[pos:pos + 1] == self._TIME_SEP): + pos += 1 + + if comp == 3: + # Microsecond + if timestr[pos:pos + 1] != self._MICRO_SEP: + continue + + pos += 1 + us_str = self._MICROSECOND_END_REGEX.split(timestr[pos:pos + 6], + 1)[0] + + components[comp] = int(us_str) * 10**(6 - len(us_str)) + pos += len(us_str) + + if pos < len_str: + raise ValueError('Unused components in ISO string') + + if components[0] == 24: + # Standard supports 00:00 and 24:00 as representations of midnight + if any(component != 0 for component in components[1:4]): + raise ValueError('Hour may only be 24 at 24:00:00.000') + components[0] = 0 + + return components + + def _parse_tzstr(self, tzstr, zero_as_utc=True): + if tzstr == b'Z': + return tz.tzutc() + + if len(tzstr) not in {3, 5, 6}: + raise ValueError('Time zone offset must be 1, 3, 5 or 6 characters') + + if tzstr[0:1] == b'-': + mult = -1 + elif tzstr[0:1] == b'+': + mult = 1 + else: + raise ValueError('Time zone offset requires sign') + + hours = int(tzstr[1:3]) + if len(tzstr) == 3: + minutes = 0 + else: + minutes = int(tzstr[(4 if tzstr[3:4] == self._TIME_SEP else 3):]) + + if zero_as_utc and hours == 0 and minutes == 0: + return tz.tzutc() + else: + if minutes > 59: + raise ValueError('Invalid minutes in time zone offset') + + if hours > 23: + raise ValueError('Invalid hours in time zone offset') + + return tz.tzoffset(None, mult * (hours * 60 + minutes) * 60) + + +DEFAULT_ISOPARSER = isoparser() +isoparse = DEFAULT_ISOPARSER.isoparse diff --git a/modules/dateutil/relativedelta.py b/modules/dateutil/relativedelta.py new file mode 100644 index 0000000..1e0d616 --- /dev/null +++ b/modules/dateutil/relativedelta.py @@ -0,0 +1,590 @@ +# -*- coding: utf-8 -*- +import datetime +import calendar + +import operator +from math import copysign + +from six import integer_types +from warnings import warn + +from ._common import weekday + +MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) + +__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] + + +class relativedelta(object): + """ + The relativedelta type is based on the specification of the excellent + work done by M.-A. Lemburg in his + `mx.DateTime `_ extension. + However, notice that this type does *NOT* implement the same algorithm as + his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. + + There are two different ways to build a relativedelta instance. The + first one is passing it two date/datetime classes:: + + relativedelta(datetime1, datetime2) + + The second one is passing it any number of the following keyword arguments:: + + relativedelta(arg1=x,arg2=y,arg3=z...) + + year, month, day, hour, minute, second, microsecond: + Absolute information (argument is singular); adding or subtracting a + relativedelta with absolute information does not perform an arithmetic + operation, but rather REPLACES the corresponding value in the + original datetime with the value(s) in relativedelta. + + years, months, weeks, days, hours, minutes, seconds, microseconds: + Relative information, may be negative (argument is plural); adding + or subtracting a relativedelta with relative information performs + the corresponding aritmetic operation on the original datetime value + with the information in the relativedelta. + + weekday: + One of the weekday instances (MO, TU, etc). These + instances may receive a parameter N, specifying the Nth + weekday, which could be positive or negative (like MO(+1) + or MO(-2). Not specifying it is the same as specifying + +1. You can also use an integer, where 0=MO. Notice that + if the calculated date is already Monday, for example, + using MO(1) or MO(-1) won't change the day. + + leapdays: + Will add given days to the date found, if year is a leap + year, and the date found is post 28 of february. + + yearday, nlyearday: + Set the yearday or the non-leap year day (jump leap days). + These are converted to day/month/leapdays information. + + There are relative and absolute forms of the keyword + arguments. The plural is relative, and the singular is + absolute. For each argument in the order below, the absolute form + is applied first (by setting each attribute to that value) and + then the relative form (by adding the value to the attribute). + + The order of attributes considered when this relativedelta is + added to a datetime is: + + 1. Year + 2. Month + 3. Day + 4. Hours + 5. Minutes + 6. Seconds + 7. Microseconds + + Finally, weekday is applied, using the rule described above. + + For example + + >>> dt = datetime(2018, 4, 9, 13, 37, 0) + >>> delta = relativedelta(hours=25, day=1, weekday=MO(1)) + datetime(2018, 4, 2, 14, 37, 0) + + First, the day is set to 1 (the first of the month), then 25 hours + are added, to get to the 2nd day and 14th hour, finally the + weekday is applied, but since the 2nd is already a Monday there is + no effect. + + """ + + def __init__(self, dt1=None, dt2=None, + years=0, months=0, days=0, leapdays=0, weeks=0, + hours=0, minutes=0, seconds=0, microseconds=0, + year=None, month=None, day=None, weekday=None, + yearday=None, nlyearday=None, + hour=None, minute=None, second=None, microsecond=None): + + if dt1 and dt2: + # datetime is a subclass of date. So both must be date + if not (isinstance(dt1, datetime.date) and + isinstance(dt2, datetime.date)): + raise TypeError("relativedelta only diffs datetime/date") + + # We allow two dates, or two datetimes, so we coerce them to be + # of the same type + if (isinstance(dt1, datetime.datetime) != + isinstance(dt2, datetime.datetime)): + if not isinstance(dt1, datetime.datetime): + dt1 = datetime.datetime.fromordinal(dt1.toordinal()) + elif not isinstance(dt2, datetime.datetime): + dt2 = datetime.datetime.fromordinal(dt2.toordinal()) + + self.years = 0 + self.months = 0 + self.days = 0 + self.leapdays = 0 + self.hours = 0 + self.minutes = 0 + self.seconds = 0 + self.microseconds = 0 + self.year = None + self.month = None + self.day = None + self.weekday = None + self.hour = None + self.minute = None + self.second = None + self.microsecond = None + self._has_time = 0 + + # Get year / month delta between the two + months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month) + self._set_months(months) + + # Remove the year/month delta so the timedelta is just well-defined + # time units (seconds, days and microseconds) + dtm = self.__radd__(dt2) + + # If we've overshot our target, make an adjustment + if dt1 < dt2: + compare = operator.gt + increment = 1 + else: + compare = operator.lt + increment = -1 + + while compare(dt1, dtm): + months += increment + self._set_months(months) + dtm = self.__radd__(dt2) + + # Get the timedelta between the "months-adjusted" date and dt1 + delta = dt1 - dtm + self.seconds = delta.seconds + delta.days * 86400 + self.microseconds = delta.microseconds + else: + # Check for non-integer values in integer-only quantities + if any(x is not None and x != int(x) for x in (years, months)): + raise ValueError("Non-integer years and months are " + "ambiguous and not currently supported.") + + # Relative information + self.years = int(years) + self.months = int(months) + self.days = days + weeks * 7 + self.leapdays = leapdays + self.hours = hours + self.minutes = minutes + self.seconds = seconds + self.microseconds = microseconds + + # Absolute information + self.year = year + self.month = month + self.day = day + self.hour = hour + self.minute = minute + self.second = second + self.microsecond = microsecond + + if any(x is not None and int(x) != x + for x in (year, month, day, hour, + minute, second, microsecond)): + # For now we'll deprecate floats - later it'll be an error. + warn("Non-integer value passed as absolute information. " + + "This is not a well-defined condition and will raise " + + "errors in future versions.", DeprecationWarning) + + if isinstance(weekday, integer_types): + self.weekday = weekdays[weekday] + else: + self.weekday = weekday + + yday = 0 + if nlyearday: + yday = nlyearday + elif yearday: + yday = yearday + if yearday > 59: + self.leapdays = -1 + if yday: + ydayidx = [31, 59, 90, 120, 151, 181, 212, + 243, 273, 304, 334, 366] + for idx, ydays in enumerate(ydayidx): + if yday <= ydays: + self.month = idx+1 + if idx == 0: + self.day = yday + else: + self.day = yday-ydayidx[idx-1] + break + else: + raise ValueError("invalid year day (%d)" % yday) + + self._fix() + + def _fix(self): + if abs(self.microseconds) > 999999: + s = _sign(self.microseconds) + div, mod = divmod(self.microseconds * s, 1000000) + self.microseconds = mod * s + self.seconds += div * s + if abs(self.seconds) > 59: + s = _sign(self.seconds) + div, mod = divmod(self.seconds * s, 60) + self.seconds = mod * s + self.minutes += div * s + if abs(self.minutes) > 59: + s = _sign(self.minutes) + div, mod = divmod(self.minutes * s, 60) + self.minutes = mod * s + self.hours += div * s + if abs(self.hours) > 23: + s = _sign(self.hours) + div, mod = divmod(self.hours * s, 24) + self.hours = mod * s + self.days += div * s + if abs(self.months) > 11: + s = _sign(self.months) + div, mod = divmod(self.months * s, 12) + self.months = mod * s + self.years += div * s + if (self.hours or self.minutes or self.seconds or self.microseconds + or self.hour is not None or self.minute is not None or + self.second is not None or self.microsecond is not None): + self._has_time = 1 + else: + self._has_time = 0 + + @property + def weeks(self): + return int(self.days / 7.0) + + @weeks.setter + def weeks(self, value): + self.days = self.days - (self.weeks * 7) + value * 7 + + def _set_months(self, months): + self.months = months + if abs(self.months) > 11: + s = _sign(self.months) + div, mod = divmod(self.months * s, 12) + self.months = mod * s + self.years = div * s + else: + self.years = 0 + + def normalized(self): + """ + Return a version of this object represented entirely using integer + values for the relative attributes. + + >>> relativedelta(days=1.5, hours=2).normalized() + relativedelta(days=1, hours=14) + + :return: + Returns a :class:`dateutil.relativedelta.relativedelta` object. + """ + # Cascade remainders down (rounding each to roughly nearest microsecond) + days = int(self.days) + + hours_f = round(self.hours + 24 * (self.days - days), 11) + hours = int(hours_f) + + minutes_f = round(self.minutes + 60 * (hours_f - hours), 10) + minutes = int(minutes_f) + + seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8) + seconds = int(seconds_f) + + microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds)) + + # Constructor carries overflow back up with call to _fix() + return self.__class__(years=self.years, months=self.months, + days=days, hours=hours, minutes=minutes, + seconds=seconds, microseconds=microseconds, + leapdays=self.leapdays, year=self.year, + month=self.month, day=self.day, + weekday=self.weekday, hour=self.hour, + minute=self.minute, second=self.second, + microsecond=self.microsecond) + + def __add__(self, other): + if isinstance(other, relativedelta): + return self.__class__(years=other.years + self.years, + months=other.months + self.months, + days=other.days + self.days, + hours=other.hours + self.hours, + minutes=other.minutes + self.minutes, + seconds=other.seconds + self.seconds, + microseconds=(other.microseconds + + self.microseconds), + leapdays=other.leapdays or self.leapdays, + year=(other.year if other.year is not None + else self.year), + month=(other.month if other.month is not None + else self.month), + day=(other.day if other.day is not None + else self.day), + weekday=(other.weekday if other.weekday is not None + else self.weekday), + hour=(other.hour if other.hour is not None + else self.hour), + minute=(other.minute if other.minute is not None + else self.minute), + second=(other.second if other.second is not None + else self.second), + microsecond=(other.microsecond if other.microsecond + is not None else + self.microsecond)) + if isinstance(other, datetime.timedelta): + return self.__class__(years=self.years, + months=self.months, + days=self.days + other.days, + hours=self.hours, + minutes=self.minutes, + seconds=self.seconds + other.seconds, + microseconds=self.microseconds + other.microseconds, + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + if not isinstance(other, datetime.date): + return NotImplemented + elif self._has_time and not isinstance(other, datetime.datetime): + other = datetime.datetime.fromordinal(other.toordinal()) + year = (self.year or other.year)+self.years + month = self.month or other.month + if self.months: + assert 1 <= abs(self.months) <= 12 + month += self.months + if month > 12: + year += 1 + month -= 12 + elif month < 1: + year -= 1 + month += 12 + day = min(calendar.monthrange(year, month)[1], + self.day or other.day) + repl = {"year": year, "month": month, "day": day} + for attr in ["hour", "minute", "second", "microsecond"]: + value = getattr(self, attr) + if value is not None: + repl[attr] = value + days = self.days + if self.leapdays and month > 2 and calendar.isleap(year): + days += self.leapdays + ret = (other.replace(**repl) + + datetime.timedelta(days=days, + hours=self.hours, + minutes=self.minutes, + seconds=self.seconds, + microseconds=self.microseconds)) + if self.weekday: + weekday, nth = self.weekday.weekday, self.weekday.n or 1 + jumpdays = (abs(nth) - 1) * 7 + if nth > 0: + jumpdays += (7 - ret.weekday() + weekday) % 7 + else: + jumpdays += (ret.weekday() - weekday) % 7 + jumpdays *= -1 + ret += datetime.timedelta(days=jumpdays) + return ret + + def __radd__(self, other): + return self.__add__(other) + + def __rsub__(self, other): + return self.__neg__().__radd__(other) + + def __sub__(self, other): + if not isinstance(other, relativedelta): + return NotImplemented # In case the other object defines __rsub__ + return self.__class__(years=self.years - other.years, + months=self.months - other.months, + days=self.days - other.days, + hours=self.hours - other.hours, + minutes=self.minutes - other.minutes, + seconds=self.seconds - other.seconds, + microseconds=self.microseconds - other.microseconds, + leapdays=self.leapdays or other.leapdays, + year=(self.year if self.year is not None + else other.year), + month=(self.month if self.month is not None else + other.month), + day=(self.day if self.day is not None else + other.day), + weekday=(self.weekday if self.weekday is not None else + other.weekday), + hour=(self.hour if self.hour is not None else + other.hour), + minute=(self.minute if self.minute is not None else + other.minute), + second=(self.second if self.second is not None else + other.second), + microsecond=(self.microsecond if self.microsecond + is not None else + other.microsecond)) + + def __abs__(self): + return self.__class__(years=abs(self.years), + months=abs(self.months), + days=abs(self.days), + hours=abs(self.hours), + minutes=abs(self.minutes), + seconds=abs(self.seconds), + microseconds=abs(self.microseconds), + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + def __neg__(self): + return self.__class__(years=-self.years, + months=-self.months, + days=-self.days, + hours=-self.hours, + minutes=-self.minutes, + seconds=-self.seconds, + microseconds=-self.microseconds, + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + def __bool__(self): + return not (not self.years and + not self.months and + not self.days and + not self.hours and + not self.minutes and + not self.seconds and + not self.microseconds and + not self.leapdays and + self.year is None and + self.month is None and + self.day is None and + self.weekday is None and + self.hour is None and + self.minute is None and + self.second is None and + self.microsecond is None) + # Compatibility with Python 2.x + __nonzero__ = __bool__ + + def __mul__(self, other): + try: + f = float(other) + except TypeError: + return NotImplemented + + return self.__class__(years=int(self.years * f), + months=int(self.months * f), + days=int(self.days * f), + hours=int(self.hours * f), + minutes=int(self.minutes * f), + seconds=int(self.seconds * f), + microseconds=int(self.microseconds * f), + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + __rmul__ = __mul__ + + def __eq__(self, other): + if not isinstance(other, relativedelta): + return NotImplemented + if self.weekday or other.weekday: + if not self.weekday or not other.weekday: + return False + if self.weekday.weekday != other.weekday.weekday: + return False + n1, n2 = self.weekday.n, other.weekday.n + if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): + return False + return (self.years == other.years and + self.months == other.months and + self.days == other.days and + self.hours == other.hours and + self.minutes == other.minutes and + self.seconds == other.seconds and + self.microseconds == other.microseconds and + self.leapdays == other.leapdays and + self.year == other.year and + self.month == other.month and + self.day == other.day and + self.hour == other.hour and + self.minute == other.minute and + self.second == other.second and + self.microsecond == other.microsecond) + + def __hash__(self): + return hash(( + self.weekday, + self.years, + self.months, + self.days, + self.hours, + self.minutes, + self.seconds, + self.microseconds, + self.leapdays, + self.year, + self.month, + self.day, + self.hour, + self.minute, + self.second, + self.microsecond, + )) + + def __ne__(self, other): + return not self.__eq__(other) + + def __div__(self, other): + try: + reciprocal = 1 / float(other) + except TypeError: + return NotImplemented + + return self.__mul__(reciprocal) + + __truediv__ = __div__ + + def __repr__(self): + l = [] + for attr in ["years", "months", "days", "leapdays", + "hours", "minutes", "seconds", "microseconds"]: + value = getattr(self, attr) + if value: + l.append("{attr}={value:+g}".format(attr=attr, value=value)) + for attr in ["year", "month", "day", "weekday", + "hour", "minute", "second", "microsecond"]: + value = getattr(self, attr) + if value is not None: + l.append("{attr}={value}".format(attr=attr, value=repr(value))) + return "{classname}({attrs})".format(classname=self.__class__.__name__, + attrs=", ".join(l)) + + +def _sign(x): + return int(copysign(1, x)) + +# vim:ts=4:sw=4:et diff --git a/modules/dateutil/rrule.py b/modules/dateutil/rrule.py new file mode 100644 index 0000000..8e9c2af --- /dev/null +++ b/modules/dateutil/rrule.py @@ -0,0 +1,1672 @@ +# -*- coding: utf-8 -*- +""" +The rrule module offers a small, complete, and very fast, implementation of +the recurrence rules documented in the +`iCalendar RFC `_, +including support for caching of results. +""" +import itertools +import datetime +import calendar +import re +import sys + +try: + from math import gcd +except ImportError: + from fractions import gcd + +from six import advance_iterator, integer_types +from six.moves import _thread, range +import heapq + +from ._common import weekday as weekdaybase +from .tz import tzutc, tzlocal + +# For warning about deprecation of until and count +from warnings import warn + +__all__ = ["rrule", "rruleset", "rrulestr", + "YEARLY", "MONTHLY", "WEEKLY", "DAILY", + "HOURLY", "MINUTELY", "SECONDLY", + "MO", "TU", "WE", "TH", "FR", "SA", "SU"] + +# Every mask is 7 days longer to handle cross-year weekly periods. +M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30 + + [7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7) +M365MASK = list(M366MASK) +M29, M30, M31 = list(range(1, 30)), list(range(1, 31)), list(range(1, 32)) +MDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) +MDAY365MASK = list(MDAY366MASK) +M29, M30, M31 = list(range(-29, 0)), list(range(-30, 0)), list(range(-31, 0)) +NMDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) +NMDAY365MASK = list(NMDAY366MASK) +M366RANGE = (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366) +M365RANGE = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365) +WDAYMASK = [0, 1, 2, 3, 4, 5, 6]*55 +del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31] +MDAY365MASK = tuple(MDAY365MASK) +M365MASK = tuple(M365MASK) + +FREQNAMES = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY'] + +(YEARLY, + MONTHLY, + WEEKLY, + DAILY, + HOURLY, + MINUTELY, + SECONDLY) = list(range(7)) + +# Imported on demand. +easter = None +parser = None + + +class weekday(weekdaybase): + """ + This version of weekday does not allow n = 0. + """ + def __init__(self, wkday, n=None): + if n == 0: + raise ValueError("Can't create weekday with n==0") + + super(weekday, self).__init__(wkday, n) + + +MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) + + +def _invalidates_cache(f): + """ + Decorator for rruleset methods which may invalidate the + cached length. + """ + def inner_func(self, *args, **kwargs): + rv = f(self, *args, **kwargs) + self._invalidate_cache() + return rv + + return inner_func + + +class rrulebase(object): + def __init__(self, cache=False): + if cache: + self._cache = [] + self._cache_lock = _thread.allocate_lock() + self._invalidate_cache() + else: + self._cache = None + self._cache_complete = False + self._len = None + + def __iter__(self): + if self._cache_complete: + return iter(self._cache) + elif self._cache is None: + return self._iter() + else: + return self._iter_cached() + + def _invalidate_cache(self): + if self._cache is not None: + self._cache = [] + self._cache_complete = False + self._cache_gen = self._iter() + + if self._cache_lock.locked(): + self._cache_lock.release() + + self._len = None + + def _iter_cached(self): + i = 0 + gen = self._cache_gen + cache = self._cache + acquire = self._cache_lock.acquire + release = self._cache_lock.release + while gen: + if i == len(cache): + acquire() + if self._cache_complete: + break + try: + for j in range(10): + cache.append(advance_iterator(gen)) + except StopIteration: + self._cache_gen = gen = None + self._cache_complete = True + break + release() + yield cache[i] + i += 1 + while i < self._len: + yield cache[i] + i += 1 + + def __getitem__(self, item): + if self._cache_complete: + return self._cache[item] + elif isinstance(item, slice): + if item.step and item.step < 0: + return list(iter(self))[item] + else: + return list(itertools.islice(self, + item.start or 0, + item.stop or sys.maxsize, + item.step or 1)) + elif item >= 0: + gen = iter(self) + try: + for i in range(item+1): + res = advance_iterator(gen) + except StopIteration: + raise IndexError + return res + else: + return list(iter(self))[item] + + def __contains__(self, item): + if self._cache_complete: + return item in self._cache + else: + for i in self: + if i == item: + return True + elif i > item: + return False + return False + + # __len__() introduces a large performance penality. + def count(self): + """ Returns the number of recurrences in this set. It will have go + trough the whole recurrence, if this hasn't been done before. """ + if self._len is None: + for x in self: + pass + return self._len + + def before(self, dt, inc=False): + """ Returns the last recurrence before the given datetime instance. The + inc keyword defines what happens if dt is an occurrence. With + inc=True, if dt itself is an occurrence, it will be returned. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + last = None + if inc: + for i in gen: + if i > dt: + break + last = i + else: + for i in gen: + if i >= dt: + break + last = i + return last + + def after(self, dt, inc=False): + """ Returns the first recurrence after the given datetime instance. The + inc keyword defines what happens if dt is an occurrence. With + inc=True, if dt itself is an occurrence, it will be returned. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + if inc: + for i in gen: + if i >= dt: + return i + else: + for i in gen: + if i > dt: + return i + return None + + def xafter(self, dt, count=None, inc=False): + """ + Generator which yields up to `count` recurrences after the given + datetime instance, equivalent to `after`. + + :param dt: + The datetime at which to start generating recurrences. + + :param count: + The maximum number of recurrences to generate. If `None` (default), + dates are generated until the recurrence rule is exhausted. + + :param inc: + If `dt` is an instance of the rule and `inc` is `True`, it is + included in the output. + + :yields: Yields a sequence of `datetime` objects. + """ + + if self._cache_complete: + gen = self._cache + else: + gen = self + + # Select the comparison function + if inc: + comp = lambda dc, dtc: dc >= dtc + else: + comp = lambda dc, dtc: dc > dtc + + # Generate dates + n = 0 + for d in gen: + if comp(d, dt): + if count is not None: + n += 1 + if n > count: + break + + yield d + + def between(self, after, before, inc=False, count=1): + """ Returns all the occurrences of the rrule between after and before. + The inc keyword defines what happens if after and/or before are + themselves occurrences. With inc=True, they will be included in the + list, if they are found in the recurrence set. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + started = False + l = [] + if inc: + for i in gen: + if i > before: + break + elif not started: + if i >= after: + started = True + l.append(i) + else: + l.append(i) + else: + for i in gen: + if i >= before: + break + elif not started: + if i > after: + started = True + l.append(i) + else: + l.append(i) + return l + + +class rrule(rrulebase): + """ + That's the base of the rrule operation. It accepts all the keywords + defined in the RFC as its constructor parameters (except byday, + which was renamed to byweekday) and more. The constructor prototype is:: + + rrule(freq) + + Where freq must be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, + or SECONDLY. + + .. note:: + Per RFC section 3.3.10, recurrence instances falling on invalid dates + and times are ignored rather than coerced: + + Recurrence rules may generate recurrence instances with an invalid + date (e.g., February 30) or nonexistent local time (e.g., 1:30 AM + on a day where the local time is moved forward by an hour at 1:00 + AM). Such recurrence instances MUST be ignored and MUST NOT be + counted as part of the recurrence set. + + This can lead to possibly surprising behavior when, for example, the + start date occurs at the end of the month: + + >>> from dateutil.rrule import rrule, MONTHLY + >>> from datetime import datetime + >>> start_date = datetime(2014, 12, 31) + >>> list(rrule(freq=MONTHLY, count=4, dtstart=start_date)) + ... # doctest: +NORMALIZE_WHITESPACE + [datetime.datetime(2014, 12, 31, 0, 0), + datetime.datetime(2015, 1, 31, 0, 0), + datetime.datetime(2015, 3, 31, 0, 0), + datetime.datetime(2015, 5, 31, 0, 0)] + + Additionally, it supports the following keyword arguments: + + :param dtstart: + The recurrence start. Besides being the base for the recurrence, + missing parameters in the final recurrence instances will also be + extracted from this date. If not given, datetime.now() will be used + instead. + :param interval: + The interval between each freq iteration. For example, when using + YEARLY, an interval of 2 means once every two years, but with HOURLY, + it means once every two hours. The default interval is 1. + :param wkst: + The week start day. Must be one of the MO, TU, WE constants, or an + integer, specifying the first day of the week. This will affect + recurrences based on weekly periods. The default week start is got + from calendar.firstweekday(), and may be modified by + calendar.setfirstweekday(). + :param count: + How many occurrences will be generated. + + .. note:: + As of version 2.5.0, the use of the ``until`` keyword together + with the ``count`` keyword is deprecated per RFC-5545 Sec. 3.3.10. + :param until: + If given, this must be a datetime instance, that will specify the + limit of the recurrence. The last recurrence in the rule is the greatest + datetime that is less than or equal to the value specified in the + ``until`` parameter. + + .. note:: + As of version 2.5.0, the use of the ``until`` keyword together + with the ``count`` keyword is deprecated per RFC-5545 Sec. 3.3.10. + :param bysetpos: + If given, it must be either an integer, or a sequence of integers, + positive or negative. Each given integer will specify an occurrence + number, corresponding to the nth occurrence of the rule inside the + frequency period. For example, a bysetpos of -1 if combined with a + MONTHLY frequency, and a byweekday of (MO, TU, WE, TH, FR), will + result in the last work day of every month. + :param bymonth: + If given, it must be either an integer, or a sequence of integers, + meaning the months to apply the recurrence to. + :param bymonthday: + If given, it must be either an integer, or a sequence of integers, + meaning the month days to apply the recurrence to. + :param byyearday: + If given, it must be either an integer, or a sequence of integers, + meaning the year days to apply the recurrence to. + :param byeaster: + If given, it must be either an integer, or a sequence of integers, + positive or negative. Each integer will define an offset from the + Easter Sunday. Passing the offset 0 to byeaster will yield the Easter + Sunday itself. This is an extension to the RFC specification. + :param byweekno: + If given, it must be either an integer, or a sequence of integers, + meaning the week numbers to apply the recurrence to. Week numbers + have the meaning described in ISO8601, that is, the first week of + the year is that containing at least four days of the new year. + :param byweekday: + If given, it must be either an integer (0 == MO), a sequence of + integers, one of the weekday constants (MO, TU, etc), or a sequence + of these constants. When given, these variables will define the + weekdays where the recurrence will be applied. It's also possible to + use an argument n for the weekday instances, which will mean the nth + occurrence of this weekday in the period. For example, with MONTHLY, + or with YEARLY and BYMONTH, using FR(+1) in byweekday will specify the + first friday of the month where the recurrence happens. Notice that in + the RFC documentation, this is specified as BYDAY, but was renamed to + avoid the ambiguity of that keyword. + :param byhour: + If given, it must be either an integer, or a sequence of integers, + meaning the hours to apply the recurrence to. + :param byminute: + If given, it must be either an integer, or a sequence of integers, + meaning the minutes to apply the recurrence to. + :param bysecond: + If given, it must be either an integer, or a sequence of integers, + meaning the seconds to apply the recurrence to. + :param cache: + If given, it must be a boolean value specifying to enable or disable + caching of results. If you will use the same rrule instance multiple + times, enabling caching will improve the performance considerably. + """ + def __init__(self, freq, dtstart=None, + interval=1, wkst=None, count=None, until=None, bysetpos=None, + bymonth=None, bymonthday=None, byyearday=None, byeaster=None, + byweekno=None, byweekday=None, + byhour=None, byminute=None, bysecond=None, + cache=False): + super(rrule, self).__init__(cache) + global easter + if not dtstart: + if until and until.tzinfo: + dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0) + else: + dtstart = datetime.datetime.now().replace(microsecond=0) + elif not isinstance(dtstart, datetime.datetime): + dtstart = datetime.datetime.fromordinal(dtstart.toordinal()) + else: + dtstart = dtstart.replace(microsecond=0) + self._dtstart = dtstart + self._tzinfo = dtstart.tzinfo + self._freq = freq + self._interval = interval + self._count = count + + # Cache the original byxxx rules, if they are provided, as the _byxxx + # attributes do not necessarily map to the inputs, and this can be + # a problem in generating the strings. Only store things if they've + # been supplied (the string retrieval will just use .get()) + self._original_rule = {} + + if until and not isinstance(until, datetime.datetime): + until = datetime.datetime.fromordinal(until.toordinal()) + self._until = until + + if self._dtstart and self._until: + if (self._dtstart.tzinfo is not None) != (self._until.tzinfo is not None): + # According to RFC5545 Section 3.3.10: + # https://tools.ietf.org/html/rfc5545#section-3.3.10 + # + # > If the "DTSTART" property is specified as a date with UTC + # > time or a date with local time and time zone reference, + # > then the UNTIL rule part MUST be specified as a date with + # > UTC time. + raise ValueError( + 'RRULE UNTIL values must be specified in UTC when DTSTART ' + 'is timezone-aware' + ) + + if count is not None and until: + warn("Using both 'count' and 'until' is inconsistent with RFC 5545" + " and has been deprecated in dateutil. Future versions will " + "raise an error.", DeprecationWarning) + + if wkst is None: + self._wkst = calendar.firstweekday() + elif isinstance(wkst, integer_types): + self._wkst = wkst + else: + self._wkst = wkst.weekday + + if bysetpos is None: + self._bysetpos = None + elif isinstance(bysetpos, integer_types): + if bysetpos == 0 or not (-366 <= bysetpos <= 366): + raise ValueError("bysetpos must be between 1 and 366, " + "or between -366 and -1") + self._bysetpos = (bysetpos,) + else: + self._bysetpos = tuple(bysetpos) + for pos in self._bysetpos: + if pos == 0 or not (-366 <= pos <= 366): + raise ValueError("bysetpos must be between 1 and 366, " + "or between -366 and -1") + + if self._bysetpos: + self._original_rule['bysetpos'] = self._bysetpos + + if (byweekno is None and byyearday is None and bymonthday is None and + byweekday is None and byeaster is None): + if freq == YEARLY: + if bymonth is None: + bymonth = dtstart.month + self._original_rule['bymonth'] = None + bymonthday = dtstart.day + self._original_rule['bymonthday'] = None + elif freq == MONTHLY: + bymonthday = dtstart.day + self._original_rule['bymonthday'] = None + elif freq == WEEKLY: + byweekday = dtstart.weekday() + self._original_rule['byweekday'] = None + + # bymonth + if bymonth is None: + self._bymonth = None + else: + if isinstance(bymonth, integer_types): + bymonth = (bymonth,) + + self._bymonth = tuple(sorted(set(bymonth))) + + if 'bymonth' not in self._original_rule: + self._original_rule['bymonth'] = self._bymonth + + # byyearday + if byyearday is None: + self._byyearday = None + else: + if isinstance(byyearday, integer_types): + byyearday = (byyearday,) + + self._byyearday = tuple(sorted(set(byyearday))) + self._original_rule['byyearday'] = self._byyearday + + # byeaster + if byeaster is not None: + if not easter: + from dateutil import easter + if isinstance(byeaster, integer_types): + self._byeaster = (byeaster,) + else: + self._byeaster = tuple(sorted(byeaster)) + + self._original_rule['byeaster'] = self._byeaster + else: + self._byeaster = None + + # bymonthday + if bymonthday is None: + self._bymonthday = () + self._bynmonthday = () + else: + if isinstance(bymonthday, integer_types): + bymonthday = (bymonthday,) + + bymonthday = set(bymonthday) # Ensure it's unique + + self._bymonthday = tuple(sorted(x for x in bymonthday if x > 0)) + self._bynmonthday = tuple(sorted(x for x in bymonthday if x < 0)) + + # Storing positive numbers first, then negative numbers + if 'bymonthday' not in self._original_rule: + self._original_rule['bymonthday'] = tuple( + itertools.chain(self._bymonthday, self._bynmonthday)) + + # byweekno + if byweekno is None: + self._byweekno = None + else: + if isinstance(byweekno, integer_types): + byweekno = (byweekno,) + + self._byweekno = tuple(sorted(set(byweekno))) + + self._original_rule['byweekno'] = self._byweekno + + # byweekday / bynweekday + if byweekday is None: + self._byweekday = None + self._bynweekday = None + else: + # If it's one of the valid non-sequence types, convert to a + # single-element sequence before the iterator that builds the + # byweekday set. + if isinstance(byweekday, integer_types) or hasattr(byweekday, "n"): + byweekday = (byweekday,) + + self._byweekday = set() + self._bynweekday = set() + for wday in byweekday: + if isinstance(wday, integer_types): + self._byweekday.add(wday) + elif not wday.n or freq > MONTHLY: + self._byweekday.add(wday.weekday) + else: + self._bynweekday.add((wday.weekday, wday.n)) + + if not self._byweekday: + self._byweekday = None + elif not self._bynweekday: + self._bynweekday = None + + if self._byweekday is not None: + self._byweekday = tuple(sorted(self._byweekday)) + orig_byweekday = [weekday(x) for x in self._byweekday] + else: + orig_byweekday = () + + if self._bynweekday is not None: + self._bynweekday = tuple(sorted(self._bynweekday)) + orig_bynweekday = [weekday(*x) for x in self._bynweekday] + else: + orig_bynweekday = () + + if 'byweekday' not in self._original_rule: + self._original_rule['byweekday'] = tuple(itertools.chain( + orig_byweekday, orig_bynweekday)) + + # byhour + if byhour is None: + if freq < HOURLY: + self._byhour = {dtstart.hour} + else: + self._byhour = None + else: + if isinstance(byhour, integer_types): + byhour = (byhour,) + + if freq == HOURLY: + self._byhour = self.__construct_byset(start=dtstart.hour, + byxxx=byhour, + base=24) + else: + self._byhour = set(byhour) + + self._byhour = tuple(sorted(self._byhour)) + self._original_rule['byhour'] = self._byhour + + # byminute + if byminute is None: + if freq < MINUTELY: + self._byminute = {dtstart.minute} + else: + self._byminute = None + else: + if isinstance(byminute, integer_types): + byminute = (byminute,) + + if freq == MINUTELY: + self._byminute = self.__construct_byset(start=dtstart.minute, + byxxx=byminute, + base=60) + else: + self._byminute = set(byminute) + + self._byminute = tuple(sorted(self._byminute)) + self._original_rule['byminute'] = self._byminute + + # bysecond + if bysecond is None: + if freq < SECONDLY: + self._bysecond = ((dtstart.second,)) + else: + self._bysecond = None + else: + if isinstance(bysecond, integer_types): + bysecond = (bysecond,) + + self._bysecond = set(bysecond) + + if freq == SECONDLY: + self._bysecond = self.__construct_byset(start=dtstart.second, + byxxx=bysecond, + base=60) + else: + self._bysecond = set(bysecond) + + self._bysecond = tuple(sorted(self._bysecond)) + self._original_rule['bysecond'] = self._bysecond + + if self._freq >= HOURLY: + self._timeset = None + else: + self._timeset = [] + for hour in self._byhour: + for minute in self._byminute: + for second in self._bysecond: + self._timeset.append( + datetime.time(hour, minute, second, + tzinfo=self._tzinfo)) + self._timeset.sort() + self._timeset = tuple(self._timeset) + + def __str__(self): + """ + Output a string that would generate this RRULE if passed to rrulestr. + This is mostly compatible with RFC5545, except for the + dateutil-specific extension BYEASTER. + """ + + output = [] + h, m, s = [None] * 3 + if self._dtstart: + output.append(self._dtstart.strftime('DTSTART:%Y%m%dT%H%M%S')) + h, m, s = self._dtstart.timetuple()[3:6] + + parts = ['FREQ=' + FREQNAMES[self._freq]] + if self._interval != 1: + parts.append('INTERVAL=' + str(self._interval)) + + if self._wkst: + parts.append('WKST=' + repr(weekday(self._wkst))[0:2]) + + if self._count is not None: + parts.append('COUNT=' + str(self._count)) + + if self._until: + parts.append(self._until.strftime('UNTIL=%Y%m%dT%H%M%S')) + + if self._original_rule.get('byweekday') is not None: + # The str() method on weekday objects doesn't generate + # RFC5545-compliant strings, so we should modify that. + original_rule = dict(self._original_rule) + wday_strings = [] + for wday in original_rule['byweekday']: + if wday.n: + wday_strings.append('{n:+d}{wday}'.format( + n=wday.n, + wday=repr(wday)[0:2])) + else: + wday_strings.append(repr(wday)) + + original_rule['byweekday'] = wday_strings + else: + original_rule = self._original_rule + + partfmt = '{name}={vals}' + for name, key in [('BYSETPOS', 'bysetpos'), + ('BYMONTH', 'bymonth'), + ('BYMONTHDAY', 'bymonthday'), + ('BYYEARDAY', 'byyearday'), + ('BYWEEKNO', 'byweekno'), + ('BYDAY', 'byweekday'), + ('BYHOUR', 'byhour'), + ('BYMINUTE', 'byminute'), + ('BYSECOND', 'bysecond'), + ('BYEASTER', 'byeaster')]: + value = original_rule.get(key) + if value: + parts.append(partfmt.format(name=name, vals=(','.join(str(v) + for v in value)))) + + output.append('RRULE:' + ';'.join(parts)) + return '\n'.join(output) + + def replace(self, **kwargs): + """Return new rrule with same attributes except for those attributes given new + values by whichever keyword arguments are specified.""" + new_kwargs = {"interval": self._interval, + "count": self._count, + "dtstart": self._dtstart, + "freq": self._freq, + "until": self._until, + "wkst": self._wkst, + "cache": False if self._cache is None else True } + new_kwargs.update(self._original_rule) + new_kwargs.update(kwargs) + return rrule(**new_kwargs) + + def _iter(self): + year, month, day, hour, minute, second, weekday, yearday, _ = \ + self._dtstart.timetuple() + + # Some local variables to speed things up a bit + freq = self._freq + interval = self._interval + wkst = self._wkst + until = self._until + bymonth = self._bymonth + byweekno = self._byweekno + byyearday = self._byyearday + byweekday = self._byweekday + byeaster = self._byeaster + bymonthday = self._bymonthday + bynmonthday = self._bynmonthday + bysetpos = self._bysetpos + byhour = self._byhour + byminute = self._byminute + bysecond = self._bysecond + + ii = _iterinfo(self) + ii.rebuild(year, month) + + getdayset = {YEARLY: ii.ydayset, + MONTHLY: ii.mdayset, + WEEKLY: ii.wdayset, + DAILY: ii.ddayset, + HOURLY: ii.ddayset, + MINUTELY: ii.ddayset, + SECONDLY: ii.ddayset}[freq] + + if freq < HOURLY: + timeset = self._timeset + else: + gettimeset = {HOURLY: ii.htimeset, + MINUTELY: ii.mtimeset, + SECONDLY: ii.stimeset}[freq] + if ((freq >= HOURLY and + self._byhour and hour not in self._byhour) or + (freq >= MINUTELY and + self._byminute and minute not in self._byminute) or + (freq >= SECONDLY and + self._bysecond and second not in self._bysecond)): + timeset = () + else: + timeset = gettimeset(hour, minute, second) + + total = 0 + count = self._count + while True: + # Get dayset with the right frequency + dayset, start, end = getdayset(year, month, day) + + # Do the "hard" work ;-) + filtered = False + for i in dayset[start:end]: + if ((bymonth and ii.mmask[i] not in bymonth) or + (byweekno and not ii.wnomask[i]) or + (byweekday and ii.wdaymask[i] not in byweekday) or + (ii.nwdaymask and not ii.nwdaymask[i]) or + (byeaster and not ii.eastermask[i]) or + ((bymonthday or bynmonthday) and + ii.mdaymask[i] not in bymonthday and + ii.nmdaymask[i] not in bynmonthday) or + (byyearday and + ((i < ii.yearlen and i+1 not in byyearday and + -ii.yearlen+i not in byyearday) or + (i >= ii.yearlen and i+1-ii.yearlen not in byyearday and + -ii.nextyearlen+i-ii.yearlen not in byyearday)))): + dayset[i] = None + filtered = True + + # Output results + if bysetpos and timeset: + poslist = [] + for pos in bysetpos: + if pos < 0: + daypos, timepos = divmod(pos, len(timeset)) + else: + daypos, timepos = divmod(pos-1, len(timeset)) + try: + i = [x for x in dayset[start:end] + if x is not None][daypos] + time = timeset[timepos] + except IndexError: + pass + else: + date = datetime.date.fromordinal(ii.yearordinal+i) + res = datetime.datetime.combine(date, time) + if res not in poslist: + poslist.append(res) + poslist.sort() + for res in poslist: + if until and res > until: + self._len = total + return + elif res >= self._dtstart: + if count is not None: + count -= 1 + if count < 0: + self._len = total + return + total += 1 + yield res + else: + for i in dayset[start:end]: + if i is not None: + date = datetime.date.fromordinal(ii.yearordinal + i) + for time in timeset: + res = datetime.datetime.combine(date, time) + if until and res > until: + self._len = total + return + elif res >= self._dtstart: + if count is not None: + count -= 1 + if count < 0: + self._len = total + return + + total += 1 + yield res + + # Handle frequency and interval + fixday = False + if freq == YEARLY: + year += interval + if year > datetime.MAXYEAR: + self._len = total + return + ii.rebuild(year, month) + elif freq == MONTHLY: + month += interval + if month > 12: + div, mod = divmod(month, 12) + month = mod + year += div + if month == 0: + month = 12 + year -= 1 + if year > datetime.MAXYEAR: + self._len = total + return + ii.rebuild(year, month) + elif freq == WEEKLY: + if wkst > weekday: + day += -(weekday+1+(6-wkst))+self._interval*7 + else: + day += -(weekday-wkst)+self._interval*7 + weekday = wkst + fixday = True + elif freq == DAILY: + day += interval + fixday = True + elif freq == HOURLY: + if filtered: + # Jump to one iteration before next day + hour += ((23-hour)//interval)*interval + + if byhour: + ndays, hour = self.__mod_distance(value=hour, + byxxx=self._byhour, + base=24) + else: + ndays, hour = divmod(hour+interval, 24) + + if ndays: + day += ndays + fixday = True + + timeset = gettimeset(hour, minute, second) + elif freq == MINUTELY: + if filtered: + # Jump to one iteration before next day + minute += ((1439-(hour*60+minute))//interval)*interval + + valid = False + rep_rate = (24*60) + for j in range(rep_rate // gcd(interval, rep_rate)): + if byminute: + nhours, minute = \ + self.__mod_distance(value=minute, + byxxx=self._byminute, + base=60) + else: + nhours, minute = divmod(minute+interval, 60) + + div, hour = divmod(hour+nhours, 24) + if div: + day += div + fixday = True + filtered = False + + if not byhour or hour in byhour: + valid = True + break + + if not valid: + raise ValueError('Invalid combination of interval and ' + + 'byhour resulting in empty rule.') + + timeset = gettimeset(hour, minute, second) + elif freq == SECONDLY: + if filtered: + # Jump to one iteration before next day + second += (((86399 - (hour * 3600 + minute * 60 + second)) + // interval) * interval) + + rep_rate = (24 * 3600) + valid = False + for j in range(0, rep_rate // gcd(interval, rep_rate)): + if bysecond: + nminutes, second = \ + self.__mod_distance(value=second, + byxxx=self._bysecond, + base=60) + else: + nminutes, second = divmod(second+interval, 60) + + div, minute = divmod(minute+nminutes, 60) + if div: + hour += div + div, hour = divmod(hour, 24) + if div: + day += div + fixday = True + + if ((not byhour or hour in byhour) and + (not byminute or minute in byminute) and + (not bysecond or second in bysecond)): + valid = True + break + + if not valid: + raise ValueError('Invalid combination of interval, ' + + 'byhour and byminute resulting in empty' + + ' rule.') + + timeset = gettimeset(hour, minute, second) + + if fixday and day > 28: + daysinmonth = calendar.monthrange(year, month)[1] + if day > daysinmonth: + while day > daysinmonth: + day -= daysinmonth + month += 1 + if month == 13: + month = 1 + year += 1 + if year > datetime.MAXYEAR: + self._len = total + return + daysinmonth = calendar.monthrange(year, month)[1] + ii.rebuild(year, month) + + def __construct_byset(self, start, byxxx, base): + """ + If a `BYXXX` sequence is passed to the constructor at the same level as + `FREQ` (e.g. `FREQ=HOURLY,BYHOUR={2,4,7},INTERVAL=3`), there are some + specifications which cannot be reached given some starting conditions. + + This occurs whenever the interval is not coprime with the base of a + given unit and the difference between the starting position and the + ending position is not coprime with the greatest common denominator + between the interval and the base. For example, with a FREQ of hourly + starting at 17:00 and an interval of 4, the only valid values for + BYHOUR would be {21, 1, 5, 9, 13, 17}, because 4 and 24 are not + coprime. + + :param start: + Specifies the starting position. + :param byxxx: + An iterable containing the list of allowed values. + :param base: + The largest allowable value for the specified frequency (e.g. + 24 hours, 60 minutes). + + This does not preserve the type of the iterable, returning a set, since + the values should be unique and the order is irrelevant, this will + speed up later lookups. + + In the event of an empty set, raises a :exception:`ValueError`, as this + results in an empty rrule. + """ + + cset = set() + + # Support a single byxxx value. + if isinstance(byxxx, integer_types): + byxxx = (byxxx, ) + + for num in byxxx: + i_gcd = gcd(self._interval, base) + # Use divmod rather than % because we need to wrap negative nums. + if i_gcd == 1 or divmod(num - start, i_gcd)[1] == 0: + cset.add(num) + + if len(cset) == 0: + raise ValueError("Invalid rrule byxxx generates an empty set.") + + return cset + + def __mod_distance(self, value, byxxx, base): + """ + Calculates the next value in a sequence where the `FREQ` parameter is + specified along with a `BYXXX` parameter at the same "level" + (e.g. `HOURLY` specified with `BYHOUR`). + + :param value: + The old value of the component. + :param byxxx: + The `BYXXX` set, which should have been generated by + `rrule._construct_byset`, or something else which checks that a + valid rule is present. + :param base: + The largest allowable value for the specified frequency (e.g. + 24 hours, 60 minutes). + + If a valid value is not found after `base` iterations (the maximum + number before the sequence would start to repeat), this raises a + :exception:`ValueError`, as no valid values were found. + + This returns a tuple of `divmod(n*interval, base)`, where `n` is the + smallest number of `interval` repetitions until the next specified + value in `byxxx` is found. + """ + accumulator = 0 + for ii in range(1, base + 1): + # Using divmod() over % to account for negative intervals + div, value = divmod(value + self._interval, base) + accumulator += div + if value in byxxx: + return (accumulator, value) + + +class _iterinfo(object): + __slots__ = ["rrule", "lastyear", "lastmonth", + "yearlen", "nextyearlen", "yearordinal", "yearweekday", + "mmask", "mrange", "mdaymask", "nmdaymask", + "wdaymask", "wnomask", "nwdaymask", "eastermask"] + + def __init__(self, rrule): + for attr in self.__slots__: + setattr(self, attr, None) + self.rrule = rrule + + def rebuild(self, year, month): + # Every mask is 7 days longer to handle cross-year weekly periods. + rr = self.rrule + if year != self.lastyear: + self.yearlen = 365 + calendar.isleap(year) + self.nextyearlen = 365 + calendar.isleap(year + 1) + firstyday = datetime.date(year, 1, 1) + self.yearordinal = firstyday.toordinal() + self.yearweekday = firstyday.weekday() + + wday = datetime.date(year, 1, 1).weekday() + if self.yearlen == 365: + self.mmask = M365MASK + self.mdaymask = MDAY365MASK + self.nmdaymask = NMDAY365MASK + self.wdaymask = WDAYMASK[wday:] + self.mrange = M365RANGE + else: + self.mmask = M366MASK + self.mdaymask = MDAY366MASK + self.nmdaymask = NMDAY366MASK + self.wdaymask = WDAYMASK[wday:] + self.mrange = M366RANGE + + if not rr._byweekno: + self.wnomask = None + else: + self.wnomask = [0]*(self.yearlen+7) + # no1wkst = firstwkst = self.wdaymask.index(rr._wkst) + no1wkst = firstwkst = (7-self.yearweekday+rr._wkst) % 7 + if no1wkst >= 4: + no1wkst = 0 + # Number of days in the year, plus the days we got + # from last year. + wyearlen = self.yearlen+(self.yearweekday-rr._wkst) % 7 + else: + # Number of days in the year, minus the days we + # left in last year. + wyearlen = self.yearlen-no1wkst + div, mod = divmod(wyearlen, 7) + numweeks = div+mod//4 + for n in rr._byweekno: + if n < 0: + n += numweeks+1 + if not (0 < n <= numweeks): + continue + if n > 1: + i = no1wkst+(n-1)*7 + if no1wkst != firstwkst: + i -= 7-firstwkst + else: + i = no1wkst + for j in range(7): + self.wnomask[i] = 1 + i += 1 + if self.wdaymask[i] == rr._wkst: + break + if 1 in rr._byweekno: + # Check week number 1 of next year as well + # TODO: Check -numweeks for next year. + i = no1wkst+numweeks*7 + if no1wkst != firstwkst: + i -= 7-firstwkst + if i < self.yearlen: + # If week starts in next year, we + # don't care about it. + for j in range(7): + self.wnomask[i] = 1 + i += 1 + if self.wdaymask[i] == rr._wkst: + break + if no1wkst: + # Check last week number of last year as + # well. If no1wkst is 0, either the year + # started on week start, or week number 1 + # got days from last year, so there are no + # days from last year's last week number in + # this year. + if -1 not in rr._byweekno: + lyearweekday = datetime.date(year-1, 1, 1).weekday() + lno1wkst = (7-lyearweekday+rr._wkst) % 7 + lyearlen = 365+calendar.isleap(year-1) + if lno1wkst >= 4: + lno1wkst = 0 + lnumweeks = 52+(lyearlen + + (lyearweekday-rr._wkst) % 7) % 7//4 + else: + lnumweeks = 52+(self.yearlen-no1wkst) % 7//4 + else: + lnumweeks = -1 + if lnumweeks in rr._byweekno: + for i in range(no1wkst): + self.wnomask[i] = 1 + + if (rr._bynweekday and (month != self.lastmonth or + year != self.lastyear)): + ranges = [] + if rr._freq == YEARLY: + if rr._bymonth: + for month in rr._bymonth: + ranges.append(self.mrange[month-1:month+1]) + else: + ranges = [(0, self.yearlen)] + elif rr._freq == MONTHLY: + ranges = [self.mrange[month-1:month+1]] + if ranges: + # Weekly frequency won't get here, so we may not + # care about cross-year weekly periods. + self.nwdaymask = [0]*self.yearlen + for first, last in ranges: + last -= 1 + for wday, n in rr._bynweekday: + if n < 0: + i = last+(n+1)*7 + i -= (self.wdaymask[i]-wday) % 7 + else: + i = first+(n-1)*7 + i += (7-self.wdaymask[i]+wday) % 7 + if first <= i <= last: + self.nwdaymask[i] = 1 + + if rr._byeaster: + self.eastermask = [0]*(self.yearlen+7) + eyday = easter.easter(year).toordinal()-self.yearordinal + for offset in rr._byeaster: + self.eastermask[eyday+offset] = 1 + + self.lastyear = year + self.lastmonth = month + + def ydayset(self, year, month, day): + return list(range(self.yearlen)), 0, self.yearlen + + def mdayset(self, year, month, day): + dset = [None]*self.yearlen + start, end = self.mrange[month-1:month+1] + for i in range(start, end): + dset[i] = i + return dset, start, end + + def wdayset(self, year, month, day): + # We need to handle cross-year weeks here. + dset = [None]*(self.yearlen+7) + i = datetime.date(year, month, day).toordinal()-self.yearordinal + start = i + for j in range(7): + dset[i] = i + i += 1 + # if (not (0 <= i < self.yearlen) or + # self.wdaymask[i] == self.rrule._wkst): + # This will cross the year boundary, if necessary. + if self.wdaymask[i] == self.rrule._wkst: + break + return dset, start, i + + def ddayset(self, year, month, day): + dset = [None] * self.yearlen + i = datetime.date(year, month, day).toordinal() - self.yearordinal + dset[i] = i + return dset, i, i + 1 + + def htimeset(self, hour, minute, second): + tset = [] + rr = self.rrule + for minute in rr._byminute: + for second in rr._bysecond: + tset.append(datetime.time(hour, minute, second, + tzinfo=rr._tzinfo)) + tset.sort() + return tset + + def mtimeset(self, hour, minute, second): + tset = [] + rr = self.rrule + for second in rr._bysecond: + tset.append(datetime.time(hour, minute, second, tzinfo=rr._tzinfo)) + tset.sort() + return tset + + def stimeset(self, hour, minute, second): + return (datetime.time(hour, minute, second, + tzinfo=self.rrule._tzinfo),) + + +class rruleset(rrulebase): + """ The rruleset type allows more complex recurrence setups, mixing + multiple rules, dates, exclusion rules, and exclusion dates. The type + constructor takes the following keyword arguments: + + :param cache: If True, caching of results will be enabled, improving + performance of multiple queries considerably. """ + + class _genitem(object): + def __init__(self, genlist, gen): + try: + self.dt = advance_iterator(gen) + genlist.append(self) + except StopIteration: + pass + self.genlist = genlist + self.gen = gen + + def __next__(self): + try: + self.dt = advance_iterator(self.gen) + except StopIteration: + if self.genlist[0] is self: + heapq.heappop(self.genlist) + else: + self.genlist.remove(self) + heapq.heapify(self.genlist) + + next = __next__ + + def __lt__(self, other): + return self.dt < other.dt + + def __gt__(self, other): + return self.dt > other.dt + + def __eq__(self, other): + return self.dt == other.dt + + def __ne__(self, other): + return self.dt != other.dt + + def __init__(self, cache=False): + super(rruleset, self).__init__(cache) + self._rrule = [] + self._rdate = [] + self._exrule = [] + self._exdate = [] + + @_invalidates_cache + def rrule(self, rrule): + """ Include the given :py:class:`rrule` instance in the recurrence set + generation. """ + self._rrule.append(rrule) + + @_invalidates_cache + def rdate(self, rdate): + """ Include the given :py:class:`datetime` instance in the recurrence + set generation. """ + self._rdate.append(rdate) + + @_invalidates_cache + def exrule(self, exrule): + """ Include the given rrule instance in the recurrence set exclusion + list. Dates which are part of the given recurrence rules will not + be generated, even if some inclusive rrule or rdate matches them. + """ + self._exrule.append(exrule) + + @_invalidates_cache + def exdate(self, exdate): + """ Include the given datetime instance in the recurrence set + exclusion list. Dates included that way will not be generated, + even if some inclusive rrule or rdate matches them. """ + self._exdate.append(exdate) + + def _iter(self): + rlist = [] + self._rdate.sort() + self._genitem(rlist, iter(self._rdate)) + for gen in [iter(x) for x in self._rrule]: + self._genitem(rlist, gen) + exlist = [] + self._exdate.sort() + self._genitem(exlist, iter(self._exdate)) + for gen in [iter(x) for x in self._exrule]: + self._genitem(exlist, gen) + lastdt = None + total = 0 + heapq.heapify(rlist) + heapq.heapify(exlist) + while rlist: + ritem = rlist[0] + if not lastdt or lastdt != ritem.dt: + while exlist and exlist[0] < ritem: + exitem = exlist[0] + advance_iterator(exitem) + if exlist and exlist[0] is exitem: + heapq.heapreplace(exlist, exitem) + if not exlist or ritem != exlist[0]: + total += 1 + yield ritem.dt + lastdt = ritem.dt + advance_iterator(ritem) + if rlist and rlist[0] is ritem: + heapq.heapreplace(rlist, ritem) + self._len = total + + +class _rrulestr(object): + + _freq_map = {"YEARLY": YEARLY, + "MONTHLY": MONTHLY, + "WEEKLY": WEEKLY, + "DAILY": DAILY, + "HOURLY": HOURLY, + "MINUTELY": MINUTELY, + "SECONDLY": SECONDLY} + + _weekday_map = {"MO": 0, "TU": 1, "WE": 2, "TH": 3, + "FR": 4, "SA": 5, "SU": 6} + + def _handle_int(self, rrkwargs, name, value, **kwargs): + rrkwargs[name.lower()] = int(value) + + def _handle_int_list(self, rrkwargs, name, value, **kwargs): + rrkwargs[name.lower()] = [int(x) for x in value.split(',')] + + _handle_INTERVAL = _handle_int + _handle_COUNT = _handle_int + _handle_BYSETPOS = _handle_int_list + _handle_BYMONTH = _handle_int_list + _handle_BYMONTHDAY = _handle_int_list + _handle_BYYEARDAY = _handle_int_list + _handle_BYEASTER = _handle_int_list + _handle_BYWEEKNO = _handle_int_list + _handle_BYHOUR = _handle_int_list + _handle_BYMINUTE = _handle_int_list + _handle_BYSECOND = _handle_int_list + + def _handle_FREQ(self, rrkwargs, name, value, **kwargs): + rrkwargs["freq"] = self._freq_map[value] + + def _handle_UNTIL(self, rrkwargs, name, value, **kwargs): + global parser + if not parser: + from dateutil import parser + try: + rrkwargs["until"] = parser.parse(value, + ignoretz=kwargs.get("ignoretz"), + tzinfos=kwargs.get("tzinfos")) + except ValueError: + raise ValueError("invalid until date") + + def _handle_WKST(self, rrkwargs, name, value, **kwargs): + rrkwargs["wkst"] = self._weekday_map[value] + + def _handle_BYWEEKDAY(self, rrkwargs, name, value, **kwargs): + """ + Two ways to specify this: +1MO or MO(+1) + """ + l = [] + for wday in value.split(','): + if '(' in wday: + # If it's of the form TH(+1), etc. + splt = wday.split('(') + w = splt[0] + n = int(splt[1][:-1]) + elif len(wday): + # If it's of the form +1MO + for i in range(len(wday)): + if wday[i] not in '+-0123456789': + break + n = wday[:i] or None + w = wday[i:] + if n: + n = int(n) + else: + raise ValueError("Invalid (empty) BYDAY specification.") + + l.append(weekdays[self._weekday_map[w]](n)) + rrkwargs["byweekday"] = l + + _handle_BYDAY = _handle_BYWEEKDAY + + def _parse_rfc_rrule(self, line, + dtstart=None, + cache=False, + ignoretz=False, + tzinfos=None): + if line.find(':') != -1: + name, value = line.split(':') + if name != "RRULE": + raise ValueError("unknown parameter name") + else: + value = line + rrkwargs = {} + for pair in value.split(';'): + name, value = pair.split('=') + name = name.upper() + value = value.upper() + try: + getattr(self, "_handle_"+name)(rrkwargs, name, value, + ignoretz=ignoretz, + tzinfos=tzinfos) + except AttributeError: + raise ValueError("unknown parameter '%s'" % name) + except (KeyError, ValueError): + raise ValueError("invalid '%s': %s" % (name, value)) + return rrule(dtstart=dtstart, cache=cache, **rrkwargs) + + def _parse_rfc(self, s, + dtstart=None, + cache=False, + unfold=False, + forceset=False, + compatible=False, + ignoretz=False, + tzids=None, + tzinfos=None): + global parser + if compatible: + forceset = True + unfold = True + + TZID_NAMES = dict(map( + lambda x: (x.upper(), x), + re.findall('TZID=(?P[^:]+):', s) + )) + s = s.upper() + if not s.strip(): + raise ValueError("empty string") + if unfold: + lines = s.splitlines() + i = 0 + while i < len(lines): + line = lines[i].rstrip() + if not line: + del lines[i] + elif i > 0 and line[0] == " ": + lines[i-1] += line[1:] + del lines[i] + else: + i += 1 + else: + lines = s.split() + if (not forceset and len(lines) == 1 and (s.find(':') == -1 or + s.startswith('RRULE:'))): + return self._parse_rfc_rrule(lines[0], cache=cache, + dtstart=dtstart, ignoretz=ignoretz, + tzinfos=tzinfos) + else: + rrulevals = [] + rdatevals = [] + exrulevals = [] + exdatevals = [] + for line in lines: + if not line: + continue + if line.find(':') == -1: + name = "RRULE" + value = line + else: + name, value = line.split(':', 1) + parms = name.split(';') + if not parms: + raise ValueError("empty property name") + name = parms[0] + parms = parms[1:] + if name == "RRULE": + for parm in parms: + raise ValueError("unsupported RRULE parm: "+parm) + rrulevals.append(value) + elif name == "RDATE": + for parm in parms: + if parm != "VALUE=DATE-TIME": + raise ValueError("unsupported RDATE parm: "+parm) + rdatevals.append(value) + elif name == "EXRULE": + for parm in parms: + raise ValueError("unsupported EXRULE parm: "+parm) + exrulevals.append(value) + elif name == "EXDATE": + for parm in parms: + if parm != "VALUE=DATE-TIME": + raise ValueError("unsupported EXDATE parm: "+parm) + exdatevals.append(value) + elif name == "DTSTART": + # RFC 5445 3.8.2.4: The VALUE parameter is optional, but + # may be found only once. + value_found = False + TZID = None + valid_values = {"VALUE=DATE-TIME", "VALUE=DATE"} + for parm in parms: + if parm.startswith("TZID="): + try: + tzkey = TZID_NAMES[parm.split('TZID=')[-1]] + except KeyError: + continue + if tzids is None: + from . import tz + tzlookup = tz.gettz + elif callable(tzids): + tzlookup = tzids + else: + tzlookup = getattr(tzids, 'get', None) + if tzlookup is None: + msg = ('tzids must be a callable, ' + + 'mapping, or None, ' + + 'not %s' % tzids) + raise ValueError(msg) + + TZID = tzlookup(tzkey) + continue + if parm not in valid_values: + raise ValueError("unsupported DTSTART parm: "+parm) + else: + if value_found: + msg = ("Duplicate value parameter found in " + + "DTSTART: " + parm) + raise ValueError(msg) + value_found = True + if not parser: + from dateutil import parser + dtstart = parser.parse(value, ignoretz=ignoretz, + tzinfos=tzinfos) + if TZID is not None: + if dtstart.tzinfo is None: + dtstart = dtstart.replace(tzinfo=TZID) + else: + raise ValueError('DTSTART specifies multiple timezones') + else: + raise ValueError("unsupported property: "+name) + if (forceset or len(rrulevals) > 1 or rdatevals + or exrulevals or exdatevals): + if not parser and (rdatevals or exdatevals): + from dateutil import parser + rset = rruleset(cache=cache) + for value in rrulevals: + rset.rrule(self._parse_rfc_rrule(value, dtstart=dtstart, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in rdatevals: + for datestr in value.split(','): + rset.rdate(parser.parse(datestr, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in exrulevals: + rset.exrule(self._parse_rfc_rrule(value, dtstart=dtstart, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in exdatevals: + for datestr in value.split(','): + rset.exdate(parser.parse(datestr, + ignoretz=ignoretz, + tzinfos=tzinfos)) + if compatible and dtstart: + rset.rdate(dtstart) + return rset + else: + return self._parse_rfc_rrule(rrulevals[0], + dtstart=dtstart, + cache=cache, + ignoretz=ignoretz, + tzinfos=tzinfos) + + def __call__(self, s, **kwargs): + return self._parse_rfc(s, **kwargs) + + +rrulestr = _rrulestr() + +# vim:ts=4:sw=4:et diff --git a/modules/dateutil/tz/__init__.py b/modules/dateutil/tz/__init__.py new file mode 100644 index 0000000..5a2d9cd --- /dev/null +++ b/modules/dateutil/tz/__init__.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +from .tz import * +from .tz import __doc__ + +#: Convenience constant providing a :class:`tzutc()` instance +#: +#: .. versionadded:: 2.7.0 +UTC = tzutc() + +__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", + "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz", + "enfold", "datetime_ambiguous", "datetime_exists", + "resolve_imaginary", "UTC", "DeprecatedTzFormatWarning"] + + +class DeprecatedTzFormatWarning(Warning): + """Warning raised when time zones are parsed from deprecated formats.""" diff --git a/modules/dateutil/tz/_common.py b/modules/dateutil/tz/_common.py new file mode 100644 index 0000000..ccabb7d --- /dev/null +++ b/modules/dateutil/tz/_common.py @@ -0,0 +1,415 @@ +from six import PY3 + +from functools import wraps + +from datetime import datetime, timedelta, tzinfo + + +ZERO = timedelta(0) + +__all__ = ['tzname_in_python2', 'enfold'] + + +def tzname_in_python2(namefunc): + """Change unicode output into bytestrings in Python 2 + + tzname() API changed in Python 3. It used to return bytes, but was changed + to unicode strings + """ + def adjust_encoding(*args, **kwargs): + name = namefunc(*args, **kwargs) + if name is not None and not PY3: + name = name.encode() + + return name + + return adjust_encoding + + +# The following is adapted from Alexander Belopolsky's tz library +# https://github.com/abalkin/tz +if hasattr(datetime, 'fold'): + # This is the pre-python 3.6 fold situation + def enfold(dt, fold=1): + """ + Provides a unified interface for assigning the ``fold`` attribute to + datetimes both before and after the implementation of PEP-495. + + :param fold: + The value for the ``fold`` attribute in the returned datetime. This + should be either 0 or 1. + + :return: + Returns an object for which ``getattr(dt, 'fold', 0)`` returns + ``fold`` for all versions of Python. In versions prior to + Python 3.6, this is a ``_DatetimeWithFold`` object, which is a + subclass of :py:class:`datetime.datetime` with the ``fold`` + attribute added, if ``fold`` is 1. + + .. versionadded:: 2.6.0 + """ + return dt.replace(fold=fold) + +else: + class _DatetimeWithFold(datetime): + """ + This is a class designed to provide a PEP 495-compliant interface for + Python versions before 3.6. It is used only for dates in a fold, so + the ``fold`` attribute is fixed at ``1``. + + .. versionadded:: 2.6.0 + """ + __slots__ = () + + def replace(self, *args, **kwargs): + """ + Return a datetime with the same attributes, except for those + attributes given new values by whichever keyword arguments are + specified. Note that tzinfo=None can be specified to create a naive + datetime from an aware datetime with no conversion of date and time + data. + + This is reimplemented in ``_DatetimeWithFold`` because pypy3 will + return a ``datetime.datetime`` even if ``fold`` is unchanged. + """ + argnames = ( + 'year', 'month', 'day', 'hour', 'minute', 'second', + 'microsecond', 'tzinfo' + ) + + for arg, argname in zip(args, argnames): + if argname in kwargs: + raise TypeError('Duplicate argument: {}'.format(argname)) + + kwargs[argname] = arg + + for argname in argnames: + if argname not in kwargs: + kwargs[argname] = getattr(self, argname) + + dt_class = self.__class__ if kwargs.get('fold', 1) else datetime + + return dt_class(**kwargs) + + @property + def fold(self): + return 1 + + def enfold(dt, fold=1): + """ + Provides a unified interface for assigning the ``fold`` attribute to + datetimes both before and after the implementation of PEP-495. + + :param fold: + The value for the ``fold`` attribute in the returned datetime. This + should be either 0 or 1. + + :return: + Returns an object for which ``getattr(dt, 'fold', 0)`` returns + ``fold`` for all versions of Python. In versions prior to + Python 3.6, this is a ``_DatetimeWithFold`` object, which is a + subclass of :py:class:`datetime.datetime` with the ``fold`` + attribute added, if ``fold`` is 1. + + .. versionadded:: 2.6.0 + """ + if getattr(dt, 'fold', 0) == fold: + return dt + + args = dt.timetuple()[:6] + args += (dt.microsecond, dt.tzinfo) + + if fold: + return _DatetimeWithFold(*args) + else: + return datetime(*args) + + +def _validate_fromutc_inputs(f): + """ + The CPython version of ``fromutc`` checks that the input is a ``datetime`` + object and that ``self`` is attached as its ``tzinfo``. + """ + @wraps(f) + def fromutc(self, dt): + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + return f(self, dt) + + return fromutc + + +class _tzinfo(tzinfo): + """ + Base class for all ``dateutil`` ``tzinfo`` objects. + """ + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + + dt = dt.replace(tzinfo=self) + + wall_0 = enfold(dt, fold=0) + wall_1 = enfold(dt, fold=1) + + same_offset = wall_0.utcoffset() == wall_1.utcoffset() + same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None) + + return same_dt and not same_offset + + def _fold_status(self, dt_utc, dt_wall): + """ + Determine the fold status of a "wall" datetime, given a representation + of the same datetime as a (naive) UTC datetime. This is calculated based + on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all + datetimes, and that this offset is the actual number of hours separating + ``dt_utc`` and ``dt_wall``. + + :param dt_utc: + Representation of the datetime as UTC + + :param dt_wall: + Representation of the datetime as "wall time". This parameter must + either have a `fold` attribute or have a fold-naive + :class:`datetime.tzinfo` attached, otherwise the calculation may + fail. + """ + if self.is_ambiguous(dt_wall): + delta_wall = dt_wall - dt_utc + _fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst())) + else: + _fold = 0 + + return _fold + + def _fold(self, dt): + return getattr(dt, 'fold', 0) + + def _fromutc(self, dt): + """ + Given a timezone-aware datetime in a given timezone, calculates a + timezone-aware datetime in a new timezone. + + Since this is the one time that we *know* we have an unambiguous + datetime object, we take this opportunity to determine whether the + datetime is ambiguous and in a "fold" state (e.g. if it's the first + occurence, chronologically, of the ambiguous datetime). + + :param dt: + A timezone-aware :class:`datetime.datetime` object. + """ + + # Re-implement the algorithm from Python's datetime.py + dtoff = dt.utcoffset() + if dtoff is None: + raise ValueError("fromutc() requires a non-None utcoffset() " + "result") + + # The original datetime.py code assumes that `dst()` defaults to + # zero during ambiguous times. PEP 495 inverts this presumption, so + # for pre-PEP 495 versions of python, we need to tweak the algorithm. + dtdst = dt.dst() + if dtdst is None: + raise ValueError("fromutc() requires a non-None dst() result") + delta = dtoff - dtdst + + dt += delta + # Set fold=1 so we can default to being in the fold for + # ambiguous dates. + dtdst = enfold(dt, fold=1).dst() + if dtdst is None: + raise ValueError("fromutc(): dt.dst gave inconsistent " + "results; cannot convert") + return dt + dtdst + + @_validate_fromutc_inputs + def fromutc(self, dt): + """ + Given a timezone-aware datetime in a given timezone, calculates a + timezone-aware datetime in a new timezone. + + Since this is the one time that we *know* we have an unambiguous + datetime object, we take this opportunity to determine whether the + datetime is ambiguous and in a "fold" state (e.g. if it's the first + occurance, chronologically, of the ambiguous datetime). + + :param dt: + A timezone-aware :class:`datetime.datetime` object. + """ + dt_wall = self._fromutc(dt) + + # Calculate the fold status given the two datetimes. + _fold = self._fold_status(dt, dt_wall) + + # Set the default fold value for ambiguous dates + return enfold(dt_wall, fold=_fold) + + +class tzrangebase(_tzinfo): + """ + This is an abstract base class for time zones represented by an annual + transition into and out of DST. Child classes should implement the following + methods: + + * ``__init__(self, *args, **kwargs)`` + * ``transitions(self, year)`` - this is expected to return a tuple of + datetimes representing the DST on and off transitions in standard + time. + + A fully initialized ``tzrangebase`` subclass should also provide the + following attributes: + * ``hasdst``: Boolean whether or not the zone uses DST. + * ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects + representing the respective UTC offsets. + * ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short + abbreviations in DST and STD, respectively. + * ``_hasdst``: Whether or not the zone has DST. + + .. versionadded:: 2.6.0 + """ + def __init__(self): + raise NotImplementedError('tzrangebase is an abstract base class') + + def utcoffset(self, dt): + isdst = self._isdst(dt) + + if isdst is None: + return None + elif isdst: + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + isdst = self._isdst(dt) + + if isdst is None: + return None + elif isdst: + return self._dst_base_offset + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + if self._isdst(dt): + return self._dst_abbr + else: + return self._std_abbr + + def fromutc(self, dt): + """ Given a datetime in UTC, return local time """ + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + # Get transitions - if there are none, fixed offset + transitions = self.transitions(dt.year) + if transitions is None: + return dt + self.utcoffset(dt) + + # Get the transition times in UTC + dston, dstoff = transitions + + dston -= self._std_offset + dstoff -= self._std_offset + + utc_transitions = (dston, dstoff) + dt_utc = dt.replace(tzinfo=None) + + isdst = self._naive_isdst(dt_utc, utc_transitions) + + if isdst: + dt_wall = dt + self._dst_offset + else: + dt_wall = dt + self._std_offset + + _fold = int(not isdst and self.is_ambiguous(dt_wall)) + + return enfold(dt_wall, fold=_fold) + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + if not self.hasdst: + return False + + start, end = self.transitions(dt.year) + + dt = dt.replace(tzinfo=None) + return (end <= dt < end + self._dst_base_offset) + + def _isdst(self, dt): + if not self.hasdst: + return False + elif dt is None: + return None + + transitions = self.transitions(dt.year) + + if transitions is None: + return False + + dt = dt.replace(tzinfo=None) + + isdst = self._naive_isdst(dt, transitions) + + # Handle ambiguous dates + if not isdst and self.is_ambiguous(dt): + return not self._fold(dt) + else: + return isdst + + def _naive_isdst(self, dt, transitions): + dston, dstoff = transitions + + dt = dt.replace(tzinfo=None) + + if dston < dstoff: + isdst = dston <= dt < dstoff + else: + isdst = not dstoff <= dt < dston + + return isdst + + @property + def _dst_base_offset(self): + return self._dst_offset - self._std_offset + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(...)" % self.__class__.__name__ + + __reduce__ = object.__reduce__ diff --git a/modules/dateutil/tz/_factories.py b/modules/dateutil/tz/_factories.py new file mode 100644 index 0000000..de2e0c1 --- /dev/null +++ b/modules/dateutil/tz/_factories.py @@ -0,0 +1,49 @@ +from datetime import timedelta + + +class _TzSingleton(type): + def __init__(cls, *args, **kwargs): + cls.__instance = None + super(_TzSingleton, cls).__init__(*args, **kwargs) + + def __call__(cls): + if cls.__instance is None: + cls.__instance = super(_TzSingleton, cls).__call__() + return cls.__instance + +class _TzFactory(type): + def instance(cls, *args, **kwargs): + """Alternate constructor that returns a fresh instance""" + return type.__call__(cls, *args, **kwargs) + + +class _TzOffsetFactory(_TzFactory): + def __init__(cls, *args, **kwargs): + cls.__instances = {} + + def __call__(cls, name, offset): + if isinstance(offset, timedelta): + key = (name, offset.total_seconds()) + else: + key = (name, offset) + + instance = cls.__instances.get(key, None) + if instance is None: + instance = cls.__instances.setdefault(key, + cls.instance(name, offset)) + return instance + + +class _TzStrFactory(_TzFactory): + def __init__(cls, *args, **kwargs): + cls.__instances = {} + + def __call__(cls, s, posix_offset=False): + key = (s, posix_offset) + instance = cls.__instances.get(key, None) + + if instance is None: + instance = cls.__instances.setdefault(key, + cls.instance(s, posix_offset)) + return instance + diff --git a/modules/dateutil/tz/tz.py b/modules/dateutil/tz/tz.py new file mode 100644 index 0000000..ac82b9c --- /dev/null +++ b/modules/dateutil/tz/tz.py @@ -0,0 +1,1785 @@ +# -*- coding: utf-8 -*- +""" +This module offers timezone implementations subclassing the abstract +:py:class:`datetime.tzinfo` type. There are classes to handle tzfile format +files (usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`, +etc), TZ environment string (in all known formats), given ranges (with help +from relative deltas), local machine timezone, fixed offset timezone, and UTC +timezone. +""" +import datetime +import struct +import time +import sys +import os +import bisect + +import six +from six import string_types +from six.moves import _thread +from ._common import tzname_in_python2, _tzinfo +from ._common import tzrangebase, enfold +from ._common import _validate_fromutc_inputs + +from ._factories import _TzSingleton, _TzOffsetFactory +from ._factories import _TzStrFactory +try: + from .win import tzwin, tzwinlocal +except ImportError: + tzwin = tzwinlocal = None + +ZERO = datetime.timedelta(0) +EPOCH = datetime.datetime.utcfromtimestamp(0) +EPOCHORDINAL = EPOCH.toordinal() + + +@six.add_metaclass(_TzSingleton) +class tzutc(datetime.tzinfo): + """ + This is a tzinfo object that represents the UTC time zone. + + **Examples:** + + .. doctest:: + + >>> from datetime import * + >>> from dateutil.tz import * + + >>> datetime.now() + datetime.datetime(2003, 9, 27, 9, 40, 1, 521290) + + >>> datetime.now(tzutc()) + datetime.datetime(2003, 9, 27, 12, 40, 12, 156379, tzinfo=tzutc()) + + >>> datetime.now(tzutc()).tzname() + 'UTC' + + .. versionchanged:: 2.7.0 + ``tzutc()`` is now a singleton, so the result of ``tzutc()`` will + always return the same object. + + .. doctest:: + + >>> from dateutil.tz import tzutc, UTC + >>> tzutc() is tzutc() + True + >>> tzutc() is UTC + True + """ + def utcoffset(self, dt): + return ZERO + + def dst(self, dt): + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return "UTC" + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + return False + + @_validate_fromutc_inputs + def fromutc(self, dt): + """ + Fast track version of fromutc() returns the original ``dt`` object for + any valid :py:class:`datetime.datetime` object. + """ + return dt + + def __eq__(self, other): + if not isinstance(other, (tzutc, tzoffset)): + return NotImplemented + + return (isinstance(other, tzutc) or + (isinstance(other, tzoffset) and other._offset == ZERO)) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + + +@six.add_metaclass(_TzOffsetFactory) +class tzoffset(datetime.tzinfo): + """ + A simple class for representing a fixed offset from UTC. + + :param name: + The timezone name, to be returned when ``tzname()`` is called. + :param offset: + The time zone offset in seconds, or (since version 2.6.0, represented + as a :py:class:`datetime.timedelta` object). + """ + def __init__(self, name, offset): + self._name = name + + try: + # Allow a timedelta + offset = offset.total_seconds() + except (TypeError, AttributeError): + pass + self._offset = datetime.timedelta(seconds=offset) + + def utcoffset(self, dt): + return self._offset + + def dst(self, dt): + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._name + + @_validate_fromutc_inputs + def fromutc(self, dt): + return dt + self._offset + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + return False + + def __eq__(self, other): + if not isinstance(other, tzoffset): + return NotImplemented + + return self._offset == other._offset + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(%s, %s)" % (self.__class__.__name__, + repr(self._name), + int(self._offset.total_seconds())) + + __reduce__ = object.__reduce__ + + +class tzlocal(_tzinfo): + """ + A :class:`tzinfo` subclass built around the ``time`` timezone functions. + """ + def __init__(self): + super(tzlocal, self).__init__() + + self._std_offset = datetime.timedelta(seconds=-time.timezone) + if time.daylight: + self._dst_offset = datetime.timedelta(seconds=-time.altzone) + else: + self._dst_offset = self._std_offset + + self._dst_saved = self._dst_offset - self._std_offset + self._hasdst = bool(self._dst_saved) + self._tznames = tuple(time.tzname) + + def utcoffset(self, dt): + if dt is None and self._hasdst: + return None + + if self._isdst(dt): + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + if dt is None and self._hasdst: + return None + + if self._isdst(dt): + return self._dst_offset - self._std_offset + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._tznames[self._isdst(dt)] + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + naive_dst = self._naive_is_dst(dt) + return (not naive_dst and + (naive_dst != self._naive_is_dst(dt - self._dst_saved))) + + def _naive_is_dst(self, dt): + timestamp = _datetime_to_timestamp(dt) + return time.localtime(timestamp + time.timezone).tm_isdst + + def _isdst(self, dt, fold_naive=True): + # We can't use mktime here. It is unstable when deciding if + # the hour near to a change is DST or not. + # + # timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, + # dt.minute, dt.second, dt.weekday(), 0, -1)) + # return time.localtime(timestamp).tm_isdst + # + # The code above yields the following result: + # + # >>> import tz, datetime + # >>> t = tz.tzlocal() + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRDT' + # >>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname() + # 'BRST' + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRST' + # >>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname() + # 'BRDT' + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRDT' + # + # Here is a more stable implementation: + # + if not self._hasdst: + return False + + # Check for ambiguous times: + dstval = self._naive_is_dst(dt) + fold = getattr(dt, 'fold', None) + + if self.is_ambiguous(dt): + if fold is not None: + return not self._fold(dt) + else: + return True + + return dstval + + def __eq__(self, other): + if isinstance(other, tzlocal): + return (self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset) + elif isinstance(other, tzutc): + return (not self._hasdst and + self._tznames[0] in {'UTC', 'GMT'} and + self._std_offset == ZERO) + elif isinstance(other, tzoffset): + return (not self._hasdst and + self._tznames[0] == other._name and + self._std_offset == other._offset) + else: + return NotImplemented + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + + +class _ttinfo(object): + __slots__ = ["offset", "delta", "isdst", "abbr", + "isstd", "isgmt", "dstoffset"] + + def __init__(self): + for attr in self.__slots__: + setattr(self, attr, None) + + def __repr__(self): + l = [] + for attr in self.__slots__: + value = getattr(self, attr) + if value is not None: + l.append("%s=%s" % (attr, repr(value))) + return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) + + def __eq__(self, other): + if not isinstance(other, _ttinfo): + return NotImplemented + + return (self.offset == other.offset and + self.delta == other.delta and + self.isdst == other.isdst and + self.abbr == other.abbr and + self.isstd == other.isstd and + self.isgmt == other.isgmt and + self.dstoffset == other.dstoffset) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __getstate__(self): + state = {} + for name in self.__slots__: + state[name] = getattr(self, name, None) + return state + + def __setstate__(self, state): + for name in self.__slots__: + if name in state: + setattr(self, name, state[name]) + + +class _tzfile(object): + """ + Lightweight class for holding the relevant transition and time zone + information read from binary tzfiles. + """ + attrs = ['trans_list', 'trans_list_utc', 'trans_idx', 'ttinfo_list', + 'ttinfo_std', 'ttinfo_dst', 'ttinfo_before', 'ttinfo_first'] + + def __init__(self, **kwargs): + for attr in self.attrs: + setattr(self, attr, kwargs.get(attr, None)) + + +class tzfile(_tzinfo): + """ + This is a ``tzinfo`` subclass thant allows one to use the ``tzfile(5)`` + format timezone files to extract current and historical zone information. + + :param fileobj: + This can be an opened file stream or a file name that the time zone + information can be read from. + + :param filename: + This is an optional parameter specifying the source of the time zone + information in the event that ``fileobj`` is a file object. If omitted + and ``fileobj`` is a file stream, this parameter will be set either to + ``fileobj``'s ``name`` attribute or to ``repr(fileobj)``. + + See `Sources for Time Zone and Daylight Saving Time Data + `_ for more information. + Time zone files can be compiled from the `IANA Time Zone database files + `_ with the `zic time zone compiler + `_ + + .. note:: + + Only construct a ``tzfile`` directly if you have a specific timezone + file on disk that you want to read into a Python ``tzinfo`` object. + If you want to get a ``tzfile`` representing a specific IANA zone, + (e.g. ``'America/New_York'``), you should call + :func:`dateutil.tz.gettz` with the zone identifier. + + + **Examples:** + + Using the US Eastern time zone as an example, we can see that a ``tzfile`` + provides time zone information for the standard Daylight Saving offsets: + + .. testsetup:: tzfile + + from dateutil.tz import gettz + from datetime import datetime + + .. doctest:: tzfile + + >>> NYC = gettz('America/New_York') + >>> NYC + tzfile('/usr/share/zoneinfo/America/New_York') + + >>> print(datetime(2016, 1, 3, tzinfo=NYC)) # EST + 2016-01-03 00:00:00-05:00 + + >>> print(datetime(2016, 7, 7, tzinfo=NYC)) # EDT + 2016-07-07 00:00:00-04:00 + + + The ``tzfile`` structure contains a fully history of the time zone, + so historical dates will also have the right offsets. For example, before + the adoption of the UTC standards, New York used local solar mean time: + + .. doctest:: tzfile + + >>> print(datetime(1901, 4, 12, tzinfo=NYC)) # LMT + 1901-04-12 00:00:00-04:56 + + And during World War II, New York was on "Eastern War Time", which was a + state of permanent daylight saving time: + + .. doctest:: tzfile + + >>> print(datetime(1944, 2, 7, tzinfo=NYC)) # EWT + 1944-02-07 00:00:00-04:00 + + """ + + def __init__(self, fileobj, filename=None): + super(tzfile, self).__init__() + + file_opened_here = False + if isinstance(fileobj, string_types): + self._filename = fileobj + fileobj = open(fileobj, 'rb') + file_opened_here = True + elif filename is not None: + self._filename = filename + elif hasattr(fileobj, "name"): + self._filename = fileobj.name + else: + self._filename = repr(fileobj) + + if fileobj is not None: + if not file_opened_here: + fileobj = _ContextWrapper(fileobj) + + with fileobj as file_stream: + tzobj = self._read_tzfile(file_stream) + + self._set_tzdata(tzobj) + + def _set_tzdata(self, tzobj): + """ Set the time zone data of this object from a _tzfile object """ + # Copy the relevant attributes over as private attributes + for attr in _tzfile.attrs: + setattr(self, '_' + attr, getattr(tzobj, attr)) + + def _read_tzfile(self, fileobj): + out = _tzfile() + + # From tzfile(5): + # + # The time zone information files used by tzset(3) + # begin with the magic characters "TZif" to identify + # them as time zone information files, followed by + # sixteen bytes reserved for future use, followed by + # six four-byte values of type long, written in a + # ``standard'' byte order (the high-order byte + # of the value is written first). + if fileobj.read(4).decode() != "TZif": + raise ValueError("magic not found") + + fileobj.read(16) + + ( + # The number of UTC/local indicators stored in the file. + ttisgmtcnt, + + # The number of standard/wall indicators stored in the file. + ttisstdcnt, + + # The number of leap seconds for which data is + # stored in the file. + leapcnt, + + # The number of "transition times" for which data + # is stored in the file. + timecnt, + + # The number of "local time types" for which data + # is stored in the file (must not be zero). + typecnt, + + # The number of characters of "time zone + # abbreviation strings" stored in the file. + charcnt, + + ) = struct.unpack(">6l", fileobj.read(24)) + + # The above header is followed by tzh_timecnt four-byte + # values of type long, sorted in ascending order. + # These values are written in ``standard'' byte order. + # Each is used as a transition time (as returned by + # time(2)) at which the rules for computing local time + # change. + + if timecnt: + out.trans_list_utc = list(struct.unpack(">%dl" % timecnt, + fileobj.read(timecnt*4))) + else: + out.trans_list_utc = [] + + # Next come tzh_timecnt one-byte values of type unsigned + # char; each one tells which of the different types of + # ``local time'' types described in the file is associated + # with the same-indexed transition time. These values + # serve as indices into an array of ttinfo structures that + # appears next in the file. + + if timecnt: + out.trans_idx = struct.unpack(">%dB" % timecnt, + fileobj.read(timecnt)) + else: + out.trans_idx = [] + + # Each ttinfo structure is written as a four-byte value + # for tt_gmtoff of type long, in a standard byte + # order, followed by a one-byte value for tt_isdst + # and a one-byte value for tt_abbrind. In each + # structure, tt_gmtoff gives the number of + # seconds to be added to UTC, tt_isdst tells whether + # tm_isdst should be set by localtime(3), and + # tt_abbrind serves as an index into the array of + # time zone abbreviation characters that follow the + # ttinfo structure(s) in the file. + + ttinfo = [] + + for i in range(typecnt): + ttinfo.append(struct.unpack(">lbb", fileobj.read(6))) + + abbr = fileobj.read(charcnt).decode() + + # Then there are tzh_leapcnt pairs of four-byte + # values, written in standard byte order; the + # first value of each pair gives the time (as + # returned by time(2)) at which a leap second + # occurs; the second gives the total number of + # leap seconds to be applied after the given time. + # The pairs of values are sorted in ascending order + # by time. + + # Not used, for now (but seek for correct file position) + if leapcnt: + fileobj.seek(leapcnt * 8, os.SEEK_CUR) + + # Then there are tzh_ttisstdcnt standard/wall + # indicators, each stored as a one-byte value; + # they tell whether the transition times associated + # with local time types were specified as standard + # time or wall clock time, and are used when + # a time zone file is used in handling POSIX-style + # time zone environment variables. + + if ttisstdcnt: + isstd = struct.unpack(">%db" % ttisstdcnt, + fileobj.read(ttisstdcnt)) + + # Finally, there are tzh_ttisgmtcnt UTC/local + # indicators, each stored as a one-byte value; + # they tell whether the transition times associated + # with local time types were specified as UTC or + # local time, and are used when a time zone file + # is used in handling POSIX-style time zone envi- + # ronment variables. + + if ttisgmtcnt: + isgmt = struct.unpack(">%db" % ttisgmtcnt, + fileobj.read(ttisgmtcnt)) + + # Build ttinfo list + out.ttinfo_list = [] + for i in range(typecnt): + gmtoff, isdst, abbrind = ttinfo[i] + # Round to full-minutes if that's not the case. Python's + # datetime doesn't accept sub-minute timezones. Check + # http://python.org/sf/1447945 for some information. + gmtoff = 60 * ((gmtoff + 30) // 60) + tti = _ttinfo() + tti.offset = gmtoff + tti.dstoffset = datetime.timedelta(0) + tti.delta = datetime.timedelta(seconds=gmtoff) + tti.isdst = isdst + tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)] + tti.isstd = (ttisstdcnt > i and isstd[i] != 0) + tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0) + out.ttinfo_list.append(tti) + + # Replace ttinfo indexes for ttinfo objects. + out.trans_idx = [out.ttinfo_list[idx] for idx in out.trans_idx] + + # Set standard, dst, and before ttinfos. before will be + # used when a given time is before any transitions, + # and will be set to the first non-dst ttinfo, or to + # the first dst, if all of them are dst. + out.ttinfo_std = None + out.ttinfo_dst = None + out.ttinfo_before = None + if out.ttinfo_list: + if not out.trans_list_utc: + out.ttinfo_std = out.ttinfo_first = out.ttinfo_list[0] + else: + for i in range(timecnt-1, -1, -1): + tti = out.trans_idx[i] + if not out.ttinfo_std and not tti.isdst: + out.ttinfo_std = tti + elif not out.ttinfo_dst and tti.isdst: + out.ttinfo_dst = tti + + if out.ttinfo_std and out.ttinfo_dst: + break + else: + if out.ttinfo_dst and not out.ttinfo_std: + out.ttinfo_std = out.ttinfo_dst + + for tti in out.ttinfo_list: + if not tti.isdst: + out.ttinfo_before = tti + break + else: + out.ttinfo_before = out.ttinfo_list[0] + + # Now fix transition times to become relative to wall time. + # + # I'm not sure about this. In my tests, the tz source file + # is setup to wall time, and in the binary file isstd and + # isgmt are off, so it should be in wall time. OTOH, it's + # always in gmt time. Let me know if you have comments + # about this. + laststdoffset = None + out.trans_list = [] + for i, tti in enumerate(out.trans_idx): + if not tti.isdst: + offset = tti.offset + laststdoffset = offset + else: + if laststdoffset is not None: + # Store the DST offset as well and update it in the list + tti.dstoffset = tti.offset - laststdoffset + out.trans_idx[i] = tti + + offset = laststdoffset or 0 + + out.trans_list.append(out.trans_list_utc[i] + offset) + + # In case we missed any DST offsets on the way in for some reason, make + # a second pass over the list, looking for the /next/ DST offset. + laststdoffset = None + for i in reversed(range(len(out.trans_idx))): + tti = out.trans_idx[i] + if tti.isdst: + if not (tti.dstoffset or laststdoffset is None): + tti.dstoffset = tti.offset - laststdoffset + else: + laststdoffset = tti.offset + + if not isinstance(tti.dstoffset, datetime.timedelta): + tti.dstoffset = datetime.timedelta(seconds=tti.dstoffset) + + out.trans_idx[i] = tti + + out.trans_idx = tuple(out.trans_idx) + out.trans_list = tuple(out.trans_list) + out.trans_list_utc = tuple(out.trans_list_utc) + + return out + + def _find_last_transition(self, dt, in_utc=False): + # If there's no list, there are no transitions to find + if not self._trans_list: + return None + + timestamp = _datetime_to_timestamp(dt) + + # Find where the timestamp fits in the transition list - if the + # timestamp is a transition time, it's part of the "after" period. + trans_list = self._trans_list_utc if in_utc else self._trans_list + idx = bisect.bisect_right(trans_list, timestamp) + + # We want to know when the previous transition was, so subtract off 1 + return idx - 1 + + def _get_ttinfo(self, idx): + # For no list or after the last transition, default to _ttinfo_std + if idx is None or (idx + 1) >= len(self._trans_list): + return self._ttinfo_std + + # If there is a list and the time is before it, return _ttinfo_before + if idx < 0: + return self._ttinfo_before + + return self._trans_idx[idx] + + def _find_ttinfo(self, dt): + idx = self._resolve_ambiguous_time(dt) + + return self._get_ttinfo(idx) + + def fromutc(self, dt): + """ + The ``tzfile`` implementation of :py:func:`datetime.tzinfo.fromutc`. + + :param dt: + A :py:class:`datetime.datetime` object. + + :raises TypeError: + Raised if ``dt`` is not a :py:class:`datetime.datetime` object. + + :raises ValueError: + Raised if this is called with a ``dt`` which does not have this + ``tzinfo`` attached. + + :return: + Returns a :py:class:`datetime.datetime` object representing the + wall time in ``self``'s time zone. + """ + # These isinstance checks are in datetime.tzinfo, so we'll preserve + # them, even if we don't care about duck typing. + if not isinstance(dt, datetime.datetime): + raise TypeError("fromutc() requires a datetime argument") + + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + # First treat UTC as wall time and get the transition we're in. + idx = self._find_last_transition(dt, in_utc=True) + tti = self._get_ttinfo(idx) + + dt_out = dt + datetime.timedelta(seconds=tti.offset) + + fold = self.is_ambiguous(dt_out, idx=idx) + + return enfold(dt_out, fold=int(fold)) + + def is_ambiguous(self, dt, idx=None): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + if idx is None: + idx = self._find_last_transition(dt) + + # Calculate the difference in offsets from current to previous + timestamp = _datetime_to_timestamp(dt) + tti = self._get_ttinfo(idx) + + if idx is None or idx <= 0: + return False + + od = self._get_ttinfo(idx - 1).offset - tti.offset + tt = self._trans_list[idx] # Transition time + + return timestamp < tt + od + + def _resolve_ambiguous_time(self, dt): + idx = self._find_last_transition(dt) + + # If we have no transitions, return the index + _fold = self._fold(dt) + if idx is None or idx == 0: + return idx + + # If it's ambiguous and we're in a fold, shift to a different index. + idx_offset = int(not _fold and self.is_ambiguous(dt, idx)) + + return idx - idx_offset + + def utcoffset(self, dt): + if dt is None: + return None + + if not self._ttinfo_std: + return ZERO + + return self._find_ttinfo(dt).delta + + def dst(self, dt): + if dt is None: + return None + + if not self._ttinfo_dst: + return ZERO + + tti = self._find_ttinfo(dt) + + if not tti.isdst: + return ZERO + + # The documentation says that utcoffset()-dst() must + # be constant for every dt. + return tti.dstoffset + + @tzname_in_python2 + def tzname(self, dt): + if not self._ttinfo_std or dt is None: + return None + return self._find_ttinfo(dt).abbr + + def __eq__(self, other): + if not isinstance(other, tzfile): + return NotImplemented + return (self._trans_list == other._trans_list and + self._trans_idx == other._trans_idx and + self._ttinfo_list == other._ttinfo_list) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._filename)) + + def __reduce__(self): + return self.__reduce_ex__(None) + + def __reduce_ex__(self, protocol): + return (self.__class__, (None, self._filename), self.__dict__) + + +class tzrange(tzrangebase): + """ + The ``tzrange`` object is a time zone specified by a set of offsets and + abbreviations, equivalent to the way the ``TZ`` variable can be specified + in POSIX-like systems, but using Python delta objects to specify DST + start, end and offsets. + + :param stdabbr: + The abbreviation for standard time (e.g. ``'EST'``). + + :param stdoffset: + An integer or :class:`datetime.timedelta` object or equivalent + specifying the base offset from UTC. + + If unspecified, +00:00 is used. + + :param dstabbr: + The abbreviation for DST / "Summer" time (e.g. ``'EDT'``). + + If specified, with no other DST information, DST is assumed to occur + and the default behavior or ``dstoffset``, ``start`` and ``end`` is + used. If unspecified and no other DST information is specified, it + is assumed that this zone has no DST. + + If this is unspecified and other DST information is *is* specified, + DST occurs in the zone but the time zone abbreviation is left + unchanged. + + :param dstoffset: + A an integer or :class:`datetime.timedelta` object or equivalent + specifying the UTC offset during DST. If unspecified and any other DST + information is specified, it is assumed to be the STD offset +1 hour. + + :param start: + A :class:`relativedelta.relativedelta` object or equivalent specifying + the time and time of year that daylight savings time starts. To + specify, for example, that DST starts at 2AM on the 2nd Sunday in + March, pass: + + ``relativedelta(hours=2, month=3, day=1, weekday=SU(+2))`` + + If unspecified and any other DST information is specified, the default + value is 2 AM on the first Sunday in April. + + :param end: + A :class:`relativedelta.relativedelta` object or equivalent + representing the time and time of year that daylight savings time + ends, with the same specification method as in ``start``. One note is + that this should point to the first time in the *standard* zone, so if + a transition occurs at 2AM in the DST zone and the clocks are set back + 1 hour to 1AM, set the ``hours`` parameter to +1. + + + **Examples:** + + .. testsetup:: tzrange + + from dateutil.tz import tzrange, tzstr + + .. doctest:: tzrange + + >>> tzstr('EST5EDT') == tzrange("EST", -18000, "EDT") + True + + >>> from dateutil.relativedelta import * + >>> range1 = tzrange("EST", -18000, "EDT") + >>> range2 = tzrange("EST", -18000, "EDT", -14400, + ... relativedelta(hours=+2, month=4, day=1, + ... weekday=SU(+1)), + ... relativedelta(hours=+1, month=10, day=31, + ... weekday=SU(-1))) + >>> tzstr('EST5EDT') == range1 == range2 + True + + """ + def __init__(self, stdabbr, stdoffset=None, + dstabbr=None, dstoffset=None, + start=None, end=None): + + global relativedelta + from dateutil import relativedelta + + self._std_abbr = stdabbr + self._dst_abbr = dstabbr + + try: + stdoffset = stdoffset.total_seconds() + except (TypeError, AttributeError): + pass + + try: + dstoffset = dstoffset.total_seconds() + except (TypeError, AttributeError): + pass + + if stdoffset is not None: + self._std_offset = datetime.timedelta(seconds=stdoffset) + else: + self._std_offset = ZERO + + if dstoffset is not None: + self._dst_offset = datetime.timedelta(seconds=dstoffset) + elif dstabbr and stdoffset is not None: + self._dst_offset = self._std_offset + datetime.timedelta(hours=+1) + else: + self._dst_offset = ZERO + + if dstabbr and start is None: + self._start_delta = relativedelta.relativedelta( + hours=+2, month=4, day=1, weekday=relativedelta.SU(+1)) + else: + self._start_delta = start + + if dstabbr and end is None: + self._end_delta = relativedelta.relativedelta( + hours=+1, month=10, day=31, weekday=relativedelta.SU(-1)) + else: + self._end_delta = end + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = bool(self._start_delta) + + def transitions(self, year): + """ + For a given year, get the DST on and off transition times, expressed + always on the standard time side. For zones with no transitions, this + function returns ``None``. + + :param year: + The year whose transitions you would like to query. + + :return: + Returns a :class:`tuple` of :class:`datetime.datetime` objects, + ``(dston, dstoff)`` for zones with an annual DST transition, or + ``None`` for fixed offset zones. + """ + if not self.hasdst: + return None + + base_year = datetime.datetime(year, 1, 1) + + start = base_year + self._start_delta + end = base_year + self._end_delta + + return (start, end) + + def __eq__(self, other): + if not isinstance(other, tzrange): + return NotImplemented + + return (self._std_abbr == other._std_abbr and + self._dst_abbr == other._dst_abbr and + self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset and + self._start_delta == other._start_delta and + self._end_delta == other._end_delta) + + @property + def _dst_base_offset(self): + return self._dst_base_offset_ + + +@six.add_metaclass(_TzStrFactory) +class tzstr(tzrange): + """ + ``tzstr`` objects are time zone objects specified by a time-zone string as + it would be passed to a ``TZ`` variable on POSIX-style systems (see + the `GNU C Library: TZ Variable`_ for more details). + + There is one notable exception, which is that POSIX-style time zones use an + inverted offset format, so normally ``GMT+3`` would be parsed as an offset + 3 hours *behind* GMT. The ``tzstr`` time zone object will parse this as an + offset 3 hours *ahead* of GMT. If you would like to maintain the POSIX + behavior, pass a ``True`` value to ``posix_offset``. + + The :class:`tzrange` object provides the same functionality, but is + specified using :class:`relativedelta.relativedelta` objects. rather than + strings. + + :param s: + A time zone string in ``TZ`` variable format. This can be a + :class:`bytes` (2.x: :class:`str`), :class:`str` (2.x: + :class:`unicode`) or a stream emitting unicode characters + (e.g. :class:`StringIO`). + + :param posix_offset: + Optional. If set to ``True``, interpret strings such as ``GMT+3`` or + ``UTC+3`` as being 3 hours *behind* UTC rather than ahead, per the + POSIX standard. + + .. caution:: + + Prior to version 2.7.0, this function also supported time zones + in the format: + + * ``EST5EDT,4,0,6,7200,10,0,26,7200,3600`` + * ``EST5EDT,4,1,0,7200,10,-1,0,7200,3600`` + + This format is non-standard and has been deprecated; this function + will raise a :class:`DeprecatedTZFormatWarning` until + support is removed in a future version. + + .. _`GNU C Library: TZ Variable`: + https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + """ + def __init__(self, s, posix_offset=False): + global parser + from dateutil.parser import _parser as parser + + self._s = s + + res = parser._parsetz(s) + if res is None or res.any_unused_tokens: + raise ValueError("unknown string format") + + # Here we break the compatibility with the TZ variable handling. + # GMT-3 actually *means* the timezone -3. + if res.stdabbr in ("GMT", "UTC") and not posix_offset: + res.stdoffset *= -1 + + # We must initialize it first, since _delta() needs + # _std_offset and _dst_offset set. Use False in start/end + # to avoid building it two times. + tzrange.__init__(self, res.stdabbr, res.stdoffset, + res.dstabbr, res.dstoffset, + start=False, end=False) + + if not res.dstabbr: + self._start_delta = None + self._end_delta = None + else: + self._start_delta = self._delta(res.start) + if self._start_delta: + self._end_delta = self._delta(res.end, isend=1) + + self.hasdst = bool(self._start_delta) + + def _delta(self, x, isend=0): + from dateutil import relativedelta + kwargs = {} + if x.month is not None: + kwargs["month"] = x.month + if x.weekday is not None: + kwargs["weekday"] = relativedelta.weekday(x.weekday, x.week) + if x.week > 0: + kwargs["day"] = 1 + else: + kwargs["day"] = 31 + elif x.day: + kwargs["day"] = x.day + elif x.yday is not None: + kwargs["yearday"] = x.yday + elif x.jyday is not None: + kwargs["nlyearday"] = x.jyday + if not kwargs: + # Default is to start on first sunday of april, and end + # on last sunday of october. + if not isend: + kwargs["month"] = 4 + kwargs["day"] = 1 + kwargs["weekday"] = relativedelta.SU(+1) + else: + kwargs["month"] = 10 + kwargs["day"] = 31 + kwargs["weekday"] = relativedelta.SU(-1) + if x.time is not None: + kwargs["seconds"] = x.time + else: + # Default is 2AM. + kwargs["seconds"] = 7200 + if isend: + # Convert to standard time, to follow the documented way + # of working with the extra hour. See the documentation + # of the tzinfo class. + delta = self._dst_offset - self._std_offset + kwargs["seconds"] -= delta.seconds + delta.days * 86400 + return relativedelta.relativedelta(**kwargs) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._s)) + + +class _tzicalvtzcomp(object): + def __init__(self, tzoffsetfrom, tzoffsetto, isdst, + tzname=None, rrule=None): + self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom) + self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto) + self.tzoffsetdiff = self.tzoffsetto - self.tzoffsetfrom + self.isdst = isdst + self.tzname = tzname + self.rrule = rrule + + +class _tzicalvtz(_tzinfo): + def __init__(self, tzid, comps=[]): + super(_tzicalvtz, self).__init__() + + self._tzid = tzid + self._comps = comps + self._cachedate = [] + self._cachecomp = [] + self._cache_lock = _thread.allocate_lock() + + def _find_comp(self, dt): + if len(self._comps) == 1: + return self._comps[0] + + dt = dt.replace(tzinfo=None) + + try: + with self._cache_lock: + return self._cachecomp[self._cachedate.index( + (dt, self._fold(dt)))] + except ValueError: + pass + + lastcompdt = None + lastcomp = None + + for comp in self._comps: + compdt = self._find_compdt(comp, dt) + + if compdt and (not lastcompdt or lastcompdt < compdt): + lastcompdt = compdt + lastcomp = comp + + if not lastcomp: + # RFC says nothing about what to do when a given + # time is before the first onset date. We'll look for the + # first standard component, or the first component, if + # none is found. + for comp in self._comps: + if not comp.isdst: + lastcomp = comp + break + else: + lastcomp = comp[0] + + with self._cache_lock: + self._cachedate.insert(0, (dt, self._fold(dt))) + self._cachecomp.insert(0, lastcomp) + + if len(self._cachedate) > 10: + self._cachedate.pop() + self._cachecomp.pop() + + return lastcomp + + def _find_compdt(self, comp, dt): + if comp.tzoffsetdiff < ZERO and self._fold(dt): + dt -= comp.tzoffsetdiff + + compdt = comp.rrule.before(dt, inc=True) + + return compdt + + def utcoffset(self, dt): + if dt is None: + return None + + return self._find_comp(dt).tzoffsetto + + def dst(self, dt): + comp = self._find_comp(dt) + if comp.isdst: + return comp.tzoffsetdiff + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._find_comp(dt).tzname + + def __repr__(self): + return "" % repr(self._tzid) + + __reduce__ = object.__reduce__ + + +class tzical(object): + """ + This object is designed to parse an iCalendar-style ``VTIMEZONE`` structure + as set out in `RFC 5545`_ Section 4.6.5 into one or more `tzinfo` objects. + + :param `fileobj`: + A file or stream in iCalendar format, which should be UTF-8 encoded + with CRLF endings. + + .. _`RFC 5545`: https://tools.ietf.org/html/rfc5545 + """ + def __init__(self, fileobj): + global rrule + from dateutil import rrule + + if isinstance(fileobj, string_types): + self._s = fileobj + # ical should be encoded in UTF-8 with CRLF + fileobj = open(fileobj, 'r') + else: + self._s = getattr(fileobj, 'name', repr(fileobj)) + fileobj = _ContextWrapper(fileobj) + + self._vtz = {} + + with fileobj as fobj: + self._parse_rfc(fobj.read()) + + def keys(self): + """ + Retrieves the available time zones as a list. + """ + return list(self._vtz.keys()) + + def get(self, tzid=None): + """ + Retrieve a :py:class:`datetime.tzinfo` object by its ``tzid``. + + :param tzid: + If there is exactly one time zone available, omitting ``tzid`` + or passing :py:const:`None` value returns it. Otherwise a valid + key (which can be retrieved from :func:`keys`) is required. + + :raises ValueError: + Raised if ``tzid`` is not specified but there are either more + or fewer than 1 zone defined. + + :returns: + Returns either a :py:class:`datetime.tzinfo` object representing + the relevant time zone or :py:const:`None` if the ``tzid`` was + not found. + """ + if tzid is None: + if len(self._vtz) == 0: + raise ValueError("no timezones defined") + elif len(self._vtz) > 1: + raise ValueError("more than one timezone available") + tzid = next(iter(self._vtz)) + + return self._vtz.get(tzid) + + def _parse_offset(self, s): + s = s.strip() + if not s: + raise ValueError("empty offset") + if s[0] in ('+', '-'): + signal = (-1, +1)[s[0] == '+'] + s = s[1:] + else: + signal = +1 + if len(s) == 4: + return (int(s[:2]) * 3600 + int(s[2:]) * 60) * signal + elif len(s) == 6: + return (int(s[:2]) * 3600 + int(s[2:4]) * 60 + int(s[4:])) * signal + else: + raise ValueError("invalid offset: " + s) + + def _parse_rfc(self, s): + lines = s.splitlines() + if not lines: + raise ValueError("empty string") + + # Unfold + i = 0 + while i < len(lines): + line = lines[i].rstrip() + if not line: + del lines[i] + elif i > 0 and line[0] == " ": + lines[i-1] += line[1:] + del lines[i] + else: + i += 1 + + tzid = None + comps = [] + invtz = False + comptype = None + for line in lines: + if not line: + continue + name, value = line.split(':', 1) + parms = name.split(';') + if not parms: + raise ValueError("empty property name") + name = parms[0].upper() + parms = parms[1:] + if invtz: + if name == "BEGIN": + if value in ("STANDARD", "DAYLIGHT"): + # Process component + pass + else: + raise ValueError("unknown component: "+value) + comptype = value + founddtstart = False + tzoffsetfrom = None + tzoffsetto = None + rrulelines = [] + tzname = None + elif name == "END": + if value == "VTIMEZONE": + if comptype: + raise ValueError("component not closed: "+comptype) + if not tzid: + raise ValueError("mandatory TZID not found") + if not comps: + raise ValueError( + "at least one component is needed") + # Process vtimezone + self._vtz[tzid] = _tzicalvtz(tzid, comps) + invtz = False + elif value == comptype: + if not founddtstart: + raise ValueError("mandatory DTSTART not found") + if tzoffsetfrom is None: + raise ValueError( + "mandatory TZOFFSETFROM not found") + if tzoffsetto is None: + raise ValueError( + "mandatory TZOFFSETFROM not found") + # Process component + rr = None + if rrulelines: + rr = rrule.rrulestr("\n".join(rrulelines), + compatible=True, + ignoretz=True, + cache=True) + comp = _tzicalvtzcomp(tzoffsetfrom, tzoffsetto, + (comptype == "DAYLIGHT"), + tzname, rr) + comps.append(comp) + comptype = None + else: + raise ValueError("invalid component end: "+value) + elif comptype: + if name == "DTSTART": + # DTSTART in VTIMEZONE takes a subset of valid RRULE + # values under RFC 5545. + for parm in parms: + if parm != 'VALUE=DATE-TIME': + msg = ('Unsupported DTSTART param in ' + + 'VTIMEZONE: ' + parm) + raise ValueError(msg) + rrulelines.append(line) + founddtstart = True + elif name in ("RRULE", "RDATE", "EXRULE", "EXDATE"): + rrulelines.append(line) + elif name == "TZOFFSETFROM": + if parms: + raise ValueError( + "unsupported %s parm: %s " % (name, parms[0])) + tzoffsetfrom = self._parse_offset(value) + elif name == "TZOFFSETTO": + if parms: + raise ValueError( + "unsupported TZOFFSETTO parm: "+parms[0]) + tzoffsetto = self._parse_offset(value) + elif name == "TZNAME": + if parms: + raise ValueError( + "unsupported TZNAME parm: "+parms[0]) + tzname = value + elif name == "COMMENT": + pass + else: + raise ValueError("unsupported property: "+name) + else: + if name == "TZID": + if parms: + raise ValueError( + "unsupported TZID parm: "+parms[0]) + tzid = value + elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"): + pass + else: + raise ValueError("unsupported property: "+name) + elif name == "BEGIN" and value == "VTIMEZONE": + tzid = None + comps = [] + invtz = True + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._s)) + + +if sys.platform != "win32": + TZFILES = ["/etc/localtime", "localtime"] + TZPATHS = ["/usr/share/zoneinfo", + "/usr/lib/zoneinfo", + "/usr/share/lib/zoneinfo", + "/etc/zoneinfo"] +else: + TZFILES = [] + TZPATHS = [] + + +def __get_gettz(): + tzlocal_classes = (tzlocal,) + if tzwinlocal is not None: + tzlocal_classes += (tzwinlocal,) + + class GettzFunc(object): + """ + Retrieve a time zone object from a string representation + + This function is intended to retrieve the :py:class:`tzinfo` subclass + that best represents the time zone that would be used if a POSIX + `TZ variable`_ were set to the same value. + + If no argument or an empty string is passed to ``gettz``, local time + is returned: + + .. code-block:: python3 + + >>> gettz() + tzfile('/etc/localtime') + + This function is also the preferred way to map IANA tz database keys + to :class:`tzfile` objects: + + .. code-block:: python3 + + >>> gettz('Pacific/Kiritimati') + tzfile('/usr/share/zoneinfo/Pacific/Kiritimati') + + On Windows, the standard is extended to include the Windows-specific + zone names provided by the operating system: + + .. code-block:: python3 + + >>> gettz('Egypt Standard Time') + tzwin('Egypt Standard Time') + + Passing a GNU ``TZ`` style string time zone specification returns a + :class:`tzstr` object: + + .. code-block:: python3 + + >>> gettz('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') + tzstr('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') + + :param name: + A time zone name (IANA, or, on Windows, Windows keys), location of + a ``tzfile(5)`` zoneinfo file or ``TZ`` variable style time zone + specifier. An empty string, no argument or ``None`` is interpreted + as local time. + + :return: + Returns an instance of one of ``dateutil``'s :py:class:`tzinfo` + subclasses. + + .. versionchanged:: 2.7.0 + + After version 2.7.0, any two calls to ``gettz`` using the same + input strings will return the same object: + + .. code-block:: python3 + + >>> tz.gettz('America/Chicago') is tz.gettz('America/Chicago') + True + + In addition to improving performance, this ensures that + `"same zone" semantics`_ are used for datetimes in the same zone. + + + .. _`TZ variable`: + https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + + .. _`"same zone" semantics`: + https://blog.ganssle.io/articles/2018/02/aware-datetime-arithmetic.html + """ + def __init__(self): + + self.__instances = {} + self._cache_lock = _thread.allocate_lock() + + def __call__(self, name=None): + with self._cache_lock: + rv = self.__instances.get(name, None) + + if rv is None: + rv = self.nocache(name=name) + if not (name is None or isinstance(rv, tzlocal_classes)): + # tzlocal is slightly more complicated than the other + # time zone providers because it depends on environment + # at construction time, so don't cache that. + self.__instances[name] = rv + + return rv + + def cache_clear(self): + with self._cache_lock: + self.__instances = {} + + @staticmethod + def nocache(name=None): + """A non-cached version of gettz""" + tz = None + if not name: + try: + name = os.environ["TZ"] + except KeyError: + pass + if name is None or name == ":": + for filepath in TZFILES: + if not os.path.isabs(filepath): + filename = filepath + for path in TZPATHS: + filepath = os.path.join(path, filename) + if os.path.isfile(filepath): + break + else: + continue + if os.path.isfile(filepath): + try: + tz = tzfile(filepath) + break + except (IOError, OSError, ValueError): + pass + else: + tz = tzlocal() + else: + if name.startswith(":"): + name = name[1:] + if os.path.isabs(name): + if os.path.isfile(name): + tz = tzfile(name) + else: + tz = None + else: + for path in TZPATHS: + filepath = os.path.join(path, name) + if not os.path.isfile(filepath): + filepath = filepath.replace(' ', '_') + if not os.path.isfile(filepath): + continue + try: + tz = tzfile(filepath) + break + except (IOError, OSError, ValueError): + pass + else: + tz = None + if tzwin is not None: + try: + tz = tzwin(name) + except WindowsError: + tz = None + + if not tz: + from dateutil.zoneinfo import get_zonefile_instance + tz = get_zonefile_instance().get(name) + + if not tz: + for c in name: + # name is not a tzstr unless it has at least + # one offset. For short values of "name", an + # explicit for loop seems to be the fastest way + # To determine if a string contains a digit + if c in "0123456789": + try: + tz = tzstr(name) + except ValueError: + pass + break + else: + if name in ("GMT", "UTC"): + tz = tzutc() + elif name in time.tzname: + tz = tzlocal() + return tz + + return GettzFunc() + + +gettz = __get_gettz() +del __get_gettz + + +def datetime_exists(dt, tz=None): + """ + Given a datetime and a time zone, determine whether or not a given datetime + would fall in a gap. + + :param dt: + A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` + is provided.) + + :param tz: + A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If + ``None`` or not provided, the datetime's own time zone will be used. + + :return: + Returns a boolean value whether or not the "wall time" exists in + ``tz``. + + .. versionadded:: 2.7.0 + """ + if tz is None: + if dt.tzinfo is None: + raise ValueError('Datetime is naive and no time zone provided.') + tz = dt.tzinfo + + dt = dt.replace(tzinfo=None) + + # This is essentially a test of whether or not the datetime can survive + # a round trip to UTC. + dt_rt = dt.replace(tzinfo=tz).astimezone(tzutc()).astimezone(tz) + dt_rt = dt_rt.replace(tzinfo=None) + + return dt == dt_rt + + +def datetime_ambiguous(dt, tz=None): + """ + Given a datetime and a time zone, determine whether or not a given datetime + is ambiguous (i.e if there are two times differentiated only by their DST + status). + + :param dt: + A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` + is provided.) + + :param tz: + A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If + ``None`` or not provided, the datetime's own time zone will be used. + + :return: + Returns a boolean value whether or not the "wall time" is ambiguous in + ``tz``. + + .. versionadded:: 2.6.0 + """ + if tz is None: + if dt.tzinfo is None: + raise ValueError('Datetime is naive and no time zone provided.') + + tz = dt.tzinfo + + # If a time zone defines its own "is_ambiguous" function, we'll use that. + is_ambiguous_fn = getattr(tz, 'is_ambiguous', None) + if is_ambiguous_fn is not None: + try: + return tz.is_ambiguous(dt) + except Exception: + pass + + # If it doesn't come out and tell us it's ambiguous, we'll just check if + # the fold attribute has any effect on this particular date and time. + dt = dt.replace(tzinfo=tz) + wall_0 = enfold(dt, fold=0) + wall_1 = enfold(dt, fold=1) + + same_offset = wall_0.utcoffset() == wall_1.utcoffset() + same_dst = wall_0.dst() == wall_1.dst() + + return not (same_offset and same_dst) + + +def resolve_imaginary(dt): + """ + Given a datetime that may be imaginary, return an existing datetime. + + This function assumes that an imaginary datetime represents what the + wall time would be in a zone had the offset transition not occurred, so + it will always fall forward by the transition's change in offset. + + .. doctest:: + + >>> from dateutil import tz + >>> from datetime import datetime + >>> NYC = tz.gettz('America/New_York') + >>> print(tz.resolve_imaginary(datetime(2017, 3, 12, 2, 30, tzinfo=NYC))) + 2017-03-12 03:30:00-04:00 + + >>> KIR = tz.gettz('Pacific/Kiritimati') + >>> print(tz.resolve_imaginary(datetime(1995, 1, 1, 12, 30, tzinfo=KIR))) + 1995-01-02 12:30:00+14:00 + + As a note, :func:`datetime.astimezone` is guaranteed to produce a valid, + existing datetime, so a round-trip to and from UTC is sufficient to get + an extant datetime, however, this generally "falls back" to an earlier time + rather than falling forward to the STD side (though no guarantees are made + about this behavior). + + :param dt: + A :class:`datetime.datetime` which may or may not exist. + + :return: + Returns an existing :class:`datetime.datetime`. If ``dt`` was not + imaginary, the datetime returned is guaranteed to be the same object + passed to the function. + + .. versionadded:: 2.7.0 + """ + if dt.tzinfo is not None and not datetime_exists(dt): + + curr_offset = (dt + datetime.timedelta(hours=24)).utcoffset() + old_offset = (dt - datetime.timedelta(hours=24)).utcoffset() + + dt += curr_offset - old_offset + + return dt + + +def _datetime_to_timestamp(dt): + """ + Convert a :class:`datetime.datetime` object to an epoch timestamp in + seconds since January 1, 1970, ignoring the time zone. + """ + return (dt.replace(tzinfo=None) - EPOCH).total_seconds() + + +class _ContextWrapper(object): + """ + Class for wrapping contexts so that they are passed through in a + with statement. + """ + def __init__(self, context): + self.context = context + + def __enter__(self): + return self.context + + def __exit__(*args, **kwargs): + pass + +# vim:ts=4:sw=4:et diff --git a/modules/dateutil/tz/win.py b/modules/dateutil/tz/win.py new file mode 100644 index 0000000..def4353 --- /dev/null +++ b/modules/dateutil/tz/win.py @@ -0,0 +1,331 @@ +# This code was originally contributed by Jeffrey Harris. +import datetime +import struct + +from six.moves import winreg +from six import text_type + +try: + import ctypes + from ctypes import wintypes +except ValueError: + # ValueError is raised on non-Windows systems for some horrible reason. + raise ImportError("Running tzwin on non-Windows system") + +from ._common import tzrangebase + +__all__ = ["tzwin", "tzwinlocal", "tzres"] + +ONEWEEK = datetime.timedelta(7) + +TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" +TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" +TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" + + +def _settzkeyname(): + handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + try: + winreg.OpenKey(handle, TZKEYNAMENT).Close() + TZKEYNAME = TZKEYNAMENT + except WindowsError: + TZKEYNAME = TZKEYNAME9X + handle.Close() + return TZKEYNAME + + +TZKEYNAME = _settzkeyname() + + +class tzres(object): + """ + Class for accessing `tzres.dll`, which contains timezone name related + resources. + + .. versionadded:: 2.5.0 + """ + p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char + + def __init__(self, tzres_loc='tzres.dll'): + # Load the user32 DLL so we can load strings from tzres + user32 = ctypes.WinDLL('user32') + + # Specify the LoadStringW function + user32.LoadStringW.argtypes = (wintypes.HINSTANCE, + wintypes.UINT, + wintypes.LPWSTR, + ctypes.c_int) + + self.LoadStringW = user32.LoadStringW + self._tzres = ctypes.WinDLL(tzres_loc) + self.tzres_loc = tzres_loc + + def load_name(self, offset): + """ + Load a timezone name from a DLL offset (integer). + + >>> from dateutil.tzwin import tzres + >>> tzr = tzres() + >>> print(tzr.load_name(112)) + 'Eastern Standard Time' + + :param offset: + A positive integer value referring to a string from the tzres dll. + + ..note: + Offsets found in the registry are generally of the form + `@tzres.dll,-114`. The offset in this case if 114, not -114. + + """ + resource = self.p_wchar() + lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR) + nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0) + return resource[:nchar] + + def name_from_string(self, tzname_str): + """ + Parse strings as returned from the Windows registry into the time zone + name as defined in the registry. + + >>> from dateutil.tzwin import tzres + >>> tzr = tzres() + >>> print(tzr.name_from_string('@tzres.dll,-251')) + 'Dateline Daylight Time' + >>> print(tzr.name_from_string('Eastern Standard Time')) + 'Eastern Standard Time' + + :param tzname_str: + A timezone name string as returned from a Windows registry key. + + :return: + Returns the localized timezone string from tzres.dll if the string + is of the form `@tzres.dll,-offset`, else returns the input string. + """ + if not tzname_str.startswith('@'): + return tzname_str + + name_splt = tzname_str.split(',-') + try: + offset = int(name_splt[1]) + except: + raise ValueError("Malformed timezone string.") + + return self.load_name(offset) + + +class tzwinbase(tzrangebase): + """tzinfo class based on win32's timezones available in the registry.""" + def __init__(self): + raise NotImplementedError('tzwinbase is an abstract base class') + + def __eq__(self, other): + # Compare on all relevant dimensions, including name. + if not isinstance(other, tzwinbase): + return NotImplemented + + return (self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset and + self._stddayofweek == other._stddayofweek and + self._dstdayofweek == other._dstdayofweek and + self._stdweeknumber == other._stdweeknumber and + self._dstweeknumber == other._dstweeknumber and + self._stdhour == other._stdhour and + self._dsthour == other._dsthour and + self._stdminute == other._stdminute and + self._dstminute == other._dstminute and + self._std_abbr == other._std_abbr and + self._dst_abbr == other._dst_abbr) + + @staticmethod + def list(): + """Return a list of all time zones known to the system.""" + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, TZKEYNAME) as tzkey: + result = [winreg.EnumKey(tzkey, i) + for i in range(winreg.QueryInfoKey(tzkey)[0])] + return result + + def display(self): + return self._display + + def transitions(self, year): + """ + For a given year, get the DST on and off transition times, expressed + always on the standard time side. For zones with no transitions, this + function returns ``None``. + + :param year: + The year whose transitions you would like to query. + + :return: + Returns a :class:`tuple` of :class:`datetime.datetime` objects, + ``(dston, dstoff)`` for zones with an annual DST transition, or + ``None`` for fixed offset zones. + """ + + if not self.hasdst: + return None + + dston = picknthweekday(year, self._dstmonth, self._dstdayofweek, + self._dsthour, self._dstminute, + self._dstweeknumber) + + dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek, + self._stdhour, self._stdminute, + self._stdweeknumber) + + # Ambiguous dates default to the STD side + dstoff -= self._dst_base_offset + + return dston, dstoff + + def _get_hasdst(self): + return self._dstmonth != 0 + + @property + def _dst_base_offset(self): + return self._dst_base_offset_ + + +class tzwin(tzwinbase): + + def __init__(self, name): + self._name = name + + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + tzkeyname = text_type("{kn}\\{name}").format(kn=TZKEYNAME, name=name) + with winreg.OpenKey(handle, tzkeyname) as tzkey: + keydict = valuestodict(tzkey) + + self._std_abbr = keydict["Std"] + self._dst_abbr = keydict["Dlt"] + + self._display = keydict["Display"] + + # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm + tup = struct.unpack("=3l16h", keydict["TZI"]) + stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 + dstoffset = stdoffset-tup[2] # + DaylightBias * -1 + self._std_offset = datetime.timedelta(minutes=stdoffset) + self._dst_offset = datetime.timedelta(minutes=dstoffset) + + # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx + (self._stdmonth, + self._stddayofweek, # Sunday = 0 + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[4:9] + + (self._dstmonth, + self._dstdayofweek, # Sunday = 0 + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[12:17] + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = self._get_hasdst() + + def __repr__(self): + return "tzwin(%s)" % repr(self._name) + + def __reduce__(self): + return (self.__class__, (self._name,)) + + +class tzwinlocal(tzwinbase): + def __init__(self): + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: + keydict = valuestodict(tzlocalkey) + + self._std_abbr = keydict["StandardName"] + self._dst_abbr = keydict["DaylightName"] + + try: + tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME, + sn=self._std_abbr) + with winreg.OpenKey(handle, tzkeyname) as tzkey: + _keydict = valuestodict(tzkey) + self._display = _keydict["Display"] + except OSError: + self._display = None + + stdoffset = -keydict["Bias"]-keydict["StandardBias"] + dstoffset = stdoffset-keydict["DaylightBias"] + + self._std_offset = datetime.timedelta(minutes=stdoffset) + self._dst_offset = datetime.timedelta(minutes=dstoffset) + + # For reasons unclear, in this particular key, the day of week has been + # moved to the END of the SYSTEMTIME structure. + tup = struct.unpack("=8h", keydict["StandardStart"]) + + (self._stdmonth, + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[1:5] + + self._stddayofweek = tup[7] + + tup = struct.unpack("=8h", keydict["DaylightStart"]) + + (self._dstmonth, + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[1:5] + + self._dstdayofweek = tup[7] + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = self._get_hasdst() + + def __repr__(self): + return "tzwinlocal()" + + def __str__(self): + # str will return the standard name, not the daylight name. + return "tzwinlocal(%s)" % repr(self._std_abbr) + + def __reduce__(self): + return (self.__class__, ()) + + +def picknthweekday(year, month, dayofweek, hour, minute, whichweek): + """ dayofweek == 0 means Sunday, whichweek 5 means last instance """ + first = datetime.datetime(year, month, 1, hour, minute) + + # This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6), + # Because 7 % 7 = 0 + weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1) + wd = weekdayone + ((whichweek - 1) * ONEWEEK) + if (wd.month != month): + wd -= ONEWEEK + + return wd + + +def valuestodict(key): + """Convert a registry key's values to a dictionary.""" + dout = {} + size = winreg.QueryInfoKey(key)[1] + tz_res = None + + for i in range(size): + key_name, value, dtype = winreg.EnumValue(key, i) + if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN: + # If it's a DWORD (32-bit integer), it's stored as unsigned - convert + # that to a proper signed integer + if value & (1 << 31): + value = value - (1 << 32) + elif dtype == winreg.REG_SZ: + # If it's a reference to the tzres DLL, load the actual string + if value.startswith('@tzres'): + tz_res = tz_res or tzres() + value = tz_res.name_from_string(value) + + value = value.rstrip('\x00') # Remove trailing nulls + + dout[key_name] = value + + return dout diff --git a/modules/dateutil/tzwin.py b/modules/dateutil/tzwin.py new file mode 100644 index 0000000..cebc673 --- /dev/null +++ b/modules/dateutil/tzwin.py @@ -0,0 +1,2 @@ +# tzwin has moved to dateutil.tz.win +from .tz.win import * diff --git a/modules/dateutil/utils.py b/modules/dateutil/utils.py new file mode 100644 index 0000000..ebcce6a --- /dev/null +++ b/modules/dateutil/utils.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +""" +This module offers general convenience and utility functions for dealing with +datetimes. + +.. versionadded:: 2.7.0 +""" +from __future__ import unicode_literals + +from datetime import datetime, time + + +def today(tzinfo=None): + """ + Returns a :py:class:`datetime` representing the current day at midnight + + :param tzinfo: + The time zone to attach (also used to determine the current day). + + :return: + A :py:class:`datetime.datetime` object representing the current day + at midnight. + """ + + dt = datetime.now(tzinfo) + return datetime.combine(dt.date(), time(0, tzinfo=tzinfo)) + + +def default_tzinfo(dt, tzinfo): + """ + Sets the the ``tzinfo`` parameter on naive datetimes only + + This is useful for example when you are provided a datetime that may have + either an implicit or explicit time zone, such as when parsing a time zone + string. + + .. doctest:: + + >>> from dateutil.tz import tzoffset + >>> from dateutil.parser import parse + >>> from dateutil.utils import default_tzinfo + >>> dflt_tz = tzoffset("EST", -18000) + >>> print(default_tzinfo(parse('2014-01-01 12:30 UTC'), dflt_tz)) + 2014-01-01 12:30:00+00:00 + >>> print(default_tzinfo(parse('2014-01-01 12:30'), dflt_tz)) + 2014-01-01 12:30:00-05:00 + + :param dt: + The datetime on which to replace the time zone + + :param tzinfo: + The :py:class:`datetime.tzinfo` subclass instance to assign to + ``dt`` if (and only if) it is naive. + + :return: + Returns an aware :py:class:`datetime.datetime`. + """ + if dt.tzinfo is not None: + return dt + else: + return dt.replace(tzinfo=tzinfo) + + +def within_delta(dt1, dt2, delta): + """ + Useful for comparing two datetimes that may a negilible difference + to be considered equal. + """ + delta = abs(delta) + difference = dt1 - dt2 + return -delta <= difference <= delta diff --git a/modules/dateutil/zoneinfo/__init__.py b/modules/dateutil/zoneinfo/__init__.py new file mode 100644 index 0000000..34f11ad --- /dev/null +++ b/modules/dateutil/zoneinfo/__init__.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +import warnings +import json + +from tarfile import TarFile +from pkgutil import get_data +from io import BytesIO + +from dateutil.tz import tzfile as _tzfile + +__all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata"] + +ZONEFILENAME = "dateutil-zoneinfo.tar.gz" +METADATA_FN = 'METADATA' + + +class tzfile(_tzfile): + def __reduce__(self): + return (gettz, (self._filename,)) + + +def getzoneinfofile_stream(): + try: + return BytesIO(get_data(__name__, ZONEFILENAME)) + except IOError as e: # TODO switch to FileNotFoundError? + warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror)) + return None + + +class ZoneInfoFile(object): + def __init__(self, zonefile_stream=None): + if zonefile_stream is not None: + with TarFile.open(fileobj=zonefile_stream) as tf: + self.zones = {zf.name: tzfile(tf.extractfile(zf), filename=zf.name) + for zf in tf.getmembers() + if zf.isfile() and zf.name != METADATA_FN} + # deal with links: They'll point to their parent object. Less + # waste of memory + links = {zl.name: self.zones[zl.linkname] + for zl in tf.getmembers() if + zl.islnk() or zl.issym()} + self.zones.update(links) + try: + metadata_json = tf.extractfile(tf.getmember(METADATA_FN)) + metadata_str = metadata_json.read().decode('UTF-8') + self.metadata = json.loads(metadata_str) + except KeyError: + # no metadata in tar file + self.metadata = None + else: + self.zones = {} + self.metadata = None + + def get(self, name, default=None): + """ + Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method + for retrieving zones from the zone dictionary. + + :param name: + The name of the zone to retrieve. (Generally IANA zone names) + + :param default: + The value to return in the event of a missing key. + + .. versionadded:: 2.6.0 + + """ + return self.zones.get(name, default) + + +# The current API has gettz as a module function, although in fact it taps into +# a stateful class. So as a workaround for now, without changing the API, we +# will create a new "global" class instance the first time a user requests a +# timezone. Ugly, but adheres to the api. +# +# TODO: Remove after deprecation period. +_CLASS_ZONE_INSTANCE = [] + + +def get_zonefile_instance(new_instance=False): + """ + This is a convenience function which provides a :class:`ZoneInfoFile` + instance using the data provided by the ``dateutil`` package. By default, it + caches a single instance of the ZoneInfoFile object and returns that. + + :param new_instance: + If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and + used as the cached instance for the next call. Otherwise, new instances + are created only as necessary. + + :return: + Returns a :class:`ZoneInfoFile` object. + + .. versionadded:: 2.6 + """ + if new_instance: + zif = None + else: + zif = getattr(get_zonefile_instance, '_cached_instance', None) + + if zif is None: + zif = ZoneInfoFile(getzoneinfofile_stream()) + + get_zonefile_instance._cached_instance = zif + + return zif + + +def gettz(name): + """ + This retrieves a time zone from the local zoneinfo tarball that is packaged + with dateutil. + + :param name: + An IANA-style time zone name, as found in the zoneinfo file. + + :return: + Returns a :class:`dateutil.tz.tzfile` time zone object. + + .. warning:: + It is generally inadvisable to use this function, and it is only + provided for API compatibility with earlier versions. This is *not* + equivalent to ``dateutil.tz.gettz()``, which selects an appropriate + time zone based on the inputs, favoring system zoneinfo. This is ONLY + for accessing the dateutil-specific zoneinfo (which may be out of + date compared to the system zoneinfo). + + .. deprecated:: 2.6 + If you need to use a specific zoneinfofile over the system zoneinfo, + instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call + :func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead. + + Use :func:`get_zonefile_instance` to retrieve an instance of the + dateutil-provided zoneinfo. + """ + warnings.warn("zoneinfo.gettz() will be removed in future versions, " + "to use the dateutil-provided zoneinfo files, instantiate a " + "ZoneInfoFile object and use ZoneInfoFile.zones.get() " + "instead. See the documentation for details.", + DeprecationWarning) + + if len(_CLASS_ZONE_INSTANCE) == 0: + _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) + return _CLASS_ZONE_INSTANCE[0].zones.get(name) + + +def gettz_db_metadata(): + """ Get the zonefile metadata + + See `zonefile_metadata`_ + + :returns: + A dictionary with the database metadata + + .. deprecated:: 2.6 + See deprecation warning in :func:`zoneinfo.gettz`. To get metadata, + query the attribute ``zoneinfo.ZoneInfoFile.metadata``. + """ + warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future " + "versions, to use the dateutil-provided zoneinfo files, " + "ZoneInfoFile object and query the 'metadata' attribute " + "instead. See the documentation for details.", + DeprecationWarning) + + if len(_CLASS_ZONE_INSTANCE) == 0: + _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) + return _CLASS_ZONE_INSTANCE[0].metadata diff --git a/modules/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/modules/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..e86b54fe2884553b2b5e3c7a453b4046a919a800 GIT binary patch literal 139130 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 !=q-K0`6U+(z!A6auReVQ!)BP?23Wd8Oj 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 S{abkaKeL=_E 0: raise ValueError('invalid length') + + outbuf = create_string_buffer(length) + _stream_salsa20(outbuf, length, nonce, key) + return outbuf.raw + +def Salsa20_xor(message, nonce, key): + _ensure_bytes(nonce) + _ensure_bytes(key) + _ensure_bytes(message) + if not len(key) == 32: raise ValueError('invalid key length') + if not len(nonce) == 8: raise ValueError('invalid nonce length') + if not len(message) > 0: raise ValueError('invalid message length') + + outbuf = create_string_buffer(len(message)) + _stream_salsa20_xor(outbuf, message, len(message), nonce, key) + return outbuf.raw + + +def XSalsa20_keystream(length, nonce, key): + _ensure_bytes(nonce) + _ensure_bytes(key) + if not len(key) == 32: raise ValueError('invalid key length') + if not len(nonce) == 24: raise ValueError('invalid nonce length') + if not length > 0: raise ValueError('invalid length') + + outbuf = create_string_buffer(length) + _stream_xsalsa20(outbuf, length, nonce, key) + return outbuf.raw + +def XSalsa20_xor(message, nonce, key): + _ensure_bytes(nonce) + _ensure_bytes(key) + _ensure_bytes(message) + if not len(key) == 32: raise ValueError('invalid key length') + if not len(nonce) == 24: raise ValueError('invalid nonce length') + if not len(message) > 0: raise ValueError('invalid message length') + + outbuf = create_string_buffer(len(message)) + _stream_xsalsa20_xor(outbuf, message, len(message), nonce, key) + return outbuf.raw + + +def self_test(): + import binascii, hashlib + + key = binascii.unhexlify('1b27556473e985d462cd51197a9a46c76009549eac6474f206c4ee0844f68389') + nonce = binascii.unhexlify('69696ee955b62b73cd62bda875fc73d68219e0036b7a0b37') + + output = XSalsa20_keystream(4194304, nonce, key) + assert hashlib.sha256(output).hexdigest() == '662b9d0e3463029156069b12f918691a98f7dfb2ca0393c96bbfc6b1fbd630a2' + + message = binascii.unhexlify('0000000000000000000000000000000000000000000000000000000000000000be075fc53c81f2d5cf141316ebeb0c7b5228c52a4c62cbd44b66849b64244ffce5ecbaaf33bd751a1ac728d45e6c61296cdc3c01233561f41db66cce314adb310e3be8250c46f06dceea3a7fa1348057e2f6556ad6b1318a024a838f21af1fde048977eb48f59ffd4924ca1c60902e52f0a089bc76897040e082f937763848645e0705') + + output = XSalsa20_xor(message, nonce, key) + assert output[32:] == binascii.unhexlify('8e993b9f48681273c29650ba32fc76ce48332ea7164d96a4476fb8c531a1186ac0dfc17c98dce87b4da7f011ec48c97271d2c20f9b928fe2270d6fb863d51738b48eeee314a7cc8ab932164548e526ae90224368517acfeabd6bb3732bc0e9da99832b61ca01b6de56244a9e88d5f9b37973f622a43d14a6599b1f654cb45a74e355a5') + + key = binascii.unhexlify('dc908dda0b9344a953629b733820778880f3ceb421bb61b91cbd4c3e66256ce4') + nonce = binascii.unhexlify('8219e0036b7a0b37') + + output = Salsa20_keystream(4194304, nonce, key) + assert hashlib.sha256(output).hexdigest() == '662b9d0e3463029156069b12f918691a98f7dfb2ca0393c96bbfc6b1fbd630a2' + + + +self_test() diff --git a/modules/scrypt/__init__.py b/modules/scrypt/__init__.py new file mode 100644 index 0000000..f2cb7cf --- /dev/null +++ b/modules/scrypt/__init__.py @@ -0,0 +1 @@ +from .scrypt import * diff --git a/modules/scrypt/scrypt.py b/modules/scrypt/scrypt.py new file mode 100644 index 0000000..4357aab --- /dev/null +++ b/modules/scrypt/scrypt.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import imp +import os +import sys + +from ctypes import (cdll, + POINTER, pointer, + c_char_p, + c_size_t, c_double, c_int, c_uint64, c_uint32, + create_string_buffer) + +__version__ = '0.8.6' + +_scrypt = cdll.LoadLibrary(imp.find_module('_scrypt')[1]) + +_scryptenc_buf = _scrypt.exp_scryptenc_buf +_scryptenc_buf.argtypes = [c_char_p, # const uint_t *inbuf + c_size_t, # size_t inbuflen + c_char_p, # uint8_t *outbuf + c_char_p, # const uint8_t *passwd + c_size_t, # size_t passwdlen + c_size_t, # size_t maxmem + c_double, # double maxmemfrac + c_double, # double maxtime + c_int, # int verbose + ] +_scryptenc_buf.restype = c_int + +_scryptdec_buf = _scrypt.exp_scryptdec_buf +_scryptdec_buf.argtypes = [c_char_p, # const uint8_t *inbuf + c_size_t, # size_t inbuflen + c_char_p, # uint8_t *outbuf + POINTER(c_size_t), # size_t *outlen + c_char_p, # const uint8_t *passwd + c_size_t, # size_t passwdlen + c_size_t, # size_t maxmem + c_double, # double maxmemfrac + c_double, # double maxtime + c_int, # int verbose + c_int, # int force + ] +_scryptdec_buf.restype = c_int + +_crypto_scrypt = _scrypt.exp_crypto_scrypt +_crypto_scrypt.argtypes = [c_char_p, # const uint8_t *passwd + c_size_t, # size_t passwdlen + c_char_p, # const uint8_t *salt + c_size_t, # size_t saltlen + c_uint64, # uint64_t N + c_uint32, # uint32_t r + c_uint32, # uint32_t p + c_char_p, # uint8_t *buf + c_size_t, # size_t buflen + ] +_crypto_scrypt.restype = c_int + +ERROR_MESSAGES = ['success', + 'getrlimit or sysctl(hw.usermem) failed', + 'clock_getres or clock_gettime failed', + 'error computing derived key', + 'could not read salt from /dev/urandom', + 'error in OpenSSL', + 'malloc failed', + 'data is not a valid scrypt-encrypted block', + 'unrecognized scrypt format', + 'decrypting file would take too much memory', + 'decrypting file would take too long', + 'password is incorrect', + 'error writing output file', + 'error reading input file'] + +MAXMEM_DEFAULT = 0 +MAXMEMFRAC_DEFAULT = 0.5 +MAXTIME_DEFAULT = 300.0 +MAXTIME_DEFAULT_ENC = 5.0 + +IS_PY2 = sys.version_info < (3, 0, 0, 'final', 0) + + +class error(Exception): + def __init__(self, scrypt_code): + if isinstance(scrypt_code, int): + self._scrypt_code = scrypt_code + super(error, self).__init__(ERROR_MESSAGES[scrypt_code]) + else: + self._scrypt_code = -1 + super(error, self).__init__(scrypt_code) + + +def _ensure_bytes(data): + if IS_PY2 and isinstance(data, unicode): + raise TypeError('can not encrypt/decrypt unicode objects') + + if not IS_PY2 and isinstance(data, str): + return bytes(data, 'utf-8') + + return data + + +def encrypt(input, password, + maxtime=MAXTIME_DEFAULT_ENC, + maxmem=MAXMEM_DEFAULT, + maxmemfrac=MAXMEMFRAC_DEFAULT): + """ + Encrypt a string using a password. The resulting data will have len = + len(input) + 128. + + Notes for Python 2: + - `input` and `password` must be str instances + - The result will be a str instance + + Notes for Python 3: + - `input` and `password` can be both str and bytes. If they are str + instances, they will be encoded with utf-8 + - The result will be a bytes instance + + Exceptions raised: + - TypeError on invalid input + - scrypt.error if encryption failed + + For more information on the `maxtime`, `maxmem`, and `maxmemfrac` + parameters, see the scrypt documentation. + """ + + input = _ensure_bytes(input) + password = _ensure_bytes(password) + + outbuf = create_string_buffer(len(input) + 128) + # verbose is set to zero + result = _scryptenc_buf(input, len(input), + outbuf, + password, len(password), + maxmem, maxmemfrac, maxtime, 0) + if result: + raise error(result) + + return outbuf.raw + + +def decrypt(input, password, + maxtime=MAXTIME_DEFAULT, + maxmem=MAXMEM_DEFAULT, + maxmemfrac=MAXMEMFRAC_DEFAULT, + encoding='utf-8'): + """ + Decrypt a string using a password. + + Notes for Python 2: + - `input` and `password` must be str instances + - The result will be a str instance + - The encoding parameter is ignored + + Notes for Python 3: + - `input` and `password` can be both str and bytes. If they are str + instances, they wil be encoded with utf-8. `input` *should* + really be a bytes instance, since that's what `encrypt` returns. + - The result will be a str instance encoded with `encoding`. + If encoding=None, the result will be a bytes instance. + + Exceptions raised: + - TypeError on invalid input + - scrypt.error if decryption failed + + For more information on the `maxtime`, `maxmem`, and `maxmemfrac` + parameters, see the scrypt documentation. + """ + + outbuf = create_string_buffer(len(input)) + outbuflen = pointer(c_size_t(0)) + + input = _ensure_bytes(input) + password = _ensure_bytes(password) + # verbose and force are set to zero + result = _scryptdec_buf(input, len(input), + outbuf, outbuflen, + password, len(password), + maxmem, maxmemfrac, maxtime, 0, 0) + + if result: + raise error(result) + + out_bytes = outbuf.raw[:outbuflen.contents.value] + + if IS_PY2 or encoding is None: + return out_bytes + + return str(out_bytes, encoding) + + +def hash(password, salt, N=1 << 14, r=8, p=1, buflen=64): + """ + Compute scrypt(password, salt, N, r, p, buflen). + + The parameters r, p, and buflen must satisfy r * p < 2^30 and + buflen <= (2^32 - 1) * 32. The parameter N must be a power of 2 + greater than 1. N, r and p must all be positive. + + Notes for Python 2: + - `password` and `salt` must be str instances + - The result will be a str instance + + Notes for Python 3: + - `password` and `salt` can be both str and bytes. If they are str + instances, they wil be encoded with utf-8. + - The result will be a bytes instance + + Exceptions raised: + - TypeError on invalid input + - scrypt.error if scrypt failed + """ + + outbuf = create_string_buffer(buflen) + + password = _ensure_bytes(password) + salt = _ensure_bytes(salt) + + if r * p >= (1 << 30) or N <= 1 or (N & (N - 1)) != 0 or p < 1 or r < 1: + raise error('hash parameters are wrong (r*p should be < 2**30, and N should be a power of two > 1)') + + result = _crypto_scrypt(password, len(password), + salt, len(salt), + N, r, p, + outbuf, buflen, 0) + + if result: + raise error('could not compute hash') + + return outbuf.raw + + +__all__ = ['error', 'encrypt', 'decrypt', 'hash'] diff --git a/modules/six.py b/modules/six.py new file mode 100644 index 0000000..6bf4fd3 --- /dev/null +++ b/modules/six.py @@ -0,0 +1,891 @@ +# Copyright (c) 2010-2017 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.11.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 + + +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 __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 + +_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("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_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 + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +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" +_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) + + +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, 2): + exec_("""def raise_from(value, from_value): + try: + if from_value is None: + raise value + raise value from from_value + finally: + value = None +""") +elif sys.version_info[:2] > (3, 2): + 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): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +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): + return meta(name, 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) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def python_2_unicode_compatible(klass): + """ + A 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)