From bf978601babf74d51380d335815a9519d40d2516 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Fri, 4 Sep 2020 23:19:32 -0400 Subject: [PATCH] Refactor services miniapps (#291) * Refactor services mini app with single container * Make few little fixes to single container app * Update requirements.txt for single container example * Refactor multiple containers example * Add single container docs page * Create multiple containers page --- .../bundles_miniapp.rst | 0 .../chained_factories.rst | 0 .../factory_of_factories.rst | 0 docs/examples-other/index.rst | 22 ++++ .../password_hashing_miniapp.rst | 0 .../use_cases_miniapp.rst | 0 .../application-multiple-containers.rst | 87 ++++++++++++++ .../examples/application-single-container.rst | 93 +++++++++++++++ docs/examples/images/application.png | Bin 0 -> 67219 bytes docs/examples/index.rst | 22 ++-- docs/examples/services_miniapp_v1.rst | 73 ------------ docs/examples/services_miniapp_v2.rst | 73 ------------ docs/images/miniapps/services/classes.png | Bin 49929 -> 0 bytes docs/index.rst | 4 +- docs/introduction/di_in_python.rst | 109 +++++++++--------- docs/introduction/index.rst | 1 + docs/{main => introduction}/installation.rst | 2 +- docs/introduction/what_is_di.rst | 17 +-- .../README.rst | 29 +++++ .../config.yml | 30 +++++ .../example/__init__.py | 0 .../example/__main__.py | 24 ++++ .../example/containers.py | 80 +++++++++++++ .../example/services.py | 56 +++++++++ .../requirements.txt | 3 + .../application-single-container/README.rst | 29 +++++ .../application-single-container/config.ini | 9 ++ .../example/__init__.py | 0 .../example/__main__.py | 24 ++++ .../example/containers.py | 52 +++++++++ .../example/services.py | 56 +++++++++ .../application-single-container/logging.ini | 21 ++++ .../requirements.txt | 3 + examples/miniapps/services_v1/README.rst | 8 -- examples/miniapps/services_v1/containers.py | 58 ---------- examples/miniapps/services_v1/example/main.py | 8 -- .../miniapps/services_v1/example/services.py | 50 -------- examples/miniapps/services_v1/run.py | 20 ---- examples/miniapps/services_v2/README.rst | 8 -- examples/miniapps/services_v2/container.py | 57 --------- examples/miniapps/services_v2/example/main.py | 8 -- .../miniapps/services_v2/example/services.py | 50 -------- examples/miniapps/services_v2/run.py | 28 ----- 43 files changed, 695 insertions(+), 519 deletions(-) rename docs/{examples => examples-other}/bundles_miniapp.rst (100%) rename docs/{examples => examples-other}/chained_factories.rst (100%) rename docs/{examples => examples-other}/factory_of_factories.rst (100%) create mode 100644 docs/examples-other/index.rst rename docs/{examples => examples-other}/password_hashing_miniapp.rst (100%) rename docs/{examples => examples-other}/use_cases_miniapp.rst (100%) create mode 100644 docs/examples/application-multiple-containers.rst create mode 100644 docs/examples/application-single-container.rst create mode 100644 docs/examples/images/application.png delete mode 100644 docs/examples/services_miniapp_v1.rst delete mode 100644 docs/examples/services_miniapp_v2.rst delete mode 100644 docs/images/miniapps/services/classes.png rename docs/{main => introduction}/installation.rst (98%) create mode 100644 examples/miniapps/application-multiple-containers/README.rst create mode 100644 examples/miniapps/application-multiple-containers/config.yml rename examples/miniapps/{services_v1 => application-multiple-containers}/example/__init__.py (100%) create mode 100644 examples/miniapps/application-multiple-containers/example/__main__.py create mode 100644 examples/miniapps/application-multiple-containers/example/containers.py create mode 100644 examples/miniapps/application-multiple-containers/example/services.py create mode 100644 examples/miniapps/application-multiple-containers/requirements.txt create mode 100644 examples/miniapps/application-single-container/README.rst create mode 100644 examples/miniapps/application-single-container/config.ini rename examples/miniapps/{services_v2 => application-single-container}/example/__init__.py (100%) create mode 100644 examples/miniapps/application-single-container/example/__main__.py create mode 100644 examples/miniapps/application-single-container/example/containers.py create mode 100644 examples/miniapps/application-single-container/example/services.py create mode 100644 examples/miniapps/application-single-container/logging.ini create mode 100644 examples/miniapps/application-single-container/requirements.txt delete mode 100644 examples/miniapps/services_v1/README.rst delete mode 100644 examples/miniapps/services_v1/containers.py delete mode 100644 examples/miniapps/services_v1/example/main.py delete mode 100644 examples/miniapps/services_v1/example/services.py delete mode 100644 examples/miniapps/services_v1/run.py delete mode 100644 examples/miniapps/services_v2/README.rst delete mode 100644 examples/miniapps/services_v2/container.py delete mode 100644 examples/miniapps/services_v2/example/main.py delete mode 100644 examples/miniapps/services_v2/example/services.py delete mode 100644 examples/miniapps/services_v2/run.py diff --git a/docs/examples/bundles_miniapp.rst b/docs/examples-other/bundles_miniapp.rst similarity index 100% rename from docs/examples/bundles_miniapp.rst rename to docs/examples-other/bundles_miniapp.rst diff --git a/docs/examples/chained_factories.rst b/docs/examples-other/chained_factories.rst similarity index 100% rename from docs/examples/chained_factories.rst rename to docs/examples-other/chained_factories.rst diff --git a/docs/examples/factory_of_factories.rst b/docs/examples-other/factory_of_factories.rst similarity index 100% rename from docs/examples/factory_of_factories.rst rename to docs/examples-other/factory_of_factories.rst diff --git a/docs/examples-other/index.rst b/docs/examples-other/index.rst new file mode 100644 index 00000000..e99ec099 --- /dev/null +++ b/docs/examples-other/index.rst @@ -0,0 +1,22 @@ +Other examples +============== + +.. meta:: + :keywords: Python,DI,Dependency injection,IoC,Inversion of Control + :description: Current section of documentation is designed to provide + several example mini applications that are built on the top + of inversion of control principle and powered by + "Dependency Injector" framework. + +Current section of documentation is designed to provide several example mini +applications that are built according to the inversion of control principle +and powered by *Dependency Injector* framework. + +.. toctree:: + :maxdepth: 2 + + bundles_miniapp + use_cases_miniapp + password_hashing_miniapp + chained_factories + factory_of_factories diff --git a/docs/examples/password_hashing_miniapp.rst b/docs/examples-other/password_hashing_miniapp.rst similarity index 100% rename from docs/examples/password_hashing_miniapp.rst rename to docs/examples-other/password_hashing_miniapp.rst diff --git a/docs/examples/use_cases_miniapp.rst b/docs/examples-other/use_cases_miniapp.rst similarity index 100% rename from docs/examples/use_cases_miniapp.rst rename to docs/examples-other/use_cases_miniapp.rst diff --git a/docs/examples/application-multiple-containers.rst b/docs/examples/application-multiple-containers.rst new file mode 100644 index 00000000..d49f999a --- /dev/null +++ b/docs/examples/application-multiple-containers.rst @@ -0,0 +1,87 @@ +.. _application-multiple-containers: + +Application example (multiple containers) +========================================= + +.. meta:: + :keywords: Python,Dependency Injection,Inversion of Control,Container,Example,Application, + Framework,AWS,boto3,client + :description: This example shows how you can create an application using multiple declarative + containers. We build an example Python micro application following the dependency + injection principle. It consists from several services with a domain logic that + have dependencies on database & AWS S3. + +This example shows how you can create an application using multiple declarative containers. Using +multiple declarative containers is a good choice for a large application. For +building a moderate or a small size application refer to :ref:`application-single-container`. + +We build an example micro application following the dependency injection principle. It consists +of several services with a domain logic. The services have dependencies on database & AWS S3. + +.. image:: images/application.png + :width: 100% + :align: center + +Start from the scratch or jump to the section: + +.. contents:: + :local: + :backlinks: none + +You can find the source code and instructions for running on the `Github `_. + +Application structure +--------------------- + +Application consists of an ``example`` package, a configuration file and a ``requirements.txt`` +file. + +.. code-block:: bash + + ./ + ├── example/ + │ ├── __init__.py + │ ├── __main__.py + │ ├── containers.py + │ └── services.py + ├── config.yml + └── requirements.txt + +Containers +---------- + +Listing of the ``example/containers.py``: + +.. literalinclude:: ../../examples/miniapps/application-multiple-containers/example/containers.py + :language: python + +Main module +----------- + +Listing of the ``example/__main__.py``: + +.. literalinclude:: ../../examples/miniapps/application-multiple-containers/example/__main__.py + :language: python + +Services +-------- + +Listing of the ``example/services.py``: + +.. literalinclude:: ../../examples/miniapps/application-multiple-containers/example/services.py + :language: python + +Configuration +------------- + +Listing of the ``config.yml``: + +.. literalinclude:: ../../examples/miniapps/application-multiple-containers/config.yml + :language: yaml + +Run the application +------------------- + +You can find the source code and instructions for running on the `Github `_. + +.. disqus:: \ No newline at end of file diff --git a/docs/examples/application-single-container.rst b/docs/examples/application-single-container.rst new file mode 100644 index 00000000..2f6c1c42 --- /dev/null +++ b/docs/examples/application-single-container.rst @@ -0,0 +1,93 @@ +.. _application-single-container: + +Application example (single container) +====================================== + +.. meta:: + :keywords: Python,Dependency Injection,Inversion of Control,Container,Example,Application, + Framework,AWS,boto3,client + :description: This example shows how you can create an application using a single declarative + container. We build an example Python micro application following the dependency + injection principle. It consists from several services with a domain logic that + have dependencies on database & AWS S3. + +This example shows how you can create an application using a single declarative container. Using +a single declarative container is a good choice for small or moderate size application. For +building a large application refer to :ref:`application-multiple-containers`. + +We build an example micro application following the dependency injection principle. It consists +of several services with a domain logic. The services have dependencies on database & AWS S3. + +.. image:: images/application.png + :width: 100% + :align: center + +Start from the scratch or jump to the section: + +.. contents:: + :local: + :backlinks: none + +You can find the source code and instructions for running on the `Github `_. + +Application structure +--------------------- + +Application consists of an ``example`` package, several configuration files and a +``requirements.txt`` file. + +.. code-block:: bash + + ./ + ├── example/ + │ ├── __init__.py + │ ├── __main__.py + │ ├── containers.py + │ └── services.py + ├── config.ini + ├── logging.ini + └── requirements.txt + +Container +--------- + +Listing of the ``example/containers.py``: + +.. literalinclude:: ../../examples/miniapps/application-single-container/example/containers.py + :language: python + +Main module +----------- + +Listing of the ``example/__main__.py``: + +.. literalinclude:: ../../examples/miniapps/application-single-container/example/__main__.py + :language: python + +Services +-------- + +Listing of the ``example/services.py``: + +.. literalinclude:: ../../examples/miniapps/application-single-container/example/services.py + :language: python + +Configuration +------------- + +Listing of the ``config.ini``: + +.. literalinclude:: ../../examples/miniapps/application-single-container/config.ini + :language: ini + +Listing of the ``logging.ini``: + +.. literalinclude:: ../../examples/miniapps/application-single-container/logging.ini + :language: ini + +Run the application +------------------- + +You can find the source code and instructions for running on the `Github `_. + +.. disqus:: diff --git a/docs/examples/images/application.png b/docs/examples/images/application.png new file mode 100644 index 0000000000000000000000000000000000000000..1ef8f7bee9272a95f4e30654e25d50839740b8b8 GIT binary patch literal 67219 zcmeFZXH=726E+$YL`A>~Dnjh^VgmsIv0|7|Z4hWH2?W|Szj-6@&G9^V0q`FuOkGnQ1j-8LSv2PYJ|BE=^|mGm@S^JYRLsM11=3XJt!b3m|a*hG_^2I zW4e1HJP}@Dp`i_ga>m@ir3)(0J?z=E{*k_hy@Gqb;WHVt-6`+GJ4ux?QsPGX+HCr$ zjI`8qFHZ)O)!H)%eFC4MGg&Man?)b#YqYk!&t9BivshBn(k|`@1j0*3T5@Kb%w{e2 z4h~C5NskP6g$4VvX@hJQ6S!>#_@j2e4E#q>P;jrH!0_;JX?5Mm@Nh$IL3U=6wdI3F zCh%m22g2()!Y%BdpzQ4IR3zHSK)0x*Y;b7!TS>vUV(h{qi@i9@W{fv9G+exRacpvW zfkvO192psCEh#Mv3waftkbGOupqnyDAhyYf?Y6!rmlATVv+Vx2H2p9ybt6qt2HA$) z`E>8@t(TNV8gfi$n&H{;IwL<|&utZz&Yr$&sKdd*F+E1QbW!CXSh$1q{Yy&p)~#FY zZDDeaMmd#~_$Q<;Cr1*b?j!B-mcE>NLF_JL; zwqfc`aR)pijc=DgXyu@1`ggIj=P%rSWcVz=@qPXS|5(i@u6^S(Uki$5BWo2b8 zEiLKk>6UUgPN14M9dVjr3q?I>kxZTRB#-2R&ZJ+Fpw}-?}wqfnz{$u+r zW3_@&m{e%W_fW~PeX_4FXIbbRz5FB!_a$lsY1U?T^6}&*6jWT|Wp1{MJX8S2Sce1-W=nTrdstbEOE|7QW!IFr6 z6!4bUtb7r85keuC&GIjqvElE)S3%eT2nxPW$rv>cVWHfc+yrwgMn>?H5XYIFOcav{ zwl3fYflPQ*7i|_5Ort1vc$!V&pwio)n+rQ|>;Zg#-!Xb8t9X{N7X zZWiCs#eunZ6#WZf?(`h1)G-@YyCc!&9tf29YtdO)_U)Z`}Iem^DIZQY(2ZX zPJY!`#UP<2)G~KwgSPW|y$&8du16Bg`U|U(zjoo(tq< zfR$&9B6nfGK{yj|GRIEWjNz>q$LMb<^Rgh2j)%aawy7xmE&DA+=R#S*Jk{9q6^k3$ zSpsa4$L_q_SmxwkUUsUp%C>U;Tm!XbHFA`6Npr1 zdF*21V_&A%W&A-jy>jkG_+>l@G*O+8x5oyX`tRKTasJ!XjhWAs9N^v2_Feh+YILw6 z@8KMvp@NM>E@SV|(8%o}?O1+JH=W4?0^Ko=sRL%Y zZvcMiTdXt)WI9NRl^z5>C>k$6-X8mdbm8Ui4~oVuSG=}c0*>hCQx-C?I9B>YG=>Y5 zNO6zDK>1H@&f?y;{3;O(MhTX3`vN9i70uk){*=iA1oOoaF$f3a0D0Y{zUq$}(k0qJ zKp?rnVnB}X;fF4g?oJ%Mdd}8E3-ebPK)#vre{#X|2l zdiaCoUmydwfZy@J%%hUy_G2Lh(%%Z= zNgt!oT%d&?opJjQj3UX#j3KAQT;{M~w#wY#ug*xkZ)@m5<#kNp%$dCIPi%;(A)?u`o_CX>7x6qfbjL4)_`H*r(2|l$SN1t+lGJBV- zHcKB%{cIj%g&^w^T4iJ!e==rz7r&K$PY}$wjdic?hE8&16vny@_lJT zbLSExZVao+xHOHS`RC1ks7#%XgZ%1vQpP~*A;IJy9$h|eBn^S6SB2~evvFlN>Nqn= z10l!mvNyi)!%1Jqy1;ls%(!~`LzSURH+d>alFg#_Ty<@y{{0`Sa$jMB6Skd!^F1EB(cb3ZaB-K_yX;? z0N8gAs2c-Sd?Pe3KaFArQ2X*1a5C-HL)W5D%WDLym=T8$cu}mb>tV$`=)qR zbKB#JscJW)1xofk5e3692Q}Twjo)*DB{f3kpfqB5yqgELRf7Qj8cIu$QB9u`zJ=GN za;issm#<|=R)$}RW4yHc=peH&>tZM{@soYz#VI3AoGj40;ORkv{Ev$4lm>8ney1xskGY_db`SJ*d5ChJ?_S z529f;>^!x?8%~WiXbpn!S2U<~%ul0ThV`Mr#((VVtVgqK3VbkTYTO4G6B7`I65_v( z&*l+NZJJ~z?bW<~hgR8h)_dw;bvC>d%{(fmE627qupJCMCS(e%S+AFmX{OuaR&4r zr0({FR~_Ll(Gc*rD(w8MUe$z)P&GHIlKyBSkD$MG-JtMK_$Q@nxQRE_a^C2Ycj@pN z3#8D*77 z^Q2%tSTr~;-oVr?=cb+aobaT&O%y^o+?=zYo+VgvTL1>U-_$@c>KUqmi)kOYa1asK zyy$3QbQUqi@(*Ai6Orn$d3r#dcCLT(WvH>d>Gl}PWS)j+hz&8X%q?@~A0GyFBS@*P zbIGwv$BpT-B8qO`m~Vo7lr<}4l)rOru#@SFWJ|=$8asY}8B8oivXUmUP^b!;+b3Ll zJL}xJp{UFJ2X2qJj^m4+x?y7T2-;y2*l2TFT?E;hto#@^_hagYgLZC5_`9cI{pfl< zp~;Si3tNO@s!KuBkKe|z+;wcL<#D*F%;{7ewMx~Q%)a6q37!una~w+iofQ=9)je&} z`e+|jL_Q8zt|F-^$*NRGNcSAxu`fO-hm0>>`ZR9F?=572z&)LI~i_Gb~BX?R5m0A*#qA>f; zky2d@P4;%4r<5dlvHFp00QlQcAkOi$PM6{OjUPc%Wz>GOi&A>sqdF206K==CQmcij zksgWZJ5iFxDesyM^<;K4(uYc>vgnyZ1XAHN>>Wn;r)k^U1s2R1464QCVN%#r+*@hN zFiT4-&c`GPkEpnY#iXH%A5-I+Vwc|rf0I%t4g@LYX`IXoiIwv_;VhRRD4YtUOq z(I&X+&Il=KMw?}|eq5rC|1tUSvg)Fc(F%W>2F-SoK*dAseo{LNr@PGSLm!XjWWk|Z zitXp4!QT4-WOmPzpfBG1gl)|#Py0MJvF)?nz)7K7xz9)f$a5j=MN(cH?L@1C*Fo{7 zD0_qfA6>70*8sn3_Qd&cajrAI7R-UW(1)A1(g!U~)lI-7) z*;$>}+eVEX9lDOH$o~kBpn1C;6Mg5Cr0F*3Sq=r?Dw-MXC>1{0`Xsflne+6wi^Jyy zbpipLgPl95E3^3qeH{ASl5e2D)aMgA)$lf!8qq1~Hj~F4o9%da@Y<{6?+ee#*YMqa z#hl-2qc_NdrgmBxlMh8rdh(;^o2N6T$A1JLvk=uERA{TsmlgCC%KL7NMMk5mytmd?bl#MR&n73_z>9IT3WSbF&<9(2QHv8E8H;0?5Awn7-$=ub8_3n5~L~C z65A7QRWCI04!R3{RqF16LvGQ>plRW~?jfE^h`qAY{86=A3Zf9D{Tin4di{8W-0!&C zI4Y8ZRdeKZjV)&X8H*a?Wu369vfcI>M1d+ zpko(+Oj8wH>pb*XHkBiJl?^A1XMsVT)ARV$9}_FiUt~6sc|(=)4y_l|)!J!T9X(e1 zAVoC2W`4mVlQf*Mucst3x$4Bvn`NF0J0D)&6>Ynek+?vx)I!%;8eG#`u=@5ruDV;F z;Sp!e;)MJvH$l{D!4B@<$knhb3P-bPlRcQ-;29H=gy6RnQ357EURg4(Q$gCb)wxB= zOd;~|RZX!Zn6qZ6DN;?1zUefjmFja+ z(A|b5Szs_BXI$c~oZv9Gfik4_?WbcbNI^ky8{}IY3;ap0e+iUjo(W9c#&PtJArMFz zGf9u)A-Ze=cdql_h;$CB~IQF;hk>fonL`*Od33xi4)1y z+I9YW--ib6tpLJ!%xP>8vL~IcH$ypPfF*;u`?tm)VRVI}3x)5<85g~eayvPDD*36e z^Xqskt19)nD62WmYg#`g$~N0$zK#U(E9P`&xes8s7=)zAw}1t-j(sq=64F4Gy6ah&wt5Aip-RH^BHl~5n^H5=uG*$ zk6X2ZE5`RgSt$CM`Ejz8VW+$zgnsbTVdwnYdCb;^noWm8pK?O?npeF{pGE7*rb$AK z4*wecvYxP&oL?e+3LC0LA}VY z?a_!*s6m3XEIE|-d4QfIxvk$G9IhqC4R*4mCEC8?{Rppj=-#kpl<1XD!|D2Dv^zGp z+ren`p7NeE`bMOpT~Y>1(x6C?Erc^C+;%&Ue#boTKGXlcBg6G450rdE^uWX3&7{tN z3K+3kLtN@(1{+5aU>rlNmZ3!;Y2&UW|^>s~Z# z%iD*Vx_b`DE{~yi2UYAqpCVItgPC`SpBcL`6w*~Ni#yxXD3D^cN@+WxPJJt3R@=^8 zQtcj@>PngKuXUA#t{A>6Z%N%D5P6P~K3XtVD8o#9xWI)&?mSOCmW1K6iPpxT zJFd1!Mv^?Ln`CnxYj9~;&%7|(c`NFc8x46zGX|sgDT?_$^=8C)8a)hh2WwQ-5u@y# zqHXYiB(ZS^KdKP(go5cwqvp>Dg`zJD>0sr`h>FIRgYXE%Cx{{2F$*4nNf@z4&mv)N z9;D+p_7vPq$d76oOmnD5%~YxC@!fqknlk%VgfP}d%*mh0EHY#`JWZUZqdp$5Re{Ao zHaoqCry-#-j5ktzWq?gWeN$3ZC8Ne>4bBCebm-wW9Gvo@)7+ z{9pQYcMSH%c+4JFdpl7-n{Ye$;}24Y>~)P}jXfHAgh%>gMWd&_EFvo_giHAdTHP{g zh$oLyUkG^|QE*bNqUxE{wb>uh!9LnpEpU8c8A|xJsWsP$_TMK-IP2Gm6Lqk%6780( z%1~{d0DC8KBYKvsKmxumG2r;7hJezJTO(|z>|Mi!+0Fg@*1lBBBkr~c`%|rI>5oG6 zjEdx0>ZnUSj%1mm7SI?Kb(eD_xl(#V-IpTVc$?#+`KS&fR*Hh?CQ8;^*kYp_>9h=TL!!|j_&8(j)t3JNHqlVC_p8%}O>{BzH zR?i3BLd2&xaq(10T+7W(v?@nH1tZ5`)x9v~cC%ASZknV>8Ai%BgAdTO*wi8ht2RSc z?SoSiyna)-^vxwi;9*&FxnG~Jl7EAgq;dZYOxwfP8&=7B>}f=jV0)@;h;b29~mBGT&V0`VDEQy{f%O$g;QU zclM4rNQ0}%ZHP??40%hUtc*w7&Qgp3;rbptob~B7a%NZyx*!TlB)F74mE|=HHTa%pa1N0(Bui^}` z*E9Pg1Eixb)rt)E9`k>Hle;hVa7PU9&mwo)V^_lISHq+%#QSsFcHUx z^n=_u&iooRY$_--!ssbi#>kF0`Vmat)gGUTH}rg^x~Gshp4K@*`>Lmp(yZVJ&)7A! zrEmvZl2ypfKRy7y0=(49`h#dVDrUG40ET-6?FP*QQeQ+U4{F~~yIEA~KWt!!nC_1A zwy9t|Ayo_TTb{0NvFBj>eF0Wx#dC_c^aB*U*@V|y5F`Flwn^OY1H5@x+^}N*;s7rf z`yOidRA@GrYxNZ~*@&-W(0&hP-2MoufVWKzf$yY7*ec`)t zG%C{+@snp>yuec`M$U|x)VDDLrPSC@JAHxmVeIQY*by!vWoLT^Uz=<`J`~$u^L)=t zm)-F6LqzWQ{cK9LzDi^4mC+abhlPl3it~Pm=yv58-J%%f%0Oi<{(9H$C`$fylf4qC z=*i0};!6ALL?-%%3a}e_^XZVQFVKElR;n>@>jtc0sOJ+MWRYu*0^Wu4H9%0iW?~^r0IVq9~HX@8mW~H;LjgIT$vdd zDf)zHzUE(t!M5wiH?*=ZOfGIkFJ5_Y%2~jsY8(~cS2%yoqyYRW)p;;pjd8xbb5DTs zf{a;JcJ|w_*?#|#S|i;rz28bil^Zv=-!-UFaehQo?hJnINZHjtUd?vW(VFNm(k44y zd^RCtg%a{CM5cY?%7&*Bp14$IO}U6hWebbhWA%U_Qvy_aMls*@#r09p>C;F$6U7jX0js*pCH8sLB8a&)_kf}pr`*spM z9;eBwxlZ=PMdf)^{xVg_ekz(9e>+Xc-mS9Z*~GydYRlruO(YEo7INWUH%Smh&IjN5 zC$6|Xn!JBhixm(Z(;LSK`hoBpdDfk=xJxw`mq(LBP-U1ew&Bf)dOf8kq!yhd14p%k zoPaTPQ@%i2oSuk10TQ(cU7o=cVak1M)f{4J8k##E*Ev6YD3a^Yg9 zp!~~)o_E0-+D41N_F2YIT=BYPrwQX+#46Kk*_aum^MEKfnqQA;dIGO*RU)#p0WDn# z1!`{t{mw;UxOvx1lPG!Om73%2({S&&7+Bbic0gX2VFr@i*NeAOE0UPkW1xF77D+B>zV0z)KCx1v04 z6v=f%@0WIl4DT<~c^?X)l@RyBRKPyKXCu(|1tbO^KfzOU@F__~WytT^uef7bY6;`c zKG5;1kMTk>i?2-$UhzC7y+9}hU!QSsV$kln@8o1oUd^M@F1r~2RJ&tSaNuV0f`xl{ zo6`=}>uU7ggRY|%PV+ra&Uq}9sVliEN6*bHoZD9;3=z>T6{;9cr@G8d_9j0)>^vl> z(O9Oq5AqpP0GqV(ArPdOEoP|_m1fmg$X=Zs`T@J}8654dwPgb?rnoD0A}*1`U|&jK^2VKjhu*Sp`_LUpl<^I4}DA z>$s1-oXMl*|J3faa4vWqc6$Gn+?TAgTa*`02mPpNoU!*GNsSi<6RZ%&aQ z-Z~8P!Bsy`faQoB%hBX(ByU3Evx0z`nOjchQGBHz*c0SuzPRk2OQxNQfJj|>QV^sr zBO-Wsvasi?>6@yto-#ud-9v7w(8=dxqo<~{@e`k@KUA)Em-K0kM4D}x`{M9s;ei{) z*w;M)6PS)LoNIq)yhUnG{pr?DNhQ-*kK8HhYj;V33~o0cw6yXNx1WJaM7d}E!$%1b z4<~%A{iLDFH>zA*&cFIKBZYJ|wmqqQ%k@jgiRUfb{yOSlYGAJ|g4F zDF@)YwU3_5WqW@2g=AACo~nzJ#f~M9#9mSmA?Qhm95eNz9hA8M(!f*ku5`V&pJqFFF4gb+JC5$E%-VtAJG)dLKJ+s=#UVG z>Hd`9c`WWCsgx6_joppM1X|&fkGE<-VF%nu?M+60*++Llio_0FGi6*=d5WV?l@Fq} zQVMgJpIrrJdY!ihKVU#F6wWsGRrslt6jdJI)8xZ)cedg+2NG%BfHH9SM%Z$)#gi_3$@$z+AX!KdXQhU&y#F!He9b2tP9r9tc2Cy*szEw(1tDx|##dL0gFVfNS0{|evZR$t!vgx= z^Eb*KQ+j$JSUs23qNKhgi9z@nGBAYJ)0`&weoQv9W2`WH*rP?D`s@5i^AywtQjRoD zQ&4;px={FbURCzo(6D`kM^?4W4v5M85VdIj$yoVlVupd$%wh*BFtIhCO@fR6V(f`D zZ_ic3xkV^54l54LGW4{pJMnlC4?pESO0%AGwUm3!OA~wb^xQ@_`Mn2%%HVIb(;hm% zxiM{by5Q!iCy}3TJ-LF}C^(!P_fhDK*x-GJx>C9vF2>GIuZmPQY65o}oUy4FCl6WJ zIlQdTYkxX-&j1rv+`g<^ph2e!3%Fd3hJc6F-aQQjI;v03lU~5|1O*cGTu;8^%T?0N z5!a=hTC^>2D*1>0@)p_b_@GxXtXoI}rgoKax?cO_nf*7ZG@cs8K<@WXzdMn633;vESx(Th#W#V7|LFu2F)*Kl@!p!bR0f5U2&7m$iW>9k`cr zh;|=1ev5RoUA4?MFa?-FS}+`cNjckF!o6V(EqpWX;W1bDkK-ZAj3SeVlKinI{S{jV zsEX2c_2za|+dY0Ii_D(J&($f9UJY4Tx{S=rDRa2Rsu+xanoc8s+*o9kf@5{$pI-&I zIKC8uu3&d|!H<&Kp!wB4vV(q3jXeE9!G5?4pQFr9m~-Ks$b}INjshL*$fr`cuy5vl zpuj*NO_s`)!o8e!CWP0f3}-`?V-cZ66lw-fxb%;umNQh$m>6yaw#KPON{AUdY=Xut z(%j{5lfk_HWhUAp5Rte6bQU;eSnia#%jhKhYuBAT$hqn55o2wSsA3}ed!eUzP6a@f z4Um(*-cv_S)HgK^ES040?Jvn{L&62-D+?7r?)bScd~7mZ#2?eY>G(7_@FwBmjmnQ{ zJATADsDOhuUCZEmm>^O|k~ue@+Z7`+J8XXXjcq8UZ}ICzor%xt>SE4(OEQnU(zW%Z zfoib+xa()+C?jjk^pv%)WF_!ytCPNRZ-^bQSsCo4PX~MV{hIK7f{lc;9;A-=%!h3^ zeMv|{2MUfVnZ*{@f?rK}bthjk1|<+4f|UZ7g1wn~#TN*&g?(I{R&NDu#WPxzRW zgwM4$%&j<)hv34!Zy1=Dg*U6^J7fh~TC{k*URXz2 z<;s_uE1g#+M~$BCNO8j6x5^hWaE&mSN)0QqaGEvx6*@UMgSrC4Gz*m0y>e4%<33Cl zOaB0e@n_0iiIF8QY}fYxUIu^eR|P-kau?VAAlAW7?)eIc>9TM(Eil%>)<$1`)G(g! z_0c*iqep6=$jb+(>39v;HHA%8`*Tu9U~R<3lplO@!kn{f()&51cVQLx;ALREjv z^5O`$3aQ<(4gK<*rcI#^wFUpeVwS`qNCUhpa+tM8?t(2SwRW>q&%G)WP%~uN0orT+;h@BmNb~ecE?A|YKKEjZAUOv>O zBaW|-Rit}zMejV#SZ`&5Dtw;PkCq1z;x)MK&nr|;{J^>3L3_T$wnF8>)iyA9!dF1SLd744@J1iOXECD~{jq%%Uj~&`tEno$BglzzKf#kF8!l?QSLgIS%4p0;v#ort0gGrvR#&bQ{ zNois$)s7Zs9L8f*Dg38gp=cx|N!t|fQEwD3-Rgz*+s6%%cD6xz_EihSsgsg;S5rH= zK+vJ(CA)mpQp#k@wa+)#d)w+&UDQ1m0Qp8-pLxMxrL^;KVU2|YEXUvXjW-pK=WoC7 zYzjD)-&H6a1vxqgFqPgxmoa0<3xIZ%|9WMvzY$wmth8@5=JMKum0&fB#+#oK*2dUH zRWx2y5tL{;zUFT5^8kfdYd;Y6SgK4_M`nl&Pr54>xo zKB$NP^_kH!;DQsRx~C6Y$m{h)}~!YItk-B1gxkvRS`&l(fJw*ajdJjbu&PMdaY>ez_p^hrKmaz z%w-Ok%W6Fj$4&Cg99tUBa{P1tb2Jndfu!mreEm`MN?!&bkqt?$2>#6yM+3;lw?PAb zPj)#H{U$%Hk27@#V00Hu{;>4iaOD%fYLD`heG6YR8uNXbRk7;u(jWj0+T=+psT6=IDrXMiHv%q3qU1!Vdc>Dc7 zO>t@Uo`0FW6ySba{VGN5FAEm2*nXe?z$@DtKWOx$e@Oo!cYYg0f-_HJ_w}su%rU3S&7p#v{IrTdZ~(>tF?rSHFOwDT?dP~~^T;|VSk53eKjqrDdeu_JbcIUnw~iBu`Ir-V&}X|r$on-Uvc^ST2c`zh z`>C5YXX$QSzwdtr2c)e3Gq@aCrGGKF4T;CZjGL${)q<6UYC_zsBUmlqIPJU+VTflo zH2DFDe4YM2wDtFQ%s;%`o6OB75az}qV2=QhRcY~#B6$uu5ihSVBiwkkb=^{u)Dmw5 zBuQ3(W=UB21f15dlPk3UKM`*%;QI#u0Lt+g=n70+TL{1|04vVTSwPOVwyOV^v;DuE zv#n;3?VXC`@5qZ=cpKgB1G`h2X32tazd`;|({g2Z$!gZKoc^w6Y`STSJRa7In zV%m9|4bGumVMwA{;NbzF1hexdkb6Jobf|EI3JH|Cm-7XviWgW7<=gyRF1!5^Lsknu z|4-9k0AP3DliOR{?xb31l^G(SJNO&Eb0VDo6#xR2iKRN)|4It0rOE%*u>M!W^7#K& z4a=OQWdhrhIQ$ar#{m>MS1YGaVRtz%5{))I+OG}y4tMbZh`4K++f(PeoEw*Z+sYm@ zubGY6BHi~#5|^ME(ADG^334TTl4Kf6CXr3AMm+jUz!H504{zu(f zc&AqDCUv&09Or(R!|5QiVlZ0l?8x9cCOYQ4F|m7m^3?843vOKd(md5)1HH0i%JU}9 zTN3*Z!Is)8YtX12J^9n|El9yR_^C{E_c|aSY-5j(Z;YP8J9&Vlv3X^DymIBsT_m^l z@@z2T`o>lYmDLZ)Q{@EN{Z-iZ%3svmzNkX{4(d7%kRn!Uv^~pD-xRzFBo1sAy+;tZ{e!gO z(|5Jk1|R7+h~GKR69B&M9FXz?o#Rj6A;$ucc02a{Jk1*aE6u(ou%Eki80@dd7~~NA zhoctr-=$lg;+M0TkJ){}-E5{1@%x7JBTMPda>l*k(`6UhFRH#ZQyjQi(_x8Kx6*n6 zHFYQWUST!n&DN17n~afCn-F0*g*m$co1~B2uTM>>Dz()97JcSJJ1Xlj|a;fW-yS!o4vg7 z%4afhGQL1*2)J{LESS+dYQ?H6{c7s$aTWAs`rk@KTsk?3z1Y!&k2cetn(R3uGI+~* z$G_ABGVShvfQT%tMSx$7tJ!(zfH>E+bw0|DX80zc((B>mT20;SMbd>AXs*QMu9YEZ+Wn!&M~3{2*@QiXB=%mjOLGdyVE*%a*4t@b zFYxgL6srE#&N0OPD^xWH4=<3!#9E7kBR31NyM;o%(J_V)FuOEB-^_8R2Vl#MHh~T~ zJptK1fJOzE=f2$bXzTi>cP}BvcXJ%;;|!Bkmk0c`>0eeg5(z{zWpDIaJkk6I}C)OA87#vfWH|4yM;JjpyIB0M1i20o4fqvT1oDW(;&yK-P&3{SQva9NxIUtJoE- zUv_$fZ}b(=K|xd1xw^DM3SgS^V4UiG(2a%l0}1R9YU$0L?Dy}6K8&f^dQ^g{X=~0A zboFvnaFy+jeO+fDYvnOe7D7Un+y$8ABT&lXT5S>Zfi=!sD!*_8czwajUW)%by5M|c z=c3~)b}fEEkllkYo>SLq-O$n6>OQVyUIP?YftDUe)3I|347|O^c93;3;KNp0f1oE9 zNgHbW)*7c?tvcBAr+}S(zl*e>?Dz2J52u{1IDy7$d93tO^YXkd4bc2b<7)Ny<*o34 z^_e%w`JuVqJ^q)(+v`NQ^v^r%E#~u9V#bM||EMmm4r1+Ohm}FB+dt513TGjzQu~U;Gb0dQ{6!R{vj^8qJq#czayBQ<-H+qHR|E0m_wXa^> z7*H7R*MAEWZ!-9M(Y*4e;4Pr6we~1aqCi4B&rbfmS3&Q7vuc*=WDXq_9_Pt0XDa7w zH-33%6Y$QvUX+vRlWPf295#zKQ3e9J&vxx1yfExu>(`HD#<|h)&R#zrE+ABMlNoYe zBZrg??kD%I--aE^5>>1HOYUl6*Sy`RNE%0*W+K-rFqzO&OS}l3`KK| zWM%I%-D(>QIq=cy&o+bbtoj0_=VHDw4dle6hvCvA z^IN7j3oNWgd-5%zZt7N+apKZXIXn1{y7$ni>7z)B&v^>aE4ddyjNn~sP^2e|oO}b?D7HO%y9}!w#fMqqf2sX)GQIvN zvRMNQNiyN?^5bso+%5ISD<5F`JWntf$oTJe#opEmH9a@xPG zPoN73waaIwkb`;@xNZVl88)b)eoU{%b8Pa0CdV!y6)vumoL&p$7c~BO^cp_K#;ji} z1ZaBYSQVqcul;YT!Hz%PYdr5vn9Eu^v}R?`h9h(CvhRP$bs2Rz{UO$}rTAU4?Bj?2 zlItq*pye-7trh}N*6S~ZPdKi&i~d&FDyGW$OAi0W-}rMH|I*B=cenb>SXTWP`tNK1+tVLj*Ztd^|Mp-3o7c^CS+2{tXzUNM zzF0rf=jUH?UF8?0|0Sxm2Gie0x8|t+c=EY50A&4_9RAY@EtUNLn#P~R52s!06Yv4J zE-RJ)KNcsjgnqZ{{-lm`_a|-sCA#id zcLU3EU1rn#xw3iJ^Sbwc$#t~{`uFl$LoE4!8Qm%=>2HOt61&>}wwZrxCI2~%6-NFF zziaIH`UI8$-%3aM&&3&!AB4YM?mYi3t5wF$-_C6{Hverbt9{bHuT`m>^RZe!6w1Sb zaV)==&J5VTuKSLEX_p>e_n6CaT_(+8lS?<4bI3O>QAooxmcRG?=drKBM7iKBn>a=| zUE1OyR=VM-$F}&VOv{u#fG<2xdIx0F7sd=iJXnM$AVixdEop#V+Meq@>Ql=-Z^F*; znICV1W<$g*Wk*pce*o0WhKL-$f&@Q>%RWM6C|IWWm7q*9yrln3V-I?JK_Vg`=l zsl>R(?zeQgKL_b^3Y^A{b(%U$zP-42N;Y~Qmz%y0yA zAPv(&@S3fys{Z_{=|JsN`lY+DAp53 z>*j86hz8PfOYMn6wO=R3d;sM>1CE!_>5R2O!lCo8#y5)gmq}9r4VmKT9 zCDKENz{J;h+De=yf{17be`mzPC#2Rw0M`f zkkZQvtDSaEeL;63rodJ7VI-`Fv*{m01VH36Fib-;`yG2?y!jV<^0#MvnY@9-z=}@ACmIj>S}umZ#bgAb&zQIM*hsFrD@|9T=bE&dEM0~5-DS&{lID#sa1~MeqKKJeKxb){V zRH6)eAHNLxt}!?(ECvnO`FlR|u^Be@Sep(l4K;GyT>(;edC-JZbWJ;@s5a&9 zakt36Yo52Lf+|fpK*8`;!jb$pl0^^PLmBW@H@%F-wWcOO5Xc z`7tKZ%o>Bgo5$rq`uP>-PhW=Exx6=;>OVFZ-|JTJ%7#6ehN@{Y!miL^(~FGAI@ljUGq-E9FQh1}YIS>)tTS$gx3q2(wSnMcLHqAlcWAvb}1Ex7gko*@$G;aTTm=*r@`{XFoA5y!H{7 z$T&5NC}hgUB1oaW$b_Uh$usR-mo_A&^F>0Dwl%x{I%#fsGjulNp_MvGi(Y+WZq z8ZOwr%`sl+<_pWAVHv(uXpsmqb*{{~+i|43oYOuV6r{-uvBMPFGjGG)@oG`R_Z&8N z5<+K)HodQzEalm2ML&Y11Ai?Tbqv@9P!~KF2Z?H59y9KG6cf>Ck1$*>YB%tnpDmJ>!eNU~ndK$?W7Nu>7g$*YRkgZz3_I`@iK2DkrY=pNZ8r?xU0FEuxS5(vf1`D_LVa$IK|-7KLxnWM zSV~dPY?2HKz$y*g8-F*D-K?tie!sK%u%Ed)OB@jTWGxZG4Dq9;YvLD=sq!%6cBXa~ z#(9W$esFeYHm3*U@9_1^#!!13QT0{r)XsueMY{s*vGx$VnK;I53<}SdSN`?kAH*{i zw#sP(?9C%CA9CTz7q+ptb6L0xoO#`l)~EZB$JR8(QL$)$a2a3VAlxUpO|fyIqF$;e zbbKroy5G~zFv-8FJqT8|`BwiGdu%Gr#xl0jr?>vGlW*!}jbEH=9v3)$^4{=PbY^uA zLJ~3EP9fTq%`lD!7ApX|g(5RXdhu`Zhg8e zszH$;e_%Gq#+s+(=pc=igND05%eMyzbL^hrhJ7b z_d6r51^;v)*>Szq*jnQh%{5RzydbyrR?5@l*utAJH$T_woP6VEclNsMrWykN$~D(e z+~76&U2c&oucp3_67mGz80-#6es$DSaT4%u{m;YxB>l)O|TXov-S3PiUv6$(C8|JqoCJ z10qJ{UP$Cv9gPfZJ4y3=_8}9V%u`0E@qP<${9M-8_UJD5`Y#df@WA~QnEW>cd^{{+ zM%Qz9j(k=+1Yc>YJbvX{WpWSezLLN zXLz%6$`N7pIsD%A2x;XrE+1U>A1c~OnHiEI!wy*k+iWr>3Mgl-(4+e?zbq0*o8{t< z2D|S>SUbo#g{WAB7?*d6JpI0!=HwZloJbF&Y-0vJ>RdBGHdr#`)FzKTMlYNJkZG?ihZ5vcmRE<-LM-fS+ zo{9GwMj$ZES&J>O+dXVB;RB*=_qw63K#D6QPi6r6M==Ug+i#)*#r+H&o+ydQQL7W! zzA#I0r+fV9c-9nzn?<2 z>ZjM%frR4t-RH(><=vw{#%`&TBt+f$rxOH^OD8A#IPv2!Ur3ZD_AOKbJ~G!Jg8m(Nj!um8e+L?H}LNJ@mhm4M#}3U=^x&crxb3 z$O_XBFf~OM&|(FiyMoY~*bHs7NP*!ww$i>7c5ax}b{GVPAFk3k$bvVa?|C z8TAJ)&AeX>xmX0(#%K%kJrs)D^M@k`z*KBTO7@N-)~)|%^R>YMeX0V= z=w0N+6z(lTOl#`nCPTkM2J1+kYSJ}1_#Ym2GxH05tP?>cnT?zZY*n7{grnYx`6bphPVA+Dw-6|+uq*oOIQE4JY zX`%!~5_+#HZ7Tw9s)ZsTARPmS4nh3@>ObN6>d=&iq7fuI5u%=Uhj2oo%4Z`goX4w zs45NjR^C>iHv;-SvE8%jItL?j=1qwvK#GFbUm`(4qtQYGZ?QruJT3tiaIa z*7>3@#D-&h6>e+tDxbQ-;iM9K>=^HgqM5u~Ti~CXx9w11HRO;{s`o9uGo!n;uPcB`GbX5 z3*X4jR!n5wp(j76%jyc^gd5*^01pSH{z`VsxVgp zF*O6!)V14c$D?R`(A{eG_X~9|d)U&o+b4Tjq{D;tDwiwE4C?OXSP2K#%4=P-p$o^= zjm;>Vc*w9`iB#yOP0;9 zrcZx4=3d$9k=TLEl=A$D)WZW9_vgO1rXrQp1qB#SiGD9~?6H>8I=a}WyVSP|AJte7 zrPmsJ<*Z&G@df50U7%&X)>0N^SX!2rMe&WbyWi?QSh>nJ+R_7e;bbwd@yK3p{u7g> z2wX-LUs`l=RW$qB`Lr8q7|!-reRR;17hZ0Wp ze3@^`{R0kceo-TXd9thLoC~9iwO-F8W=5B|tl3y&YqR2>MY-Gaml!F``RBhdU#%<+ zm*JvabaVAGSYIw${%If=N6}dnC8dH1XYwdDC{`{BYR1%^rI5#rJh+1aqG_K{M*-=Z z>uc_!5_96xH&NG*%Q+le_M8ofvutmvYwK0wHKvvQvq_(I)c=yD^GvMQlvV2 zt&?o+rh6r{Ug*)AeGKEFYrKsD(#7{@b%<#_Am)I3E-#oQAg@P`(7%l(0t}@<*bK-0 z%=`mpb|(^64P?#Fh|xU+&1M!An4hd_$`-Ee@6iowGmTe(v_$x%09a{FDdZD-H8$&rdbHuc(ZP^dWB4!kr- zM}a^CWhmcm%pm#)j{9!@_sfY1JJ&VgT{B}BUpd4YNI@h}<5JOPqQXH=xB3C7;Arpb;g zMv*uvA#e~Z-{LV~g3}6Il>2o*@%(EDVg=0Ntz7&ZMDY`&G>E|f>zpqE-y*I{p`LlAZ(_!eT*r+;W~!p=zqiG7>)>t52_KHhjHL;SHu$+kJ?i6^#YIWpB{N&g;# z&w%U~x7WUT+f@8Ne_8#ZpiyMo#M+Yc z*zfwyIMsmR8-(vz{2;Ok2g+Y|yba0me(VR9)!QFw$Xx)d3?2s3GlQ^yPz)0JiN$Eg zh2R;dD%9EtHx^$mAfefrO7eJq$FtW~dZn^>KpltIeLq~-wiRx&e7N0J+mE?f2XDK( zfwHkt7v)*nCAo}#z<>O8M(WHZ_^sK;HZ!#oM|BJYer>xD-%7YULgBdCx}LYp6s4bB zVbq>2xkG#Cs#-Zv#YVrj(yi4?fYBj*!nElXQi}DKWK?xsmnFaGn_G_aekrBip6)Ei zqG%G~jF9gLfsER`rAhu0rIGQb*fFHi&6LX+{aY*IzP-1(qnUUturqxDiA`9P!D3oG z@t0u2Laq_elQ*n0RWqyeK@gz>(wlTnx5)5I#>1ZaQAQ5g5vZdsovnZFfB?2niFFD7 z!``^*>>?CgL1W{XOpgU7O7k3cCR3||KHNlW?d~u=|MPLt<1}R}b6huge-1rIBKQex z*H+B~ZubEEepaU5YgBU>F1JUly?SVaHv~61UB2J0*pYX(@cqe@P()?3*a_un+GC$b zxg+>wOkH03ShVCS#M9>XnVriYbm}INJ^J1dHhkO2@Jh9QTcxGqNDP6c0D%~t|_`tc&Q-gO^gDK|bO{4ILJ zoU!p1Di}SR(D!cCQBwKOIk>aUaLA=axpze!6SYl$Yp_gO26b4ys2SIfrx1;r4IZrx z8qAec8n+K>TJ7U>`_$*6T?EgfxS=xTv!NzW)v0~**R&+8j_XH^YU{k%_fX^p93w2W!py&QBlSlGD}99FShh^!wuL6q z;G3afpoB!Msc)3>gfm0*O=cqlYfC`}8s*TTgA_g3teWbjS&UYXJg+Uryo z&LrJ6yK+pXOJmtlt2<4hYR|K6%nsckyi?gt#sSOMfbIC!K5y)do9sVD&)Hi#%A2Q@NUW!E2v7JD5wDeY2YL;H7Ct6T`5iy4#{A5*sQrXzvW05aq+V z?@jKSuqR@km&WE{TE})NRI1fVI~|%^TpA;zX^`DdgN~D!NNkkhsab~suc5JBf%T_u zL}!lm6t0H5hDHq*T`MWH5^v$pT~gE`j{7E&bbE0Ry!f60y=y&o7?Giuco*V4sM|fq zJh|cFi3n;Kt;_>LZvunb{pbPC(Ru!f><(k9--aMB)kA5E{{I6I}s zUg}S;O_I3$V}TVaB$vre^upC=1JCR_Hi7Rci?p6+l7d$oRM#;)tK(;wY}D}m|g$mh#WCC{q3TD2Q6XA3lEP+IAPdb;PF*A z6~xfumESQn=8^{XJeo25i^?;GVz+u`vICG(>MW{aqpp$#ZK)-x#lB0s{93%H9}O>D zI@grdN9lu^lyxpS)o+&jvVAJG&qY58o)JQEgZ1iCp!F3@KI9u$)kNGho}`E4F1PS1 zVi~G5dD7kjf98w%O3kP9Q%gKTS!4Z$CF#A-U@0Bvq@>c?v=N89wiaQ0K7NEQ@vy#%+XV+~Z3A{7 zgKh=pjID*{vADFq>LFCWsZLaCIQnaLR*y`GkNz6WMe-GFu2N(A=|_O4=;oL#M7bx$ zS<@E;h;gMa5Z)T#p;f4%C+U7c-WAFEm%>mE z%U&UAE?X2klp}yl31tXU^8HxT*7c=(txc*ZPi}FW6!$YCQF;r1T-RkcQ4W>M2QRp?2WhtUG?o#0Z()> zis3*cv$mif^aF=v!8YlfBKlFC?#oC03h=<#j)we4I4ZqBgaB@`6& zPqwC%l-cGpuo&p?<#4m3MRr+=im^uaZNwB+jpuiB7#!rNK-ZZ%rwgQhX~-Jxo%1xR zu|GcHPp7=csNmgq;Tu%6=ayxyXS)rV0&4at^kTA9VlT5Ov%;1g*A(xBmR2@mysdF} zUWbh=oV#PS&^Ot`x$IRvyGunryn*6iz1pKH7-Q4wY%mA9RMGjGuy|Qq)=ZtDIW=-f z!pheEd@e)nbl%W)7WvMMnaWB2lzUZ8Qkx|`ggks`C<3>3z_8#2xEcic;F{5g`nb1BUl!xDWiTJpAJ89dbi73d9+OOML9v0WMLA!&TX2N$1aUIz6g z5ulx$C#RuAG|DanQGbWSGc~fzNJ8ej$^Cw$M*bA1L!O6i?ChdESTL?$<8zx=L?+-w zU4mx%Ua9OGDOseYFHup*`Xu?S+##5n9hv7l0v`V;*XhvdM+$y9N}eKlCLSS@SL?zf zY^~;HxXOhm(l*Yu(|k^j=f6t1 z&wrS?wsGtk5v%}64phiy2c2e!)YoKRgfzm8QzxbT`n>8RQ<5MUDv?#fpbR2m1$bMj zLG}qT#g>8kFcMV52``FB=W;x&69~W&f+?KEd|0qo+G`X^PJ$54)ZG)RhSK{17(Dbm z-`?7w1kmYk3u9xX;cmXD0cz1N-$HFvY^d|B*KUMwsP=6nuN?_hIrZ6a#iefMG$3Jr zp?Q)XP+e{6(I>n?-iU1n+e!_a{~mr~^vB8h+PG}QJ&ZmLeOT!eA>tKoRyNx{8LhJI z&fzM{DYI~=cW-TH5984#Vv$=}qpp_m(RzD2ls~BByiWXw&GI8CQJ4%@(Bl|vj@oZ< z-(%2Vy|Zj^BXGTu8mfTq5os%DwrXWJ1SkI&G5Ezpk@a)pAoso$33Ksmx7%p+U}E{S zvF5{9_hu}eF%cKNmM|UsZriU2rH$DA%Ai&(HhV2x#N}y6R|FymRg&@=b-U#z>JZ6^ zL)E4B;3m7QSaf#T&1aN7_S#qk|6Xf89d$H5?FhI%UR%+{48H6X+GX4(I!h`?#|)GM zyJYe(*JovNF>jP#pdRk{cc?-Vx3|IurS8EY>Gh`QiiLm)W6@}G0)iKQPKS~hjgm~Z zm6{T}IIUI>K`AdrME1&?lDSgqPFqG=&Ul10=}&f`p>d2w_3OdICs%#J)sD9<`a9%Y zZmj`M$W*_=_npX(`RwHaD@=!T@>ETMG-419X6zwi!n#>;i6xKZ!J0MLpCmmrp(2AH zr09B)@{Sw%*=y*_JFXj2&5iD*O=p7+0F2pzr}vs$=ArF_xrt0Hq_~^jIMrs`m~q9a zm2TT9Lj*lAniDTwQ>JEsxu7E%1gu)e`6Mk3cN`D0yvaElF_E64h7xJh(>fN9m|V%D z;HA;2qPzT?i3q?k9L8VYRD>5BT11T#xvSP1=Cy6GexVtc5(nY3ex^Efq-T!@K>@h; zV-B>UFx6oo{R})E#aWj*Y+VpuE%`yH>t&?F4%IvY7?H!) zy+$}B9*4{XsL>T7zzPApV*u;a>+s|beurQ}MTfNck*+cXk=7ROq=Q;1Ro0t8iy}5o zFPDtM9AZgu@P9y1?Z~3J>>81Kv4a1&*XOXjad4BS3~JKR6kfaoieVrKyEm(!wUGiD)@NVi5-~h{40^IdH9cVNhj& zo(P2+{zB}VNW>;Kz6m@$jLh<`7+I!mpB2N7dUbL&r+Kl1YECZ;(dLRr^D%^)*bW=O z&kQ61u-pbk%D2>Fnj}oFW1N!#CSF6QN_?&%${SH+2I>@|l0nj3B3vf4ozQ5dOvUH+ z|6o`!o~ck=>@#u6o@X1Gf)`V6?KkF_af<&I*U~}dMEwTgy@}G3-%0IMhnWKOlOcu_ zVH+T{#Ek^KfOjBlkd0T$QSc)DI&s6;Eg`f6$c7vu+m*p6xifGKX~OLUNO=ulJj73~_Nz5}4DkYmG9W5M=-gLyS@*R1LUqdc#oG(ovo>O0Ws%?d zw(~)>3j-0tE>2>eK0^>FB@nnZH=`W*f+zET!%(&GV0Bb*#kWb&Z2aJ5H1?yY$%^qvN*=cO zRmc2V8=_OiVx`>f017->q~{A(C@~#Bvr}F&pCIruzbm*y*}?@U4qSeJ!?bHhWW~eV z4gX1=m|4*ZR5daGHyjoeO9bqgY&+vgFeB3v?YEIEc;0rrnr?iDI-;~0`BBuL%;7iU z2<^fr2giTMVnO`uZ&)mtRpP!}^lxY^ZV)3l{5L#SJ5mpW&D=rG5q;3Hyuag`00jk- zi5(mpKHl5G{}OkY$G^E7{Qr5^Z}CjsG5fYW~FPYim%!LSgK|69f2Z)|EOs6g0W?Qpc%VFJN361qk=qfAKvGTI-kR<4Bg9pKICv#%^oeXt z#ttSPN_?7fgv;hg<9QC^?u#t2lrygA%|aijuA(t#MC<*+-=ki#kFj%+ z;k3Yo<}ILRNVAp3jAi?We<5sIn-*MFWLD7F1nnPttbzqCzeSjDYLyyv?O%Q20TLfaw2K7J;cxI54 z8i9#XB)t1h;6DnGB6!|`CN*1C=XjmpDCi}yd!B92Et0eztdOb`>$e^hH7fk)z(UXW z2t&eu@m$8Tgz#)lL=X&s@;%7+fsAA{h%(+2Ti*i{mS|>7ewK(6ZC1EIcE+T?-ZC8_ z%^XP`?f&bq8nTE{-3CwMod9%5E3k;_UD6|LIWUpj2cj&U2bwel!6H<`zG=Qha>b;U z3;zQ{`*H06(xqWfkuCG}2QSdWT;=1htJ4UQC8F`dN7u%TNs6AYcHA+SmRAon5=ggw zn@%qu-4cDi4?kwf3pgWK;U(BmmP1RB(&BA2-s1{J$M-%zb&-upK6jfYm9$lXIVOD! z0hV`jZ%{T)vxN;^e&26{YS>^F7m{|+yYaxwFL*emU$$VQ zHw9`lauKtco}TsA?$lbuSQ+yJ5BcT)KG4Syaq_M;MRL7t){Q2ifBgx)h%e;;*mu;r-GtM<}@OVuZ z3kNX=-cF`*9`qVZ7&U-DcqZ}0|BC(iunens&q*{ERN@gfIRVO#ZnVf2iZ`_H59top z=vBV|rq}H2$DTLXLt)^8S06)h$7Y`i5ZbwSjdb>-#`jcz)mN#DWyCv{g&k-9LeGxu z^PvH1V>^#QIBNmT8NnM9Pw|mEFoJ!5$zWn4dG-azgnFr?Qp(DP=w zr|zD$Y;&cT@k=iqtePcu#q0B#72iH3R5|lz{?5J={KORKS(D*&q|I?EU?X`q3PT zWcD!_X{oXC*I7lxL#qNrE^AlO&Ao;1%A@0G>9=%DU;UU3uk}Tj-W5%?;SjE(176ab z_X{|evo#m)Ejak+U~|XRY07<72G^_R$M|37C0pEjjFj77)kL5opxz?;z6$Y`?z8gM z*L5h#dV39{PyS3zui|S2&oyY`ZP_A_eHaMEnd-(QHn&f_2h|MZVkYS&x%==-7K!5u zSzdW4&go`Mv;V1CpbMJ4x>kz$BDx+tnvuQ>T9Uyi63r9zE~Z<-Tn^6>G|jSe*)tm0^!V#l=lk&gy&G&f5PW{bSYwjSikP zOz;qoXg^YLU_0IJ?z+C8U+llzMyp)zX&iYj0UAD{?3&f@*rxfqIz%QFDPX@F-`50 zwZAz=wFz`X4*G1(*~p^5Xti%M$7I4Q>OPhA2wrwHlhM&N)NTo_ON@%5R#?0J znLV&wMx#oYp--utUb(?VzJZbh$Q=vG(3NcN1D6>7Dy@9rC|qN1wik*Nz?!zTW$89l z%?nCXNYs=cK2U8>mqw-aLP^HSk%`{SmfgYLet!1y?P~jwrg10x@&(Zos-lI*qbhVR z9zL?925s{;9sXgGh{xYcKOCU3%+cK&I zVSVHeiT$TyrA`t)LR1&I^w5UR@J)EOR7ELMh;(ktwp zn!;!>$zcm&Z?+Kx^b~1i-HE8b!4AJrCcQPoG>h`(b3<0EP7A284d`z6soP;@bHgL6 zY!I$cl?A#nYHm)$kMLLWj}A3Zfclb}<=Me%sbr$q}osx_eM{W4NbCb#$2^eDO9%X&KACC8WO# z%ER8(>1DsY%*)8)NTcc=;oT752L^@MGJ@)>P!hah?*1vQEcJNIBx2*%+Qa23e~!x3 z%W4>XPBVOFqR;)_n7FeXu-IbKs#_+Nq+ zk4@Z|*y;q2kO8Z!M=17CoZ)!%nnIqH*8}C4yC2e08L67(za|1wt0GvoAJwAqy@w_Q`&!TvFE0MRg{k4K_de_kMHIf*TCQCmK4uUsidczTe3m)8Zn8F7 zt=ax$tnaK|CtJHsj>q3^w{D`c29?)=zNz89B8z2&)aQQF<e!EJ*dQ$WI8zAD9%J(-cbA9 zJoiR)TG!i}84qT3Z81g`QVOTfr1EP6E(Kxw@7dLf2>YKX$Ucl z19F<}aRi|Q37Oa?;0m@>(r#*akQ=lE+lAPQA4uI83t%pzNuxP@Zv!_;L%d1JP1JkP zT=OdtOCivDki$1=Wb*5w+4^S!hzeQsUj$`Bcz=^k1QlwiMz-~boA4=QVYiFqy#JB+ zbT6+_J=hw6?Kk;6@p^x`7VRd9k+$3fN#6Ul9s}KGJqML0CUc{*U<)eU-hC)v>i~zpJADLLQzZZKf+yWn~{u)`s#N1-`{YquW zj6jeMIS@V9C}I1OYyfC2LIns=_-q;dz96|OqT4s=kdQje5hyj#?d%6JCfyk*5N|3Z zy|M+Z_6Z>Gz2AfuOpohTS+C!y&*O9#bar}B5Zf-)&cRj z@@Dr$JnGVHVQ`U^I0ytrMvb0AAVL9GHk-`{mxZhvZWJ9n>G7CFQ?^`aeBW zQ}W;xo~2H+zIAu`1r2TwdbYeD+A}y1-AR;vfM*-fyLjW`5A+d^FiDa!@NvTl&O}unsHhm$yF7*`U|TEO=m8$}J%YWL$kQJswst zlw9BJ4_Ge0~MMNFDOm##-`pv`gea>n9ekLvmCYZ&FSOsS&d z0W@eC?xo$ayQQVXB@S$Ocv!EUYJPAPBMjEjs1nl7q8QnpyY492bKZUE=#qASg%2vU;2Wy~uf8E9>_7C|oGM zW+8Q8;=JSm*6GP|tzvxb2nl-nwecn@jv)15`EEX*a!iPlGvb~`xn3=uIpg4X3((5=Ei>>|{#P_gXCQlr=N2Mq_S6p)LQW)M9z>%1R zRSx!*R59PLT+g?(covd+~DyDYUM0GD`&VCNB7f~p(k03QHG#A z-ZyULyANwDA75_1DC2AvHXu&DNXzrV(1M_Of1RjgmX2eM@WvShT1Q7KI;j~px57!R z$1#?hmey#o!5FUBoOL~Cho=pGmem+11;^6WuuO8g%@6%yCP>47ZADBREl#8IiU$2e zMp?T5^m2%$&Z(76XcYBV*=m+~t`o?ytG(7WPk7H6Q`B`~UESL*l$weC5Y06@Z zPf=F%>SCheML7F!$HOU1E0kz5jCIQA8=@G$@#t8hkvJ2(Rs+h$YT%{h_XlspTghw7 zXK0Z#rHPdY^ceTNn)R*0Dhzfua1Gx7q1ak9v`W+IlVz#zrDfJL#a2h!93PYaBiXE~ zwio=%OYVeDkhr3q|4*cndEr;7FH4r9h2tafad66$=fAP*)S8{c##qPBpAG-eYvp=Q zYf0dvx8D2l5dLdEwxJEB!@&o$h9s{SCqEYGuoS0ijS)ELBDS{9LC|NsoCNxeszYCf zg+}MX;x^%s9e`pcFoC3cz{Q8P;x|zoIX%e9@eso<62(_Qm-6H68!nCf^ZvCB;#KoE z^18XJ2YNC6#vIis1JzbRhaba-6FMp4wHr8Yy}naquq zmtfhoGoUb_hNXmQs%ERpvb860B%Hn8y54W#Hu>1aOm03$MEv&JMo^OH%j1 zB)BxsH+<2fu=^#usJoZN0rVMy{+2>oD9^n61?g}b_KN;Q4X)5MXZ@9%PGG%1U)8kF z02F5Jr92O6lo~C`Up+=r=fGa3?Usn*4$Bt(r(`~mhPa`O%$fqb*Rp0HVc;3XY1rUe=;piHkfIkG_euU2cEtUhWCWI zmRv9CTQc>U$zwdHqp&?9)ux8Iyqv3l_a4sC!}axM_Y;lbPY?6;w0N{$_#Y^-gbnmq zDz5l9e)*$jg}yu7A!fXTEi#MewPfzu+m5Lt$Bc$sDTa^UM2)MVw!wHnDD$-D!bdo5 zD5C|PnpFsOkj%Gf$8~t}da}V&ypjSJUeF6SBcI#Vkmi&UhGQ#H!?T?ncINu{ap}9ryO;Byz(G#me3+4inm%AsRcu0v zx0MXBeqFO^<2&lbTX=iif6jLqs)QwdlgAj}gd$vf`=8o6v{$Fz%X%xNbVuN|>Y;F4 zPst{@8MX$w0A?*T@LKdWEujHT8d9c;pAGJKZO&(H&R}40M9Zhk0L9T`Ft;EbG z96j?n85B^vPh)1SYHF1mRA_1svibx%Fr02)i>^rdTPb>7P$wLYx^g?W2R`fDcLkCA zyt(m#e(p}S9Xb^f%* z4NShm7IOlj=Hso%w65vXw^JVqDl2O$TZmViM^VL%zUFlKH1>J;wBGdG!xubBp(18q z${GuKryV3}=)O~j#G5HpnIto=)`X{7i&h=KWZ#k)4uF@MHTLL;Kjtz^7Ed=M)|fM& zroB0NhRa~G+Y32Iz8#NbhB5N2Gl*BUp1Xm%Ej}QUoOTmcG3cup;-B^OQOfBC^IND2 z1x4`$GwvHfCSn@pG(q>I#BNqt(^uIqJ7#@iGFiA@Ok1i^pU_x6zbLTOhlcz9HA`7B zvM?ziRjxM827CW;T*-Kz9ow10h91`M*9;i_RI01?v;b+)j@q@ zR*8&RgBZ$9c!+59`$$^}X4dFf$#Q_-e-oo3y8Zrt#3WQpm#1mfH$4b`5f6{$96SrI z%K#1sZ%^_oiGD=xkeD$Mza@15*b3y+0W}ePE5b zRBc~-Bu`_>X~Y|Cl;zW%Xxns9rd}9IvwHr4B+IZ&6?L?jHM)I&K%jznbQ|Yp2)@!`<06B?&6Hp+ zVk}y@;ZKoLoTAi{46HnDW>uFl_FbQO@wZ_6lP#dw(s3sWB@~K#TOarGEH>7ciK4fs zpra6;qIBw@gyp^Pt(!xjh}k~=4XLuzlHjy@p*UEbY}8K!RHLpcN*frt{^Fw8K3FO2 z$HtqwATTT%uP=yNX`tes_)601hT=ez-F|2f*vk9)FVLf^>*rgM%vz>RGd&7otojEz zmMS=TvE4(dUD3;Yd_K)IWK72-`(AxSM#_{)Z~g%!;@7zfY+tU>mDww{HmRW=a~9cI z+a77Rw0=I&%YUKUlq$lrP8Hr_;nFU&0M4HPJRxDo4xOXKPG<{gqQci^6V&6^dY&^WStvbO zI9t?NdE51Ri+^y#0so2?%DG#Bp8k z$H(8a3yZx98@Vfb!l%2nmW6BC@i1gPSN{Ip-I#bEGJHDi8@C8YRj9GIU^OZqGs;GulF{O1$MHjwrKPJmk8sO(LkH%Bc2KQ;~ zo|=7alROSoo0R8tbUlBuzp2aK=>_a?zxpO!jImWx7_Lrm?@ARzPItU^mQ7i&`?TUU zHvODD&lM!IItuuZkYJaO(hG#)RP{|iB5;oBaEi(_o9p-VY8o%VMx{2NXkwoMdSky*}-ocGxB_OMbv8!Y^rZ) z$2BOi-q}L8Q1K0vr5d?{mrE-^Z41!ZLd@kaPT4j~47t_2zl_U_+BC zFn85@5t(RK9}pK=QS&ntwJg_v4deWrbP2FU+n_e0hihMLOw0+XP96b;Ig&TU;Dhzx z=QN=jCA{^tHGfW??ez9L_AmnuG9)C6>ND?E7stJ~V7j_UTlt3b78L$@}}neyrM?nX7NdwwBK zBmZQ^TR6qY0O|rd2*>;_9H)Jv>;c#2J;66MN_z7hQx0QXO>Wf=Iazc};I8_BcU7j)`iNy~a;*TXi8iCcIU1xz43*7<{a zfenOtlUSKGPk1XZO6u|xg>PJ$o>po=B_O4AT7Gn|3KsRBz1_#Y;voeDK#NCCGF_ZmJ8qTD6!Ey^N8KBB>@~K`gM?rsII7r(`~En$BLzc znhyGz#ig@{`7EZjmwA2BPuJrX{qWNcvg|#l^HVkszA>p#(-zw;c~-~Mhn&gVY;Y6a zz}I-ZJMTZ~tCO=nN$)YO(~Q{tz*5rxu4Wy*WRr(>8`o=6{TT7o4Y6WiWFW=8{XP8l994&9$p}4Y4NXd;`A0ICYoY)%VR5`IY1dc0 zQ9F^FxO(5AoiUWBV6^PifPFK$HL+@dq)iK9D*!4Yz^^29C6BC-fbz{&zR->(hq$1+|tAo&w-4 zLtHG0w~lN&Hk&%khvb}yZPy+b7Bv5QpQH$2puOmqEOb*%wAturhd@GUaJkaAL>4c8~q5F4+U_?PCRMd^hfc@3NU`+tHvB@&3F zkUR-(ND#ZSeFj#Jzj#CN028~MllPS=TS?_TWjCxLl+OG%vB)Mexy1GjplLLi-J~oc zvCj@E9}Bq*;R$sTj&`eZl$6XRwpTqwWFYYsLQ-GZKC>7-BB9?%o3Y_V3lQ-SO2+V= zGyX#n8nK*>Ac52Q>52TsIg-?w5x+)~^pU2YTm7a;@K-@D$$9@fs9Txz zzk>?k*WcE){vb}le`o5yk3T45gf?+P@3LKaqgD2z#}CTe*ER$9f7jiAH!~1548s475P)OzNvOKv|8|6smkH-czrUXv z_{afa2mr*kR)Mz2VaB^d;$c77dWX4u^d5p{W&pF@g5+D;!N*#Kl?7-ds|pGuBy%`K!iBR%0bl5bs!omkG3eZ6|v_7S%=333Mvs zdD8lh6E*G5^u7yt>ll3Dtq4-q?QOE}lMARfpi#AwRbt0cIfAz>RKL>YBg~S&{>8^Q zm*ZvWa#z4;oSf&++>FRy-Pd#TR-)V8$hfoePGMpUxt78oozJ}DF)yApQFdipA8$yU zRY9IkrpkRsdAzTue$JU@vfb#o!E0@7ywNY1?sEhW+2YAN*7scrjrF?sv6ab*giYbS zprI;y@ab#0yIA8}wwi;IPqxb6$+~)x*3JHRF$w!+{!u2n*DP$$MPIKC)EH!) z;<;>ZrcKh!m5@qqjFXYcfNUb^1H98?12oAcl!}tMzlEP{cuJJVJz@ogd$#w8_$^r% z1&Hy5!qR?~9g!7Y#9U5jGTlqkje8Mz*52@kjs8k*$YelcUT~;2(sr$23j-zxqXsNw zUGWJ3!*#5hyL3ycMib$!b{}I-+QJwN1o%SkNeUL`!5?#KKa-Ab?Z}^KsVP6tTpHn) zS~H!w8&Jki0q;^>(qQw$9Sect(|%E^2D491DN0Eb;P z)k6PeT~U3(&U``Cxi4;bP>)64LsE-X)$Q5Lq*Yl(Ng*YNM{Qih(^2vIFUDX&BEW_J!Kt-Tr zt3Oha{wjg`%(Yd*RG7u*Nen2(({w-(G>&qgKdoS+;&wP^A~&PYX7;Z&DxtS=0+HSm zYRjT>us6{sSU43LSfhncAtcl`*Cw(_S#%XDTEavESE{C)Ri{`ldYG>!kS_z7^&V~b zM@*g=b_kt_?p1U}2|lY_oJz0xX;PznI4~$yIZAx>YvtlYNgA2gjbme!aaH~Ua1}s_ zdyL<;raQB2YOf3jd515xf9_yx#e!lfG&Q<%c4rs z&Ji@_#=LQb5$|Y^!=}`%|D2orv&iwrxu%9b#rpTsdEaF2f)X^-dEKQ*hS-bazJW4| zwKGw$=<`>L#iQ+dAe$xp0{Ms@hzCp*>h{+?z&u&JVlq1we43?!wx*ntCbU+o+Wu|C zBPE$7?e_PEVX@c30+6?&gfg8!G@Cl3>)NhdDu!nuZ?Smkh`ytL{l(WIo#|taIM5nP z2wnUO$caDEf+=+4mQQagMX+Opk%@AzdgA^58BiuMI>XOXTX`|>1y@JXNfsafb4vN5 zv@Ud9_PD0(SGN`ii(HIOTJ#SdjE6mgTNcW z{ya3`G`SJTL7$js^6J}}xv^4;(lQYq|B(y*wv%Nx{d_{z?XvyLD{lVEV}X4x|z zE!pJFJvTWGP+C*DkC7CZyL5qZ$uKP6Ic(BQT_b2P)!kEh&yK;~JP?V#jnw}h9r8UK zp_W%8qu90NPAwFMBlJBccNIfkwh`4Uu%A^i^8C&@`{ee z)@p^ljKsYI4T6fV!UbGt>~^(Twh46%!R>qbUwQCwV;@)>{-hny!c~PMY_+GDX@WVJ zj1+8K#dJkw$0C)1l{+xPnmE;b%y)OrXuC;(m=4+G<;9~Nc3gUK));vYQR)rbB;Ly} zewsZ*ebjcmZd)dJu2OFCTzK7m-*Ze8z5Lgr`_68-iq$>Q>sF|t3%pQdvl{P} z=-0beUo)eS^`lFAMeo4#Zt2HheRSuHNMQYf1uAl8dJ4i0LiqU7)UQcJ0V7;3ptM7=NvO{=T} zH8YL0ZLYfdLiuO5WawfiY zh6$$*h4(t;-QQn6a=2XK=xpF>$)i+8m`<-?S@rP3s6<(ltOa^O7m?xtO5MQHcoB0= zDS6#<9aegE)Txexb~MV3nXb(q^C+3x?jA!-IoH>+>I!#DW~;N)_alR$);ZuuhM2PV z%Ag-eXR?8T*91T4^Lx2l5xX3waJP|XkBZ#p+b1#&#nqp~k`>8pZoisP7@e zhb0`KF@=RgJAH#ib1n3F@d1xz;59dvPPG~OK$PxitFsU{HV1tE1^BcP%@O9H)#eB| z1Vgnwp%_aY8^MFO1PO!Y{2u7&-!SVN*n+GMQKaB`G`Di&6|DvG0^_d*oJm(T!>R9+ z6(Byb@jqzsXSt}lC+tz)$Zol(_1t;#oWXgko>3Bib3bdny0#ySi!Z|B+Q(102yZZ( zjB)&VL10GJ*Gjp3LHc;k7b5|B&M&T9i(kZ;vW`8zk0Mi*3E`RjOVrrRz9tN5_Xk)t z2=Bbi63PKp;-mb*uHbjA!+Cvc)znp zjAwO*4u7D)#5mN(Ba1}ei08CFZmp?3k}=@gQ#Oyjpo7v0Oy|RusrXQiFXAs$+;pjfUyzD%XIl6?IeOATXCXdlSF6pd9 zJm*+A^BegeoLG9U_U@%Q*dLMM6ZAekOV#vNk;S>n8H*!s&aZ+ZEP8cZqrK)3A-KHC zxMtw>uzA4@r5`SQwXK?OyyoC&^-42w!T3^ZR7qv}V9{S0j`6zl(s=`;V|08OAv(EH z>L^xz6PNyn80&o}$iN2U&2re}g&1liTp3fW+fP$#aMmiH4Pj&WMprGZM-Y+GSj`*y zFj5P}+Le|9t4=l`j`?zbi!!x1Y{fWK!j+!(&(C}IPE@Ba2&~RxN3$H%4wT#&FP+gZ zl<*f2mb|#aDVECE-sLyOelR?(&StnJX&2duFLY=LAyHHTC`=pFnlNy*=s2ghFx@^J zztA(=K05m>v0K9+!l!;zPq!p;U}zX?dvjjy^&1QH{SPpQRcXe*q?FQ+%fTnl#-1uM zq*F**?HR$&jB-23enCnp6{=4RtuEtcX*vo$oYHj>&i;Pk7KPOfi%q=BD(GbQ3oCQ; z_*PS&sG*tGqUSJE8sGwBWId&nGW93pF-Mmu(Epb{yiTq4{bRl|8D9gNuma;czp+E6 zi$aMRJ?F3uV?LfTQNF$p<810e7^1XL!}rx{*{XePJ3o6Kv?wz>9VjXO-Zjj{exD}w zh1)2sN(vA5yRQqX6NIvgEN{Q7VpzsxvLZ8KOc7ab!R{-32ZTiJmXZtMDvi}ary?_Y zF67ejsZYu;jme?HTI34TweO^5ps@kGu0n3_F=~CGRr8U{H)@a+k?~`pGoa-m4_R)h z{V7A{M)#VgFZWRmoJBMy6oon6-rZ9_=_fMcZexbt8?TNow+UX(4k{gC(=j>@%%zlA-Nse|v>6BMsU$&*hL~Tt6g-XBNe29V9MtH~kmtoVz znBWFVrYv$YkrwmQsd25WS$0-6!eg~(g*-c#eK{OJU53%)C4{T4dXa3r9-7zvjoZlB z7-!)SayFoMu86nO;8?hdsEmZm5_?e*zrkR%ukge{WZC1M$>E|K8LpvH{q$Out$8D9 zVov)`KoNsamq`JxQYrQ5VH*^0y8j`TYDpDF5l{ucRxh&(TQ3MgQuhjqM>wamTME0c z4A?D6ap(x#$e5dU9XLsD2;w-w4O3n|Y}PDBE^tz?=u36BOz>;F95@s`w1I3d@UM@D zbtAH6Vpwv^Ml)d$q$gtbBNhTfD1n1JW&x zY0@oPGNoZ`aeDYp2tKqStVbrJ<19waK)g(KhNd>l6X^X`G)+q#hWS>qkaNlzbmkc@ z3E64Lcv;usm1b6bRJTx1U6<{7pL^dV41i_oJ~H#S@E8Sv>G^g@g#&fUA>XPg>?>rX z*#Fbsmxn|7e}CU?C?xeMmC&aoWtXfWpOjFf1=*LzlCc!VK4eKgg-W(;*^{-wP(nt? zzKm^*H6hCw!dQpreb1;*eZSw|?|QE5d7j^2Pgh-Cm)m{c@B6)+^E&6e&N*MA(c*?I zQ}^$kE^~L2jlqsz9EvF6xrrrwx*#Y-d>jpbQxW$qeQaRA7ttPAp}asovP6(_N*u8& zX})9FI*M9w(C(iM>g7FfplG~dbC_xwrAx{ z@kCejdrbFt3~TZ-_4ep~ggRmC$E!d{9#29Vg1SO+%vCy7FNNg%rb_QCX}{yJR7IBs zGrEkqJ5$8_o-NwCi{O`@2tU#O!y|4Wk1d~G z%w+ta3QE@(d{svr7|fF9cqM$+;}Bc!8zHy%uaF`g%_sIT)?FdFVdiz`11G20|L8oR zUpAq0&u;+CLt7PQghd>30E$ljSkqC*dmSPo@PP=%Oulr9vgYM}`?`(M!9Yt60*A2%PbC3y_-$)^VY-O%CE|kn z_|*ACndIg&x5bsy{V|Jz?`FP}I-H$aQ|~w8C-FGe9MDYbj~23V)zFH9S9?B!v&t<` zgRp!XonS(Qo9k^#>8bX?FVpWMhcZa;72oz-EZ)UG<)m->Q4`)17X0{iEUbxpZ=ZS=Fa zM(1a}Umkbemdeq_&MIGY9pIR_5ig{litmH_mTsYo{iuBBsVLILygHkw(d2&P41DUC zrN}^ODcVb`c!kihu&^+BW&}K?%YLBrT)pjQhkIUL?km%><*pkTnq`9T&*!`V3tmKiY6)JHuay(x>{{B*iQ}o*QaNSD62e0lNx=V;J~)HHmz9yJ_9THFme06vKPrF8ZXTTuBx z;1GftTDSl&Fz}fvfev=iD>MwD#DYH_x^v6{z=n1<`f)Mu5Mc*ZYc_Q`ob2nz1Q<)3 zcf(2gI-o_}4xg1oSe#A2k6}ac6IcFC83F}-P>|?HwZ!g&i$?Ah1!gOK05V{f{{ASY z554Arf!>jPM3Xk?zqMs^K$ZXl9?O&5x;dau(0T6n%iMR4ff{R@mnlc(=BEz{>Pt7= zB-qKn!6Mm8SLxcgq8UZZ_IEb>b&7_sP%n~y#dtduiMB<1JA+!S{mqFx)Cp=TU;#_F zZ~l}vS1dQ+q)4dv&n1FgNqC-y@|EKb4AD${^$}-2L-ZXd?FZ7Nl|^r3vsQL&N-;Rp zf7G`YgGguC6htUcdDxI0EI%l2wz;w;)1!V|b=dVQ5hMy8EZp2s{4Z#gVzLoU@)xR% z&EEdYE{ZV69&>0ou(>=npn2JTKxPJWS&5J|1sX&5|$3 zf*;JJO5@A39$byt$d2=>E4o0}ef00`Ouk!BX(_Q^ZQKUM-o#@7nw~#*vSKlL;E4am zfN!FIKkwf?=$Z1qzvHct27N^+AUQzYtXR^s4A5DV%L0RR;nvZ|a%v*WT_B&BhzQc% zi%||m$kohqp&HjREA|~^pmrL#CiI)QR~gmht{D_++162-;^?b<35MtgPZKGx39{oO z_1Jw)Ns{`@7ZOO?nH{&$ex+JAUy8mP%S~T-A7|>iZ$jvgI?32l+Y#@xid3tW&lbYu zLKVYuV(m1q^j=Qy_d{;M-vhfo?;h7kuuhIFsnC*((~MGRf90*2)|?tOi<203l zRy+!7n*%fHQXN+65ue7%cNp8y)z+$a1D!=5ZtZ@H{UWk(_;%LCZBYL!oy!DVV6FSk ziDx%8!;KW|K1H%Tmm6nn{?j8ibz8wnye6Nmbo?c|bO*oSdnkeIJ1D7ld0glEwViG& zs?TeDe{qt~7cy+0)n$K*O(15*aOOog=M}~We&sKW)(%o*n!f%Wb!m`AoL$B3HS+#iVV?$D zQM{n61@UIN&3J3K2FkB?+!7E%pUX`zK4<*qV!?gYHTz)#GpXQeK6z*Yk=bif9*1Vu zWT+D9Jn@AE_!0M3+<|+~0ywNpU|`+9R<@Rs(9tnpztVr_AP?#$<2LO_Z(g-uQbIB? zDydjoTND+&MtYFWE6nwfj+zsn6<-Kcy};nomf*(HcM+fE=XRlZh?Or?7G0M;NF2Oc z-CXI`tNnc{Epqu}Gcjx`dV2(%CZ5|3oSli4$RiZprDtO)bYM%|c zX};U%U5?{Krg%qs(QU}mhkDpq)9OKd-8tN8 zgq&KgxJIaxMP{cY^S0fN%|`_y^gTx1G};Cy-SX8voxjT~cueLKnTi+2@aj4Sf#JJdb0Y9WAmPrWYGNJWq}H)AJdnAT(_>e} z(2uM7FzP&XZG3qCR1v1;S-+~>>V%efL*_##@hQ&UL4J=AR)ikPjldukp#yW(>}{!FyyX*% z`nM#+NvDOYFS6a0YHlyO@zGe&{o~_{iQ#?q{L9JWlCO+Jymr@Ce8oy_ z9KnL_NU5LP-J9#|Y#TY*dJg}ak}-dUj8U_8o9oQKwVNl$;z$}o#kM}=`rWc1Ox&o^ zaqbVc314%#6hIl7iVJoEH69&-V%O}Gb+c>>k&nBW|2pF)p~@Pn)?^AYE4@adkou?e z5y3lF5kU9|)gUhpMhQXfnr;{r^_nR6;Zy2d-T|17bNZ)@WM4hYUhFx|BT;&rfZf%X zqnt=)oWn6o-u>1nXA$FZu=Z%h8-)H^^P{%01NON_O^OH z^!t4zT*tQeb;k^wRgAp%%H!r40#zGw^{{iNwGEMpGg6qg9;WeRa~PT8sBJ7#4HkIGVt45vgb{2`dqg#mVAOxmK9ZU@sb(c_*Pp@%0 zdBX9Jcj-qwt6nd)8wwERTz-LX`31<5^|q}q(`>vCZYT*6t(XZxk;HUVc%L&q*ecvS zJS?RdN6;r>ScPZ&^d}03>-|2?<5@o@%`@8(*&+_xxU9YlG!!H{1opNrE$4M;hLW&h zBY(L2&D-b*6E)r+sO0VQkSaEd!VU*T+NvBl>*6q*7e~B$Y~ux9oDI7YtR96i+>d8E zCJKzT1uz<&Jvj=KMK8UY7sKA~+TE@pGNo}i;JINc(r6Jgfr}zP;)C&OrV0mq``ItAA(iUc+V`yS!H94t40H zOUZr0!;M=lR;Wrwhos(DQcUFmbvrGFr^K+&IitPmOpVz1hFB?@y@L8K=|C}E+1_+? zo6m}v7Ix^G<-rhnRJk6W~H%JG|KRiimaFS+FHtpNAM`WOGnfEnZ8sh=_!U98_sp*Wv^9f>Gf}+ z)y-iT($bDuuCWwGBM%|(LDsc2%zaW`#}QXA@1!OnmXr8Qx3YmcA$8ICK7U(jFBG*b zff!KK_Y!@aAxwMawHzUg>tvCrw%zG9$qeJ(nUaY|6?tcO@|fC94Z4*a>dhYrUgQz> zsH@mEzc$V9#n(6G7g1q?c+ZZV90ViGl*eGz;D1o}zkk8qD4@qAN3~MmA4&?qd@{jlTqkB*# z%CslPoPgs4exfNQzOBGA`luI#qm(?xch+KTi-@Ic<>7U##AibvG`rCb)sMRUrdmo= z2%QPw(7N9Qd4%qZw&j)3`KqKms_XEusBJxZ7m#~(@%fXygazzb@;)q2Y^$a9>;J-- zaybaS*4>Nc)ar3Bnaf3GB8YaOc20TM(~&j#ftSZ73pHg)v%7R+6(+*I?Drrw)lP8* z&q=v*A}0G=ZIba!HCWItAp8kthtC2?f&5ZXT8iP)_|g)S6%~Id4r7~z)%c4T%6+B0 zaH+K*Or`H2LX}|sDbo2x1G1{9`>F_YIy=Lqdh8f@7&|aUe3^ic_+wA^EgYDaemz3G z>!Chr{L=IOy8=Fi7)Cr#4ocqsDQ{-3K}0|K)mMfwAyw3z3^7bEGP?xN6bmNM5=`Lx z?uGU|!xvcfdX#5AKj)&Kd2Co~O1U84SPQ7ft%e}(3W^4np&o`Ds3&JlZ@31J0(oDLNbXV_tv za%Uy7J0d+h5o0SweK@u{j=U=p1-v)X4IyX-*)po;+WwcVc&Fqry%|bqB)q6+R^zb{c zk#iW3>bj=89+ITeT^DcY8a(@&V81k8Ik|#%P;Bjz%a;n+#8t5ITPP}mRL6H-CY-*I zes8!C@0Vb@a>}sl^!f@qWc-%<__F1AYO+(Fb4suF>C#ry6{|H>qA7dH zq@6-{B3%_KmeNybV84nSD|C~07crrEjW;#{%U37KLPY0t9x+8W2w(5Gl71shR4);k zen>8AsqT)A^gxR})>lqdI7S~!8rtb8&m;5Y3Lb2GeFU)eXr4@v?+KSP;zkLl^vIs5ILf7~O>R#RnSjMa_mq)2 zcK0xpyDiS?fDWiNv#fj>Iv`AXj?zmA_;Wz10A2J^D+|+-J zIV2Idj8PGp$r8P#^KVh@FSA_;?>!noU<`Cg+X0OU?IH#TlN-`NpVkl}$#v|(H@1<0 zj#xSWJz1PB6AUUxy_}PC0N&6;Nxp~_YV3F-S4=|O*nWIOtl9t0joY1>x#;`uHDQA; z)Ub;&?p65GMvcTra@pEvbWIY&6+Xs9+MZD?mQ%CSk{K4JGIW$6f&1o%Wo}R;jg*o^ zJM!7q*Go!`yqjs6{v5MWzl@UDc#~jJcjY7h?v4;~g6vIqdp=&yx^qg?y@lE<%o`V< zld3-`371#eUn=jYkJlx%-~isv6}udwEKu}1eps7~ai-;LT{LMr039h3M2@9o2VuA_ zm5Bcl>R64Kyjs`XeL8X8U~hoX+3OtP**(751Pn>Dx|u^R`qMq)=iHI~25nqvY;UHD z=Yan=WM`4tIF9**W=1~^6?;4lg0%-Q3>);hvH5VT*SOcEzQ-Ru``*1%lfvd5l(2er zBX2)-clkUrQ{1r*b)BF16|xlL4Gh3EMu;+1AH@aiKM;E?;#`7NxftS;*-9f*r1&Eh zS4FGCi8DMbzV)4b@}GB?deBXaY!#3Q=Vd=0I@K2W4YzIo5TG059j`s;5YfI_%6~CE~u0%)@gmve?_j$fsGyDSsm5Vm$;6 zK=2Q^G(7H{2K^BOf&CI!BjujBV@zRf?UM2GCkp3os23}uy^Cnp1MJ$P-FmI|Q5O;$H8?myVr`HlGTKOHXf=`&L%lUYs?pKSe`xos*z( z?)Kd;NJH~~dXg_0s}i=pF1b~SeF~(Oq{ElS$KCzD8STs85yViBiEU%Z@S5W5mW)b9 zu}7w>L#uT%i=?!M9o-0$qg*qo$T%EO2fMEvU+XRzdIh=xK@)Zhd5!7Wtr%7tQGkU{ zk9Xm@RjxfW4U=Z|l{n;aYvbI$=b%Ui+S95-XZPFq7d2n9b zWf=`(-N@~lMe6i(&UuXX6-ldWI&N|Ngmd|m(cf$5ZTRYjx-6!5x}=Blkvfg0v&0n3 z%r3kuLiZBn5lk+zY>OiZqFRw<)Trb4H~|s;(#pOf%}<9w1E~|f+cMDs!%Y1Ny+>_< z|NVUSctG|`EJK|(VL_ln^mG_El0Q(_dGRP`dX-c*Udc(ILXE|Gy75Wl>7B8c zaxl%)Edt!8xBhZU@@&`O9^);km_qq7Cns^Uy}0I8rbeJ(Sb3|x5IXs8pzp9uaHA}H zCi+(KjSDkoiUI~G*|Ent>5*|g8Wk_aZiC)_8K&mLn!YNtwTYB2fS!D0nJXz_p%UoR zY1SnLNV)9PuKBoR0G{Y46Mkpq^-Dhoz&(ggdKZcK_$Rgnso{zsi|-Mx|)?#{P!F z?~F>Is;qDW=lB~!Vez?Rk6&G7Z-Y%6O@w^(Kc#zl$4p&-Fx2t<^_!@ z>sDCFVT7U*4`Ho)7&hiA-r_a<@}AlGwb-O`jG_t*6Oon1{h>*@q)|K5C zH&oGc4STXuv{bO~nAA;v;@Ggo3YTrlJ~mr#3P!Mx^CV&4jc`zerLSPQ^!W(4UX@5; zlgW=*?VFf<)^)p6`f|SH(v0$NF--&w+x1=w5rix*dvnHw;&abFl{=XoMAp=MC%?x% zSsQPUy|iqc%&7lBNaobtgLbZ+a^ov~y&+94(pr`Ay(po zCDTJi%hmOYTlH3#8y41Zsl(d!izNXyeKw3Pp3i$tO2WH>5>z$5KYe%-?=g?2M%tqrP!o8vm)R=UcF4edAxdaF`@IG%PKKDD zF^5BN;OmX{3wxX`ox-7ZbzYglH{T@mgA!$w;S1SfaG0e1B?*+j0E!ihD`Ynbt;Lki z_Ap6R0qVuXlw{W>=spa;-Lb%=C$dH5mmccO-*L^Rs5oBeDWGA1WOCTMB?WrESBq!? zlmD81Q4wf)a@>p!bAilFHy9?hIz(E5AKn=n)E-ZvH87PpBuF%L8AFC%f!osnsz=FO z@t4SN`-o&2$e~_aQ~xYT!?hBZ4R%2Osi*T7LG3(Yt53c5RiXh%m2Kfo0>w4=Jh0vl zIrOZnS;xR#8((gE;(;`R?1+Yc+A_n5vtf_uCYm;ND^w$-Ni?I~4l2Z=?(TU2(*>RD z+faP)yY{}laUlStTXRREx;ngr*4rsW0H#du^`I>`um83)|29ryTSS%^g# zX)mk5$ai~N$eGrQS_*6dLG3(-Iy}mV{>q=YrBmqzRxx2&Ra%Pn+Iv6HhEL%h@Sp%5 z|2eojCv4tqI&VI@M@3>!(7y)e*wZ}xy8D1;h5LU>1OaD0n>c$nP`I5J#cb8-cY8eL za$lrJVgU689n@b$mD~p1>Y-l0O8gG8brWk%{!PX0FIP_JMu%|KoMPi-pa}FNJ(q z@dbtl;aLFt>G$Z;hu36F(f!1*FeBX`Z9r5l+eQPmY%!ilD-5P5MFCHmVikc3`DYku1aA-{@D`7Lq{x5 z(_mk($*R|m$=4M67Sum0ge&^bPM;xljRgkOG~2 zunUbPDxBOo`RUsmV_+>~pR~ded=;%93^9KFd^{)$nhc_60OZgJVk{A`Icw2;G#SZ- zz#wJ7VW5#dDu6?kMI7v>AMpYj8>(ay1HzafW(P|Em>hHv_>5@vlm45C+~%f(KY_l~ zjH}JalSt44^aKo~>0~EI?7y(v>VJgN=(IJEN2hFJyM1%@{*BcJi&GOeaY*-qvCz5% ze4c0#&0lw${c7nBD64y+On1rcH|EDrza&_75b`|$<(avxBzu_lfMdMLT>Ej1`_?3u zT1!Q3VF%4W2RT~goc(S1w~vF=i8G&-L3{_&6;bULRs-VIT{%{%`i=3iJP>rr3g<_b z&(-1zz!_Hg{T038})r@(=OmU#9;s^!k%;UR(nF@ZXQULmw_LE{66}e$ZA%+2rsR zV1@A_$5LsZ_EUgDMMA7Z8MvI-FTxW_zi7J3$s@FOPxlIKh*>LD8m!0i08zA9a5u5> zgd!U={g$7hc_=XZAhwGB<-f(};RDy3PmSLO72O@l6Qa#5Lp>a44!1J|>WdbnZ)#&A zO4wFzd`*Yf`e)3eSWN&KRzN+sR7(@?=AdOcZAPWl&LBJJ?}`P?hDRS^24`A{VhfA@ z3EFmh3n%LzbA=!m>j2vbY{f>8-3c1!3;6PUNjyD}&;6ICF=@6lidg-flgdAX#K?_y z2I$4A?qaFOoRqJ~s`wJmukn1d!%7fQv2Y8-Y?THo_Ph9P6!xj9%{Z04`iT2KPGyC;z9InuR`+-mRuu^rX?XE=?!{34-1r>#Xgi z#<5BO<5xtTo7C-+2b4wUx7BU-L%?;BE!*hPXjKKnFTMqk6+Hmca zmvm^j9Z*kvh)D&8#m#nNzvB#mgRT;z>H7DdHF|%-T8@+UeqFd6+NS&C3Q>kW18^Oo zeGlXT#lefY?FXd!4-gLty*kX85Sc>i{ckZw<`$nfQ;nL$W!lr}bPN55m~bE96)hB% zvQ>v`t?>Qh-*k@(!_o?&ggvtM-_?9nF77?Q0maUpaBB#YIgTJB(@h#-)=E}TbOAUu zslX>&s27JrRE{IEqX(l8_v8roDgYAft>Out8IhSR2f}!)$oqIB)Lpq9HVnwj2INSw#{a7e4eC-T_wY`6__tqf$4Jw#cDs_oVkYG4Me(Tf=71*`1Yc-ZLwu$EZOYj=|gu$wW%w^<3*DJB>zeS9ejC=qQnA6BOI%Hn47NHD~C1TX_F%6 zRpp787#W**V?2twyCk@j{rMf~VdXh;n)g|6lL>M(4J59D#~XsjFSIhNHMTr%?u$Fz zK+0Jq92jN_+4Ye9N#l)t0K%oel+{RYmDpa%l)&je`>up$GTx|~3mY`Sj)C7C$&vqF7*fMVC2weZJ%SHb=(C266dkT^m4I--bDj=7U)0&sFv6 z&EJ;IVH&sP_OYjKRQ7#4b(6X%m6y+;)}u;lkgQFF>N%#0y*tw#l-H!p><+->F$Wdt z&^_G6I}TWV%H4M(pGmlxaM*xzDBr4xq1dv%J*jF72+={7z0_Wc982e;x6~Q~BhCuY zfml~wAG^^hTHoz!%nn4@M=oIVo978DNR`Y!%XGw!QjR{Rt}h1ZN3KzL)*lR&o5B=< z$lktj8vT1((I6auP*N)}x_AyZq(~gR)fsn~qFG6CtRE%X5dI+1iOB z8ue>=UMZV&E{7(4+S7_fRO+(jZI7V&Yt;v%>_PN zJ`U6IN*1miB22Jp1zm<0-@@gt4+PV6z{Tly6DJqL%fPmx-ARK@$jH5&^%Oka4A@xR z0YPtmTAVF#jy2Tw4m9i$)3*Xh6oPQ?XmJE9&>|CphI(m!eJhZP3qk&lH1~VrV=&iH ziX%wFmjC<;B*a3{o+i3u8>_x^60zwu*#MpksDsAt#$MQIW3g#cZY*zwe0%|a4XDik zZjf{e%>r%!a%UR*t03SPfnNfH_*X%|l!TuP%Ms@HFq0D+H!y@*Nw0#y#)dD^IxW5I z2H{>XPLS#hvLr~?aqfg4_M>+>1f6%L^K?N)xtDNck)Ql$JF8n`Y8 zYw@awCP7jBrj1jZi>nBJe9Zw_Vp0!`s0;8r`T;Rrs6t|^1e}xwUZMd=n1>{Qu}qtH z7>y}$fEHNL(YQ<)e{GWh|7fqIrFHHDoJ)9|v?$tJLp5CyqUI(TCWA?Mz57RC>o1V8 zP1_wX?oA8=NlWlPg1u&mLGa>%Nrr18yb1zsV&R#jRX`91TOWGb)Ilq~$Omt6Fh5{V z&rcJCPh>@IZs+wyuo5?E-U7nz6Is!5H8k(b`WX1}E_fwDAyjwZB!E820gu6ns7pS4 zfTat+1O}sie1O{kKbID0{RaG5w4DUIwS4$McP{u6&7QR~1NIj@P8t`Bwy##L>6ntf zu_|by=An!}eWQ*888Q+jBykULO2KZBw*WaO612s00FDh%5K{sGc-l_4R;SGmEvJE)>c4GLfH{4;=(7HR^K;zMw zAY2M>S%u4H4DE{v}~^MwNi%X``(cY3SVh|4aV3Msk7Ci=`oy&>rN}J ztcpNp{r^Az`!jHC&AXpB>Hz-f@&LH$gwsV`r#q%j2wC&{2=E^yE-Ef6Bq}2$DsfX> wO!lmltf;tvsHm){sKQp3=fB-xYk$Yu!tKAlLDRTE7#^1DMGfVgzf64p7ezPB6951J literal 0 HcmV?d00001 diff --git a/docs/examples/index.rst b/docs/examples/index.rst index 244569b2..2d3545f2 100644 --- a/docs/examples/index.rst +++ b/docs/examples/index.rst @@ -2,23 +2,15 @@ Examples ======== .. meta:: - :keywords: Python,DI,Dependency injection,IoC,Inversion of Control - :description: Current section of documentation is designed to provide - several example mini applications that are built on the top - of inversion of control principle and powered by - "Dependency Injector" framework. + :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Example + :description: Python dependency injection examples. -Current section of documentation is designed to provide several example mini -applications that are built according to the inversion of control principle -and powered by *Dependency Injector* framework. +Explore the examples to see the ``Dependency Injector`` in action. .. toctree:: :maxdepth: 2 - services_miniapp_v1 - services_miniapp_v2 - bundles_miniapp - use_cases_miniapp - password_hashing_miniapp - chained_factories - factory_of_factories + application-single-container + application-multiple-containers + +.. disqus:: diff --git a/docs/examples/services_miniapp_v1.rst b/docs/examples/services_miniapp_v1.rst deleted file mode 100644 index 5277c56c..00000000 --- a/docs/examples/services_miniapp_v1.rst +++ /dev/null @@ -1,73 +0,0 @@ -Services mini application example (v1 - multiple containers) ------------------------------------------------------------- - -.. meta:: - :description: "Services miniapp" is an example mini application that - consists from several services that have dependencies on - some standard and 3rd-party libraries for logging, - interaction with database and remote service via API. - "Services miniapp" example demonstrates usage of - Dependency Injector for creating several inversion of control / - dependency injection containers. - -"Services miniapp" is an example mini application that consists from several -services that have dependencies on some standard and 3rd-party libraries for -logging, interaction with database and remote service calls via API. - -"Services miniapp" example demonstrates usage of -:doc:`Dependency Injector <../index>` for creating several IoC containers. - -Instructions for running: - -.. code-block:: bash - - python run.py 1 secret photo.jpg - -Example application -~~~~~~~~~~~~~~~~~~~ - -Classes diagram: - -.. image:: /images/miniapps/services/classes.png - :width: 100% - :align: center - - -Example application structure: - -.. code-block:: bash - - /example - /__init__.py - /main.py - /services.py - - -Listing of ``example/services.py``: - -.. literalinclude:: ../../examples/miniapps/services_v1/example/services.py - :language: python - -Listing of ``example/main.py``: - -.. literalinclude:: ../../examples/miniapps/services_v1/example/main.py - :language: python - -IoC containers -~~~~~~~~~~~~~~ - -Listing of ``containers.py``: - -.. literalinclude:: ../../examples/miniapps/services_v1/containers.py - :language: python - -Run application -~~~~~~~~~~~~~~~ - -Listing of ``run.py``: - -.. literalinclude:: ../../examples/miniapps/services_v1/run.py - :language: python - - -.. disqus:: diff --git a/docs/examples/services_miniapp_v2.rst b/docs/examples/services_miniapp_v2.rst deleted file mode 100644 index 4c29fca7..00000000 --- a/docs/examples/services_miniapp_v2.rst +++ /dev/null @@ -1,73 +0,0 @@ -Services mini application example (v2 - single container) ---------------------------------------------------------- - -.. meta:: - :description: "Services miniapp" is an example mini application that - consists from several services that have dependencies on - some standard and 3rd-party libraries for logging, - interaction with database and remote service via API. - "Services miniapp" example demonstrates usage of - Dependency Injector for creating inversion of control / - dependency injection container. - -"Services miniapp" is an example mini application that consists from several -services that have dependencies on some standard and 3rd-party libraries for -logging, interaction with database and remote service calls via API. - -"Services miniapp" example demonstrates usage of -:doc:`Dependency Injector <../index>` for creating IoC container. - -Instructions for running: - -.. code-block:: bash - - python run.py 1 secret photo.jpg - -Example application -~~~~~~~~~~~~~~~~~~~ - -Classes diagram: - -.. image:: /images/miniapps/services/classes.png - :width: 100% - :align: center - - -Example application structure: - -.. code-block:: bash - - /example - /__init__.py - /main.py - /services.py - - -Listing of ``example/services.py``: - -.. literalinclude:: ../../examples/miniapps/services_v2/example/services.py - :language: python - -Listing of ``example/main.py``: - -.. literalinclude:: ../../examples/miniapps/services_v2/example/main.py - :language: python - -IoC container -~~~~~~~~~~~~~ - -Listing of ``container.py``: - -.. literalinclude:: ../../examples/miniapps/services_v2/container.py - :language: python - -Run application -~~~~~~~~~~~~~~~ - -Listing of ``run.py``: - -.. literalinclude:: ../../examples/miniapps/services_v2/run.py - :language: python - - -.. disqus:: diff --git a/docs/images/miniapps/services/classes.png b/docs/images/miniapps/services/classes.png deleted file mode 100644 index 8e9d29f7d97114cc16de01fdd3bc420d766ac546..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49929 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 Qg@fqoY8z-#Z(G0oFB0h7X#fBK diff --git a/docs/index.rst b/docs/index.rst index e1eb64c7..e12ca2fb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -129,11 +129,11 @@ Contents :maxdepth: 2 introduction/index - main/installation + examples/index tutorials/index providers/index containers/index - examples/index + examples-other/index api/index main/feedback main/changelog diff --git a/docs/introduction/di_in_python.rst b/docs/introduction/di_in_python.rst index 644fef9f..fe56d0ca 100644 --- a/docs/introduction/di_in_python.rst +++ b/docs/introduction/di_in_python.rst @@ -3,90 +3,90 @@ Dependency injection and inversion of control in Python .. meta:: :keywords: Python,DI,Dependency injection,IoC,Inversion of Control - :description: This article describes benefits of dependency injection and - inversion of control for Python applications. Also it - contains some Python examples that show how dependency - injection and inversion could be implemented. In addition, it - demonstrates usage of dependency injection framework, + :description: This article describes benefits of dependency injection and + inversion of control for Python applications. Also it + contains some Python examples that show how dependency + injection and inversion could be implemented. In addition, it + demonstrates usage of dependency injection framework, IoC container and such popular design pattern as Factory. History ~~~~~~~ -Originally, dependency injection pattern got popular in languages with static -typing, like Java. Dependency injection framework can +Originally, dependency injection pattern got popular in languages with static +typing, like Java. Dependency injection framework can significantly improve flexibility of the language with static typing. Also, -implementation of dependency injection framework for language with static -typing is not something that one can do shortly, it could be quite complex +implementation of dependency injection framework for language with static +typing is not something that one can do shortly, it could be quite complex thing to be done well. -While Python is very flexible interpreted language with dynamic typing, there -is a meaning that dependency injection doesn't work for it as well, as it does -for Java. Also there is a meaning that dependency injection framework is +While Python is very flexible interpreted language with dynamic typing, there +is a meaning that dependency injection doesn't work for it as well, as it does +for Java. Also there is a meaning that dependency injection framework is something that Python developer would not ever need, cause dependency injection could be implemented easily using language fundamentals. Discussion ~~~~~~~~~~ -It is true. +It is true. Partly. -Dependency injection, as a software design pattern, has number of +Dependency injection, as a software design pattern, has number of advantages that are common for each language (including Python): + Dependency Injection decreases coupling between a class and its dependency. -+ Because dependency injection doesn't require any change in code behavior it - can be applied to legacy code as a refactoring. The result is clients that - are more independent and that are easier to unit test in isolation using - stubs or mock objects that simulate other objects not under test. This ease - of testing is often the first benefit noticed when using dependency ++ Because dependency injection doesn't require any change in code behavior it + can be applied to legacy code as a refactoring. The result is clients that + are more independent and that are easier to unit test in isolation using + stubs or mock objects that simulate other objects not under test. This ease + of testing is often the first benefit noticed when using dependency injection. -+ Dependency injection can be used to externalize a system's configuration - details into configuration files allowing the system to be reconfigured - without recompilation (rebuilding). Separate configurations can be written - for different situations that require different implementations of ++ Dependency injection can be used to externalize a system's configuration + details into configuration files allowing the system to be reconfigured + without recompilation (rebuilding). Separate configurations can be written + for different situations that require different implementations of components. This includes, but is not limited to, testing. -+ Reduction of boilerplate code in the application objects since all work to ++ Reduction of boilerplate code in the application objects since all work to initialize or set up dependencies is handled by a provider component. -+ Dependency injection allows a client to remove all knowledge of a concrete - implementation that it needs to use. This helps isolate the client from the - impact of design changes and defects. It promotes reusability, testability ++ Dependency injection allows a client to remove all knowledge of a concrete + implementation that it needs to use. This helps isolate the client from the + impact of design changes and defects. It promotes reusability, testability and maintainability. -+ Dependency injection allows a client the flexibility of being configurable. - Only the client's behavior is fixed. The client may act on anything that ++ Dependency injection allows a client the flexibility of being configurable. + Only the client's behavior is fixed. The client may act on anything that supports the intrinsic interface the client expects. .. note:: - While improved testability is one the first benefits of using dependency - injection, it could be easily overwhelmed by monkey-patching technique, - that works absolutely great in Python (you can monkey-patch anything, - anytime). At the same time, monkey-patching has nothing similar with - other advantages defined above. Also monkey-patching technique is + While improved testability is one the first benefits of using dependency + injection, it could be easily overwhelmed by monkey-patching technique, + that works absolutely great in Python (you can monkey-patch anything, + anytime). At the same time, monkey-patching has nothing similar with + other advantages defined above. Also monkey-patching technique is something that could be considered like too dirty to be used in production. -The complexity of dependency injection pattern implementation in Python is -definitely quite lower than in other languages (even with dynamic typing). +The complexity of dependency injection pattern implementation in Python is +definitely quite lower than in other languages (even with dynamic typing). -.. note:: +.. note:: - Low complexity of dependency injection pattern implementation in Python - still means that some code should be written, reviewed, tested and + Low complexity of dependency injection pattern implementation in Python + still means that some code should be written, reviewed, tested and supported. -Talking about inversion of control, it is a software design principle that +Talking about inversion of control, it is a software design principle that also works for each programming language, not depending on its typing type. -Inversion of control is used to increase modularity of the program and make +Inversion of control is used to increase modularity of the program and make it extensible. Main design purposes of using inversion of control are: + To decouple the execution of a task from implementation. + To focus a module on the task it is designed for. -+ To free modules from assumptions about how other systems do what they do and ++ To free modules from assumptions about how other systems do what they do and instead rely on contracts. + To prevent side effects when replacing a module. @@ -115,9 +115,9 @@ Next example demonstrates creation of several cars with different engines: :language: python While previous example demonstrates advantages of dependency injection, there -is a disadvantage demonstration as well - creation of car requires additional -code for specification of dependencies. Nevertheless, this disadvantage could -be easily avoided by using a dependency injection framework for creation of +is a disadvantage demonstration as well - creation of car requires additional +code for specification of dependencies. Nevertheless, this disadvantage could +be easily avoided by using a dependency injection framework for creation of inversion of control container (IoC container). Example of creation of several inversion of control containers (IoC containers) @@ -131,18 +131,21 @@ What's next? Choose one of the following as a next step: -+ Pass one of the dependency injection tutorials: - + :ref:`flask-tutorial` - + :ref:`aiohttp-tutorial` - + :ref:`asyncio-daemon-tutorial` - + :ref:`cli-tutorial` -+ Know more about the :ref:`providers` -+ Go to the :ref:`contents` +- Look at application examples: + - :ref:`application-single-container` + - :ref:`application-multiple-containers` +- Pass the tutorials: + - :ref:`flask-tutorial` + - :ref:`aiohttp-tutorial` + - :ref:`asyncio-daemon-tutorial` + - :ref:`cli-tutorial` +- Know more about the :ref:`providers` +- Go to the :ref:`contents` Useful links ~~~~~~~~~~~~ -There are some useful links related to dependency injection design pattern +There are some useful links related to dependency injection design pattern that could be used for further reading: + https://en.wikipedia.org/wiki/Dependency_injection diff --git a/docs/introduction/index.rst b/docs/introduction/index.rst index 0c3588de..2a4c77ff 100644 --- a/docs/introduction/index.rst +++ b/docs/introduction/index.rst @@ -17,3 +17,4 @@ dependency injection pattern, inversion of control principle and what_is_di di_in_python key_features + installation diff --git a/docs/main/installation.rst b/docs/introduction/installation.rst similarity index 98% rename from docs/main/installation.rst rename to docs/introduction/installation.rst index dcd8e3d3..bfd72043 100644 --- a/docs/main/installation.rst +++ b/docs/introduction/installation.rst @@ -31,7 +31,7 @@ Verification of currently installed version could be done using >>> import dependency_injector >>> dependency_injector.__version__ - '3.15.2' + '3.38.0' .. _PyPi: https://pypi.org/project/dependency-injector/ .. _GitHub: https://github.com/ets-labs/python-dependency-injector diff --git a/docs/introduction/what_is_di.rst b/docs/introduction/what_is_di.rst index 1288bfcb..9b9d1248 100644 --- a/docs/introduction/what_is_di.rst +++ b/docs/introduction/what_is_di.rst @@ -175,13 +175,16 @@ What's next? Choose one of the following as a next step: -+ Pass one of the tutorials: - + :ref:`cli-tutorial` - + :ref:`flask-tutorial` - + :ref:`aiohttp-tutorial` - + :ref:`asyncio-daemon-tutorial` -+ Know more about the :ref:`providers` -+ Go to the :ref:`contents` +- Look at application examples: + - :ref:`application-single-container` + - :ref:`application-multiple-containers` +- Pass the tutorials: + - :ref:`flask-tutorial` + - :ref:`aiohttp-tutorial` + - :ref:`asyncio-daemon-tutorial` + - :ref:`cli-tutorial` +- Know more about the :ref:`providers` +- Go to the :ref:`contents` .. disqus:: diff --git a/examples/miniapps/application-multiple-containers/README.rst b/examples/miniapps/application-multiple-containers/README.rst new file mode 100644 index 00000000..fe9840de --- /dev/null +++ b/examples/miniapps/application-multiple-containers/README.rst @@ -0,0 +1,29 @@ +Application example (multiple containers) +========================================= + +Create virtual env: + +.. code-block:: bash + + python3 -m venv venv + . venv/bin/activate + +Install requirements: + +.. code-block:: bash + + pip install -r requirements.txt + +Run: + +.. code-block:: bash + + python -m example user@example.com secret photo.jpg + +You should see: + +.. code-block:: bash + + [2020-09-04 16:06:00,750] [DEBUG] [example.services.UserService]: User user@example.com has been found in database + [2020-09-04 16:06:00,750] [DEBUG] [example.services.AuthService]: User user@example.com has been successfully authenticated + [2020-09-04 16:06:00,750] [DEBUG] [example.services.PhotoService]: Photo photo.jpg has been successfully uploaded by user user@example.com diff --git a/examples/miniapps/application-multiple-containers/config.yml b/examples/miniapps/application-multiple-containers/config.yml new file mode 100644 index 00000000..6a568526 --- /dev/null +++ b/examples/miniapps/application-multiple-containers/config.yml @@ -0,0 +1,30 @@ +core: + + logging: + version: 1 + formatters: + formatter: + format: "[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s" + handlers: + console: + class: "logging.StreamHandler" + level: "DEBUG" + formatter: "formatter" + stream: "ext://sys.stderr" + root: + level: "DEBUG" + handlers: ["console"] + +gateways: + + database: + dsn: ":memory:" + + aws: + access_key_id: "KEY" + secret_access_key: "SECRET" + +services: + + auth: + token_ttl: 3600 diff --git a/examples/miniapps/services_v1/example/__init__.py b/examples/miniapps/application-multiple-containers/example/__init__.py similarity index 100% rename from examples/miniapps/services_v1/example/__init__.py rename to examples/miniapps/application-multiple-containers/example/__init__.py diff --git a/examples/miniapps/application-multiple-containers/example/__main__.py b/examples/miniapps/application-multiple-containers/example/__main__.py new file mode 100644 index 00000000..1d8eefc4 --- /dev/null +++ b/examples/miniapps/application-multiple-containers/example/__main__.py @@ -0,0 +1,24 @@ +"""Main module.""" + +import sys + +from .containers import Application + + +def main(email: str, password: str, photo: str) -> None: + application = Application() + + application.config.from_yaml('config.yml') + application.core.configure_logging() + + user_service = application.services.user() + auth_service = application.services.auth() + photo_service = application.services.photo() + + user = user_service.get_user(email) + auth_service.authenticate(user, password) + photo_service.upload_photo(user, photo) + + +if __name__ == '__main__': + main(*sys.argv[1:]) diff --git a/examples/miniapps/application-multiple-containers/example/containers.py b/examples/miniapps/application-multiple-containers/example/containers.py new file mode 100644 index 00000000..5bcec302 --- /dev/null +++ b/examples/miniapps/application-multiple-containers/example/containers.py @@ -0,0 +1,80 @@ +"""Containers module.""" + +import logging.config +import sqlite3 + +import boto3 +from dependency_injector import containers, providers + +from . import services + + +class Core(containers.DeclarativeContainer): + + config = providers.Configuration() + + configure_logging = providers.Callable( + logging.config.dictConfig, + config=config.logging, + ) + + +class Gateways(containers.DeclarativeContainer): + + config = providers.Configuration() + + database_client = providers.Singleton( + sqlite3.connect, + config.database.dsn, + ) + + s3_client = providers.Singleton( + boto3.client, + service_name='s3', + aws_access_key_id=config.aws.access_key_id, + aws_secret_access_key=config.aws.secret_access_key, + ) + + +class Services(containers.DeclarativeContainer): + + config = providers.Configuration() + gateways = providers.DependenciesContainer() + + user = providers.Factory( + services.UserService, + db=gateways.database_client, + ) + + auth = providers.Factory( + services.AuthService, + db=gateways.database_client, + token_ttl=config.auth.token_ttl.as_int(), + ) + + photo = providers.Factory( + services.PhotoService, + db=gateways.database_client, + s3=gateways.s3_client, + ) + + +class Application(containers.DeclarativeContainer): + + config = providers.Configuration() + + core = providers.Container( + Core, + config=config.core, + ) + + gateways = providers.Container( + Gateways, + config=config.gateways, + ) + + services = providers.Container( + Services, + config=config.services, + gateways=gateways, + ) diff --git a/examples/miniapps/application-multiple-containers/example/services.py b/examples/miniapps/application-multiple-containers/example/services.py new file mode 100644 index 00000000..338888e3 --- /dev/null +++ b/examples/miniapps/application-multiple-containers/example/services.py @@ -0,0 +1,56 @@ +"""Services module.""" + +import logging +import sqlite3 +from typing import Dict + +from mypy_boto3_s3 import S3Client + + +class BaseService: + + def __init__(self) -> None: + self.logger = logging.getLogger( + f'{__name__}.{self.__class__.__name__}', + ) + + +class UserService(BaseService): + + def __init__(self, db: sqlite3.Connection) -> None: + self.db = db + super().__init__() + + def get_user(self, email: str) -> Dict[str, str]: + self.logger.debug('User %s has been found in database', email) + return {'email': email, 'password_hash': '...'} + + +class AuthService(BaseService): + + def __init__(self, db: sqlite3.Connection, token_ttl: int) -> None: + self.db = db + self.token_ttl = token_ttl + super().__init__() + + def authenticate(self, user: Dict[str, str], password: str) -> None: + assert password is not None + self.logger.debug( + 'User %s has been successfully authenticated', + user['email'], + ) + + +class PhotoService(BaseService): + + def __init__(self, db: sqlite3.Connection, s3: S3Client) -> None: + self.db = db + self.s3 = s3 + super().__init__() + + def upload_photo(self, user: Dict[str, str], photo_path: str) -> None: + self.logger.debug( + 'Photo %s has been successfully uploaded by user %s', + photo_path, + user['email'], + ) diff --git a/examples/miniapps/application-multiple-containers/requirements.txt b/examples/miniapps/application-multiple-containers/requirements.txt new file mode 100644 index 00000000..5d5e226a --- /dev/null +++ b/examples/miniapps/application-multiple-containers/requirements.txt @@ -0,0 +1,3 @@ +dependency-injector[yaml] +boto3 +boto3-stubs[s3] diff --git a/examples/miniapps/application-single-container/README.rst b/examples/miniapps/application-single-container/README.rst new file mode 100644 index 00000000..100693b9 --- /dev/null +++ b/examples/miniapps/application-single-container/README.rst @@ -0,0 +1,29 @@ +Application example (single container) +====================================== + +Create virtual env: + +.. code-block:: bash + + python3 -m venv venv + . venv/bin/activate + +Install requirements: + +.. code-block:: bash + + pip install -r requirements.txt + +Run: + +.. code-block:: bash + + python -m example user@example.com secret photo.jpg + +You should see: + +.. code-block:: bash + + [2020-09-04 15:27:27,727] [DEBUG] [example.services.UserService]: User user@example.com has been found in database + [2020-09-04 15:27:27,727] [DEBUG] [example.services.AuthService]: User user@example.com has been successfully authenticated + [2020-09-04 15:27:27,727] [DEBUG] [example.services.PhotoService]: Photo photo.jpg has been successfully uploaded by user user@example.com diff --git a/examples/miniapps/application-single-container/config.ini b/examples/miniapps/application-single-container/config.ini new file mode 100644 index 00000000..4da3e155 --- /dev/null +++ b/examples/miniapps/application-single-container/config.ini @@ -0,0 +1,9 @@ +[database] +dsn=:memory: + +[aws] +access_key_id=KEY +secret_access_key=SECRET + +[auth] +token_ttl=3600 diff --git a/examples/miniapps/services_v2/example/__init__.py b/examples/miniapps/application-single-container/example/__init__.py similarity index 100% rename from examples/miniapps/services_v2/example/__init__.py rename to examples/miniapps/application-single-container/example/__init__.py diff --git a/examples/miniapps/application-single-container/example/__main__.py b/examples/miniapps/application-single-container/example/__main__.py new file mode 100644 index 00000000..a14d92c9 --- /dev/null +++ b/examples/miniapps/application-single-container/example/__main__.py @@ -0,0 +1,24 @@ +"""Main module.""" + +import sys + +from .containers import Container + + +def main(email: str, password: str, photo: str) -> None: + container = Container() + + container.configure_logging() + container.config.from_ini('config.ini') + + user_service = container.user_service() + auth_service = container.auth_service() + photo_service = container.photo_service() + + user = user_service.get_user(email) + auth_service.authenticate(user, password) + photo_service.upload_photo(user, photo) + + +if __name__ == '__main__': + main(*sys.argv[1:]) diff --git a/examples/miniapps/application-single-container/example/containers.py b/examples/miniapps/application-single-container/example/containers.py new file mode 100644 index 00000000..ccc6a2d1 --- /dev/null +++ b/examples/miniapps/application-single-container/example/containers.py @@ -0,0 +1,52 @@ +"""Containers module.""" + +import logging.config +import sqlite3 + +import boto3 +from dependency_injector import containers, providers + +from . import services + + +class Container(containers.DeclarativeContainer): + + config = providers.Configuration() + + configure_logging = providers.Callable( + logging.config.fileConfig, + fname='logging.ini', + ) + + # Gateways + + database_client = providers.Singleton( + sqlite3.connect, + config.database.dsn, + ) + + s3_client = providers.Singleton( + boto3.client, + service_name='s3', + aws_access_key_id=config.aws.access_key_id, + aws_secret_access_key=config.aws.secret_access_key, + ) + + # Services + + user_service = providers.Factory( + services.UserService, + db=database_client, + ) + + auth_service = providers.Factory( + services.AuthService, + db=database_client, + token_ttl=config.auth.token_ttl.as_int(), + ) + + photo_service = providers.Factory( + services.PhotoService, + db=database_client, + s3=s3_client, + ) diff --git a/examples/miniapps/application-single-container/example/services.py b/examples/miniapps/application-single-container/example/services.py new file mode 100644 index 00000000..338888e3 --- /dev/null +++ b/examples/miniapps/application-single-container/example/services.py @@ -0,0 +1,56 @@ +"""Services module.""" + +import logging +import sqlite3 +from typing import Dict + +from mypy_boto3_s3 import S3Client + + +class BaseService: + + def __init__(self) -> None: + self.logger = logging.getLogger( + f'{__name__}.{self.__class__.__name__}', + ) + + +class UserService(BaseService): + + def __init__(self, db: sqlite3.Connection) -> None: + self.db = db + super().__init__() + + def get_user(self, email: str) -> Dict[str, str]: + self.logger.debug('User %s has been found in database', email) + return {'email': email, 'password_hash': '...'} + + +class AuthService(BaseService): + + def __init__(self, db: sqlite3.Connection, token_ttl: int) -> None: + self.db = db + self.token_ttl = token_ttl + super().__init__() + + def authenticate(self, user: Dict[str, str], password: str) -> None: + assert password is not None + self.logger.debug( + 'User %s has been successfully authenticated', + user['email'], + ) + + +class PhotoService(BaseService): + + def __init__(self, db: sqlite3.Connection, s3: S3Client) -> None: + self.db = db + self.s3 = s3 + super().__init__() + + def upload_photo(self, user: Dict[str, str], photo_path: str) -> None: + self.logger.debug( + 'Photo %s has been successfully uploaded by user %s', + photo_path, + user['email'], + ) diff --git a/examples/miniapps/application-single-container/logging.ini b/examples/miniapps/application-single-container/logging.ini new file mode 100644 index 00000000..5108c567 --- /dev/null +++ b/examples/miniapps/application-single-container/logging.ini @@ -0,0 +1,21 @@ +[loggers] +keys=root + +[handlers] +keys=stream_handler + +[formatters] +keys=formatter + +[logger_root] +level=DEBUG +handlers=stream_handler + +[handler_stream_handler] +class=StreamHandler +level=DEBUG +formatter=formatter +args=(sys.stderr,) + +[formatter_formatter] +format=[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s diff --git a/examples/miniapps/application-single-container/requirements.txt b/examples/miniapps/application-single-container/requirements.txt new file mode 100644 index 00000000..0c4ba267 --- /dev/null +++ b/examples/miniapps/application-single-container/requirements.txt @@ -0,0 +1,3 @@ +dependency-injector +boto3 +boto3-stubs[s3] diff --git a/examples/miniapps/services_v1/README.rst b/examples/miniapps/services_v1/README.rst deleted file mode 100644 index 87373537..00000000 --- a/examples/miniapps/services_v1/README.rst +++ /dev/null @@ -1,8 +0,0 @@ -Dependency Injector IoC containers example -========================================== - -Instructions for running - -.. code-block:: bash - - python run.py 1 secret photo.jpg diff --git a/examples/miniapps/services_v1/containers.py b/examples/miniapps/services_v1/containers.py deleted file mode 100644 index d1ae0a52..00000000 --- a/examples/miniapps/services_v1/containers.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Example of dependency injection in Python.""" - -import logging -import sqlite3 - -import boto3 - -import example.main -import example.services - -import dependency_injector.containers as containers -import dependency_injector.providers as providers - - -class Core(containers.DeclarativeContainer): - """IoC container of core component providers.""" - - config = providers.Configuration('config') - - logger = providers.Singleton(logging.Logger, name='example') - - -class Gateways(containers.DeclarativeContainer): - """IoC container of gateway (API clients to remote services) providers.""" - - database = providers.Singleton(sqlite3.connect, Core.config.database.dsn) - - s3 = providers.Singleton( - boto3.client, 's3', - aws_access_key_id=Core.config.aws.access_key_id, - aws_secret_access_key=Core.config.aws.secret_access_key) - - -class Services(containers.DeclarativeContainer): - """IoC container of business service providers.""" - - users = providers.Factory(example.services.UsersService, - db=Gateways.database, - logger=Core.logger) - - auth = providers.Factory(example.services.AuthService, - db=Gateways.database, - logger=Core.logger, - token_ttl=Core.config.auth.token_ttl) - - photos = providers.Factory(example.services.PhotosService, - db=Gateways.database, - s3=Gateways.s3, - logger=Core.logger) - - -class Application(containers.DeclarativeContainer): - """IoC container of application component providers.""" - - main = providers.Callable(example.main.main, - users_service=Services.users, - auth_service=Services.auth, - photos_service=Services.photos) diff --git a/examples/miniapps/services_v1/example/main.py b/examples/miniapps/services_v1/example/main.py deleted file mode 100644 index 2f80a1c7..00000000 --- a/examples/miniapps/services_v1/example/main.py +++ /dev/null @@ -1,8 +0,0 @@ -"""Example main module.""" - - -def main(uid, password, photo, users_service, auth_service, photos_service): - """Authenticate user and upload photo.""" - user = users_service.get_user_by_id(uid) - auth_service.authenticate(user, password) - photos_service.upload_photo(user['uid'], photo) diff --git a/examples/miniapps/services_v1/example/services.py b/examples/miniapps/services_v1/example/services.py deleted file mode 100644 index 04206916..00000000 --- a/examples/miniapps/services_v1/example/services.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Example business services module.""" - - -class BaseService: - """Service base class.""" - - -class UsersService(BaseService): - """Users service.""" - - def __init__(self, logger, db): - """Initialize instance.""" - self.logger = logger - self.db = db - - def get_user_by_id(self, uid): - """Return user's data by identifier.""" - self.logger.debug('User %s has been found in database', uid) - return dict(uid=uid, password_hash='secret_hash') - - -class AuthService(BaseService): - """Authentication service.""" - - def __init__(self, logger, db, token_ttl): - """Initialize instance.""" - self.logger = logger - self.db = db - self.token_ttl = token_ttl - - def authenticate(self, user, password): - """Authenticate user.""" - assert user['password_hash'] == '_'.join((password, 'hash')) - self.logger.debug('User %s has been successfully authenticated', - user['uid']) - - -class PhotosService(BaseService): - """Photos service.""" - - def __init__(self, logger, db, s3): - """Initialize instance.""" - self.logger = logger - self.db = db - self.s3 = s3 - - def upload_photo(self, uid, photo_path): - """Upload user photo.""" - self.logger.debug('Photo %s has been successfully uploaded by user %s', - photo_path, uid) diff --git a/examples/miniapps/services_v1/run.py b/examples/miniapps/services_v1/run.py deleted file mode 100644 index da4f94b8..00000000 --- a/examples/miniapps/services_v1/run.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Run example application.""" - -import sys -import logging - -from containers import Core, Application - - -if __name__ == '__main__': - # Configure platform: - Core.config.override({'database': {'dsn': ':memory:'}, - 'aws': {'access_key_id': 'KEY', - 'secret_access_key': 'SECRET'}, - 'auth': {'token_ttl': 3600}}) - Core.logger().addHandler(logging.StreamHandler(sys.stdout)) - - # Run application: - Application.main(uid=sys.argv[1], - password=sys.argv[2], - photo=sys.argv[3]) diff --git a/examples/miniapps/services_v2/README.rst b/examples/miniapps/services_v2/README.rst deleted file mode 100644 index 87373537..00000000 --- a/examples/miniapps/services_v2/README.rst +++ /dev/null @@ -1,8 +0,0 @@ -Dependency Injector IoC containers example -========================================== - -Instructions for running - -.. code-block:: bash - - python run.py 1 secret photo.jpg diff --git a/examples/miniapps/services_v2/container.py b/examples/miniapps/services_v2/container.py deleted file mode 100644 index 8ca3c048..00000000 --- a/examples/miniapps/services_v2/container.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Example of dependency injection in Python.""" - -import logging -import sqlite3 - -import boto3 - -from dependency_injector import containers, providers -from example import services, main - - -class IocContainer(containers.DeclarativeContainer): - """Application IoC container.""" - - config = providers.Configuration('config') - logger = providers.Singleton(logging.Logger, name='example') - - # Gateways - - database_client = providers.Singleton(sqlite3.connect, config.database.dsn) - - s3_client = providers.Singleton( - boto3.client, 's3', - aws_access_key_id=config.aws.access_key_id, - aws_secret_access_key=config.aws.secret_access_key, - ) - - # Services - - users_service = providers.Factory( - services.UsersService, - db=database_client, - logger=logger, - ) - - auth_service = providers.Factory( - services.AuthService, - token_ttl=config.auth.token_ttl, - db=database_client, - logger=logger, - ) - - photos_service = providers.Factory( - services.PhotosService, - db=database_client, - s3=s3_client, - logger=logger, - ) - - # Misc - - main = providers.Callable( - main.main, - users_service=users_service, - auth_service=auth_service, - photos_service=photos_service, - ) diff --git a/examples/miniapps/services_v2/example/main.py b/examples/miniapps/services_v2/example/main.py deleted file mode 100644 index 2f80a1c7..00000000 --- a/examples/miniapps/services_v2/example/main.py +++ /dev/null @@ -1,8 +0,0 @@ -"""Example main module.""" - - -def main(uid, password, photo, users_service, auth_service, photos_service): - """Authenticate user and upload photo.""" - user = users_service.get_user_by_id(uid) - auth_service.authenticate(user, password) - photos_service.upload_photo(user['uid'], photo) diff --git a/examples/miniapps/services_v2/example/services.py b/examples/miniapps/services_v2/example/services.py deleted file mode 100644 index 04206916..00000000 --- a/examples/miniapps/services_v2/example/services.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Example business services module.""" - - -class BaseService: - """Service base class.""" - - -class UsersService(BaseService): - """Users service.""" - - def __init__(self, logger, db): - """Initialize instance.""" - self.logger = logger - self.db = db - - def get_user_by_id(self, uid): - """Return user's data by identifier.""" - self.logger.debug('User %s has been found in database', uid) - return dict(uid=uid, password_hash='secret_hash') - - -class AuthService(BaseService): - """Authentication service.""" - - def __init__(self, logger, db, token_ttl): - """Initialize instance.""" - self.logger = logger - self.db = db - self.token_ttl = token_ttl - - def authenticate(self, user, password): - """Authenticate user.""" - assert user['password_hash'] == '_'.join((password, 'hash')) - self.logger.debug('User %s has been successfully authenticated', - user['uid']) - - -class PhotosService(BaseService): - """Photos service.""" - - def __init__(self, logger, db, s3): - """Initialize instance.""" - self.logger = logger - self.db = db - self.s3 = s3 - - def upload_photo(self, uid, photo_path): - """Upload user photo.""" - self.logger.debug('Photo %s has been successfully uploaded by user %s', - photo_path, uid) diff --git a/examples/miniapps/services_v2/run.py b/examples/miniapps/services_v2/run.py deleted file mode 100644 index 30edbabf..00000000 --- a/examples/miniapps/services_v2/run.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Run example of dependency injection in Python.""" - -import sys -import logging - -from container import IocContainer - - -if __name__ == '__main__': - # Configure container: - container = IocContainer( - config={ - 'database': { - 'dsn': ':memory:', - }, - 'aws': { - 'access_key_id': 'KEY', - 'secret_access_key': 'SECRET', - }, - 'auth': { - 'token_ttl': 3600, - }, - } - ) - container.logger().addHandler(logging.StreamHandler(sys.stdout)) - - # Run application: - container.main(*sys.argv[1:])