From 75eccecfbab046c6fdd3dec2b98583433edc0a23 Mon Sep 17 00:00:00 2001 From: Adam Ladachowski Date: Tue, 3 Feb 2026 23:01:33 +0100 Subject: [PATCH] Fix PLR2004/PLR0911: replace magic values with constants, refactor _resolve_version_id --- .coverage | Bin 69632 -> 69632 bytes pyproject.toml | 2 -- tensors/api.py | 11 +++---- tensors/cli.py | 67 ++++++++++++++++++++++++------------------ tensors/display.py | 25 ++++++++++------ tensors/safetensor.py | 11 ++++--- 6 files changed, 67 insertions(+), 49 deletions(-) diff --git a/.coverage b/.coverage index 1d6cb60c3e84aa7712ae042e0deb48a0e9ea6df3..54a3d726c5adc5ce0043edc2839bb46139dbe213 100644 GIT binary patch literal 69632 zcmeI433wGny7#NPPj^>Wcb{f8>^TN70wEzl0s#~O5fBhj6c9)VZ~_666LJ#5s&>cS zam9tO<2nxB(HZaD5fmIh9i2h_97l1zDtE?hu2)CLafVHE-%52A&CGB=p6~gd=YD$Y zJdphUy`AoVSHEvn^$QnIpHkhBPE}>|t79}6HM1pLM^ zJ{j7m@TNnae<^a!S;#Nd$#qO8R7I(OPiBoMA*oCk{eUt;r0p);l;CtghFx|Cs zx^~6k`i9B{%hL_@l?&7L{HHhWqVZ#9j8DxNGj_`O6knD)lcwM^aA0aosy@?{s!P|W zs;ZZ#Q`NPLsuxx^RM##}H7rSstsArHMJ#8*GRRtN!@{=x8C6s-g1s8ji{TJ;_0=_% z^{Z3M(yI&FHi*8hA-$@BFMxxjs~6X@EmCJjyZ1}gr>oNS>Dq3sRNx3KwSJJXbaF ztF6z}6g6a0%d2bKF0(dU-B6vWO{G_*7dAGe7yZKvEMTUI511Ff8aUQ`c0u#`p3!&3 z+&RAUKiD(cJleSmPOdg1Y(2gs`)b&^?VM{HYZidzOB-wI*v)}G_!l>1PPXUC_HD>S z7kkS0<{#Y4zFxZ9WjDd znO>D$u^hTi6_t$*nKoSkH@~77ez;e)Css~wF1~g}TSH;?R4k}&$V#wJXf$&1NtzB@ zfAV(2o{|$A344l`FHdEv+8!CUcZ%N}XwC6wrbw7kSvwXu_hb4V>% zo>_2WTWf1E*)%*L5DUa+RgJX^*`tzW*RU|NGF@M}I9(X6K+DV-)BR+Z6Kz*f903Wegq72|GYzaeg-^r;`suosi~}86y+~QwJ8DhkBL29un-V)>(~|Vx9gb(Cb1NIt4b?U26x*cjWZD{5Q)M=_us#hd zqg>i{V&K@4zdb8{n2E4P#87@U{qptys_n2_Tv5z^vQaW&UAZ6^TR8&;;Gi*jf_ZSN z$U;w`rczvMB()P-*59l=-pHmwl!h)+ym2wZV;(Zezkx+We%lvt>%>-32Z{Fc;4HC! z6u}?$Q4S~vlmp5E<$!WPIiMU+4k!nd1IhvAfO6pb=YWniY_ajb<{w1<=kP;)lmp5E z<$!WPIiMU+4k!nd1IhvAfO0@Npd9#4I^b%0PhXUU6t}~A_pVV5z_7yNA%(>hu1fPi zLH;NH*WbwtR41(*P!1>ulmp5E<$!WPIiMU+4k!nd1IhvAfONpsdtxySpi#ZM9nAu; z_y1<{Rph_uZ}n^aQN9gAeUt;r0p);lKslfsP!1>ulmp5E<$!WPIq?6#14B(6Wr{Az z!hbnXv~Wpv(S`MyrRjwY*`kJYZ8if7#qVroRhl#WZv(}Kj;c>(uSM0_y5*ItVV6M( z9W6eUUEqJgsH$EJdkoZdRCy|URMu6q&9zh9eBpAo`CzQ0Wv6oVii+ym>V}F6*oVFU zHmeFpi_@y4m498eA@2b2TK z0p);lKslfsP!1>ulmp6v@1X;_?%C}9KWg_qyf}49$^qqoazHtt98eA@2b2TK0p);l zKslfsP!323*!zDq{+D(rP!1>ulmp5E<$!WPIiMU+4k!nd1IhvA!1vMt1Fj1DN7%Ks z=fA#}m#HpFIiMU+4k!nd1IhvAfO0@Npd3&RCfir=Z|ulmp5E<$!WPIiMU+4xH2h_V531w{wwy$p54Nq5qEmivPU7*MH3ap}*C? z-M`jf=dbXq{5k%G{&;_cU*z}kyZT9APaaBsmi%4vjpU2Tr<4DZ+?Cvsyfb-2@`_|b zvN~Cwyf`^Ac~)|8GC$cP**@?^18F zcaB%$_49gp9X;ZHU`sT z;r!lt-TAfir1OY#k8`JUowL@dbt;`1&ID(KGtfyn9UYS%rvE|Tr@y7Yq>s~GbQ`^m zUPYVeQaX=bNXOAq+K+app51DHY5(3nU_Wo~ws+ZE?OW`?&e~~vraj3XX%Dn>?e?}# zz9t`&x5x`*FL{LQBpb-pq={6MIblIiBA*nCSFcFop>y9 zf8y@M^@+8KnnXq7qQtq0(!`mGZV6ZaR{ui(o&K8soc_4}BYlg0vmWXz^`&~bez87L zAEgh{`{-SCS4Y~PwNJEv)qbNrt39qgtnJY5(5}VUYAV=9HNiK$$4bxid{S4q_q=c4AA>V-n72=0jjq0l$z5W3QB zdu@a@bPOH3LW-m4(B)DbL5J2$aTpz1C&eL5)*ct=n>8_UY;{Z=T@@2YnquPcN+~`< zhZ?1L3w_ie#ZzccR*HRSPrVd-(Y_Tiv8PUoC(+(aOzf$Z;t8~;Mv6V?t>rQC)Uue^ zw=^d9R>#DXOJd@Q#WAtFN{Zd+$#hKYStP|y)Ur^D9jIl26x-2`%9v=GFU2;ry&@)B z%B9$fw#|!)mbp@FL0jjI*iC9XbO;;OR*E=`XTSe+gz za7lWEz{Tk@fmP|@0@F>yBrYwLSY0A<$xw-lhe)g%EO1mwvA~ffg9MHkStPNfP+-}J zff7p!1P(77AhD#sz+uDtNh~>2U};Idz>@SC5=ZrwII@q#5xpgrq+hmKd-)ctW z`9?$uFA#`5(BiyT5YxxDUJA+*{qy zZE&k#lz+ZE$}MzLZU;Bv{KfgidE0r>+2=e8WBfauYn)ZiQfIDnfiuP#;`DWL9Gf1c zpTP+K75WVQ8GVqp&>QJ`x`L+ZY&w;WrNig|noB!U%RXxV5ytp$*e}>m*^k)|+M8jN z-)uM8%j^pKVtWFN^NZ}>c8=|kR`L~$^xq*blV`}!$u6=T#`@QiwIo9pl9^;OjP{3+ zeA1oxM7RD5#Pr1YL|I~B;`Bu4 zgsp$0f2n_{zooyVKdt{ve@Ne|-=<%!uhwhyO8pXjl75z6toPNs>Yk3FDSZDwZLu6{ zX2pYequ}xf@!f*wmE*f4&&78No?C(MkUS4>5IkotzFqL_R(zY_(X;Wbf=3R=w+J3F z65lMiYy`eZ@bH892ZBq>@Qsp7@ePvqjti2 z!4<-&spI8san{z%R=iAXaWD@r6}-O`R||e{4(7c(tQmM@D_$IRm<_0`8i}iFNiKOfgWfr$5W-cAJi17?n4jY$uV_5KEJJthqVN{rxj0< ztG2f`;)zmi12sV^=mO*Oq=IfQ9xv5qv>l%tQ(N#jsW!q<#!7WJ+Kk7<)JA-cRCl4f z@o1^;M0eq{rMd&%iO-U11G)o`lIm9YhLKX;f;Qk0F?B00lj>%43mz`jjp!acj2Qv# zBGHI$#HEqniTVw=MDU6_JXCPq3Oq#e20U1BrXCkduET=_*EHfH!Q~I(Lcw#(@j$^4 z0Sg3QHU|$7JnJ&tU+~Nt+)wf>e5T;Z`*6PCNt5vzf+tSGeFaaLi2DdWZvyTuc>H-d zPw=?$_;kTzXX2FPaX43S$pPF;@Q@PRQ}Ey+xQF24!MMBNLB+V6;G#jetKh;S+(mFf zAg|q48Tb6zOkrP@Iy1vw}Llh&^MwHw>F}i(J`sY(S7J>q;%AZ4x=MdeGBTaRNsI) zB-Jrcf0gPesJ}>c1k~439fqU+Ii|ivU&Yin=ua_q41F0>N6{BCbp-t}ia|O$+=@Pz ztKLShq0gjx3)Fu|^(LrKrFsL@zf1Kxs6R+`0MsW^y@tL;AIH?&=%bi=3w;<J9Y!n0g)kE~XBkf0JrIeBVK-oj|=sNmo6dm+d^pogG(eMqe z=+Ve&4X?vDphu)yfGg0CrK$w=Pg2dtmFVG^szAG>s=({ek78;8dMKtU(GO#4K6)^w z%FzQ-mE-y7{+Oyj_enJkCTs7N>H<*rNHrDIPN}AV+9B0sP}`+CAD5$TF*OZsjj0RJ zmYAA~HpkQyv?-=0qZX<9LLL7`srrDrTdLl;54tO+dZ9a|%EP_U9Z>&|3$-+R|Nn*m zvHzZbz<<$y24(>s^&jxJKn1{c{^c+eu+*RLU+Pcs$NIxzHlUB+&2Q(M$z#bs!HmFr z$=8zmlfQr}fQMmLU}N&;r~^1VIW*ZnnM$6Pbd#EQ$ot&;04f1~ z<2~m+>HXCEp|{Pu!@I$|(p%{*gIa)@-c)a#SLPLYy}caIaa-N5pcddA_ht7P_vh{| zce}g6z1CgpX55ADOsEDJ?GADC-R`dM>ds%CPo4LjSDojbC!ikSUgvJ-M&}AA>nwKW zIMbYQ&M;?ylM59A7R(p?k^Y{(L0_Ox(Z^uMU=zKGUP-gG3T6zZ(9v`-?Mu5*$Nm;- z0zQEGg8lYh`%(K|`!4%>d!3!J7uc8B6JfTX&_3PnWLxA2`HUPSuaf7;Zt^h972FPW z0jtO|Qck9mbIC9=fSgWph)b~b7nmzJXdSTjTTfa4Y&~Rcvo=`QS?giGV6ipVnr@v3 zGX_OgAFHdCv~=^3`KkGy`HK0h`E&C}=2r7ov)Nn;^#OCuY35k7#5@z`43Z``{tWd2 zZyPTezc3y%9xz&r9~f5{^+wv5WlV-SgTY2$ql@7rzD<0Y_#p8{Vn56nJes&SaTnAG zT%O1#mcWd`#feFYvlBz0PM~L^L&DOJ!JNTI`aAk>^k3?G^hfmj^cJWVXx3N3tib|( zranaa-2Ac zgnc+pm`K9j9M79T!aR=S&m-aK9LJ3(VT$9}nIz1Oa2yGHaV$AN!k!$5l#s9o$H7BL z*qvkXU=nuYIH;I}T{#vFB4HPfg+(OH;aE^e!qYeo7)!#=5f+fJ6UW{INZ65MUT+e1 z;F#NkgzY)@$|Yetjy-#k(C64AkAz8%U3-$y<9J$E61p5apGHE5W2ep}q#QeRBB9N( zeFqW}j&^$zS{#W@LX)FKNN8|0EfOX;8YT&$GsteNZdf6Vf?*V$(5(=&g?6Hqgov*` zFn|PCaNIYR1ebGsXeJ5PbKH=j!8$+~!?4aE4c0~i`hqlA(NMgn?)G-!wfbOLFRmEv0()JyRV4OU2Tj0Sa59Hl`sBgXNKc&L0hyMFRSMG*}u5==#y1Iug+Hqrs9$K*x^;iz5O3J{nX> z@iq<8QoKcjMN+&;gN0JOL4yTSyiS8kDGtzJek7p7M}vw;K!1-0<&l8y9u4M20(yHi zm>UV`?9pIOB%rTHgUccTT|FAijs*1dXfP`h(9xs8%t$~#j|P`U0=juLxI~JbG?*d9 z4jNo6#daE8B*iuwOqXIS4K9>o3k{}8v6%)JNU@0qQzHSrI}%I*0W;x^84^t9*Z_Dw z$1LC^j`e^OIj#Vlz_Bi42j?-=ShtP@{QBFvItY=reB7!%1a#SGzyd!L&|{+k3;j$$hm8g-_%i|h zH5#z+&jfVWXuuKx7t?6KQUDhh(10ZYE~e6er2#Ic(10ZZE+*4}r2;0PqecUk448m^ z8Vy)FU;?^nG++sV3s}Qa0u#_lqXA0_T)-NZ7MOsF1RAi!zy+*fseuWoN1y>q4qU(* zmL8aZY6Kdv1i=NYVJU(Ms70UwOA=i4r2$J5T)?@pM8O4|4oekWz!|V)!3BINOBYE$%FAx%5w+fBb28O#)m1-92$a} z1IiPJhM?qt^1PuTs5qcJZD1>+|u&lQY!Q=Td`1T_YfX9~u9 zC{Gj`VsU;!I>-nXgQ1cJV|H>stYL35sX_XPZ1h|iB-xo1mhOU6NH9f zik0&Gz_^9-^q?WAE1*0(Fm9ndIcNx~3MkJFj9VyA4H^u`cT=7j81JM!F)-did0t?= zo$|E6cpK$ef$>(#lLF%{l;;G-n<-BTj5kr15m;MlK^rMc35=nVfU=yxxrOkg0Cfa} z=LBtC6X7XATiZn1G6L4tG!dQ;wAD?7rvq(OliijLG*&@C#=;@t6>igd!jpm4w2qt; zEn7=?D$ttN5S|INrqzTe0@D!k> zn+VSUT6!tr2|!C%6CVGy^b*3uzm{H1SoDYUOIMM$=#RBgC4@zPST?eRu-FeYVkBXK zpHT^6Q6H#m1YsecQ3+x39%y(OVZokJ31N{QXxMPV!aSoA!eTs7X$fHg9;hTuSafGJ zim=emXe42AozV!A7f~5u5uGg?PErvKBP^D)Wu=4#az;Z5i{d~-hY=RS8I=$gzk!N} z5*ECHiV8?qPS9^>VH=hWEF>&u0~Hh#7O)u&BrIA3^)DbSR0HMrCoE0_<>nI>q=7o; z5*DF>lAQ?)&p>XHu-FXbxP%2}AnFhnm4OH)EF=R#pPR*FAV|_I7z06$W|0^OQZx(0 ztp8!L5DYD})iR+tfE58)MjLp(m1u*6XR*2g=;@-C$s;@s{hBmT`^|%6 zc7N>N>uz*!f_i`^cez{XUJ4`qbKDZQznklJa&6~Z=PRfRc-Q$YjP;*%e&RgfY<6yS zu6EWq8E26*8)^f_!9}T$azHtt98eA@2b2TK0p);l;Qx^Wq?r#7poWk%^YMXM4#)=x z=Dc#!%tr|3yt$;A4-w3{6{MMu5zM*sNHZTKnDF|enU508*{!6R4-?GMvq>`_CzvCL zlV(0pFk#5m%ts0)jI^5hP{AC2kTmnLf>~Ndn)zVCEG;F?e6(Pe?kCM`xPY-a1eUf9 z7_d1gk2JFx8(3Nh$mVMR2M!|5Y_^7BA!%lFHGnYUY1TOQhi%zB4J_?fK$_Vs4a5E< zWRo<2`Ta=9=4b%>!n+AJMFZF;pM-3NhGAb4vI!c%-hD{O=4Sx&;I+!?2;ud|st6B~ zu!&=8GznL7?3p5ABgY=_&ZB{27kE{Y92X~-vJ447cqWru7= z21bNX3P?gWBZDzi0+NtT$N-K?laS5F0QMV2LN*-(n1VaQW@7+zz`@xZJLQmY7RQe8 zQfMZ}4joB&DM$8x=n{_Y;Qi1Hj&3^=Ud)lYB)o{D1r|@|2t(BHLXOPLX@E91z|0HS zLfZg;rUJn`A6EEB!zqz~x<49Djs%qb(eQjJ?zh89Okh^@N5hHH3aI&`;e<#)$sY~b zR17;Zo0tmOYzz}@HY#KjGE6|d9}U@@3>RBy$Yy1jfNDP)j)?>m`_Yii&9F6h!_-j7 zre~OdQa>8985$;_(vOC0l7{AkFgYM6jBKN_;x8YZC1kA`f*hKpNi zI6M+i<440`k$@6EJ1hkOrH)YHN5T@u7z+GIIF#dxIuZ`yShs?NgCm3uiaBQLNjNA% L*tUpcP2+z8MUuM0 literal 69632 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 try: response = httpx.get(url, headers=_get_headers(api_key), timeout=30.0) - if response.status_code == 404: + if response.status_code == HTTPStatus.NOT_FOUND: return None response.raise_for_status() result: dict[str, Any] = response.json() @@ -94,7 +95,7 @@ def fetch_civitai_by_hash(sha256_hash: str, api_key: str | None, console: Consol try: response = httpx.get(url, headers=_get_headers(api_key), timeout=30.0) - if response.status_code == 404: + if response.status_code == HTTPStatus.NOT_FOUND: return None response.raise_for_status() result: dict[str, Any] = response.json() @@ -269,7 +270,7 @@ def download_model( follow_redirects=True, timeout=httpx.Timeout(30.0, read=None), ) as response: - if response.status_code == 416: + if response.status_code == HTTPStatus.REQUESTED_RANGE_NOT_SATISFIABLE: console.print("[green]File already fully downloaded.[/green]") return True @@ -279,7 +280,7 @@ def download_model( except httpx.HTTPStatusError as e: console.print(f"[red]Download error: HTTP {e.response.status_code}[/red]") - if e.response.status_code == 401: + if e.response.status_code == HTTPStatus.UNAUTHORIZED: console.print("[yellow]Hint: This model may require an API key.[/yellow]") return False except httpx.RequestError as e: diff --git a/tensors/cli.py b/tensors/cli.py index d0f58f9..4a59daa 100644 --- a/tensors/cli.py +++ b/tensors/cli.py @@ -38,6 +38,9 @@ from tensors.display import ( ) from tensors.safetensor import compute_sha256, get_base_name, read_safetensor_metadata +# Key masking threshold +MIN_KEY_LENGTH_FOR_MASKING = 8 + app = typer.Typer( name="tsr", help="Read safetensor metadata, search and download CivitAI models.", @@ -211,44 +214,50 @@ def get( display_model_info(model_data, console) +def _resolve_by_hash(hash_val: str, api_key: str | None) -> int | None: + """Resolve version ID from SHA256 hash.""" + console.print(f"[cyan]Looking up model by hash: {hash_val[:16]}...[/cyan]") + civitai_data = fetch_civitai_by_hash(hash_val.upper(), api_key, console) + if not civitai_data: + console.print("[red]Error: Model not found on CivitAI for this hash.[/red]") + return None + vid: int | None = civitai_data.get("id") + if vid: + console.print(f"[green]Found:[/green] {civitai_data.get('name', 'N/A')}") + return vid + + +def _resolve_by_model_id(model_id: int, api_key: str | None) -> int | None: + """Resolve latest version ID from model ID.""" + console.print(f"[cyan]Looking up model {model_id}...[/cyan]") + model_data = fetch_civitai_model(model_id, api_key, console) + if not model_data: + console.print(f"[red]Error: Model {model_id} not found.[/red]") + return None + versions = model_data.get("modelVersions", []) + if not versions: + console.print("[red]Error: Model has no versions.[/red]") + return None + latest = versions[0] + latest_vid: int | None = latest.get("id") + if latest_vid: + console.print(f"[green]Found latest:[/green] {latest.get('name', 'N/A')} (ID: {latest_vid})") + return latest_vid + + def _resolve_version_id( version_id: int | None, hash_val: str | None, model_id: int | None, api_key: str | None, ) -> int | None: - """Resolve version ID from hash or model ID.""" + """Resolve version ID from direct ID, hash, or model ID.""" if version_id: return version_id - if hash_val: - console.print(f"[cyan]Looking up model by hash: {hash_val[:16]}...[/cyan]") - civitai_data = fetch_civitai_by_hash(hash_val.upper(), api_key, console) - if not civitai_data: - console.print("[red]Error: Model not found on CivitAI for this hash.[/red]") - return None - vid: int | None = civitai_data.get("id") - if vid: - console.print(f"[green]Found:[/green] {civitai_data.get('name', 'N/A')}") - return vid - + return _resolve_by_hash(hash_val, api_key) if model_id: - console.print(f"[cyan]Looking up model {model_id}...[/cyan]") - model_data = fetch_civitai_model(model_id, api_key, console) - if not model_data: - console.print(f"[red]Error: Model {model_id} not found.[/red]") - return None - versions = model_data.get("modelVersions", []) - if not versions: - console.print("[red]Error: Model has no versions.[/red]") - return None - latest = versions[0] - latest_vid: int | None = latest.get("id") - if latest_vid: - name = latest.get("name", "N/A") - console.print(f"[green]Found latest:[/green] {name} (ID: {latest_vid})") - return latest_vid - + return _resolve_by_model_id(model_id, api_key) return None @@ -359,7 +368,7 @@ def config( key = load_api_key() if key: - masked = key[:4] + "..." + key[-4:] if len(key) > 8 else "***" + masked = key[:4] + "..." + key[-4:] if len(key) > MIN_KEY_LENGTH_FOR_MASKING else "***" console.print(f"[bold]API key:[/bold] {masked}") else: console.print("[bold]API key:[/bold] [yellow]Not set[/yellow]") diff --git a/tensors/display.py b/tensors/display.py index 9f5d64d..0466767 100644 --- a/tensors/display.py +++ b/tensors/display.py @@ -12,23 +12,30 @@ from rich.table import Table if TYPE_CHECKING: from rich.console import Console +# Size formatting constants +KB = 1024 +MB_IN_KB = KB * KB +THOUSAND = 1000 +MILLION = 1_000_000 +MAX_TAGS_DISPLAY = 10 + def _format_size(size_kb: float) -> str: """Format size in KB to human-readable string.""" - if size_kb < 1024: + if size_kb < KB: return f"{size_kb:.0f} KB" - if size_kb < 1024 * 1024: - return f"{size_kb / 1024:.1f} MB" - return f"{size_kb / 1024 / 1024:.2f} GB" + if size_kb < MB_IN_KB: + return f"{size_kb / KB:.1f} MB" + return f"{size_kb / KB / KB:.2f} GB" def _format_count(count: int) -> str: """Format large numbers with K/M suffix.""" - if count < 1000: + if count < THOUSAND: return str(count) - if count < 1_000_000: - return f"{count / 1000:.1f}K" - return f"{count / 1_000_000:.1f}M" + if count < MILLION: + return f"{count / THOUSAND:.1f}K" + return f"{count / MILLION:.1f}M" def display_file_info(file_path: Path, local_metadata: dict[str, Any], sha256_hash: str, console: Console) -> None: @@ -174,7 +181,7 @@ def _add_model_basic_info(table: Table, model_data: dict[str, Any]) -> None: tags: list[str] = model_data.get("tags", []) if tags: - table.add_row("Tags", ", ".join(tags[:10]) + ("..." if len(tags) > 10 else "")) + table.add_row("Tags", ", ".join(tags[:MAX_TAGS_DISPLAY]) + ("..." if len(tags) > MAX_TAGS_DISPLAY else "")) stats: dict[str, Any] = model_data.get("stats", {}) if stats: diff --git a/tensors/safetensor.py b/tensors/safetensor.py index 899ca38..710d57b 100644 --- a/tensors/safetensor.py +++ b/tensors/safetensor.py @@ -24,18 +24,21 @@ from rich.progress import ( if TYPE_CHECKING: from rich.console import Console +# Safetensor format constants +HEADER_SIZE_BYTES = 8 # u64 little-endian +MAX_HEADER_SIZE = 100_000_000 # 100MB sanity check + def read_safetensor_metadata(file_path: Path) -> dict[str, Any]: """Read metadata from a safetensor file header.""" with file_path.open("rb") as f: - # First 8 bytes are the header size (little-endian u64) - header_size_bytes = f.read(8) - if len(header_size_bytes) < 8: + header_size_bytes = f.read(HEADER_SIZE_BYTES) + if len(header_size_bytes) < HEADER_SIZE_BYTES: raise ValueError("Invalid safetensor file: too short") header_size = struct.unpack(" 100_000_000: # 100MB sanity check + if header_size > MAX_HEADER_SIZE: raise ValueError(f"Invalid header size: {header_size}") header_bytes = f.read(header_size)