From 2f04160e7f8ab595e7a466bd20f1f002c084db65 Mon Sep 17 00:00:00 2001 From: zilto Date: Fri, 8 Mar 2024 09:23:08 -0500 Subject: [PATCH 1/2] pre-commit docs added; fixed CLI docs indexing --- docs/how-tos/_pre-commit/validate_changes.png | Bin 0 -> 39805 bytes docs/how-tos/cli-reference.md | 2 - docs/how-tos/index.rst | 1 + docs/how-tos/pre-commit-hooks.md | 136 ++++++++++++++++++ 4 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 docs/how-tos/_pre-commit/validate_changes.png create mode 100644 docs/how-tos/pre-commit-hooks.md diff --git a/docs/how-tos/_pre-commit/validate_changes.png b/docs/how-tos/_pre-commit/validate_changes.png new file mode 100644 index 0000000000000000000000000000000000000000..f08f9c71e08a64d6c6fedac1577e4deb46b7324d GIT binary patch literal 39805 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

P&FEP4qExq=yfyXUYl{BiDsb9cbY*vr5?`j>1v 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⋙Y(FeVOM_HjtfsDv;SkB+A`h1NR0`E}%h6N}U0g4HJq^a`IA-xRa3-q(zK z?_c{j`9!1Gv|UKA@I4hPx#tCF#XjvY)US_({yY`CrTj)fTDd?>+fX?ellyGcU2!M# z121i8cjx21i*uDbGrW7wFNFmx+SZ*)e0fM@MV$%POta&;_wlta?A{)Qdc22)qJU3M zV{9nH{bJ*0Z&me*oh{8e4;R}h6RKzVfMuh+#nYT6%D0#=2#@+P(u@5FWAF|#VEnwokKU_c8Xx>*Wo zcMA#%UTW79b8>P*?CS|6;(S+SLkXd{Wo+y;dk377FJJ-|h>J$dEy+~pxacC@WAJ2-08*7<9d3i4tm6XUc@q4j*M_XA`l7wg4ET-K8!Z%!Xbx1|1_v7|&3r1q`=e&0Ql)qbh6@7pc z(1<5>YH@h`EivEk3|7ScS#)hP+2NS(aCHm;f$KpGiQlOH+1x_!8TYDOxtEsF@ITf= zV!kjy-o|S!6jIy(E*3Fzb`I@cir?UTC8bd#FZ3fKroA`3Rk12o>C0ozs6V|of4MZ7 zod+lVnV4hZNaX9?C9s8yHQI@M5M0or^}$8^4uRn|Geklg*6V&W!;5k&gV~Y5H`;95 z_flsot%nO}hx}ob0x1_4NPbZuSvMbTj22Z?v`tRNfw#0D9uL0Rd`4!b*t>U6fFpL) zzrc22{|+VBkYnbDh6InM9v#j8$4Un_C{V}i8R@oB&*k$czGebSo4zwnww~m2=i#K}B{wR+^Y#{uX6!cT57RnA$3J*lN_A)TjV` zmocKMsi|$w)YZH~q-0ViqJY(r8U`OM^kS9~aV+m7Ozyi6k^KR9T;lu@z-^Avn_g+k zbSj>d@c8*OD)d(-X2pH|e`kbEr#)JJX6|$BD7c*HDV7tta-#o``z->boX45`@($d?QpjVgb7H}3MQt^+nMQiM0G6(+oSY7 zu74M~)kxGnlL9D~v9>M@t4{Btg5PB}8TK-^%?A>hiR;3}nQn0@eXw2LzI_`sK_WIb zEKmv@ZcXA_TU$>}X?5@Zwq_ydkb1X&j>*rREM(-&qspk&SYoF!%;GKd`s8)fJ3hmk z_o)Ktm^>bRHjX>SaOC>+H8B5aP)}JE&~CqQ0O*ft(1{L`xOI0UXKCbqWKI5a|J2bl1c@jKIytKR=79K7P-38)0ZdCNVtG~f? zZZBtbI!hjh7dDdj$Z^7JuUI*pvbQ37x-{Iys4mEWq zw#Q3G0gJ~c1`qnvcbNcJzCXRUyT9R?QiQH?KG@`)t=M z=`RymzLbzUrM0n}%)|zZ1?g_d+3w5Qls!vWglIOu-g0nRWrI+}gWnHX`Kgbeyf;mn>eoLnr*K1f+(rP-@SdoYOt z79`&hF?g0M^pY623Q^%>3(rt&5>n;({wp5`jO`1um>9n$By(S(PYOH!A;wQ z+2E?z2X7JM!Q2R*yUDtpC-?GQNZEEK+OlQ&hwUt{8}C-h5fF_0$=H?IKoIKol)V9pf=2(J+>-^uR$8+9R za5G$SDVP-T^X^W$BBe;?^-OOaZ-q*C;m|OMBhIc)W???&T(IEvb82c-D3LN`6C}ZI z>z@!`^7CA8U%Eb;uVnX5ejF+<1Mc)m8ZgPGSGuDJSL@t+5z)UsIQ{F5XY*vF>ZbW6 zog%;6zgu`_aym*$We@eLKFsD|ymW>xh%g3YX@l1u`>tr~?U!c+5DC6H?9AN(_Rr@Gd zZuT_|?sxo||JGP>ohk*(UNV$5df2Iv!NrrC7S|J14|cTNhO9i0h94cB=e+itbAV1l zgE;`E5@_>;kSsbYYq%#vv#p|uX}?ReiMqpjN5O+!ZkJ0MPQA2tahaB%dEAdVI4app zvWmLzdec16qIiUDt^M}x;|GLna{z9^%015ZycH82oeX1P&v|KS(Ld{ZZ2vB?t690; z+{}Ba$8hDJpwE8z%tiaN=#Cw70q0%OTC)tcnTI51lCUlgV-du~>aD!1>*IKTx`Lar zyp$zhMTcTY{}R~~E}$M4Q|mn4rdQWD(_)Igu=yV?K#)s?^+)n$!fz*=KlLJV9MAA- zeY&lem)B>0ja|_zjRbTI$D{GO{}}v+NL53%=KG#iR*mcaSd*WhgF)++p&0e1Igc$b z?H8DkSLC!t)}f}hFnHo0HSwSON!vNXH|We!9^s#F=r{?D@pzFwDu4^b{LZ+ntgL{1 zG=U_2b*hRL#2Plq-i~+ZRP5#s`e((TLk}YZ_jrDNy#pi`zW)9$+oLxbe_-5zn(&pq zz5N#h>LiVl$EyTSu_rwb+=VvlU-fWb7wrq z8Q9+F{`*=8mdO|Bq4JckZ``=zzu5q<0@=Ym@ESPN(anO);_gWNoyOXi^p0cdZO4MVHNn6)Of z(NoW6GE)5a1gPv6-}8mF@`sJnsBxnsskGf?!u9D@d5+e4|48UpVL20$>*K6*S_aLA zFcuZ%Uk<>>QlO0ly#cIgbPNm%28M^gih+c%89LCt!^6v7Zi68;7_TkiAIYutJ=`84 zU=*bCA5c<~n1qA`Fg2~oiwg76AdpywgoW)VZJ3#w_Cpm5tGW%E5AAi^$@9bHe7Rt~ zBof3yac2Fs+Bx%R&S>)3V@VKi_`CJgy)mECD0N?UTZ0JV)+Xz`&I4t-g?1IO>WNS7 zXu3=2#v&%HX7vXaTTf%ICtHorFIT2vg-I3xQwx(O#0$0RcK7$4|8ajE3azW-*Qh{k zf6X8|717D{X`SW^AgBuSe3o9-s;}kbf?;&%-o;J>I8an9$bKr*f#nw$By)`YoT0Nm zUX@SW&y6qu%_KN#>TM~A(jHV!cW!@D_YON1J4>y^2q~#2Kk4r74s1T%Kn4)QvwJqb zNNf*8`6M{!vsDWdZ~v#SS%vX67BGSz>U-=*j}$K76b3zXc6NrrZ9bTSm-07Hh(Wc$ zTF{#|@f^ZS*0%ZzEqcFN*ULN_eIqThW4VEOZ1M1K>{c*uTC}y*i+Jn>>N$Gb3F1p= z5i_WC!gZiDrx{L7ON-&OekK#gkq#dVFgX-xE9`e>0;`<<`G0`MJU%`i?f(66gVj6D zZSQX-TX5e;@&`%$hB_&iH}E7Sx3I(?gvztty?gi5+Eh$k+{j+TvpUM>!Q(ZiPX#oq zai7pBqmee};t>;Lyh;-dqf8289uJz$M9QT}_p%Ks*c%mtyk5nrDw?HGT+uVCbtXKbTin zWV0j*1El$pI7PD`&c_cQbl?mD$V#4|>7U=)(o8-IC@f?IXci!yl(e)a5VfmSSZRi~ zV}17azJmfBZsce4N*9N1`GWWVyu?B6FaZO$+Io9~mr63`j;%U{!s8=2)M8>TkDESS zIv@R+p4PnQ3kxdHex>Ir07PhLXgc6*%4uJ^y1Lo}Ne)L*`zVV=-MF0VoZh z-4!TsZ+?JQ0FqPGt>ly+l9c^X*Xhfj>%9}h3CU6->o2x>x2LOHT3ZgJ++K3LjBQK! zDN-NDt#r=^`PV+3Y-_4m%E276wb=$i6fjznGc$)mMFxt%tE6_U#coqBkbr>4;F^`- zYb{}_*>G+yl^kB|+CUZ4vne^+V(vT&5g~fyWNR3eU9=ZFH%6R*|wTP~P46WzY@^OKjy{NK*~<27aV|(BBps@=h!Q|Nv8^B#-FqWiE!?e^1B#MUIBe-+lJ9rok~A!K1#20 zLE^;mqe|cBpaox>Nv9Eo7Rho*vcU?134ejv*|f+w3pcN{l#P#{KL+FhFkYp(qhmea zViG2tcR-28b#!urkjWAk^I2TI_*vhv|EnkK5(zNmhE6oDWs( z^R+2aB|Rxx+8(VU?gOIJJTX!I*N)-}&kTw-OgKCO)KI@+SU(!vX;6C^5Svw*47rOY zU>dg{`gtYY(?t>q1gxSD>#>Atbs_FVKCcx;g7yXV{MKP0(joW3Tv>4{amrL+1yG~? z#*2SY8pm+jQ>f6XI;{^;Dhg9b1~BWlf;{i5U(n+wx9c|0eqcK>kBmnBuuK%eU3U}ej` zPqrcyWJAtz*BBq)U8CqG3~j@`=S^$^H*%hyiHQmJ`YYh=qM1t>XJ1}FAP;B3j>a~* zN_EE}cHa0t4F0MZ;9j5w2L=Bdad9NQaT^sCr*s}4TDx{}B>6we$&|4kKG93B;J~^(07%Ul~~v|#L&*x?;jbB9dfe} zJD`VP;WvH#sQn1`0yG!`N-Vbrz9A`9toeMHVaE-s&q#W0S4lrG!KQ*phRrl=V#oT` zD-_Vg(J{BR_4EWaeZw3q*m99xpN*rvCHU{W?1kcoVR@*5a#wnd`Yz}tYHa=Cb_?3F zQ2a+=MB1zYL}*Kj@Hdpxjlu@s@86xxCv8r5b#d?AfoY8qw+?QDVre8_vlG_1W5_2@ z0BEDR^8_LDycTtiy}e4WNEZPQwZ-H3lYseF`%dmvjDRJfaj(ynaZ(DkM0gc84Gj$% zh$w)C`(SVX_DTZ`;iaV%ZFr0fe0=fnG3U3pgKBDcLEIdk47tVpmtM;~2#21in9H!U z=PPF2_2JB&-YWqpjr&&5&W2zvJUy4~G6omvuaO4EU#=rF{svZh7}OeetkQpiI< zMrOoa^*DRbm%>0+Kaf7blGcOv2C0CN2&ZZnPcxH7PG`?j5)!(JiusHJBqHFTP*hZ; zcxCPT!{$3OLGz1ua-WTpSV19(8ndOwP=t{*5DK;srw*QJk(cwW7d4V7oQJ4i)P6FJEo| zH;Ysu5OaO_{5C(aaN!#GKuC_pKnv%%i-1Qp0~9?lL=zw46Ue5TV3Z?lN|;pWUuC|@1g*zid+#entp#&+oSyp z)_jWM^goLI_cYKJY10wJa&EtS(LC*jL^zfzJUW_YmhgeGwKXDX z1K8_aIPs`U8k86hUya*5W{kj~oULx(YQBwv6ie+}n55W2pLDCSC^C1VdGmkh#kY1L zU#UcBk;j<;`}7s=G8{7UY6ce@7M#UQ?O6mzoqCNuz1#UADGN*3(2(*&5&>!0F=tR- zqd(~8ZGgY0#a`J2RxWwEh@5X&>2Iahy_t?$OV=JxR+K<}*I2WayU73aMC1BnB4AezPY-~=> z(~cE5$ODSb@yd_nNp8pEFV6Bn!Hv}E4bQ^hhJO%VjdtgLd-?bXoQS_?eBm)>siC1k zizb9kCR3eN``9V8I#GsA&!e=g4C>ZKI6VbspyN-B!iZa7vHm~-3(tmo7viw6Zi#VoZeybp7VYlQQN-8h6r_r%H5_@%fOVZ3g$e@P zc}oW{?01J-T&;*w+|GM=YWyA?k1}O-bWFrMgEUVa7oU_qdnokl!#-l~qB;TvGyqtI zu}%)WhZq=R`@NLiFKBppatKpWe`aLlgr#4Ky?;-PxIDn*w3?cOS_}E=$msB}3Z-N) zLIcjO0nU;}wIF_7*UC!EjCU(DJ^drZ+MB`9R)FX20o-nJxR0cyua6LLC*ii-c42E`TA-Eh8abB0+3EmT4fR;A-O0$~R?>|X`)B+U}{-09`m8Y?v* zMHc%&$*AZ(b6X=TKMn%i0@u;9W7>d&QS)z+n~QkuGylZ;8+L>o%L|NckF%9)E~7xRiWjr$R{4f+d=_oHhK*sZ$n zY3Fr4WA_3f_wHbM9SHvQJ?5yE0qFi4BdFLu6RL7hzXH*XnN}lGG=Fk$FG`=8iOqR! z3s0!l9dUiZ2#9qzFDDnz!~x}BglSJw%wPPwGiusXB)9JT*L^=)*3S??{il>MA4Cc52}Sif+|RkEvg2#Co~VX zE6Zv1uE1dt|6jV0-Yb4ocw(JnGxgr}2gX5{Eu)Bdy_rZ&VU7qrt(qN3>5_{&0A|N9CW>BoT>i;GJ35{E3;vA=KOX$JFHf2`Ng( zbwkJZNM`~rCsK^ntf9EKwy4=(Vd}CbWCYD!x(HO{9cq)fmG9acc99%@O3zWBT;Jso+b7N99q0XW-1T2g9gf!Yj8X^G60k$IR?IbXg!(c3yWjH8stl z_y^G^r)|Mw7Sz;`t!&nZXovS9$v<{xgnTJ2BSWi{fq?|vpFEL=Be@O_4{6{9QBl!V zLE_tUdkDcqSID+mqN_J5T}}#e9#g$|;Sa~Bg@=U+fe0H$Gfg#kB!NsD246^-nVEeV zU1o)1VQQ+juj%*il)mCi3PyzPu1NRa+(?g6MJXx&si~<^b#mpoO2JAw*FzPvq3mx$ zQui}?c64-=66fk_Rs}vh*>!v{kmf~0c@{)Ru)lxdb>iYK?NmbtxJ+*tK{y)suP7iU zS9$FRY(08eSy@h(Sp~5R+x7vh9~{X<#iLVQdfmYfomb05((*5-@l>I-=@t=9GH5`| z5@qghW!hfd1-i`kx~u(WX9)}InbW{Kkk}z*48aLeJ^#Z<3N4)QB&X$e_Se+_HG}I? zVWBg}IoLNm=<*MC72g0fZKM13AX3PCA2V7Eg<$F5DkVTeKib(DLb{ZWVOa{4DNhu< zeFAkk(uM{9C}P1wj%5Jv%IaVL%DRiJ9bq87n4`%0f%62t-^T)kN#hq#*bpPs29+I* z4ib1D1tiuBu&<9qg&p1_AS48%Bqg0n&f)t)NUO9gp^_33{{WBsnVue+m?#Sj8=xY{ zX99zySxBCZ0!E3q&Iym}Yk9f-7r?BgrTd15yDx68t;%Oe6TtMK&rlOW&HRLjNFRatUJA}YYS?#d;ZuE_rGt56CoOkVqft3ZI) zEa=98#@;S6GKq_1nD6PPaDMnq0#r0AoiCIN{v=yxJhIVY1pSQms!Kg+Wtz3Bx`635 z8C+9{bKrM-0YBY-Z5dbjY1<;jtzE7Sqf+!3j!jwzPpThO%m5c;t21=3!}B7137;9bKV^?iJ<4?< zEG|y_aZR5;-WuZpDJ{b2(I_H{MHv9 z;CakEk&uIsXD0twRp|n)d;k~_8&e|h7pXFon6~%U&Zaqh`AJQ+jtzio>Dp9&qs{Pj zO!VSbQc=5Y~rrGIR09Dx8IiK&`?3%A;Tunw6ED7~}-@lq2v2Xm7gUp2TNnW||3*f$o;> z&i>AffR-?>6Nj~0{`rG@`AM@Q4a>YoBsG0Jp$@v_Zr{8dDH)Okzm=CiRLL22{PcC8 z|99FOqKDgP6qg2QAQkywKJos@)%~Eni|lUFUvWA8;ow=w8bT6?XpM9+i|FH>UxgmV+uM z@@Gl)p7J!fz^n_Q?PZ*vp6VDIQ-8rS6U$}W{ zjN5q&_KIY%DW+tlpU*3!HOMCY4Pw9co}q4t(O{37kIRUlH5Ub8v)5C>j{Tj?fjrL& zvs1)PbcIv3Pe+Gim5w(MK3Bq!m(f2m=bh^EY~WvubH6ZmJ$K(zoCJi%TJpL%Pbe!+ zQC0XyXrid}5G9{R8GUJ<)`C&a-r0enk--P-W!WTQQ7#qW_@;AWUf! zEhnV1vFEup|6fk&^54|cp5VL6aBDx2um!XzQT@{SL-SbriOO6V{2J?AoGIfWKbT2> zJ2c8)8q2&mtUqh*xk&FTHckjBD;FcT&r|#2XR3A{j41CrIAfQ|7dRvZRS~&c-90{Q z3$qTdH%^5fF_0gS>TWtiq{lBVT=;0h?mm64O5q3ASgC)y!1&@3N)&*=q(F5Zx7E7|MbS9U4TFzWT%|?Wsd*Ao>`@Qe;d@s*q`3w1dBYIA?gwfi3DQUfl z_|2E{dN0|VBLkE!KYEN%t#(0F3z+n?th{9clewmAuJ!o$!?o1{Lg~t&66eggj0irp zJ96iQPpX=au<52}1bjBV%fcQOKe-b=aa4J(j5R_IIvaAXg0tFM>?T?d&)XoT6(bhfL~R-H5E_K74DN zm`P+=uJY_Fcv1F{D3vs|uIfsYTlOS^i2$M`7YoJWF{mc#-LVD?)v6p)6!^aY1vG2v1Do^GK}_BUZpBzf$e5vJ zc911#8fa1%J0R8*kO>9nom#Vh|8e6`2i-3{RTaT0W>>K;3K|(!(!1{fGY0pB^BZ4v zJRmm^|JNpHeWSDtYZw9K1*eh+KYl6*2{e$Zoitb~Z)Jr7*iZ=7WBj4rDhe^`h|7e+ z1Z;SE8tFstz>e;|xYJzUY6QcAaWGlY(J@1sDwZQZp`K6As{E#7-%Xy9Y-hG(HhOxJ zSB?Os%I=x_`l#k@3gA&ReRjKdYvFp8$5M@1XRe%uMW>KoeXfyM?6+0Pe%Uu?Pzku` zuF?A&Mmmda;~SK~fE8_*tOsZfP&e&^jajmJM{L?eK~eDI(RW3WMMqx1pr{HU+u9B- zQ5Pdt62I2V8Wv@k6Y&t>^(vTR$^%KsFQbX4|2zmMO^5Ik)UcPBV%=_Og-8*-f(3HL z(uGnsuoH(F41&yRRiqpo2U_5MKUy*XjgXe=WEz}%&f)A0zYMy<+0p31ndzZ(P)-4{ z#Xve79a*S89G1GVs!`uZWj>-XNf|oqCL#4{)3}@=u^OE074Ih^?ozTESEaj2i-fY z7US8c4}mDtP+L}Ce>?XYZzsI+=RE?@Txb9#OQ2D9IgJ%w<~NLcJ_Bz}uw+#% zdHhX(^L)Q0T)`&FkQM-E;MAHA9YE*w>0GZ8>C%6XG@AV)b2Lt^TJ$1yHKuqyU@c&z zq6kA~w`F9+5nQtHJ>l-QMn3CM)+m4EQanQHMrVfAOq4l2M4P8u5J)B4F#NSIuP#Rxv1Vd>E85f- z>I8AtkyfgZR{g`5RDle%fjg*#GGFr;H2YZMQ4Y`E;RB$8uS;%im0zjewV+;sIL~Zs zay=`JABKfGcmP@7vqmo)C4e;<^jI$s{S&T-bH-L3Z z5kH;lv<0Z?72wk93ZcmLGDm-#X4w3kpPG`IY+>i&A$VZ^f0WA8__a})neW5hofI7Q zLaohY=+yb=@Ev+tFo~~vYRI7{UE*~6@FjSO6<-e;NTb=a{C5k}Y#nFpH>=ha3Vd+{0_MFt(BT%e9eHp2d#CNfE6{!>Lc%M6 z1q;>8!^(@m&TE}E(VHk)vc!6)0Ec`ayFd~+BhXSH&!)v@E-_qSVDf7wMDUPN zIg?&4ZFApR{mu#RgV@?{;9wX*8=&Y-Q-EEBc=>K+MmB*hMZQ2D=7=*J*<=)dl{Z`F V9IIXFF@w}#rBQ7uh2(>0{{n8?v7G<_ literal 0 HcmV?d00001 diff --git a/docs/how-tos/cli-reference.md b/docs/how-tos/cli-reference.md index 73698e290..b140a09a2 100644 --- a/docs/how-tos/cli-reference.md +++ b/docs/how-tos/cli-reference.md @@ -15,8 +15,6 @@ You can verify the installation with hamilton --help ``` -# Reference - ## `hamilton` (global) **Options**: diff --git a/docs/how-tos/index.rst b/docs/how-tos/index.rst index e48b4c2e3..a528f3a5d 100644 --- a/docs/how-tos/index.rst +++ b/docs/how-tos/index.rst @@ -20,5 +20,6 @@ directory. If there's an example you want but don't see, reach out or open an is use-hamilton-for-lineage use-hamilton-for-llm-workflows cli-reference + pre-commit-hooks cache-nodes custom-driver diff --git a/docs/how-tos/pre-commit-hooks.md b/docs/how-tos/pre-commit-hooks.md new file mode 100644 index 000000000..9ae1764b0 --- /dev/null +++ b/docs/how-tos/pre-commit-hooks.md @@ -0,0 +1,136 @@ +# Hamilton pre-commit +## Use pre-commit hooks for safer Hamilton code changes + +This page gives an introduction to pre-commit hooks and how to use custom hooks to validate your Hamilton code. + +## What are pre-commit hooks? +A pre-commit hook is a script or command that's executed automatically before making a commit. The goal of these hooks is to standardize code formatting and catch erroneous code before being committed. For example, popular hooks include ensuring files have no syntax errors, sorting imports, and normalizing line breaks. + +Note that it's different from testing, which focuses on the behavior of the code. You can think of pre-commit hooks as checks and formatting you would do everytime you save a file. + +## Add pre-commit hooks to your project +Hooks are a mechanism of the `git` version control system. You can find your project's hooks under the `.git/hooks` directory (it might be hidden by default). There should be many files with the `.sample` extension that serve as example scripts. + +The preferred way of working with pre-commit hooks is through the [pre-commit](https://pre-commit.com/) Python library. This library allows you to import and configure hooks for your repository with a `.pre-commit-config.yaml` file. + +### Steps to get started +1. install the pre-commit library + ```python + pip install pre-commit + ``` + +2. add a `.pre-commit-config.yaml` to your repository + ```yaml + # .pre-commit-config.yaml + repos: + # repository with hook definitions + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 # release version of the repo + hooks: # list of hooks from the repo to include in this project + - id: end-of-file-fixer + - id: trailing-whitespace + - id: check-yaml + args: ['--unsafe'] # some accept arguments + + # download another repository with hooks + - repo: https://github.com/psf/black + rev: 22.10.0 + hooks: + - id: black + ``` + +3. install the hooks defined in `.pre-commit-config.yaml` + ```console + pre-commit install + ``` + Now, hooks will automatically run on `git commit` + +4. to manually run hooks + ```console + pre-commit run --all-files + ``` + +## Custom Hamilton pre-commit hooks +pre-commit hooks are great developer tools, but off-the-shelf solutions aren't aware of the Hamilton framework. Hence, we developed a pre-commit hooks to help you author Hamilton dataflows! Under the hood, they leverage the `hamilton` CLI, so if you are unfamiliar with it, feel free to install it and view the `--help` messages. + +```console +pip install sf-hamilton[cli] +hamilton --help +``` + +### Checking dataflow definition +Hamilton doesn't have many syntactic constraints, but there's a few things we want to catch: +- functions parameters and return are type annotated +- a node consistently has the same type (e.g., a parameter in multiple functions) +- functions with a name starting with underscore (`_`) are ignored from the dataflow +- functions with a `@config` decorator received a trailing double underscore with a suffix (e.g., `hello__weekday()`, `hello__weekend()`) + +Instead of reimplementing this logic, we can try to build the Hamilton Driver with the command `hamilton build MODULES` and catch errors. This also ensures the verification is always in sync with the actual build mechanism. This hook will help prevent us from committing invalid dataflow definitions. + +### Checking dataflow paths +A dataflow definition might be valid, but it might break paths in unexpected ways. The command `hamilton validate` (which internally uses `Driver.validate_execution()`) can check if a node is reachable. + +For example, take a look at `my_module.py`, which contains the nodes `A, B, C`, and the changes between `v1` and `v2`. + +```python +# my_module.py - v1 +def A() -> int: ... + +def B(A: int) -> float: ... + +def C(A: int, B: float) -> None: ... + +# driver code +dr = driver.Builder().with_mdoules(my_module).build() +dr.validate_execution(final_vars=["C"]) # <- success +``` + +```python +# my_module.py - v2 +def B(A: int) -> float: ... + +def C(A: int, B: float) -> None: ... + +# driver code +dr = driver.Builder().with_mdoules(my_module).build() +dr.validate_execution(final_vars=["C"]) # <- failure. missing `A` +``` + +![alt text](_pre-commit/validate_changes.png) + +In `v1`, the dataflow could be validated for `C` without any inputs. Now, a developer made `B` depend on `X` instead of `A` and removed `A`. This change accidentally impacted `C` which now depends on the external input `A`. Note that both `v1` and `v2` have a valid dataflow definition. To catch breaking changes to the path to `C`, we could use `hamilton validate --context context.json my_module.py` with the context: + +```json +// context.json +{ "HAMILTON_FINAL_VARS": ["C"] } +// will call .validate_execution(final_vars["C"]) +``` + +```{note} +You shouldn't rely too heavily on pre-commit hooks to check dataflow paths. Use unit tests for more robust checks. +``` + +## Add Hamilton pre-commit to your project +We alluded to the relationship between pre-commit hooks and the `hamilton` command line tool. In fact, the basic hook is designed to take a list of `hamilton` commands and will execute them in order when hooks are triggered. + +To use them, add this snippet to your `.pre-commit-config.yaml` and adapt it to your project: + +```yaml +- repo: https://github.com/dagworks/hamilton-pre-commit + rev: v0.1.2 # use a ref >= 0.1.2 + hooks: + - id: cli-command + name: Hamilton CLI command + args: [ # list of CLI commands to execute + hamilton build my_module.py, + hamilton build my_module2.py, + hamilton validate --context context.json my_module.py my_module2.py, + ] +``` + +The above snippet would: +- check the dataflow definition of `my_module.py` +- check the dataflow definition of `my_module2.py` +- validate the execution path specified in `context.json` for dataflow composed of `my_module.py` and `my_module2.py` + +You can pass any `hamilton` CLI command to the pre-commit hook, but it will only care about it succeeding or failing. From f4531832d8e782537845cfa2081a07f8056d4958 Mon Sep 17 00:00:00 2001 From: zilto Date: Mon, 11 Mar 2024 11:51:38 -0400 Subject: [PATCH 2/2] included stefan's review --- docs/how-tos/pre-commit-hooks.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/how-tos/pre-commit-hooks.md b/docs/how-tos/pre-commit-hooks.md index 9ae1764b0..780105285 100644 --- a/docs/how-tos/pre-commit-hooks.md +++ b/docs/how-tos/pre-commit-hooks.md @@ -51,7 +51,7 @@ The preferred way of working with pre-commit hooks is through the [pre-commit](h ``` ## Custom Hamilton pre-commit hooks -pre-commit hooks are great developer tools, but off-the-shelf solutions aren't aware of the Hamilton framework. Hence, we developed a pre-commit hooks to help you author Hamilton dataflows! Under the hood, they leverage the `hamilton` CLI, so if you are unfamiliar with it, feel free to install it and view the `--help` messages. +pre-commit hooks are great developer tools, but off-the-shelf solutions aren't aware of the Hamilton framework. Hence, we developed a pre-commit hook to help you author Hamilton dataflows! Under the hood, they leverage the `hamilton` CLI, so if you are unfamiliar with it, feel free to install it and view the `--help` messages. ```console pip install sf-hamilton[cli] @@ -87,7 +87,7 @@ dr.validate_execution(final_vars=["C"]) # <- success ```python # my_module.py - v2 -def B(A: int) -> float: ... +def B(X: int) -> float: ... def C(A: int, B: float) -> None: ... @@ -107,7 +107,7 @@ In `v1`, the dataflow could be validated for `C` without any inputs. Now, a deve ``` ```{note} -You shouldn't rely too heavily on pre-commit hooks to check dataflow paths. Use unit tests for more robust checks. +pre-commit hooks can prevent commits from breaking a core path, but you should use unit and integration tests for more robust checks. ``` ## Add Hamilton pre-commit to your project