From e1c03608c2e3bdba79aa301e7b73bc8808b46b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A2=A8=E6=A2=93=E6=9F=92?= <1787882683@qq.com> Date: Wed, 24 Dec 2025 21:57:35 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat=EF=BC=9A=E6=94=AF=E6=8C=81Union/Option?= =?UTF-8?q?al=E7=B1=BB=E5=9E=8B=E7=9A=84=E5=AD=97=E6=AE=B5=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2=EF=BC=8C=E5=A2=9E=E5=BC=BA=E9=85=8D=E7=BD=AE=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E7=9A=84=E7=81=B5=E6=B4=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/config_base.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/config/config_base.py b/src/config/config_base.py index 5fb39819..715ee944 100644 --- a/src/config/config_base.py +++ b/src/config/config_base.py @@ -1,5 +1,6 @@ from dataclasses import dataclass, fields, MISSING -from typing import TypeVar, Type, Any, get_origin, get_args, Literal +from typing import TypeVar, Type, Any, get_origin, get_args, Literal, Union +import types T = TypeVar("T", bound="ConfigBase") @@ -108,6 +109,30 @@ class ConfigBase: return {cls._convert_field(k, key_type): cls._convert_field(v, value_type) for k, v in value.items()} + # 处理 Union/Optional 类型(包括 float | None 这种 Python 3.10+ 语法) + # 注意: + # - Optional[float] 等价于 Union[float, None],get_origin() 返回 typing.Union + # - float | None 是 types.UnionType,get_origin() 返回 None + is_union_type = ( + field_origin_type is Union # typing.Optional / typing.Union + or isinstance(field_type, types.UnionType) # Python 3.10+ 的 | 语法 + ) + + if is_union_type: + union_args = field_type_args if field_type_args else get_args(field_type) + # 如果值是 None 且 None 在 Union 中,直接返回 + if value is None and type(None) in union_args: + return None + # 尝试转换为非 None 的类型 + for arg in union_args: + if arg is not type(None): + try: + return cls._convert_field(value, arg) + except (ValueError, TypeError): + continue + # 如果所有类型都转换失败,抛出异常 + raise TypeError("Cannot convert value to any type in Union") + # 处理基础类型,例如 int, str 等 if field_origin_type is type(None) and value is None: # 处理Optional类型 return None From fe75dad3bb21f4bcbff05d306749b0b22301a20e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A2=A8=E6=A2=93=E6=9F=92?= <1787882683@qq.com> Date: Wed, 24 Dec 2025 21:59:54 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat=EF=BC=9A=E6=B7=BB=E5=8A=A0=E5=AF=B9Opt?= =?UTF-8?q?ional=E7=B1=BB=E5=9E=8B=E7=9A=84=E5=AE=89=E5=85=A8=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=EF=BC=8C=E7=A6=81=E6=AD=A2=E5=A4=9A=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?Union?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/config_base.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/config/config_base.py b/src/config/config_base.py index 715ee944..1baa9372 100644 --- a/src/config/config_base.py +++ b/src/config/config_base.py @@ -120,6 +120,15 @@ class ConfigBase: if is_union_type: union_args = field_type_args if field_type_args else get_args(field_type) + + # 安全检查:只允许 T | None 形式的 Optional 类型,禁止 float | str 这种多类型 Union + non_none_types = [arg for arg in union_args if arg is not type(None)] + if len(non_none_types) > 1: + raise TypeError( + f"配置字段不支持多类型 Union(如 float | str),只支持 Optional 类型(如 float | None)。" + f"当前类型: {field_type}" + ) + # 如果值是 None 且 None 在 Union 中,直接返回 if value is None and type(None) in union_args: return None From 3fb258cbc283f9a8fef8afce0130cd5cb0188b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A2=A8=E6=A2=93=E6=9F=92?= <1787882683@qq.com> Date: Wed, 24 Dec 2025 22:22:29 +0800 Subject: [PATCH 3/3] WebUI 0899868dd820d0b3796fb20131356aa98e672a7a --- .../{index-B0pA3UYF.js => index-m3qyCSFT.js} | Bin 946064 -> 945515 bytes webui/dist/index.html | Bin 1855 -> 1855 bytes 2 files changed, 0 insertions(+), 0 deletions(-) rename webui/dist/assets/{index-B0pA3UYF.js => index-m3qyCSFT.js} (92%) diff --git a/webui/dist/assets/index-B0pA3UYF.js b/webui/dist/assets/index-m3qyCSFT.js similarity index 92% rename from webui/dist/assets/index-B0pA3UYF.js rename to webui/dist/assets/index-m3qyCSFT.js index 96410ba2161720413eda0d50c5bb8713271628d7..0925803e442cc8c7b8872b51436883b5f8075eb7 100644 GIT binary patch delta 8178 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 C0Oykc delta 8661 zcmbU`d3;nw(!c6CUQd!q2v;t^B*0`Ic}xxtC1DbhELVUK2zQKOGV_v5czKh|n_MJd z;);OC;f~tkt_ZFN2m%`Av7Dl-vL7qxs)(@edLej#D6SCBuii`o$*1lg-|v@9byrtc zS65Y6RrmY$KIOrCJFZXz@ zUVCmQ!SFP}W0(E+rVqlY8`1^*J(8XZa{IOPn*`kL*G3I6z})tjOb>vwedXBWI;2=Q zZdQye7T1U+neK9{TTHOq5eu{j-* zWOX$P3ZI>jn&ECgnIA#G)BerW`gm~p9o6TMcDj5wAlT;N34-Ibp8?eQcPr&k*M5g@ zXk?a%vr={b#~by6ey;esxPy_R6ox1VB7VYFJZDuR!1_8)j| zaQnT_l$iR7l08jOBzt0FW_g|L^tnB9e53z|7sm^tBusY{&vYawm)MnV?OaL7mF!BU zCWY57N|}mX@t19{8L6=vJ9b3+@B+o^YPQ|wIr z&IbKUC80>P&T^t-X^k2$+D$0+uRAvcO8u{%8w{oGAD_FTGuBAHeJa8C#$W5X#Y=Fo? z32B)wPrz5pM7wrY9kOT{K5E41yY5UKlQu$g-?G3l3l3#l7b%u9k+<9JWnzZ4M!MAr zI=ma1_%C0dg<6_+BMN`V-i*WFwi_cNk=7lUh`-IayzGDT#@NBQq*tsLy}qfquuvf? zzB;d%p&e?$5%BYj9-JO^fW^P`X5SpA5)f5svAacKzNCT7;0}V*gvGMO;;^~|6jpF) zJF9dk;~f>Ex5(%4iP^IM{hMk0Z>%obItBSH88XstuU{5izf8>3HYEl;W=TV(TN15u zSAeoua#C)T6w{#exZkc z1S(jE5z3%Ky_tDOcz*@dalgwv{~K@r9*^}r0s0FYV7Oqhn}0mEjsSnY8y7F_{X74lkI zf|*WiX_jcR9DP{bUp5H@7*Aho{C|pm^O8LDa7iqSg=>w zTosBhv&t^3AC|%A&=+yJHpN{gx#bEpAg01xfkiHaXtu8$%WSB-woz`&h?1%NxM=h%bFB(<- z&!sN=c&(@*f$5+C5vYzO)9Ml?nc;zz`F_E zxm?}937&-zRApYK&)d{2tJ0srLcpi`p9Rd1UUlE|@EHM@y5R-TW1szb8w`O0_3$=0 zs9AEwc8oHz+Oh*S>Y<4Zd<93F)ZAAf5wI)1ila?z{w^37)ucJLgU>AUijGB@P6rkb zBhoUO*v?%@+r-}Ah3B(?$tt7_ERX_`!)4F$Wfn;2pVl0o-N$30=*n(jdsIj;b#fC5 z*q15{#U(>_!z_R@R{9q>X!1!`hn8N;SkfM70Uvv258Q==pX`B=*z3I)21A)TdN2G* zoAUlXC>&T8Ja;_>Ov}jeVMOw=arUj1grq9f3yd-ggAv)b&9-6i8aG#f@Xc!@bLF z!7&)15408^0})mK=`nc65TTh2<2s+YNQ>?=+xH=KkS2-U_#FM{*l{?3yw`jLYY_}O z0ZXBQJ$VA%rh!>M##r3I@;`<$T#s*_qgcIyw^L4Jbns)?`l|y=PNH)*us2RZR!oCr z^$4zCnzMn$o`NK#$~^_yw2O+ZISB*UWfdZ!K~+z|R|fQ)4WGeB2lb)dkhLP9E!XFHV-_*1-?S;QiK zL>F?f7k)(R%IxTm7;#-}%SCV^L-S7<@MX2)Coo`5$*M2Gm;H*gN^5owRK*UUc&lLYHNZ;Ie&4h$OrTQu%ZvxUb>q$O=BK0jJ!JHzq z`6iNtpv^>h-2GM)`J*03Cx(+f5LqNyMg&x7u`JLk2#4Cc7r6saS?Q4^H?Bz1T<~{3 z++L~IJ`Y_{tF!_-WAR23t8R%TSgkwMH+mDS3mhu+C2wiQI^2)sVmGuu*`T3ch$1Bb za~X{x1*plnF$CkwT(&fhoW*X%0D_mNx$5LWn&-wbq|D`cKEs3aeuKWIPO0o9R(s`=M~uWJvAMHJHCtan2B;~M$x049 zY~u_vjD1l^hIX|_%pi$f?baD+?Ep=F7pmV(65rKcIFk$vwxigiGs);~?HG1#CK=Qn zp-w3xj}e^1PRQpgVHlX%urM3ULPb4VQ;^gnZmNjpF{=ac6UJhy=CH{m|31douA z#+JHBfB)H+&Fb1>vRO}LksT}{v59CDy!CqR4!nbu2;QK&cwT96`<#`i8orrbaFXF{ z`h47>tR7xSoSMyrGE%1HrnOG;6?W@gB*vH`i}7wb-piv;${dnWpRthNaR(xh?E4vG*%uyhu1gDT`dM`Q}&un7NTqMDc0QK zvTF5^+J2D7p17Y(LvZ$fQVMzMq-6xlhdj3T0g{g3`~xHnvGL2v0)nu4C7GtdzpcVK zk*J&+++eQFBTF6!UQ1ZgYVxT*PqNKpOIMOXtavTChb>=2Ue<8a0yq_MJMX}qhwVV` zm(|oAq``nkaPuqVKfUpAZFrk_&=%o`$>Ro$uFt=~ap}7x>_$st7yYN3IBkSb}jZzPe*Bu`Lsp?}*Iy-u~`a!cU2_j^vSSrcHa#8Wc zJFz+oS)#M(BV~No2;v&;PN5RTOEHxyapX#+rf~+pPPj{cvSC(v&ry|ectQ3o| zlI4X8)PCI0x)>dfu-c2d{svxXu*%D4{;X4H{;WS07Veeoxv7F%vJXiW<~dlx33U0n zIslJ06&?uDinUj6iQX#R-<4Y9W?A*P^cyP1uR$~kTYpd~&G2Q5Ec>$?s z3W-?vSGke?Hml%}Qxp=lOM$mZW7gVgj72VgY&FIqP+N_I5u9o@S`d&nV=@9;n=t`F zVVf~2_7~jd35jSNjnl(z0TwT|855EE-8SPL2rdMWYS?|o^;zgp&%I{+JQ*am@;hTX zf_>i^w;)mZIio$;i^ksmbH;ZOtT}JI2WOl+Z@kWpl|=hIUvagsBvZoJoa6O6nrv<* z0IiZ}tM<4=sZ??+S_#+_VV6|PC1XV-W{@?MJBn9XRuak$#%^0EH`Pd-5_|8EVGwKW zM?=_Br-^&)I1N=zy}9Tx%t7Tmw}~wjLe1*CgSbaSa28^=g=+S#{m>N4Y@@hvlr`W) zHKVxq(KRk)(sk@u3`f;}j^_0Dvla7<2`pqUSIQdqa@p+jHvQ0ucr>o!@eZ%?xbX_* zWiJ&QW7MpD+$v52gV0#N{a@V&U`oF{?&vR+m%{$NeD6X^nXYOy| zk| z>S%DdF)YYfUsQ^AiFpx^#Lr#fG7x-rg-b!=$g2U6bCnxNB#|Aufc`15DLFN?U&^}(rwuXD2}colo4SX`l$ zWO@~g#jAAlBQ2|R@7sIya5)L>W?kg7XG!e9b*|2!iP6lC>1bT)Er)cw@Bq8Z-J(~} zWXiw+4GNALcPU=1>v7zRNVfDe7o}}0MNb28`)Mv->mNJK4MAD2-QeQ1?j}7Q5{W!~ zVm0O=oB$*EhNO z8td~v8ZAZ%+07E!X9x8fzoS4C9(|Q2|9^O=i$xO0+Vg!F zLP`Tl5@uTy6M0lkb$v9e! zpd(I`PfZy>H=3ws;d0HukzeWtF!Pbn5cT>{S_n~d9fC)m<*BHU#3Hnv)0ZPj8JPKY zTWFHHDv7QKOeoC4(}(+Z9n2=tnix0~7|_`fo+jTr-yt;n+&)RnNIbLm{xA3LI=yr4 zSDQDT-mog(+U@x8i2~Zg>2=p=UzRcwzuL9pY%9ZbarVIPPe1egnJ4ZK&i;PiL#Oxd zIlF!Pm-`Q%d1Uj~txuia^PEO=a4XW>;+-h?9Ll0Rj~rRI1HyOFi_p5t(U4bttJeo(V^X$ZWL&B4+GSD+)hn6jWk^|(d^;W zgN}Qfl~`RSx?K3a*-4eg%r<(jM!LpE|A6u*q|o?Zk$+|iZJpYkQ6PkPUFD@^b#BS^ zE8Dnb{9sb=vV3Rg;n|L-L!t)+$8OmyXtJ*8q?xP#FV+l{4C?nOv{oxl9O*O${kb`v z&g$&;4eyyQ)gMRFP{2@R&k7n=6b1a|B-Ls8RdC|B5*OCZ*v8TE%)n~_0d96{V=pdb5lasaO z=;PV0IdrZGNwl0L@+^Ed9j1<(OY>;gf~=I9Z}ZsklyQK1xRloZL+@7XvsmQodVG~m zH)RpG!X74EoVqpLv>bX4hOyZrO+pt1+c453_ard0)KRA7E&{c2l<6kLyHttYCEAo4 z$?dZy4o~DW$6IG%zM5vmr6Tf73)FL2rhb2dd{uYKH0ou%{f${2l7*l#0918#NL+RI z8(kJ#_pQOiY*$0R$9n&V>mf5)&9#tGzfO|Hvag4{gr0_U<3}P_n8!t{)6+xubnjRQ zTQe%eq|R`M=1o9HeRN0YXmBt8`;Jic?>oYNPSK%~n0hK~kov{H!!``Wd*Xf9!=|i7 znU6kXw)G?V4)*XS^8hvWY4gtpSimATn@h|K9K2BMbCuW&J=O)Rd9(S$gar=m-Ml!b zK$x_s#O`xh12l^ZgbAHpR=dT#0~V+WFPIwu;rgxS1Tbh+zhi|v&1vd|ZRQ+ph4h!r f*Kj