From a6e6856282f353af62b56c82517073c66c8ba2d7 Mon Sep 17 00:00:00 2001 From: taras Date: Sun, 18 Aug 2024 02:12:00 +0200 Subject: [PATCH] Simplify interfaces, update benchmark --- README.rst | 4 ++-- docs/source/_static/picows_benchmark.png | Bin 31254 -> 33970 bytes examples/echo_client_benchmark.py | 11 +++++----- ...ient_cython.pyx => echo_client_cython.pyx} | 15 ++++++------- examples/echo_server.py | 4 ++-- picows/picows.pyx | 20 +++++++++--------- setup.py | 2 +- tests/test_echo.py | 8 +++---- 8 files changed, 32 insertions(+), 32 deletions(-) rename examples/{picows_client_cython.pyx => echo_client_cython.pyx} (83%) diff --git a/README.rst b/README.rst index 42fc062..c450723 100644 --- a/README.rst +++ b/README.rst @@ -74,7 +74,7 @@ Connects to an echo server, sends a message and disconnect upon reply. async def main(endpoint): - (_, client) = await ws_connect(endpoint, ClientListener, "client") + (_, client) = await ws_connect(endpoint, ClientListener) await client.transport.wait_until_closed() @@ -108,7 +108,7 @@ Echo server async def main(): url = "ws://127.0.0.1:9001" - server = await ws_create_server(url, ServerClientListener, "server") + server = await ws_create_server(url, ServerClientListener) print(f"Server started on {url}") await server.serve_forever() diff --git a/docs/source/_static/picows_benchmark.png b/docs/source/_static/picows_benchmark.png index 04f44bda5e317d6dd12e001f53cb920359c7c719..2412711c5b0660a6fdc196ab708dbeba90d951e5 100644 GIT binary patch literal 33970 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%0D;#@E6mCl3l-8aEx9Kr0$XiiVz*Rsrni zRaUB_+{tTbI87SL)I>wOqiznFi13r&1c519LfDb92?;D9%E!^8;R|DO#J9rI z(v?8rWMmV%)p8rnokqI82=!KyVM^#a$paa=dbjZw@WiTz=Bz_V@P_B%1YT?UO+mYi zAa?AV=$_4BQ#1lE`VoC8tz-BVA6|qT=Nxd#)PT-*th2W|UIErOJ`ALQFX}5q@S_f! z=ggY*7VKIpjQKI}$hkv8BlG);hML8n5UsaL0H2q6i_}o>y8Lqu9*?68uOA zR)rRarrmkeUWXqR$<@3uFhM9`mn>ht9LNf=X122dnWpqOGX6-ncV!jfa7J`S+<*ws;)jv!2N=t)Z<3k4q3r<_4#;Y*W{{{rX z%W37Aj7@(kG8F^$W8>p}SMVLfYhqvP!IhzmEkveyAt@F@0`^Vn5gWnWdLBd>GS(Y9 z6L7<3Oe7ORuan*?^i}%8ZT}+I$lgSeE7@Tp0b7zgmgPL|*0aj*t%!c|8oIh6sPuRm zS~b93ELevzw%4|OY)J>^8>Q$Vdp$vliBNWbgPmpt}H?9c$|s7Iz14$W~NM#%L; zeH+=#jyOPm;|O9dH|k_xRBD9?@7$bZMi?^@(N_Kf!^DZVsuqx2gHVC)B7UU4yXdPU zL%!+L@WF_NU|~K+s%$1V3@z>QxWSvTDDsbBi&%-@mH_9VP*ottz>s)lgI`L~ir2Y= zVCRA0N(Q?X4S3vjgD16raPS_&z(MqZ#GS&JMa?iBT=oVa&#&mpMi&%X+Ym5)a3NaK zJ&*rebLalo^WFdP52i6<4mn3s)4|~~hiP3b)=802Q6kFc>PkYzl2j(!ItZmShhmY# zCzC8xN(>#hwaO`_HYzd|8L519-Jh=8_xt@9zCX-z|phXzMi| z*bD%64TJ>0XZNNIXTZRHEn@zLG2Ltrdi?~VZxJ`+I0Sdqqt_vwDqVHe?mk2{-d79{ z(!}@SPMN#7GjHNQ5i#}dF|-!psxnDT-SHxlY=MWoAr-6yXMmU3g9nWe;!LUtQHz%+ z;YgPG$nSzgYQ_9{4WZCRTd^2sp!4Ogew4Su+&cxaTJeh|k`q-eQ!6HO;dL|~$GuGYH!AL_@H$_Ls42KS$+*}C3hG>XC zMuPI}7_kXCPaKRT8>rTC>cyLO^ff~#yaw5l-@_Zd13Q~8-Q=6WZ$3MLpEy|D_N6as zRCyEbN@dCo-!Js_6DVP0v0ZYkgughMvaWbTom#2{7X9cjsY<>!jY8S^%C^7*_XF*v zul~_xx?I8Va7IdQu;o)Y#x{wRoF&juC0vHuJ;y&NrZ(vrJNde)GG+x9n(8m-W_NNg8`U9;i`~!5`FouL+8;v{&XLxv& zW06|MlFf>+8?0j*X)^XzVL7 z(j^6M`g}!1QC+T)5v(1n*MLjnXh#MfG(emJ8*6eeuVsNlTV8Qd2NAC4cCo)M{u^DE z6$vZ8Ye?stH*s6s9R@Rq&JVt;xn1Qsi5IRYBwbk4{kG@LgD%6t1Dr3cjc0bd|6Vg( zTw~Ou#ho8UTnO!Y_bKkOj?PpXN(z#j@2hfR_0o&!;nrj+$C6hpagv)E=*IMpy5c;N zw*dp3bGD=tm4_YPS(Ex?%<#64diB~%(WZJA)t*0cgEhT#eEm@U0nAq78BP{)sv=k9 zt9Qq6(j@>$E$4if>x0Z?;D8vEl~Z`OTle~`TW9l6=liUx_#VOh!`WO2#a>Q?mT4*e zDHFvH-ZwTOVLTP~;)y-B!-1bOy%x!sF_@7msaT#;`IX?H0a-7vucNso5H33LxV}E( z(4j-ym`3R)H)l;4sI5IwLF}`-kQ4jb(W2us?V%PgYC88Ek>X1j#yM@Rt+CM1tpmHL zDpKusMwvDmmxMIYoFdaW-4Fz(RF8Av;+h`zuXbrAKGV{_WdT?EXk?@B;<&e%Mi19Z z5Nk?U_o~ofyckf>JMOXVC)vr(?irHxLL(b&JmzBvDeZsaV3cb^EPZ+Ty zA-8qm4g8V1>EJjUbuEWWA|{*MB`Uf#5J!|X{kUjyWZ6u0s#xwsx1${=Eqx`e(-3qw zqs#Mto;cR;WWWX8cQ1Zt%~5@Em=rj&1cpil^i7|UiNe~??>wd)g?KqRylk%g$qESe z#EsCr(t6xdkAs)o{G?WC@)#KH95e9r8=DO~=5y>GlY||+o=b6cZ3<7;n7{NqP2~W? z5I0z(PT5~It$nWTV4ik&q-&PF>-@H<4AEJeGbg=ZZVZ~vk=xoNBy=}>)&Jiy1&?SD zG;92wEqEp4fFF_7>EH3Xb7r{oK$uh=rHEA=&B?pmS0P3EL0bQ`)!aG_WgEz2+FKv! zy`n=OKi+ss0J}~c^Qa$YZhkr#>}2oZ!+kLa5r?t^&42uHbnQ5la)Lj8{?)T^j30-R z!40q9`E(8b!=z|~7M4BiplL8FVfDjR(!AqeV~{|CbXMmC4pN+hWh9Oyq|9%lqcvrl zo7kNXkA5-7{n!7R%sVQ@?G~y!4($=#R*ZD)4lwt7UT!;C_Pl8*5Yas(IARubN)=7} zY#i!6Z3Oc#pwSfvPBM^$Y%zKI3<@megO+-vD%mIRQUF(`1=ynfCBFU+6yRQ!RV#qb z?^*tytorN2A({ErvtcWPnx@yksYFUHTkUvw3WWeD^r$_7l&_0f67`C4Q$t;y*~yC2 z5AD#;!gJtUMn;!H#>2mE(Zx3I@kV;{+qskVPcOf8_UY)^6xhl!;=L_I>CKnUoin2( zi1RpK_@J{~1XyujMaErBGb+!k&&)J^7gSrD8@e*EUW?^0eXk}4bK#pfJ!~Hf4_c~WU3<-0 zdW*vJUZ(FjvC@DpGVIprQy$XGi|*e11GG$YAS?fgMvm#RsM{D&SOQ${#SxCKKQ-h# zek6kEb$C{glUG_s@Za>weltNZX*;X5dvkSwDfJH2+lLrB zI1~$2lfp-MxDmBzz=X#a{J7ef(J|eZm_c9&LW$~F5oNm2AdM1h7y>>`LVsuHMaMfH z&AuJ$fA}t{NU4SASzztwG=!F{zv6Ebs7qmAQ@cV#0XC=d5)cfcU*F=<=4C_ohR40p z-`rBm^x$9%xTZL!i@`VAFyNVnuQ%vUZZ60?bq3>E*`Tymjcw*e<|sJnYNI#cYDL4lGN2S8WgI?^Q=1;lDl9Tr$-l3MK%Uo$tadlcs zQ&?=snW(DK0hj#SUTnSeT{_40QHX0?LvyCwV*;LrkQd?NVPBg%;z`Q1wyL!HSu-LV z0uS#I;p9USv$!kD;29*Phgo{nl?;zRnp@AW>*1j_u@sv3i0J%3Oh;x;-D@W_88)EI zN3<_XG;vY(x5(N%2>!zVTEimF3fww1`p3sB|gE#=^*ae%@y7o%k`p)`2UU1Iqf zZ%g_4V}p_nb4lonSf{|foPYaY2rP{n28m2lzi%IgHuJ=7NN)Tjyt)Io)EMDG_M2RLo)ZG8u?Sk%() zJ`b{|i{9F$D|soE!$x>x)u@z!b-re=3qZiGysmdut4!*nFK`m!S7y#J<>%(YNpebC zDURHaDEAyrw7+?C6f*@m1vm^WZ%B&OMX+VXC8P&-aZlvlJ>mgk5AK*=(Nfc5(ZVXD z51?L$DOaFy<4aDsnAoX3?rP$oeI%(rDO{Ee{1!-ueL&hYB^tTAg@P-LEGQRSdCZ!t@0_=6{pnTqcJYT3zugd32pZj+hJ79 z9#W7mEJS!PBHbb18A^nJI{S0Gb@w_9UZo z{8G#Vs9TtFpSf52XL7CmAMcKk5^cp`iCuMuA`XF?s;93X-*{LINm7zLyP4sWEje2B zp{jzR7gzyHySoDh%$&Cfrp<~9lCUR~QGtwZsj#opt;g3siT!O*u{+LWa_Zs29n0RD zswdM1ILr=R&spd64OPxlU#~qCAPwEM0yQTca56XFDXk*3qB_XYio<6Y#kxyJakdn&IV!3 zF_%MLw)TR60io(w32WI}_I-v=l@pq1W2Wx=+1LDO(_K_~;oRm4E*aLe;HUdO`H8Qg z96i{7@(-8|_#7k7Mp_<=0n5|Mt1z-h`Z9Co%tDBddV<~vAOz%?dd6yAj}8HX!Az8l zjc)dP;@^_{FLKk;=us=HANzx;Nap}fxgVaZagXEOK89{DWxL>0HAq7R1rj2O*M^^W z_HN}!srEx=o3C7@qW+>z*o~qhHQze3EnuE`9f)S}RZC`3F?e03T9(Z6Z`dUO1L3Nk zf6i}eT#S3Yee?7oSyQj>LG{}@n^m+|@X@cIeFynCHT{8W!8=*z5=Uh!TNrKbdGue( zH56E3cv(A@fGjOk#5HxIe2F4Opg6Z-lre$G87Ei@?heN?{OM2F+gV2rjHQSH?96~# z;z+`)7o0t&^N7EEKc)=3c(sy2=h@lq2l*XhdbAWsbS$*(;?Cr5!KmeRrN?@IG@9<~ zGb~{Uq|0542;$1t3CP8Rvt79|>6b*)rLfCK%5Q>?P>7oUCaj#SOK%9LT1cM4d|Wo= zFuAhXxbfU5lo*@Og#2A*1xj8oL0;oy8xni8UucUjC)f~fyFl0TY;0l~j;#cZ(X{Lc zbqxu4QU;CV8}Qmfh9f!vqVhas0q8ZIX%^rEZ;|E+s4wU{U2$=*T^5m}$J-BX$=$Qr zV8jsDw7c@uP!TMdKH&|j?BjF7G-M9ImJT7>!&0zt~6ZK|WpV8HYSW(8>O=1`>&UijH$Ni9unx~7!A|_Xn+!N)eNA7Naej}d z^h1KH;S*Ke?u$MtPKLYg-rmLt%Uc@`ZP@>&FoM6Xkc4*g+O;`?R@cx3u}HViaO)=EsjXBWdE2}kGh>AB(udvvpELnf9Svu;jsCAuv^UF!95!fD(p7EsF= zu_NPPSKjO9F&U>P?dtKo{rJ5&>m@y-!d$c_X5PBEqT`Kb$!Xv$M0;TE>W%nWdGL@wl-7EUKg~hIx3T zF1nqV<2tE;@PEwS;07hQ2blYbW6xST7n?Voii^*4`xWm)jJ+5NPKB`P;PuNz_mtHS zBJpL+2il3VNii7wkuSi(urXuOygmsaMh&B``IR+cP1c4tMOwcn5wB@x3d?U7Hb@JB zuQQ!xqk(N3MNJl%ZkAUDyEr7zpX9aS|rr>Yym-?S$kVKREZs_Gbv&F~9y z3+xJU^B1Wa_#6(jr08w$-kL~h*1qx!OtJ7c&281bkdfgK7~Gh*NUuM`O47W+kf^fC z%Hcrh(eM3QtNH3v&z`Ee-@Z9|x{F)hyCJ78O>)cKswIAt%wwSuxy`q;yEAR%+W6q_ zqQ|=aJfw~KnX++9y-~dju4y;WibFpd8k(CV!a_38^|DFcP4n+YlB9>ws-9`wdg&l# z`M+@pAHK9~U20SSmZ`cd$mp7al!9}Dt^M`$pw_(?sy4SrmP$Os(a8ZKHmB* zs{@Rwup=2p+;Yu0zWD)*eoyRM|5HZ!mFxK%OZC_Jq8qdJ@bCa6Uzj?&6QXhk0|?ah z72vX;2e#I^rFr`LPFWyj#DGe>kG`3EFe%AS2)h+8YV$%Dw$$b=95iT9Bkj&~P0fQo zrbqZ}HJf9~OB=MKqN2R0cH?bQHeptxd1j+}cF5cBi|hRs+;wi{)TEV1Y%%V5{^yqm z4<1x)`YYvCd`bPSii($3Z5z9MUMDuC{aD#k(T?jy)`>^7c>B2y`?dxJRq!D{JHDTM z=TrFdVx*;q)lpYM1IF9oyP(w{|=_0m8 z6h$pn+ApU`QFEp#imrXZd^}UTL*p*~Z>OWYo}-rSaYq+Zdvof5siWOVTgQ`DW*eN% z?H#OaPl;}m*tT6@gQcURor9#Hpv_;uu+7%qLXd--%L#9?&~CrJ14S{KlK;{r$|hJ* zl$)=T+#YS$+dcJedfKg%vi+m4MFd{H=Dfw(yWrf~`*+sd47!qy2UJ?YVa7sxH@JSv|LSa@61WLwS+UIkyd^qg~H`K96Xt zYjcZ!^6s6qf16b73MM)x@)P-xFT9?Pll)Y%gnH2)tSF6(3Bb?mV&M)R@{Gu7)rmmz zjOx1o{VV_By0G)ATo*X2mcajuiG#y9b;7zj^-@KJhK=N80AFpX#fRGm%YK%}gm9}| z4@-_78yjPg+&sHkH`TB(STKLV>eZ{;9VLf*>vPg`jxkF*cL!7(dms5IM&5*ue!gYx z{P0U^2}M8Ng!1RCpEhYoRn znI3KQ{(eckVRuh$#@*_!=FZNbQ}xfA%fk7U-`$fB)`(SDYFOZPms*O|QL(SdYhN+v9&?mNIIXy#6ICM$Fmzk2o3L}*KSvbJ#kDfi}=^PZllUiS0nPuJ0U zm&!YP=%?^?zCJ$6FgP?6o;5Q)`Tcu{?5p99j{`H4eJ7q=l`9eINKm{M6T@Nf?4*88 zy{if5xm)74FKcU`r{@e-tXjEpcf!ZQh6eVtXU~S$M=@Nxb!+j76DQWmc(VTL{9N=_ zuwY7~b4TlUqeCC>AJ`Woa|X+EiS|9bzfZo%I{jV9;isxjjcB=)FUwW~ld>>g?#!-eT|F^3=HwPwvG8o5)KXyOV&%S(OWm(xGc6N4FSud$Y zixz1MOepYKi0&Hc{O zGwey{G*)?z`~f(_wAZq&~?}6tZr^@P8v*)Zp`x-y>R2k>QjyRHY>k9cRKOZ zva%(*CuWz+vb5a6MTUlkSkUJ0-+lF;9AEQUx8eI_>kZ?3rbQQUC;NZ>l2=uIcV<-H zD(J%iPSaDXkMqBL`I2}fM+^sb3H9}nDZRl{OS-jd*N#n2a*2r@%9x2)xe<0$&#@uX z<{Qi5%u_mMa&xhteQ^m7A3m%|H&?;woY(U$Q*m;n=k4c4Jc>OtIYL${BV&j4=OjOi zm@|idxo?xnO>pk1HD|l$)>6Fiyr18L%hkB;MyWI5uS!Zv65qXhw*;Gi|A7N%?nsv3 z-s$_RtD1|Gb8mS6ji2}i3TkQ}WF?IQ%H#vsH)6e5sC19fQ@mTYXhyea&rn{Bi-ub=157WB+tP&Jjxeu+_-_bq2+nzjCoDEdAi%sakrWAws}6?b=g{h zlQ{LoA3msIzwo@hzBP2|TCvwEf_a;$ANY%W+XA}%*ky4_p_|jKKl9b(pUJ@i*K7Iy zz2xP~y>sTwIfk>IeyUy)XM_nCAS^6gHW9k{z_n{esq&NG5lvbf?sF+#4!Csb(%8gA zS!SabzwA`MuVY7*JQgQDX$a4RK6mO;#@6H(7Cy+lVnr*STETR6{VwM%-$v>j%HK@< z>S|4h@w)c*h9Li2=kBj}pVkz3dp~~qG|V8w`pu^YTHM>Vsclw|7C>-gW@B@2oMv6O zPC!jft);nnV~p3tgOd%p`kY)`^L#F{3hxc%7Z3$8cQ8)3{T?p8bUkW;Qlk`wC|B`BERY6^#$ImDJZC zxg8Z{KGI#2?$mWiKhHg53ETGlnwpw&azC(ARS)$=b>8vBcyiA7-4pFKVU>;TC>_!m z#;a9~Pfs<O!%`H#8#sVkR_96}Nmy3#s#9=Ws^W2@rTY?2Q z;T#|&@>0ieK9mjJLedfv5(e1}3cT_hduny4*RwOD1s=V@RV$;uH#9V)TUK(` zbm#Q3sj-1MyDuzR!->e9Zr8l`fll)EtehMbJ3G6`N%x6itH0;8 zj5nTGlqa=Z-+Fj6^&>eg|JrI}&x6v)EGx!)MxT0J9Jr!SKL&?WPhbB*LoSkANoD213(M9gh2@R41mCOfYOTw5 zNcxrI)U{bmOzc{CxKp_^6;Nn$g1w<$o4MHHgxB|gO5GjnzWa;1g2xw!>KMndAKbd%JR zzpKN$Ey`uCPj}4|#f;uZrbTiIA9+g)a!WIxR%-wp^koYwYdK!-Yb-G4&G8&hkBE#6 z%*e>#D^yog6Prla%COc63<~1Qn8BI{VCSwja^0g79TBmGDhnT8y?*q^j~^bdR;f<} zuia@MhohhTA!gf==lkE^+`+wT*S9TS%F2{~-Q?>Ly*qYvprvHAyA5}Jp3lR>nQA)u zCu>AVkUm+Lb?}*O<2H^n!&fCFC4-WalW)s<8wCbmzRW_I+1d&M6JGoJRgc^Q5^s=q zegKGyTMr>Iu;}VmdyE9Pn>P2eyKmZh_}LQVAuVY{hggBer&cQ{li~oT(UfGoI1z_v zunzbHA-=fgiA8uiNmfToFZT}*Yp3ioc|p}>ou&^A3>>v->qv+>H2roI@#O0h3&v=V z-ksVh20FarqN4j;T_rxm$cEr`^_dL}_4g5{@8A!1PEI-*(=QhbhUMfTsr5mr>EK*@^TK2*O#Uyazsfw zohvl)iHuw$ARy4+;L+DS*!uSM>(?43*IPZmVj(0Q+wTnD-6T6byaI=%0#Wj_S5H;E zO5}0t=ZG+YMSNvtb#?0qh{&Iv@i=B-K~F7Oy!f%_xczYF=f%h-@u@jyCY_PR?&%~S zN*Y8Fv0E-o|8SXyr;5kY>G8H03GbOHdYgotoLxwaVQrT$U$#4ade4*N@0*&N?}n{j zpPUnNH2F}0&hc|>H>iQx`q z!%CsYH+P&c!!~ZOieEf2(i4mgNP^_5!^I^f^VY0clZE7yJ-Ys&+kk?R`=;@+vAQ9J z2E4uyK<3n`Q`mBGa(mLMxx~eF-Mq#J%@MxWmaIBM8RvVB=l85MmK7CU!8sVxwN*(! z^FiFS*7Y-o43)lVuptb_&aBGNzuuF#tB}uT8eoT$pMNpnBKMa8Z2RQs8ws=WlPP1Y zj)=uLacgag@;bN4tkpQ^t-@E}Hgtc`6P3||g$ws!13kvA1@<9mQ!n$>`ul28Ns=FS z*L?8DmDaml2*vWi7?%CLhl-$x$qqMt#C9)L{cW}RFHn+0U?;N)u&3^3u%bMI`vhD-D zIeqtlb=p5BFy2>>Stc$n9zV;y-hYt&miwUF?&PXa;WIc;2E*9V-;Nr#NwYIsZ z8-M9O)H}Oy#pb5G(T1X?Lf`N~eAIWO$+7My<^44lG2vOg^=$^&U}c|-MMa-5zg>-T z^X@)>v)5NPMhG8Oi0s7Yu3{5AnK&_ca9FbI(}U2Ytw1*0ZE9B*Opn|;D2=kVZ0U@P z@zlnuIiX%6N+>uKO-(meef;<~ydwP7n>P!stgIATS8p?Z^z7NTfz3_#T1OAh_GXs_hqX;@dv0JHmbld@mlXvV z?tiw$H}f+~Z;XyLbnVc-J~VFX;OzVg8CBS&*YGy#1uS>zuD)E~ix-!mROUOw*W;w! zkUWjreqWArkA|LJ!1Uzk_b25t`?R%J8Xh?^fDCv2c>}V(5O&8BR-qS`Wy&Z{TV5}3mBHI3&3sOuyG@u?93P!7RI}^QtGp9NMCsG z^^%SnEoBZQtl~1$s@m?S=_Ip|svMAy{~PemiS}RavDM=UbC)h$3HL|TH^@Ad@-9`Y zN~eeO&6_u_AOZ%EV!i{Mi5Q$h)s8nsHB4_=6{i%~R$FhJ$nNM`evMnDzxG60bGWWi zAz_D7&wG#d)=QjjIp3LA;MDacQUA$ipcw5Zfk(3J=n9Y)0%KMrs71NQdA~i+zBRW> zJtPi9#SMx1=|*|(iAH&nsPO&!`|Zft&vG5$#9DwSI*k$*@!14LZL75{u3!x5wc_qR zhTPEx4?9Q4Vh~7=O^fEM#>gD`<-4_&Y5n?3_@=S@{FjlESYYc`Vec7FLaa1;O{NTP z9DXNS{-Uyy$(Mog6=1!|sZ*E1fl2j$vZwlEW~Ug)#{Q@-dkOE(2;ew2HB_#0+r6@} zkptmOFVB4k3jD<&Jy7GyYUCg^M(jAjf{N$@PqgbCZ2g!199{v0*m}bgliN3e)W<4z zk`-6ve_`obGoUZMEW3?(c>m&I?}3ulkL_EPrH0;#x`G<3NYdfg)6;7L%|)ms1UNsL zGZRu8v&P^IJW+MFHx|f#xV@9!+uM87rcEZbPpw>kMp{%n&`MxP%9MKU(svoigiAnR z$-WCq3R~aa5C%!~S!ZzYril3>lA*{>U9nl!r=UPKJw1K-+O@&hIRNqFi)v?qo?op(valdyIcCkr>j&1O^4en zT|44$T)A;W_ddrZ4^(|%-9#Mw8rEywS3QejU+m;pA5>pQNeSeQl#j?o+hAZ{{8Cc3 z5*`wJy2z64)jdHm7xlEsN9Gn5s4~uj@%^l6QY13w&TI%LtE=|-L%F9HLTKnZ{J==)*9jO6&Coh z_pH}vdt+ncuiw6X4^@{b#D4=pGnnEs39Er>FGzNFY6UJWw)2Jq#}DztsMTgo`pQtzUgM+QBH_^$s6aF)_K56e2&;ThDJ(`;-Yq-h+d&7gTRJ3yf_~o$e8(rfw=GtwMq88YIav=5Q`rzXOU(d$eO^EAQsB$Z> z^vjn+%h;v1V5_VKqj}oVF${q84ahs?# z<(duT!k79KqyhWtn0X17EVlqr9+U--e5=nN|G9QxWP~NpNDx6OyuIC0AJvnrB!{M! zmU+dUJuRqBSy)&I+W{0A3QWT%jF=rdJJlxJ($kZYn+jgL6^BCmy_wdv?(UNh3^I}s zZ0arb^~?J>Q8mrkUHi=Du-V}fi#{f%ogO0(0f+)aLqlZ5ywn($uj2@{kuH2vKpxIKpC_)yMl)3F7CVSg3q2kGeeTYx({Mwy!BhWTKS;|lFDH53iW{S@Nm{8^E+yu zB&^$MU%chiw`VqUc3-=8ZDwF(dAOfi+*iTud+pC0RP z_OBC7+Jf``61d#%$7`koy)KFSk<+*oF22Zh8|q!-GTeS}`>8rs6e6LO?^I`5@MRV7 z0wJraO{x(>chhr*^cDb}BO>-=mmG4|Z5J+VmX?##Hk}!YnKc1rqlZGU@@d3ogwY%s8If@AW+hnD!^30Qjt3if)T4{BDmq1B0Z(5MNs8}caxy<| zT_EU)vR-AsHNs|ckzS&4_rBKFn$6Bk>pit(LU=V1Vy(zOb?ir+*Ugp3W4U835yLBJ`klmPa;b81n=dQL zk|(YP9;N;I|GHnHX+*0MMs}^`%UL|k7&V^gVeWF4T>TzE3vw<0PhQ<$%iwV=bOtd8 zYMPw9Jg+(Dxi_IaMMXvQ;Fl~L^Q9qI+%phF1&d95?#UBT@L-isEYt~wu__d73g8e< z6#jGG-=B-0UuBg;Z{1GhTY8*BXgvzYj%|Vhl~HR*1k!s9Bl|Z`qi`!il=cG^kXVNE zK`_D5hFrI=nYJ8tb#)cl4i<)a?mB^BW?lkuF|AnPd;a`f!yM@jzmM>ejOgTHi1baO~;xs49;Kj1CvI0k*uLMc_0=fr9)zs9G!vgxk4+X$o zl;?Vn&E^4Zo=-WPIfnu*OE5}W!3^7;J$rCiUt$*z0Gu@=ds9AOAHj01=xcb1 za*qcd*xTD{Gm?+vOo`NWW+>y@ym>xK#I;E17EcAFrPotUot;U1E>2D*ggRn{M)TQw zL6BL{7j<=Y|KQ-@gVWo#ZA*%1>5-6-c!BgD$g2c+)8IWjJ!!LHNkpk7xNqZBFfUmZ!pl3e$1G?qq_gxVPr?P> zTT_!+L_}oy^5qmb0u=s7+df2%0STCbhc|Q+^op0};@YQ=!sNo#l;^SI#~Va{wv+@b z^cX*PR9dB#8wK`z<;s<;(+o|g-1FEGC6gFv=llzMWLyClED6W<<<~+(!xd3?4FF|u zVcQ_z`hjA2cCzN)!_b(Rkf>cQ_W(yIA0HnI$uZHas;bIvtlyM-^Oh|OuonpUVFo$O z4*n?FY#E0Pp$)hwCG6 zbO&PJ^S*um?i~o$`9w)X0qO_+3&#*D@4KLHdbDpXQWY4W?aafji{Omi`1-?#)GM>g zKY3aB{cii(POnx53VP{^)? zhx-v6%z7&->JpK;#|K+ooBdW(6lf}+gajT)9RVQWH*j;$%g@gT%_@%4Zz)9{oI7`} zA3Vv6Z{OAeBJQ=c+yWSZ+(1VK1O^t8H@|w7&ckC;*m+#!!5HBSo!;V;J`SsC8d=0$eT>%wk z<-L3M(Cyv3H%x?)k#TH%+!O$v@_`18lg-`C;Pc|eZamVth`4>NpMLj^ZAVax`p3n^ zX-?k81C)KAeF?DHe4}m!COq>SKZ8A?&Rj?|>+xnFWad&Y-oIZuK0f~X-8=0P^JB-h==Rpv)!7ZS$j3g%y^agw z^^zx)GL#^NS-PxSMrpdo+4$aD_G$oa^IfF`Rzv;>5ND4skiKzJ1ZrthF@{h$nh`|t&&`=X|1&6$Z|x_>jU z$7&_0Q4|LU2PD>2_>?guBsvPD$R6xj+ypvm0kEBLN=h08|D@Rk+qd1^G+kI&Sk?$Sst{V{07xrdbA}jWnW5XtD}FZ8BDxT? zMg`D5ffnRw!BJ&5HFla%R0iU-5@z^o@)eFYC|a9wtS^A$Vh30$foX%o(z5*Ayu}qL z{86_R!AJ-cnK-B3=*ZSWXi>WkW1Hp15-*JwNaXl*qGgNl;j0thz(IzUAvn~8{u zGO@ES^DU$Rbx~^^GdF)=Mz~6LY4;aMf6xjbsK|MFd1YtP)^h)en~Z&QK8Rq%Rz#Gn zGn3D(Yrqs6{B#E!5f~yfu>jGCfg(Vkq&nE8DzZ)=1!J-v(C!sjeVLiDqr^9dTt+zR>w?CYA!E}~cJKtc zy1Nrbm#BQGRhqiGf^jvmNUv0Ho$vCME6;;Eq=&16NT@)SS8v{2gb$#0Bj3$m!s>JJ z;zdnwa|vV^C_!aINK<%2P89Ncy&|(v*lxG=ed_cxwl}q_PVhuabK@o495UaK@Zn^SHM(XdCj)SA)Q;}n@O@!=5$cE=kT7Hvf+I#xf zY9LwDqeq7{I(MN60ByGj4mv0T>#E zOlGDy3XZG5GeACfaNoaXJ0zq}^)~N&5p~NW_{J(-srzwb41v6AY1RAEsvDmO(jo`r zt)EIns2Bj7*$jcVe{>W=yPvgNd9?Ifofuq!J(n$V>0@{%hy$!lVrF>Q5dtj#0JeLX zjvFM%A8l>G`dUsIvRqvB$a)9Owi)GGe(FWs(f&mLiv5o00P2)w<8R z^<#%43hgPyU%?9iH#HK})@NmBo8m()8*+D28{kFU<R{DGFRE^i>Q;K_{^OXqZ_;LPYi-uVLe5w;uPv$(Vr);$)GZg3Drh}p2QZMXEn$sl6GqsNc;=;^Ui zFHD3?K!Oul0Jal2ZUY`4Eqw`v1U`Z}6uiWLFR7Y7ghhc^fFL%XI)DEBi|T6PMkMDg z)iC=UUWf}i=W^wDMWyEV?&Itu!(3M;{LX$A6<>gD0CvIxAS%&QuP&|iud6d8MG^ox zu}Tu3%+3iq6F>tYadE{0uW3?@A*`6axyD_#Zki&P4#=GE$rM}^D?k)zUB49>2}*h? ztBj`vm|Nnz1!HRp7uu?)JOm~Nl&KB5;XMKGQNwM<(FSkV1Y2ZggZpBTmLH#YcxMDf zAH6;_EIT2gP6)5I4)5hR)oZfNN){3GGb&FaM8kbbjHcvjJ}2q?71KXXDHnu;SAop5M-=jy;j>=2xdlg8{>O zAEi3_kVc@Ngy1B^3&89EfonJJ+~3wAegu|~<_8JU=b%O16t`VQ?N?K~fY^>;p6WD2 zyY>+2_n|Yy5e%z<9;$P%*=ZM`G2#Ly+Lj3_u|Iz^Z_yfUTWHGg;{MY&r!yh76ha2} z?pfrzITW^(FI>fUIdA?>{12_7hXwT>QAEB%5W50`515AX@%5$u`5`L#&krD%QZ6&O z1XNZr98Wr0jyV3z{>#=^{e3M#+=uIX^n$}WJw4&{4{Q-VT>U_c0d=B6E2^|}X=#5$ zFEM#-DVYr~aU1N0ejW%|0Ocnb_7#@O%1Yw4_{W-IOV~B-u6ypZ82PQ}(pvH5s6xr| z5SD=SM|?g?#G&o5&A7=!b;R{Ru2!FYKUkFy=x zIAJ;Mx=#!23szkj9)(2+!H=E-{J0By5K&Z#_&IMei{;;T`Yf!i#9Tm8jb}#JgUYlU z>DCv3>P=j#e@AGxby;*YPyr%_xNl&^+aM&g9GLs_-xkmtyv$>mZ;@rp5EhUl>8NAJ zj=gATF#7w>gQ=4J)#dfUsK`lX2Maj|$iQ2v;FHSld~)Nbmb1)goddb^C~__UsqwW! zmY0KT|F1KqyK!hZCp#N1tphq=zRuY{L@dI3(D35gJORw0N9$kh@EB!3bUWGfM< z1wrV#gy=zVwMe*OZKyM5Px}8Z&ClM~oq%6e2>TsgG3C&SCr_TxQNV;rgJWZ^*v!OG zhnuxqLE%EW059)CRLNzZ?o#lj(V=FCUv)7lIzSfR*)&BhXJsXif(8Jqn7t?h4e^Qi zirQx0v0w(8_ktIRj*gzSW2Ak2eawBO0wj3Q5XnmQoaZ3Oe}{A6Jg5xK2KYiA8fG&? z%Ps|B@Cb-RYI^u%*{TsB5!e(l!^@g9NPL>(-_R`qOXT@K^n36j*9~F< zupfEL`(`F%it7&0aMg|cgS2zNRw31qW?Ud6GuA{;)`ai_85ufTLoQwvaET(K9+3CI zP`PX@=>E}*PA)F~cxScS5_C-;hVTACP8{hefBkwl!d6%Wif^{hP90U;t)2NGk%=eM zAQ}Pl+_^civlMOZbT%VU5_JH{b)Tl@wMsn~)QPEa#&U$gdw3*=Wnf?+Q7hrYnd)rM zaS^g7dV81sCn1&Yxb#`#w*e3xHzP<3TZqtg4|e^N;KxI_+mL#)AdA2ueG%SH7AV&e4nLP{R*j%Q8n#w@FP%?6pOO6{NM=&{ zVB-6>#Vu72`5;T`uvPNX?e*~uP6-RoI*b=~};`ja|_7QQLHbci? zG%Q8Qcm=#4>4kw4-tD1L9vg)U>jJD6caiG}>qZ<`a3^pI3o|)8J1a|rf!+g4AfYp! zqKkuAVo*w%9zTA+nC#7&nHfaRJ^S}BxVhcx96Trde=^xHX5R&qO`2TXM|&?p*_Y`b zA+UwXY7YVk1u#Ew{ShEFL^Jxn+@Z_Y-_Itu{`di~iF=TKtOgPO+V3W{uPa+==@st{1N;~a%;`3DtFqgs=5q_Qk`jae zhF1Dl7R9<%%|hfx`_ioJ_1rC$#_;ig5&bK~Cye(;L+K-wlVS*a350-t9ONco!2PPK z#elE>$gbnzGqB?{0R)vGwQ8gpNrMzr~4!_YDD&GEYwgnq^>{xZ+=JrCEXaDxa!e<)2XV(MF`TP3% zYW{3$BJs2(kSDt0ANf>wA}=rRG@%p+;R*bdW^DZTEJ~Ca0MY!(-d)74(^ei+3Ol-g zV&WE0*xg^hd2^7Rc3s2$Rx-If+qbWzDzM>Nt6k;iQY4eLW~(La0|xt2|GR?ewu~Nz zp>AK-X4+P7XGcYYYs)xwj`ru?Jt!AxZ>XL9Z%fnwkLB;*lUoqh25?XUQ64P{2~Hnr zuROnK|AYrn2q+*r3Orv0vJDX!38{f|^pEAizQD@eg^Eb|poe7{!motKhya+;3+l4d z{{&;P<$a=})>9i0L*RRmhZPhFBLHC0`for7Ma%>-+#+B-M!ylNCW?Z12iYR+AGDf2?J33X!P5>k;5ix=e`r46_`sIX2xM6tVrViHiJg_pOTfS>yq` zzDAC-P3$1xS5Z|}x}lT2qT+Y#JhF_y4iz~rw&}3l%Yz9DF2uL*ICn|^ zZOkNCE%rNN9TY!5Sa;eV%kHercHkivWRO2cfn#CrW`S5? z2UN~|9IFr^?IHH_{Vn2P1O8hI74#LNPk012Kgm&GyZ7(k{{l<4oQ*9A^#J!~P{6?N zKsoZ@z)`$`qe~Sa%no=90`^hGZGgml;>*KA6w>U+#D8C;0xhh9883u?2AzE2yt}aO zE?_H2qe9YG(g|hggy6Oi3Jcg0o@n<@-hc&IwB@T-`2k(8eGw(ditRgwZYyEOb`2o0 zSQ0vb#Ee{<=TR?U_i5_r2*ioG2ZI&*ABjLpnm3VN=qd65%mWTI2g?0d0K()O!CC?~ zlA_=k5Hfk;qv`D`fZoVotq^S)0EB2dCI?^g-!vk>XKe+a3if{EvRT*ZokVU6FJ zKwA&8(e{EWV>GA|mK7SAAGiykLneF@APV0;iy-#a@TjPz6uhBxsXqv?vb32wK1OMZ zc6LHA_|2pC(WcN;MZv}<Gq!^$MXWBQ8Fk*H{QF5cWIM^5y*%&uF)%J*H|?N6dCBW}jUj4bUIN zj`$+Xwr_R=Ij1Nwo8i($2#FGLZZHudO!>bCk5XUiUv*y0O5Li~| zsQ)Vn?VQWi-*i{d{i74m{lj>LI25W1ObX{vyW!r^X$AHrS}jGP_$JnY3yd6>VH#hN;U2L)CSevamGsCQ+s-4aGqvu??S$FNQn!hYJ$BJv zoFY4JClpNKx4cQr13D#r1z7owJUoiOXapyxjg^eK1!P&ex;5+$qXIJWT8D8@tqF5Y zw2_-2e?Wsh{YQR|E5Q0eJ`osTc!EUtH*gd&gR-v)-9-wmm`5Od(}L!93t*Q4+Q0XB zvIL2CDOxZ7N^&1hx|V~;rK4~Uk$<YWfg9 z1HfAdpN;-;E2}`nDKn^l20Lhy3#6qdaRruNq9I%mA+?#2=XbHe{NAEZKiBo~mjkpv zS4vx68wS~x`R*~mQ69??+B)2ujeXNCjpk7|$TCO4%R%`sNS8v@h{s?vEAG+6BF^W* zn{_Ap*sF$+17qO{M$4igkvf__bW6h&-zGuRcL@lTnr`$QS_%dt8Pz4h>ZC%%u!G8f zKrf|PfXyq{u9;~^U%Pe=)#Bgy!5WYGXdFOOU^7m=9D*BVf+RB59veam-oLX(=$l@F z<4@#EKn03Iox&=9Y8_0>v42M$AX7}x>PG$wwkOI*yq}q5ADs=jJ2}ENG@>iX^=9L1 z4`4yK)`-~+wjv?$<lX6N>0e0D-IVK@}a22I5LI*yU0w)uE1jhzSVi zP$^o~ucRfn@ZCer(nuG}nNn`iAmBYsK)~{-U8_+mECc%+>!d|{z4n~<{-e45FBij? zGam*A2K1v7v(a_qY`g$wzhTF?0>}vl;xqwP zZuE{@l{ZXm9^24JasA8h`|M7rJZ$&LXon$KG5%?vW8-L-gHa&O_wS;m*P{IRC*BEI z_);1C&uE2ZB+d-rg3r%?>zyGx-nVrK9soHF`61svWr)sBc+{I-uQn>G^wv;%BxghnL^{Es;ozV@G;060j`6F4&d9ryoZawy4T z@qf)gc>f>Qz|o+E%pSYeO@1!!HC!dUx<{Z->;qZayCxR134Z=O)RK-%6fk(Y0CJxq zm~o1N1CTgZo}H=>`op<`&gZ|JE2Q57$-=Jtt1vv&TtMDfPTLkwnv}FxmCf{w(Ro|w)-7GphPem@7{BG4S`W0eTf;?pO?cqh%)FUI{GL=VS$(c zQr|N0TT?57b)h0&Wi|TV$sCb|vJ~zY9+_a%030a*aKOq>6u0M&9p}L9ZQ|yJ(|sBD z$ku=8fw%qh#%Y^c;m`7BZzVV(soANa{=vbQV1P+~Tnn7v3Q+VQ@Bw$2T3IawqWJ6% z90#8x0~ME$z;Ddl`?ICxUeIaunQnFYOK3y6N}Bj*9YueOZ1O{K92AP-K*G_&0EB>w zF&?Bf1vP4(f9(f!M&Ft0X>0RGar5%++r_XhWVH$G;k?IoPP?jak<@s2(R2cZ>JBef3nX*01TIza}6C2=aq>n|8$C!S|S99@69d>|N*sLAb3+5C=j_q{|a!Vz5DqcA{4QW zfp9nW8t0}=(5px=Ftku=!=_Dh;n*VfcVHDeOeMik5f%i`Tz6Vxo0UY|#ch;CL2;=J z4FM{-rXxngl?IFuOG9O(8wd|v!Fm7ttkC-ZL00IxLmw@nTB$?n!sQYRCZK^I7!r_; zV5RAR5>ID)ze_@(Tz*yahAl8{z7y!BX0#U;kqIHz7qOyf>N{QTPKJg+59rA6p`{HS zF{CAuWWZ$+E*L@)W7CJau}~g$c&Fq3UIRNxt|uENcl_rq;-06r6xpB&Mi8!veI#w* zNBYZlj*s5rkQTny)NF*U7p&%9s0jS&e>gy{9ar)`bclr*ptaMC)*})Si|uJ>BBWU@ z@o?rgQX<*rk1Hhq=4;w#EyZ#z&;T=r4(LkE<$%ktg*0HH?mN|x8wj`TMvN6m{;ie3 z$ih8M+(I}8#O_RFP~!YTx_B!sPV?O}O^e=K+LvwDJO}5Djv@}+@bFbe9z8o!Zek(| zpqQrR^^7XxY?DPND^p#C3ebrP;Jdn%MIH4ucZ0hoad_6g~BH z$jczt$e4kQf9a+XhFybohxJMbTBOB3TH2!p6k1XQAUXj&C@8>L!Z-$7MpA@B+lRYI zIN-wl;O~c3;%ZDxTFG%*(3s|eM-)2LU-|X)I3j>Bh!wdKY;yHo3 z>kF3IGIM~IR2W}l%$5fGANf_;a5HDbE|+ba)our%ke)+b-*#jXs#ij1BQME2JBwi+ z(RnbuG^n!TLj3*4zdLX))@-wl_&kWKA&H0EvqS@pjzW#N1P zi+f?r!X7snm|v;gXs~*W84wrOiZ4UqOFSG{I-&#;3LT@E^kE467gi8bf0&V`t=F&j zn_rK?nN0%S^>4W^Ke?}_Mf~tvYI1Suy9Tn#-n=;r<`Y@OZ_5A9-@I9?YNO$eSY#9O z^mO9v-5?vg^t)ytu2esyR{WGuFR)5XberHOhWyQ<_kY*1%Csi3|8RaEiJt$z7=3tK ziF+F9_yQreAnJ#;{;M9TB;v#V&-F+pbBbLhiwQsl99=ZepBu}`31K{f>TL-oK$Uqk z3nWmu9_9p)X5s%%*>a;c6Ey-{oV?|z(%66Ar{$0E*FRE+V3@mS&C$XjniXtvva+@a$FKQYYZARJKP}woM}F#$Cj7tmBu2&o zaKp)21m=ecDmn#HAjwItIPqati!gV|jd*r|{5&J?#vU5e;vS*esqB3keQ?moO5vmp zJ3Uj9_R4~|+h^f!zk;X;)3wJd8he=n_HxbiYSJGV(BAkWE_4RVi3WieFv&Z0emeU{ zo033zc{%aqlW#`t-o2Z`JREId*ytoY#~v}S_{(pn8wetlku+Md(!#p;icq!&QFIlN z);!dPdqJJbO!e=>fFt5e#5W-w_UJDqtx4N0D=2UPm`<@2g)&7!A|}llVDg)O{FsY& z4T9UDtCpQa@g>tDW%b}Y-;gc|Y%4h>4+|%p#@8!wtOGH%+aN-q)8(t3BSmdobOeVrW+d=jJ zRtM4rXnsF?KL4!;Y45io<4)tdJy*q+{gQQ@z2k57w}7b0Hm+^y5w|o(R+BF-D(J&q z)w4<=zo~A{KO0Cx{_koa{i{Xue|4wof9->28Q;gqa*#a<;Eqv9{Sf}Ooo|qh8=xIA zJWCHOsDM~Sa2h?svO8cy+rOl&#irG*IYNW@ni1$hWH%>OFU)KMn9)H;!Cn^p{+9UB z!gKQiE?*W4DuWj-SMi}tXauhfID zBm38`@@z2V|1o)qH!*Sa);(vrF)yt{lFbe78{pi4FSHP2>|}vm6?w^wSJFKSnTst& z6jpS?Fs!OMK+Z{M_)Uxt-f`lPvpD<0+^SihnVCu3l`JeT{{Ub?!^6YO)g(|Tgs`Vb zp*36#Ceb-iiU6(-OrGcj<4E+UKf0BjUD}xnhc2+sEnn-tT*{Vx8&e^KHMs8%)*Nv= zCe$(OAH>tescAhYt-1@7M0CvI6Zx2QB7_@ML4KrW_CW4khTpHhs2XRKU8feY;{%CI# zoPQMHv|bzpi?sZSMX(|kp%vYz=7>i(Y`7S%@Wyg%Wo2IfUmw0U>E!LaHjbOJ-ZE%A zzl>%Y(&7XZ_vCjt6kl)dKXVZg!GgLR`6GHaCA_19Y{ay(JM{2k6R5wc2H{ z3w{-hc14ZxHdhU03Dc@o@%k~!PcW5U$IU7mIM zXLi9gIGiRCZQ%8+LKY*PXFjvX3$l-Po#-;1 zBgT`$n{b)o>7;uCG?N^{GaB}S!7ZD@uQ~d#4VTRdI+~0x#xNL|?{2VWV&W_e(`d3? zgc2Iv9-g2U1da1AqV^#E6xdPmsZf76VMm5VV6HML7%>>4d#&2dwL&JlAG8(b5s12i zM@=rA&qeb&?kY^i#G!^kh`a-NRv7AGS)IBbr3R+x`hf5-h0A8trVA^NBIg2`fBGr$ zEbQVDWkr98`X-GSV!2x2I{b)5HV@Znm}BAII9CN2Wed)-)h-9 z2_H0SZ4eP*hDgnu(LiWaI8}%V*l|^-8lZyBMq5}cCH7yY0=PHgD``Wbf9p*ik3NJQ0A}z-eLiC2$^4Qe00vEmi;PY?l5>c9je#Cv=~u2Scf*KD zIk`+6qFOT?K-TEJOFVOm(^5+-Gp2Y$gLgtUW*uAH%5YLG*70VuB5tGTx%2 zHc5a(!tM@c6CBAP(=fB1KQF^P@i^cvh0+=1q_Qpx>^0`)%gInr=9McqVZx?=QYspX zRKR_ZQM_zoC%3@3p|zD^13cGZ2mxf`3h^*`jWsjqs3KG1p0rMlWkL@Rg$?e|PFqY{ z+D3}xfT`KhCrHFwpbQMqrl6pO1YY_#@C2o}=gr5|6@;!Iuw65QmInbJj&Z=eZ}W0< zwM$FhzE#2ObJZkQ)g@$aOLtHpvEbx<4@CNdC84SmVdby=S!U zPopmO?)aR1fF=ViWcX z*+<~aMPRTrubGxfmXkyK@eg?GnbAh^;84mTH7s;;pSvU!qtbT zE+Bvmwg4f;gG{^vdxwmQfknfhLw8`v9vJl6jT=1JUCY6cVE2%72B54}3$iUt3Kr}5 z-tR4E_}+tIB14xuPAFk=lpw@bFqIF&Uoa*D&{(k1RJ_2~12iP;AMt#`~&~oVcXWlHk>3 zHwk@SZ z{u?C&gPSn8hd;Lg^;(75*EJa@ZggXGDhV;PCL+X&61dMH5QEC`r{nk~X4#P79aA3p58(jCzjD2U+e{AN5jPn}fN8*Q5 zwsgR<6o%extGcYmU}tzIPvU-X+RqNPVV&-M?D&C7kCT&m)hcepuED9H^5EA|KXSk{ zkqO+eiQ;9sgJt~a0^~$ky+IDcwcED^IA>+=LfO)_3BmYZF!30b@sxPLNPH0%6(v)N znX*GIQOmr6Un$IqoZ-hA8Q}oV%7+eJhffYl^La=;O7cm=os$szCm}n9(6n{Ysd> zoI5r6R^ctIb;r;rWS*qB7%aACyz(k?ECK@Fq09j7-3qseh8YQ)7^!dt)vGcNv^gNF zJ^EK^aRh)v1VeGTN4(Nni6llw@sbpRv|I-AEo^k_)k*I|>#;V&?s;f|R>gU0h4eOv zR&U-ubr+tm#IuCE`v#)-AH%s(JFnSpc?F12<1)K68CZ+~U0cEF+W+d*>8MVNArrNC z3Jwhp9)oM4rK5uvjQ$g3B?YhX*45;OVH)CrTV=4hXm`qNJ5d!A8p;JHNz!7G5=o zZRg^2k=fmszn7CU9V2s3ySNBuG?49%n;R$PeI)Nk@>^-CDzS!`d($j0Nde|B(sk4o@^Gn_+u}v9jn6&wC<;7EqK3icaQj zaQw2MMLynEUTGE(n9PaBPJhhY^}KQ10g}~eXXop%)2PYycKbsphVckvosb^L+{(d5 zR(P^CGc5oT&4^jd<)C-4H?Ic=76zRMNkiC;S72W}?6zz->7xjW+-`LZmW5A=veXYGHQn$G zobU0L7_b}E4~$S2GZ5$JSHY~M62NEdKOpGDcawLY#udOQLuPvG)DUP%X&diV8H6(@ z2=|Y=f}ZFtZFTi%AT#PPUU~Sb<=rWlG|c5DFCYjBI8;eRWd)fe#ED`Gt+LBMKgYi} z5lpg4%`;?Ci}yFVVZ{;{SgfTllT1h(D+Kcg-!zA1`jMW3AzPerGk$7$wQ?MDD|i4` z$iFwh#5*oU%Yf+pQ0O}>j|^Vv&7jixaT)@kA-14yi<@Q~bS8y9U9{ugpfrwXFuOe} zXQ^T@9i9ya%e4XIDLRq_cHGB&Z}P8X0QdzVlF<7MPW6gV5-z>Bt!q%EUWTY#lGyD9 zyF^&Q`t|EE2Imr-=JDn5@#~b3f5ZXFU^j3-?2u$|CK-vuT zV#5)Aw8Z0WM0WO07+oGn*?vl70*t%=G!#Y&)HI3DNY5B}HDc1COpsE+PeSQ`sTy z$2VX*wf~x6?(@x_T=##JclLiZ-+dgfv75`yl4Z>mlhQSXB4$~MO*JPq93?lYiKs9O zsUwBiH8(kHr8H7nv1p0PU6R^`$#Ij6%i6jz91)#pr8}o{J>T}Yu7BeC#SdeTbI#}c z`Mz&n@7L?~c32m%rq-xTNcNNvJ%qu{2fW6{*SWW}K?Dt4QRDMA^Pn z$LFvp5Oa~N?>3y9@XOhlXmr3++yr})whfK=6g&bxITeIhC5cvnfM7g%x9g8Y(WZ`$KGj9_)_64$Paa6jNSGd;Vv4Ud`zB;BxN%fH_#v7>%!>Vw5m6an|1+UL+dBr?IS=V#B zVS&pJAtkp1qWg-(7B>*_9;0q@ed;~gCZ^xp$u&&Mg*F}5Wy0Ukp9&-(S^<_+^q?mb z9~aI-Tw;xl5Qdpl`w?vM&&tt|x4tIZS@B!3){*T(VmlSwAs2$mOk7^uAEd6lp17S>x!4m1x-7cEQYMai_LWns2eG(d6GS7&R=qB^gcr*xqQQ&~Iv=6(&AbKk zBdqI7s$=%*T+OG2WruXdrPMs0V_w-g$FiZ2ItdIXKg5f4+IXNTI$wLCfx%za| zX-qvohKC}Eb`W@^XbmsCu(-CHFMbabuAeyXZ@>#gDK4%ZkDZ;I-YHAFeFbF1m-ZR6 zMpR>)AZ67Qsx$A^g9lRk{6QMo0%#&sS+Tk0Sp;|H+FkAnqk9%CVI=ef-+EvZSi%_% z8@O5mL~!WJ$IDo6#Qjy9xV9S!nHI&8nDz&&6#E>s;-M>3PwuNnU`E`C#`QgA*5TVx z`bV|ZBCk$^I|_LtnjaX~hi|?^7N&0;LKp6rSe7Z5FEVd^UC9+#VuW*#;I=}rd2JJ2 z@}NnvgE$HV^PQc4_rA*WvLCnn*M3WuEWw#Zg&k%;G4QTlF)kB?VA;D=J94JOD< zq`cWoZTpH5P71~uUWJJw;6$I}mp;UKZ6AV`XCV1$6X$a1MCrqsly5W|LtxSUiwi4? z6B6-V8X=Fyq?1QLb1uUCQ8yf7dLUv+M*HI0*UoB@q+kKtstRo0w)fvErG7`wssvg= zOYGes=i0vifE{3`RDb?MvSQw8wlW2_q%O%?Goq?i1tXF-Bn z3|JW&+A(=-J^H7lQ~Jo#@tOuoN0T)pM`*aR32+fam?ETT=-Im6xtwqi9a7HuQ2vz2 z$hm8(^QiAD^NVgjJAJ$8Ggxzhfpz^WzQ60%lZrA}w^TtEJc>5fcP=vGm!{sq^|3L= z84c4b2RzWYE*0^Fm~W7H-O&|*k5XHqIXl9rjI(ENPf~mLf|lCe)rRAg8fu}5;9cmx ze!UAXpaeh1nAz`#-ngH>ufj+)U=&|HWW7&V*d3S_cTj$dOrW9kr_YRLbgDvG2qnfN zA_^zxi=T|Pq~usdN`ZJOi0Ts5CZ~H^z#frc5RY;k9&u-3j?VD@L_ghi{?tVSCZ=!I ztpE$47qld9ClhyP@m;i}e)};eGJkGCpEL&=r5#%HM82;likxAHHQd~&X?#8ivY%UU z{-H-LcHP^MmD4CVNIynR=vy22ePqI%$V_Q*e+qPjQQY$6hM+of1KN*CC%~;t% z^$>NnT)6@arp2o`Y;X7GmR8zH%z*Q*JhilHh7c*C_ddtB3U9%`_3fKH`|!L&&kZ!C zHh_0ZA()2my0EmIuQtW6()$}8_YEv*xEpB*Ys$%l+Ty0Ov(&{*4)e*uZLUi<OGJx zrU|k?h=HNI+shUXooE$Mqx2@=C;K^3$Iq*s4&?FR%VD%FIp)yV*r?^hc1d3cR3`sB z_<%2uG+Zzk!bCIC;1I}+TlrhlU4}}-&ofof6>V#aGD}Er1^! zn(eTn^3GeR;^O^ufKOq&j?k8rQ`ZcW8GB!*HA?Qf7PI(OBHjH#Wpww+laUlniSj$5 zn@ZjT{>G8$o8hhMcPGwEk9j`VH&rm6&wZvV6bXPO(q1%l=iY6aYh{I$Z06@7Y|bf> zCi?woNN?GKO$twM!n&4&)a_IGeM!;&Zo2AUALG{SQg?LWX4yNXiRsVO;9IHz3$?qT zxrawB3ns=-p0V;TQo-C#z3>eLLt|#r-6QUj0w|_C=i#r<$&;EiO3}iKp zvO*tl8BxYAx`KI>2*U)PVdN}*fs9?jG%9lVfwDSBdRZ}#pejfp{-s1&H^E;V3Ry?< zCzps`+=17wQ0@wdgAI0I(1U@4knj1u8+45#Bbf(_QCv zsg1QC+Zk7bPfZ2uO-|jze~lP8c<>6JHobUblR>mJi*hbl5q%q3tIED<>&(z+Bj@rw!6)FgPYsSyZhw^>6Gy}r)abjUZAv#cE3DRMMaeZCiw<_C#uJ+M{k1n z-sh&IAnnBaKrJ9Op-kN=Hv4iEHH+uVGwx4cQthN_OG<0=>~zot;9aFE-7kg~0!I=){c6VwKENC(*XPICS3@+{pdkAT zNZ}PPQs0~g-ZoQ;mRnvygw`Zb!Z^&kot&Je5``=KOuA54=(Z!KfW+|CWQi=E*$%x8d}iNObQ`3179 z>{j1gd;yc0RZk;^Ucf1yDNE+*$__vx>K{)U5dlDnAU0^TBeL`YFzW`zLj{)gQNV0+ z4oNZdU#xfG^~al`ocsU+L-3Kkia-|s^?BdA^$l&zC~NB}Rjx1UlG50qkSx%s^!$7R zUaZ3f=*g*&qHLCN%9?X857j7pR!J=mIi;TZeWw!yc z!IF13%RkLFj_j`x=+xHs(f4gKHl>D9Hkci3Q~tZkvRlnYoCBnv979x6%d0uVFi-Y7 zJEuEURuUjuv>7@S%kt*(5s!M$R-`jcP&)kV(u+p%JccX}KkhMcPwl$Lqq26Z?JiI1 zSU$8d+-uUzS7qhHXb5r9-M=kNKy<+9=uKHc1n&dajJPee`zR5_>0C4C#zkTOay7rE zYA(L=m@$n7y+|49tvBjjGJM#t=bD`nE5jxT8P~gC=u#?6&5oJznYaus>VDCod7@|u zNyWaI(M$J$(sKQ&*b^>xYpf1b7ckhgqC;li$Yjpr8QS2V0JsW@_urs>l$D?y&EY3` zE@nJgAuMEOW_tR^KDaUv`?}Y8>Ca;Yr*jS-GVwH}lk|y Non if os.name != 'nt': import uvloop asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) - loop_name = "uvloop" + loop_name = f"uvloop({uvloop.__version__})" ssl_context = create_client_ssl_context() if args.url.startswith("wss://") else None try: - from examples.picows_client_cython import picows_main_cython - asyncio.run(picows_main_cython(args.url, msg, duration, ssl_context)) + from examples.echo_client_cython import picows_main_cython + picows_cython_rps = asyncio.run(picows_main_cython(args.url, msg, duration, ssl_context)) + RPS["picows(cython)"] = picows_cython_rps except ImportError: pass @@ -157,7 +158,7 @@ async def aiohttp_main(url: str, data: bytes, duration: int, ssl_context) -> Non libraries = list(RPS.keys()) counts = list(RPS.values()) - bar_colors = ['tab:blue', 'tab:green', 'tab:orange', 'tab:red'] + bar_colors = ['tab:blue', 'tab:green', 'tab:green', 'tab:orange', 'tab:red'] ax.bar(libraries, counts, label=libraries, color=bar_colors) diff --git a/examples/picows_client_cython.pyx b/examples/echo_client_cython.pyx similarity index 83% rename from examples/picows_client_cython.pyx rename to examples/echo_client_cython.pyx index 2100394..6340917 100644 --- a/examples/picows_client_cython.pyx +++ b/examples/echo_client_cython.pyx @@ -28,7 +28,7 @@ cdef double get_now_timestamp() except -1.0: return tspec.tv_sec + tspec.tv_nsec * 1e-9 -cdef class PicowsClientListener(WSListener): +cdef class EchoClientListener(WSListener): cdef: WSTransport _transport double _begin_time @@ -36,6 +36,7 @@ cdef class PicowsClientListener(WSListener): int _cnt bytes _data bytearray _full_reply + readonly int rps def __init__(self, bytes data, int duration): super().__init__() @@ -45,6 +46,7 @@ cdef class PicowsClientListener(WSListener): self._cnt = 0 self._data = data self._full_reply = bytearray() + self.rps = 0 cpdef on_ws_connected(self, WSTransport transport): self._transport = transport @@ -55,11 +57,7 @@ cdef class PicowsClientListener(WSListener): if frame.fin: if self._full_reply: self._full_reply += frame.get_payload_as_memoryview() - # assert self._full_msg == msg self._full_reply.clear() - else: - # assert frame.get_payload_as_bytes() == msg - pass else: self._full_reply += frame.get_payload_as_memoryview() return @@ -68,13 +66,14 @@ cdef class PicowsClientListener(WSListener): cdef double ts = get_now_timestamp() if ts - self._begin_time >= self._duration: - print("picows(cython)", int(self._cnt / self._duration)) + self.rps = int(self._cnt / self._duration) self._transport.disconnect() else: self._transport.send(WSMsgType.BINARY, self._data) async def picows_main_cython(url: str, data: bytes, duration: int, ssl_context): - cdef PicowsClientListener client - (_, client) = await ws_connect(url, lambda: PicowsClientListener(data, duration), "client", ssl=ssl_context) + cdef EchoClientListener client + (_, client) = await ws_connect(url, lambda: EchoClientListener(data, duration), ssl=ssl_context) await client._transport.wait_until_closed() + return client.rps diff --git a/examples/echo_server.py b/examples/echo_server.py index dee127c..7d33fa8 100644 --- a/examples/echo_server.py +++ b/examples/echo_server.py @@ -24,7 +24,7 @@ async def async_main(): url = "ws://127.0.0.1:9001" url_ssl = "wss://127.0.0.1:9002" - plain_server = await ws_create_server(url, PicowsServerListener, "server", + plain_server = await ws_create_server(url, PicowsServerListener, websocket_handshake_timeout=0.5) _logger.info("Server started on %s", url) @@ -34,7 +34,7 @@ async def async_main(): ssl_context.check_hostname = False ssl_context.hostname_checks_common_name = False ssl_context.verify_mode = ssl.CERT_NONE - ssl_server = await ws_create_server(url_ssl, PicowsServerListener, "server", + ssl_server = await ws_create_server(url_ssl, PicowsServerListener, ssl_context=ssl_context, websocket_handshake_timeout=0.5) _logger.info("Server started on %s", url_ssl) diff --git a/picows/picows.pyx b/picows/picows.pyx index 25adcdc..02af127 100644 --- a/picows/picows.pyx +++ b/picows/picows.pyx @@ -996,20 +996,18 @@ cdef class WSProtocol: async def ws_connect(str url: str, ws_listener_factory: Callable[[], WSListener], - str logger_name: str, ssl: Optional[Union[bool, SSLContext]]=None, bint disconnect_on_exception: bool=True, ssl_handshake_timeout=5, ssl_shutdown_timeout=5, websocket_handshake_timeout=5, local_addr: Optional[Tuple[str, int]]=None, + logger_name: str="client" ) -> Tuple[WSTransport, WSListener]: """ :param url: Destination URL :param ws_listener_factory: A parameterless factory function that returns a user handler. User handler has to derive from :any:`WSListener`. - :param logger_name: - picows will use `picows.` logger to do all the logging. :param ssl: optional SSLContext to override default one when wss scheme is used :param disconnect_on_exception: Indicates whether the client should initiate disconnect on any exception @@ -1023,6 +1021,8 @@ async def ws_connect(str url: str, :param local_addr: if given, is a (local_host, local_port) tuple used to bind the socket locally. The local_host and local_port are looked up using getaddrinfo(), similarly to host and port from url. + :param logger_name: + picows will use `picows.` logger to do all the logging. :return: :any:`WSTransport` object and a user handler returned by `ws_listener_factory()' Open a websocket connection to a given URL. @@ -1060,15 +1060,15 @@ async def ws_connect(str url: str, async def ws_create_server(str url, - ws_listener_factory, - str logger_name, - ssl_context=None, - disconnect_on_exception=True, + ws_listener_factory: Callable[[], WSListener], + ssl_context: Optional[SSLContext]=None, + bint disconnect_on_exception: bool=True, ssl_handshake_timeout=5, ssl_shutdown_timeout=5, websocket_handshake_timeout=5, reuse_port: bool=None, - start_serving: bool=False + str logger_name: str="server", + start_serving: bool=False, ) -> asyncio.Server: """ :param url: @@ -1077,8 +1077,6 @@ async def ws_create_server(str url, :param ws_listener_factory: A parameterless factory function that returns a user handler for a newly accepted connection. User handler has to derive from :any:`WSListener`. - :param logger_name: - picows will use `picows.` logger to do all the logging. :param ssl: optional SSLContext to override default one when wss scheme is used :param disconnect_on_exception: Indicates whether the client should initiate disconnect on any exception @@ -1092,6 +1090,8 @@ async def ws_create_server(str url, :param reuse_port: tells the kernel to allow this endpoint to be bound to the same port as other existing endpoints are bound to, so long as they all set this flag when being created. This option is not supported on Windows + :param logger_name: + picows will use `picows.` logger to do all the logging. :param start_serving: causes the created server to start accepting connections immediately. When set to False, the user should await on `Server.start_serving()` or `Server.serve_forever()` to make the server to start diff --git a/setup.py b/setup.py index 16db3e4..63336d7 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ ] if os.getenv("PICOWS_BUILD_EXAMPLES") is not None: - cython_modules.append(Extension("examples.picows_client_cython", ["examples/picows_client_cython.pyx"])) + cython_modules.append(Extension("examples.echo_client_cython", ["examples/echo_client_cython.pyx"])) setup( ext_modules=cythonize( diff --git a/tests/test_echo.py b/tests/test_echo.py index 5358451..bc2c8d2 100644 --- a/tests/test_echo.py +++ b/tests/test_echo.py @@ -67,7 +67,7 @@ def on_ws_frame(self, transport: picows.WSTransport, frame: picows.WSFrame): self._transport.send_close(frame.get_close_code(), frame.get_close_message()) self._transport.disconnect() - server = await picows.ws_create_server(request.param, PicowsServerListener, "server", + server = await picows.ws_create_server(request.param, PicowsServerListener, ssl_context=create_server_ssl_context(), websocket_handshake_timeout=0.5) task = asyncio.create_task(server.serve_forever()) @@ -103,7 +103,7 @@ async def get_message(self): async with async_timeout.timeout(1): return await self.msg_queue.get() - (_, client) = await picows.ws_connect(echo_server, PicowsClientListener, "client", + (_, client) = await picows.ws_connect(echo_server, PicowsClientListener, ssl=create_client_ssl_context(), websocket_handshake_timeout=0.5) yield client @@ -146,13 +146,13 @@ async def test_close(echo_client): async def test_client_handshake_timeout(echo_server): # Set unreasonably small timeout with pytest.raises(TimeoutError): - (_, client) = await picows.ws_connect(echo_server, picows.WSListener, "client", + (_, client) = await picows.ws_connect(echo_server, picows.WSListener, ssl=create_client_ssl_context(), websocket_handshake_timeout=0.00001) async def test_server_handshake_timeout(): - server = await picows.ws_create_server(URL, picows.WSListener, "server", websocket_handshake_timeout=0.1) + server = await picows.ws_create_server(URL, picows.WSListener, websocket_handshake_timeout=0.1) server_task = asyncio.create_task(server.serve_forever()) try: