From a746d168bf74cde2a8a95a8eef2710059cc35999 Mon Sep 17 00:00:00 2001 From: bielsnohr Date: Wed, 14 Aug 2024 12:33:46 +0100 Subject: [PATCH 01/16] Updates and modifications from June 2024 run at UKAEA --- .gitignore | 3 +- README.md | 8 +- _episodes/11-software-project.md | 1 + _extras/common-issues.md | 24 +++ fig/git-feature-branch.png | Bin 54204 -> 0 bytes slides/README.md | 16 +- slides/pyproject.toml | 2 + slides/requirements.txt | 4 +- slides/section_1_setting_up_environment.md | 9 +- slides/section_2_ensuring_correctness.md | 9 +- slides/section_3_software_dev_process.md | 187 +++++++++++++-------- slides/section_4_collaborative_soft_dev.md | 7 +- slides/section_5_managing_software.md | 7 +- 13 files changed, 171 insertions(+), 106 deletions(-) delete mode 100644 fig/git-feature-branch.png create mode 100644 slides/pyproject.toml diff --git a/.gitignore b/.gitignore index 5022ef274..d547a78c0 100644 --- a/.gitignore +++ b/.gitignore @@ -17,10 +17,11 @@ vendor/ .docker-vendor/ Gemfile.lock tmp/ -.venv/ venv/ .venv/ .*history .tool-versions .vscode/ .ruby-version +slides/*.ipynb +slides/*.html diff --git a/README.md b/README.md index 8566003e6..a73fbda34 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ ## Intermediate Research Software Development Skills (Python) -This is an intermediate-level course in collaborative research software engineering and development skills, -using Python as an example language. -It teaches these skills in a way that mimics a typical software development -process working as a part of a team, +This is an intermediate-level course in collaborative research software engineering and development skills, +using Python as an example language. +It teaches these skills in a way that mimics a typical software development +process working as a part of a team, starting from an [existing piece of software](https://github.com/carpentries-incubator/python-intermediate-inflammation). The lesson is developed using [The Carpentries](https://carpentries.org) Jekyll lesson template. diff --git a/_episodes/11-software-project.md b/_episodes/11-software-project.md index b7e85a026..4dd991414 100644 --- a/_episodes/11-software-project.md +++ b/_episodes/11-software-project.md @@ -20,6 +20,7 @@ patients’ inflammation data and performs basic statistical analysis using Pyth --- ## Patient Inflammation Study Project + You have joined a software development team that has been working on the [patient inflammation study project](https://github.com/carpentries-incubator/python-intermediate-inflammation) developed in Python and stored on GitHub. diff --git a/_extras/common-issues.md b/_extras/common-issues.md index dca82e168..875f37f1e 100644 --- a/_extras/common-issues.md +++ b/_extras/common-issues.md @@ -137,6 +137,30 @@ $ git config --global core.editor "nano -w" ~~~ {: .language-bash} + +## SSH key authentication issues with Windows Git + +Rather unhelpfully, Git for Windows will use it's own SSH instance by default +which will result in you getting errors such as the below even after adding your +SSH key correctly + +~~~ +PS > git clone git@github.com:https://github.com/ukaea-rse-training/python-intermediate-inflammation +Cloning into 'python-intermediate-inflammation'... +git@github.com: Permission denied (publickey). +fatal: Could not read from remote repository. + +Please make sure you have the correct access rights +and the repository exists. +~~~ + +The solution is to change the SSH instance used by default in newer versions of +Windows with: + +~~~ +PS > git config --global core.sshCommand C:/windows/System32/OpenSSH/ssh.exe +~~~ + ## Python, `pip`, `venv` & Installing Packages Issues ### Issues With Numpy (and Potentially Other Packages) on New M1 Macs diff --git a/fig/git-feature-branch.png b/fig/git-feature-branch.png deleted file mode 100644 index 517712b648dd5ee1010f13164198360f7d7bff5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54204 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 m!F&(mxWh-DLlYirqkpGS`F#2(11t{xkm zpTCj}K3uP-MIJNFpY2~h$fRjKk+hAIhx767K&9Si@uidx_!8b;matk{xBLQpKO}-~}(5VzqW)lioAAfql z{39+tevl~}+2sL?Nr?X10>2OlXRm%k7+UD6W%I&4_is05Vf`s_M|SiRMa=L_jg1w1 z@N2&|KT9(+Gps9cJa||{)bBE$&KL;kqo1b{jY0~5!+D6LZNhcMa$#*Ec(!ewicr)$oCa?ysAioOpokK=xz@MLmMsS#CLBF@3rKg1!@qIGA)n* zw`9Ke4;+rgAJmopiptyJ+?VxJ&`qQwwM)rw6H#Tq%Op4U*1Df8d`l$ZiHNg@Bxqy9 zsq})gn?$CC{)|6j`b5<+J3G5bG@j*QfAL_z=(JqaYQlWU5v~7jz_>o({K(bywPI-K zmKTs!RyKHBi*;y%i8{%!S+E#_VqxW`*K)4pBExmK!};mK%PTE;`CqSrgupm!-a5&E zuT2KZ&Q;w>zD1=?qQ-yRV%eX2C4`Vt%3oOU@bQ2pGz2X&~luvh=; z5={V**$mYf@usY~h=#31M?HAS@^xmt<7~@a*093sb3`$?wBbDUPN?pIgtX6KDZIM6 z`Yyob|MTh4^%pBjk8{t<5=1W(Yi@fa z4QAF)nVX4&?|0}hAMv@9k)~U%uLtFGe|ix3j;iN%NEe^r0|HCil!TuH)G7InR_n5& z*E1@01Ipiz#er3oXPwDUE}KDx@G0nDYkp7=2O0o7KKJ5$JG-xb#87l4pMbY0zdN@D zu4}Z&&uUVdq#1EddOQj5eJ+Hc<0H~Z9{tL{Z(tX*RTjNepfy$>ul;zuF)TBd*=`zi zzBUi~Kh?62UADi_I{D`oCOvZ{ga>t+I1- zJ07Sx)z#Ip4oE(I@}w&i3+(lt76DV|3%Td_@Z?TTeSbf{H+TKDQ^-S`Ien_EE(PyANupG>xj6gx79To}D4Fzy>tsOS*(N#&4&dik+Cti=K6Zw__(-MQdP6Bk5|)%Q=Qwzh=Q#m+nT5pB$K^yAG{#C* z1c&tO;kE?39FLfo7~fpxzu(bMwg#@;uWFuOKCs5f%QRn!1vhYT*#kkXVD?ibW=u%s zk|+480h1ak-2%Egl?kt(AL9Ym9v27kO-)VSpT?)+IrSEaWIYK7iErU%k`z>c{3JJX zc9aKUgw4A1mvOyp0x{rwdV0D&<&pdp$d-Qk!^#(XtyTZLta>rtY^1>PQI5 z<)5)2xQw-#ZX%CK-<`MZFBr$8cm@yX$0jDo_fv>kBs=>elDQ9RzM;P4+eHubJ4LNx zuC9Ace&Yns&l}Ix5$`#cjrQk9%^FtxlDN(E>9yZpd$(A{WjX6AzYf!<2Yh1)nwn&k z6rpEDbVkM^c?sPkZbOggP=t3R>8RGyY45-~+R32R=dg$X(gay3JM}W^bInm{T4C$v*9CaO?$^1BboDr*Gcu6A9V*ziU z39_At5>uQ(xb6A7C8p3>ULowuQ-`-wcZiz!6}{ zcO1npAONej9;Bfrc<}DKe$_j9U4Q?}C~%ABB1L&+wgJHlfEU?lko96_OpIG;T^;IZ zeK3{w!FmMN2#g*)8Mu18F`XyCb}?=x_xbmlUURt&`HnGY#t?n|{ghN+4rzh5_G#;N zVW1_{Rwnq>KtO1+8fbik&H~!=&AYw`RT$QBFcl1fo+^lXro?-B;J!5PgJo7vF7=x| z5T_D0(5v zz*WrfaRQz{3}pR~A#H$fP6HkowPdvB;)ya<$kU2X*&u6 zK94$-NkS|Zz7W%9pT1t)r}I6HxE>WZ&U`K~L{~S+%CX@IY6!VfkvR}#OVv{_^39-N zpw(==cH>@`%f|Y;6J&z+Dx82Q@N&D^!RB)0rdJD$u_P4+OjnwB?Fw163 z0b_Bk5F$3m{^8$mOrs~7>4Czcq8YpnXe7LPf5vugoYRKnpzd1sp+*21o;pYaZGgNp z@SM0~e4*8YRKVH0$(VJaRe>OyKD=y!GD2@psj3l>zEEPfqUup#P9a}r zm1e%U6HF_~5aoHH8P0c{IZ*ZGqDHh*D5$8d8e?EwlZ-iV5#>U@0C&9s4v=@dybclx zHrk#2*wQAs?;>iw)r@H=E-tR8DxlH=XPHyn>u~$sTunknagtrL4}-r*c~y7vIq#+o zpDELFS#R+KNyv)#&+TkyqFYymzE?6ii8>;Y`-|^dFpM3}(ZP(rQ&Lj4)6&v>2d*w3 z@csd9BFJR6@u|EaASDIW^VdKLsb9(eLDQGVL;ZdKzlK5)#=a(FFZ(WAF_x?$+4qd> z`@T&nMz)kN2w4kR60!@S4ARKHM)o!P{=44a&#!;#kuk4(@45HfbDq!VIj;g!#7)oo z2?`#K35tZyO2(Fz`Gfi9Q}U+d)OD9j*>;*kDOpvGeM3?%!!H?VuY$!U09N8TK0W&g zf&Km+4#iyHg8lp?>DH+je#1Fk))3mLYO{yL?avmXJEz@iV(SmUQ3>zl04c$TP8fQ3 zWhe{Ubnre|To(|r5)1mcb#?H3N@y(&0C{=s4#*sOK$ZFe*f@l&B@`-68;jVoZ(4@_Ad>%k&|j_UaeF1XgygYb4qII09(kNtyA zvXzF_XVgk$41mgbeXVVHpr=<)7xm~-QjlEcU8!$!5)!*LkYMVpMEDVfxRm(|N5QD- z73t3wHO@gr1qJhuwO+#!SA(jAd_g+04;f!4{dnBQ@xDeV9`5-B%_gLIY46^dBpI{rV zc1?G^`!j%9!s!7mq{;pvG=1=o#6GD=w_RsZ^Pl1CV~Fz=AB%)=dnFLgqhGvu(GL#g zW#PdB8EAUDuCzHE`1m*x9Rc$@=eU%Lqaj5!9Mf~DE~ap$d_6oIH8cQ1 z27lXE&nWcB{=r5t;u*<3J_(|hZ4%GzKHNUox3dg|zj+J|4Pn}Ng*IGr zz5(;ufhNmH72mSV>ckf!v&=KAdhV>YnFI3yYLF%~7}t8)mriYj!&@GCGmF*0`-y!l zbceAe6|YiK_MM^RAK^R{4NvLWf_aypJEaR|c20#Ky}7SBb7qlw7W1UFe~g}vj;@I; zo8V3g!U#-5y1}9|&xG`)gbOlnJEFPqT5Hk=9GjiBqzCZ$WWFK0Y({aS>}I-J`|2%Y3;1{KBCgLJD?=5ebt9AF?4 zw%;ujf~msKzZ-e{Yq?duhC3;%D=m9546|EY?9c51FjnZGaBMw9CMK}}OI_U!svlFm zPXZ@?{`~p*a2(HQe$&-ygL!GO8X1=O{lz-L;2-|_Mn?Wk z@82)-qy4dCf?4v>QchBKzi@|LT_RSuqukHuo#gh5-E;H5*QKRmg+#VrNH`CYB|nX| zrCh|*T7W-xcke#YL2eMg9IWjjx#Z!qPnRL{iNfwh;r>d*$mHnLwZ8Mq&Sxt&V-n|g z6@d;t8S(J@+;D}RHkev5_AnIrsPu+HeR=ujXHeb2nlkr{k`A~*a!Bz?{|T4H&^cvy z2;%F?E)zAkR)8h`vww>?#}#N&E{r6q`nUDs`w9jTKilC>o3@sBYi@GKG49j2#Kw1) z)dEsd`|?o~Y;F|CcYs5`k)$63JGoM@vNrvzn1XXfx#aP7P-E~_Qc?r!w6{c%fVRT}+(0F$H#CU&xA7`S9sSJ=!UesbNiP7KhQg8+o6J@#dDdnW^a>L3wNjRX+$0j{Bwa-aPnUwEKRK-9#p}W_2R9YKM-DS-#0A^pnl> zz*nO!r=FWEqwk_jBZ+)y6r0V4c2kZrzEEBfECImxeT_E+0kD?EbYJ6@f%U;{yF*@ zp~NlzJzLX0!}YdViIh2F`2%=pRUyUhXFbYKI${!2L?R~?v~lUiu}@7%g`IvqB4ZPSFJVp8?`cMr z9o{r8m7M;ArIz;Jfpk{KCemFmufWFNtsHg3kMBNHzhOfGK`j>nT&Tj-Eb=o#|BCM{ z0!^_}cfJ1fgRQ24hGA~*Pg{Ci2+-n~P#*(4q-Cr!b9K;Bk}$P!<%qeWm(MgwpATyK zj+&*!Iw=u)JI+LR9B652 zbFZ}>+!PBZD6k-C{f3hOp-`}dgak83zm+I}Dy$slqg>Jya#oJczX2$cNH%|<6FTWF zaC}?3Qe%xCKvWo*m|!0Q6!;2c^`z(apm5_|q#_7xj&sKkNZE;)3h{xmAmqgFm{|-7 z)Wa}Mp0(yu%#mAbB)tl5HuL8qWV2IJuA@GZN}*R1e5!+gkA#PG(;MNr3L1~q;Oe{5A3**Buoc3?F1>_x8}DIqiHKb zCnwZM41yjv*v}#D)KJLs(q^GvuIk5E+o>QT_qzSo%w_q#`6XfHq1~5eX3-Mf~k>M_paQ;?f7Za;g*kf$VnO0kaohn3Ew z@&Ec`&>q)7(?W(wyLy9ywZ2tYObV_ik8Z<-+`)g219nR$0uKc%~UVnm#6MSjM`67K7TJ``L$~0o5 zNruv6g(z+CN&3Ms^C6iooGi#aat`u1+q0E5J{ZApBHz+MqF(@_o~#5leE>*Pz1DYP zMcco%!-Yg*xL4{XnF>EYqs)nUY!pB@Ba(WkHh%*?0X9550nEw;m^!5`2uLxS-=v2F zEaokUD(Qv2YqjxxbMsatiRzD_8+YOT)KD)Ln9Yji7u@zI!2PnSt{j8xtuK{_Cg(<-nVn=xx&7!FgZZR3(s4e!z)`>b9)Q2;1yhPI z6-=ZBdpr@J%QVt-nB>#P%340(XLDq-Bd zLwBBXAZlbfaQ47x$xGjrFc@Qk8^2i|%@_W883Tg2OAqz+U%iOmh!b2bodS=8cY$B= zmS(vQodLSyM@d;(79#*@85a5Jy>KDlXSZAu)BvTyPG#igwoU+<6nNa=;!;VDy~0lQ ziVWiX0`dYWNcs`}5uYT-vqeRer4xX`=(=_;%)^Zfd8>U;HNBe-5_1+X5%Q=W7~&NS zVn=B4Nj$@7;5-qdED0e1dgqj@UlGxSIe;~$0mfkA7lX*!US52XU%EWmjXDMV^BJWd z|BxNdMaml>uT)RL(}}z3ufS>BIpFL>U-McigG1g(XhOG`VsA)Q@ z21q0dI(Rq$u89S+gJt#k)1oWAW3hS{ZVy0%bl`9>c%;^*VsI5W>HLe``~(n|vet@( zE&BCf&rv<+hpCertv!v4iyNuXL1Lc7Vpt?+Zi(n3!DeYCy+lymE#mF%?OHbWKigCN z^lijquX#5#Hd-u0BybbIh4$|MRs-3psi|36PwxC?1Pk^%HzD->iH#U38oPb5lL%kw zJCQ)5o#0$qTQccx=@O3pAUU%a85y}P zqO1D9!Qfq6KY7Q3BMEHuPx=u#W#R;V0wsbhtzg8m>-XSbb*CI7Skfy(?Y^mhgS5f? zqQSaQ)C1Qp5vBr8*T?&2_h<;$NZ z5T>_7Ax7yg_G!+4U;SlbT^Z6zpT(`d002K3?}T|*?SB*urd<{JS8@ip;jEZIaBBLA z!CppU-gj{OY07!QZP6cuiBk{J=3T&-l(*q(MxX5(FMtV#auGuJ+J(uw@#?6i(7!+4 z)B*wx1Dgw-M^B)^;UTHGAhIBEC{hEJ|2ydli7+%L;9p3gI+uH@NbJXtAN@P|xY%E1 z>N^pk_0tP}SM;iYW%=Xm?tTtIQG6j1^74(huOEW{vjWDU8u{#KAdgPLd7_#W$(Kk( zZU}FOO3p~ZN)o^_fQ7cgY{V5_Tnl!Yv6MvYN-V&G#P7y7cSkVvV^idOgwwD~^5iP(eo zf0wjERNFfMFPs@?;IGq|3#Por`g&(zu6xu^By_NI0q<=F{7~MV8`%&}lFaTlJWj1s zMm`S11QREeZpB3b0LHIPxOy4fiai+@9)~X!reoRwC3bv2Bf9h$ogKW;5T*~hnd~aHDw<5AC z#xT5LlP?#L=E6oxGdz7BG48T_uc|qdi{DDhQGer4ujnWyoORXHa+J@7;F|qWt zv?hXS=f(55=C|}97&ix?sRhlE4v9|Tb^`LzhU^X>tbx5u%|wy zfQT9ecx&39SE-Z;c7Cw&9)tDUk6x_A;NqG`_#leqx-%cn+cBz75kxu}(w*)6)r zZf)&0pxoZ9tE;1*ePtY5YhHQ`Yap?6*T@#&(!&LAu&Z|q(QD9Wex&j z6XNG@SO&r?wH0Y4&NRRX79_AKp1~n7w-a90NXVRxD9XKqo41gOi4sAAS9du@=;03egTZ-lT&kOL*VAkG&$GN&&#=L3Dx_BVp!PA z_NUH7&TG}V>^T%5^K9V;jghDmyr&ylH;vi$c&E zjIFfVvg2F9YiaL8;brEPpZG?3v1WB708PRq5Q^$-(rIJiq-q8i8weQMxLY zy~sIKtdi3YN=lNjTvlxXdXEo>WWDCMpAGJ1hF?Mi@e2uUY=1XrsHmJCI5)B`y7m?4 zij#An?T91(lVqy&Wvhc(>>^Tq5f3YPi6PYjNp$aE~de^@^uMw`d;-ya3>D)ZI7RGbTCe~qt2IIq59r}ZD7+iaXUYA$J# zD|MDcCZgQ!*|a4*yCgvYyeSfoq`BiWq8l>qb@ebjkNwO7-D%m(9r3@MlHNDH`jhEbed|?lR)OQjVxN`;2nD?Wu4^A@vnYIOChE-rf3sMpFs*uORBDc${PpV}XI<^R z?^@m8#-IL86$!f%>0sg)SowRkK9;ahYPPD*_wkp(wb<`(AvfT&m9nTdi1P>hVIe#Z z>I?F%e=I&vmH8U|RL?tGZ~t|0>a~Uu4>DpM_trB5=KlE&Ka>7SXp(L1MV?4DUw)B| zSpHi~*qmQI@h)D{4Z-*SwE*Gy1Hw=-#D8TsErXrIGj!&vxl|b6r>LD$a_hFLZE24n z4$ES*1k}_UfUj`#@GPu<{TyL`{g727k#{OaccJp4Z z6booT$y~9KH;J!383MWVb+5KjXTcQbL>mU?Xx@+B8Q8I>L&l%BUr|OR=`RyQO^ikI z-x4FS)ETJ~U!z6Z3U-S6Km(fijEQ)omBE>wNbrt>EFur*FAFyqAqGf^_R^)zNPtI) zzN`)^23vYyXh`w$v|#<-KoYyL+^!(*R>NgJ)3gcAphq4Mv5sr&yt3|eS{eI5w;us- z!Z$4_e?SU$(o!0S5%vyYq|>cuu4T+^U7i}6j2)d7%Gn#fc|gcc8mAx&Ac^|df!yfs zzd=b@MR)vXA+QLMUqfaYL;hLCQA(XNI?8+rJUSM3eoCE2H~E9Qxj%lW;rFoL+|s(6 z{zJlmn$nfCa&iRnscD_hlCd^kl3K6A;`k@7-=nvI;@ctDjjJ2KaNx15YFXdbXPvDX z_5c-e^n7fsy55qK`ztK{Bcz%RIKvoF2}19wu1S&z)ip~Fd4zuT;!pG95yeMEhQ`*B zmffA&?_5XeX9BFmgsNAvT%XVy>gw+HF3it2r0ho;;7L#q0qUq5&^U>4XNObds>U60 z!9&|iU0X3L9{V}&?|8#46LeeZmGjucAG6PE@vF5}1(`Pl{aCl3#RghhSWLXe{K3=Y z4@ALEev}GGaZqgBJe9%;nMUteD)4=J{YSm6J+V^QZoEf&$b-hn%(d`!?-#$dQT4K+ z^rR{-fVvG~56?%$C1hFRX@weheqmF2-bg?GH}_qaXsFagi_x2K-E`+#ESm@+az@A% z-}R)Ttc7eVQ}o5xkW0 zGDS>eH9b6g*4*-?6up6J%AcKl!pDxzpMyu@z}$`U)>ZeD1ubshG=BfNr68~h$VzX_ z-vc?`Q~hOSh<{9-u%*02B%rUg%%(TbD~~8eHPYF5{xRp(JR-+ITpY;+~VgC z8DH7mb4)HJgUBGDyOaRbgA)`&awqabAPf4r_dM?Kc&07tbx`O>{be`AQLV+;NvoOX z*h%=>P+Ymm3yy2}4nfr*4Wea1W9J!o{{WCGN`hBk@Yxctr&l$!vS#=s{wr$f zSAH%Fd8a8N&DG6ro?1DQtacXw`tY%MuuJ8D!TC{P$r%2j`OCTUf7Vr|+JEA@e*1C# z6848`P@*!{4`!b zo&VrnfujVp>UPGAJsbqc@2sb~HVNF}LVoG&iZZwRx2Qj^cl5U=rb=}bY|PXoDdwm? zH|3VwB*K;D{A?)=jdoDcTZUNT87pUL%ZRE?3=z4)x#9p6mc{ ze96KW)kTJefp$JkmgeRmuohXDv~AcpsFFRX17!+n6ELs`cgq7`$fAZHHB`=h4g09M z9s|6}jMuh_xyx-wx3v2p&MGS{bqnW=uYC0V)eOiRl#v$m77qCoZ5A=jBP&0Co*KTC zBIXfXuPVMGT%4VqT~}J#TP>~BAOM2PML?NdC*H(T3XS~PK3pFLRV(QCF>pZ;gA=36 zu(K+M+j_S``^wVTr-%A z3w&P9C54DrSN1#$VNp014O#0ckTezd{L?;_CGClr9~0`Ip#WU* z?~9$22t5RW#mfHiISjAA68qVFoVCq)`yKYs`@p8Me3&Yr~g{rkAB;9%x}#tIP9C~*?z4^SdLt=<+D4JL5Q za0(83k+w6y&!4_6ViLNHs*?g7tE6X1p9j~U90wsw3>6+C=a+u6B1S~5Y z=;Ih(zflPmm{*`_qI3zQ;~SC^aZfH{dhqfhcgDyH+IRcImYPUfpaE;7=^m#_#4nkv zBc-sjF@=Vs9rC^OQo^KAc&Se!{N(ggQS!iiNZ^4fQ{ZC+So-^2J4C zseimo7x_WuFn^yO9_o3^Wsk*D{F^8dl^EBzjq)*zCzMAyd<9ARMfv&m6!1_sv}de`|0KZmON^$g|6v&+R)9p-_+Jqf!124qPNs$NgcJmY+Kxc8Zq&(m9Wd;v!fs%kZ z0_clp5SI&g(KQ}KJy!%_vc6NKzSA|hJVlFJx*{Y^dn_;mz*G?5&`! zm&hwDIL#r)Pg6w1z*gptO|=;k=T)%N_%H(UL*2yy@WKo!q2EBpM_@7JT5|?hA`|qh zGFSaeXMKXXpORT2Qr1$Pj5`5Z=A6vQTZB*qx^#sY|Ld3ArOoh(2L5!+S=GmnYZ(1y z#W(sDS?+G1Kr?`M$VZHmb-iaO@d8w=nXS|=>Jh+G^KU<9tHnLRQXe&b%AP{STG`TP zGLC18pe`^WP*=OA!N=Od4NVySx_!ANA7B7BRup#k^)-5KAiD2N`R<)}h)A@}S85}@ zLEz-{){6uARFq6P@xyIbQw`OD>XQ}H`VC!_AG?x-3ss!H6ByF<{DBzAl2j+*nxPyo zG#v~AP19;nP*z#dtxuBJ^>m?2P|2LWb0Du_q>IV+qauKaRdhGvPL;&r^gsmB11aPr znUYS!hfB0Cp{?N=eL1-=+hM4%46M~Kh*2!t54e8_$EpaQ#uwcFB{F$-MOEwXqu6## z$<~*EDrfc(ZM+*1<)GxnyAMlnWwmAomP6s(@!Jbp69B3Lpiu<= zhVVZ0@AkjNE%x7V#8Cq{QOrvXM3mhdj}c@=FxF-VyGZTlHD_-Gx29|F5J5=&^5bw!{P4l4AJAUdXHT`Y*0~TcE5ImtpCT|7f|=_O#3hP;N+&{2svm_Zk#zA&_N$S zLBo^qv`U=V>DqivegQ5*j!FS8*dN;yP{Tk7$y*L^;h+{!UwQR3?3@sabV}Qc0ww&~ zGdq=bo$(@oQN4@0u8q_M=6L4Iu{r8ocFo<{Irt?u29m4ym}xD~^lCa zfmSp$HQ9J_zUZh~Hiv~0qKT9A!=cSkXah-sKYyxgu>tb{5hn!YKcGhA7kNbk7(pGt zi{abOs7&|xZ@K@%c{x70c5{uddDRAmD6zIa+P=sAE^M;IQ##Vr`(Re%LtyAq6qd&+ zO)>(@cQ7@+B$hZiI4Ft4A64Iu#*C&OlAwXYR!D_Lr8L;agwYhwe#e_J@zJ{KeoudfNszlkL_kY z|8r96D`K+Iyo9Xhs(g_C=-AlQbq2|EcM1Y1p1PkM(uDZ+G*sA~*^#1lsBYXic$96O zg_pllNR8IoQYesfmV>HsIB15!43C3BsHX7Fcvf1eagT_?gi}!JP>sd%H2>BGx@HSN zoS!dhDuy4AO|iJTx-vsJiZj9~Gc&W}XkeA|B~8%VWpHqh1#alG>>!6`+pWra46s{T z$e%YKMcr?ddPuk!riwNp*UHtJXC7AXW_%@`PCG}&=tzIP2lqx#LDhWj(Xg960;=mum=HlBU@JwN$lwsZNq2v0KcR-y5SRIeldOL`~v7{n9wONnxzM*AfYfWr`S3ZKFXSaI! zk{+o2aWTqBPJ{2-`SLQ%`$3Nvkfg3oqhY15}u;I(D!wZF=h`4kJ_hSsSjx_ z?JanB0<}b)d_PY+8H8{W&C15s_8t2=rw#55?(CfH*=;}XUkD?3a>rXk2{V$7+Q7gYYtArQ9`1z%zbw{(CmUNg_T z)`qOK&p1cxH?0yGS+tKl%W?>iZ`OTud_^e+d3k#qiEUTwybIFChm8z{&G%(BV=;q1 zS0xAzJgtsFD+-wwT=+VTlg&KaPE{5tm~Or@t$6$SC@iF~?6e&gESISbz#N^S-4zfA zb`pL`z@DdLr9`(61Gon19!C$o69`!w$_a~=3@12kaP5(AR_DxbKQiai5mZ8IAl`P5 zo%EltgR-h8Hxqjo6waCjAt}rXFa;VrMkt&dAoINWk?-u*#)YA_r_Ylgpl|zc-z@Td9G_&F`^9H{N1IueEh?gHpWU z(}73~V1pG?YZX=MiLRlV=nJ@XqIZH(1{q{fHD$jT;HWqiP?_pTf6?tcg{I#W(*)=s zM3GwOo$y=mLqK(yud3--MLHHwL^>~spp0$#rD~2Fh!w5OY_qgCF2{%#=FCnNY2(G& z&~(s*<>FO#q$194{`fYrl6hhKa|^+*8=u{{+9Bwt2!Kh;q^N+1tUH0kg`T!V2$~O6LNVvjZvpRg0lAXqdLgjyGSBU{MtEsQCVA&2oI$1x5lD`&w*lmrw(ZlfV;4 zX!_T0ytU-dZq~S*Wn26`%~(tkkZ^I>`0V@EXY1co!GEp=;tc4^&#jFtyoDOh9EYQT z4t!NrJ8FhXOz;e<3K5a`B^C{&1rbEGT zmx83yD_||EkOXqBx4V1nqEg^Z?WV?ubO^kAgYM5UVm_5IrPMSlMtIu}NCy*?H2@P^ z4m9xd-~ib}(?5P!3QNDF9D6lpkxnQ*jz6YCfu=oTs7Z>L9V2QIg@BN;D5{`+7EW zb>IfAg#eqpv40p6t79fweQ4&<)F0EY9MiTYi6Dq%b0}k}a2}=qKD$`APFyZ~u&@A1 z6t&zP9cx)zP9ZmyPCT0|A9Ve(qhnxM_ur#0V{7qU(-U_#4_W zwYLU8b{%imQV}MAk}y1=Q0UQm@;I7*BHK>d__L&xl$ruS3<=O3UT&_hBZG!Rt=iiF z>JIR#Pwj+H?jZaADv^j#k}-t2hJIb009P;*Tl(TR>%@;A$~40$Mu*4I_|H_e(jqt%=&| zm*vb?vZ+mjP*Im!CxDF={;@ zC+vC+|7-A3@GPq{&$5+T%c5ybBz6qQU8= zR3QiDO8ZxT9qW!{YFZBZ%A@K{T|koxG@Sq)~jN{N&3{bZ>&Cn zyNU(se!10TkQ|(xIu<^fMhJH$4THtp1@D6`c@NSYQnB!+^pv-Ro6mu0U8!d5bQ3IR zJu`o7+Th^e$3^4xGs(8i*=g*7U*-kKYpB=G9(qed zcQ%$9MWGv169)5lCY~F4ehur<)cSl__MGWiz^CR=>csp6;0CrjYTur&-FA5w@1Ek! z55oI=AW}J@X?cJOqhaNdB}LR-X^980W37m`@D<_8zuvyn1FD}te=7@7y1?y&daFy+ z&BOYTf9`AafC@uuG*!JHN;37Z*+5?(1?o(S4T~RstHPav8V^%zgP6yCTlBL*Ul%`y zfAc$4oD<`IM#&BoM)SkJ2Q{U=_WlOsZC8)Z7O%Aey*I@!de!y68Q`HhIeQeUA!Kez zI-hV=?no|VEt&XLtWG`Wv*LmRRMbS!f@@Tyg7)IT$LW(IQn2ix)nT_mYI206EMd? z6``+jIfW^XV(S&C4%iBi%Q6QMoie}o$`gC3RIqXtiaKJhsn=ZOT~2vLwnaeQfXs!e z38@eG=;PE8(S`7_nZjAg_rY_kei`o4Zqd~JvyFkxWDw>V;6M#gXYSr7T_bCogUwXi zAprJ_uuwA^k@yJCg*9h9xPSV3Dq@UuZEF0rIqNoFzoy@xnPwbwZc}s_E@xkL{e%E^ zTHQGeiCGQP1$DBjpx22npqm3ryfq)xl?DT5v|i@y$5zVyDP#KW5P%4|9|r@sh#3-h zgNJK;@Fxg}#(24=VX5l)y6rsc&L##uWFAxb@NxIBP-!hl>uQyq6w9!Q7&3TybXpmz|L`dc%itWjoe zgHi096BUhl2Dr5pf&*r>4^Y*En85-h)K84*_E$Ji^Pn~YJncdAQR?4)`82TTaoWT# zJn!Fvo-*gml|aOTxCh`7`O7VzSO=@y45j`5KGyOcDc=w8AFvH20xJ3hP^UK*e1URc zo`K80P7JxTF(2T)Vqa1OD305q?IL1>jeBMTQbB8J=gWTeHEPSoUnw>s<^x(b?F`rY zTorc`=cPs~iuM+&Wm%yEZ0XA6dmcCuXy(UFA`8XNCC%zvO!4#c^9nRo5o(|-(ugA{+Q#Y*i!Z>8 zD}9!Jtyfbrivz@MDD|*GCTwopuOxV_MvwIp2}4-P7i=MKNn?nO}Qz`_y))1zfccI^)@9G%9_g z6j7xh(G}9uzC*%JL|HS|n1;;~>69()VS90Oqh*Ch(i(qv4DScEIQUp>_*Mw3wSS^Q zylAj2Yc4PM5JOWCE`Dl0e&f90vHT|tp!#a2KVD+4*Gx>kkSIHIc;BiG6r{&MO%bFtuX0)C`lGsb$1 zpgr|CIoOf{*l;Pqm*3-le_B^I57R~X+{rPBW4U<%TAzd-Qz);c8h~0YH~FxWfRpM| zAa>*wxBroJ&2MNPEN#H-xStk_3@O0g5J6ffLLs(5Cg!yiFIWqOk3|jL<;6&FMDk{K zgZgM`XfoJyWFeng6Gx{;Ohlm+AeyAf`k0s#XW!QUBg&jO+<2GyECL&zw{m0$mr0GA z?M$elp`~@bHksjDOX$2^V|~s?!D4!~gFKxoF{ZQ4p=PGmLh+k@dZEhVd+*JA;Fh#) zrl8>f>!so8xn+t9V}oN0sdNSS`CWE{8Y7yH0F@*U#1&o0Inf#mH-#9Eg=)K zr;sq!)lJs_@3o9C<@D65xR#gV+s5`&B4T@4d)9_)9FO-bs-4~O;%ho!Xim)(;FgQf z@*kR4IG0#e{Cn5biMxYLAabcz! z=ry}xkb_qQEL)^X`gs}?!U~00+b`T%pr@6;&~b9<$~_q_Grp=zEkVXH(T5V+-h)6O z8>eh~rSq@XX(U$Bbx0NY#qC7@g7}TJkGoQQP8!EkzSe~YR*zq|+*7s~YNj28A@vP3 z1i)#0Bp)q7c)zZ6~-Jq?jMR??abqGF{#@9j12EmPZdt8Lp4*`8wdn+N1El#`p@L} z%IDDbY|m7QE!$YuFhA3+G1+vz^HIC^6DM6WbSO(RK<3l}-<9;OEa4d|c#Ddt_BJyV z7Ae?k1OIxZGb0|alN$3{RZHg2HAA(}m&ceg+ce!jwkEUgss)uUoL^&8IN1hob?6;! zQ&fKUGX!ozW_`z~jE|4y1^C4XPYw+cugw64KXNT~N-;PMR&4P0|zd{8Nsfk+@3ETv_2tZy8!hB;;xp znD^$rEX>^1>QV29nxfh}YisJa(Fjo6vTDXERm3&L2QWxEWy%z7t^M5_B;Fbcy|HK_B~OJ72rPNRY^*{jihkxw1&e zGy~anVXDiMhyeM^k2hRYHIFS<-NHz(9asDL>un!HA)}v4s(rRm#1QN6`QEp|1vU)l zmxMp0_*YpNmc1`3da?5_jJz1U_v$e}T|HroT5zDzitAWY38-=TW;v-lh)b7pzc0=d zO4Kr(r`=QSn)T4aV(p9RLj?`Fh||e+AIaUH-PNvE`W6$MBwe>LjB4?`Us7M_Dd*(cK&t5Vju zX9_yxnr*O&p|lz?T9;q;cS4-koddQpa1MofK(xA@e)jo1{K;PPI90^18q>vfFa0W6 z8$&e!Bo)rhw_HZRV^*knbQ(CA;(}mFF+L($oZ3J{iBJUHPmTDknofGOItp^T!Edjl z+x`SZ+L)iTQ|mUoib(oo3&z-&z$&{}2B{KAQ)AEX7TiUa*6^=H0wQv)$HN7I&;U%W z_x8mISwW+b?xNfHnI!}4X141XDL8l~T9;-?pk=9f99C>xIF=bX7Xt+yk;O|w;gzn( z7bHcf*I!4xcwZCq5>2kP!YV}%tEJZ*G6i1+bH1N~qe5&s(6TEq8El6?bp zUj29iK#oe=ieV|3Ifg^4aVfIKaJL5Gm2PjM&8VJ$!PN0p6!a8Q@6X!>QE3{w!UK_p z;ih13zJ5moD}b@4-L=9k_n3Tu1wqf=gr&lw}vF-SyK&pmy$CnlAsH=wjRfn!n~ zOPXCTKfVDgf8gt>rq&rg2#}xMBFQA9I^&ewD=nA0KTUpD(W!l4ViM@WPj`Ijk3mBL zhp#EpX!7B2o@;GitPE33lWEr)`GHqrjs@@MsdhcqhszMRL{EfR@t}PX|H_Nb+CG(7 zGID(D!E%U!<#2=)A_52xt@}PK3qY1mPEJ7uiX}uq*Ld;-u)x$Rk8&`v(0#HN z=P^GHv_B}UrixRwx+v^bjyLFG#EZKVZXQN-Kd4ehcI*$PxQ5%N6&NA8ip^kQq~UKP;}jQja6G@#r2!5LfH*AY4bHO&a&z7GJ6xFIRx8}x1djM-1_S3I42^igz;U` z6c7|2d6xb|{GcyzJY#9;x%bUgAW9+EF~QNm(dR-g9`OS?^aJr z9PTE8;=*2E-gC1-F?KK--JdK8%H?a#kvF|wlIt})jQRa=&@{A~nU#vhU_M2#Ldc{I#P`cZOuZ@yWMGSy zAZ21w-wiOvUGj_PTY5W%fS2gjc0GUJOr0pk`eFoj2Z>DL`tR`tMSQmsV!AGE9w+kV zf~L~OkhQs2t?QWiDOd?!&436^WZ=ij%58fn?Ewqut0pZ%e@MA&1bPf9FruYCNK0C` zpap8XqQHmQ{#&+D16}#w?~B}l>Lhjd5$34EnBVV(@|Lq^GQVSAkSG#~4194h;(a9N zU-~ZaLl;3W_T^4}1%sj=R_7NJ!hjzmTP&kK{Q+w>(ENrIh}>s(MnI#{9LrX62!Y5J zscjLCP3pJ`-&NNrOmiLM%aRA?U6%iEMmz|hRmk#U(f*|b`=@%kMuOmOC0Q?P%<@ew8Gcl{0g0oBz68rm`!_EBd zI1C08L;PB ziie+tG-G1#3DSctb^XGWR2Qb4yinO)Msw31`>zg*+`ce{6kwis2-Vl`$E=UlxN8dj zyVzPr|E)v~2>VcwJ9i3TDDd7FTPL@uP$tRsLvis6PDKH%%i)D7kkEr&m8efL|7{S@ zBmQXh9}e*s!=}F&_MeAb^zWUd>7&7;5c)Is8$?&4FIGVYj0X-ACbLuV!eTCI<5@Cd zLNG@$-hJ}^m(cgV|E+c{44DVt@_(m^q@^kJc#wS;J9pjiZPtIE?CtF(g}T1l7uD6* z)deZX>tCEuaMZ!t-ZwHD`cU-yBD%2$6IvE}u={UL6dnzggIR#3wzLE1q91nd#VZTi z%FE<_%DkedDC|Dj&|k#lH9WUt^50(R04tkwsd?(0lBEC^Aa^~U;D&}WxR7~NI1erD zD9~!&i4@?=nl8Rfuk}~HW;#r6v3%*)h39%C%a)Tn^7Jz43&=d(To4f>(CyTHSL0&$ z_NA{}0#1agO?ds2>+$P*-#U*@&x&Cu?t)&+-CB4S(65-d!?uDQkrQdN8m*dJFVH>u zdSyIF8N?g+;fy|;Th&!njWtfci}t2eF2e9DvOxwi`g#hLuWRn94nV}6&>BSk@J z7#94uZf2nPw$3jqWe~AN?*91}V>i-Wz3}vFb{PnCI1#i(x0zd(ph&;1fBvwB_eHm^ zwHd!FGEBBtsZ4R5vQ0y=qm5uJ;U2X`4~+~BWj?oD5^ma>(Q5e*AOx&3pG$HDMaHvs6~ldy`C|D?v%1?VbC&SK zb#QN2JMf}Z0h<|H;d)#MDZpA@zbn^+9~J`esrQq8w`JdboPt3AlYesP)`)JyswNQb zklm>XdI0t>@Aemx8}P)+4e6xIVCtcRV&6qUkTc_P5M1r5D1@N5FW;ho6bu`GRIky4 z49Vyxaj7z}$xdp)VVp7l9s#=hWo?Hd6kxdf;<5OV5--is5#Z#+80&Rke6wv<)1PI7 zK$SrGmM-wg1~ScCz>&r9cL$jpvUv^r+yx(38l?X5s@;36_`zRJX$fyZlX^bVZ}#0X3mM+#V8Okha}I>cL|_7oPs8Hb7O1VW0xtycUdoX1 zR?UCw!A4IW)y#`vQx`P)t&(gn(5<2a?CS#!fy1}_v}p1MVu65H;fpyk%uG+O!VkzH zc4ag^8i55&QqAq}M9vKhT5%He4bN<5`Vq*@COmhTvsT}FcY3L=jJBd?O4((b9BCCm zSRv)d>Q=Mh>*G^$Evjt+q!uY+-kcM5Y4eP%tgO12F3+goiL=?=ctyI7iU@W>icmz} zy7Ti!JBpli{~JfG=@?h%MbTu;1Mo&p-{{$iR4m65^M9#9(b>&fbHo4F-go~~{l~+7+=X>A(!2P&? zyn8$<503MGpX<8b*Y$cmpU>CxWdcySc4aH93k=OSDMY}6 zX(5QCjeHVwS|mHfk()$RZh?=wNXAM}t|rG=Qu!3TE*MnU&a^&W^OjfHQAPW(hPgTO z(&u_4`lm|CA?jQwS3m@`!(hIMWV@{Jzaf47 z4kbg_P$oW3blTd+C4Zp=%{OK@8onynG%*uE6^0WFx@D#ITg9mBjZF&ZYskm z-jH6bjnY}$4J@|_%rQDmMIJ;=O&vnA>K5K{o30X)dY|G{COCM#UJ{PTkx%!&J%7*? z(xK%SnTRp%{rPis2R`kH_SvqYTLPXZLuvWr+e5E1ngu8;ws7`1?r%kLMr4YlQC$2d z=Tz4$+2NNsn&XhfNwgX<<$jXUvf=YgqlEw6Y@c1_u7nBZ)%P3K@}qEi)Vv+=9;GDs z6d_XA7ZvUXV-L-R$sv#N!s6Khgt%RN$Gv>eu0!$g_i5D-@j)}q!TO)71HJ_Q*L`F1wJTB zyf;PZu*kYvHlLmSw|_W*X`PK--{5cbSZ~!@n#L&RL_R*QY12t=j%e*Oy2-0O;aWl8 zAc182ti4t-j@#_HoDFLhB@9mzru9AS(1qtWBk1?38L-E8Nz+|WikU7yS52Fs5>jV@ za@G=-(P4i6c=g@K8GYX)#ug1q+`P=IM+>nIN#^K7fA>l;chOVRseL`G1-o6Ti?Uy@ zqFv_mC^gBST(N*XHv^|kG=4V+?olYt#w`7BFMR4&GQ!wb=D-ug)bJTMU;4EYsi^$? z*~{93jru28k|3(zyGEaJrvC0*{Wtn|qaozVPlxuWw*(mH9enBJy5QiDpl1$-^6A?o{mRt>_Z0u|8hEMpG$TBJ?`mEn&<{_7Js;bE;VvpW_ znz5P|SN-=~V2sYiC$E7@hF!e0vx{Z6K%ihJx>oY<3PMpequb4{9 zEzTL$PU_tS=bIAJc~e*8@m%m3I)wFg1#rU#U^c%zN%X zw6NI;N%V2+J%5He;ZOD=uCy^|Xewu7t56gJGF$b?ez=s<`lbhO4NkT*zJ& z@RbCNuvM}H9=5=mk`j*?OyJpuiGH36Ce@+!zwNXGYwB0KXN(O<#0*p>MQPAFY3l3!b;7`Q3j;7u23x zQE{HAwi-MCx7%Lw#P~DG#%#7iw_&kYe}+}ubjisrBiX3-m$qpxMt{3^1=*~vJtkzd zQP;B0z_PHmo+L&tAY9>`EJbKk9a{v7V|nQSR-wQK;n|D50%r>}CGDceCe!B)QqXLy zhvI}8vCojxr=FQJVb9U|siQ5Rt~dr$NeU& z&NNwFWJkDPZ|$Are(hhwPo*tm+U$3Y_U0Ti+qTB>5Ri)tkVGS)_mD;;lEM{-OXKE# z-);YIs_7_-`6gNcH5CUiVYsVD!G;9F6r4QYG&c8HOrXCMRo})KKY;0`CPSfSQ zX|FB?^G!PNjP)V;K)RY~#hec2zxiIt&`YP|4fnb}^2A`XbHF@c$&ty-^Y#zzNMN>T zfPL}Fo%ch`>9AeoPrl?t|5NUftbh4Vqg*>pv2yphU;z?ZPgIcs;)fn>;e~-h&WHG| z|TC&9X;d!;Jch0K*LbYDSUzD^cur0&RwNk1GE3$e+hK3(eCCi$qojC%?0jt7PdMb zuAEaU%IstwJsdXax0j;xHTD_0{>>|&Vv5bM98IjPie7#YvTjtWVjMpQu(yO-(Ql$* z>T#wx8>fz2KN@lQ`Ru8KGnWxos>TXjX=(4~iB}-^eug+-so9uJQ{`Iydn1O=t0JPv zp@;7nT!@d6adAXJeJbyZJIPSTQ%7;r!!p1rYD1Cn*Q*B|A^{0$O+jT~FCS zJ01z; z`VDZ9oWvDpRVhN#34K7mI0<(tZv0pSM7IzpuYpcyz~;4o|DNW-%I_w0bmmWVmDYl> zsfoz~$*KZkNbGBfa;71FvYmr%cfk3}gq9F}Bwx)1Y5BlNGgMLUreF?`HOHS$aC0Wk zU5PpA!TxW%X7<~U_9^Lh^+cIXC#2&6mhFIrD}srBW%8@L+pPnTQJ?CU`XB%`Lu3$4 z?))+6nU1AjGH0DWP7@<~Sn9N&l&hapRks7<5_laIOfoKuJ~V_10j9lwVR{k zLOM|(Tf+iUASftkGA|tuv*ds{Q;6P1$MY%USE^p>8yap4p=;^Za{3R%nW;wqNMFnT zeKqJx`dt!0Q}$8!g^_y9k5ymA7sJgTpf28^Z;Y6kW}F#O`IwtqDf-g%=+s-{wHFHp zjiMX|XhZf3fMqA29FNAR*gAf~1m=~M^>2RfEOQzo8XrapU%{6h*V=b;=V*v}|2~9Q##FR=nZTpT!tKr!%7DaTTB-R{J%2Hc zOoOf>xDdywIpv;>JCdP~cqf%Mu&_y^8mRn_NqvgEe5KrJe*m|)h7!)nn-7t89=^NC z*9^j^7TmlFQ6$+d(s7A^?d94wCt>K<5@N8)5MiqG*=aumtZQ!NETIy{HNKKczmI|_6QMDu3-gmeHWvCrsBRG-5o)iDjERc_vB%HDsX_CR5RBR`|&Pls0ypJdDqMiSeojS z37=>Ho{@f7{GbRtj~3E6P|o?ausQnozu6v&m{V`zt}FQFGrzDP;>F4j&l2(YRo()V zGN)s^J{2|e)f`Yz(EgNxF_(R#WM-+Q5Y+{e2x~}d>madXBB7l#s z^1*n+qcOMS>p-J>Aq8qRUxG4mNBK~R=2cjOxlE5`!GzFELwRh#Nb`sMe4#T~Jl0+Y z*)fJG%^7J1d+J@WKkPZrFk>xBP?KwH*th~#lT#F{oQU1|U@c06&1h#2!e;Oacf{Cm z3u!dijEBn5nd+A@5oNb5up2a?>Mzdvq3-+Zt=%twDS$7tlV|MGd3GfN9+;V>=CQV*tQ9KVCDzRDyxQqp zE`yRxy6a{Pys0b2u>mirf>dZ>_6p9ic64;)oFBDUF&*}FpPLWvbV^x+Gty_%wCC>S zIILq)<5*J;PJg87F2q!)n-mObqQFhSe=UWD`NcOjPU_!ppa@j|^f9(Tqf&N67hjed zVSO%Oy^TM8oKN+Sd1lMD7EQ|(96LF9`cz-L3xCB&oHXucl4P_jk%wDf+VZcB8ELYA z_>e8ycc$;luFX_R8`|VUVXGg7>b;%qjT_&R`L&aJ`#e6g%PBo>7mf71!>_|}?|3?| zux{SlSMPgp^eRSF1DBN2PSYql>c#*Kmt>b@7$PuyhyOm$6|PA ziVHt{u+PKG(um&{5rX*SybPTmN5grwykBw_uaZKl2Nr^cv=PwS(XCx;*#EW5DmOL8 z^5UDhC(E=t(xxvCza|D0hweh#(+fA_N&#w9qK`lc(_<&mo0>=n8a>b`FL5KQz;@za zL6m*r%2kE^&b0?;qqaZo8wOD}0MjNF%5b?o2{w|T%9NRvRj7FoW%l%9ztcAZR!JV6 zO+JaKgel86DVIik1uqsX%mtbSGTHq(_xtzI(A<0#8mdAsg{pO8wb=EO2I{AR>T7*g zMpK2Y-WE_^hlPexEHMe_r+}{5II-X0~zW|t-4-YyWm0I zxoh_)WoT}Df6SYK%S{9uc%`jrJ95Fp7D7mEq>T5B%8K`_vbaIL&h{hR{JiqdvZZ`v zi|@?#dyi(X|CuKQ87vFB5PHazo{yXNzitAy?jb}6=hn#8s2bS8R6PXF!aSnIV?E#% zFF4P9h=21&2;G-{UOByke096Ixfwq)O*~$LY{?jm61zs=<>n*&g2prgIdZ7S(;ule zY_77P4~c0~b`%iPKi*OL@c#1181gf0hb@I-dkMdt4DlFE!_kcrYHs&LJWbYSG0mJ0C$?)PNhPf|f^=lh#mBQxIMSJZ3WTQS-nj8W;Qk zD)M~i&MRq3MxYR_kP z6l7OyU2^KUDm*l(;q&?ag?MR%8tEt5VG8BsD=5hU{*xV6${G0I$0Y7A?nz~N)4ifR z*KzrSFr4|HEk5ZY?=EU&tB_^g9x9m=@uu`+-=Z%Z;d!|SV5n=S_?p$3%oru8Ch3uV z4gDH>=CFla-v}>*;3$&qvms8<#BIGh%Zv_NNIY2Y-06(2{ko*MSkfuKphQZNJ(Jsk@mtaQ;ZaVfQeXiAl8X1!a=Bee8pVtEtDW%iPaCZaYaJny72#2)sy>OJ z?%$B_>7;M`%X#CVoUFlcxIlx88;J0DWN@s)R~@Ya$6u4Ys>+o=9Khg_t=g5l$tx(- z(~RfB5Pz7@ff^^);id$LI{~DnYN$ru;y#x-g$y|f6PA==AD*s$V1;xw*-~`JqTCHw z)EU=tiq>-VDf)89r*L~NbXXBCw=UB0wkD-;K>#kxksX;o)W>%a8L}r=3|}u#t3sQb zuUP{;_fjL+E57nN{P-tSUDwQ%t%ZDosE-?|gG)?}=6#$ghC^1B>-1{1Qwm=mMDqQE zgB2}H&o(U~Z*W~5|MD!cm8@ZO+55Sy5Ha|kskqc`_KT!?^&72tt(7?&K?-*ZHm@!x zijFUYgVDvwN$5LD_}jN{FP%m1w^d3?N>W|7b4z=YZ(h5XaODZP)FyBIOIDe)izC(L zvJ90}oXRyrOOJJeo{9l5dIf?a66(g;Zr?))JS5PyJLAQ~9`vVF<{vzLQ0P#bb1L=o z#Y2e@umPyo;x`9gJow(bZ~Lcgbx;%E<|4&Ap;L|Wv(6m-{+h$Qfd#LT)xj=NDkn$B zCr32g+gD*)X8YoswR?*tT{K|-D5SJ|bhp$VJM%6^=Nvs1xlg6p({k0l&;rQ)Mj}=kp#;W`kxIiFDTlSh5fkXXHLuZt3dKTZ0{CniL zwpDj_P(eTBEo5jH<$U6;m~&h9n}zKimj^6+r@aU9`XREPofMXULInttjR1rFr9%gu zT9ccgKsk9Tmv`(^8!-mi3ckIVW$J@AG|VZPr9~Rbmb_#|RU~xIq_Vn))TFYOWee+5 zNJ>jdvA^j$!v{!HT{Hn(^=m5rD2>k75nc)O*BAaJCmH@ZiV~|5)%&KhT1f0TNd0@A z-pq)d*^F_EyoE8idu*kEScN;Y(TYIa9aYLya_y~~U}{IRHog3L3A90NqCA^9dZk@; zCwB5>Z5cyRhl2QNd8KY`7oIXHFS;cvb`b#9usr{L5EcSM7#e$~0DojMDM?ztJtX~` z(~WKOC=8=GSNi28O&Gdk5j=qszI|j#xRo>r zxg1L1wF5F+^9r-AUkpOG&UEo?-I-@7H`t8RQvB)+ZAzE8*YQ*9gJtBN1^z`j6J`q^ z{E3QW*fbOlY}fvLtF%ac1t7|v(0})q0U^;??t||VFYp}3l>4b1N?QYO1|1c;*AgZ( zw_Q9vJr69hw-`>49+7g{{0PA}evZC-)}_;r}Os z-}^;txt_Eqs@ol}HyfZ`oAzYn6RnU>L1?qyzzzeJl^|A0$_)}SD!!kN8ICQ^kO+&b zS3DVDnAKP<-agug=~sv&x!p2(h_f-OwU`62EOy{ONGj)YdliW)+P$(iho$3qpVZTE z<+H{T&_NrVajLBB_?_b@6gRDne>y-{9EdJ&{6gKG(t|ZOpi^x9<5x<27S%U6Il^lT zFW4mGl+Q+>j)oi+_+h3s{~O(-VlY*HkLbl^m9uVT%#}xTZ+*wJj5+)paz&8BSz?k|>lD2AZ*i41>FsF-$f;@Lu*5=CDdnS5m$`uxZq0x8aHe(k^KvN1R z^OQVM=0h><4LlP3+92@%>_c^YY&3?r`Ff&Q&t2|xiqRh3(3O7-<18xl&7bI1X=-Eb ze}us*D(YC_s#?=-GXK3?@pJ6Ts1()Y6G8AhiN})6Wdq2qf#M(+t!QWRqxRKRp&RXq z3g4b;T03{+$;?AmZ#ABK$v$sACKoVu73{JHYjynGl-$@8tjB~Eh9*dWfELQ+Nd`4o zA~+)xyFMr%9T&5t;y^C``^H~X?yb6A#+LgfHY1#Fs(Tq)YbcL_^|; z|C5$bu@5IsFg6{J{U!=a*m{CJBvqQE}ce+Y# zbo4Op<1Qs}<*Y=gE3~bO_4{Qro{cV7KCIYI{dVm9^0!v|QdzGEEN$ ztCRnJmK(Iq#^v)u0SVq5z)9o7b(ht3wsDN8U6%f`ch7F>)KIFuO>F*&d z4P?l{T$5hkS>%|=)sfpdT^}@#91S%v;yi9OoaDC>Y%8RZV=~t~8zWu2OB#gDGmY>} z(XkVZWi=5&aBEU(n%NNgnFrFib8^4@<6K9PlgaM^sK zPqt8L`XsY^mbgiS^vCT!H#7D^kRh%oVGQ!--NE#47D)9iTPZifm3aQUl2vHpH<@lY$A zF)_mZ>jYF2a;?sH2iFkh>6jq4!Th>sbcZ!`#v@gujQ8A5(7h32!Ua87FT$zv-ygqF z!U6ODzx;nN4h;m^i$5L|1YrTSqdn-)ihjwMlzU0jiogwl=VUMs)9&rAglxHAqLDpL zcxAE%E<}w!d6eYSfM}#4MC2BxmF|D>mF>mH1R};9tqU+5BOME=rR%+(0A%aKyddzp z&;C<=S|zbS)8drdPzPJu&LPqz0aGh~>3RVJ*E|{hsb{t?TE8Kekd*$8Z6^lo z8HB^2kNZtQhZjjlC%I7~TU zMQ^eaWI{WKM~=uaVi`!=xO5t;;swB=6(DUZHe5>cpfPrYbnX@Z{tD0Kwon{id*ST2t{3fRkK;XgHk- zyFirkv(eQB*-;8rF^TKz-B^SZ1mhpZj`VV39Fh!)cjQ8`;Q<*qj=gJg2O6sAZK9nU z04wJMT$QTyBj%Si=oh;pTBC`mI__JdM})Y?Sd{~J(wRie+1l?fW~CBX(OMl;T{ubO zh{SuMhH^N|DTAh%)|*a*cje7Tqoxw5!pGj@#vlvbqugu#ZybUlsQ4ePHV`b^$cfGM z0%`Cax*0EGar97RC7cFUM;p#R9iz4M*Q^hI(jEst(G*#k`EU!Qul^9?Q-rqFR&t`v z@^SHohKBY1$@_Azffmw-G%N`D?(d<3tI|OUmk{W+VGwK4-8O_!Ln9w{HVf496`9~O zaoj4A))Zf4vzb@<`|B1Y7?bGCPWpDqebvLqp4=gbXeVM+%Wuce*VDVGdHj!no<9ZS#F)Mp5kIw zBWLqZs~sTSPpuu1?=tX8$Cz3h^^+)RrNRuhN_itcZ?{Qy=$;Sm8T}h`SrIN`BF#~E zga0eD6tnCP*N|!}&P)+!4Z9`VrZOou?=kN6N#I6nCG~ThPdvLiKvaQ7RRtR4x*doK z2yoJ_%<(tKZmsh4TCXf#`S)!J)9-w3vBNApAQzB#ZfvW|ul9pk(6`rJZo3csgQtMORsnww6G0@n5n-L4dkPfZIa z$yjg9gxsdD?=_AiMF`vo8kNbG>L8!^LVdIe%DMFNf{z0~D4I~Q_6Dr3KeW5k9I=h-_mX`4o>T*iPFn<2+rgY>e8qLg`E@2SV{jmOpbfvxeT&phlb>4+J!aJUhUi)-aGzg} zT>Ka&Ha>b&{^l?xahh}e{Smj<2G~%5crjtC{O{Ohm!hZq+9%g> zQsx@XweB22D=L*go^FN^KIVsf2F;u-ANL!80xo4Kbm)VfwcWRSML9&Iugf8eRO{|L z^Xfc_c$IE|u7vT5=_8mcc_(KXD(h*`)6Gj%^(cd?We&j+?ZE4GCLe%{qfJuoVSd@i8FzA~?U z3qg`=3i%LmbCFo?$sFylhiV2 z3fC|jqX2)qncXjv9aj<9D3fYBB&-;pE9;+J%PG~B$l`e(r(npn_scscKN;L$_zR6n zvQ6?(98SROqrsmpjZ9S9FWKi;#0gWItAS?1CEXHBsrGe2Muuur>99-s4uO1~2!Mb^0oPpCq))Tu^Kaq_Z!_IPdRsN9k%TZYQR%9CjzU8 zN7te}YWd-SaoHU0^k`q$JVJC{9-qv*<%zEN?=7~i8fLnd9%w(3U_E!v`NxhhytFZ6 zZG}al#*p_bHJ~2(w;_xL&oP zy*_JF@UF$|Xsi_HZ=#e3M*aOCd0$_E!XNMjiGIiLz}&)U-7ug+dU4(E0__N%zHmER zE#%-%-&r~q03xjnyBCCeBwL7mPd1#V;Cu4?R-pdbu9D_ElE>>-IiJ;k*O;#~5v2NQ zZ%~B66mcU(HvK?*;Gg4n{EVri)f@)vK$*0FN=`}Vr!{kPc}e{BV{UGXCyjxNU)yc% z@^6zjF;xpz1s3#-4bZ+-QC^#{I}ftAvTzpv zsWFM;JKC20?$E1_ugmEqpMs2Xpd=0&@PYE%8@ZJpm%tkA))x}Cde+f6WxDFP*HhgDIt323_MXx{K0cds-(|2 zFL&IQSDm1E5{g~#o277dqxm&=2B2nf-uyig4w7dxx}CSLsZNA{PvLuiW*_QUHYL3Y zwJEPP5-|-bA)2~sIr*!P-}czYsDgL+SJNpqSWvsLN%jtyy*3?B6+M;KUAZrIN0WFp zE=QTi6s_<6o=5X=KTYmjGv-QVD;dJ2_F_WGv9u>Ucw^g3-mvyPzm1QkHvQz_m({fC zil4T8^GX-OT-FmJRit~Vt#?#?yB5c0SYX^YLD>HmYKOE7abYp-LHfF#>hj?N2}#AT zWKmO|J(BDuqOlLYJSJ1e$@5g!ycIbJ%6WejM5L9h5)!;@9LWMjo^p&0Lv4S_dfY8m z%mT~QB63w7ZfKo{w-A}Bce^8(x`2~!lXV<#|_)50?ttGt7O%zQ~a8_4LOyzM~4MVhtKW|DoP>f!mtFj`U@aO5|rm8K7 z$yQSqcbJ<#uBQbPH^sOcU6Q2Yc~3O9lwiK{kXPF$^nd?)4hQXyVt8!C^+#6B5+1P- zV$yATz(=lIjGQ8#tgz-%C(|kH5XKgq)4C|9AFnt6UBcE^^N9ezB6&^ci;2zfdY1>a z>7E~Z0{?wvsHY8U{KtVW>sq`r*gMHCLwtS7zEv4=_97m==jhKWDRzX7fa3i@(;GqX zUt|Yec4E=pkHLHY=3KHFtigJm3^avMeCMQ}tz~q9+gsj^pv*Xb?qymwZXsJIHK||X z161!=xw*OBMQ_JJ-+MYbZGIhqp&ek?IF0QC%yb3bsWFV21j2r8w3C2{h(){(6#^Wr zl0LcxoM`7S>Sr(BkkRppXua5GlXm+_KtTD(w4C#a%&vz@@3c^tu4bHOTAL@QOH4PN zW|87$%9UM@JQq286Hj{)ip>rn7BP;}`+fpnHgiMIx?ZHOTI_-7y}~8McUou_=2OqS zz1QLz>9HHH+xLF2-Rr@J#}1V#6_sy;YD+;2Z3m&KA@nz~xGtl^S1^LyvanDwLTltb z@G>$Q8uh&Q0ap}!xCg~{OwfMY@z=W>>~l?Ry_*3`zdx%{HzvHvmA})sN$AhVGa?Jj zb___)ndx%xvF9|k*|Tp?Dd!gU7vY@K7 z-y|~=)(Lcg#UYC!&HXBmzl{HNR#62E9heZ~KlRl}ipiZjcUJhqOdxI(M%T`diy!;h zH6A}2&L0l?)LA8De;9x+=$SPpHPKu6kUmI(?m_t#)HK{nU*z@LT+uPei#`(xsgW1QTc4(#9asCcTUV(z4 zBK$}D1sH>ATvW^o7Bw}|kt`GP!-;*MuTR^suLb+T8d13Ll;{T~zQnD)RbCzH8i_ zko7Xr^sc=D5-Pad4Wuz4WaD{2cPReBK6q~~CmNJ;f<%iJr8CD;($c}a zY&04pO*eYiCXp~C6nFHorse?ed=F8DJ>f5itNwTmC(1^f4Y*mn+7NpM8mBy5z4`$; zF>(OtSh?G&Q>W^llDrH-KG-`tdTicYgfW56jKmc3*Pa_}8ajJiIcRq{I6B6(K8CKI zIS0S}WLc#jG82tcP@JJAUG*rBfcgNJKdKN72m*6nB@ z^>=qH=-y7qW70hx;X`!`Tu)u>O_v4zmUFA@yHZNM@ll4S*rUr0nTQy%$6p>@1NmHa z1i4zd&HQgO6O({TG?GU|mK8YNw96U@Lcewcb;Y1+Ir6&ciXhFUq}U1(*KcJ@h5y}w zvZC{FMeJ=}jZ9p4ZxQC5NlX2gN8L$e-0pdJQ>e(~`|nj@x%1J(%??gZwWo8v0`u{* zm@(a&kmz?fPWicaF`YiFyp>VlE>~AK+V_V8+NH1ac208it%13cuH1(;+IL1RP|wM! z0$(CIfW~;vSF~(rsMcisH;g-55?zXu4>CB=QuJPX!R&ds1^I%!JOvgCh&)AmMX3h1 zGr8Br*z6OC8ys-+RZ&VDU52i2;k}q5S!I@=I8P$7@1oF~$+{|srFmrUCD1MKtWRP+ z>qWTdde=0t1}#W|2SLpfOh$Oi)h~bk+#B$E2o2t`_6Pjy`_lwPr{WJ2y$?9-hn_>P zMg6|9%8vI0DMo154CVMw5m!X^4%+Oc&aJ-Klhryl;XOk`awpolo|BsvM!v^gTmf@r zv@kpj%rwJy|4K4Nw|HFE(|B>;`rD`8iEPEy_h-u$iQv^db&f3u8xq0Eh+<}K#i_VO(yjM&_ zK>gEL`Q_PJS2STyejc^AqN%~{ze@Ip z)km=Pi+*yh;$)-v?UvTzZ8!O)u+cr`v(YV+xG16aONgtM-rA?@yua+Sd#bS;)pbK9 zb~4<`T2n#X?)CWd^SCPJhA$pRChAjGGGd*|`-@8m4a+?d?qhcb66l$Bor9*hYjW+f z8-w-t*oz`WviD~ba8(?(e_q`r6uzC35rg`4_RtQcjtkTsZDhYk6Q7NE>sRN|UGJq1 znp<%!&z4$RpY84T>9py%TD$#zLkrIqU0xLLlYDS^7zoM|DSmPNfn{*W`HYnDwYV|x ztfhq$m2w!|=U=8Yb*m^_(x#H|5tQp}hF!55Hwn^Esq?u}XfG^YUijk08@ZF71rs`6 zp!i#`8m0*utL2Dp@nTnCYiu^O2-@_M5d%A#;g7gzzB3~3)nUDG8V(R$H|K2jf(@Am zF{ZgxjPCPdxBH{a=z3sMOYrHO8Ez5~6m%qd6{u1-xO8h5S_Yg$Orl7^si;f{^KxL z;?S+~3S8YbvxZT#cAjj=hyr=Y%maTNSvF^q)%14<9IguN8HKY9wPzt8(F0uv30lbn zJwPDjnw%m*<9UZr56|4d00)3vlO2l&^!rRKOw?+0Oo>ffc?a6K^ zjd8H&WB;H{602qVT7{n8ST~vR^`p#YvCOhpJS*mHv~B&G#nizkZ&Z^%6?$s%OS3v) zWn_PWGw=S#k?LaaT9N#abd@eS<(XlRXv?n3nThTF`cvZzn3a)`TsT4}J=eLo7gi@4 zEx0phFtgsa&L^K;Rr&E&xE{g{y@**cBppgJu2$`D0_pvJ|L{oaM-6pDKB^a*sf4_C zHWPp4xq2d6XYjq?i3N9eGmL#~;+Tl4mo2+(0bl0DlH70qg7tgQN&am=5gn#~p-Z7I#gvKr``r1wCr_SqfwONU@gQa=x;8(N zEh+isv5$qjxpm`xQeKcUD)`9aE zv^O5;9T_=f`f(uDL~$$v6O~#!uc9R?E4xz&$D%c+eC=(ffJx7x7!3lInyMpYQg9O(I7=(=NX(6&6cshC*lXToXfqmIfM_Ud9Hz8n`g{Fb4- zoj#NeqE9*QB=>|{Lr`0O;HdlPfh2mTPnvn;hV@~*r1rk^`S}b` z|8US=8b#ig7_pQuI(Ac@YmY^{$Gnxd9xlj)nN2UG67=YM>wYsR8~p|1&0fcI3ujckmV3w7yvW{M8g3o;cV*(_GoV&OHMIAS^dPM$KLY-J!@oAQFX zndZb{nkE&7?8Up0L^{f-!Eg76m%R^vuw8yVLuP5S>-P*g_?^$@VL&qNp*t0LoyOYa zvWe+YpI(37@)bjUVeGpaik<93=f|cW6by|YZ%%#+^QhYMyY^96xUuTA<|&b9+pvUR zr+qnBM^%^LIdyH+NR6o>k~R_wlA#MO4%u-9g?I4bxDL6@!R-aWy^O#&9}A^?$pq< zy`Go9Su{d@aqmk{=$kD$*P*Or(W-}CP9qLX2{h`rM>VN>6!wsk&@RWOw?11#-(odY z7~o1My4NR>+r0+p!wd9vpFQq|k9udSN)i9yA!_QpAfnHoIdJM$s?p0t3vH^BYy5A0 z7?;(mM~C7c7k9DW?n4ws{zdhamD9!Q z{m+Hgu!IX4o0w1#@s7~4IVs&qP-?G=xURo`Da3Z!q}2Ylr(U&ZIp2D@6I9!$JaR`#YeO-%5?$PFJkjpM=`t~|$&-6v5d4-2RIww6Z%I!c0) zvUd~Z<_^_;wjZ}-4I~BqmSwzBI#ibK1-j+5zyav<`t78dy<*aStBBRbE-5S2sO$-p6F*0B|MK%pUL}!ejZ?q6zPfOb>dt!Ke&62(ZBb9$zE4;>O3;He zG%;rDa&nGJ*ik38rmt~!K0KIfj$8`yHHzvtGvYp;)a^09^nf<+1)2HU5axzQi z6=UZQ3MuWIztxoSfQw5J&+1Ug&}_GKk+d?@Zki>usFc3g%g=T&GC@yn>?R>>*QsQp zNi)jRf7}r>eDBy|#i0}fZ?Tk=z4YBgop3*HW-#euf~UJxL^n|Pd`thHh&(KNvi?zA zm*)I||F1zJBq1?9w(+>&B<#qfq3?7Xu&Lo2?#9)AyqMM~wX)kWUhOM-F^V7$j=cDM z9rXvDjGT5@eCl@D?^TkM|I9vSvDj>l189QW%S@ zP#x36mC1km@u3!BO1`NJd6GR%h_Dp(C6u2lhN|3*l+dLi!V$MP-E`M0>sG?7ib!(B z1>TdDI0oRuDM05`T@>k)FLXIQxg@>e+G)ad-x(l*+`bg7?or=D>r1Z+JdTA>$Y|6=*6 zan7@fXgHDCE^X&T|2MbTkxb@>rz-AIY(5YmNT zq~GscZZ}uy(SCY7!<8BEkXDW~)C@Y&JfmN+gMHws0F5{im+hIHo=VCFlIv_Akx!jw zkKHObzaN|uNH+shjS9AnsndLARIQHW1c#J`tJfW?x$oA&u-}Otx=1|8R10CKl zvn3JOevO$7A8J)IS=#S^92e}GyCo6^N1xw4t~AI!fKuwciEz&8<4eSZi875B=RAM% zHW#hZzn|LK4$H)=Ik~?CZaQD}I~)Af%5Ila0QfxTz^yx9azk3ZL32A32KrD`SAcP7-z) z069Qi@2_94_x|4R2Aqp&p$bY^Ip1q$37aq&n@zK5@U@Z_jXyj;rxO6LzsnHE@_g2^pF_slkcD;Rqt>4P*T)0#q zOVJmrgu@vmXs_fTVRsd-EZr8pHB4y8`VuGyiqOwJe3$hnSGKphRE^vvVxMiOp0Po+ z>&7V0-))RP&3Zsu^vLRyTVKh!AfztS;dd!dpWbJv5_|@7G$-!8Kcy$zdw0fqoo>Dr zu4E~-6#h`mW=XC=b$6!aPl(~+j3s#((FU&ieiBh2x~H_J;6q}H*6_&&3Fw>V(Ukt# zcC9x!Y$q;kY`f%qRPKm1l2!<0yg5{gqBS}yU(L+C2?p!NtC+`xy>bEU;wut%?T~qQ1C$D#(%+3; zWB1tlgfQst7Mf_mmnky!0kh4h?gs@#WitZDM!6XoRzecTqz%v6&U90bpVaP=_lONK z1~p&_7_-F_9b6G!|F7xmyKIMj4{SZ*)Nj&)twi9ACOA%5|2Qza@6hn|2PZ=}EmD)| zVBE$@ZumoyBl-!3Aa3%(eBiq|L{1bK0y1%xzm6YkAw`Ujj*L8@4h`6i3-=lx8<+|k4ncuE$hZ|g$=OS)5pnxRBp%=FDa~8H0u86nKW)7LXX5*7V0BE( z(0$f+l=LlkM+hg$&r{=omc4li#VGFbK6HsGCwyZQ%SAV?aVJfjjIOsse8UqhD4$(b zuo+j8h+!bIAYYXYrAF8sZ0X28u{PwvdF8@{Z&2x;LN%mIY)qm`=^UKJ?%A?8D4_nG z3r!VYWrC}tie5o%Pz<7I&qnYXV2ZGpk{ElrbGVL1rb%u=FKkRF&yo5%W9T6cSN6HA z%v|_>?YAEQQ~CWk;0mR{*kAaw;zK9VMS*aiv!31nJbs*!ZpnIjE09yt@BtsKz3Iyx zuM|OKxa~(dlAwX2vZMn4g<%a8e}ugSfYe7y0b7ww#iNO^k~T2*QlS(Heq<73>Zo$l zp)Mrw)Z1u`j#1p_Xw24)|6&=!+9PZ!|2O|=4jq#mt*EbiVvVo}{J8;UAq7`0AOA0x CkXQl$ diff --git a/slides/README.md b/slides/README.md index 45a742a32..e22f86b7c 100644 --- a/slides/README.md +++ b/slides/README.md @@ -15,13 +15,23 @@ python3 -m venv .venv # it is important to use the dot prefix if you are creati pip install -r slides/requirements.txt # launch jupyter from the top level of this repo, **not** in the slide # directory or else the relative figure links won't work -jupyter-notebook -# navigate to the slide file and edit +jupyter-notebook # or `jupyter-lab` +# navigate to the slide file, right click, "open with > notebook". ``` +> Note: we have had mixed success using the RISE extension in both `jupyter-notebook` and `jupyter-lab`. +> Try the other if one isn't working for you. +> To use the old `jupyter-notebook`, put this in the `requirements.txt` file: +> +> ```requirements +> notebook < 7.0.0 +> rise +> jupytext +> ``` + Use the RISE extension to Jupyter to view the slides. This allows you to enter the slideshow from the Jupyter notebook web interface. -There should be a button with a histogram on the Jupyter notebook toolbar +There should be a "projector screen" button on the Jupyter notebook toolbar next to the kernel name (you might need to go to the 'View' menu to get the toolbar to show). Click the button, or press `Alt-r` to launch the RISE presentation view. Use spacebar to advance slides. Presenter view with `t`. diff --git a/slides/pyproject.toml b/slides/pyproject.toml new file mode 100644 index 000000000..dda678fbf --- /dev/null +++ b/slides/pyproject.toml @@ -0,0 +1,2 @@ +[tool.jupytext] +notebook_metadata_filter = "-kernelspec,-jupytext.text_representation.jupytext_version,rise,celltoolbar" diff --git a/slides/requirements.txt b/slides/requirements.txt index b2c21d0c4..2684b16ae 100644 --- a/slides/requirements.txt +++ b/slides/requirements.txt @@ -1,3 +1,3 @@ -notebook < 7.0.0 -rise +jupyter +jupyterlab_rise jupytext diff --git a/slides/section_1_setting_up_environment.md b/slides/section_1_setting_up_environment.md index d8f3fdac2..ffc4bf0bf 100644 --- a/slides/section_1_setting_up_environment.md +++ b/slides/section_1_setting_up_environment.md @@ -2,16 +2,11 @@ jupyter: celltoolbar: Slideshow jupytext: - notebook_metadata_filter: rise,celltoolbar + notebook_metadata_filter: -kernelspec,-jupytext.text_representation.jupytext_version,rise,celltoolbar text_representation: extension: .md format_name: markdown format_version: '1.3' - jupytext_version: 1.14.6 - kernelspec: - display_name: Python 3 (ipykernel) - language: python - name: python3 rise: theme: solarized --- @@ -451,7 +446,7 @@ Start from this heading and go until the "Git Branches" heading. ### Git Branches -
+
diff --git a/slides/section_2_ensuring_correctness.md b/slides/section_2_ensuring_correctness.md index 1a2e4c144..3f3473238 100644 --- a/slides/section_2_ensuring_correctness.md +++ b/slides/section_2_ensuring_correctness.md @@ -2,16 +2,11 @@ jupyter: celltoolbar: Slideshow jupytext: - notebook_metadata_filter: rise,celltoolbar + notebook_metadata_filter: -kernelspec,-jupytext.text_representation.jupytext_version,rise,celltoolbar text_representation: extension: .md format_name: markdown format_version: '1.3' - jupytext_version: 1.14.6 - kernelspec: - display_name: Python 3 (ipykernel) - language: python - name: python3 rise: theme: solarized --- @@ -204,7 +199,7 @@ Follow along from this section heading to the bottom of the page. - split learners into breakout rooms (~50 mins although likely less, so take a status check early) starting from this section heading and going to the end of the page - - if learners are using different editors, then encourage them to try and replicate the techique of debugging that is explained here + - if learners are using different editors, then encourage them to try and replicate the technique of debugging that is explained here - status check diff --git a/slides/section_3_software_dev_process.md b/slides/section_3_software_dev_process.md index d70b91e3c..0b7ed8669 100644 --- a/slides/section_3_software_dev_process.md +++ b/slides/section_3_software_dev_process.md @@ -2,21 +2,16 @@ jupyter: celltoolbar: Slideshow jupytext: - notebook_metadata_filter: rise,celltoolbar + notebook_metadata_filter: -kernelspec,-jupytext.text_representation.jupytext_version,rise,celltoolbar text_representation: extension: .md format_name: markdown format_version: '1.3' - jupytext_version: 1.14.6 - kernelspec: - display_name: Python 3 (ipykernel) - language: python - name: python3 rise: theme: solarized --- - + # Section 3: Software Development as a Process
@@ -29,7 +24,7 @@ jupyter: - We are going to step up a level and look at the overall process of developing software - + ## Writing Code versus Engineering Software - Software is _not_ just a tool for answering a research question @@ -41,7 +36,7 @@ jupyter: - Software can be reused 🔁 - + - Software is _not_ just a tool for answering a research question - Software is shared frequently between researchers and _reused_ after publication - Therefore, we need to be concerned with more than just the implementation, i.e. "writing code" @@ -52,7 +47,7 @@ jupyter: - Software can be reused: like with stakeholders, it is hard to predict how the software will be used in the future, and we want to make it easy for reuse to happen - + ## Software Development Lifecycle
@@ -60,7 +55,7 @@ jupyter: Cliffydcw, CC BY-SA 3.0, via Wikimedia Commons - + The typical stages of a software development process can be categorised as follows: - Requirements gathering (coming up next): the process of identifying and recording the exact requirements for a software project before it begins. This helps maintain a clear direction throughout development, and sets clear targets for what the software needs to do. @@ -96,15 +91,15 @@ There is value we get from following some sort of process: 3. solution requirements: the how - + ### Breakout: Reading and Exercises Read from the top of the "Software Requirements" page and do the exercises as you go. - + If you are using a shared document, you could have sections for each of the -requirement types and get learners to write their suggestions in their. +requirement types and get learners to write their suggestions in there. Afterwards, you could go through some of the suggestions and see whether there is agreement about whether they have been categorised correctly. @@ -172,15 +167,17 @@ There is a limit (and it is low!) Reduce cognitive load for a bit of code by: - * Good variable names + * Good variable names: `toroidal_magnetic_field` much better than `btor` * Simple control flow * Functions doing one thing + * Good abstractions (next slide!) -Good variable names - give examples -Simple control flow - explain means not lots of nesting if statement +Good variable names - we not longer have punch card restrictions, so use more descriptive names! + +Simple control flow - explain means not lots of nesting if statement or for loops @@ -192,8 +189,13 @@ An **abstraction** hides the details of one part of a system from another. -Give some examples of abstractions -Maybe ask for people to think of ideas of abstractions in the real world? +Give some examples of abstractions, or maybe ask for people to think of ideas of abstractions in the real world? + +Examples: + +- A brake pedal in a car: we don't need to know the exact mechanism by which the car slows down, so that implementation has been "abstracted" away from the car user +- Similarly, a light switch is an abstraction: we don't need to know what happens with the wiring and flow of electricity in order to understand that one side means the light will be on and vice versa +- human society is full of things like these... @@ -248,6 +250,10 @@ When making a change to a piece of software, do the following: Rest of section we will learn how to refactor an existing piece of code + +In the process of refactoring, we will try to target some of the "good practices" we just talked about, like making good abstractions and reducing cognitive load. + + ## Refactoring Exercise @@ -255,7 +261,7 @@ Rest of section we will learn how to refactor an existing piece of code Look at `inflammation/compute_data.py` - + Bring up the code Explain the feature: @@ -264,7 +270,7 @@ In it, if the user adds --full-data-analysis then the program will scan the dire The main body of it exists in inflammation/compute_data.py in a function called analyse_data. - + ## Exercise: why isn't this code maintainable? @@ -279,11 +285,15 @@ Maintainable code should be: Time: 5min - + Solution: + Hard to read: Everything is in a single function - reading it you have to understand how the file loading works at the same time as the analysis itself. + Hard to modify: If you want to use the data without using the graph you’d have to change it + Hard to modify or test: It is always analysing a fixed set of data stored on the disk + Hard to modify: It doesn’t have any tests meaning changes might break something @@ -314,10 +324,13 @@ Functions that just do one thing are: - + We identified last episode that the code has a function that does many more than one thing + Hard to understand - high cognitive load + Hard to test as mixed lots of different things together + Hard to reuse as was very fixed in its behaviour. @@ -346,7 +359,7 @@ but we will remove them in the end. ## Exercise: Write a Regression Test for Analyse Data Before Refactoring -Add a new test file called `test_compute_data.py` in the tests folder. +Add a new test file called `test_compute_data.py` in the tests folder. There is more information on the relevant web page. Complete the regression test to verify the current output of analyse_data is unchanged by the refactorings we are going to do. Time: 10min @@ -376,12 +389,13 @@ There will be no side effects from running the function - + Externalities like what is in a database or the time of day + Side effects like modifying a global variable or writing a file - + ## Pure Functions Pure functions have a number of advantages for maintainable code: @@ -391,7 +405,7 @@ Pure functions have a number of advantages for maintainable code: - + ## Refactor Code into a Pure Function Refactor the analyse_data function into a pure function with the logic, and an impure function that handles the input and output. The pure function should take in the data, and return the analysis results: @@ -406,7 +420,7 @@ Time: 10min - + ## Testing Pure Functions Pure functions are also easier to test @@ -441,7 +455,7 @@ Python, and other languages, provide features that make it easier to write "func - + If there is time - do some live coding to show imperative code, then transform into a pipeline: * Sequence of numbers @@ -450,34 +464,39 @@ If there is time - do some live coding to show imperative code, then transform i * Add them together -``` -def is_even(number): - return number % 2 == 0 - +```python +# Imperative numbers = range(1, 100) total = 0 for number in numbers: - if is_even(number): - squared = number ** 2 - total += squared - -evens = filter(is_even, numbers) -squared_evens = map(lambda number: number ** 2, evens) -total = reduce(lambda number, total: total + number, squared_evens) -# OR sum(squared_evens) + if number % 2 == 0: + squared = number**2 + total += squared + + +# Functional +def is_even(number): + return number % 2 == 0 + + +def squared(number): + return number**2 + + +total = sum(map(squared, filter(is_even, numbers))) ``` - + ## ☕ 10 Minute Break ☕ - + ## Using Classes to Decouple Code - + ### Decoupled Code When thinking about code, we tend to think of it in distinct parts or **units**. @@ -486,25 +505,27 @@ Two units are **decoupled** if changes in one can be made independently of the o - + E.g we have the part that loads a file and the part that draws a graph + Or the part that the user interacts with and the part that does the calculations - + ### Decoupled Code Abstractions allow decoupling code - + When we have a suitable abstraction, we don't need to worry about the inner workings of the other part + For example break of a car, the details of how to slow down are abstracted, so when we change how breaking works, we don't need to retrain the driver. - + ### Exercise: Decouple the File Loading from the Computation Currently the function is hard coded to load all the files in a directory. @@ -515,17 +536,37 @@ Time: 10min - + +### Decoupled... but not completely + +Although we have separated out the data loading, there is still an assumption and therefore coupling in terms of the format of that data (in this case CSV). + +Is there a way we could make this more flexible? + + + +- The format of the data stored is a practical detail which we don't want to limit the use of our `analyse_data()` function +- We could add an argument to our function to specify the format, but then we might have quite a long conditional list of all the different possible formats, and the user would need to request changes to `analyse_data()` any time they want to add a new format +- Is there a way we can let the user more flexibly specify the way in which their data gets read? + + + +One way is with **classes**! + + + ### Python Classes -A **class** is a Python feature that allows grouping methods with some data. +A **class** is a Python feature that allows grouping methods (i.e. functions) with some data. - + Do some live coding, ending with: -``` +```python +import math + class Circle: def __init__(self, radius): self.radius = radius @@ -539,12 +580,12 @@ print(my_circle.get_area()) - + ### Exercise: Use a Class to Configure Loading Put the `load_inflammation_data` function we wrote in the last exercise as a member method of a new class called `CSVDataSource`. -Put the configuration of where to load the files in the classes constructor. +Put the configuration of where to load the files in the class' initialiser. Once this is done, you can construct this class outside the the statistical analysis and pass the instance in to analyse_data. @@ -552,21 +593,21 @@ Time: 10min - + ### Interfaces **Interfaces** describe how different parts of the code interact with each other. - + For example, the interface of the breaking system in a car, is the break pedal. The user can push the pedal harder or softer to get more or less breaking. The interface of our circle class is the user can call get_area to get the 2D area of the circle as a number. - + ### Interfaces Question: what is the interface for CSVDataSource @@ -589,21 +630,23 @@ class CSVDataSource: - + Suggest discuss in groups for 1min. + +Answer: the interface is the signature of the `load_inflammation_data()` method, i.e. what arguments it takes and what it returns. - + ### Common Interfaces If we have two classes that share the same interface, we can use the interface without knowing which class we have - + Easiest shown with an example, lets do more live coding: -``` +```python class Rectangle(Shape): def __init__(self, width, height): self.width = width @@ -619,7 +662,7 @@ total_area = sum(shape.get_area() for shape in my_shapes) - + ### Polymorphism Using an interface to call different methods is a technique known as **polymorphism**. @@ -628,20 +671,24 @@ A form of abstraction - we've abstracted what kind of shape we have. - + ### Exercise: Introduce an alternative implementation of DataSource -Polymorphism is very useful - suppose we want to read JSON. +Polymorphism is very useful - suppose we want to read a JSON (JavaScript Object Notation) file. Write a class that has the same interface as `CSVDataSource` that loads from JSON. -There is a function in models.py that loads from JSON. +There is a function in `models.py` that loads from JSON. Time: 15min + +Remind learners to check the course webpage for further details and some important hints. + + ### Mocks @@ -649,11 +696,11 @@ Another use of polymorphism is **mocking** in tests. - + Lets live code a mock shape: -``` +```python from unittest.mock import Mock def test_sum_shapes(): @@ -671,11 +718,11 @@ def test_sum_shapes(): Easier to read this test as don't need to understand how get_area might work for a real shape. -Focus on testing what we're testing +Focus on testing behaviour rather than implementation. - + ## Exercise: Test Using a Mock Implementation Complete the exercise to write a mock data source for `analyse_data`. @@ -684,7 +731,7 @@ Time: 15min - + ## Object Oriented Programming diff --git a/slides/section_4_collaborative_soft_dev.md b/slides/section_4_collaborative_soft_dev.md index b2bf0abb9..9ea47f4d0 100644 --- a/slides/section_4_collaborative_soft_dev.md +++ b/slides/section_4_collaborative_soft_dev.md @@ -2,16 +2,11 @@ jupyter: celltoolbar: Slideshow jupytext: - notebook_metadata_filter: rise,celltoolbar + notebook_metadata_filter: -kernelspec,-jupytext.text_representation.jupytext_version,rise,celltoolbar text_representation: extension: .md format_name: markdown format_version: '1.3' - jupytext_version: 1.14.6 - kernelspec: - display_name: Python 3 (ipykernel) - language: python - name: python3 rise: theme: solarized --- diff --git a/slides/section_5_managing_software.md b/slides/section_5_managing_software.md index 45e216cc1..23da490b2 100644 --- a/slides/section_5_managing_software.md +++ b/slides/section_5_managing_software.md @@ -2,16 +2,11 @@ jupyter: celltoolbar: Slideshow jupytext: - notebook_metadata_filter: rise,celltoolbar + notebook_metadata_filter: -kernelspec,-jupytext.text_representation.jupytext_version,rise,celltoolbar text_representation: extension: .md format_name: markdown format_version: '1.3' - jupytext_version: 1.14.6 - kernelspec: - display_name: Python 3 (ipykernel) - language: python - name: python3 rise: theme: solarized --- From 41cafbb5b3297da355603497f811d6aa375d501b Mon Sep 17 00:00:00 2001 From: bielsnohr Date: Wed, 14 Aug 2024 13:52:27 +0100 Subject: [PATCH 02/16] =?UTF-8?q?=F0=9F=92=AC=20Soften=20language=20around?= =?UTF-8?q?=20use=20of=20SSH=20over=20HTTPS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _episodes/11-software-project.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/_episodes/11-software-project.md b/_episodes/11-software-project.md index 4dd991414..75da4b17c 100644 --- a/_episodes/11-software-project.md +++ b/_episodes/11-software-project.md @@ -99,17 +99,20 @@ and then obtain a local copy of that project (from your GitHub) on your machine. ![View of your own fork of the software repository in GitHub](../fig/github-forked-repository-own.png){: .image-with-shadow width="900px" } > ## Exercise: Obtain the Software Project Locally +> > Using the command line, clone the copied repository > from your GitHub account into the home directory on your computer using SSH. > Which command(s) would you use to get a detailed list of contents of the directory you have just cloned? -> -> +> +> > > ## Solution > > 1. Find the SSH URL of the software project repository to clone from your GitHub account. > > Make sure you do not clone the original repository but rather your own fork, > > as you should be able to push commits to it later on. -> > Also make sure you select the **SSH** tab and not the **HTTPS** one - -> > for this course, SSH is the preferred way of authenticating when sending your changes back to GitHub. +> > Also make sure you select the **SSH** tab and not the **HTTPS** one. +> > For this course, SSH is the preferred way of authenticating when sending your changes back to GitHub, +> > but if you have already authenticated through HTTPS in the past, +> > it is fine for you to continue using that method. > > > > ![URL to clone the repository in GitHub](../fig/clone-repository.png){: .image-with-shadow width="800px" } > > @@ -270,6 +273,7 @@ are bigger modules that contain several smaller sub-modules. Another example of modules are classes in object-oriented programming languages. > ## Programming Modules and Interfaces +> > Although modules are self-contained and independent elements to a large extent > (they can depend on other modules), > there are well-defined ways of how they interact with one another. From 0a867e0a8d755f4ad0ccee5451ad625673ed7642 Mon Sep 17 00:00:00 2001 From: bielsnohr Date: Wed, 14 Aug 2024 14:44:04 +0100 Subject: [PATCH 03/16] =?UTF-8?q?=F0=9F=92=AC=20Make=20suggestion=20of=20V?= =?UTF-8?q?S=20Code=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _extras/guide.md | 13 +++++++++++++ slides/section_1_setting_up_environment.md | 13 ++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/_extras/guide.md b/_extras/guide.md index df9f2c360..8b42346b2 100644 --- a/_extras/guide.md +++ b/_extras/guide.md @@ -9,6 +9,7 @@ title: "Instructor Notes" ## Course Design + The course follows a narrative around a software development team working on an existing software project that is analysing patients’ inflammation data @@ -41,6 +42,7 @@ The course has been separated into 5 sections: Each section can be approximately delivered in a half-day but even better if you can allow 1 day per section. ## Course Delivery + The course is intended primarily for self-learning but other modes of delivery have been used successfully (e.g. fully instructor-led code-along mode or mixing in elements of instructor-led with self-work). @@ -86,7 +88,17 @@ to make sure everyone is in sync as much as possible and are working on similar This synchronisation becomes particularly important for later workshop stages when students start with group exercises. +### Section 1.3 Note + +Although not explicitly endorsed, +it is quite possible for learners to do the course using VS Code instead of PyCharm. +There is a section for setting up VS Code in the [this adjacent extras page](./vscode.md). +However, because it can be a bit difficult for learners to go through that while also paying attention to the course content that uses PyCharm, +some instructors have found it helpful to perform a demonstration on their own machines of how to use VS Code to achieve the same functionality as PyCharm. +It is worthwhile preparing this in advance of the session. + ### Helpers Roles and Responsibilities + At the workshop, when using the "self-learning with helpers" delivery mode, everyone in the training team is a helper and there are no instructors per se. You may have more experienced helpers delivering introductions to the workshop and sections. @@ -111,6 +123,7 @@ Roles and responsibilities of helpers include: if possible ### Group Exercises + Here is some advice on how best to sync and organise group exercises in later stages of the course. - For earlier workshop stages, diff --git a/slides/section_1_setting_up_environment.md b/slides/section_1_setting_up_environment.md index ffc4bf0bf..97ace3b23 100644 --- a/slides/section_1_setting_up_environment.md +++ b/slides/section_1_setting_up_environment.md @@ -390,7 +390,6 @@ pip install -r requirements.txt # great reason to have this file - Preference for Code Editors and IDEs is one of the more contentious and strongly felt topics among software developers, but the bottom line is that if a tool works for you and helps you be productive, then it is absolutely fine to use that tool - But again, for the practicalities of this course, the decision to support two editors, PyCharm and VSCode, has been made - If you are comfortable enough in another IDE or code editor to get the functionality demonstrated in the content below, then please feel free to use that tool here, but this is a disclaimer that we cannot promise to resolve any issues you have, and if these issues are holding the group up then we will need to move on - - For VSCode, please consult the "Extras" content of the course web page and find the analogous functionality described there; if you are having trouble getting something to work, please ask for help! @@ -400,10 +399,14 @@ Start from this heading and continue to the end of the page. -- Send learners into breakout rooms to read through and try out content from "Using the PyCharm IDE" (~ 30mins, but could be less, so poll helpers after 20 minutes to get a status check from the rooms, or ask directly if in person) - - Remind to use status green check when done (or red x if having trouble) - - Encourage learners to try out the features that are being discussed and don't worry about making modifications to their code since it is under version control it will be easy to reset any changes - - Reinforce that we won't be using the version control interface of PyCharm, but it is a perfectly useable feature, and again this comes down to preference +- Before launching into this exercise, you should poll how many students are using each editor + - If the majority are using VS Code, consider doing a demo of all the features listed for PyCharm + using your own VS Code editor + - Otherwise, send learners off to read through and try out content from "Using the PyCharm IDE" (~ 30mins, but could be less, so poll after 20 minutes to get a status check, or ask directly if in person) + - For VSCode users, remind them to consult the "Extras" content of the course web page and find the analogous functionality described there; if you are having trouble getting something to work, please ask for help! + - Remind to use status green check when done (or red x if having trouble) + - Encourage learners to try out the features that are being discussed and don't worry about making modifications to their code since it is under version control it will be easy to reset any changes + - Reinforce that we won't be using the version control interface of PyCharm, but it is a perfectly useable feature, and again this comes down to preference From 4d6b4e08e11059ef1795d8864cf99da83463cbf7 Mon Sep 17 00:00:00 2001 From: bielsnohr Date: Wed, 14 Aug 2024 15:08:37 +0100 Subject: [PATCH 04/16] =?UTF-8?q?=F0=9F=92=AC=20Add=20a=20link=20to=20IDE?= =?UTF-8?q?=20section=20in=20instructor=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _extras/guide.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/_extras/guide.md b/_extras/guide.md index 8b42346b2..91632c63f 100644 --- a/_extras/guide.md +++ b/_extras/guide.md @@ -92,9 +92,10 @@ when students start with group exercises. Although not explicitly endorsed, it is quite possible for learners to do the course using VS Code instead of PyCharm. -There is a section for setting up VS Code in the [this adjacent extras page](./vscode.md). -However, because it can be a bit difficult for learners to go through that while also paying attention to the course content that uses PyCharm, -some instructors have found it helpful to perform a demonstration on their own machines of how to use VS Code to achieve the same functionality as PyCharm. +There is a section for setting up VS Code in the [this adjacent extras page](../vscode). +However, when progressing through the section [Integrated Software Development Environments](../13-ides), +it can be a bit difficult for learners to pay attention to both pages. +Therefore, some instructors have found it helpful to perform a demonstration on their own machines of how to use VS Code to achieve the same functionality as PyCharm. It is worthwhile preparing this in advance of the session. ### Helpers Roles and Responsibilities From c78739528d9c9e2b69b8f731ddddaafca86e684a Mon Sep 17 00:00:00 2001 From: bielsnohr Date: Wed, 14 Aug 2024 15:13:13 +0100 Subject: [PATCH 05/16] =?UTF-8?q?=F0=9F=92=AC=20Add=20advice=20for=20helpe?= =?UTF-8?q?rs=20about=20how=20to=20monitor=20student=20progress?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _extras/guide.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/_extras/guide.md b/_extras/guide.md index 91632c63f..97bf7b9b8 100644 --- a/_extras/guide.md +++ b/_extras/guide.md @@ -122,6 +122,10 @@ Roles and responsibilities of helpers include: - Helping students get through the material but also being ready to answer questions on applying the material in learners’ domains, if possible +- Monitoring the progress of students + - get up every now and then and do a walk around the room, look at stickies and have a peak at + computer screens (particularly if the session is running a bit behind) + - ask any learners that you might have helped previously how they are getting on ### Group Exercises From 1b6316403e48aa113bcf933c566c0252556b0438 Mon Sep 17 00:00:00 2001 From: bielsnohr Date: Wed, 14 Aug 2024 16:38:31 +0100 Subject: [PATCH 06/16] =?UTF-8?q?=E2=9C=A8=20Add=20content=20about=20integ?= =?UTF-8?q?rating=20WSL=20with=20VS=20Code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _extras/vscode.md | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/_extras/vscode.md b/_extras/vscode.md index 628df311f..7aebba421 100644 --- a/_extras/vscode.md +++ b/_extras/vscode.md @@ -18,12 +18,29 @@ in the VS Code sidebar: ![VS Code application window with the Extensions button highlighted](../fig/vs-code-extensions.png){: .image-with-shadow width="800px" } -In the search box, type "python" and select the Intellisense Python extension by Microsoft, -then click the "Install" button to install the extension. You may be asked to reload the VS Code IDE for the changes -to take effect. +In the search box, type "python" and select the Intellisense Python extension by Microsoft, +then click the "Install" button to install the extension. +You may be asked to reload the VS Code IDE for the changes to take effect. ![VS Code application with the list of extensions found by search term "python"](../fig/vs-code-python-extension.png){: .image-with-shadow width="800px" } +### Using VS Code with Windows Subsystem for Linux + +If you plan to do this course and your development generally in [WSL (Windows Subsystem for Linux)][wsl], +then it is a good idea to make sure that VS Code connects properly to WSL. +In your WSL terminal, navigate to the project folder for this course and execute the command + +```bash +code . +``` + +This should launch VS Code in a way that ensures it performs most operations within WSL. +To do this, the [WSL - Remote extension][vscode-wsl-extension] +for VS Code should automatically be installed. +If this doesn't happen, please install the extension manually. +You can also launch WSL sessions from VS Code itself using the +[instructions on the extension page.][vscode-wsl-extension-launch-options] + ## Using the VS Code IDE Let's open our software project in VS Code and familiarise ourselves with some commonly used features needed for this course. @@ -175,3 +192,8 @@ When clicking on a test you will see two icons, the ordinary Run/Play icon, and a Run/Play icon with a bug. The latter allows you to run the tests in debug mode useful for obtaining further information as to why a failure has occurred - this will be covered in the main lesson material. + + +[vscode-wsl-extension]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl +[vscode-wsl-extension-launch-options]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl#commands +[wsl]: https://learn.microsoft.com/en-us/windows/wsl/about From d1bde663d92354781577ad1e1b3f7025a848f272 Mon Sep 17 00:00:00 2001 From: bielsnohr Date: Wed, 14 Aug 2024 16:46:48 +0100 Subject: [PATCH 07/16] =?UTF-8?q?=F0=9F=90=9B=20Add=20back=20VS=20code=20t?= =?UTF-8?q?o=20the=20extras=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/_config.yml b/_config.yml index 213b9606b..3338e16e6 100644 --- a/_config.yml +++ b/_config.yml @@ -100,6 +100,7 @@ extras_order: - object-oriented-programming - persistence - databases + - vscode - quiz # Files and directories that are not to be copied. exclude: From 446f4dad4eacb7fa36c9997cbe9500331eb1684e Mon Sep 17 00:00:00 2001 From: bielsnohr Date: Wed, 14 Aug 2024 16:50:47 +0100 Subject: [PATCH 08/16] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Bump=20python=20vers?= =?UTF-8?q?ions=20in=20GitHub=20Actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _episodes/23-continuous-integration-automated-testing.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/_episodes/23-continuous-integration-automated-testing.md b/_episodes/23-continuous-integration-automated-testing.md index 2440ec8bd..deeda1fdb 100644 --- a/_episodes/23-continuous-integration-automated-testing.md +++ b/_episodes/23-continuous-integration-automated-testing.md @@ -201,7 +201,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Python 3.11 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.11" @@ -370,7 +370,7 @@ jobs: strategy: matrix: os: ["ubuntu-latest", "macos-latest", "windows-latest"] - python-version: ["3.10", "3.11"] + python-version: ["3.10", "3.11", "3.12"] # Here we add the reference to the os matrix values runs-on: {% raw %}${{ matrix.os }}{% endraw %} @@ -382,7 +382,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: # Here we add the reference to the python-version matrix values python-version: {% raw %}${{ matrix.python-version }}{% endraw %} From 1efc1a0f20dfe438722e1052363bd2d518496970 Mon Sep 17 00:00:00 2001 From: bielsnohr Date: Fri, 4 Oct 2024 14:39:14 +0100 Subject: [PATCH 09/16] Address comments and tweak language to ensure consistency with other parts of the lesson --- _episodes/11-software-project.md | 7 ++++--- _extras/common-issues.md | 4 ++-- _extras/vscode.md | 7 +++++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/_episodes/11-software-project.md b/_episodes/11-software-project.md index 75da4b17c..ff3aa7286 100644 --- a/_episodes/11-software-project.md +++ b/_episodes/11-software-project.md @@ -110,9 +110,10 @@ and then obtain a local copy of that project (from your GitHub) on your machine. > > Make sure you do not clone the original repository but rather your own fork, > > as you should be able to push commits to it later on. > > Also make sure you select the **SSH** tab and not the **HTTPS** one. -> > For this course, SSH is the preferred way of authenticating when sending your changes back to GitHub, -> > but if you have already authenticated through HTTPS in the past, -> > it is fine for you to continue using that method. +> > For this course, SSH is the preferred way of authenticating when sending your changes back to GitHub. +> > If you have only authenticated through HTTPS in the past, +> > please follow the guidance [at the top of this section](#downloading-our-software-project) +> > to add an SSH key to your GitHub account. > > > > ![URL to clone the repository in GitHub](../fig/clone-repository.png){: .image-with-shadow width="800px" } > > diff --git a/_extras/common-issues.md b/_extras/common-issues.md index 875f37f1e..55f5184be 100644 --- a/_extras/common-issues.md +++ b/_extras/common-issues.md @@ -140,8 +140,8 @@ $ git config --global core.editor "nano -w" ## SSH key authentication issues with Windows Git -Rather unhelpfully, Git for Windows will use it's own SSH instance by default -which will result in you getting errors such as the below even after adding your +Rather unhelpfully, Git Bash will use its own SSH library by default +which will result in you getting errors such as the below even after adding your SSH key correctly ~~~ diff --git a/_extras/vscode.md b/_extras/vscode.md index 7aebba421..37d93846b 100644 --- a/_extras/vscode.md +++ b/_extras/vscode.md @@ -26,8 +26,11 @@ You may be asked to reload the VS Code IDE for the changes to take effect. ### Using VS Code with Windows Subsystem for Linux -If you plan to do this course and your development generally in [WSL (Windows Subsystem for Linux)][wsl], -then it is a good idea to make sure that VS Code connects properly to WSL. +If you are developing software on Windows, +and particularly software that comes from or targets Unix or Linux systems, +it can be advantageous to use [WSL (Windows Subsystem for Linux)][wsl]. +Although this course does not explicitly support WSL setup, +we will provide some guidance on how to best link up WSL with VS Code. In your WSL terminal, navigate to the project folder for this course and execute the command ```bash From d3ca9d4cc6e8a087a34cb0f2ac22b30275e4d1d7 Mon Sep 17 00:00:00 2001 From: Aleksandra Nenadic Date: Fri, 11 Oct 2024 21:08:31 +0100 Subject: [PATCH 10/16] Apply suggestions from code review --- _extras/common-issues.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/_extras/common-issues.md b/_extras/common-issues.md index 55f5184be..ac2a8fe22 100644 --- a/_extras/common-issues.md +++ b/_extras/common-issues.md @@ -138,14 +138,14 @@ $ git config --global core.editor "nano -w" {: .language-bash} -## SSH key authentication issues with Windows Git +## SSH key authentication issues with Git Bash Rather unhelpfully, Git Bash will use its own SSH library by default -which will result in you getting errors such as the below even after adding your +which will result in you getting errors such as the one below even after adding your SSH key correctly ~~~ -PS > git clone git@github.com:https://github.com/ukaea-rse-training/python-intermediate-inflammation +$ git clone git@github.com:https://github.com/ukaea-rse-training/python-intermediate-inflammation Cloning into 'python-intermediate-inflammation'... git@github.com: Permission denied (publickey). fatal: Could not read from remote repository. @@ -158,7 +158,7 @@ The solution is to change the SSH instance used by default in newer versions of Windows with: ~~~ -PS > git config --global core.sshCommand C:/windows/System32/OpenSSH/ssh.exe +$ git config --global core.sshCommand C:/windows/System32/OpenSSH/ssh.exe ~~~ ## Python, `pip`, `venv` & Installing Packages Issues From a751368aee1d72884536c64a50b451e41523f43f Mon Sep 17 00:00:00 2001 From: Aleksandra Nenadic Date: Fri, 11 Oct 2024 21:09:15 +0100 Subject: [PATCH 11/16] Update _extras/common-issues.md --- _extras/common-issues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_extras/common-issues.md b/_extras/common-issues.md index ac2a8fe22..3c8b70e99 100644 --- a/_extras/common-issues.md +++ b/_extras/common-issues.md @@ -142,7 +142,7 @@ $ git config --global core.editor "nano -w" Rather unhelpfully, Git Bash will use its own SSH library by default which will result in you getting errors such as the one below even after adding your -SSH key correctly +SSH key correctly: ~~~ $ git clone git@github.com:https://github.com/ukaea-rse-training/python-intermediate-inflammation From f5cf0801788081e2f8fb63683ad4fa86ec5fd611 Mon Sep 17 00:00:00 2001 From: Aleksandra Nenadic Date: Tue, 15 Oct 2024 13:03:58 +0100 Subject: [PATCH 12/16] Apply suggestions from code review --- _episodes/23-continuous-integration-automated-testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_episodes/23-continuous-integration-automated-testing.md b/_episodes/23-continuous-integration-automated-testing.md index deeda1fdb..7570033e7 100644 --- a/_episodes/23-continuous-integration-automated-testing.md +++ b/_episodes/23-continuous-integration-automated-testing.md @@ -370,7 +370,7 @@ jobs: strategy: matrix: os: ["ubuntu-latest", "macos-latest", "windows-latest"] - python-version: ["3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11"] # Here we add the reference to the os matrix values runs-on: {% raw %}${{ matrix.os }}{% endraw %} From bbfff0aef573b95939e1343098f4d2b04c3eaf08 Mon Sep 17 00:00:00 2001 From: Aleksandra Nenadic Date: Tue, 15 Oct 2024 13:27:15 +0100 Subject: [PATCH 13/16] Adds small fixes --- _extras/common-issues.md | 381 ++++++++++++++++----------------------- 1 file changed, 154 insertions(+), 227 deletions(-) diff --git a/_extras/common-issues.md b/_extras/common-issues.md index 3c8b70e99..61715e0b4 100644 --- a/_extras/common-issues.md +++ b/_extras/common-issues.md @@ -1,275 +1,202 @@ --- -title: "Common Issues, Fixes & Tips" +title: "Using Microsoft Visual Studio Code" --- -Here is a list of issues previous participants of the course encountered -and some tips to help you with troubleshooting. +[Visual Studio Code (VS Code)](https://code.visualstudio.com/), not to be confused with [Visual Studio](https://visualstudio.microsoft.com/), +is an Integrated Development Environment (IDE) by Microsoft. You can use it as your IDE for this course +instead of PyCharm - bellow are some instructions to help you set up. -## Command Line/Git Bash Issues +## Installation -### Python Hangs in Git Bash -Hanging issues with trying to run Python 3 in Git Bash on Windows -(i.e. typing `python` in the shell, which causes it to just hang with no error message or output). -The solution appears to be to use `winpty` - -a Windows software package providing an interface similar to a Unix pty-master -for communicating with Windows command line tools. -Inside the shell type: +You can download VS Code from the [VS Code project website](https://code.visualstudio.com/download). -~~~ -$ alias python="winpty python.exe" -~~~ -{: .language-bash} +### Extensions + +VS Code can be used to develop code in many programming languages, provided the appropriate extensions have been installed. +For this course we will require the extensions for Python. To install extensions click the icon highlighted below +in the VS Code sidebar: + +![VS Code application window with the Extensions button highlighted](../fig/vs-code-extensions.png){: .image-with-shadow width="800px" } + +In the search box, type "python" and select the Intellisense Python extension by Microsoft, +then click the "Install" button to install the extension. +You may be asked to reload the VS Code IDE for the changes to take effect. + +![VS Code application with the list of extensions found by search term "python"](../fig/vs-code-python-extension.png){: .image-with-shadow width="800px" } + +### Using VS Code with Windows Subsystem for Linux + +If you are developing software on Windows, +and particularly software that comes from or targets Unix or Linux systems, +it can be advantageous to use [WSL (Windows Subsystem for Linux)][wsl]. +Although this course does not explicitly support WSL, +we will provide some guidance here on how to best link up WSL with VS Code (if that is your use case). +In your WSL terminal, navigate to the project folder for this course and execute the command: + +```bash +code . +``` + +This should launch VS Code in a way that ensures it performs most operations within WSL. +To do this, the [WSL - Remote extension][vscode-wsl-extension] +for VS Code should automatically be installed. +If this does not happen, please install the extension manually. +You can also launch WSL sessions from VS Code itself using the +[instructions on the extension page.][vscode-wsl-extension-launch-options] + +## Using the VS Code IDE + +Let's open our software project in VS Code and familiarise ourselves with some commonly used features needed for this course. + +### Opening a Software Project + +Select `File` > `Open Folder` from the top-level menu and navigate to the directory where you saved the +[`python-intermediate-inflammation` project](../11-software-project/index.html#downloading-our-software-project), +which we are using in this course. + +### Configuring a Virtual Environment in VS Code + +As in the episode on +[virtual environments for software development]({{ page.root }}{% link _episodes/12-virtual-environments.md %}), +we'd want to create a virtual environment for our project to work in (unless you have already done so earlier in the course). +From the top menu, select `Terminal` > `New Terminal` to open a new terminal (command line) session within the project directory, +and run the following command to create a new environment: -This alias will be valid for the duration of the shell session. -For a more permanent solution, from the shell do: ~~~ -$ echo "alias python='winpty python.exe'" >> ~/.bashrc -$ source ~/.bashrc +python3 -m venv venv ~~~ {: .language-bash} -(and from there on remember to invoke Python as `python` -or whatever command you aliased it to). -Read more details on the issue at -[Stack Overflow](https://stackoverflow.com/questions/32597209/python-not-working-in-the-command-line-of-git-bash) -or [Superuser](https://superuser.com/questions/1403345/git-bash-not-running-python3-as-expected-hanging-issues). - -### Customising Command Line Prompt -Minor annoyance with the ultra long prompt command line sometimes gives you - -if you do not want a reminder of the current working directory, -you can set it to just `$` by typing the following in your command line: `export PS1="$ "`. -More details on command line prompt customisation can be found in this -[guide](https://www.cyberciti.biz/tips/howto-linux-unix-bash-shell-setup-prompt.html). - -## Git/GitHub Issues - -### Connection Issues When Accessing GitHub Using Git Over VPN or Protected Networks - Proxy Needed -When accessing external services and websites -(such as GitHub using `git` or to -[install Python packages with `pip`](../common-issues/index.html#connection-issues-when-installing-packages-with-pip-over-vpn-or-protected-networks---proxy-needed)), -you may experience connection errors -(e.g. similar to `fatal: unable to access '....': Failed connect to github.com`) -or a connection that hangs. -This may indicate that they need to configure a proxy server user by your organisation -to tunnel SSH traffic through a HTTP proxy. - -To get `git` to work through a proxy server in Windows, -you'll need `connect.exe` program that comes with GitBash -(which you should have installed as part of setup, so no additional installation is needed). -If installed in the default location, -this file should be found at `C:\Program Files\Git\mingw64\bin\connect.exe`. -Next, you'll need to modify your ssh config file (typically in `~/.ssh/config`) -and add the following: +This will create a new folder called `venv` within your project root. +VS Code will notice the new environment and ask if you want to use it as the default Python interpreter for this project - +click "Yes". -~~~ -Host github.com - ProxyCommand "C:/Program Files/Git/mingw64/bin/connect.exe" -H : %h %p - TCPKeepAlive yes - IdentitiesOnly yes - User git - Port 22 - Hostname github.com -~~~ +![VS Code popup window asking which Python interpreter to use for the current project](../fig/use_env.png) -Mac and Linux users can use the [Corkscrew tool](https://github.com/bryanpkc/corkscrew) -for tunneling SSH through HTTP proxies, -which would have to be installed separately. -Next, you'll need to modify your SSH config file (typically in `~/.ssh/config`) -and add the following: +--- -~~~ -Host github.com - ProxyCommand corkscrew %h %p - TCPKeepAlive yes - IdentitiesOnly yes - User git - Port 22 - Hostname github.com -~~~ +#### Troubleshooting Setting the Interpreter -### Creating a GitHub Key Without 'Workflow' Authorisation Scope -If a learner creates a GitHub authentication token -but forgets to check 'workflow' scope -(to allow the token to be used to update GitHub Action workflows) -they will get the following error when trying to push a new workflow -(when adding the `pytest` action in Section 2) to GitHub: +If the prompt did not appear, you can manually set the interpreter. -~~~ -! [remote rejected] test-suite -> test-suite (refusing to allow an OAuth App to create or update workflow `.github/workflows/main.yml` without `workflow` scope` -~~~ -{: .error} +1. Navigate to the location of the `python` binary within the virtual environment +using the file browser sidebar (see below). The binary will be located in `/bin/python` within the project directory. +2. Right-click on the binary and select `Copy Path`. +3. Use the keyboard shortcut `CTRL-SHIFT-P` to bring up the command palette, then search for `Python: Select Interpreter`. +4. Click `Enter interpreter path...`, paste the path you copied followed by Enter. -The solution is to generate a new token with the correct scope/usage permissions -and clear the local credential cache (if that's where the token has been saved). -In same cases, simply clearing credential cache was not enough and updating to Git 2.29 was needed. +--- -### `Please tell me who you are` Git Error -If you experience the following error the first time you do a Git commit, -you may not have configured your identity with Git on your machine: +You can verify the setup has worked correctly by selecting an existing Python script in the project folder (or creating a blank +new one, if you do not have it, by right-clicking on the file explorer sidebar, selecting `New File` and creating a new file +with the extension `.py`). -~~~ -fatal: unable to auto-detect email address -*** Please tell me who you are -~~~ -{: .error} +If everything is setup correctly, when you select a Python file in the file explorer you should see +the interpreter and virtual environment stated in the information bar at the bottom of VS Code, e.g., +something similar to the following: + +![VS Code bottom bar indicator of the virtual environment](../fig/vs-code-virtual-env-indicator.png) -This can be configured from the command line as follows: +Any terminal you now open will start with the activated virtual environment. + +### Adding Dependencies + +For this course you will need to install `pytest`, `numpy` and `matplotlib`. Start a new terminal and run the +following: ~~~ -$ git config --global user.name "Your Name" -$ git config --global user.email "name@example.com" +python3 -m pip install numpy matplotlib pytest ~~~ {: .language-bash} -The option `--global` tells Git to use these settings "globally" -(i.e. for every project that uses Git for version control on your machine). -If you use different identifies for different projects, -then you should not use the `--global` option. -Make sure to use the same email address you used to open an account on GitHub -that you are using for this course. +--- + +#### Troubleshooting Dependencies -At this point it may also be a good time to configure your favourite text editor with Git, -if you have not already done so. -For example, to use the editor `nano` with Git: +If you are having issues with `pip`, it may be that `pip` version you have is too old. +Pip will usually inform you via a warning if a newer version is available. +You can upgrade pip by running the following from the terminal: ~~~ -$ git config --global core.editor "nano -w" +python3 -m pip install --upgrade pip ~~~ {: .language-bash} +You can now try to install the packages again. -## SSH key authentication issues with Git Bash +--- -Rather unhelpfully, Git Bash will use its own SSH library by default -which will result in you getting errors such as the one below even after adding your -SSH key correctly: +## Running Python Scripts in VS Code -~~~ -$ git clone git@github.com:https://github.com/ukaea-rse-training/python-intermediate-inflammation -Cloning into 'python-intermediate-inflammation'... -git@github.com: Permission denied (publickey). -fatal: Could not read from remote repository. +To run a Python script in VS Code, open the script by clicking on it, +and then either click the Play icon in the top right corner, +or use the keyboard shortcut `CTRL-ALT-N`. -Please make sure you have the correct access rights -and the repository exists. -~~~ +![VS Code application window with highlighted Run button](../fig/vs-code-run-script.png){: .image-with-shadow width="800px" } -The solution is to change the SSH instance used by default in newer versions of -Windows with: +## Adding a Linter in VS Code -~~~ -$ git config --global core.sshCommand C:/windows/System32/OpenSSH/ssh.exe -~~~ +In [the episode on coding style]({{ page.root }}{% link _episodes/15-coding-conventions.md %}) +and [the subsequent episode on linters]({{ page.root }}{% link _episodes/16-verifying-code-style-linters.md %}), +you are asked to use an automatic feature in PyCharm +that picks up linting issues with the source code. +Because it is language agnostic, VS Code does not have a linter for Python built into it. +Instead, you will need to install an extension to get linting hints. +Get to the "Extensions" side pane by one of these actions: -## Python, `pip`, `venv` & Installing Packages Issues +1. Bring up the command palette with `CTRL-SHFT-P`, search for `View: Show Extensions` +2. Use the direct keyboard shortcut `CTRL-SHFT-X` +3. Click on the ["Extensions" icon](./index.html#extensions) on the left side panel we used previously. + +In the Extensions panel, type "pylint" into the search bar. Select Pylint from the result panel +that comes up and then the `Install` button: -### Issues With Numpy (and Potentially Other Packages) on New M1 Macs -When using `numpy` package installed via `pip` on a command line on a new Apple M1 Mac, -you get a failed installation with the error: +![VS Code Extensions Panel showing searching for pylint extension](../fig/vs-code-install-linter-extension.png){: .image-with-shadow width="800px" } -~~~ -... -mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64e'). -... -~~~ -{: .error} - -Numpy is a package heavily optimised for performance, -and many parts of it are written in C and compiled for specific architectures, -such as Intel (x86_64, x86_32, etc.) -or Apple's M1 (arm64e). -In this instance, `pip` is obtaining a version of `numpy` with the incorrect compiled binaries, -instead of the ones needed for Apple's M1 Mac. -One way that was found to work was to install numpy via PyCharm into your environment instead, -which seems able to determine the correct packages to download and install. - -### Python 3 Installed but not Found When Using `python3` Command -Python 3 installed on some Windows machines -may not be accessible using the `python3` command from the command line, -but works fine when invoked via the command `python`. - -### Connection Issues When Installing Packages With `pip` Over VPN or Protected Networks - Proxy Needed -If you encounter issues when trying to -install packages with `pip` over your organisational network - -it may be because your may need to -[use a proxy](https://stackoverflow.com/questions/30992717/proxy-awareness-with-pip) -provided by your organisation. -In order to get `pip` to use the proxy, -you need to add an additional parameter when installing packages with `pip`: +Once installed, Pylint warnings about your code should automatically populate the "Problems" panel +at the bottom of VS Code window, as shown below. You can also bring up the "Problems" panel using the keyboard shortcut `CTRL-SHFT-M`. -~~~ -$ python3 -m pip install --proxy ` -~~~ -{: .language-bash} +![VS Code Problems Panel](../fig/vs-code-linter-problems-pane-annotated.png){: .image-with-shadow width="800px" } -To keep these settings permanently, -you may want to add the following to your `.zshrc`/`.bashrc` file -to avoid having to specify the proxy for each session, -and restart your command line terminal: +There are other Python linters available, such as [Flake8](https://flake8.pycqa.org/en/latest/), +and Python code formatters, such as [Black](https://pypi.org/project/black/). +All are available as extensions that can be installed in a similar manner from the "Extensions" panel. -~~~ -# call set_proxies to set proxies and unset_proxies to remove them -set_proxies() { -export {http,https,ftp}_proxy='' -export {HTTP,HTTPS,FTP}_PROXY='' -export NO_PROXY=localhost,127.0.0.1,10.96.0.0/12,192.168.99.0/24,192.168.39.0/24,192.168.64.2,., -} - -unset_proxies() { -export {http,https,ftp}_proxy= -export {HTTP,HTTPS,FTP}_PROXY= -export NO_PROXY= -} +We also recommend that you install these linters and formatters in your virtual environment, +since then you will be able to run them from the terminal as well. +For example, if you want `pylint` and `black` packages, execute the following from the terminal: + +~~~bash +$ python3 -m pip install pylint black ~~~ -## PyCharm Issues - -### Using GitBash from PyCharm -To embed Git Bash in PyCharm as external tool and work with it in PyCharm window, -from Settings -select "Tools->Terminal->Shell path" -and enter `“C:\Program Files\Git\bin\sh.exe” --login`. -See [more details](https://stackoverflow.com/questions/20573213/embed-git-bash-in-pycharm-as-external-tool-and-work-with-it-in-pycharm-window-w) -on Stack Overflow. - -### Virtual Environments Issue `"no such option: –build-dir"` -Using PyCharm to add a package to a virtual environment created from the command line using `venv` -can fail with error `"no such option: –build-dir"`, -which appears to be caused by the latest version of `pip` (20.3) -where the flag `-build-dir` was removed but is required by PyCharm to install packages. -A workaround is to: - -- Close PyCharm -- Downgrade the version of `pip` used by `venv`, e.g. in a command line terminal type: - ~~~ - $ python3 -m pip install pip==20.2.4 - ~~~ - {: .language-bash} -- Restart PyCharm - -See [the issue](https://youtrack.jetbrains.com/issue/PY-45712) for more details. -This issue seems to only occur with older versions of PyCharm - recent versions should be fine. - -### Invalid YAML Issue -If YAML is copy+pasted from the course material, -it might not get pasted correctly in PyCharm and some extra indentation may occur. -Annoyingly, PyCharm won't flag this up as invalid YAML -and learners may get all sort of different issues and errors with these files - -e.g. ‘actions must start with run or uses’ with GitHub Actions workflows. - -An example of incorrect extra indentation: +They will now both be available to run as command line applications, +and you will find the details of how to run `pylint` in the lesson material (`black` in not covered). -~~~ -steps: - - name: foo - uses: bar -~~~ +## Running Tests + +VS Code also allows you to run tests from a dedicated test viewer. +Clicking the "laboratory flask" button in the sidebar allows you to set up test exploration: + +![VS Code application window for setting up test framework](../fig/vs-code-test-explorer.png){: .image-with-shadow width="800px" } + +Click `Configure Python Tests`, +select `pytest` as the test framework, +and the `tests` directory as the directory for searching. + +You should now be able to run tests individually +using the test browser (available from the top level menu `View` > `Testing`) and selecting the test of interest. + +![VS Code application window for running tests](../fig/vs-code-run-test.png){: .image-with-shadow width="800px" } + +### Running Code in Debug Mode + +When clicking on a test you will see two icons, +the ordinary Run/Play icon, and a Run/Play icon with a bug. +The latter allows you to run the tests in debug mode +useful for obtaining further information as to why a failure has occurred - this will be covered in the main lesson material. -Instead of -~~~ -steps: - - name: foo - uses: bar -~~~ -{% include links.md %} +[vscode-wsl-extension]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl +[vscode-wsl-extension-launch-options]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl#commands +[wsl]: https://learn.microsoft.com/en-us/windows/wsl/about From 574c54ab2def0ffd477db560c168c29e5ac43754 Mon Sep 17 00:00:00 2001 From: Aleksandra Nenadic Date: Tue, 15 Oct 2024 13:27:43 +0100 Subject: [PATCH 14/16] Update common-issues.md --- _extras/common-issues.md | 379 +++++++++++++++++++++++---------------- 1 file changed, 225 insertions(+), 154 deletions(-) diff --git a/_extras/common-issues.md b/_extras/common-issues.md index 61715e0b4..e90a70b47 100644 --- a/_extras/common-issues.md +++ b/_extras/common-issues.md @@ -1,202 +1,273 @@ --- -title: "Using Microsoft Visual Studio Code" +title: "Common Issues, Fixes & Tips" --- -[Visual Studio Code (VS Code)](https://code.visualstudio.com/), not to be confused with [Visual Studio](https://visualstudio.microsoft.com/), -is an Integrated Development Environment (IDE) by Microsoft. You can use it as your IDE for this course -instead of PyCharm - bellow are some instructions to help you set up. +Here is a list of issues previous participants of the course encountered +and some tips to help you with troubleshooting. -## Installation +## Command Line/Git Bash Issues -You can download VS Code from the [VS Code project website](https://code.visualstudio.com/download). - -### Extensions - -VS Code can be used to develop code in many programming languages, provided the appropriate extensions have been installed. -For this course we will require the extensions for Python. To install extensions click the icon highlighted below -in the VS Code sidebar: - -![VS Code application window with the Extensions button highlighted](../fig/vs-code-extensions.png){: .image-with-shadow width="800px" } - -In the search box, type "python" and select the Intellisense Python extension by Microsoft, -then click the "Install" button to install the extension. -You may be asked to reload the VS Code IDE for the changes to take effect. - -![VS Code application with the list of extensions found by search term "python"](../fig/vs-code-python-extension.png){: .image-with-shadow width="800px" } - -### Using VS Code with Windows Subsystem for Linux - -If you are developing software on Windows, -and particularly software that comes from or targets Unix or Linux systems, -it can be advantageous to use [WSL (Windows Subsystem for Linux)][wsl]. -Although this course does not explicitly support WSL, -we will provide some guidance here on how to best link up WSL with VS Code (if that is your use case). -In your WSL terminal, navigate to the project folder for this course and execute the command: - -```bash -code . -``` - -This should launch VS Code in a way that ensures it performs most operations within WSL. -To do this, the [WSL - Remote extension][vscode-wsl-extension] -for VS Code should automatically be installed. -If this does not happen, please install the extension manually. -You can also launch WSL sessions from VS Code itself using the -[instructions on the extension page.][vscode-wsl-extension-launch-options] - -## Using the VS Code IDE - -Let's open our software project in VS Code and familiarise ourselves with some commonly used features needed for this course. - -### Opening a Software Project - -Select `File` > `Open Folder` from the top-level menu and navigate to the directory where you saved the -[`python-intermediate-inflammation` project](../11-software-project/index.html#downloading-our-software-project), -which we are using in this course. - -### Configuring a Virtual Environment in VS Code - -As in the episode on -[virtual environments for software development]({{ page.root }}{% link _episodes/12-virtual-environments.md %}), -we'd want to create a virtual environment for our project to work in (unless you have already done so earlier in the course). -From the top menu, select `Terminal` > `New Terminal` to open a new terminal (command line) session within the project directory, -and run the following command to create a new environment: +### Python Hangs in Git Bash +Hanging issues with trying to run Python 3 in Git Bash on Windows +(i.e. typing `python` in the shell, which causes it to just hang with no error message or output). +The solution appears to be to use `winpty` - +a Windows software package providing an interface similar to a Unix pty-master +for communicating with Windows command line tools. +Inside the shell type: ~~~ -python3 -m venv venv +$ alias python="winpty python.exe" ~~~ {: .language-bash} -This will create a new folder called `venv` within your project root. -VS Code will notice the new environment and ask if you want to use it as the default Python interpreter for this project - -click "Yes". - -![VS Code popup window asking which Python interpreter to use for the current project](../fig/use_env.png) - ---- +This alias will be valid for the duration of the shell session. +For a more permanent solution, from the shell do: +~~~ +$ echo "alias python='winpty python.exe'" >> ~/.bashrc +$ source ~/.bashrc +~~~ +{: .language-bash} -#### Troubleshooting Setting the Interpreter +(and from there on remember to invoke Python as `python` +or whatever command you aliased it to). +Read more details on the issue at +[Stack Overflow](https://stackoverflow.com/questions/32597209/python-not-working-in-the-command-line-of-git-bash) +or [Superuser](https://superuser.com/questions/1403345/git-bash-not-running-python3-as-expected-hanging-issues). + +### Customising Command Line Prompt +Minor annoyance with the ultra long prompt command line sometimes gives you - +if you do not want a reminder of the current working directory, +you can set it to just `$` by typing the following in your command line: `export PS1="$ "`. +More details on command line prompt customisation can be found in this +[guide](https://www.cyberciti.biz/tips/howto-linux-unix-bash-shell-setup-prompt.html). + +## Git/GitHub Issues + +### Connection Issues When Accessing GitHub Using Git Over VPN or Protected Networks - Proxy Needed +When accessing external services and websites +(such as GitHub using `git` or to +[install Python packages with `pip`](../common-issues/index.html#connection-issues-when-installing-packages-with-pip-over-vpn-or-protected-networks---proxy-needed)), +you may experience connection errors +(e.g. similar to `fatal: unable to access '....': Failed connect to github.com`) +or a connection that hangs. +This may indicate that they need to configure a proxy server user by your organisation +to tunnel SSH traffic through a HTTP proxy. + +To get `git` to work through a proxy server in Windows, +you'll need `connect.exe` program that comes with GitBash +(which you should have installed as part of setup, so no additional installation is needed). +If installed in the default location, +this file should be found at `C:\Program Files\Git\mingw64\bin\connect.exe`. +Next, you'll need to modify your ssh config file (typically in `~/.ssh/config`) +and add the following: -If the prompt did not appear, you can manually set the interpreter. +~~~ +Host github.com + ProxyCommand "C:/Program Files/Git/mingw64/bin/connect.exe" -H : %h %p + TCPKeepAlive yes + IdentitiesOnly yes + User git + Port 22 + Hostname github.com +~~~ -1. Navigate to the location of the `python` binary within the virtual environment -using the file browser sidebar (see below). The binary will be located in `/bin/python` within the project directory. -2. Right-click on the binary and select `Copy Path`. -3. Use the keyboard shortcut `CTRL-SHIFT-P` to bring up the command palette, then search for `Python: Select Interpreter`. -4. Click `Enter interpreter path...`, paste the path you copied followed by Enter. +Mac and Linux users can use the [Corkscrew tool](https://github.com/bryanpkc/corkscrew) +for tunneling SSH through HTTP proxies, +which would have to be installed separately. +Next, you'll need to modify your SSH config file (typically in `~/.ssh/config`) +and add the following: ---- +~~~ +Host github.com + ProxyCommand corkscrew %h %p + TCPKeepAlive yes + IdentitiesOnly yes + User git + Port 22 + Hostname github.com +~~~ -You can verify the setup has worked correctly by selecting an existing Python script in the project folder (or creating a blank -new one, if you do not have it, by right-clicking on the file explorer sidebar, selecting `New File` and creating a new file -with the extension `.py`). +### Creating a GitHub Key Without 'Workflow' Authorisation Scope +If a learner creates a GitHub authentication token +but forgets to check 'workflow' scope +(to allow the token to be used to update GitHub Action workflows) +they will get the following error when trying to push a new workflow +(when adding the `pytest` action in Section 2) to GitHub: -If everything is setup correctly, when you select a Python file in the file explorer you should see -the interpreter and virtual environment stated in the information bar at the bottom of VS Code, e.g., -something similar to the following: +~~~ +! [remote rejected] test-suite -> test-suite (refusing to allow an OAuth App to create or update workflow `.github/workflows/main.yml` without `workflow` scope` +~~~ +{: .error} -![VS Code bottom bar indicator of the virtual environment](../fig/vs-code-virtual-env-indicator.png) +The solution is to generate a new token with the correct scope/usage permissions +and clear the local credential cache (if that's where the token has been saved). +In same cases, simply clearing credential cache was not enough and updating to Git 2.29 was needed. -Any terminal you now open will start with the activated virtual environment. +### `Please tell me who you are` Git Error +If you experience the following error the first time you do a Git commit, +you may not have configured your identity with Git on your machine: -### Adding Dependencies +~~~ +fatal: unable to auto-detect email address +*** Please tell me who you are +~~~ +{: .error} -For this course you will need to install `pytest`, `numpy` and `matplotlib`. Start a new terminal and run the -following: +This can be configured from the command line as follows: ~~~ -python3 -m pip install numpy matplotlib pytest +$ git config --global user.name "Your Name" +$ git config --global user.email "name@example.com" ~~~ {: .language-bash} ---- - -#### Troubleshooting Dependencies +The option `--global` tells Git to use these settings "globally" +(i.e. for every project that uses Git for version control on your machine). +If you use different identifies for different projects, +then you should not use the `--global` option. +Make sure to use the same email address you used to open an account on GitHub +that you are using for this course. -If you are having issues with `pip`, it may be that `pip` version you have is too old. -Pip will usually inform you via a warning if a newer version is available. -You can upgrade pip by running the following from the terminal: +At this point it may also be a good time to configure your favourite text editor with Git, +if you have not already done so. +For example, to use the editor `nano` with Git: ~~~ -python3 -m pip install --upgrade pip +$ git config --global core.editor "nano -w" ~~~ {: .language-bash} -You can now try to install the packages again. - ---- - -## Running Python Scripts in VS Code -To run a Python script in VS Code, open the script by clicking on it, -and then either click the Play icon in the top right corner, -or use the keyboard shortcut `CTRL-ALT-N`. +## SSH key authentication issues with Git Bash -![VS Code application window with highlighted Run button](../fig/vs-code-run-script.png){: .image-with-shadow width="800px" } +Git Bash uses its own SSH library by default, which may result in errors such as the one below +even after adding your SSH key correctly: -## Adding a Linter in VS Code - -In [the episode on coding style]({{ page.root }}{% link _episodes/15-coding-conventions.md %}) -and [the subsequent episode on linters]({{ page.root }}{% link _episodes/16-verifying-code-style-linters.md %}), -you are asked to use an automatic feature in PyCharm -that picks up linting issues with the source code. -Because it is language agnostic, VS Code does not have a linter for Python built into it. -Instead, you will need to install an extension to get linting hints. -Get to the "Extensions" side pane by one of these actions: - -1. Bring up the command palette with `CTRL-SHFT-P`, search for `View: Show Extensions` -2. Use the direct keyboard shortcut `CTRL-SHFT-X` -3. Click on the ["Extensions" icon](./index.html#extensions) on the left side panel we used previously. - -In the Extensions panel, type "pylint" into the search bar. Select Pylint from the result panel -that comes up and then the `Install` button: - -![VS Code Extensions Panel showing searching for pylint extension](../fig/vs-code-install-linter-extension.png){: .image-with-shadow width="800px" } - -Once installed, Pylint warnings about your code should automatically populate the "Problems" panel -at the bottom of VS Code window, as shown below. You can also bring up the "Problems" panel using the keyboard shortcut `CTRL-SHFT-M`. - -![VS Code Problems Panel](../fig/vs-code-linter-problems-pane-annotated.png){: .image-with-shadow width="800px" } +~~~ +$ git clone git@github.com:https://github.com/ukaea-rse-training/python-intermediate-inflammation +Cloning into 'python-intermediate-inflammation'... +git@github.com: Permission denied (publickey). +fatal: Could not read from remote repository. -There are other Python linters available, such as [Flake8](https://flake8.pycqa.org/en/latest/), -and Python code formatters, such as [Black](https://pypi.org/project/black/). -All are available as extensions that can be installed in a similar manner from the "Extensions" panel. +Please make sure you have the correct access rights +and the repository exists. +~~~ -We also recommend that you install these linters and formatters in your virtual environment, -since then you will be able to run them from the terminal as well. -For example, if you want `pylint` and `black` packages, execute the following from the terminal: +The solution is to change the SSH library used by Git: -~~~bash -$ python3 -m pip install pylint black +~~~ +$ git config --global core.sshCommand C:/windows/System32/OpenSSH/ssh.exe ~~~ -They will now both be available to run as command line applications, -and you will find the details of how to run `pylint` in the lesson material (`black` in not covered). - -## Running Tests +## Python, `pip`, `venv` & Installing Packages Issues -VS Code also allows you to run tests from a dedicated test viewer. -Clicking the "laboratory flask" button in the sidebar allows you to set up test exploration: +### Issues With Numpy (and Potentially Other Packages) on New M1 Macs +When using `numpy` package installed via `pip` on a command line on a new Apple M1 Mac, +you get a failed installation with the error: -![VS Code application window for setting up test framework](../fig/vs-code-test-explorer.png){: .image-with-shadow width="800px" } +~~~ +... +mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64e'). +... +~~~ +{: .error} + +Numpy is a package heavily optimised for performance, +and many parts of it are written in C and compiled for specific architectures, +such as Intel (x86_64, x86_32, etc.) +or Apple's M1 (arm64e). +In this instance, `pip` is obtaining a version of `numpy` with the incorrect compiled binaries, +instead of the ones needed for Apple's M1 Mac. +One way that was found to work was to install numpy via PyCharm into your environment instead, +which seems able to determine the correct packages to download and install. + +### Python 3 Installed but not Found When Using `python3` Command +Python 3 installed on some Windows machines +may not be accessible using the `python3` command from the command line, +but works fine when invoked via the command `python`. + +### Connection Issues When Installing Packages With `pip` Over VPN or Protected Networks - Proxy Needed +If you encounter issues when trying to +install packages with `pip` over your organisational network - +it may be because your may need to +[use a proxy](https://stackoverflow.com/questions/30992717/proxy-awareness-with-pip) +provided by your organisation. +In order to get `pip` to use the proxy, +you need to add an additional parameter when installing packages with `pip`: -Click `Configure Python Tests`, -select `pytest` as the test framework, -and the `tests` directory as the directory for searching. +~~~ +$ python3 -m pip install --proxy ` +~~~ +{: .language-bash} -You should now be able to run tests individually -using the test browser (available from the top level menu `View` > `Testing`) and selecting the test of interest. +To keep these settings permanently, +you may want to add the following to your `.zshrc`/`.bashrc` file +to avoid having to specify the proxy for each session, +and restart your command line terminal: -![VS Code application window for running tests](../fig/vs-code-run-test.png){: .image-with-shadow width="800px" } +~~~ +# call set_proxies to set proxies and unset_proxies to remove them +set_proxies() { +export {http,https,ftp}_proxy='' +export {HTTP,HTTPS,FTP}_PROXY='' +export NO_PROXY=localhost,127.0.0.1,10.96.0.0/12,192.168.99.0/24,192.168.39.0/24,192.168.64.2,., +} + +unset_proxies() { +export {http,https,ftp}_proxy= +export {HTTP,HTTPS,FTP}_PROXY= +export NO_PROXY= +} +~~~ -### Running Code in Debug Mode +## PyCharm Issues + +### Using GitBash from PyCharm +To embed Git Bash in PyCharm as external tool and work with it in PyCharm window, +from Settings +select "Tools->Terminal->Shell path" +and enter `“C:\Program Files\Git\bin\sh.exe” --login`. +See [more details](https://stackoverflow.com/questions/20573213/embed-git-bash-in-pycharm-as-external-tool-and-work-with-it-in-pycharm-window-w) +on Stack Overflow. + +### Virtual Environments Issue `"no such option: –build-dir"` +Using PyCharm to add a package to a virtual environment created from the command line using `venv` +can fail with error `"no such option: –build-dir"`, +which appears to be caused by the latest version of `pip` (20.3) +where the flag `-build-dir` was removed but is required by PyCharm to install packages. +A workaround is to: + +- Close PyCharm +- Downgrade the version of `pip` used by `venv`, e.g. in a command line terminal type: + ~~~ + $ python3 -m pip install pip==20.2.4 + ~~~ + {: .language-bash} +- Restart PyCharm + +See [the issue](https://youtrack.jetbrains.com/issue/PY-45712) for more details. +This issue seems to only occur with older versions of PyCharm - recent versions should be fine. + +### Invalid YAML Issue +If YAML is copy+pasted from the course material, +it might not get pasted correctly in PyCharm and some extra indentation may occur. +Annoyingly, PyCharm won't flag this up as invalid YAML +and learners may get all sort of different issues and errors with these files - +e.g. ‘actions must start with run or uses’ with GitHub Actions workflows. + +An example of incorrect extra indentation: -When clicking on a test you will see two icons, -the ordinary Run/Play icon, and a Run/Play icon with a bug. -The latter allows you to run the tests in debug mode -useful for obtaining further information as to why a failure has occurred - this will be covered in the main lesson material. +~~~ +steps: + - name: foo + uses: bar +~~~ +Instead of +~~~ +steps: + - name: foo + uses: bar +~~~ -[vscode-wsl-extension]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl -[vscode-wsl-extension-launch-options]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl#commands -[wsl]: https://learn.microsoft.com/en-us/windows/wsl/about +{% include links.md %} From def4bcc18984325cfce392755ccbaa3b36149e37 Mon Sep 17 00:00:00 2001 From: Aleksandra Nenadic Date: Tue, 15 Oct 2024 13:28:06 +0100 Subject: [PATCH 15/16] Adds small fixes --- _extras/vscode.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/_extras/vscode.md b/_extras/vscode.md index 37d93846b..61715e0b4 100644 --- a/_extras/vscode.md +++ b/_extras/vscode.md @@ -29,9 +29,9 @@ You may be asked to reload the VS Code IDE for the changes to take effect. If you are developing software on Windows, and particularly software that comes from or targets Unix or Linux systems, it can be advantageous to use [WSL (Windows Subsystem for Linux)][wsl]. -Although this course does not explicitly support WSL setup, -we will provide some guidance on how to best link up WSL with VS Code. -In your WSL terminal, navigate to the project folder for this course and execute the command +Although this course does not explicitly support WSL, +we will provide some guidance here on how to best link up WSL with VS Code (if that is your use case). +In your WSL terminal, navigate to the project folder for this course and execute the command: ```bash code . @@ -40,7 +40,7 @@ code . This should launch VS Code in a way that ensures it performs most operations within WSL. To do this, the [WSL - Remote extension][vscode-wsl-extension] for VS Code should automatically be installed. -If this doesn't happen, please install the extension manually. +If this does not happen, please install the extension manually. You can also launch WSL sessions from VS Code itself using the [instructions on the extension page.][vscode-wsl-extension-launch-options] @@ -105,7 +105,7 @@ For this course you will need to install `pytest`, `numpy` and `matplotlib`. Sta following: ~~~ -pip install numpy matplotlib pytest +python3 -m pip install numpy matplotlib pytest ~~~ {: .language-bash} @@ -118,7 +118,7 @@ Pip will usually inform you via a warning if a newer version is available. You can upgrade pip by running the following from the terminal: ~~~ -pip install --upgrade pip +python3 -m pip install --upgrade pip ~~~ {: .language-bash} From f88f3d3807a4df453effdc87c58f70b88fa70254 Mon Sep 17 00:00:00 2001 From: Aleksandra Nenadic Date: Tue, 15 Oct 2024 13:28:37 +0100 Subject: [PATCH 16/16] Adds small fixes --- _extras/guide.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/_extras/guide.md b/_extras/guide.md index 97bf7b9b8..76935c703 100644 --- a/_extras/guide.md +++ b/_extras/guide.md @@ -88,8 +88,6 @@ to make sure everyone is in sync as much as possible and are working on similar This synchronisation becomes particularly important for later workshop stages when students start with group exercises. -### Section 1.3 Note - Although not explicitly endorsed, it is quite possible for learners to do the course using VS Code instead of PyCharm. There is a section for setting up VS Code in the [this adjacent extras page](../vscode).