From 21c201b6814a71a809d288f6337301d5991a8989 Mon Sep 17 00:00:00 2001 From: Daniil Fajnberg Date: Sat, 11 Mar 2023 16:17:12 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Write=20and=20configure=20docume?= =?UTF-8?q?ntation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/api_reference/decorators.md | 1 + docs/api_reference/schema.md | 1 + docs/img/ide_suggestion_user.png | Bin 0 -> 48529 bytes docs/index.md | 73 +++++++++++++- mkdocs.yaml | 17 +++- pyproject.toml | 1 + src/marshmallow_generic/decorators.py | 30 +++++- src/marshmallow_generic/schema.py | 136 ++++++++++++++++++++++---- 8 files changed, 234 insertions(+), 25 deletions(-) create mode 100644 docs/api_reference/decorators.md create mode 100644 docs/api_reference/schema.md create mode 100644 docs/img/ide_suggestion_user.png diff --git a/docs/api_reference/decorators.md b/docs/api_reference/decorators.md new file mode 100644 index 0000000..53549cb --- /dev/null +++ b/docs/api_reference/decorators.md @@ -0,0 +1 @@ +::: marshmallow_generic.decorators \ No newline at end of file diff --git a/docs/api_reference/schema.md b/docs/api_reference/schema.md new file mode 100644 index 0000000..8e0a448 --- /dev/null +++ b/docs/api_reference/schema.md @@ -0,0 +1 @@ +::: marshmallow_generic.schema diff --git a/docs/img/ide_suggestion_user.png b/docs/img/ide_suggestion_user.png new file mode 100644 index 0000000000000000000000000000000000000000..971bb6bc57120b8fcfbdebe160332441ace38a77 GIT binary patch literal 48529 zcmeAS@N?(olHy`uVBq!ia0y~yV3J{AV07SMV_;zT98@?-+c_=n1znl@oa@_H`v%z`CQkCwx zbN0lfWPZtCeY#!$aBoRWRyGdGvGT3-0*z zubV7aJbER};aH zPs2~1GUELH?cSr@=)$S%mt~w4KQVW=)a+2@XYY?*==Br}@tFOU#r1L#XUUd3w~XRi zXH7eQOnrZse$lk;_ii~_%wkZGdy`slBjw*-{)d;VGhRyV(S0h+;wWGjY5rX@cvFB$ zGUH9T+rF`Hb5(D8u8-@tJ!|!?i8cMD(xuOz^M5XT`!ssS$20Ot)16)Z9)6Pl(_1TS z?cNJEg{4B)SfUbNuNWEjG_E%Qk!I$KCi&ZPu(P zkJwKabffS3L`U?VSG|0-py^xO|JLs!(N@7}=M+!BR=IQAr?ysElljUQrnE3IW0}hz zZUvi|d^O5_7xU+N--TOmkF%t7H!oVbS@YCc6~T84-(B%kI(c&5w|v{9!G6!qEWHuq z_hn)pQ@Xu^nT%|8_@OWZ`TJ9{M2crTxcOc3BfI2%>ITlPOQgo`={w&|{)Gu*Kq5ZT! zM=f-kzsTA*B=+fL>(B6aJIW)ns73mlz&rUJ8+=dBJ9Onuk|v^EMfMG=IC; zvc}$cx%h?RC6AxY-XlwLuC1Nre7EzlN{abv zyYIYV=e3pPuAi8{bJEhQJ5A=TRbCit{USg%GDJVlD>KuwRaLzH%`t<hbI(HyYGE+Rp^iGdrp@XbFXba z^XE?SiIWVImUmt9jaR$4XYz{z9?i8qn*8hTw&6<&WOg4-OHVfF1qIsD2x92g_IilGoSIz2H zR^|=%Q!oD-USR!)XKpr=O3mtjQ(k`0b&=;x(2jBZc&gV$%3eXxT%+oV{D;&d6Zd@9 zjN8U$yL9r-H<42g)U~lY?|5$S|Ml_tg%XBSdR~g;{^DAB?R!Uy5<`UTPMwGy4bL9$ ze!(60^v5i}>PP7cMm1fA2oY zWqOpOz&jh^EQ^$@eubWz%kTe}v(Aok*ccpP$;%|@u&Cgimr?T8KOfZf1^O~m!q=vJ z{$-i6`};*(CI!O_JNo0_TQw%0PPsh&pS1C^@WN$}4Q@{5*4V^e`L^r-H^1qA)oTAe4op%RYIY$Fa8zp*_SOYTdSz^^74;c z+vhLV`JL-m!m@tn*w9%sGxUGl(k*6y~v7YthaykflUOV|ICeRxn|ld*jM zRi6@um=yinhdj=H&$zKyI=D!v`pJ9Eh+T~f!|UwI4k*?Zo?zhz5#v+;7@0aFZuV1ut`QtNMELIz>Qg&_oFS>WRf6}R!B6i=tKRFZYSARR3 z!DCX7il@Y`y0gj4v#mBgJvB9_+-mx1*Z6pO>#{cr#>SIHwZkUNo7a~)OX-0C%fmjC zhqv!t5c*P5y0PSEn43=f%lGfoSL~j6p+F>{pxUN~XPaQBx}mRxMfrsAU*`q-=dDjQ z`1rka@l1yrmU<1RX@?mbjL&i}b9D2QG@IXmbg zWIfQ})&FC-@733ovz2r1O!dF``P%)9wcGeS&pHIUcYG;p&P;NN&ir|EzVgy@?D1+R z7luvhRGBeB;?02otqGx=Jo;hV8&l3651z<6sVmdt#-&>EZ>O)6`ZyoHV(N4EfC-OE zBpc5kQ~QGxqq}zJFnVZpPubOoEF?7thVC1-U{PtsqeObIOH_F zOy;qiY`d)#fBmmlP|lLyMiTO~W~eAJx-Bv{n{`YkbHPjT{|@gK{XP--QzmTrT$kT` zS@j>+XSA_Q>PkPBcIDBD8$wbCzoi|MU({XCv+w_Qp14nbS{Hoo-%I;tUo&xyX}!nC zRm&x^erEHi9J_zXcgw?l8|(Y$T`PQ^c;{>^)5*5{i^5Bmp1bbEBk=C^%$oL8^`Eo< zPnmPcXVOE4Wtz#SwOh7%o15;sdve|tRnKE4dG+#hZY#?5U%q?SH#Jq2;rY3_hYiJM za5pL3>0I~0Le(*AM)Lc=UCP#urB63}?B0L2>1ODh*OqPTUTcVbJ+$`ptZ9e8%naQ! z-^#(D?rpDR+p&W4k=y=0InJZbm=ZF(Yo1BLlGy5-hh9y6qWN~uhrjcMF8WN^{PR=& z|I4p$c`o1H<-Y%;jO29Fl~>;V;J%`qT+AE)xe=Pr+ zkR{mEYdYm@rNGrZ^~9`%RVTzGFEDDqEH+dAoy}3g7n6N<@$V%^U(|fH=Hcs^XX3e4 zSyEF?dWTANda%LjmTCWg)wb*40VntWWG%rT0TG_W1+F}8r zv-;0IOgz@Lc1@Ha`%|lVA?($+HZGYS7&obZ(TXQNd)xJG{)ZZ^ovhcjFf&3X?v9`I zrZoWpdl}}3KC4<~q2#>$=fn8=+5dmq>;3(C_%(+agYuDraQT_sa!2MpI~KUBrRR^1 z(%!cWH%`4vz83zE{a#azN;hj{se-3e+p^}D={ssU-oGimF_$mJUGHfBA6-%HaKE@L z+t#QZO`FcftDT+R_~?vk{z+zui*Z34C92jm|JX2fkLvg2jg|#rd5e-xPcPiH?y+X% z`{W(ke%G2zBi4lJNE)xG_>`|X$29qptFQCnDRXb^o$0ZuX;x(G`bAlPUb+7%;d5It z%j)Z@-z%4i2FE>GvUum;b*~MyIGPH~X9XNyc(i$rRZ$DO+z+j?K zybM3G?>~OaymQU}OY)yr1gqY-eBj;nx>Es7p6~xo>^uDC&g}EQ)c2kJtEc@jK;QKJ zpPojR9cr2xzdL2GOt(8L{_jD)$FapFb5|v568tPv&`{?R_JVA<1%j1@A-Z$-Jmc^L|g;Dl5yninK zFYg*O$0c6&;li?Gt(I;r!R+4*o_F(aT(WOs^VZnM*A`kBMQ;3Bd_s5k%C}t#98C&$ zelF2IFQ&aPDp#Y6XIbYouSKy|j~pg#j9PnQQp>vK9!s}y=dUbdPc!N>o3(+_b?d?f zlWqP>db)+K)#z$jn7w4u%8L^m!#1oE3+iN-A1SIVVNhX<5sXTkh^v5>jqPb&0zt9qrSResS{2 zQKMB_RZ3NbFAIN*?7uMIY1`7oWlA%aHLZy5lK1e z;2(QQ-YrNodPk@3>~Q0fPq}qppXmkd>Reu-$Zz|P<6MUJ`n_M0e;w-VuXMBfz07y^ zu~ng~HT3nLN8kS?=C?f9WF<#a!SP*Q(<>&282P=*@}9|d^2k){oF$JQf7W}klI1a* z`>bPAujjr{5nepsWb2dGvcD-dbtm0}RK9e3zxwD|lyS#pnV9?ab(cJkdMrG?zG{oX zSC(CMtD4!+_zM&&8@IxpA*-kIz{pvt*;+x=kq;N*;t}`Oj4o*;(Dp z>nPB&yH}!F{f^bLw*8x@eYeim-y@P^f2{rO_KiNE9#gL3v)3Xmw-2Oko;ZDa`NQ|0 zJ{6@z?#t2GtiW+-t}}~FQ-SoFn4OcZt_}|l4nF+*{r>aj`+xebTD>~Id?SmaK%V5q zB}yFI8`r(A*c&&m_FJUSil8=amAEL%9l`lV+H^3bgF*`f*IS<+j6>B#x$n?SgaNSRDme9Ctj;(|91@ zpLuAmN9mGa%X;3YKj+-Y;8F!Iif0;X-%Kb4C9D z9==vQd18~%H)XB3JX6oGb;-AO`_EXTaB|wl`O&q!9v+{LSf=N^JbLH*m-ibg&(^3U zJN>)jZ~1cZ|NB2J_<5IgDsP_TzVBDH&g|Ha7pIG}JY2Wr$&)94jvkLR*qz1t`kJot zjhZRz4o*BNKI6z8-TP0n&(}{i{hpipbJJ0tEAP`Yo^1Zs`v0ba=1ZH;bN_eV+Sfhr z!PVeR^^0Prp82y@e8$I}`9ZrrnxD`uyjc6^aI~5EeA9H<;{OlwJ7g+n9{G9Z+4SM$?;CU7to!$#v!7e@c+<%vcKd?&|4V;Wre(2tb;X;L z$Ma8FwVt2gVO;wE56h%A58^)U_L=*QGw1WB1hv&UjKM~ebL#)vE?T)eamx+u+b8UA z?(LmFBaMkm_4B*btG3>SLJQx0@bn7Pd(vG0x7x~Wl8Q-ykmov^!c{HXSInCF`daXe z$Exr0pC9PUH7Vus`z4U`J?z5+@p+Fvbk}=(A7Ht(qxaJpRl>6^Rs zR;8Uk=-G0M|9;y4hf#TNzproEUSXDPe$U;pjLofR#xg|h zhA;hQ{(CmLW=FMpth0FbVxv=HpLX`1K3U?mWlqI>+iHHn>pl+45}w)W#BZG9v%S|L zTvvYIcRtzX!#ldfK0VmW&7r1ZlxygB^}9j!#U!@Ay$5&y_D@ur7-~0v>Gw5@EUvrj zTsXLJ?)u>Cx9m?=8W~IybU5S@yY!lC*@TTRGP|a&JDz1Kk$KDY_`3Omy#0PE%a?=) zmAnuCFg4t9&-{aj?HB+45Ld!{!0p+o`wI##+Mf8R*XDQKH=;J|WXP9BwqMPU)u*0b zv!}iH^r?#PPvwn8SUpcZ>&*YBcKqM#_Zc!?iG^vK3*IuHnPz420wd}o5V>F#?=*6n0xJ~3lkqxyBxq78Zn53ydpcqmrp&d%Ol)9#(r zE?1eFsl9De%}1Fh8Twzh+CFa5Fx8#jy83BG-PepR+^~3LZTFe_&#*k*CcJ^3U`VpYY!9ziZ8s6+4WL0?(V@ z^fvPIXXBgA)u3Ci$A8DVKluIMrtr95{(jqCSH^9VTF*N{N2lh2cE~R0J>NgofBg6F z@|lR=md||8%13XX`sEJSr@Nfr_I*^6pZ6x|;k5LXXXfsS*>`Da&BOBlJ?m>fUz%^9 zniu)&$?ufX%{h;iEu7LT{~oP>{e||KpB>))VmjA~PpXyq$J;~J-)=arw0PO{4gVU~uJ~uX=V!Nk;jYt9{w<%U zcDnGf@&?&p`P5g(e1Cke?O%5NKeKVQY_OSan-cS;xlOfv4O^9~5FOup@HzhIf&iCgaT zmRxu)6TAIv>-qw=xV^1!_x~~Ut<%nyub3gT-Rk8>5AEn>|NP3^ ze!;?O6NR-ApA=3%6Di0!u3u4nyKK+qcj;d*NyglJtt!X0V5g0R=*9P50xj9I3&ba+ zaQjW~ZoKUKym$TwDrG<8hh`~y<++HNRYzGxK(LSia+h&=k6WNaqIVqx%(Pc z2A^8F^0j1aqv4sGv-mS#b1Qyl)0n@viLO_$vo;lhdRH{{P$IV;(Wb}>=_#k{@Uu^ zU}K)ay!XqtWgXV<4(fUKsQh_z_nY#cy|PCpST`RK(C){_T^33GWBz~Uz=h05%7qzk4+Zb#DvuMi?akfM^?XtJ`-QJR z_yu`3x6JeYwnqJUuH&>6pTj>++;w}S#~-z$y|0hYEs672wRPc&30Drj)m6H^c;n;e zyI*i-eEHHgbKetxjnn?i58GGGJ9~w}{ky!%lEVRaPMy)Wb(?nH_x^_eQqfaSXs=bd zSUYK!^6`#LwLr~w$>8a+KU{uqe7l9e+(@FQ>5AjIEv`PAS}nr4EY;DRkJ8d~8ehF% zw)yLw9b3Pfzj?a#7Q9^is-8XT#aR-Agqir1B_Dp7 z=xkGS-#EqZwW7?6=XYL8NzFQReeb0R6QgZLb6u2LT_x}KEQ-1qTxgQ}Mf!9{?}Q!a z^2?7V9e($1_2eiF-UXjy-t{Z7?M;S4QD8`COd(@BSA{(bEh*mdn}Qizd}NAcSS$F(*K z@)!SlB59WYLx^>AH3#3aq-nFAx41H@q+D$ilRsiJ z-^)hKY5%3I-woMvORmeNRD64E^D{>A-vNQWXVWbT-&m+E&@ju%j1$YRnw7=tpmsY^ zz2anPw%Q_vlUOXJ21(_ zdeV{!v9o$F7SyUM&GZN~t=Hw=lj$+ZrOMIJYs3F!-Yh{D#|G`M4-HIjWyL*X*EJOp z6`vezG4-o|o$USB|BuZRUTeO^@Unqhd$N~MLQ~F9kH-0NS@ zeEs}?bJMTR;V~|gmK0u|AFqAQyXAN0I(yfoH|t``bNKHnGON9epAo%x-rCvE3jQ>I z&ye<0(3G5VX7|_43r@|;O8a*#Ug+L${U5X09g7w|Is1bn+u&j;uj@vGveLN2FF391 zH4e``{`2PkZ9x%1eqnivo5O5fBlpdY-@A{S!B%qN;)f!QM}EHkX7TTw_I%|aWs|wK zKLtDZ%!409Ce_-WUM0Ea|DWO|?0&^hzIc`hb4}4%c57Wh zhzNJrx_HY9VHU?7M_L-3w^^Lf*H*t1>3(-3->W`Zm8bubPj!EKz{cFSV88fP>*-yW zl>E&2gUs^n-f{L%QB&~s&|0>1QN~G;ySmF?aJ_7px@eWx7j3iGJ?R;SlQd_V^h>iH z_AnFAvo1OA*8a(|ruB(M>8)9Q7q6CdEt{4%uj1L~lx4R$_*^@Wr#S8Xdd0k*VP?-E zBbi^mFBH;eEf%hb6)Tu={o_;J)qY%FUK}N60k`kFz1`R>@$&w{6$Z0s{bGFdktLH= z@`S-H%Z)jj($~(Veb=u);PB?wYri9lB|Lkl{SxL;_Yqm$e&`ZY$=x{@+Uj?{nWVKy z*4?sZMVp9%wN^y^PtCA3Q_e>DHQIWdT_`O4%V);2MwOEW@2;(1c>Ga>@g}{RbC1rr zDK*VA3gAkAZ1l=KrC=q2$RH9i8c+H(J`*HlEix zveMznq_pM`qpqaVr0usAgxt%j3L}$_JZ4jPti9&IG?^t^-$|@3J)Xfm!=}=owd^)~ zM%0uLr^Va9nyjtk4LCX9h}C!hMZKeHoi0r6r<+};dAw4~nkD}FhF#<)FU<`+pAPNY zThgASaQo!E>i4OP?UhFlczt{l^up}M=WBVdZvAa&*_xsnT&z}iZFYErh*VO9xbqEG z#~s@`O=Y@zBeUl(3dnzylFQwtJ>4=@@729m%qlgiS3^Wn6)lSjEboS#iR;^&(eiL! z)vM_C^*c9C+_w0Qty%BX{{MQbzi8y9zmnYd=H09g7S5xIANTe@$a{IzW%1W1j(==> zOMYD6@NePy;!WG8ZfS};{_y!k+lo0~KDDMC*O4IMkl}%Zv#W%{>^}Rch`tHxnITeq8 zmwdk;@%GdS2j%K*=5=dQRlA#iO7Cybk2?MG7hi}!<5kzNJ~=5ClUx4R5_EnG@!RKI zvYV3f>d@2+KN$~p7-sI;ey>gW$hDf@-tYNOW5Or=S=p^w`t{?Xot?_odgfPd_Edj1 z3R~B8$vh_@$ddc(rs|2hy3?z-*|_dhckq8J8FgqnTfW-IcU4b*&tIMstN$@VsCEhS z|C#)p^KLnwEZ%>9%UT1Q9L4I3%r5@_r8lnVGzsty`Kk3(dVb1^&GPOSo$f^1B_+oS zyGead$os>swY`n=OYelZjWtfDZuOrzzdY?X+G^9D?E6BOkxg-@;Q2Hb=nr>_Wq*|PP)39Y3|mhkikFZ+05w~Xc0jx>)a zA-YNT*1Y_&C)1_9ve;fC$=7ZwIC47@w&-`Ixlj?ua|DvJC+F085f?xG|d1b*xYu8HWJE|Ua ztvvE8`{v(wd`%lgx^#Kx2QT`0$5JOVQevUsFDA~SPnR=oD`zci|t&1|(J(JGgDML9_l6k+{Ak1?_L(?hZmPI={lunTTlDKdX#JC(=dVPz2x5@OW9u+MiDGlfLB^G%vn~>KV zT>r_W?dXEN-*_(m;E4`wyl5P zvBxb+g1fADZAhIK5ERrR^!{Ovvv|=d|Ke=*U7C;2oJha7%Y9~a#)sG8@ya6NjO_{> z*VRJ9!|NBVU0+ttck^akil@@z9M120XP?;rpZ~x0Kij_bSH3vCyZgyv=e*NeIX!}W zd;S?qRp;-S-(FB2kn{3PSS5sxG{#MV)n78Ni`?ZryYh}N$k2P3$(aA5Yig-|e8||h_rKKf zS5|f>l{gM<^)$+hmn*875a6=p%{+%~k4~Rc5tC5(#(C`(XVZt9-#Xc4n!rQcwJ!VQ zK%<8Od6t3&93ZiR(~&8WN1)PCVxec_?oGi}PXjd2w^gY^%$wR~O!L^LNbI(0WU4 zVqqv~pwsy7v<79dH;W#AG5l4fe=hjpDV=2{UV>uLQkFCIl;*12$1=7HE?xXjrLfB5 zPES&#%#-P16CZARd`{Q#-3?W+gCF*lPHk{jIy=4n`eP@5w-4XHPrgtxLBxpL>!LE_ zoILY`jf`*Z<>>t4XlAZE=l|>Fhm^?=zbef1bZV~cIpX$w@r1Q`pFS0D)DY5L&L=Fx z6@Me?_r|v+GSdsv{GVP_pTFbiZrh2kJk+(VxkD#<9Bu!|&okkI+xHW*!~2eBCN5b$ z&B4SVuPk`=Y4>Hvy)-5rn*H#oM~{KY1%EI5`G#?7y2|Y({oN@?{Ld6T2uk|G|7Fwb zt@4Ts1NOem)+vkM8#6O<=88>@85^b-sMN_;D?dDQ;&5{3WorS6=>F+G@lwvVbCaKj zdz?Ra{B4oKOsR<%6dE6VXqCL3+C8`DP0Sp#)`%PZ_vQB#)Lk~}F7Oguw2OEBaaW^P z=?Mx2KXcxL)4)MdCcc?-GQStxZ=0&o*0$sX&)n4y|9-!xo@5pA@x%3vU*ryk8uVuP z+ZIOa{e1c1-ugNR@A@e{mt1r1Y?0d@%jl_ORna$h{eGYS1wy8ari zho|ogADlV5yy~)v;o1J;+mi}*?6t_zikT68`N)MyJnc;{Ca($F^lQiW&+$uD4xfAR zjJGPg{PVfpzde+UOMf0dqx*Tmt-D98zJGYcy?(MpwAtb$&FJePm!&0+O`q6$mMhmZ z|I@j7k7qP&PqCZ5?Oy-!ECJr1l8U`s9t75XdsUsWth2MRa%%L}v(aa6>GlUsNOU$= zIrBrIdi(!L)9zhRIqtl$;$Yge^pX#YJ~hs&YDN^Y^$ohck$%bDP`xmR5=_%Tt=Tpzgl3j7241Lsx45Mh*Kz33ptloo{ya z3*7wIT_*d*&UuqT%hK+&s;te|I}4KPji~SdGfXPxPi>ioT^u+b!J5${kAvV z-`Ur9`g4hLn+GYBe>h!V-<#;0da^k_{iW^c z7nMJ+AL%`Ly&!bbnV#0wl2$*t_Wygpvg_#KFRiBwmn>yu-1F2s!s6)B+xGwEa%%;- zp68^!KBjx+`d{IH8&3x7Zt^=T{on6?$B~{=|IK-4rTonQ_DoTHWy#_w(9)^KCSxWq z88`QF?^}h_D$a9DewG#$8!gam==9W@sj~Elr|RSvnscWtT&%&BQ1IgFd!>_~GHjiX z_}@>M!R7y}_3P|5!(YY0@4W-F;{M8+uCLp(bZdpk*=I|aY?+dh&ED82{3dtzl7?lC ziA|MLXJ^`6a8AFk(3W_ieqCQ;reX1&4Vjt(iU^`puIa7c84m-?QdkxaIz6wYO#OLo;z~C*xD?rAH3y$eq{Ds_ml;_ z2OU^EjXsuN6MQCTWNY;A;6)GhE7#8?7_>2(T)S3u|4!wN6i*gVfqY0bcB7~3wjGrZ ze%ib577(4W#U%dx2A-L#MEi@*Uy6({VBnm6`s{^^%H|tSn8$B=u95%tO!~=N=Qp2v zt}6NN*^hU*GB(!^d<{{NSjIVBM*A|0C)0!pD#C|N^!B7nT#q(YR6MB_eBH+N;-)99 z;frmzUJqU7>2>B}d3{&iT`=0dx47U;#kE81_oOa@BKS?|Ds{hN42aMQX1#7*ZG{A zaq!pG%QwFs`oFMJf#*iVhX=(WcfKUWpF26}_`KwsXS`>Js4UvMbW*f4;I&U%muN=EjT4GYtG7mM?IhpG&?oVY6kCM7BZa8CxTUVQ{ z{qwucrPKGUne64e&3JNGF3 z{gsAHqbns1*W+uuSCkdbje50MmfJP(!dL%`9?GCaecNUV6l9!KSh&F{Z(sj3z_>sumrlv^dGI`_$Vvw|CFaD4DTvUtZ9& zEgMzhttP3&YFK-m*wj-JD;tx%#nfzb-ovzMDtBFUZ~ADSX((JA?;CVdBrM#@tlKHb z{P;J^#`=PmOM94fo@FLUO*s?kyiwcdYlQP9DBVX6am!dF;)BlmC3u^q6S3 zh|^)y#t-|>3+CVPjP-v*qT;>N4wI@0-^O%AD3Wjx;Enoj7<`enaA0am`2}$89$hFP&UB z>%&w#ySkf=ResynrM-Q=@6;Rzj>`rcHN@u^d`?@FeAf1JfUcHg=KNWqMuXC08Bt2A2dYE1FMC!DglcUk{`gzA! zi!FSqw(9IbkK2mBnlx2ZyU)hgP4nTtVRN(Mjg`NRJ*%$h=dBOyzI21@D`F z>eW%6?T&D$mupJIgXL;woUh$EX$c=+ zd+X)hb(VP%@2>J(m4EQ&&B~mgkMbv8IFRO9m~%YZrKCaR;*vvMYvWVnZ1^Uz&URg# zR;|p?_)O|mnt$@&$MNd(JQBCw=A3i;^odedX$GNpYnV1|Dn9usl>xLm&2h(VPi`@t z9fy*4wWu$j;j>;^Su%0M_HGXw)=f@%Cqo|9^17(--;9&~Ys*~0?KQ#emn4sCKu+cp zF_EP02R;PIYpxcbF1Tp5eZ%2shq`(BI!kx5JYW62o&Cj&SGzB$J8ax}Qd{RpVkqO> zf7jR!73L-x<}2-1pFL+zPKc|LB)8#(%g#1Sn^P9ASjF{wN6#~%WeYFfj_OW3t!RGO ztvsE57UQO)u8EbL&bnKto4EJrDW41zv-x2Y`pmO#4?L{M2=tf~Ap5 zcsTzq%UREr)%C?!=Lkr>IVLZ0cIrgiQr7Nt|HPO#Og#KoK0ahORbf0j%PsHWi=8`4 zZeH|VmGX4@1JB}yiAgu^AAb<=AxVz!s%Kf9qTcS-h>DKJe|5($O?q%rSG`xjw)WrZ zXZHf;+Hrl_{Goiifz0lUTh@S@rRjG=nfM+q+qh%q{wn6mmwYNm-5h_4oXh*4{%!uw zjmzuG<3xkIig%b4Fn6?9XROM9cC9U4em>KS>3`(jMji@f76;|@a|O{sv+sxc%=O6V zXh=J3Vlu05k8|?rUUNg9?`B1NwtrL;`7k+H<8(-*$ga|6Cf8-R4Ngo}ZE8Cy>FZvi zFUsP$qqtLwkC9>d<<3>BCf9#p3(+u|+veE0=0e_!DJQsM+xq;ceUw()ID7USR{<8s zA1$p;;pba)vn*H`V%C|M2#ZASOucPkZ?XLCru%=B_vh#CPzJ4Ok5N-QW_n3xM#JG{ zT@B7Vh!4jW8V z|35>4qp4tekO=GRh<(dW_o~e@*%{`O__Jd3X0GyCtItMggL(niUq>0XW&{b^F$r^e zY?j=yMr{_~!5v1^=0E<_qcC5`Gi<|3qmL~oekyV_eaKW{;(KT{Q)F4Tjz-tymqy1f z?QynIEzApe8nJxtl)~9fe4eStHdRL&E6>ff|1COK*(%~qNLbjT(!MfR-?rbDSFfnP zDr9l|;j`Jw_J@oR^FM)IoSTlim2I1zP%q!8HDtm8a*NJvOsAc6v&= zD2wBdHyd|eI(d4^2X^NdRvKc~l|OeLX$S2PVB8dA#od*pk-XQ-^zM^_*a;Pv67rU| z^KADjn|1EaAq#h_Hxkc|EO6d-2yu_}V4Dv>P%Uo#!_1y62q<8@x5yoH;jb zuI=IZD%jIkW;ZECG=Eut$@1P18>jyVF0}qknR74alTl2=$pZ-tXC=BdwrHgHU1t!g zG@ibBoQ-`G@4o5DEnjZ&JwzQZ=F6QxHhULJkhVp1IQ z)Adn_qrf{czkIV$RZEE|E@95ltp*n^`YgBSn&ebhsA(yueR4{l>sBHDZ$;~hIhYGl zp9D-zR+hLNsLN$Gdul*D`&wJab;;*0cy=~8?@(Kv^r*OR|COk!q@E+CyV`}OJh#^0 zlNau5By+5#L3wWi*VMSV-^<=Dv!0@|JjYCW`uU~X7B*WbEOoPp`>e>yeQknhMtZ)l zw!i&WmhDQ0k9^cmCl;0b&zn{4ykpbqik#OmMgFFjEGI46{!wDRx$N;-wo}v1!|R0F zT?AMj2A`h%&{s0p5L7l{dN_9`D9$eeE7n}Ef?j3uN$aHN_M8~nE$=}{ZeCr<7wL$ zx;we}Sg-S5_TJR@^DJl3I-+-CD^s{uU-oUg-WwY#!O3^&wLF`{)>{XB1pQyG5aUiW zayzr((U!PBc2|W1-5yyb2=TRhN-j8IzC3^4iiZO0JdKxd$~tmy&S7brJmsn7@x6Jo z1bqGW7^tdTDqg#}`TpeY{B~iZ#urC+M)+J-xe$6jdN-f&?i^FU=PmvH!4amjbut^S*Og9K z!8hs6(~Ifye>8T33M_WRiN{R+JQgdnpSM4g=~-oU>%~h3&%l%VD;I20nC*~ko-nDS zEn`joybrN|JOzBU4lQ~0do4?{xW4FYt;x@&=lkC`c^I<2cZb==Z;BFM9)C%jJo6oc ziqWU#8wwA5U0R>K&CGM!#8gSwX^fotj*RoXvn*6RohGTIcrvM^c)CqGHeGS%l>k}2 zIbqzNZaimo6tL@l^wC0WH+TQ#J{8R!dta`o>e<1jFflyH(bLbWi)C5jgi9vp)YE*_ z9(i=hZqNSmJ9=yN46C(GCSNCWvbylL8?5vQN~-T}uGUK0k;|sn-n#OrxW4S&hFf;p z&)GuPO(`td@mwao>T~42JmF=DO(~bk--#U7-0{i8LHz4BGlQ5d9cQEE6lQr$;@}td zoV)(ZX2bleS<9YQP7sk6n?03Xu3F@-iA8k+ySla8Y9{B{vtQ1|9dGWpX-b^s(r#7x zzqz@^H7LkYz%DsRnQ@nMT-RxBO*V(+>sL>1;Y<+fJaR%)N6mPBMGj~4y>EFw3ZmSX z-}=3BOsWqGZ1iUw=>9$za(Zcfyz&1+Hx=(TSS!&?4n| z1;-cug)R+7{_v`}!4q~wu5%ty9dl)I^S`^b(;6^kPuGEVR}oPBxhb(xa) zg5{5X{8cn5e9GPQK~lw&t86!Kx6;C;la2_l^!^p+5s_=QW)&-^uFJ_}J5;*o{8S%*-yzhqr9s7Pt3~(gT49 zRZM-|Yq~nGCM8G+ZJc6ab{MoYv|#xzNyc_i{kUm~-oz{FWqhKbcJFG$T0F!6*{qNj z?vhw{@6LtNpr%PhTV}!wsa?xrf7aH1IwGg^KtS(8kfwmOS>013$Q#4+q67&!c?`(m(5%9nig4|IDP)e z{i~JHYAL@pTqbuJXmU=T`O|9l>aB$b*&1S7Z?>{Ur!C(Q>CztTD9{pp^4-ZL7W&+$ zZ`_RzoGUV2?D3AilLq(=Eta8g-uZm8;c`}%ikunu<%hxE+Gdrn-HctAA1mLU-q+-puwniI4;hC1Ji#*C zu17w~axxB^(;gH{l$3p)BsDw##4|5-^V{uOl0Vpg>FH>5G!=ZGSg_+#fo0B#87hwg z4=Hc#E?(=wm6>PGH=9*gP+6p}pzf8~)rZHDjU_v0@3<`U;-tjFhbLNhhUHD+uxRm| zapuJ3jqyUZ{YOj|aW*f#YvJR^N20a`lWXd8LeH6LbA9T1hP4v8CnM z-u1;R3I)=`G^MYvKeEu>Q-Uj#@8$pW_kObS|G)pbsgc|Pt%ss+cnHKWyjvTW zQo7yk)x@V0t`;sWh-t5_eScc|;d>K7V?+J*=_;qCJd<~-Mo!CF`8{vl12(23d?H!# zo3)J3*D?xlGv@g=q-;N@zsQ+slTqQO8I_+-ZnpjNF?|1%Z&^=&*1C2r($MdWusZx? zBV)>L-;M*De5Ng5Z@f8-@oYlI8sum3koLOZ~Dc@u(Ia(HDLRZ*SU`WA1K_sJ^w@d`Mk^57S}x9{l4ho6^-Kmg~AP& zKFQV@hd#b5U(-Fi{)c4tz1?#pUL4&qH~q~Q);p(6>?_|ZziK?!nqN1c`?yW&yyT_{=k`&3!qNuHKx?(I3{wXsw7{nM@7?k{3a zP0G*XV-TG5^yTX((u^z51p4uRGu}UGaTSB;DyhQ@Z6>YTcs0CY{zC2+wq^Uif7*EV z@8Jh&mdCRe`be-nnKo@jwqBsLL($v*_aEO+PZMlwUFylTl!q$me>k%$<;rY4{Z|Pezqay8*o=5> zri7b(ExxnjI#Oqy2%8#}A>5sF{pr5&rFT9sdMd^*5N%p0>$76Ps??_wZ!uQzuSv1K zkQ%(=N6#0xvcGX&D=L4V51AO0_gztGVo=VxC08Z(|1hgBOJiv=3*vf{(RA$1BjG82 z3N7bu9Nqq6_Kmnndv}=?C)D9mk>_=InuFJ#tUm4evCIE4ExWKvPxR@=Wz$NzzP_EG z8@yOQ^X;#mDIad`aeJIuyg!pkk)>^);O|*^?9F>us^1RZ7IFUCOKCZ!tV{)-WBPke zZjYaTASis>B8_;*vsJQ(dnfno&Y0!rzKi?x`rv%V44yuJgK00Ft}E~`N3H#KZC6j+ zX1-nC>4#Vk6iuFEY3|tmw?@n7>Y;@;VON{$cjtau`#4ufu*0q782jC|Zyh-l$2D!g!rP_>`vvKe#cgzUY`^_Q8sB`86HzuQr@v*SsLTFL_DBStET9824d zoek~1IcHn?-`?Lo>W*5gHqO%)I?piD+-TBpOMyvi$5_s-2Nlwm)8h4Zi3)~e)eoAyLYi)BkT`O9jh zdTiP}Im2XE{>)9UU$Zoxzw_qJ;nQoG_*mzuJh+keQt0-bjGGx--lazbins>uTg9jJ zARt!jRB$$H(>3pXY5NyxbbTrcbbc-38YtpwS)%nI0K|HFVP%6eNW&E87W+#DOP_T+spK(?y8tUbKYX5+(*rt6oPOWb3QqUrE`T6i&wP|4+m>-Ts6 zdBoH^y{1;>Yt<^7X$)%CmuvKmw`t#C=y<{Qe3I=RYm2_MFZI_=(AT`%XIQ4<{Oz4c zx!9&HP3f^W!V{vzw&e=@ml<1K=>0cgcg2SrnaA3z3v#4yZ`-yjuqkZr&FJehwLOj< zV>1kW-?gYF(?uwj=j&2Kv-j0^OC+TvADzCCurkVG%JElyE|rU|o7cVlpCasZ;kirY z($)RnH+#3p+{yZRsKdbIg740NBX8dvGIZMXLg(R32I#o}+NYEvFw+!9M?dqg zTYAj5> zE7o}AfZ@5X_HV?*RIC;~5z{M9-ngZFGfzZxoRYDj&z+g-@!$EMwTQY_9%S(|al5kd z;)IH`R!_5%cZZ2tZLE+vZuVg>+tQ-m-=_0k9`U>}DJxj0-;!l5+fuLG`(ju0oj^q& zXTqY*-6zt1{is>%WzBP;<-nEFS#Ql``?`~(=Dd0FD9g}$=UuA}``7Q(-1l>Bez5ME z#V^ImO#1Gg`*eB!KKFGGg?;-!%q`#7D17Ck)7%-#KOV2IJNfo1SD4As+y8&+uh2ce zz|9txX-!ktTlKMPM+SY`x zu>D~puI7B)&tOK+g{=gLmf6))9Sm`}mFZ zLIP47UD&rR6gD)eF?S1}&KDH4eoE#Wj*LBNbHi3Vbkh+n&^Q(DqBA$~(+!4Achy`H zBGpb`zCJT@bwiqgNm-MRQPcCShB>p3{j#~99>($1?fd(eJ%_7I515rI_4(E%&WipV zId5knD691+W?E%dTZ{fZ{<&PRL7t5z(ZlAELc{ic-v<(Enzt=4G%z#VU9hWSa{b>N zsk^?P-^Kr*@6)&J$K5Md$;YuW0-r0Q8V)bjOM1eP* zye7Ao?!KJ*@LlP%O`#^26ALzo`wBNGT%E2cCi3&iE0eVW6C`J=7+*PGRXFRKa$e0s zNuHjbi?XI0l%+Q1&xo*cX|wyD=f82reaQ_TP8Auar!LZnw>w$FUsu@ayhyQMIx?)358vEG*Xa-na9ok&?>k%hykuGX>3DJV)+LdY!8K zSB86sLJlmnnWV>ZtdD&X!#xRE_4xIhg7k{&3Z}&0-Lq-y-+*IqUtZy)2(RFG{$zL+#0?$aC|(5+xrnH@@GvV}pmn!gFc%;m127#0mm5-+h-?P-LDT#;|goi70^t zeW;;T@(i{ua)!r(*>v+_|ILWbm{a%HH>x;%$JQ9z1;v3PPgAddvUyW7Xll($Q<4y}a4~{mlYa$>R4HOzzI?quk|hw4E=MO^ZIY_FDUfX;0Pve|Be&{q?v0_PWC7-%J)OH~Y+yJ#tO; z=hVtPpYxZ$m>)a3gsty`S5-y!{Ib0j3AHP`7S))hK3~3Zt03EPzk)*>Gp>5w*=6Xu z?GlsXhDDxg=jYyE=Y6a6mbI3cWZw18O%tF0?f=9cW+qyo(SL={S?smwue%aX99Bwt zvjigA9MAME;B>rnHzIn`m2W31d!I*Gd1}<=?fIYo=hvq{^Vae)F!^3?U=ZNwl|6X+ zVfJsvsyi+_QFC<{Y3Q8&ZDqvFRz*0xM6Kvin)fZl7G)8C;^S#K4lvZ@(jcB1?$2+{YBI zXz>O&tKuoa$unbvBmN$6H_y#E?7;ESLG?>To=&4C(<4jJ;Vd=-8bj(lAEEX=gab8^{ zKv(8ea_C>N{jocbTmN0G;#0j^J9W3W>!-@PsnJiHv#Y9&6wBAwocXZ*-jntJe&uth zc5FC#eE)7QZ5DTj7r!M=CjR@Ce_!F1i-!K4C*MxlU3TI3Ts&#<1(^pYPB2Wp&3$Kn z;p=MNSql&Tcw%|%soQ`5fav6{H&|3!r&e;wK92cu>-vO+EH5`(6t{otJ2ulz(R<(B z!#lgzD>RlZKC)HYeBO(*uG<&!@PEI2LO(Ef73*!0r>75Tr_VgS)UxUmkNZRgXR)+* z7p5>MUiIodbHcEy{n&(twv>0@c!V;pa-CVbOv29Y*RkZvrx{vLXRH(}@0ohT^YNYN`%g2jGq1kn zadL0Ae8FCUhqvSH+jDI`RLM>GaaO<1opbWLb$nSzZN6Un=O1J&q;A9!wjGj@NU%B4A0mTmWRik_b{IeJ!eg_ChXEPL?v2}TP7 z*I&N1k*yBl;i4nlxa97q6>oEnib(Z6 zPMB%6@cy!}{G8tB20HA=r-WoxM>j6z@hmG7>U}qVp5pF(egZ$voIPi^<<6>B=gOp| z+m7#FubYr*B^awdVbZSqb!Uv?6}vA7hltDPY?>TW6nZPJ`Sta6i|lX6E7(dcWVxxd zd)m{$TW6)R&$jDG>M8KpTr1YDHe)@j<<0*xHd8EoXH8{(xbuZ|7bhulUk;uk{%u~c z<@RznkIj3|T|Mn<)ZBbTHe36_goXZVqbgriw${z|`gyUr;BVOEqU`^ZSA?JX{m)LS zJ1}Z)a;2*G;m2tA`dc z_+LBpF?DftV4sX+%G-Oor+iCiYIiNF30&3c94M!ladk)W=V#q|ZHqLznr1Csq|voV z<5ckitPN#QlNr*Qo+s>UeqrnK7`~97uY{zQ6>^;96Md?>l!@=@N)Cm@sFXVvcLl|r zZPt5xB-|AIqW@!$*;|fd+|@U@=eW!|x%s)o+k5q1Ge4<*XKi9PzIM3v^Kwa_ykw;lmEQE|Bp3W{;#op z?f-AVZ?)IR{agS4d;Z6~fAi%3{|YW!|0w&y+-f&;Yj|J8S$ELqEyz$o-L|A_u$FN%`WfalB-t!nBn?G zgjH#y%F7wYK4tYeH04=+`C2|PK4!tkN%~T2K<#9q11agV-lrZbW0htQy!#+#Q+ql4 z@r_##XUl!lT3z$$;qjQ+k+rXCQ+J^ zU%Q0E=k%d%xsOifxv}rMC|@MbFK2!BM*)9dZu0GEug+PTFt%<@O-p;eVw>6j+SYAf z-%s)QYH;cPR`Z?ncr@1Ne)zdH@|W9j57)D{?qO5dT~QnOhkgph*sW@Hu3YoZV~Y7M z;}^I7`I{-S)cQ>c-}S4)ez!|S!~4ubB@%Kn2aH&*-jRBG?3{Ut<0k#)$Kflur35>m&u3R|>!{y!1;Cq??^w}_aS1%(c4 zW%Cwshp#S9U%TV`9h2q?CBGvk6Bha}jahc{UY+a3vmBi+BF-MKu5xWnlRjkD{r%l0 z4i&b(!gc=>w{K9mD5$!kD{dUmw_Unlj%l+mrHO`voqc-!` zU;m$PuP*%^ZCUrx*Ke0;S7TqU>x&Mlnh&ez7gw%pFi?`QE^TY%>#kVUKGl2w6Y=^z z&I}o0EsvLC-6nwg3A$aS@!UJZk#pQXMLRRF+bJs ze{P?gyF5~6_6!A4!7YCTE*oj;o-Zi2YZvq_`jnEU%+Pgw#kB5>vN)l4tU9wh3>|6q!yNMb6uI?o1S`N%O*u!YPvkZPxcD-Zr`Re5c)JCk^vm z_5SHH*H$xbTPa+a^NIWNomuBUo$1}YqQSY6$NRG9?RVGC%b&mR%1|dE(4zRUqnY8J zompe@j3qhG9hmv{Z#t%5|4rCnan1AJ_BWmjGuf{Bl>h(t_bD}VuiyX4C@g$@$IcIe z^THo~|Nkjipy~R)-`l^uvB_I}X8*rG?i|Wz@BjT9|M6tCeX7>(i7dYFW`F(GeX+FH zfa9A@F-cvYE_IXU#)*cz7PFPPIDg|6_HI9M;rYF{nqr^7w!i-Jbf>zI`YcjOho&H?@ak9H)adynkt!Ivg z`wML4m72kmUKP2sy7OiZ?`6-TZ+9&AcA7q$xA9Egrs?}uJb5)WTjZ6UtnJ}BlJbd# zn(o{J`;Nc)q8sF{)^WWkX8yTPz4q2_E*v(uRr5}tEzU|P|MKxxtDx1hX(!E@yR@n(|De++K=i2xkJqzC}EA5i9S!J_1Wf%MY9Y>Z%cmD`dzIoTXr}tf+^(>XiMKktR zPMw-0Y<=%^ws_x#ol{ir-{C23O{x54_0!Ekt9Z)g=D%mwKUqO#YB-XaZR(@1L>KSQ$KE$Xk&}p zv2%UP-=vP-$6hK5hd($l_vZe#cS?D1nM>j7)Mbwx*IrCXEGygm^3s9cTUS206Q>p&U3*%-n7jA#`u(2S@3^*4lb;s)>80m_D6I!S6(0nyx$>gp z1_z_#=0jJm@bbhSHt}6@{lH|lZ7Uo1f2?gcQsFRb3YBbQ;b399yJ4fx{H(pbsj~!3 zR#Y~v$UbPyCe!!&Qr5N1)`XdCZ{4PfIq%rEfVIWQ<AGcY!{P6%rbtWsZfdP*yyj4H-S|NyC_^mS#*@h^Exu@qkfOtN6O)B+rEIF| ztIyV#SbaS#@NUH$^HV>J_Nv>d_+(w*QCvOi;zUQab9_m!C;d5PW4`Qp<}c~TNkPo+ z6{@y-w)*dmv)QokO8UmnH?<~4H!GHEpZd}HxWv`>kAcgAhf-3f{Yyd`^DO3A6xSWM zY+GX&EPmmyGxwcyap&gx=S_{>_R7Z7zc=r=Md2q4&*L|&mcK~k*(chh{-Qc?%JEa@ z{FAeldHUu)Xi2`fYU+;C`yJ0+d6lzvr4a&e0>GCK1<8t>-TD&a`x+R{;>+4U*qy} zL2{Vp%BRzRytx&7i+>AO^tZ+3zF+x;m!CNixoz;WQ`O?8al^uwd+dz*rcClL);4Ksmdx2XS%As+vOvR)oXE;cVLfYW zj~?4M>)V5Uxi#_Xa^)Ad?p0rrZMWH9g##OuB7QyF`ulnWA114Zc(O{>`OhJ{pXIQ=%aODtAale*@1PsyDma?{`2si zp>{tsYhw?m|9Drw%faP($FWBz-n}n+GS%$q(%F9=#oO0D*>~}Z#{J)?w|Bl4=qycO z$^ZOcroP!Nv9tc?^!SO{4Ln*gyUmWjzW>kOLbl}!clV!9^Xq*NTF>}ou4Tz}ma(sg zCt&B^4O{s--kWB$u|1yrTw=PA0Lxa>ux?+6pop2vFBqO{U0=&9={3Qw+qnFOW%TyI z6O;147rc9d!bQtik9PI6X(TWpR7;seI&(wROPt(c}3?= zfk}^E+4M5VZ7*>w%EQhn&)l8o$h0=9apzT$!^hPnCkZt!m=?d}T9w_x zb)fkb*>?E+O24DE&urt9c}96M-j_KiPKlBWUMgdJ_0hVGBDYP>?fn|tS7sZq)f=tf zt-ZaDDCbP_cs`jFU<@HaYLj^D1`Mrze zv(2eqv;O(ES=UVuFI%nMnVQj(+yADDulxa|>wU9gs%qWu+5VOvua!&s+E(}}8c(-> z@OkE&b@A>(nSOI+ob$zY7N^fjs$DGVk-@g^b;2vDAU2a%rBT*lYL|H?uXw#cF|*QF z>fXJ5Y=^rZSeebbA<+@F_S&{(>vw)+xwQK2?}B&MRkJpxoLD}6S%%HWEy0b#-HxmG zmHHSz61vEwRhqxsMvz<4`PEmpFcYucVGDIj7v;62?d`gv?DC}{%2+|FJo9SymgZiC z!(}l_(;9hhU3uLb`?{dRqsu(r{^*&f{t+4`-?bkc_FnhWV)p&5_djKr*f^P8WAC(7 z+ZrW%x6II~-;Mi`EZ^6lsSE~dIsA?=NnTiCzHxD2)K;#w40AF>Zr`~ba%oLy#=95$ z4cBl4rY9HVzSzF<+N*?JGP2T(*I!P1_Dsb4=PaqC3vC1!zTB~{d1IyRb?JM3+3pF} ze1fN46f(ElWbx)dKWEX`ocgMzZg$k(ji&PsZ;+7*4qEwk@BSrNMho`b(RvW@;3QMu zZyjItMXyrfh@=hF31{ zeE!{W_pw8Nv`(JU=(IgMVbZ5xTsJ@6E7VC-aZ4+hu)yGW&AXdvu{PGd!s2nx=kKOv z>3v^XdBU~2zo78M!^P*9Wyl>_#GpSz@a>}`0qowNyG63tuPj-n(eA=3bE>g&R)h1; zO_x2tmj#}GzC!+p&V=VGLjFRRqn~uGQgsNNx4oIe;>u;$z<*Cz)~sK*_l1+q*0iMu zE|)IR==x+7xH$yW--k?*fmo29zlduf<~-U{R-sva3-|vx{xNa-Hzf<_VkbQ&oa z_RXD@@+sqew;tEVb&lue+q%XtuhuvoemQCC@=L}O6s3Ce|H`y6YIlFG@HI^g3>0~q zDdETd{G9KDr(!eaowloMcMdzNZ=B9=RQF5f+LhF2MO&F|_ihuyEs3Cxa!It&dGy`sAW%LCa)jLA(DZ>=`_A)}Cu( zm#FgS96n^mx_y0I($-5(5rKX4E&9UOE1Jk&-elOMHC_Myf?dit7x3H;(UQ9uAu@5T ze1sOGX+}t8R-#1Hl$K8?gQadydvMHs|MTPa|J>_-$_Ib(jahJ=Rp1Lh-=w{NIsgB7 zT>qs0-xKjWy0>F2On1%YiZx%Mckjvke=pVlyior?+v^L*L-k^J9a%no-Q_KpzbuzO(XXWHD6v4Y+E~&4vZefs zi&NIC_+GwHQhU5!D#(lFQRDsB9qgO;q1VsrPS7^ z??3tJl~tX_$5pS-oLQf|d)=-IQP-cJsyt#l(;iy|tTZv;5uR&y)ou0+>6?EXP8@n; zl52hR$jL$tedGCS-&naUjX3hz+3CJ=_!^;&Z{PMd`mfw`QTB0l=(W$GJeO1aGtC}P zZ=dpsJ1|!3l=94JM>{V+@36Ric}dH*k1PDYf4e;M)vd2TZ@!FPvtH-DW#5!ZZyn;4 zRA-ydIrih|_Me-Ll?Ap;3`%epHc1mNf2+Op&aLm0zyEvqz%L+B zF!4Rzxa3YP<7tHjf%PX!R<_5?b?T5^xXD3S`uLSclJ|BQ9Fed4mYlaHgki4m{U6+M zy|PavHM$n4%@n_0?rHPx)o;`1`cKgQu+qe9_7|hug5x07hq-4_Cisre_ zj%C|SxE19@Z@=;9@HwoqLjKKz8u0u~-qszZZ;kdc&pckYEo^1LiS_Sy>t33ew!-@4 zm86;T9xg6em!?**x8xvvE}Q@GTl zm%6sR3u$u8{kQVeGD*FYeBDofuF85CFi-!oiDluhKgssy_Qt-7+y6f@J-h4phnx0~ z51Ctc-mo&BzxRBUuv0$zDw6UG%NKwaK5(wmSrZ~5C8-v_+j(p0{!XQYvZ}Vulc4k&IsEXdUM9STT>-fS6<(-X~xpKTr=iIKf2QyShb1u+Ny`o1a7YhLWp;@u}to}8YrQ}C(lYkBZ&!wu=?tXY107JuG&Jfre# z?e3^5zq+~Q=a2k6SzUjm!)0RKWT$;D)^~P%F*&R>^UV=66RE96zL~<8Z>MyJUeMjO z+iHdA=ikk$XJKmru0&Ta@7L^FR1=xG?DWUZ;1UU4v9$DvGojnJMG8*u$aRpokYlp# z+QSZ)#}W(O*IvApQ7a&IF{0dP6Pezk8)4=v5Uqy6hfo8Rs1fbV9z-CF!?o;|qw zdP~#ZyQa%-AG~oxa97O1wXd(A4BN)cr|CRt?p&9}7j1ttrd+?y$uxiG&71A>)@MBo zkkjU2_G-OjEWiBjib>jG-~QNriGH#v+qy0|eRZQ@#>Cj_kKX^Zx)#Z7*%15u+Sb)y zzNL4#%ia(y*fOg$>bK5=J8NtM+D}cmb?s5N^yP@9Jek+B-`tF43(>mDUcdWj&yEWx zil3f)sIzVb6W`Mdp<(Yn$?Hx&c6vkEde9Ozl`po_p1^~ zFaDX^zlXP%PLY0X@OJ;ljmN_o+lyXE9SzMBi7(LKD7#4TZi%^cfI(oz=!sKCw|_{ zKdHd-_bbh5>uSzbB^N&pOe}i#cK?&}=a>DE|NnM>@%m3S;VO3{bPJkVbc!FJ`(ylE zuH(q=7yif1nZ-~3x@41@|Ns3p-;#@SdlqIlg)KgE$*Xb0%;(S69PNCZ-eG0F({%f} z;vdrkMV>mm_`=T{6kYwJwm#nE=IM(U<5q~SNR!{U;QD2OS)MlU0`#YUPgz(SrhokP z%1=SUI7aOIH?=-1de0W;1)TQ))=S=N>d`RB(_4(fn(Jwr*ic7Q)EuEA;{WxA+_>*t{|Nieg%N-7~HAucpc=Sf|QTv4i@O%P%-Az3+hvJLk zMSCt?J^uTq>LKQ{`W(BSUb=nT*M z7e2eeL*>y7DU;Wst4j03&uW@9yQIr_w^wa-y8D^YKsnIrvEKLlzf&Xv^h7?CU9k^S z@v@O}Jkb!f)hRh$PHUlX_whoV<^_8L-?J1MFAq&;7gTRtDXnt(?hLyV+uZj>r1^jS z#tIrLQepHdDHFWuBR_wAXl6U(EP)xj{5IcRcDS(6r+cz&Y>aN2=Jxr2b?aZhpB;Fw z;`#15uSzwiuCACWb*zsy&FYui&WbdSyG1Yium4>C|Ear(*<=|`?Nh7s_ch;*P`#j& zqu!>x>d&wF|68kf{%V}!zdc9BcT>#$-BUKqe)IUt$HWiOEgU+hQ{LY5T{gYTBhkR1 zs^{T5jd|A(ZBR1#-0gE#=b`}bAFYBgE&Pk_*tSyQyW%1#Y)*cqcP z*_|}usQm=vFp;N`nQ2oxjfIbVVX$YpEU@Oxy~MPwTfWX*_fK%w8jfkHi`#jZt@91c z`Elc-Ut4>X0F%ktBU@Ro+y9@j^_5$|L$%*CEX;NUzUN@}{ycM6Rd0|n!&%6%w)gJ0 zi)X9F70z6}{h8o2=lV2(>qdsA&G*hItQ7cD)mgOEses|HHfZ(5$x9`dw(b7<8Rw!L z<$JbAD%KzVz5k!(buq7!-g(dS)sAo5rtiD=y#1XY47hfz%Xe|#uZ2k}y?GHc*m~rx+gM&5P1W;1yRn$XTY`ln zw*Db=q^Rr7X)h!da!r2xxWt>4>-qjeVILp=rcH5+1erMgxXqI`zjm;z@YNShwo9B{ zpDS1GbhSIF%aq_cd5(Q?&GCt*Y;EnCPoM9w+w8R9X3t!!vo9RPr@vd5cswX&SIo2g z_;kbg<9$na7UcC@Ib@W*RVZA)woTTz_~X>%JA3|ER=!V&UL6WstFumoagU*u-@dri zr7=(bJkZ#`t1>nFw`2d~4vRvk^+$@fHB6Z>JAQ%CzHP6KgHE;Ut=4v)fA#6q-gVQ` zCeEzZp3`UbqTogTpWO3@J<2cKz3RC!Xz{G*g*#>n-P$B$a&gf^)`(@CZ>6>#P_ z!;E(~9$hl2kC>%)vA(C6nN7Z?uKAnuv6FdA*1NIFbbY#Xq2MmZ)}nc{o+~#7ZS=S> z`=HT+i%b)yaJl{oaN6v)@zx^sP3I%l*nXYodFASgHCHZ{2>W`Ry2@s|QG;p28jef( z0XkPF2+f*Wx-KU3arMD@&UPn*p89)(hP$_>dT8I+A+dA{2ji|~3psPIKe+h&cJ{x6 z%S^;NdaWP)+p2$f`Mg5GcRLF6>VKTSW4GawZB$)lTV!#|!NtcdAKuvVDs@@@-Grq# z?nG=>o|N$DP35+>xh%Xz>=y%fH8b4!_*Jv((<9D+<&sQ&uf~K0PuR?54vLj#jnSnYRfv^>uWsu}n)8*sPr4G@)cM z!<1WcmhTzZQ#Ms*cgU`P;Z(iy+BA+PhL!^tN)yUAs5Xmr8*h)C!?J(oRW^O zr?)$LMfD1W+wXLFD=<5X=d#nUQd8Zqz&NF#ypr#Ef=mbBDLCI2tl?m6@&9y7VWLsO zw`Zx(KK;IOboRv4eae?7zFHJ}GB{tpPIn=3L!`A;*RoZ&cuacOm}j!^V)Yje+1>Ag z8wHqrg%2G2Z+GKU(F~{cjz_P*c-3RO!SzD@6PDxc)dnVwJj{E-s*AZR;^qp>PC0(; zhlKK_gD*D=FS+w}XGTyPi=stev)aA`m6>L-p3}2d9{LkX_v{dM`VS4 z%{@v>7qXh}Sz9jVXf*fO6_#bQ7ii}N=i8jTu|w=?mcypi!ok8EXH|-ySUjF7!Z7bT z!{w#gQyqkvwr}n?UVglD*6oE2Qy3Rs>AdmG?Qc~>`Z~~@_o`pIb-RoD%YV6h&iHn% zIsZ4K#!s95r`8tzd+@)kY~JeceV66y+{&{M)QcXM&du)&Hjd{z-rRKrU zb2-yw$F$Wl$7X7GePRvW#I3W`zwp-`)`Z7#^LJZ&8>j4+ztU6q?T~BEX_bs=GtN!> zCYQ09?_QqdM4fxHpFa7)vA#xk^Tkr3EjwzH7xQI=e!dg!X;}0{WaeV&-y-5^C(XCL zmfatkoV)kfjhW#VN~*n&w8Hbx6$$VvBo!3}mA=>d!p|e%)tG2w!}iu7_ngY^lRFA^ zeAQiovMPP17M%&{-hIp~=d#83Yvq==z8=sr3MzVki# z`4pzkv>Pgu=SD<%Yz$DG_io9iZC;1POG=h@x_+Csb5i``H=aQ&^=@rtntJTrJHE4h zpMK50zeNAvfqe^)U**4-epTkd-1WWhE!TxdKH9$j=Zs%{!J@m4UbtAu7Hqcqv$2o9 z=)3%bT^1JGIvpN?hsmy6$p{`lXvXWb-*xZ38`jrjU$`vRTegsKQrcR#;^LMSd9EE% zOk^U*lKlE}9-8c&DRP;YhYg+1uT*?_acl2k4e> zcf0JnD3Co?xaF%4)34fis&4l>cUkh4|6=|_4aJHRW z{f+yUso1md?`H}GXE|=#J-xBz*wx#g+k;=It0;M@^_Ofk+3&FMR>;A&MK!Hoc;wR! zo;EBK+~{*neA&*u6RID6-^pb$Mg5xzV>@WMc+!zccT0Au8S$*VeoEx@S8=(V1Cv>0 zUcCg3n`y)gU3Hq4_3-2TYRQ$6T3sM987pW*zUW2BfrX$Z{i%h)V51gkwEKYvzq%HI z75O_VO*ry)LSx^AcM%;6J>%N0BsdutWc&$&H+r~mnR zQ{?~S{eR2fh0aqe?r`*9Hv82ruCL#^`R9nZ{tRAz+1|t>H+SBq?=QX{S$s0CukV}F zS_ji;+X?&iOeR;{Ss17HSiWHD3Q(~iqQ zvE}z;tCuXcPD*`gT>o53zUGJF&0m*wf9QYV+xNM-T=L}xz8y=t7HPycfLqB)=U<(v z7M=e7!|(qU^Q+lKau{Yz(?3aXoz!FSPL0OPSp6A!J^2Vb(O34+PN4l@Je$d z4$zVkt;RbV{+l*8cT7HTg?G}+JRkX#+sg4e@6Uez0&ev3oLsrD;Y44?_FErTRx7Tb z?~@QM9oQjIut)vLE1{5C?BB1qNvfCG^dx{b~pFk^T;SX|E#i3`@b^^>r#`l*!IqJ3{w2@Rp_Z#>jQ>g;ox=Axennu z-&@Ug-&%5wBjQwb&o`^+{pC6yJ12R4eh_8x=&p0mcdI@BU(YYjUFdTCMdiP=qMYpC zm#+N&bGVm(L$&(P-}3Wr#eR#J#l8Iaj;{~(EB+tjDmyN4|M$P!JzrxVPrhmM|JP@E z#U7LT@89c>s=xm`W#t>UB`V*~R;>p4U&xkuNiUmx{4U3}x-Vtz&Ru-y7Cz`V4THE(ues_Kv-gW+naSCs;eCalKRti4w|Oo1 zRSCYQ6H9n|*w+a;i*xY$)V_b3Je$3`(QS^!!tf-^Xopi1?)+hz%HEdgq4}LxNXOdm zNJi27dt8nhE5)DvylVfyYxnD|Z~r=VvZP4ZeX;0Tq`~ffxK#PdDb}oc{XEQGN0l0% zCFGhidU&-e)oFa3ZQ6BJ%J_$Mr-B-aVXrfGb*}V0b(QU6#uw0L#rcmd-m{#q zpT9@@wL>gJhcpYvB!>J;n^L@sZg09<^!vuP-OH=f7FQ?Te>Z=NzIjr4d``)ib3N6! zmg}6}WGhzs!Oo$H-9hF^-#77nQx5Fw z3TqUw`n&jX34gw9e60Q(i;vr;Xo`P2U;B6dbkHnB7F$zmlbiqX&C6$rWjDnv-OJeS zvQ2K1`$;a|-22Np76clOe7smoz-d7yQ^w!_fw~Lhv;)4% z^7Zm2Wi(Y>=eS?_J(_3Po!s<{N58E;?&w~ZX`Lm}wMgTwc#vMTOR(f4K~Nho-6+Gc z^;gnn* zrnV)}@z%Xu8++rk7dI-Nt6jSOYN?RPj9EI27c`#Fi1aZwG4i|b@p{IeJ0_O-tMA{* z4~d9dYf$w_hJERg)B1&PubI5fU=TdsCmX!@iAYsdHRsX;2J=Lky@dLnRK#aT7N4Hb zq9|h18|^K1Is2CIwfh$=zJA-?lTx-;Nm))4l zBgIHl$5}b&T4ZGG;kUBSZ^ujCo}9OL%K`TH@efYies47A-rVIAeutkce);c|@IGhP zz$s!2rCsiF>}qh^>$djZ9f@eg4mF`4aguk73`}j> zug=5_n;G-!ispqLSa_h0sZV#B-o~?Gr`xV)yxs80rfHb%;t3m z4}V#%ydcwJ+R0aUVwXL;7Wb+y!cNg>^mYzB*kl=fFQE zzOQBAy@C3jpMG&Y{W`r?bbdc<88r8DutwLwI16a|>(e3FUP5s{RvE8oj;&tHK@B<) z&dg`}gN$A5Q3be^5R%qWsi*0yiiYyFS6L|g;^70sB)a`-UwkvID8J6A6|s`Y!C zCr`nO@2ig}&*e~@5x2|ZYJ}3oiHCZRO3KM9&pcW5@{fF`+4s%w{)h1-9p0&>r?j7Ln-iO%_OYTR zYh%a#XRLN12Nu3*USMy1ZsNb~0!iuDJ(q?k@i-kaIKt1=^;5?2fxYV5Zx?-MN9eRA zx=BdMEwCtd1+^$^c5=?0x3F19{P}#zf+zC#9)8#U{&DtO?PA~M&7G4M-?6Hn_tosW zG-#+J_|V&wE#FH`)?Uqde$^{umZQ}z|MVmYmYDhfO{WGmw%(LHv|;aU8Sc9(mv6ed zUs?8YYQ>HnPR1=;Z_eIhUM*~W@96z+U+iBdOq%IuBpdjrs`cs8)IGVo1#VtcVc&Hr zV%LH=-HluJ?Am(0>%z$rSp~^*frb8x+fQZf`{cFe)pCtf(`Tnes0bDM@Mc{#dYV=1 zepAV$*~O@Dsq@yDMN#>zqRWI;CClaC-frVz?t102Wv;pVc>4Q#myL1>$M`-p@2{Pn5wKLy<4wouUtd(~ ze*V@rS(5PL$n2WmZv?KcVxQ%Bgy+W#^Zk#W9ZD|C4H0p@seZP{s4%H%hTMbp1gWN@ zFRkyy-_nXUvUN_;VL!d;qI3W3>-m$u*SyJ?bg)qm+Vbl4p1&z3k5x496@zg5uY>sl zT+H5|cUHc&YK>x;b@0Ip!C#m3&MxcEn|0+g|IV^%u|(GwFJB+u>f>{E!S40*OE~Jb zI&2J4%5eVQ1*!s#O1djlc0W#cd|-T~oauB|LGCB!tgS}R`TlY=c$e3o^Ge*6`@FxD zuOP|dXzTsIO|SW{6>eVq=WqZ0ln0+m->tE&<*z*4cWHWW@l*fzg)h$u`Bk;-|9$ed z%$q&up6;~yIIsE3Y|DS=>wiifb@SMjKBM`rAdjrn!h;JBw@2>zXt)2@6S+$TlCt(q z7xu|CZx(T_ELGiAby7-he%!nzm!_2P%cZ4kn1A#tQyhoFhrKeBC$CO8Dq(Numv81+ zXXaj+JMa9pXN(&bY5L8#*rS`;d+(s5)K2z0JNA1lxY^SupT5KV@YCwkH#VBoN>0DI zQu@sERW~;3r2F+wnpb_IXz78ir^PoejyyNd`p8=C=_jtXbXgiulx0~duqG# z7G+z_TDJJ*u|7lTw!H0M|Kyo;=7`LQk5T4WxTR)#{eScRsHZn;r|k=WP`>}4<+RL4 zE=mE~fmvBc?p)jUsn@G>S@Dd;$%pSmM=YE(`|+V9VNFKCAg$>yU&|-HNSyjKrt!DA z-sLa1Z^tgtI2A9&d%bjpNO8z3J0XoHpIPqKM!03O=S>i~_W8%cmZ;X6osQE#&)8Jz z|9YM0m8%-MVc_AtuGIc@5h9Ec)0)57ern|ka$39P)Ya9#e?Uzs^=&fsjG%H&i({5a z^ITodxV1-$^2Ba;UC6msaF=gEY4f$&Q@72FSBUaI@NBRArcVWV@yB14HJR`GncMg6 zSf)uWi&bYI$7<>M)5X<)9(^BsZf-^M^?fr{K75M#_v8DV{ofh_*kb-1u8vs0|5)_> zzlySgbGNou@A?1lePzUot)~55i~ghvGL`Y%xKXh)Uw*>=zpeLIvAoqzZMpDLUUqqP z#p@rhsuGWPZeQxNb3*;Q*Z=o`@@J@kS)~5?$z5M_{+%fmYB;i5bf!c#w{!khiHcsP z)cmz;pHFX5V2M7z(C_^PfvZ#5pRD%Je|myP_IX74~F?UqU3;z1eSYp59PMHa(R18fUFZlf8lS^Bp z&+D-FUE|A1p6VA`4*1GFIP%eO(X(@(Or}`%qT+DkWly!bcdpzvfA=P<)5|A+u6g>p{%q%ZZvlhymp1%d(fsk3wF^Ig zi@jI#yS=C0mf_};$?^ZKw?E4>wEdV{_v8D2hm}!_1GR#_%YO~^`lPKLk$(S~{Qv8V z9kegZ*#A{Cd#ZqE^U|wI`TKs_`qkO8up}-%B5yx4zgkYz_2=oJsF}%%*5^J3r!!1& zoXq}x*~+b5QVva$kDtukd}EqtfObLoQ^U=V&9oe%b(JUmoSE6z$6NSLyLRom$qNo% zN#Sg8vpjw#Y1YDtg}>aQjz4+6>||y1+M{)g4}HpNy(s+LLPIsKt}}c4vP^g84vVrk zGor#A7B1gDJ%Een@kzssBYp-KDw|5yh&ylRNcGqnmynn{Y3d=9AiH;cS0D|;Gjr{y zO`7@o`Ob~+KAgMmmngma(TfEKe%t?^;G`fp%W==*=7=MQ)>l8d_W6z6Zq1VJ61CRk zR}D)x>~h-qF4-#Z(n$*|#h+ho%9K0<;+DVIx=Lu9wE9&+XYmVbs@FW{Hj($W5VtxR zB6ZzRk!QuUZjn=RLcPZKE2cmE^3JC9&i>a0W-MsJh|Ejs(&bY8n zQd4|2D0E^cl8duJR#lkPcS^@}GKw1Y4G1$WuXI^9$e4oDQ2Z9W;+pEa-4( z%ZuX2C02TE0oyN}UOMru^s_IWi&d1LZLW{~**)Q{{gIW)$x9Yp+gu;{^OBJ=XInda z$Iad0k~O9)GdG^v$Ll7%XWrYTmkZ6mi>zd|o7`$@7ZJUY%b%;ZxUzmy$1*Z?f^;`0tC6>8w|2DLbUKUQY?1x4))ZX5q{u zj|Ep9RjQjUpBZW!I?t2g_@u~v_sw@H-(V;xX%1egS9is}ZSCH!z-{Lqbgut+?xyqn z*J^}G!;wBP3&pIj~|wJkUC zw%J_UzcL=K^1riZNv8g}Vff34Gw1fPZ9btBY}6M@u6=*5?1ik1+S6^@--(-KpSipH z?A@hOi`&^YhP*#Dz5T*VNh=TVDC1L5L%9A=$4oO;X@+H&4<0#L_(*@j!H}&58t1RP zbg5jtnLoDr53@qc0|%8;JI^rMMMWy)eBM>QOLF1r-IHe|3;ie!6BF0Gx-F}@Y)kXI zb0*6;POjD27`Kjb!ijH}=5}44+$n6jTSD5}g=0#Xc*LC*J71SgVZJ-pg~xH_hGSmq zJTw$9UG%IEXPg4Mfczq<7(Iohn`^g?C1qZe4}Hv6rNxOb5$ad+XP zb-g^Zrg*7J)p49P+Ma)x&BV>WOrZMEOy1jSNr%3y^_+J4?aHa#XH{l;J(%j{@~E&x zGC)ssW$tZ5ugkhZof6CRR#c06O_dV5kUO2veX*v`mj!NH6+4dSXdQj@OiswjxKlmp zMw{E>u%!`o-|h*V-;;J&X=+E1-jht5Tk`J49?OM7xGS^jvenml==fdPIH9gNX{C$t z1d~(QK7Q*O4J>9_eVB2iG-#$sTwU+1`?rE@X1#FuzFK5;Z*RPz=aJ+H|1-fSZardZ zo}0Ynu~_%fzi-S|{n?q(=Bgxk{K|qb-QacC%K!XrNt@ttY-7oaY2G2P*QTE>S)}s# z-Lo0Gl58T^udmF%z4&C&q9cp>oY+E6sqQ)UZEr|P(nkxy(+U~aG(=9bY$~-~nZoX; z(zsH3$)>q8>`tW3joIg6y{!SXqa%cAhDTb#muqV$t)7zE23do!_U01hM?!|WePt;- zro}J3k&}FI+3uRJbN4^XnC`vxnuD=yPJQeAe_w2iwl-vxb-b_p?W^QCwQ7ZMH^;v( z_w;8*X-$49|Mbp7xjTI-fx3rg8p}4M{3@Dx^s83dmdWq`oh?5(!}pcl4#CMM`|Th6 zdBU{u9OJpUl1KYgJFN;HO>xywU0tqkv^yv*<&g<9=j4(jYIa3aBK#*`SYKRMJ1=PO z!4oUDTTK2MvCiR0VaM^Db$=gDpR<4Om3i-7Gmb3Md%AQlYokF--+U{_7PUHsi@uZH z3OlnW8=I&ev1XrPQNgf3ko~TU$!Q-yzrZ}3s-5Thvi)F3>aZi7({Vnatq9Ff&Dd*Wo z7mgS{)Bo4l8pQBIMndkOuFZp2XS80JwX63{-10PS-nHlN4Nd3Yp6ItZaPc$mMS|=h z2R@#Gwa9W@qg+$CO&BY|3~=R zzc1PUO43Dd?DI7dsQB~Je7|b=Ws6V8%70v&nH;lY^Ob42^8^>XlGdWTx?PBmw%H=C0a9K1?m zORM&J3B~SB;qCX=_YcbS+QrUqY;@!Nqbx>!7J(meeJ4+bZb<5pmtSr^Tfu(z;yq^P z)3ZM_L|hbE?k{t1+9tR64-|UuFz4NgSGkbmcD&#C&g_Su_NKmxn6<-dpbl)rxU?D<={oLO3R4=Ktz;FyC-C}6UvJqu zRn)J4`9_`<<|;wk{yBT4?AZKlU)g)5|GDRmUEo#X3*oBVP_@xlgY}Jt*_5L6)DW*H zZr%+%PtQ3dJ7?B*)-3hp@VP%<(Z-@dm*s3(nWe4T$_0VPuPe*E@Y=j7ZdsV_hXpgT z|E@|CE=^qcBvC@`LGZU(B|9gk&s!B4_3(0k_=Y4&KY9Bjdsolj5S4k~AyJa;$#rgz z1%Y~jc}=-nyEOE-A2{07xGpn-&18?(3r!zcCIud66-CDjzx>-2gE&|O3S*_VYV``} zTwd^UYNkO$HutHk`k%IQM8-!ZKM0sM!_t3c=7hIz6#g%`I?3ho<8NnQOSJW6&bqKB zkeg-O*1wXOrPFPm-L7~wQ+b{D-PIR(j;76L=WyOIo5br>+j4{o`wb|D=BH%lkAy2b5i%@-R&|%BxdgLAKPa3uQcVwO=Za=D*MB z-LUIWzWop3^>b~{-T(7Lrtg`tab;tCeTAUD0LMb(s&Bb`J~?x@@2RPeXVwV*`?%(P zbo|j{>QjX77H#}$m>OE%X? z>iO22SGP0>u(2QaH`?&lTT6F;Po!COu4qxw=Q`#Bt6dg_UliCBgS<=*ADVDa`0`Ch zjyoyl%l(#bZheyo2HkzPw~2{ntXNV9z#11V;Md;0A?zmS^t&J^WpA{;s2~I~xLi`P6-}t}9Oc_EX3- z=3MPpnb+oaPsHcnWmm8+ne~3Z?VWG0>lIa4>MpFZw10N^&LifcZhnW1CDm;b@>VBK zosxRZ+d0K<`}&$=K}%~d8^mjzO)996$bYGKeOWYPqR!p(9}my{Z5`*OwUbpZZvXXp z@7ClV+I2i@rC+(3>(TDZ8qZeb@LcF#R{gTKz2@7;%2kXFXFIQ-w^5s_zQ=RPjcqe# zJ8*3LDKsf4C~ofaL%lDTv-vq)HZmYL?5X zhL_Rd6%(p`BKHXH%<-PtSJIZI>Av+Y|MBD%eYQ_$u6rJSGM7ato!xB%@8oq6O^ajq zO3XU<+Bw-nbLWO_9pYQIClnhq@A>_%+~S6)yFzA#sMNp3vmBbQPSs*(WABKP;%MGz zod5CMYvG95<=d9LWVg7evFu{jRKYEM*IU|_y#*Iw~Z)(&X5>54uk5>dK?8Y~E#E%*u@q6WYo>1m#GHq>Z4ayXRBsNrQzhuB_jKBh*qUDp4xe8frWYVuxbNrR`8h7n zrFx%A--%qsr1*VZ-KhuL?>&0||D*lZxK)Y0{`Py0J!|1+diMU_GRuFw|6a~_XPECT z|7Q;OW!81E`%jkNubBMbq-K)a<>gUdx65ANWK`aNXyyA4lmGwl_mC|3ewqKr-K{%& z{cZE^&v`cc`M$IN%d8Hp7yo&+{`YzvySlY&%qFkV(KD!dX7KlBXxg;y*=Lt*EfO@W zY1#aITNJ~o?9U>vg*ayHtnTl0R^D^1R)CXbt5Gn2S=^Ufmz;MUXRG-8RZb-!YSzPB zdym$d{L5Ae;_TUQDr%pv{>}v_5B*^}?#BNqYSr{KflC=%+`hkWpB|s4H~-Oxjlm}Z zlv-5wn{;o_Em$hsDOBt`?b)aP$Oms97{Kk!rai@8dSVBH^&%V5K?^oAn zT$+9JEcVV^e&?`c*6mMAB>9+|YfZSlpRA5gC|Pru}zOQa@ukQY%g^^ciyMCB&VY|{$;l9ddKFKCxsY#i~43BHE~vc z{H1qG-^ug7lRt0M>cSjox_!w?qfgd)nVW>>moMLhKu+1?uD`Yo|~S^PKxbS%_` z6~&u39Q#`|@59FC9uvR4fAy4S#`^vbGAjOSV}4Ec-p^Z_ImzXhof^N_e^YFASTvjO zWD0xVN?|r%uGf~UZa^Cox8|+xP&HPGkycb$dPmE3J*SiMJRiaS z2$O74uQ(5-w8_!Yw?7(nADOK3SZ`KCmg4rE=h(MgbMjak?4rXg`D=}y?qv?eS!aYM z%B^laH80!p)2;0@UUa|z`Sb8f?wEPGpPtQLFSxz@>gHyn=k`D3b*7X()4gkbXyQVh z)~A*4&glf)UlI3unXgIGVot{g<{xd=n=-t;Sf1wIaYOAy!>s39uRXlsnBXqA^-V+k zca8ij8(Pu?dfirpdR52&+I9BG%HSJEBlYf8?ojXdS2CHKab;bl*7V^yMj|*E^C!8yfC5Ss#e=$p=;XK*Tfw& zc3jk$wyv?EYHQQ=8xh5ckKd~}%dg+{IbXv>_IjydN#p$$!C$vsD=e_Kl9ZJ^vh>uG zvzlC{SC@WTCAiD+O78;hj4Rn!UiG(4=3dmvvM<~Xw0!PWTxqediL`Zdp&l#Au&w@+JbzWmvy z{N{hv39sI%|D9p0r}Z;6?6jh-?cIo9@7^yy`RESQ)MxKztx}kF=ED7}2M@jBaee=E zU&I*?_T_$ZiSM2*)xYQz-1IOx-P2TThsoCsXHQ?-EA#SRbIKa4&)nYDO~p@7uh0Hq z@b@K0hE!kbCad!4D|c?4UUz?0gY(Vp&p9nbo_dxTJE@5(Zl9F>+m1)S&ux`)bNs2` zD$VLg8mImp4VtSGD=gU-qV4)=chuFmX@6N{UcFtU;dPtExBux={o4#jCw!ecb$YE} z*Czd^Uqw&7*8i<#CvWQQ8VK6<3Yzi-jrlFwQvf?!wiUJtPk#=Vg~-y_mIEfYc30kg zzMJpu)g64Ufm3opa}wf=Dn8wI=6=f`CvlW=GiJ1%J#a$soG;JzQq2eFoh>rY&9gk{ zkk0frHNIz&hJKHvr-6-)!-8@1 z#GebBI_u$un^RTx@9_Am8q<`vOJ?2-1;uWkdo%wYDtZjoP^8GDoLjCS5#JY3iz1>6or=|-X*qAcy{rBE?9A*qMcOQhT>X!f7 zpO~$$eyZ%z-aWq;m%q%q9G7_B{eo%1ge`H;PJPw-t%4xSi>-_xKh1HLD+UF_1()S>B46dfc`jkUF|HnRNi)CjI95kG>OJz-X zg~5K0;8$P1e7mIa%s@yoNo>t}oypo;3lp`Joyw1;rhYTqYAapLDP!(+rt|`fNtP(r z(%fnxj@}HLzU1LiGdZo>S3jLz z6Y}fjIkgDAm91+a#QIeqa&TS%rx`$u9$Rr?0qX{_T1pXlQD2?d=JP z4u!RIe{K789BF9k?djXxb0cj0ju>&wT3e((VeM;ARqFyJPM^C9Q{Nu>;%N5z$tTxu zBKh;1K~uFt2V9Jr7jMs-8ne{L7;-Fa)>$=K!RZ~Dg~oUiPI9}gWrNP~CCR6Y|c7ysmuc)_}+ zUGoal1s*o{{tMH~GR$xsY_p`z8xgzx)5DW>ii2 z|Diws^X>ZI;c41J6FfK%F)rjh^vLG_x82$vtDDQ~z9jp}y?cCc{<2$__!ehMd3gSq z@lm_8M19ewBdyZLCQB7=RyORoAkZNBcENt_gUz?YBO|_VSQHs&ZS9qoId$o$%lr3d z{9OLw&^Qx$Nvc0bUv!tfxM2h219Gw+^to=%el^;_5XPKr>29z zdzokN|2_QO3N#2v?BaXFvn}O9!oPnapT9f4P-AXalaQ55_hjjm7W*j7}|b)9f3&Cs;*rP_%jxvTEg9Xn>KF`YSRrphuk zfmxpS4<7FMx|DInbz6g2qn9kR1a_Qco?~I>cI6Hi>k=N0rG3e>ITUZ*l3k6_+53ES z`&XtZF{gA3chAWFy)*kMQ>ftc+4ujSbSqhRF=F4rxl7kZs(n4B75x2L#q0L}6W6mU zw*HL%^XBj8H*?KDF}0M+={=K>mr&RpmQq@LVEK}mRTu27&V*ODXU6VK`FBsHj$>0) zLRw;Pb9>)Kb1gxRRj>IquWx5ElQgOPQP6o<`S?k`b_M2V%kzvjZ<{|&)^S_;ge4CP zYuA2JDq5#;>Uju!j=ka9=6?_FnQWb~wl2W<*h%B`jk=Q;ggtoYm6-1&o%$i{bVo{E z+N{ON&U1cy#jk#?l3`$aQ;S*Zsf=-VW}Dlwm(rQCljqM?d!9Sh8nfpEn__=;FZ9LB zx2m86af`qQ;(C-`dVXx9$y@=?g@Mo05_^9?{nNKmC>qxDS$)Fe4aC`u$(pd`HTkny)E-`^Q~p=H8m$+R4>L!TP!%4*s4Wbaz_l!f2kJ zo{RIhMb6rL{%-46_xTByf=ZwrHRi8E_WbHAwV309w>*LI< zvem2g)-jI2yG0#QO+hzrTe>-yOGrvDRex$!{akFbwySBIb6}jIlX=H3!4k&as@|x* zJ3r3hT7=vs_`I{d_td-#9;N(+B^n2}p4SV!6Lv&$TJ!4&4yRZ1wm-hHw#Z%m%j}R< z_vYP`@-^>tQ+_c^A;(N3bWi#;MLgYse7nzXXTI@W-U!-IlbPGOb=Nruf3e!~^2RmB z*0%nSS?|tuGPk^NyFL5ezI6>-kG6+wm0*rNX?vXYELZVn@9MN29A_1--t^6PdVK53 zo9>-2YK8pN(7NASor4lXBN;g zFsDundY4Wr@lBBkS}Sr=e6_yqjfTIh;D$bE%ZL!TVSoPL-tdXUuJc4R`sX#fD0JRd z+$*a%ccXxFb}svRjjlz1DrZ6$cYb?!>F=TynHx{pd9np5#>?`ZIQC?*Xz5DRhu<7)4`&e8Q|I{q(oAdbm??cx>t2*7>S4|Fn9=F)|+n>OF7p#B0 z>#si&(EgQa9q9NDOYj;`m2Yd#?Fs+)P`2*>@%|Z^!5bJ8tYm%W$f)Pp{VtzB9dtIu zN@KmBuL>IW75@L-_+;~K*DT(XCnsvG+~HXsQ&e{0`JdY6fNk5CI-4t(l$Hw>`!MZ4 zcj}SS(pl$kz45&_dvB-g`$zfJzvSyTT<}cY?V%nsYpc*{@6}iS2>5P)AuDlYtFQ~t ztEmlwe&)^3`$eg&V3Yv4Wu=vMM8t5Q#0{nfY0 zHG{1)MaH{5Yo|+Ta6NI{daetzbp=5Z2!bF=_51axosKS-~0@Dt+-y^Hz9hhh6{(q9nqp( zMz6PbmW7u8d}UpK_M+bOthJ44RyY1}+5df6{UmE`yN6xbWeW#m%c6I=`|8$+ua7#s zLAk;^_|K>2{*^EJ+WY4()6NpSTGG}(Z^>%ON8#5s*VrA&X5*P#F*$#}?4qcz4#pib z80G&y3%?Unab{g_*CLJIOJ)YD`S}Q3X8t4K*58!q%mXNV^ ziodn==9i1UldJwIUe7zPw1&gB_V12qohv0vzJHy}cW}L!#xyUM+!yhIpedqlj5BYX zlg_PL>2{Pm;>1|Ue^Dww)_3nf9K9i zT~n4!Suj(^s%FMf>Gz*k`@d7se3V z>~y}ftEe+(@7}jNLCYb%t{=E@b!x^7riG0w-@i@Xa`xN31&)jR#9#2p$t0d_j@fvo z_49MiviHmXNKBJGZushqO@zvXNuN&1bf@-~9AY$M>JCt1lx(w(_Dbui$_E^?JqY-S$hA7j52F(Et7Ze_MX;w{MJtM4syR z3cQK&O<3G>E6yyp{-HPLo7EYj91AboTlszeaP8yOuZQ}C^Sx&lDgM86{eR`k2ItCE zX$lb<8DC0ls&{2>R67xR%ih$?-2SNk^W_YVdx~f5|K0xH*sipjyLf84j3krPtZy$~ zie)f0)w;bq`$?<*$Jz5gnG=q0s(D;}|8ehQ=Rec!@BMQ687T5pe>P|tX6jP#GR#wI z;ANN_`or#qE!9f+bxl<6$)4l$B{u4o36?A|Hu1Jxbyoa-6=-e#`I65I&sCMazQ1i{ zp~BW;sg+X-3d0U(Z~J~HFRh{c{$D=!-H$j_c-K1}z8@>h@uBv`ix&=;x12ryYAe&# zWrts!U}#%5@r|uVzD-JM?eFFrN4}RnDip81z0s+>;&rwCvpW&yyBPKd9$x+PceZSx zh->Ad2}L)RXPpx0pOLZ4(Q2|Ix7HQ&)BcWHvyX!IUn>Z2U%Bm9)Vzg6ExHWTP)K#q zD3!VG!m)9G+tk`1U71tHQ(u4BtvNYgHg8ga!mAfOUw6-*&#o8~dtpY@wVUa?@9(>M ziglIJIrIJhC-=UHS-i2P+1fi=FaX=4%W(O1A5R{gy{Gou>_5+qg;;d%?>lq%>7!>G zjWi1X{$h@+f1YnC*AjC5`Ol~Nb%(1KAH0+`(fs}&IraT=OiddCAN9{av)#15Ytf&l znrqK&oFM!B$4X;{6AMl@S6{vo<;tsYu&40TnaG^eDl0^H?EW<4W}&p`g12{%eKx)< zBP;#rPWZ2=SanBSqv5~K{&j18dX;Oj`nkdYuRVW+uIKq)?qd@ud~?Ha>zW29|9t(0 zA`-_YCk1#Onke%=TQ@sTa*{zy)AGV>&*UoK>O;SzrIz2Tn?CW(y3mv*DbIdY=B=^b z^LcCVjos;USy=D=zu7Lr)R||cGFjGQnV*D-=9=d#%*x6>a@-lhom4fD)bph*s zE0`q7Y6`7ujsJe)Ik!n!@81hNvwhTBQ!`r5T4}G&{-LN)GHIpBx@0Gb09|p-DYhz7 z>H9wF9e#PYl51!EQ=7?4HSYhs<$30=aMT~0_y7NV7rAA)Fg9?CzA2#%I!68+j84i| zZI4`B$a39ow@YF4>rIK4JNjA`mb|O9?sLtsn8>khrSLCbtB1ZY!VBXS@2Dr&qT3 znyaO3RhwSy-mR4~_vXt=sr4`J-n?n6FrUdcnon`n8Tuq6Heizu7Q?_d^#Q`S<_ZyDcaCUfaC+@&8NJ_nYAi z&5DiD8Ju|GPyvo-&HZD0JIg9Qvc4m!rqCOr=~&3{)izxdr=>>36%_l~;$f2sm*RzEOD~;KZSLWaH!p zXAZ>{f#ZHme2OgsPBPY<7H|dp2NxJz`TOu$e6VS6vgD5#om0P*f-05@ZF@6Kucd+| zZ|A{Bk2JIX&E3sYXLoW_#K#7n<;P6oqjhGinHg>UN+`Xc^z@_W<}#Zeoi2a=W%c5o zX*|}uW97=y7duU^05=jZ)TzRr)ep7f1Nao>xaXMS^2 zHtcoM-}yguxs}=B$=${V8NzQ^+=F&+ih1?bC~gL z4F-*!AMS9T%|4&{*e8rn)cBnf~#lrdzh^ zCXIyT;%fbg6OwYiu{_=5ze3B$>=ese*=hVInva+sz2noqeOSe7T69uXRae$Y z#dqa5?_>r_d{~vNbH>-e%Gh;hQ1Zt!k!nWHf@KkL*^BmYajQQs$aSqRpM6g;QsM6_ z4&&g*J60{&yV+N}gK@)Nt*59)zZZ6b%j}rt$Td^9zILh z)DpJ*cwmf^$^(JrFoFO%z!qGH3R+n5g!;nwG1#Rpb7o z-+y*9SlOkpD}LRj{XgyOe|)T7a_iZhZvBtiQyJ$TI`-YGCq(vM-D}=^`)?jw@B8t5 z{9nE1-q#Uzy;*x5ukAX=U-u(;muF-2>_^x4|9j zSKR3zvKNPVf4K8m?v}(X`M*t5-|C%O`|<4if8KuAL<^$=w5CV=e~|uXefczvUccgx zk62Glm73ah{wKHiNmG~E;Xf<(cE)BE{M-3FbBgV;;=@Sbn$ z;}%A>ENMNsJ|shL@9F6J;>e;M93L$ccZQy`FP}Fnc0zW6?9pe(y>;?yCkd7%y)zMx z<2?U)W9gCh>Gn&6pZ>TSDCXz8SR`Va_tEg059RMS$nrFIow}j(e7+TDWmg#Ays4ib zEUVqo@ zOQ>;W;wy~b$1J?{_WuiZ_qrrqSYNMgIXd&xzt^e!$F$q(YD#Qn>T-Ktc0GK%|Cjf= zbz4`So%j5E{rB*h)!&WGf3XGJuzIlm{vXb}cb>i0|8MsEy#3LsSLZV)L|%ydIYIva zU#_VuH~%@8U)y|n^83&KqNe{W7LO>1u6h4E|Dvy`fBo0!nzyaoF|qej(qI1nIN$CU zE4TQ*qw8%~R{pL3W7;fp!<#vF-i-7!Z$4d{ZJ)eY*tq^%c;(xr-G3hKRV}-|?!)r> zs;MiFivK)k?)ORj{{P~#$q%1IB|CgKtNi@c-np`L-)HIlmB+2ORlYjJ9nze-@BiWL zoLi4ykNd~F@7JHpYxaD7_kn}E%zobyfBp4=Gp$bV|9ky@LgF7=z5H*IGwYis=XKlv z)3;wRKTFNe=H#O#-O0|glv{qtWcZvne(AF3^F4jZl(w0z#Wp(&CY?>2p0WPBS%1`@ zPt$!rWZ8UvV|>Qn?8TZb9A(!l%FB5rzAcNZd3UFAlI}BcF^_2Tp9z(d4}G%yd}g!G zIsXs$zUKb9WudO7wI=3w!`85l_}NcdpPx6_x9i}y1*eyOuWO8-`{d|)yQ5RT|L+Sc z+TySI?aP-h@^YD-kGNOHtxpb!xOVq(TjHu6H@8LHll@n-W7&%|yVbJoVNVJdr@sHc zPBKE}=S5Z-SD_6hxt(eGGvq8Kzhsm$-&(8Yxz6*D-D>k`H($9e;J&pscGq6Hxp@w8 zPrgK1*KDboY+=5n*>3k;xw+T$5~C7k_FR4XI%f-e_1#IkOR5>?=9SNwCwJmbwui@M zI0_W}Z1zrhxj1%uO5_@bcl&j|Etq*ZedY(#W17acHx;;=ly5fXr93n7KXWCp&wb|` zp>Kzsi>HUYD)qSBejbdIS4P&%6-e(da<(~8v?9EAle5{r0`E|@V{Q_&=OyQ@ zY1$MUGVfmK^AlHOQibIr^r{zBTwng`_L85;+dc~ztNTwZwO096xIcvb{~`GYKX*Tw z`|QKi$?FquN59bcb!GF~i<>oMle+ePUthDQHUE)M%#u%2-!Gcd`_%fu{KKoopX~jX zye*ugP`^-S@sy;>%9(4|RxeNbu_blmrNfGHU3y%ILx{^QwO71WuUnZJWp5z*nl=x3}GhyYFySZH7kljlQGD zj+ssUsHxR=C{$8@y(#~c)hkSn&D_5F$X~v-3J*IsKR>}%d#6b9kl1Qj-?cGKGd-_u zFVpPxH4NIeF}HheZ)w*po$h2a&t;ME_Z4(`;&!^NjX8A2XT!d$i;T=%?}jJrT=G}V zdawVt#QAX*LbqN&*&2Cl@024uLgvm{+HWp7bKbI)#$2H=vs*rA&OO%dP0qe{efvtk z`ALVazff8@H&i2BR_&Nl*{y>Mot=-(U|z4Smj3*sZfo|^HS2PlH?8RJTfEB5?_zrC z>gL25@rg^~7Qd?M;S>5b^X#238m@^2A?J=AJNHj5Ij(k+`m)tWGSB60_AJd2x-Ahi zwWzpu`p%=q7MTs(Y&)#iy!(<@yVpc5En`Yf%=M(=(y3Qg8QB|mnRVG@T)eEhOiFdi z;|sSUmezZOc~6c}-?PW_=gs>|fBc-^a7|)uVDEFyxpwSfy){pHxxOn+y?nXl{eO=C zFQh8FX-o;%w{kvfYx89Q)@!)4OYsXtA5IsciDqueooE zMCY>bJeu)#t=i1s#Jo)# zdi>5#_&O^mCugFisoU!LOP0yrn^A0b&0J$o;IY$P2NN3ZODxXO?lI)4%DrY@uF>yb zXt46_g6qp}zM2%RDka+AdDHUIHL>i~*>6~8&3wtZDZ^n#q)+eFcR#lWC~yb!BBtMemzm@e2;mezMo}lC=F!+Zz@==M1fm zOY}!lz6URut#`+onIf{yl-7h7lLupeGmm|8tU zN%@?~vXsR>ej=`44uy8t#Xd=i zEc?K^X^%tn;pI`R8peAjYGp@+*Q{P=b9~iR&&w^3`hK!!%{a!Q!|u*|-h3HDYU07% z!*Xqj@13VTKm6cugHm#FZu7;Bg<^hTJ9o}a+SH=a*%_8MX}RUrHZ|YiErt<1`Om&D zy`p}wuW07XxoR0(Pi}R5>pRz!&EVIE1*z{{j~BKXS6s|6mT|V#@DKjv^w`48b(X+! zLGx+(6U1h#&vE~kTYpC>_Fwgs=K&?xPfRx5l*C}Q?8TbovvZF>VQ=r`t9Nw9AOJ-j2V7Fa#Cc)CKca^`ymz|rpM{9Nm?Q~ikm!7huZ?VJ8YIE_g zCsVF5b$cCc)=@jN_V2Em<4&5bLFN6=?_0U2DmtcJToJzV>Z>Dv-{l@`+oYo-GEM3q zgT`&Ao2su;r#oIyzrNsc!dtb>a{p2|P%eC^%omaH&cA8oA^}etEva8ljU43Qf>W~~~ zfu|-?rZ)GNKau@(XOYN`tBCD^a4fi;7KY+XEZ5rY?KubUeXfS6vnsjwZKuj-%l$i!V-(sR-zQK`wxmqaz5JGo`iy%hrA4&Q!t$<=R;%HgSMR?4RisxH3F zdr+h$(IcuMSu$$(+S_4FPcxks>XghAYG)S8eR#90Zn<-bf%j}pk4sZbPG77%q;Tqk z&2$y13D0KzdLgmOQnl4kRf^Z z4K;O%Bkf;YC48(LjvroVESx)G`?QOJlNXh;CYuI?IbGbzxvl?Ytn&T6Z3kWXw_W_} zJLPfGBeOeZadQ?P&h6S@`-b5p^OpqaKdyJIL=xxMe9o88Q0) daniil-berg.github.io/marshmallow-generic + +**Source Code**: github.com/daniil-berg/marshmallow-generic + +--- + +Extension for `marshmallow` to make deserialization to objects easier and improve type safety. + +The main `GenericSchema` class extends `marshmallow.Schema` making it **generic** in terms of the class that data should be deserialized to, when calling `load`/`loads`. + +With `GenericSchema` there is no need to explicitly write `post_load` hooks to initialize the object anymore. 🎉 + +If the "model" class is (for example) `User`, it just needs to be passed as the type argument, when subclassing `GenericSchema`. Depending on whether `many` is `True` or not, the output of the `load`/`loads` method will then be automatically inferred as either `User` or `list[User]` by any competent type checker. ✨ + +## Usage Example + +```python +from marshmallow import fields +from marshmallow_generic import GenericSchema + + +class User: + def __init__(self, name: str, email: str) -> None: + self.name = name + self.email = email + + def __repr__(self) -> str: + return "".format(self=self) ... +class UserSchema(GenericSchema[User]): + name = fields.Str() + email = fields.Email() + + +user_data = {"name": "Monty", "email": "monty@python.org"} +schema = UserSchema() +single_user = schema.load(user_data) +print(single_user) # + +json_data = '''[ + {"name": "Monty", "email": "monty@python.org"}, + {"name": "Ronnie", "email": "ronnie@stones.com"} +]''' +multiple_users = schema.loads(json_data, many=True) +print(multiple_users) # [, ] +``` + +Adding `reveal_type(single_user)` and `reveal_type(multiple_users)` at the bottom and running that code through `mypy` would yield the following output: + +``` +# note: Revealed type is "User" +# note: Revealed type is "builtins.list[User]" +``` + +With the regular `marshmallow.Schema`, the output of `mypy` would instead be this: + +``` +# note: Revealed type is "Any" +# note: Revealed type is "Any" +``` + +This also means your IDE will be able to infer the types and thus provide useful auto-suggestions for the loaded objects. 👨‍💻 + +Here is PyCharm with the example from above: + +![Image title](http://daniil-berg.github.io/marshmallow-generic/img/ide_suggestion_user.png) + ## Installation `pip install marshmallow-generic` ## Dependencies -Python Version ..., OS ... +Python Version `3.9+` and `marshmallow` (duh) diff --git a/mkdocs.yaml b/mkdocs.yaml index 5a41e3d..2b927c5 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -26,13 +26,28 @@ extra_css: plugins: - search - - mkdocstrings + - mkdocstrings: + handlers: + python: + options: + show_source: false + show_root_toc_entry: false + import: + - https://marshmallow.readthedocs.io/en/stable/objects.inv markdown_extensions: - admonition - codehilite - extra - pymdownx.superfences + - toc: + permalink: true + +watch: + - src nav: - Home: index.md + - 'API Reference': + - api_reference/schema.md + - api_reference/decorators.md diff --git a/pyproject.toml b/pyproject.toml index 9086c39..5182983 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -120,6 +120,7 @@ ignore = [ "D203", # 1 blank line required before class docstring -> D211 is better "D212", # Multi-line docstring summary should start at the first line -> ugly, D212 is better "D401", # First line of docstring should be in imperative mood -> no, it shouldn't + "D407", # Missing dashed underline after section -> different docstring style ] [tool.ruff.per-file-ignores] diff --git a/src/marshmallow_generic/decorators.py b/src/marshmallow_generic/decorators.py index 9d672a5..93f50b3 100644 --- a/src/marshmallow_generic/decorators.py +++ b/src/marshmallow_generic/decorators.py @@ -1,4 +1,8 @@ -"""Typed overloads for some of the `marshmallow.decorators` module.""" +""" +Typed overloads for the [`marshmallow.decorators`][marshmallow.decorators] module. + +Implements decorators as generic in terms of the decorated method types. +""" from collections.abc import Callable from typing import Any, Optional, TypeVar, overload @@ -34,9 +38,31 @@ def post_load( pass_original: bool = False, ) -> Callable[..., Any]: """ - Typed overload of the original `marshmallow.post_load` decorator function. + Register a method to invoke after deserializing an object. + Typed overload of the original [`marshmallow.post_load`] + [marshmallow.post_load] decorator function. Generic to ensure that the decorated function retains its type. Runtime behavior is unchanged. + + Receives the deserialized data and returns the processed data. + By default it receives a single object at a time, transparently handling + the `many` argument passed to the [`Schema.load`][marshmallow.Schema.load] + call. + + Args: + fn (Optional[Callable[P, R]]): + The function to decorate or `None`; if a function is supplied, + a decorated version of it is returned; if `None` the decorator + is returned with its other arguments already bound. + pass_many: + If `True`, the raw data (which may be a collection) is passed + pass_original: + If `True`, the original data (before deserializing) will be passed + as an additional argument to the method + + Returns: + (Callable[P, R]): if `fn` is passed a function + (Callable[[Callable[P, R]], Callable[P, R]]): if `fn` is `None` """ return _post_load(fn, pass_many=pass_many, pass_original=pass_original) diff --git a/src/marshmallow_generic/schema.py b/src/marshmallow_generic/schema.py index 797faac..8e68ecd 100644 --- a/src/marshmallow_generic/schema.py +++ b/src/marshmallow_generic/schema.py @@ -1,4 +1,9 @@ -"""Definition of the `GenericSchema` base class.""" +""" +Definition of the `GenericSchema` base class. + +For details about the inherited methods and attributes, see the official +documentation of [`marshmallow.Schema`][marshmallow.Schema]. +""" from collections.abc import Iterable, Mapping, Sequence from typing import TYPE_CHECKING, Any, Literal, Optional, TypeVar, Union, overload @@ -8,23 +13,58 @@ from marshmallow import Schema from ._util import GenericInsightMixin from .decorators import post_load -_T = TypeVar("_T") +Model = TypeVar("Model") -class GenericSchema(GenericInsightMixin[_T], Schema): +class GenericSchema(GenericInsightMixin[Model], Schema): """ - Schema parameterized by the class it deserializes data to. + Generic schema parameterized by a **`Model`** class. + + Data will always be deserialized to instances of that **`Model`** class. + + !!! note + The **`Model`** referred to throughout the documentation is a + **type variable**, not any concrete class. For more information about + type variables, see the "Generics" section in + [PEP 484](https://peps.python.org/pep-0484/#generics). Registers a `post_load` hook to pass validated data to the constructor - of the specified class. + of the specified **`Model`**. - Requires a specific (non-generic) class to be passed as the type argument - for deserialization to work properly. + Requires a specific (non-generic) class to be passed as the **`Model`** + type argument for deserialization to work properly: + + ```python + class Foo: # Model + ... + + class FooSchema(GenericSchema[Foo]): + ... + ``` """ @post_load - def instantiate(self, data: dict[str, Any], **_kwargs: Any) -> _T: - """Unpacks `data` into the constructor of the specified type.""" + def instantiate(self, data: dict[str, Any], **_kwargs: Any) -> Model: + """ + Unpacks `data` into the constructor of the specified **`Model`**. + + Registered as a [`@post_load`] + [marshmallow_generic.decorators.post_load] hook for the schema. + + !!! warning + You should probably **not** use this method directly; + no parsing, transformation or validation of any kind is done + in this method. The `data` passed to the **`Model`** constructor + "as is". + + Args: + data: + The validated data after deserialization; will be unpacked + into the constructor of the specified **`Model`** class. + + Returns: + Instance of the schema's **`Model`** initialized with `**data` + """ return self._get_type_arg()(**data) if TYPE_CHECKING: @@ -37,7 +77,7 @@ class GenericSchema(GenericInsightMixin[_T], Schema): many: Literal[True], partial: Union[bool, Sequence[str], set[str], None] = None, unknown: Optional[str] = None, - ) -> list[_T]: + ) -> list[Model]: ... @overload @@ -48,7 +88,7 @@ class GenericSchema(GenericInsightMixin[_T], Schema): many: Optional[Literal[False]] = None, partial: Union[bool, Sequence[str], set[str], None] = None, unknown: Optional[str] = None, - ) -> _T: + ) -> Model: ... def load( @@ -58,12 +98,40 @@ class GenericSchema(GenericInsightMixin[_T], Schema): many: Optional[bool] = None, partial: Union[bool, Sequence[str], set[str], None] = None, unknown: Optional[str] = None, - ) -> Union[list[_T], _T]: + ) -> Union[list[Model], Model]: """ - Same as `marshmallow.Schema.load` at runtime. + Deserializes data to objects of the specified **`Model`** class. + + Same as [`marshmallow.Schema.load`] + [marshmallow.schema.Schema.load] at runtime, but data will always + pass through the [`instantiate`] + [marshmallow_generic.schema.GenericSchema.instantiate] + hook after deserialization. Annotations ensure that type checkers will infer the return type - correctly based on the type argument passed to a specific subclass. + correctly based on the **`Model`** type argument of the class. + + Args: + data: + The data to deserialize + many: + Whether to deserialize `data` as a collection. If `None`, + the value for `self.many` is used. + partial: + Whether to ignore missing fields and not require any + fields declared. Propagates down to [`Nested`] + [marshmallow.fields.Nested] fields as well. If its value + is an iterable, only missing fields listed in that + iterable will be ignored. Use dot delimiters to specify + nested fields. + unknown: + Whether to exclude, include, or raise an error for unknown + fields in the data. Use `EXCLUDE`, `INCLUDE` or `RAISE`. + If `None`, the value for `self.unknown` is used. + + Returns: + (Model): if `many` is set to `False` + (list[Model]): if `many` is set to `True` """ ... @@ -76,7 +144,7 @@ class GenericSchema(GenericInsightMixin[_T], Schema): partial: Union[bool, Sequence[str], set[str], None] = None, unknown: Optional[str] = None, **kwargs: Any, - ) -> list[_T]: + ) -> list[Model]: ... @overload @@ -88,7 +156,7 @@ class GenericSchema(GenericInsightMixin[_T], Schema): partial: Union[bool, Sequence[str], set[str], None] = None, unknown: Optional[str] = None, **kwargs: Any, - ) -> _T: + ) -> Model: ... def loads( @@ -99,11 +167,41 @@ class GenericSchema(GenericInsightMixin[_T], Schema): partial: Union[bool, Sequence[str], set[str], None] = None, unknown: Optional[str] = None, **kwargs: Any, - ) -> Union[list[_T], _T]: + ) -> Union[list[Model], Model]: """ - Same as `marshmallow.Schema.loads` at runtime. + Deserializes data to objects of the specified **`Model`** class. + + Same as [`marshmallow.Schema.loads`] + [marshmallow.schema.Schema.loads] at runtime, but data will always + pass through the [`instantiate`] + [marshmallow_generic.schema.GenericSchema.instantiate] + hook after deserialization. Annotations ensure that type checkers will infer the return type - correctly based on the type argument passed to a specific subclass. + correctly based on the **`Model`** type argument of the class. + + Args: + json_data: + A JSON string of the data to deserialize + many: + Whether to deserialize `data` as a collection. If `None`, + the value for `self.many` is used. + partial: + Whether to ignore missing fields and not require any + fields declared. Propagates down to [`Nested`] + [marshmallow.fields.Nested] fields as well. If its value + is an iterable, only missing fields listed in that + iterable will be ignored. Use dot delimiters to specify + nested fields. + unknown: + Whether to exclude, include, or raise an error for unknown + fields in the data. Use `EXCLUDE`, `INCLUDE` or `RAISE`. + If `None`, the value for `self.unknown` is used. + **kwargs: + Passed to the JSON decoder + + Returns: + (Model): if `many` is set to `False` + (list[Model]): if `many` is set to `True` """ ...