From f88eb0f9ffa433c76f51af08e8034f5dd030f2e8 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Thu, 5 Feb 2026 09:09:19 +0100 Subject: [PATCH 01/53] Added the AnimatePlayBackLines class which facilitates playback animation. Collected all animation-related tests into the new test_animation.py --- examples/MobileCrane.fmu | Bin 912867 -> 917159 bytes src/py_crane/animation.py | 89 ++++++++++++++++++ tests/test_animation.py | 185 ++++++++++++++++++++++++++++++++++++++ tests/test_crane.py | 113 +---------------------- 4 files changed, 276 insertions(+), 111 deletions(-) create mode 100644 tests/test_animation.py diff --git a/examples/MobileCrane.fmu b/examples/MobileCrane.fmu index 446f2ff2d684b6663babbe6dff1b23b4017ed70c..73b315edb8ae8e244f1d42cfe4feb86f814b7514 100644 GIT binary patch delta 4665 zcma(U4Nw%<`F8gb5qS!N9$*4bP~`A9;Q&z@k_w_mG{vdLDj+W09$Yx~F1LH~lUO9J z)~4xnoSX8}i4&U{oy>HasI}#ivDRiJ8XHr@ztM3T1pe`(>9^sdwa`r)_PPeYC`-rdhU0{H z)>WKXi{4)(+^H%%Avk8FvKO@B^g;I0H)^)hm(aBY?i3>mbuZ^qI~eT5G44`g==Rrq z&e^P*wG}mOb1(FatT37AN(aq;N%02!4U)1#~ zigoz`qT`8Yh%XKO_$=3(MP9kV&0Xjb+snj;<_&IHQnJ1EFGvllwFJdtcrr3FwkQ%g zeuK-*^h>!uAa%=)*h5LNUuy9BostvROD?ZpY7*t(PA;X>DXJpQRs%kdR4x1LvQzZ? z#r9fj309jV?3R526;m2JR^2TCP_WeI1Dzm9R2YrR?N?OH*ld~|)E0O^fXpcBMMc6s zuUiH(*{Q)rxgE=amhyVZkG(D|$G({^uKa1ucYonIHrY&sh{Gor&Xt+wqa@E-&pkm_ z^>h2mBIzirjp@*RHcCfJ3(kESqOPdb><1!J8$3O1=>#QiRbk1Y)x;EnnO0U8kPod& z6g%Eb(Ml5q)x!%M4k$Rc3^msv$+W0xE&J_I@rH{Ku13gwDc0jgB^zi_Q8FYrWFT`g zvej<96z7BfQpN@B!2_q0o;8AQwc%_-1g&aUM!DA$Xpxy0ODN?MEPGWfy%-QZR5i8X zCGCGV5w~;mmx5+pG0Tgl)DG_XB&wO316(#-V5RC=d1yHY^%<){MK~@-L<&|p6@umEI$Wm9G3*BUZ(qjeczv~^01{gN8+L)nH!l0Oc?vAWvNog@b?aZAYi^k?7_ zx1du|ML%>V;~W`P2fZ;mf;KV6XQTbo#=3)yJKVl(cD7j>^GkB6xdy;dqHjH|09XC) z2DNlEsr(g}Nb+*{$4Jv_TR`TmB zTxpEo3(XPIOuo9pm2YKTni>Zk8QQaUMZnFc&>Mv!6W+dGCHP^XAbjE zlgc#n?2uXDE4j%riu}OL7ftR$-DZBHIZK~l>}=AibF~C^vNm7h^fr|2)UlyiGkMP} zgLh97ziN)L{YxhQ4?|Dr>|&m5XC`m`gZ-kKX)@83_TNk%! z&G(X-ih|cSi~j_(>55(xp~`>raaD zb#B?M*45d4?Qq@eS;RgVg2hJ3Zl*IpXyT0U)wYD2n^)J|oA|ORXEDtDux!Bu$CD(! z-26DHxrgFH%hSxwX3|^1Cy-+q=G-ipsCG#WaOU9F%PWCa0Xr3%D%>`*)F`U%T@vod zB~>bK3Es+{L6&8j<9VN(9No>&Poe`@B+oQb6yh_@{W-$B>neV7ZKE01W3V2FH3L=* zteLQ4Va#mHx@ea5r>AQr`O|H*mVX%q-yD5{WDXz;{oo;`184#NWj+N4?T#4o!vPe> z5I?7giAqL&b^tl}u2Fc;B6twRGJ@PeAoyT3`03~(A`gbSb^}oOD1i$e8eKqo22mm- zy+EP+mut{Zk8v>s4WT&xET;biJL&8k*`w8;z4bN+cDitQkWhs&pH7$q_f zrZ-lDzr7Hq;V;R; xag@bc=lXGIHwE3KaUA6tE%eYhns46IjZCEfS#uKkWE{oCw!t|~c|Z^Ae*kJ>^A`XB delta 2477 zcmaJ@3v5$W7{2$MqFdJ^>Daomv90TdLu9XRUAtvW89~Tcw;>2@ly#*oy1?2oh5-gC zT_6NlqKNDtN)$yUzT(KjE~u#JB%;d_brKW_DiVpAkG)|}&uL3z)P&*2RYOeqax9DDSUc5JX#T2MCC7y0@Zp%p!fo|dr^B+bcH8Nyn+n`mTGajo zIhUfW-kNJp{8cso>)9N~panik7T=zx`*}9juP_Zkp)KAJOsPB6@y8`k*3|SCL6JFHm!OpjWN2 zI_;W#yUUhe9dHVJucOToLFu>uM2lty*^5TeK+tSL!8n4-)Jj_GA6*L!+mkttAyK+k zxU(8x7Rf^P3r5!91aVY|UyBoiN;zDWAZ)PVq?j~YR-rli-Q@I(3ptKqj`VE4gMzlR zxD2WdyaMEnxI^R*nO(Tmh+7NNlH7&n!^b>N_R#yqp!r|!$4E9rq1HGgSgwy z;lv=5E@i2Z@FCu!_8x0?>niIgC1#lJSkHWi6}(%ucyk2}3bg5#TDK#;VMm?AE?%~q zPJ`L<=-JXpn#(ZFkz`mbHO`s_izU5ohgPc%T!eIL5V?%;!SjP8R%q?yRTqV_9^6}@ z%&5;bH$?K?ucvSvgZDK`2F*B82#x8oO)~iX16~D_I@!Zeb%Iy3o?Ph4mi5fXyGo1p zpRAHm%%FIH;z5cqif{@!MFfRHI9Ww14D8*9siUOO+j+c&6o=7#^M$`%N8p7Pa!6w| zY_J-1hHSghk(c8z>hpAYc7rY3YOv+l?0H5jECjMxu3MF9%-3h->-EAxATL*tdlUzB zeI(yIfvbzNF+~YK+W*y#pkYNfaDo?gP1AcO28C z2)H&tC!(SSy=?5QHgU|WS236vYNsL=cx9(n@4o_nzFQsA`R%cDEwV}r4q*tJP zIgg?^pDatsmB_a2ulv~COtORMEcfzB37W{)gpg!)8(gm1kiw+Yh7rZHM~7$S9!VxJ z-63kXlyIMNDkLVXx`P2y<;lS+@{rD86^pb>x30! zL<_NFM33J4h8b%RC7fi=t}zm+xlaM_ZVUbG@bt#B(6#7FkuPQEILSnnCZEwgPGU4n z#dIsaS1V{d1O3tj+8+Lr31&=?B}h>UB@?6=Eh~W536hpX)zEz5(%dBxG?qaf$YHb2 z-A7<%3z-d56I3;ray~RkRbwM{m$#5)D4Qe-@5D?#pRVLM2H&gh list[list[int]]: + """Get the limits of time (data[0]) and all points. Time shall be sorted in ascening order.""" + length = len(data[0]) + assert all(len(data[i + 1]) == length for i in range(len(data) - 1)), ( + f"Columns of 'data' not equal length {length}" + ) + assert all(data[i + 1].shape == (length, 3) for i in range(len(data) - 1)), "Data points shall be 3D" + t_minmax = (np.min(data[0]), np.max(data[0])) + assert all(data[0][i] < data[0][i + 1] for i in range(length - 1)), "Column 0 (time) is unsorted" + axes_lim = [[int(np.min(data[1][k])) - 1, int(np.max(data[1][k])) + 1] for k in range(3)] + for i in range(len(data) - 2): + for k in range(3): + axes_lim[k][0] = min(axes_lim[k][0], int(np.min(data[i + 2]) - 1)) + axes_lim[k][1] = max(axes_lim[k][1], int(np.max(data[i + 2]) + 1)) + return axes_lim + + def init_fig(self) -> None: + """Perform the needed initializations.""" + ax: Axes3D = plt.axes(projection="3d") # , data=line) # pyright: ignore[reportAssignmentType] + ax.set_xlim(self.axes_lim[0]) + ax.set_ylim(self.axes_lim[1]) + ax.set_zlim(self.axes_lim[2]) + start: np.ndarray + end: np.ndarray + for start, end, lw in zip(self.data[1:-1], self.data[2:], self.lw, strict=True): + self.lines.append( + cast( + list[Line3D], + ax.plot([start[0], end[0]], [start[1], end[1]], [start[2], end[2]], linewidth=lw), + ) + ) + + def update(self, row: int): + """Receives the frames from FuncAnimation with updated line data. Draw the booms as lines by data rows.""" + time = self.data[0][row] + for i in range(len(self.data) - 2): # all lines + self.lines[i][0].set_data_3d( + [self.data[i + 1][row][0], self.data[i + 2][row][0]], + [self.data[i + 1][row][1], self.data[i + 2][row][1]], + [self.data[i + 1][row][2], self.data[i + 2][row][2]], + ) + _ = plt.title(f"{self.title} ({time:.1f})", loc="left") + + def do_animation(self): + """Do the animation. It generates frames and updates the animation plot.""" + _ = FuncAnimation( + self.fig, + self.update, # type: ignore[reportArgumentType, arg-type] # ok as long as `blit=False` + frames=range(len(self.data[0])), + init_func=self.init_fig, # type: ignore[reportArgumentType, arg-type] # ok as long as `blit=False` + interval=self.interval, + repeat=False, + blit=False, + cache_frame_data=False, + ) + plt.show() diff --git a/tests/test_animation.py b/tests/test_animation.py new file mode 100644 index 0000000..0afa3af --- /dev/null +++ b/tests/test_animation.py @@ -0,0 +1,185 @@ +import logging +from typing import Any, Generator + +import matplotlib.pyplot as plt +import numpy as np +import pytest # noqa: F401 +from component_model.utils.controls import Controls +from matplotlib.animation import FuncAnimation + +from py_crane.animation import AnimateCrane, AnimatePlayBackLines +from py_crane.boom import Boom +from py_crane.crane import Crane + +logger = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO) +np.set_printoptions(precision=4, suppress=True) + + +@pytest.fixture +def crane(scope: str = "session", autouse: bool = True): + return _crane() + + +def _crane(): + crane = Crane() + _ = crane.add_boom( + name="pedestal", + description="The crane base, on one side fixed to the vessel and on the other side the first crane boom is fixed to it. The mass should include all additional items fixed to it, like the operator's cab", + mass=2000.0, + mass_center=(0.5, -1, 0.8), + boom=(3.0, 0, 0), + ) + _ = crane.add_boom( + name="boom1", + description="The first boom. Can be lifted", + mass=200.0, + mass_center=0.5, + boom=(10.0, np.radians(90), 0), + ) + _ = crane.add_boom( + name="wire", + description="The wire fixed to the last boom. Flexible connection", + mass=50.0, # so far basically the hook + mass_rng=(50, 2000), + mass_center=1.0, + boom=(0.5, np.radians(90), 0), + q_factor=10.0, + ) + return crane + + +def animate_sequence(crane: Crane, seq: tuple[tuple[Boom, float], ...] = (), nSteps: int = 10): + """Generate animation frames for a sequence of rotations. To be used as 'update' argument in FuncAnimation. + A sequence element consists of a boom and an angle, which then is rotated in nSteps. + """ + for b, a in seq: + if b.name == "pedestal": # azimuthal movement + db = np.array((0, 0, np.radians(a / nSteps)), float) + else: # polar movement + db = np.array((0, np.radians(a / nSteps), 0), float) + for _ in range(nSteps): + b.boom_setter(list(b.boom + db)) + # update all subsystem center of mass points. Need to do that from last boom! + crane.calc_statics_dynamics(dt=None) + yield (crane) + + +# @pytest.mark.skip("Animate crane movement") +def test_animation_sequence(crane: Crane, show: bool = False): + if not show: # if nothing can be shown, we do not need to run it + return + + def init(): + """Perform the needed initializations.""" + ax.set_xlim(-10, 10) + ax.set_ylim(-10, 10) + ax.set_zlim(0, 10) # type: ignore [attr-defined] ## according to matplotlib recommendations + for b in crane.booms(): + lw = {"pedestal": 15, "rope": 2}.get(b.name, 7) + lines.append( + ax.plot( + [b.origin[0], b.end[0]], + [b.origin[1], b.end[1]], + [b.origin[2], b.end[2]], + linewidth=lw, + ) + ) + + def update(crane: Crane): + """Based on the updated first boom (i.e. the fixation), draw any desired data""" + for i, b in enumerate(crane.booms()): + lines[i][0].set_data_3d( + [b.origin[0], b.end[0]], + [b.origin[1], b.end[1]], + [b.origin[2], b.end[2]], + ) + + f, p, b1, r = list(crane.booms()) + fig = plt.figure(figsize=(15, 15), layout=None) # "constrained") + ax = plt.axes(projection="3d") # , data=line) + lines: list[Any] = [] + + _ = FuncAnimation( + fig, + update, # type: ignore ## this is a function! + frames=animate_sequence(crane, seq=((p, -90), (b1, -45))), # yields crane object as frame + init_func=init, # type: ignore ## this is a function! + interval=1000, + blit=False, + cache_frame_data=False, + ) + plt.title("Crane animation", loc="left") + plt.show() + # assert np.allclose(r.origin, (0, -15 / sqrt(2), 3 + 15 / sqrt(2))) + + +def movement(crane: Crane, dt: float = 0.01, t_end: float = 10.0) -> Generator[tuple[float, Crane], None, None]: + """Create movement of the crane through definition and usage of Controls. + Generator function. Returns a `Generator` which steps through time and sequentially yields updated frames in the form of tuple (time, crane) objects. + time is defined global as a simple way to draw the current time together with the title. + """ + # initial definition of controls and start values + controls = Controls(limit_err=logging.WARNING) # CRITICAL) + f, p, b1, r = list(crane.booms()) + controls.append("turn", (None, (-0.31, 0.31), (-0.16, 0.16))) # free rotation, max 1 turn/20sec, 2sec to max + controls.append("luff", ((0, 1.58), (-0.18, 0.09), (-0.09, 0.05))) # 90 deg, 5/-2.5 deg/sec, 2sec to max + controls.append("boom", ((8, 50), (-0.2, 0.1), (-0.1, 0.05))) # 8m..50m, 0.1/-0.2 m/sec, 2sec to max + controls.append("wire", ((0.5, 50), (-0.1, 1.0), (-0.05, 0.1))) # 0.5m..50m, -0.1/1 m/sec, 2sec to max + controls.current[2][0] = 8.0 # b1 starts with 8m + controls.current[1][0] = np.radians(90) # b1 starts at 90 deg + controls.current[3][0] = 0.5 # wire length starts 0.5m + + # From time 0 we set three goals + controls.setgoal("turn", 0, np.radians(90), 0.0) # turn pedestal 90 deg + controls.setgoal("luff", 0, np.radians(45), 0.0) # luff boom to 45 deg + controls.setgoal("boom", 1, 0.1, 0.0) # increase length 0.1m/s + for time in np.linspace(0.0, t_end, int(t_end / dt) + 1): + if time > 10 and controls.goals[3] is None: # Start to increase wire length with 1m/s + controls.setgoal("wire", 1, 1.0, 10.0) + controls.step(time, dt) + if controls.goals[3] is not None: + r.boom_setter((controls.current[3][0], None, None)) + if controls.goals[1] is not None or controls.goals[2] is not None: + b1.boom_setter((controls.current[2][0], controls.current[1][0], None)) + if controls.goals[0] is not None: + p.boom_setter((None, None, controls.current[0][0])) + crane.do_step(time, dt) + yield (time + dt, crane) + + +def test_animation_control(crane: Crane, show: bool = False): + if not show: # if nothing can be shown, we do not need to run it + return + ani = AnimateCrane(crane, movement, dt=0.1, t_end=20) # type: ignore ## It is a Generator! + ani.do_animation() + + +def test_playback_lines(show: bool = False): + data = ( + np.array((0, 1, 2, 3), float), # time + np.zeros(12).reshape((4, 3)), # base + np.array(((0, 0, 1), (0, 1, 2), (0, 2, 3), (0, 3, 4)), float), # end of base + np.array(((1, 0, 1), (2, 1, 2), (3, 2, 3), (4, 3, 4)), float), # end of next + np.array(((1, 0, 0), (2, 1, 0), (3, 2, 0), (4, 3, 0)), float), # 'wire' + ) + ani = AnimatePlayBackLines( + data=data, lw=(10, 2, 1), figsize=(10, 10), interval=1000, title="Test of AnimatePlayBackLines" + ) + assert np.allclose(ani.axes_lim, [[-1, 5], [-1, 5], [-1, 5]]) + if show: + ani.do_animation() + + +if __name__ == "__main__": + retcode = 0 # pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) + assert retcode == 0, f"Non-zero return code {retcode}" + logging.basicConfig(level=logging.DEBUG) + parsolog = logging.getLogger("parso") + parsolog.setLevel(logging.WARNING) + pillog = logging.getLogger("PIL") + pillog.setLevel(logging.WARNING) + + # test_animation_sequence(_crane(), show=True) + test_animation_control(_crane(), show=True) + # test_playback_lines(show=True) diff --git a/tests/test_crane.py b/tests/test_crane.py index 1853abf..e031cb9 100644 --- a/tests/test_crane.py +++ b/tests/test_crane.py @@ -1,18 +1,16 @@ import logging from math import cos, radians, sin, sqrt -from typing import Any, Callable, Generator, Sequence, cast +from typing import Callable, Sequence, cast import matplotlib.pyplot as plt import numpy as np import pytest from component_model.analytic import sine_fit from component_model.utils.analysis import extremum_series -from component_model.utils.controls import Controls from component_model.utils.transform import rot_from_spherical, rot_from_vectors -from matplotlib.animation import FuncAnimation from scipy.spatial.transform import Rotation as Rot -from py_crane.animation import AnimateCrane, AnimatePendulum +from py_crane.animation import AnimatePendulum from py_crane.boom import Boom from py_crane.crane import Crane @@ -615,113 +613,6 @@ def test_c_m(crane: Crane, show: bool = False): p.boom_setter((None, None, radians(-20))) -def animate_sequence(crane: Crane, seq: tuple[tuple[Boom, float], ...] = (), nSteps: int = 10): - """Generate animation frames for a sequence of rotations. To be used as 'update' argument in FuncAnimation. - A sequence element consists of a boom and an angle, which then is rotated in nSteps. - """ - for b, a in seq: - if b.name == "pedestal": # azimuthal movement - db = np.array((0, 0, radians(a / nSteps)), float) - else: # polar movement - db = np.array((0, radians(a / nSteps), 0), float) - for _ in range(nSteps): - b.boom_setter(list(b.boom + db)) - # update all subsystem center of mass points. Need to do that from last boom! - crane.calc_statics_dynamics(dt=None) - yield (crane) - - -# @pytest.mark.skip("Animate crane movement") -def test_animation(crane: Crane, show: bool = False): - if not show: # if nothing can be shown, we do not need to run it - return - - def init(): - """Perform the needed initializations.""" - ax.set_xlim(-10, 10) - ax.set_ylim(-10, 10) - ax.set_zlim(0, 10) # type: ignore [attr-defined] ## according to matplotlib recommendations - for b in crane.booms(): - lw = {"pedestal": 15, "rope": 2}.get(b.name, 7) - lines.append( - ax.plot( - [b.origin[0], b.end[0]], - [b.origin[1], b.end[1]], - [b.origin[2], b.end[2]], - linewidth=lw, - ) - ) - - def update(crane: Crane): - """Based on the updated first boom (i.e. the fixation), draw any desired data""" - for i, b in enumerate(crane.booms()): - lines[i][0].set_data_3d( - [b.origin[0], b.end[0]], - [b.origin[1], b.end[1]], - [b.origin[2], b.end[2]], - ) - - f, p, b1, r = list(crane.booms()) - fig = plt.figure(figsize=(15, 15), layout=None) # "constrained") - ax = plt.axes(projection="3d") # , data=line) - lines: list[Any] = [] - - _ = FuncAnimation( - fig, - update, # type: ignore ## this is a function! - frames=animate_sequence(crane, seq=((p, -90), (b1, -45))), # yields crane object as frame - init_func=init, # type: ignore ## this is a function! - interval=1000, - blit=False, - cache_frame_data=False, - ) - plt.title("Crane animation", loc="left") - plt.show() - # assert np.allclose(r.origin, (0, -15 / sqrt(2), 3 + 15 / sqrt(2))) - - -def movement(crane: Crane, dt: float = 0.01, t_end: float = 10.0) -> Generator[tuple[float, Crane], None, None]: - """Create movement of the crane through definition and usage of Controls. - Generator function. Returns a `Generator` which steps through time and sequentially yields updated frames in the form of tuple (time, crane) objects. - time is defined global as a simple way to draw the current time together with the title. - """ - # initial definition of controls and start values - controls = Controls(limit_err=logging.WARNING) # CRITICAL) - f, p, b1, r = list(crane.booms()) - controls.append("turn", (None, (-0.31, 0.31), (-0.16, 0.16))) # free rotation, max 1 turn/20sec, 2sec to max - controls.append("luff", ((0, 1.58), (-0.18, 0.09), (-0.09, 0.05))) # 90 deg, 5/-2.5 deg/sec, 2sec to max - controls.append("boom", ((8, 50), (-0.2, 0.1), (-0.1, 0.05))) # 8m..50m, 0.1/-0.2 m/sec, 2sec to max - controls.append("wire", ((0.5, 50), (-0.1, 1.0), (-0.05, 0.1))) # 0.5m..50m, -0.1/1 m/sec, 2sec to max - f, p, b1, r = list(crane.booms()) - controls.current[2][0] = 8.0 # b1 starts with 8m - controls.current[1][0] = np.radians(90) # b1 starts at 90 deg - controls.current[3][0] = 0.5 # wire length starts 0.5m - - # From time 0 we set three goals - controls.setgoal("turn", 0, np.radians(90), 0.0) # turn pedestal 90 deg - controls.setgoal("luff", 0, np.radians(45), 0.0) # luff boom to 45 deg - controls.setgoal("boom", 1, 0.1, 0.0) # increase length 0.1m/s - for time in np.linspace(0.0, t_end, int(t_end / dt) + 1): - if time > 10 and controls.goals[3] is None: # Start to increase wire length with 1m/s - controls.setgoal("wire", 1, 1.0, 10.0) - controls.step(time, dt) - if controls.goals[3] is not None: - r.boom_setter((controls.current[3][0], None, None)) - if controls.goals[1] is not None or controls.goals[2] is not None: - b1.boom_setter((controls.current[2][0], controls.current[1][0], None)) - if controls.goals[0] is not None: - p.boom_setter((None, None, controls.current[0][0])) - crane.do_step(time, dt) - yield (time + dt, crane) - - -def test_animation_control(crane: Crane, show: bool = False): - if not show: # if nothing can be shown, we do not need to run it - return - ani = AnimateCrane(crane, movement, dt=0.1, t_end=20) # type: ignore ## It is a Generator! - ani.do_animation() - - def show_crane(_crane: Crane, markCOM: bool = True, markSubCOM: bool = True, title: str | None = None): # update all subsystem center of mass points. Need to do that from last boom! _crane.calc_statics_dynamics(dt=None) From 0106873a35ef4eb6fe20b7e4a63efa5570a500a2 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Thu, 5 Feb 2026 10:51:00 +0100 Subject: [PATCH 02/53] Extended data type of playback data to Sequence, so that explicit cast to tuple is not necessary when using AnimatePlayBackLines --- examples/MobileCrane.fmu | Bin 917159 -> 917110 bytes src/py_crane/animation.py | 9 ++++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/MobileCrane.fmu b/examples/MobileCrane.fmu index 73b315edb8ae8e244f1d42cfe4feb86f814b7514..7c456494287849527971877687e4d92447383432 100644 GIT binary patch delta 663 zcmY+BO=uHQ5XX1l)}&1{dPpRVipDP#Y|EM^*(4}Elnp|}G*m)c+d%VGiW0OcYCxz6 z22$`KbSHRGDxxRxl5XflV<{+zO6#E@f?r6F_EM8od^Ha<2lxR$D|K zS^bzF#5)R1JKU1!NyOtZug~pvxx|<+<&<3Ul&>eAki?!;438rOj6T21>vwt79Kx3& zx=Bs1!Ak@D=Lzti2 zv7x?Jz$2VqYACS^M}=F<4Ry6jJD)Yx(oeAAN)>h!>w64qun%`=;O3uWzXpS(V8J@8 zz1BeFIn*^6B9{g>YMB~{WPNl)ys3e053|G>UhXh+9LKOznlZ&Eu|o(dpu@?k83c=n zg}&s&b{?XqjGi3TgI9b>>7k_TL46UTpadQz31 zo&ho!x+6nc240haL~rd2pOwSOhPU%u0gltLiZTs$HBWdrydoYvZF=24dsx zn5s-nD3dO?+db%lI-zExH-f_NnrJNH5?EH7nJ1@7M7@(=seAu%wb4tZ56AhwP8 zhKEKZv>IC`wu%!lynqN$beNYa{$~Icz9Egnxzc0(28@6PF6mNmqI)E*HRx ztG(2O&LXtZ$(u}?B8?a=f=I6~)S~A_Xs37AD8|=lGa5_K%4_H?L5TmVyMuVY1b!-S zQ1ot47k<|Z@@Ox^CAziWpo=Z|pbQ@Bb=RUVWj66~j{#?T3=TYBVY?lXTJB*54%zif zh!@ta7LMa|Y>XXC@mXB2KqtS6O;zZk2fEG3cbf&gR|QMU9P3W6EF8rn+hF;%TmOZt{T!#$_}7FKpTcw7;4s?qz~rA% C8tA$J diff --git a/src/py_crane/animation.py b/src/py_crane/animation.py index a330594..13efa49 100644 --- a/src/py_crane/animation.py +++ b/src/py_crane/animation.py @@ -1,6 +1,6 @@ # pyright: reportUnknownMemberType=false import logging -from typing import Any, Generator, Protocol, TypeAlias, cast +from typing import Any, Generator, Protocol, Sequence, TypeAlias, cast import matplotlib.pyplot as plt import numpy as np @@ -275,8 +275,8 @@ class AnimatePlayBackLines(object): def __init__( self, - data: tuple[np.ndarray,...], - lw: tuple[int, ...] | None = None, + data: Sequence[np.ndarray], + lw: Sequence[int] | None = None, figsize: tuple[int, int] = (8, 8), interval: int = 200, title: str = "Crane animation", @@ -291,14 +291,13 @@ def __init__( self.lines: list[list[Line3D]] = [] self.fig: Figure = plt.figure(figsize=self.figsize, layout=None) # "constrained") - def _get_axes_lim(self, data: tuple[np.ndarray,...]) -> list[list[int]]: + def _get_axes_lim(self, data: Sequence[np.ndarray]) -> list[list[int]]: """Get the limits of time (data[0]) and all points. Time shall be sorted in ascening order.""" length = len(data[0]) assert all(len(data[i + 1]) == length for i in range(len(data) - 1)), ( f"Columns of 'data' not equal length {length}" ) assert all(data[i + 1].shape == (length, 3) for i in range(len(data) - 1)), "Data points shall be 3D" - t_minmax = (np.min(data[0]), np.max(data[0])) assert all(data[0][i] < data[0][i + 1] for i in range(length - 1)), "Column 0 (time) is unsorted" axes_lim = [[int(np.min(data[1][k])) - 1, int(np.max(data[1][k])) + 1] for k in range(3)] for i in range(len(data) - 2): From 7c0367858613083984bdcd21105bf7805bf1d98c Mon Sep 17 00:00:00 2001 From: Eisinger Date: Sat, 21 Feb 2026 20:26:04 +0100 Subject: [PATCH 03/53] Updated pendulum action and much extended tests releated to pendulum --- examples/MobileCrane.fmu | Bin 917110 -> 921686 bytes src/py_crane/animation.py | 19 +- src/py_crane/boom.py | 144 +++++--- src/py_crane/boom_fmu.py | 2 +- src/py_crane/crane.py | 4 +- src/py_crane/{enum.py => enums.py} | 0 tests/test_animation.py | 24 +- tests/test_crane.py | 535 ++++++++++++++++++----------- tests/test_crane_configurations.py | 221 ++++++++++++ tests/test_enum.py | 2 +- tests/test_mobile_crane_FMU.py | 6 +- 11 files changed, 693 insertions(+), 264 deletions(-) rename src/py_crane/{enum.py => enums.py} (100%) create mode 100644 tests/test_crane_configurations.py diff --git a/examples/MobileCrane.fmu b/examples/MobileCrane.fmu index 7c456494287849527971877687e4d92447383432..cf584796843048ce74231f1fc7e236947393f67f 100644 GIT binary patch delta 7275 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 delta 3506 zcmb7Gdr(x@8Nc_OU09aA7f{}y9zbMwS#X!PG$5!LUsV{5#s}iMdlz(_oQMb$1%!}G|is|`>_~* z`W>Mo3qHPv8%-VV?v|h@&;_%H@ly~S#?vis*-Ju_(ixJ872X)eGm|@mTiqcq>6Jo( zB2Op~4*TU+xIBzE3elq4>)0JP;n4;uH*h^%xPiNQFMMSLOYox+Tm_$w;MU2oO`qTw z;f0%c?sn$N>Cl3>93&W$y8MC8FsUKS1G40JY!%Fe{(ONDQ5>X;_+^T=OA3W0ueIGD zR;<1LpjGjANMX9`Zl`Nl3Hd!r&8kqRWV6|-b$jN+@0*Mh$!*Onqyjlu9QL=$)-D^V zsUc3nvN0i>0JYxLY=g#4e0nT3V7TEr!X%S@xhP-_g@(XZ?3cY#w-wGW!?Uxr6MpHw z0PoOAr{U0#$|2vFF_?^u#xC|HnPlyt>Pnc2YR+e&`o&-$6iL03u5ruXSEBKe}+5q3d?GU_!_s6+)uWkP4?6#eD8?V}J z=-jhOPpxR!F!N6btY@k?j?oM_FoaT7%MKJ<2s?J6epqlE{YG7M0uAv-zpOMj!@(cm zRJHsh%I6IX1FSoRX2F3skqF;8h4wF_N>TJnC?K`X;j3QsPbvF zi;rjR&3@FCy6X(WS)zT*f(`X*!R!kw6OjG%7hzZC>3^( zp?J8q7pKAWm$4a6JwSPnR90y+_0tEa!l=&t934u5yJILFu1~?GP#?xAa7E??wQ?#x zgrFuB%f_(O?kk3)srcOZr!NinXKT_HS=!ptF3GgH*cC%a9=$07Ay_erQVU2pK$JGO zLKmrrc-%7CED^WQCwY`kw<39ow==|U!BZ9r^u$AC)*{2-A0mtTMH==Aknjx7E0|1$ zt(pap19Fki-%af|2Lc_m6WjcrHqxQ)&c_Mys;L~WHbHw0UM{rw+@5OUYY$KeUx#Z` z_!RYC4R#>|+koCWTsy^XbrrTV)@E1qIFYZ-NP(+$SO8RyGjgj-*;sf(EnbD#$+(VI z61sHJWA{MfGCTt^>+v+B<|&fXniUz17$cjG#Vsr(n(Rqa<|0JwHH5~VJm7@mP|5ff zHDp-#h=%kzi&r<+ZBzQH$u&3Iw0$ z)9iZY=zSU8X|svO)23@x+So-eYloa?|uV! zWWq1sz!~cGmvQQJHRmn5!u}zYW{~904t3=RcrP!?=Sny1{pMe9cw#xu6AO)h#=E97*G+sGE}wyJH2~uv*1h1N3206SEC| zj07{hJKc~Dug^3TsP|_W)-|%fTsxbB21-UsF_dB{nJC3kGE<7DWKo-f!rDrv4aW}) zwc5vD4hu_6RI=gLA76SF>W&DzNO`5R!t3#Du9{O`URLJZJjYj3S?2N0DfM_Oou$6b z@YxYzUc9q>m2*yYSygpegx1q|764~r$}i=`S9 zk>{IWu|c$>fm^y@^qLu7F^E<)I4*Dwo8UIn_*D`l#faJ9GKwzrLWR!s8^v<;{gaOv z6J#IZ&2ZC54Q5hc^RpJJ zt3|T+phSyScDkG$xH+IJEbpNYPHh}@R<%Qy^vBU-ZGTQ@eitXEkthBgOt{fJ9r`vj znZ+59XBHi3-v!1Llj!%B<1(X6E`7wbQs5_MacWM~T+!K@9;N!yYMQell)f*1Ts3OY zqEr|4YO2ZF;K5hp#UkAB)}+V+uf>bmNzq7Fy{9W^QwSr>=3AG?Z3--634UjS3tGgq zoah1NxWXs^$?s!0m2b!aID4$ zToS|ztgIa8Y@+DIW6k4SpGZNm`6+3-oX)DtDC03u{k*i%0Pdh66E2Cg$VWE9KSgl~ z{$!a6hBulBv?YiZ&BNfY^Bdx+C8PAmJe(aE+$+G_hlLC{L3On`dpkkQ!f2lW4-X4@ k@Q{kh#1OZx<2Xk8xphDN{EB^GPZV>_-2%tm8l?;OZ@nir3IG5A diff --git a/src/py_crane/animation.py b/src/py_crane/animation.py index 13efa49..d31003c 100644 --- a/src/py_crane/animation.py +++ b/src/py_crane/animation.py @@ -270,7 +270,6 @@ class AnimatePlayBackLines(object): figsize (tuple) = (8,8): figure size in inches interval (int) = 200: Time in milliseconds between animation frames title (str) = "Lines re-player": Figure title - **kwargs: optional additional keyword arguments to generator function """ def __init__( @@ -281,6 +280,7 @@ def __init__( interval: int = 200, title: str = "Crane animation", ): + assert len(data) > 0, "No data. Cannot animate." self.data = data self.lw = (1,) * (len(data) - 1) if lw is None else lw self.times = data[0] @@ -291,19 +291,19 @@ def __init__( self.lines: list[list[Line3D]] = [] self.fig: Figure = plt.figure(figsize=self.figsize, layout=None) # "constrained") - def _get_axes_lim(self, data: Sequence[np.ndarray]) -> list[list[int]]: + def _get_axes_lim(self, data: Sequence[np.ndarray]) -> list[list[float]]: """Get the limits of time (data[0]) and all points. Time shall be sorted in ascening order.""" length = len(data[0]) assert all(len(data[i + 1]) == length for i in range(len(data) - 1)), ( f"Columns of 'data' not equal length {length}" ) - assert all(data[i + 1].shape == (length, 3) for i in range(len(data) - 1)), "Data points shall be 3D" + assert all(data[i].shape == (length, 3) for i in range(1, len(data))), "Data points shall be 3D" assert all(data[0][i] < data[0][i + 1] for i in range(length - 1)), "Column 0 (time) is unsorted" - axes_lim = [[int(np.min(data[1][k])) - 1, int(np.max(data[1][k])) + 1] for k in range(3)] - for i in range(len(data) - 2): + axes_lim = [[float("inf"), float("-inf")], [float("inf"), float("-inf")], [float("inf"), float("-inf")]] + for i in range(len(data) - 1): for k in range(3): - axes_lim[k][0] = min(axes_lim[k][0], int(np.min(data[i + 2]) - 1)) - axes_lim[k][1] = max(axes_lim[k][1], int(np.max(data[i + 2]) + 1)) + axes_lim[k][0] = min(axes_lim[k][0], int(np.min(data[i + 1]) - 1)) + axes_lim[k][1] = max(axes_lim[k][1], int(np.max(data[i + 1]) + 1)) return axes_lim def init_fig(self) -> None: @@ -314,7 +314,10 @@ def init_fig(self) -> None: ax.set_zlim(self.axes_lim[2]) start: np.ndarray end: np.ndarray - for start, end, lw in zip(self.data[1:-1], self.data[2:], self.lw, strict=True): + + for i in range(1, len(self.data) - 1): + start, end, lw = self.data[i][0], self.data[i + 1][0], self.lw[i - 1] + # start, end, lw in zip(self.data[1:-1], self.data[2:], self.lw, strict=True): self.lines.append( cast( list[Line3D], diff --git a/src/py_crane/boom.py b/src/py_crane/boom.py index 6e579e3..2b24794 100644 --- a/src/py_crane/boom.py +++ b/src/py_crane/boom.py @@ -14,7 +14,7 @@ from scipy.integrate import solve_ivp from scipy.spatial.transform import Rotation as Rot -from py_crane.enum import Change +from py_crane.enums import Change if TYPE_CHECKING: import py_crane.crane @@ -30,7 +30,8 @@ class Boom(object): modelled as stiff rod with length, mass and mass-center. .. assumption:: All booms of the crane are completely stiff, excluding bending, stretching and vibration - modes of motion. This includes also loose conncetions, i.e. the wire is assumed straight and without stretch. + modes of motion. This includes also loose connections, + i.e. the wire is assumed straight and without stretch and torsion. .. assumption:: Per default, the mass center of a boom is located at the geometric center of the boom. This can be changed using the tuple form of the :py:attr:`mass_center` parameter. @@ -55,9 +56,12 @@ class Boom(object): The spherical angles with respect to the previous boom can only be changed through new settings. loose connection boom(:py:attr:`q_factor` >0): The joint to the previous boom is loose and the boom can move freely in all spherical directions, - i.e. it represents a wire, exhibiting pendulum movement with respect to the local center of mass. + i.e. it represents a wire (with load), exhibiting pendulum movement with respect to the local center of mass. :py:attr:`q_factor` serves at the same time as a definition of the pendulum damping. + .. limitation:: The wire is always streched (stiff) and free-falling movement of the load + (e.g. due to high accelerations of parent boom or length changes) lead to error messages. + Basic boom movements are length change, i.e. :py:meth:`.boom_setter` ( (new-length, None, None)) @@ -116,7 +120,7 @@ class Boom(object): while the active work variables are the cartesian origin, direction and length q_factor (float)=0.0: optional possibility to implement a loose connection between booms. - * if q_factor=0.0, the connection to the parent boom is stiff according to the boom angle setting + * if q_factor=0.0, the connection to the parent boom is stiff according to the boom angle settings * if q_factor > 0, the crane boom (the wire) is implemented as a stiff rod with a loose connection hanging from the parent boom. * The q_factor denotes the dimensionless quality factor (energy stored/energy lost per radian), @@ -131,8 +135,8 @@ class Boom(object): :math:`\tau`: characteristic damping time :math:`\gamma`: pendulum damping. This does not cover critically and over-damped systems, which are in any case not realistic for crane wires). - In general the `q_factor` is a fixed parameter, but for testing purposes :py:meth:`.damping` - is included, which allows changing of related parameters in a consistent way. + In many systems the `q_factor` is a fixed parameter, but for testing purposes and if the wire length changes, + :py:meth:`.damping` is included, which allows changing of related parameters in a consistent way. limits (tuple): Optional tuple of control limits for active boom control. See ControlLimits @@ -154,6 +158,7 @@ def __init__( q_factor: float = 0.0, limits: tuple[float, float, float] | None = None, tolerance: float = 1e-5, + additional_checks: bool = False, **kwargs: Any, # for compatibility with derived classes ): self._model: py_crane.crane.Crane = model @@ -163,11 +168,12 @@ def __init__( self.description: str = description self.q_factor: float = q_factor self.tolerance: float = tolerance + self.additional_checks = additional_checks self.direction: TVector = np.array((0.0, 0.0, -1.0), dtype=np.float64) # default for non-fixed booms - # velocity of CoM relative to .origin (for pendulum) - self.r_v: TVector = np.array((0.0, 0.0, 0.0), dtype=np.float64) - # acc. of CoM relative to .origin (for pendulum) - self.r_acc: TVector = np.array((0.0, 0.0, 0.0), dtype=np.float64) + self.r_v: TVector = np.array( + (0.0, 0.0, 0.0), dtype=np.float64 + ) # velocity of CoM relative to .origin (pendulum) + self.r_acc: TVector = np.array((0.0, 0.0, 0.0), dtype=np.float64) # acc. of CoM relative to .origin (pendulum) # default animation line width of boom. Can be changed, e.g. 10 pedestal and 2 for wire. # self.animationLW: int = 5 self.origin: TVector @@ -189,7 +195,7 @@ def __init__( # rot denotes the rotation which turns (0,0,1) into the direction self._rot: Rot = Rot.identity() if self.anchor0 is None else self.anchor0._rot * rot_from_spherical(boom[1:]) self.direction = self._rot.apply((0.0, 0.0, 1.0)) - self._new_len: float | None # place holder for length changes + self._new_len: float | None = None # place holder for length changes # self._c_m = np.array( (0.0, 0.0, 0.0), dtype=np.float64) # just to make _c_m known. Updated by method c_m # save the current value, running method self.c_m self._c_m: TVector = self.c_m @@ -304,8 +310,8 @@ def end(self) -> TVector: @property def c_m(self) -> TVector: """Updates and returns the local center of mass point relative to self.origin.""" - self._c_m = self.mass_center[0] * self.length * self.direction + np.array( - (self.mass_center[1], self.mass_center[2], 0), float + self._c_m = self.mass_center[0] * self.length * self.direction + ( + np.array((self.mass_center[1], self.mass_center[2], 0), float) ) return self._c_m @@ -323,15 +329,15 @@ def damping(self, q_factor: float | None = None, damping_time: float | None = No if self.q_factor != 0.0: assert self.anchor0 is not None, "Flexible first booms are so far not implemented" # pre-calculated loss term used in .pendulum() + length = self.length if self._new_len is None else (self.length + self._new_len) / 2 if q_factor is not None: self.q_factor = q_factor - self._damping_time = sqrt(self.length / 9.81 * (self.q_factor**2 + 0.25)) + self._damping_time = sqrt(length / 9.81 * (self.q_factor**2 + 0.25)) elif damping_time is not None: # new damping time. Change q_factor - self.q_factor = sqrt(damping_time**2 * 9.81 / self.length - 0.25) + self.q_factor = sqrt(damping_time**2 * 9.81 / length - 0.25) self._damping_time = damping_time else: self._damping_time = 0.0 - self._new_len = None # store wire length changes, so that they can be affectuated during .calc_statics_dynamics return self._damping_time def update_child(self, change: Change = Change.ALL): @@ -393,9 +399,7 @@ def calc_statics_dynamics(self, dt: float | None = None): dt (float)=None: for dynamic calculations, the time for the movement must be provided and is then used to calculate torque and force """ - if ( - self.q_factor != 0.0 - ): # non-fixed boom, which might be moving or there might be pending origin/length changes + if self.q_factor != 0.0: # non-fixed boom. Might be moving or there might be pending origin/length changes if dt is None: self._c_m_sub = (self.mass, self.origin + self.c_m) self._pendulum_instantaneous() @@ -442,6 +446,34 @@ def _pendulum_instantaneous(self): self.origin = self.anchor0.end self._rot = self.anchor0.rot() * rot_from_spherical(self.boom) + @staticmethod + def _energy(mass: float, c_m: np.ndarray, speed: np.ndarray): + """Calculate the current energy in the pendulum. Should not be used for other booms. + + Implemented as staticmethod to not get many duplicates of the function made. + + Args: + mass (float): mass of the boom (load) + c_m (float): vector from origin to center of mass + speed: the current speed of the c_m + """ + return mass * ( + 9.81 * (np.linalg.norm(c_m) - np.dot(c_m, np.array((0, 0, -1), float))) + 0.5 * np.dot(speed, speed) + ) + + @staticmethod + def _angular_momentum(mass: float, c_m: np.ndarray, speed: np.ndarray): + """Calculate the current energy in the pendulum. Should not be used for other booms. + + Implemented as staticmethod to not get many duplicates of the function made. + + Args: + mass (float): mass of the boom (load) + c_m (float): vector from origin to center of mass + speed: the current speed of the c_m + """ + return mass * np.cross(c_m, speed) + def pendulum(self, dt: float): r"""For a non-stiff connection, if the _c_m is not exactly below origin, the _c_m acts as a damped pendulum. @@ -484,7 +516,9 @@ def pendulum(self, dt: float): """ - def ivp_fun(t: float, y: np.ndarray, r2: float, g: np.ndarray, dr_dt: np.ndarray | None, l_fac: float | None): + def ivp_fun( + t: float, y: np.ndarray, r2: float, g: np.ndarray, dr_dt: np.ndarray | None, l0: float, dl_dt: float | None + ): """Solve the initial value problem of the pendulum dr/dt = f(t,r), r(t=0) = r0. Without losses. Args: @@ -494,21 +528,17 @@ def ivp_fun(t: float, y: np.ndarray, r2: float, g: np.ndarray, dr_dt: np.ndarray r2 (float): the squared pendulum radius with respect to COM g (float): gravitational acceleration as ndarray dr_dt (ndarray): Optional movement of the origin through dt - l_fac (float): Optional change of wire length through dt: l(t) = l0*(1+l_fac)*t + l0 (float): start length of wire. Used only if wire length changes + dl_dt (float): Optional change of wire length through dt: l(t) = l0 + dl_dt* t """ - r = y[:3] if dr_dt is None else y[:3] - dr_dt * t - v = y[3:] # if dr_dt is None else y[3:] - dr_dt - if l_fac is not None: - fac = 1.0 + l_fac * t - r *= fac - r2 *= fac * fac - # print(f"IVP@{t}: {dr_dt}, {np.dot(r, dr_dt)}") - term1 = np.cross(r, np.cross(r, g)) - term2 = np.dot(v, v) * r - self.r_acc = -((term1 + term2) / r2) - # print(f"IVP@{t}: {cartesian_to_spherical(r, True)} -> {cartesian_to_spherical(self.r_acc,True)[1]}") - # if dr_dt is not None: - # v += dr_dt + r = y[:3] + v = y[3:] if dr_dt is None else y[3:] - dr_dt + if dl_dt is not None: + l0 += dl_dt * t + r2 = l0 * l0 + tangential = np.cross(r, np.cross(r, g)) + centripetal = np.dot(v, v) * r + self.r_acc = -((tangential + centripetal) / r2) return np.append(v, self.r_acc) assert self.anchor0 is not None, f"pendulum() called on fixation {self.name}" @@ -523,15 +553,22 @@ def ivp_fun(t: float, y: np.ndarray, r2: float, g: np.ndarray, dr_dt: np.ndarray ) and self.mass_center[0] * self.length > 1e-10 # pendulum length not too short ): + R = self.mass_center[0] * self.length # pendulum radius wrt. center of mass + if self.additional_checks: + e0 = Boom._energy(self.mass, self._c_m, self.r_v) + lz0 = Boom._angular_momentum(self.mass, self._c_m, self.r_v) if np.allclose(self.origin, self.anchor0.end): dr_dt = None else: dr_dt = (self.anchor0.end - self.origin) / dt # translation in time interval if self._new_len is None: - l_fac = None + dl_dt = None else: - l_fac = (self._new_len / self.length - 1.0) / dt - R = self.mass_center[0] * self.length # pendulum radius wrt. center of mass + dl_dt = (self._new_len - self.length) / dt + self.damping(q_factor=self.q_factor) # re-calculate due to new length + self.boom[0] = self._new_len + self._new_len = None + if R > 1e-6: r2 = R * R g = np.array((0.0, 0.0, -9.81), float) @@ -541,14 +578,16 @@ def ivp_fun(t: float, y: np.ndarray, r2: float, g: np.ndarray, dr_dt: np.ndarray ivp_fun, t_span=[0, dt], y0=np.append(r, v), - method="RK45", # BDF', - args=(r2, g, dr_dt, l_fac), # type: ignore[reportArgumentType] ## according to spec + method="RK45", #'RK23' 'DOP853' 'Radau' 'LSODA' "BDF" "RK45" + args=(r2, g, dr_dt, R, dl_dt), # type: ignore[reportArgumentType] ## according to spec atol=self.tolerance, ) - assert sol.status == 0, f"Pendulum solver did not succeed with dt:{dt}. Status:{sol.status}" + if sol.status != 0: + logger.error(f"r:{r}, v:{v}, r2:{r2}, dr/dt:{dr_dt}, l0:{R}, dl_dt:{dl_dt}") + raise AssertionError(f"Pendulum solver did not succeed with dt:{dt}. Status:{sol.status}") from None y_t = sol.y[:, -1] # last column - position = y_t[:3] if dr_dt is None else y_t[:3] + dr_dt * dt # position of CoM relative to origin - self.r_v = y_t[3:] if dr_dt is None else y_t[3:] + dr_dt + position = y_t[:3] # if dr_dt is None else y_t[:3] + dr_dt * dt # position of CoM relative to origin + self.r_v = y_t[3:] # if dr_dt is None else y_t[3:] + dr_dt if dt >= self._damping_time: # pendulum stops within dt self.r_v = np.array((0, 0, 0), float) else: @@ -558,11 +597,26 @@ def ivp_fun(t: float, y: np.ndarray, r2: float, g: np.ndarray, dr_dt: np.ndarray rel_direction = self.anchor0.rot().apply(self.direction, inverse=True) # dir. relative to previous boom self.boom[1:] = cartesian_to_spherical(rel_direction)[1:] self._c_m = position - elif l_fac is not None: - self.boom[0] = self._new_len - self._new_len = None self.origin = self.anchor0.end self._c_m_sub = (self.mass, self.origin + self.c_m) + + if self.model.current_time > 0.0 and np.dot(self.r_acc, self._c_m) > 1e-10: + logger.warning( + f"Free fall of load detected at time {self.model.current_time}. Acc.:{np.dot(self.r_acc, self._c_m)}" + ) + if self.additional_checks and dr_dt is None and dl_dt is None and self.model.current_time > 0.0: + e1 = Boom._energy(self.mass, self._c_m, self.r_v) + if e1 - e0 > 1e-7: + logger.error(f"Pendulum energy change @{self.model.current_time} {e0}->{e1}") + logger.error(f" c_m:{self._c_m}, speed:{self.r_v}, damping:{self._damping_time}") + logger.error( + f" tangential:{np.cross(self._c_m, np.cross(self._c_m, g))}, centripetal:{np.dot(self.r_v, self.r_v) * self._c_m}" + ) + + lz1 = Boom._angular_momentum(self.mass, self._c_m, self.r_v) + if abs(lz1[2]) - abs(lz0[2]) > 1e-7: # not np.allclose( lz0, lz1): + logger.error(f"Pendulum angular momentum increase @{self.model.current_time} {lz0[2]}->{lz1[2]}") + logger.error(f" c_m:{self._c_m}, speed:{self.r_v}, damping:{self._damping_time}") # print(f"origin:{self.origin[0]}, d_end:{self.end[0]-self.origin[0]}, speed:{self.r_v[0]}") def pendulum_relax(self): diff --git a/src/py_crane/boom_fmu.py b/src/py_crane/boom_fmu.py index 872a4ae..f7d78a9 100644 --- a/src/py_crane/boom_fmu.py +++ b/src/py_crane/boom_fmu.py @@ -8,7 +8,7 @@ from component_model.model import Model from py_crane.boom import Boom -from py_crane.enum import Change +from py_crane.enums import Change # from py_crane.crane import Crane diff --git a/src/py_crane/crane.py b/src/py_crane/crane.py index b7ff757..335aaa0 100644 --- a/src/py_crane/crane.py +++ b/src/py_crane/crane.py @@ -8,7 +8,7 @@ from scipy.spatial.transform import Rotation as Rot from py_crane.boom import Boom -from py_crane.enum import Change +from py_crane.enums import Change logger = logging.getLogger(__name__) @@ -115,6 +115,7 @@ def __init__(self, to_crane_angle: CoordinateTransform | None = None): self.boom_: Boom = self.boom0 # keep track of the last boom self.torque = np.array((0, 0, 0), dtype=np.float64) self.force = np.array((0, 0, 0), dtype=np.float64) + self.current_time: float = 0.0 # used to make current_time from do_step known for the whole crane @property def boom0(self) -> Boom: @@ -284,6 +285,7 @@ def calc_statics_dynamics(self, dt: float | None = None): def do_step(self, current_time: float, step_size: float) -> bool: """Do a simulation step of size `dt` at `time` .""" + self.current_time = current_time if any(acc != 0 for acc in self.d_velocity): # linear acceleration ongoing self._velocity += self.d_velocity * step_size if any(v != 0 for v in self._velocity): # position change ongoing diff --git a/src/py_crane/enum.py b/src/py_crane/enums.py similarity index 100% rename from src/py_crane/enum.py rename to src/py_crane/enums.py diff --git a/tests/test_animation.py b/tests/test_animation.py index 0afa3af..e9d118e 100644 --- a/tests/test_animation.py +++ b/tests/test_animation.py @@ -41,7 +41,6 @@ def _crane(): name="wire", description="The wire fixed to the last boom. Flexible connection", mass=50.0, # so far basically the hook - mass_rng=(50, 2000), mass_center=1.0, boom=(0.5, np.radians(90), 0), q_factor=10.0, @@ -156,6 +155,25 @@ def test_animation_control(crane: Crane, show: bool = False): def test_playback_lines(show: bool = False): + # first do a one-record case (which can cause trouble) + with pytest.raises(AssertionError) as err: + ani = AnimatePlayBackLines( + data=[], lw=(10, 2, 1), figsize=(10, 10), interval=1000, title="Test of AnimatePlayBackLines" + ) + assert err.value.args[0] == "No data. Cannot animate." + data0 = ( + np.array((0,), float), # time + np.zeros(3).reshape((1, 3)), # base + np.array(((0, 0, 1),), float), # end of base + np.array(((1, 0, 1),), float), # end of next + np.array(((1, 0, 0),), float), # 'wire' + ) + ani = AnimatePlayBackLines( + data=data0, lw=(10, 2, 1), figsize=(10, 10), interval=1000, title="Test of AnimatePlayBackLines" + ) + if show: + ani.do_animation() + data = ( np.array((0, 1, 2, 3), float), # time np.zeros(12).reshape((4, 3)), # base @@ -181,5 +199,5 @@ def test_playback_lines(show: bool = False): pillog.setLevel(logging.WARNING) # test_animation_sequence(_crane(), show=True) - test_animation_control(_crane(), show=True) - # test_playback_lines(show=True) + # test_animation_control(_crane(), show=True) + test_playback_lines(show=True) diff --git a/tests/test_crane.py b/tests/test_crane.py index e031cb9..1677499 100644 --- a/tests/test_crane.py +++ b/tests/test_crane.py @@ -1,12 +1,12 @@ import logging from math import cos, radians, sin, sqrt -from typing import Callable, Sequence, cast +from typing import Any, Callable, Mapping, Sequence, cast import matplotlib.pyplot as plt import numpy as np import pytest -from component_model.analytic import sine_fit -from component_model.utils.analysis import extremum_series +from component_model.analytic import ForcedOscillator1D +from component_model.utils.analysis import extremum_series, sine_fit from component_model.utils.transform import rot_from_spherical, rot_from_vectors from scipy.spatial.transform import Rotation as Rot @@ -16,14 +16,19 @@ # from mpl_toolkits.mplot3d.art3d import Line3D -logger = logging.getLogger(__name__) -logging.basicConfig(level=logging.INFO) +logger = logging.getLogger() +logger.setLevel(logging.INFO) np.set_printoptions(precision=4, suppress=True) +def np_index(times: np.ndarray, t: float): + """Get the closest index with respect to a value in the array.""" + return np.absolute(times - t).argmin() + + def do_show( times: np.ndarray | list[float], - traces: dict[str, list[float]] | dict[str, np.ndarray], # [tuple[float,...],float]], + traces: Mapping[str, list[float] | np.ndarray], selection: dict[str, int] | None = None, title: str = "", ): @@ -108,7 +113,7 @@ def crane(scope: str = "session", autouse: bool = True): return _crane() -def _crane(): +def _crane(wire_length: float = 0.5): crane = Crane() _ = crane.add_boom( name="pedestal", @@ -124,15 +129,18 @@ def _crane(): mass_center=0.5, boom=(10.0, radians(90), 0), ) - _ = crane.add_boom( + w = crane.add_boom( name="wire", description="The wire fixed to the last boom. Flexible connection", mass=50.0, # so far basically the hook - mass_rng=(50, 2000), mass_center=1.0, - boom=(0.5, radians(90), 0), + boom=(wire_length, radians(90), 0), q_factor=10.0, + additional_checks=True, ) + assert np.allclose(w.origin, (10, 0, 3.0)), f"Origin {w.origin} != (10,0,3)" + assert np.allclose(w.end, (10, 0, 3.0 - wire_length)), f"Load position {w.end} != (10,0,{3.0 - wire_length})." + return crane @@ -234,47 +242,101 @@ def test_animate_pendulum(show: bool = False): # @pytest.mark.skip() -def test_pendulum(crane: Crane, show: bool = False): +def test_pendulum(show: bool = False): """Test crane with 1m wire and 50kg load at end as pendulum (in various configurations).""" + def _pendulum(q_factor: float = 20.0): + """Defines the special crane used here: a pedestal with a wire/load hanging down.""" + crane = Crane() + _ = crane.add_boom( + name="pedestal", + description="The crane base, just a pole for the pendulum, here.", + mass=2000.0, + mass_center=0.5, + boom=(1.0, 0, 0), + ) + w = crane.add_boom( + name="wire", + description="The wire fixed to the last boom. Flexible connection", + mass=100.0, # load + mass_center=1.0, + boom=(1.0, radians(180), 0), # 1m down, so that load at (0,0,0) in equilibrium + q_factor=q_factor, + additional_checks=True, + ) + assert np.allclose(w.origin, (0, 0, 1.0)), f"Origin {w.origin} != (0,0,1)" + assert np.allclose(w.end, (0, 0, 0)), f"Load expected at (0,0,0). Found {w.end}" + + return crane, w + + def maximum(tbl: list[list[float]], col: int = 1): + """Find maximum in 'col' of table and return row.""" + rmax = [float("-inf")] * len(tbl[0]) + for r in tbl: + if r[col] > rmax[col]: + rmax = r + return rmax + + def check_fit( + e_y0: float | None, + e_a: float | None, + e_w: float | None, + e_phi: float | None, + pars: tuple[Any, ...], + eps: float = 1e-3, + ): + """Check the sine fit with respect to expected parameters.""" + if len(pars) == 4: + _y0, _a, _w, _phi = pars + elif len(pars) == 5: + _y0, _a, _w, _phi, tm = pars + assert e_y0 is None or abs(_y0 - e_y0) < eps, f"Translation {_y0} != {e_y0}" + assert e_a is None or abs(_a - e_a) < eps, f"Amplitude {_a} != {e_a}" + assert e_w is None or abs(_w - e_w) < eps, f"Angular frequency {_w} != {e_w}" + assert e_phi is None or abs(_phi - e_phi) < eps, f"Phase {_phi} != {e_phi}" + def sim_run( + crane_spec: tuple[Crane, Boom], t_end: float, dt: float = 0.1, min_z: float | None = None, max_z: float | None = None, max_speed: float | None = None, show_select: dict[str, int] | None = None, + idx: int = 1, title: str = "test_pendulum", crane_position: Callable[[float], float] | None = None, crane_roll: Callable[[float], float] | None = None, crane_pitch: Callable[[float], float] | None = None, crane_yaw: Callable[[float], float] | None = None, wire_l: Callable[[float], float] | None = None, + _ext: Callable[[float], float] | None = None, ): + """Perform the simulation as specified. Results analysis is done in calling function.""" + crane, w = crane_spec misc: list[float] = [] x_pos: list[float] = [] + y_pos: list[float] = [] z_pos: list[float] = [] speed: list[float] = [] - time: list[float] = [] + time = np.arange(0, t_end, dt) + ext = time if _ext is None else [_ext(t) for t in time] _dz0_dt = -1 - z0 = r.end[2] - _time: float = 0.0 - length = r.boom[0] - _boom = crane.boom_by_name("boom1") - assert _boom is not None, "boom1 needed at this stage" - while _time < t_end: - x0 = r.end[0] - z0 = r.end[2] - v = float(np.linalg.norm(r.r_v)) - assert min_z is None or r.end[2] >= min_z, f"@{_time}. Min z {r.end[2]} < {min_z}" - assert max_z is None or r.end[2] <= max_z + 1e-6, f"@{_time}. Max z {r.end[2]} > {max_z}" + z0 = w.end[2] + length = w.boom[0] + for _time in time: + x0 = w.end[0] + y0 = w.end[1] + z0 = w.end[2] + v = float(np.linalg.norm(w.r_v)) + assert min_z is None or w.end[2] >= min_z, f"@{_time}. Min z {w.end[2]} < {min_z}" + assert max_z is None or w.end[2] <= max_z + 1e-6, f"@{_time}. Max z {w.end[2]} > {max_z}" assert max_speed is None or v <= max_speed, f"@{_time}. Max speed {v} < {max_speed}" - time.append(_time) x_pos.append(x0) + y_pos.append(y0) z_pos.append(z0) speed.append(v) - misc.append(_boom.end[0]) - _time += dt + misc.append(w.origin[1]) if crane_position is not None: crane.position = np.array((crane_position(_time), 0, 0), float) if crane_roll is not None: @@ -284,180 +346,242 @@ def sim_run( if crane_yaw is not None: crane.rotate((0, 0, crane_yaw(_time)), absolute=True) if wire_l is not None: - r.boom_setter((wire_l(_time), None, None)) - crane.calc_statics_dynamics(dt) - assert abs(length - r.boom[0]) < 1e-4, f"Pendulum length {r.boom[0]} != {length}" - + w.boom_setter((wire_l(_time), None, None)) + else: + assert abs(length - w.boom[0]) < 1e-4, f"Pendulum length {w.boom[0]} != {length}" + crane.do_step(_time, dt) z_max = [[0.0, z0]] z_max.extend(extremum_series(time[10:], z_pos[10:], which="max")) if show and show_select is not None: + title = f"{idx}. {title}" do_show( - time, {"x_pos": x_pos, "z_pos": z_pos, "speed": speed, "misc": misc}, selection=show_select, title=title + time, + {"x_pos": x_pos, "y_pos": y_pos, "z_pos": z_pos, "speed": speed, "misc": misc, "ext": ext}, + selection=show_select, + title=title, ) - return (time, x_pos, z_pos, speed, z_max, misc) - - def maximum(tbl: list[list[float]], col: int = 1): - """Find maximum in 'col' of table and return row.""" - rmax = [float("-inf")] * len(tbl[0]) - for r in tbl: - if r[col] > rmax[col]: - rmax = r - return rmax + return (time, x_pos, y_pos, z_pos, speed, z_max, misc) - f, p, b1, r = [b for b in crane.booms()] - # start with a 1m pendulum through the origin along the x-axis - b1.boom_setter((2, np.pi, 0)) # boom down - r.pendulum_relax() - r.boom_setter((1.0, None, None)) # wire 1m long - assert np.allclose(r.origin, (0, 0, 1.0)), f"Origin {r.origin} != (0,0,1)" - assert np.allclose(r.end, (0, 0, 0)), f"Load expected at (0,0,0). Found {r.end}" - - a0 = np.radians(1.0) - set_wire_direction(r, (a0, 0)) - assert np.allclose(r.end, (-np.sin(a0), 0, 1 - np.cos(a0))), ( - f"Wire angle {np.degrees(a0)}, load: ({-np.sin(a0)},0,{1 - np.cos(a0)}). Found {r.end}" - ) - _damping_time = r.damping(damping_time=1e100) # no damping - if False: - show_crane(crane, True, False, title="start") - - # Start the crane at maximum potential energy of load (1 deg) without damping - # we should have theta(t) = theta0* cos(w*t) with theta0= 0.0175 (1deg) and w= sqrt(g/L) = 3.132 => T = 2.006 - set_wire_direction(r, (a0, 0)) - assert np.allclose(r.end, (-np.sin(a0), 0, 1 - cos(a0))) - time, x_pos, z_pos, speed, z_max, misc = sim_run( - t_end=10, - dt=0.01, - min_z=0, - max_z=1 - np.cos(a0), - max_speed=np.sqrt(2 * 9.81 / (1 - np.cos(a0))), - show_select=None, - title=f"test_pendulum. {np.degrees(a0)}deg through origin", - ) - a, w, phi = sine_fit(time, x_pos) - assert abs(a - np.sin(a0)) < 1e-5, f"Amplitude a:{a} != sin({a0}):{np.sin(a0)}" - assert abs(w - np.sqrt(9.81)) < 1e-4, f"Angular frequency w:{w} != sqrt(9.81):{np.sqrt(9.81)}" - assert abs(phi + np.pi / 2) < 1e-4, f"Phase phi:{phi} != -pi/2:{-np.pi / 2}" - - for t, z in zip(time, z_pos, strict=False): - theta = a0 * np.cos(np.sqrt(9.81) * t) - assert abs(z - 1 + np.cos(theta)) < 1e-5, f"@{t}: z={z}. Expected {1.0 - np.cos(theta)}" - # print(f"@{t}, theta:{theta}, z:{z}, {1 - np.cos(theta)}") - last = -np.pi / np.sqrt(9.81) - for t, _ in z_max: # Note. the pendulum has 2 max points per period! - assert abs(t - last - np.pi / np.sqrt(9.81)) < 0.01, f"Period {t}-{last} != {np.pi / np.sqrt(9.81)}" - last = t - - # same test with q_factor - a0 = np.radians(5.0) - set_wire_direction(r, (a0, 0)) - assert np.allclose(r.end, (-np.sin(a0), 0, 1 - cos(a0))) - _damping_time = r.damping(q_factor=100) - time, x_pos, z_pos, speed, z_max, misc = sim_run( - t_end=100, - dt=0.01, - min_z=0, - max_z=1 - np.cos(a0), - max_speed=np.sqrt(2 * 9.81 / (1 - np.cos(a0))), - show_select=None, - title="test_pendulum. 1deg through origin", - ) - time = np.array(time, float) - env = a0 * np.exp(-time / 2.0 / _damping_time) - x_pos = np.array(x_pos, float) - z_pos = np.array(z_pos, float) - speed = np.array(speed, float) - energy = 9.81 * z_pos + 0.5 * speed**2 - e_decay = energy[0] * np.exp(-time / _damping_time) - if False: # show: - do_show( - time, - traces={"x": x_pos, "env+": env, "env-": -env, "energy": energy, "e_decay": e_decay, "z": z_pos}, - selection={"x": 1, "env+": 1, "env-": 1, "energy": 2, "e_decay": 2}, - title=f"Pendulum x0={a0}, tau={_damping_time}", + def _initial_angle_speed( + a0: float = 0.0, v0: float = 0.0, damping_time: float = 1e100, show: bool = False, idx: int = 1 + ): + crane, w = _pendulum() + set_wire_direction(w, (a0, 0)) + assert np.allclose(w.end, (-np.sin(a0), 0, 1 - np.cos(a0))), ( + f"Wire angle {np.degrees(a0)}, load: ({-np.sin(a0)},0,{1 - np.cos(a0)}). Found {w.end}" ) - assert np.allclose(energy, e_decay, atol=1e-3), f"Energy should decay with decay time {_damping_time}" - - # Move the whole crane according to a sin function in x-direction, causing forced pendulum actions - _damping_time = r.damping(damping_time=5) # damping - set_wire_direction(r, (0, 0)) - time, x_pos, z_pos, speed, z_max, misc = sim_run( - t_end=500, - dt=0.01, - min_z=0, - show_select={ - "x_pos": 1, - "z_pos": 2, - "misc": 1, - }, #'speed':2}, - title="test_pendulum. Forced acceleration sweeping angular frequency (0.01+0.01*t)", - crane_position=lambda t: 0.001 * np.sin((0.01 + 0.01 * t) * t), # angular frequency sweep - ) - # Qualitative (visual) comparison with forced oscillator is very good, but oscillator is not harmonic - # and sweep in low frequency area too fast, so that numerical comparison is difficult. - a, w, phi = sine_fit(time[4100:4900], x_pos[4100:4900]) # t=41..49 - assert abs(a - 0.0101) < 1e-4 and abs(w - 0.9054) < 1e-4 and abs(phi - 0.2861) < 1e-4, ( - f"Low freq. a:{a}, w:{w}, phi:{phi}" - ) - a, w, phi = sine_fit(time[19988:20201], x_pos[19988:20201]) # t=199.88...202.01, - assert abs(a - 0.7555) < 1e-4 and abs(w - 3.0134) < 1e-4 and abs(phi - 0.7548) < 1e-4, ( - f"Resonance. a:{a}, w:{w}, phi:{phi}" - ) - a, w, phi = sine_fit(time[39990:40080], x_pos[39990:40080]) # t=399.9...400.8 - assert abs(a - 0.01484) < 1e-4 and abs(w - 8.0171) < 1e-4 and abs(phi + 2.1028) < 1e-4, ( - f"High freq. a:{a}, w:{w}, phi:{phi}" - ) + tau = w.damping(damping_time=damping_time) + + # Start the crane at maximum potential energy of load (1 deg) without damping + # we should have theta(t) = theta0* cos(w*t) with theta0= 0.0175 (1deg) and w= sqrt(g/L) = 3.132 => T = 2.006 + w.r_v = np.array((v0, 0, 0), float) + wd = np.sqrt(9.81 - 1 / 4 / tau**2) + e_a = np.sqrt(a0**2 + (a0 / 2 / tau + v0) ** 2 / wd**2) + phi = np.arctan2(1 / 2 / tau, wd) + time, x_pos, y_pos, z_pos, speed, z_max, misc = sim_run( + crane_spec=(crane, w), + t_end=10, + dt=0.01, + min_z=0, + max_z=1 - np.cos(e_a), + max_speed=np.sqrt(2 * 9.81 / (1 - np.cos(e_a))), + show_select=None if not show else {"x_pos": 1, "z_pos": 1, "speed": 2}, + title=f"test_pendulum. {np.degrees(a0)}deg through origin", + idx=idx, + ) + _y0, _a, _w, _phi, t = sine_fit(time, x_pos) + check_fit(0.0, e_a * np.exp(-t / 2 / tau), wd, phi, (_y0, _a, _w, _phi), eps=1e-4) + + eps = 1e-5 if tau > 100 else 1e-3 + for t, z in zip(time, z_pos, strict=False): + theta = a0 * np.cos(np.sqrt(9.81) * t) + assert abs(z - 1 + np.cos(theta)) < eps, f"@{t}: z={z}. Expected {1.0 - np.cos(theta)}" + e0 = 9.81 * z_pos[0] + 0.5 * speed[0] ** 2 + for t, z, v in zip(time, z_pos, speed, strict=True): + assert abs(9.81 * z + 0.5 * v**2 - e0 * np.exp(-t / tau)) <= 1e-3, ( + f"Energy @{t} {9.81 * z + 0.5 * v**2} =! {e0 * np.exp(-t / tau)}" + ) - # Start with circular motion of load and shorten wire over time - _damping_time = r.damping(damping_time=10000) # damping - set_wire_direction(r, (-90, 0), degrees=True) - r.r_v = np.array((0.0, 10.0, 0.0), float) - time, x_pos, z_pos, speed, z_max, misc = sim_run( - t_end=10, - dt=0.001, - min_z=0, - show_select={"x_pos": 1, "z_pos": 2}, - title="test_pendulum. Start with rotating load and shorten wire length (0.01+0.01*t)", - wire_l=lambda t: 1.0 - 0.002 * t, - ) - # Qualitative (visual) comparison with forced oscillator is very good, but oscillator is not harmonic - # and sweep in low frequency area too fast, so that numerical comparison is difficult. - # a, w, phi = sine_fit(time[4100:4900], x_pos[4100:4900]) #t=41..49 - - # Forced roll movement frequency sweep with damping - set_wire_direction(r, (0, 0)) - r.damping(damping_time=100) # damping - time, x_pos, z_pos, speed, z_max, misc = sim_run( - t_end=100, - dt=0.01, - # min_z=0, - # max_z=1-np.cos(np.radians(1)), - # max_speed=np.sqrt(2*9.81/(1-np.cos(np.radians(1)))), - show_select={"x_pos": 1, "z_pos": 2}, - title="test_pendulum. Forced rolling motion", - crane_roll=lambda t: 0.01 * np.sin((0.1 + t / 20) * t), - ) - # assert max(z[1] for z in z_max) > 0.028, f"Overall max: {max(z[1] for z in z_max)}" - # assert max(z[1] for z in z_max[:5]) < 1e-4, f"Low frequency max: {max(z[1] for z in z_max[:5])}" - # assert max(z[1] for z in z_max[-5:]) < 1e-3, f"High frequency max: {max(z[1] for z in z_max[-5:])}" + def _move_crane( + v0: float = 0.0, + te: float = 100, + tau: float = 5, + c_pos: Callable[[float], float] | None = None, + show: bool = False, + idx: int = 1, + ): + crane, w = _pendulum() + gamma = 1.0 / 2.0 / w.damping(damping_time=tau) # damping + wd = np.sqrt(9.81 / w.length - gamma**2) + T = 2 * np.pi / wd + if 4 <= idx <= 6: + osc = ForcedOscillator1D(k=9.81, c=gamma, a=0.001, wf=wd) + else: + osc = None + w.pendulum_relax() + w.r_v = np.array((v0, 0, 0), float) + time, x_pos, y_pos, z_pos, speed, z_max, misc = sim_run( + crane_spec=(crane, w), + t_end=te, + dt=T / 100 if idx != 3 else 0.01, + min_z=1.0 - float(w.length), + show_select={ + "x_pos": 1, + "z_pos": 2, + "speed": 1, + "ext": 1, + }, + title=f"test_pendulum_move[{idx}]. Movements in x-direction", + crane_position=c_pos, # movement function + _ext=lambda t: ( + abs((v0 if v0 > 0 else 1.0) * np.exp(-gamma * t) * np.cos(wd * t)) + if osc is None + else float(osc.calc(t)[0]) + ), + ) + if idx == 0: # v0=1.0, tau=20 - damped oscillation. Small amplitude, so that linear approximation holds! + for t, v in zip(time, speed, strict=True): + expected = abs(v0 * np.exp(-t / tau) * np.cos(wd * t)) + assert abs(v - expected) < 3e-2, f"Damped oscillation speed @{t}: {v}!={expected}" + if idx == 1: # origin moves with v=0.01, while load starts with same speed + assert all(abs(v - v0) < 1e-3 for v in speed), f"Tail speeds: {speed[0:10]}" + assert abs(x_pos[-1] - v0 * 100) < 1e-3, f"End position: {x_pos[-1]}!={v0 * 100}" + elif idx == 2: # v0=0. For t<5*T crane moves with speed 1/s and stops then + i_change = np_index(time, 5) + i_zero = np.absolute(x_pos[i_change : i_change + 200]).argmin() + assert all(abs(v) < 2e-3 for v in speed[i_change + i_zero :: 100]), ( + f"Speed zero at period. speed[{i_change + i_zero}]:{speed[i_change + i_zero]}" + ) + assert all(abs(x - 0.25) < 1e-2 for x in x_pos[-200:]), f"End position {0.25}!={x_pos[-10:]}" + elif idx == 3: + # Qualitative (visual) comparison with forced oscillator is very good, but oscillator is not harmonic + # and sweep in low frequency area too fast, so that numerical comparison is difficult. + i_low = np_index(time, 40) + i_res = np_index(time, 167.5) + i_high = np_index(time, 300) + check_fit(0.0, 0.0, 0.66431, 1.9054, sine_fit(time[:i_low], x_pos[:i_low]), eps=1e-3) + check_fit(None, 0.0013, 3.1979, 1.8262, sine_fit(time[:i_res], x_pos[:i_res]), eps=1e-3) + check_fit(None, 3.7e-5, 5.9758, -0.7238, sine_fit(time[:i_high], x_pos[:i_high]), eps=1e-3) + elif idx == 4: # resonant oscillation + _y0, _a, _w, _phi, _ = sine_fit(time, x_pos) + logger.info(f"Amplitude:{_a}, angular_freq:{_w} - {wd}, phase:{_phi}") + + def _turn_crane( + te: float = 100, + tau: float = 20, + _rpy: str = "r", + rpy: Callable[[float], float] | None = None, + show: bool = False, + idx: int = 1, + ): + crane, w = _pendulum() + gamma = 1.0 / 2.0 / w.damping(damping_time=tau) # damping + wd = np.sqrt(9.81 / w.length - gamma**2) + T = 2 * np.pi / wd + w.pendulum_relax() + if _rpy == "r": + crane_roll, crane_pitch, crane_yaw = rpy, None, None + elif _rpy == "p": + crane_roll, crane_pitch, crane_yaw = None, rpy, None + elif _rpy == "y": + crane_roll, crane_pitch, crane_yaw = None, None, rpy + time, x_pos, y_pos, z_pos, speed, z_max, misc = sim_run( + crane_spec=(crane, w), + t_end=te, + dt=T / 100, + min_z=float(-w.length), + show_select={ + "x_pos": 1, + "y_pos": 2, + "speed": 1, + "misc": 2, + }, + crane_roll=crane_roll, + crane_pitch=crane_pitch, + crane_yaw=crane_yaw, + _ext=rpy, + title=f"test_pendulum_turn[{idx}, {_rpy}].", + ) + if idx == 1: # yaw. Torsion is not included. Nothing happens + assert all(abs(v) < 1e-8 for v in speed), "Speed should be zero" + assert all(abs(x) < 1e-8 for x in x_pos), "x-position should be zero" + if idx == 2: # accelerated roll, then stop (shock) + i_change = np_index(time, 5 + 2 * T) + assert all([abs(x) < 1e-10 for x in x_pos]), "x should not be affected." + assert all([abs(y + np.sin(0.25)) < 0.03 for y in y_pos[-100:]]), ( + f"y at rest with rolled crane? {y_pos[-10:]}" + ) + _y0, _a, _w, _phi, _ = sine_fit(time[:i_change], np.array(y_pos[:i_change], float) + np.sin(0.25)) + assert abs(_w - wd) < 1e-1, f"Expected angular frequency {wd} != {_w}" + if idx == 3: # accelerated pitch, then stop (shock) + i_change = np_index(time, 5 + 2 * T) + assert all([abs(y) < 1e-10 for y in y_pos]), "y should not be affected." + assert all([abs(x + np.sin(0.25)) < 0.03 for x in x_pos[-100:]]), ( + f"x at rest with rolled crane? {x_pos[-10:]}" + ) + _y0, _a, _w, _phi, _ = sine_fit(time[:i_change], np.array(x_pos[:i_change], float) + np.sin(0.25)) + assert abs(_w - wd) < 2e-2, f"Expected angular frequency {wd} != {_w}" - # Forced yaw movement, which does not have effect on load (inertia not modelled) - set_wire_direction(r, (0, 0)) - crane.rot((0, 0, 0)) # reset crane - r.pendulum_relax() - r.damping(damping_time=100) # damping - time, x_pos, z_pos, speed, z_max, misc = sim_run( - t_end=10, - dt=0.01, - # min_z=0, - # max_z=1-np.cos(np.radians(1)), - # max_speed=np.sqrt(2*9.81/(1-np.cos(np.radians(1)))), - show_select={"x_pos": 1, "z_pos": 2}, - title="test_pendulum. Forced yaw motion", - crane_yaw=lambda t: 0.01 * np.sin((0.1 + t / 20) * t), - ) - assert max(z[1] for z in z_max) < 1e-9, f"Overall max: {max(z[1] for z in z_max)}" + def _circular( + tau: float = 1000, + v0: float = 10.0, + wire_l: Callable[[float], float] | None = None, + angle: float = 0, + show: bool = False, + idx: int = 1, + ): + """Start with circular motion of load and shorten wire according to wire_l function.""" + crane, w = _pendulum() + w.damping(damping_time=tau) + set_wire_direction(w, (90 + angle, 0), degrees=True) + w.r_v = np.array((0.0, v0, 0.0), float) + time, x_pos, y_pos, z_pos, speed, z_max, misc = sim_run( + crane_spec=(crane, w), + t_end=10.0, + dt=0.001, + min_z=0, + show_select={"x_pos": 1, "y_pos": 1, "z_pos": 2}, + title="test_pendulum. Rotating load and shortened wire length", + wire_l=wire_l, + ) + if idx == 0: # No change in wire length and angle such that conic curve emerges (stable) + assert abs(angle - 54.75550939085597) < 1e-10, f"Stability angle {angle} != 54.756" + c_angle = np.cos(np.radians(angle)) + _x = c_angle + _z = np.sqrt(1 - _x**2) + assert all(abs(z - 1 + _z) < 1e-5 for z in z_pos), f"Height != {1 - _z}: {z_pos[:10]}" + i25 = np_index(time, 2.5) + check_fit(0.0, _x, 2 / c_angle, np.pi / 2, sine_fit(time[:i25], x_pos[:i25]), eps=1e-6) + _y0, _a, _w, _phi, _tm = sine_fit(time[: np_index(time, 2.5)], y_pos[: np_index(time, 2.5)]) + assert abs(_phi) < 1e-6, f"y-Phase {_phi} != {np.pi / 2}" + elif idx == 1: # No change of wire length. Note: curves are not completely circular => accuracy reduced. + c_angle = np.sqrt(np.sqrt(_b2 + _b2**2 / 4) - _b2 / 2) # stability angle + _y0, _a, _w, _phi, _tm = sine_fit(time[: np_index(time, 1.5)], z_pos[: np_index(time, 1.5)]) + assert abs(1.0 - 2**2 / 9.81 - _y0) < 5e-2, "Zentripetal in balance with gravitation. y0={_y0}." + assert 1.0 - _y0 - _a < 2e-2, f"Energy and angular momentum conservation: y0={_y0}, a={_a}" + elif idx == 2: # wire shortened with 0.05*t => l0=1...lend=0.5. Should have wd1 = l0**2 / l1**2* wd0 + i02 = np_index(time, 0.2) + check_fit(0.0, 1.0, 50, None, sine_fit(time[:i02], x_pos[:i02], eps=0.1), eps=0.16) + _y0, _a, _w, _phi, _tm = sine_fit(time, x_pos, eps=0.1) + check_fit(0.0, 0.5, None, None, (_y0, _a, _w, _phi), eps=0.1) + assert abs(_w - 200) / 200 < 3e-2, f"Angular velocity {_w} != 200" + + _b2 = (2**2 / 9.81) ** 2 + stable = np.degrees(np.arccos(np.sqrt(np.sqrt(_b2 + _b2**2 / 4) - _b2 / 2))) + _circular(tau=1e10, v0=2.0, wire_l=None, angle=stable, show=show, idx=0) + _circular(tau=1e10, v0=2.0, wire_l=None, show=show, idx=1) + _circular(tau=1e10, v0=50.0, wire_l=lambda t: 1.0 - 0.05 * t, show=show, idx=2) + _move_crane(te=50, v0=0.1, tau=20, show=True, idx=0) + _move_crane(v0=0.01, c_pos=lambda t: 0.01 * t, tau=50, show=True, idx=1) + _move_crane(v0=0.0, c_pos=lambda t: 0.01 * t**2 if t <= 5 else 0.25, tau=20.0, show=True, idx=2) + _move_crane(te=500, c_pos=lambda t: 0.0001 * np.sin((0.01 + 0.01 * t) * t), show=True, idx=3) + # ??_move_crane(c_pos=lambda t: 0.001 * np.sin( wd* t), tau=tau, te=200, show=True, idx=4) + # ??_move_crane(c_pos=lambda t: 0.1 * np.sin( 0.1*wd* t), tau=tau, te=100, show=True, idx=5) + # ??_move_crane(c_pos=lambda t: 0.1 * np.sin( 10*wd* t), tau=tau, te=100, show=True, idx=6) + _turn_crane(_rpy="y", rpy=lambda t: 0.01 * t**2 if t <= 5 else 0.25, tau=20, show=True, idx=1) + _turn_crane(_rpy="r", rpy=lambda t: 0.01 * t**2 if t <= 5 else 0.25, tau=20, show=True, idx=2) + _turn_crane(_rpy="p", rpy=lambda t: 0.01 * t**2 if t <= 5 else 0.25, tau=20, show=True, idx=3) # @pytest.mark.skip() @@ -798,18 +922,25 @@ def test_force_torque(crane: Crane, show: bool = False): dt = 0.01 time = 0.0 _time: list[float] = [] - traces = {"xe": [], "load": [], "force": [], "torque": []} + _xe: list[float] = [] + _load: list[float] = [] + _force: list[float] = [] + _torque: list[float] = [] while time < 20.0: crane.d_velocity = np.array((0.1 * np.sin(1.0 * time), 0, 0), float) _time.append(time) crane.do_step(time, dt) - traces["xe"].append(r.origin[0]) - traces["load"].append(r.end[0]) - traces["force"].append(crane.force[0]) - traces["torque"].append(crane.torque[1]) + _xe.append(r.origin[0]) + _load.append(r.end[0]) + _force.append(crane.force[0]) + _torque.append(crane.torque[1]) time += dt - if False: # show: - do_show(_time, traces, {"xe": 1, "load": 1, "force": 2, "torque": 2}) + if show: + do_show( + _time, + traces={"xe": _xe, "load": _load, "force": _force, "torque": _torque}, + selection={"xe": 1, "load": 1, "force": 2, "torque": 2}, + ) # --------------------------------------------------------------- logger.info("Sinusoidal pitch acceleration: a*sin(w*t)") dt = 0.01 @@ -820,10 +951,10 @@ def test_force_torque(crane: Crane, show: bool = False): crane.d_angular = np.array((0, 0.1 * np.sin(1.0 * time), 0), float) _time.append(time) crane.do_step(time, dt) - traces["xe"].append(r.origin[0]) - traces["load"].append(r.end[0]) - traces["force"].append(crane.force[0]) - traces["torque"].append(crane.torque[1]) + traces["xe"].append(float(r.origin[0])) + traces["load"].append(float(r.end[0])) + traces["force"].append(float(crane.force[0])) + traces["torque"].append(float(crane.torque[1])) time += dt if show: do_show(_time, traces, {"xe": 1, "load": 1, "force": 2, "torque": 2}) @@ -841,7 +972,7 @@ def test_force_torque(crane: Crane, show: bool = False): # test_mass_center() # test_initial(_crane()) # test_orientation(_crane(), 32) - # test_pendulum(_crane(), show=True) + # test_pendulum( show=True) # test_animate_pendulum(show=True) # test_sequence(_crane(), show=True) # test_change_length(_crane(), show=True) diff --git a/tests/test_crane_configurations.py b/tests/test_crane_configurations.py new file mode 100644 index 0000000..f078e2d --- /dev/null +++ b/tests/test_crane_configurations.py @@ -0,0 +1,221 @@ +import logging +from math import sqrt +from typing import Generator + +import numpy as np +import pytest # noqa: F401 +from component_model.utils.controls import Controls + +from py_crane.animation import AnimateCrane +from py_crane.crane import Crane + +logger = logging.getLogger(__name__) +logging.basicConfig(level=logging.ERROR) +np.set_printoptions(precision=4, suppress=True) + + +def _pendulum(): + crane = Crane() + w = crane.add_boom( + name="wire", + description="The wire fixed to the last boom. Flexible connection", + mass=50.0, # so far basically the hook + mass_center=1.0, + boom=(1.0, np.pi, 0), + q_factor=10.0, + ) + crane.position = np.array((0, 0, 1), float) + w.pendulum_relax() + return crane + + +def _mobile_crane(): + crane = Crane() + _p = crane.add_boom( + name="pedestal", + description="The crane base, on one side fixed to the vessel and on the other side the first crane boom is fixed to it. The mass should include all additional items fixed to it, like the operator's cab", + mass=2000.0, + mass_center=(0.5, -1, 0.8), + boom=(3.0, 0, 0), + ) + _b = crane.add_boom( + name="boom1", + description="The first boom. Can be lifted", + mass=200.0, + mass_center=0.5, + boom=(10.0, np.radians(90), 0), + ) + _w = crane.add_boom( + name="wire", + description="The wire fixed to the last boom. Flexible connection", + mass=50.0, # so far basically the hook + mass_center=1.0, + boom=(1.0, np.radians(90), 0), + q_factor=10.0, + ) + return crane + + +def move_mobile_crane( + crane: Crane, dt: float = 0.01, t_end: float = 10.0 +) -> Generator[tuple[float, Crane], None, None]: + """Create movement of the crane through definition and usage of Controls. + Generator function. Returns a `Generator` which steps through time and sequentially yields updated frames in the form of tuple (time, crane) objects. + time is defined global as a simple way to draw the current time together with the title. + """ + # initial definition of controls and start values + controls = Controls(limit_err=logging.WARNING) # CRITICAL) + f, p, b1, r = list(crane.booms()) + controls.append("turn", (None, (-0.31, 0.31), (-0.16, 0.16))) # free rotation, max 1 turn/20sec, 2sec to max + controls.append("luff", ((0, 1.58), (-0.18, 0.09), (-0.09, 0.05))) # 90 deg, 5/-2.5 deg/sec, 2sec to max + controls.append("boom", ((8, 50), (-0.2, 0.1), (-0.1, 0.05))) # 8m..50m, 0.1/-0.2 m/sec, 2sec to max + controls.append("wire", ((0.5, 50), (-0.1, 1.0), (-0.05, 0.1))) # 0.5m..50m, -0.1/1 m/sec, 2sec to max + controls.current[2][0] = 8.0 # b1 starts with 8m + controls.current[1][0] = np.radians(90) # b1 starts at 90 deg + controls.current[3][0] = 0.5 # wire length starts 0.5m + + # From time 0 we set three goals + controls.setgoal("turn", 0, np.radians(90), 0.0) # turn pedestal 90 deg + controls.setgoal("luff", 0, np.radians(45), 0.0) # luff boom to 45 deg + controls.setgoal("boom", 1, 0.1, 0.0) # increase length 0.1m/s + for time in np.linspace(0.0, t_end, int(t_end / dt) + 1): + if time > 10 and controls.goals[3] is None: # Start to increase wire length with 1m/s + controls.setgoal("wire", 1, 1.0, 10.0) + controls.step(time, dt) + if controls.goals[3] is not None: + r.boom_setter((controls.current[3][0], None, None)) + if controls.goals[1] is not None or controls.goals[2] is not None: + b1.boom_setter((controls.current[2][0], controls.current[1][0], None)) + if controls.goals[0] is not None: + p.boom_setter((None, None, controls.current[0][0])) + crane.do_step(time, dt) + yield (time + dt, crane) + + +def test_mobile_crane(show: bool = False): + crane = _mobile_crane() + if not show: # if nothing can be shown, we do not need to run it + return + ani = AnimateCrane(crane, move_mobile_crane, dt=0.1, t_end=9.0) # type: ignore ## It is a Generator! + ani.do_animation() + + +def _knuckle_boom_crane(): + crane = Crane() + _p = crane.add_boom( + name="pedestal", + description="The crane base, on one side fixed to the vessel and on the other side the first crane boom is fixed to it. The mass should include all additional items fixed to it, like the operator's cab", + mass=2000.0, + mass_center=(0.5, -1, 0.8), + boom=(1.0, 0, 0), + ) + _b1 = crane.add_boom( + name="boom1", + description="The first boom. Can be lifted", + mass=200.0, + mass_center=0.5, + boom=(3.0, np.radians(45), 0), + ) + _b2 = crane.add_boom( + name="boom2", + description="The second boom. Can be lifted", + mass=200.0, + mass_center=0.5, + boom=(5.0, np.radians(170), np.pi), + ) + _b3 = crane.add_boom( + name="boom3", + description="The third boom. Can be lifted", + mass=200.0, + mass_center=0.5, + boom=(7.0, np.radians(170), 0.0), + ) + _w = crane.add_boom( + name="wire", + description="The wire fixed to the last boom. Flexible connection", + mass=50.0, # so far basically the hook + mass_center=1.0, + boom=(1e-6, 0, 0), + q_factor=100.0, + ) + _w.pendulum_relax() + return crane + + +def move_knuckle_boom_crane( + crane: Crane, dt: float = 0.01, t_end: float = 10.0 +) -> Generator[tuple[float, Crane], None, None]: + """Create movement of the crane through definition and usage of Controls. + Generator function. Returns a `Generator` which steps through time and sequentially yields updated frames in the form of tuple (time, crane) objects. + time is defined global as a simple way to draw the current time together with the title. + """ + # initial definition of controls and start values + controls = Controls(limit_err=logging.WARNING) # CRITICAL) + f, p, b1, b2, b3, w = list(crane.booms()) + controls.append("luff3", (None, (-0.09, 0.09), (-0.09, 0.05))) # 360 deg, 5/-2.5 deg/sec, 2sec to max + controls.append("luff2", (None, (-0.18, 0.09), (-0.09, 0.05))) # 360 deg, 5/-2.5 deg/sec, 2sec to max + controls.append("luff1", (None, (-0.09, 0.04), (-0.09, 0.05))) # 8m..50m, 0.1/-0.2 m/sec, 2sec to max + controls.append("wire", ((0, 50), (-0.1, 1.0), (-0.5, 0.5))) # 0m..50m, -0.1/1 m/sec, 2sec to max + controls.append("turn", (None, (-0.9, 0.9), (-0.9, 0.9))) # 360 deg, 5/-2.5 deg/sec, 2sec to max + controls.current[0][0] = b3.boom[1] + controls.current[1][0] = b2.boom[1] + controls.current[2][0] = b1.boom[1] + + # Set initial goals + controls.setgoal("luff3", 0, np.radians(0), 0.0) + controls.setgoal("luff2", 0, np.radians(45), 0.0) # luff boom to 45 deg + controls.setgoal("luff1", 0, 0.0, 0.0) # luff to align with pedestal + for time in np.linspace(0.0, t_end, int(t_end / dt) + 1): + if abs(time - 10.0) < 1e-6: # goal2 reached + assert np.allclose(p.end, (0, 0, 1)), "Pedestal unchanged (straight up)" + assert np.allclose(b1.end, (0, 0, 4), atol=2e-3), f"Boom 1 aligned with pedestal? {b1.end}" + if abs(time - 35.0) < 1e-6: # start to lower hook + assert np.allclose(p.end, (0, 0, 1)), "Pedestal unchanged (straight up)?" + _b2 = (-5 / sqrt(2), 0, 4 + 5 / sqrt(2)) + _b3 = (-12 / sqrt(2), 0, 4 + 12 / sqrt(2)) + assert np.allclose(b2.end, _b2, atol=0.01), f"45 deg in negative x-direction? {b2.end}!={_b2}" + assert np.allclose(b3.end, _b3, atol=0.01), f"45 deg in negative x-direction? {b3.end}!={_b3}" + controls.current[3][0] = 1e-6 + controls.setgoal("wire", 0, 10.0, 35.0) + if abs(time - 47.0) < 1e-6: # turn the crane (causes pendulum actions) + _w = (-12 / sqrt(2), 0, 4 + 12 / sqrt(2) - 10) + assert np.allclose(w.end, _w, atol=0.01), f"Load position before turn {w.end}!={_w}" + w.damping(q_factor=100.0) + controls.setgoal("turn", 2, 0.09, 47.0) + if abs(time - 54.0) < 1e-6: # stop turning (causes pendulum actions) + controls.setgoal("turn", 2, None, 54.0) + + controls.step(time, dt) + if controls.goals[3] is not None: + w.boom_setter((controls.current[3][0], None, None)) + if controls.goals[2] is not None: + b1.boom_setter((None, controls.current[2][0], None)) + if controls.goals[1] is not None: + b2.boom_setter((None, controls.current[1][0], None)) + if controls.goals[0] is not None: + b3.boom_setter((None, controls.current[0][0], None)) + if controls.goals[4] is not None: + p.boom_setter((None, None, controls.current[4][0])) + crane.do_step(time, dt) + yield (time + dt, crane) + + +def test_knuckle_boom_crane(show: bool = False): + crane = _knuckle_boom_crane() + if not show: # if nothing can be shown, we do not need to run it + return + ani = AnimateCrane(crane, move_knuckle_boom_crane, dt=0.2, t_end=70.0) # type: ignore ## It is a Generator! + ani.do_animation() + + +if __name__ == "__main__": + retcode = 0 # pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) + assert retcode == 0, f"Non-zero return code {retcode}" + logging.basicConfig(level=logging.DEBUG) + parsolog = logging.getLogger("parso") + parsolog.setLevel(logging.WARNING) + pillog = logging.getLogger("PIL") + pillog.setLevel(logging.WARNING) + + # test_mobile_crane(show=True) + test_knuckle_boom_crane(show=True) diff --git a/tests/test_enum.py b/tests/test_enum.py index 8794ecd..281f226 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -1,4 +1,4 @@ -from py_crane.enum import Change +from py_crane.enums import Change def test_types(): diff --git a/tests/test_mobile_crane_FMU.py b/tests/test_mobile_crane_FMU.py index 6fef968..895e545 100644 --- a/tests/test_mobile_crane_FMU.py +++ b/tests/test_mobile_crane_FMU.py @@ -143,7 +143,7 @@ def test_vals(v: Variable, k: int, val: float, rng: tuple[float, float], typ: ty # @pytest.mark.skip("Run the FMU") def test_run_mobilecrane_static(mobile_crane_fmu: Path, show: bool = False): """Load the FMU and run it using fmpy.""" - print("Start simulation") + logger.info("Start simulation") result = simulate_fmu( # static run str(mobile_crane_fmu), stop_time=0.1, @@ -240,11 +240,11 @@ def run_simulation( if __name__ == "__main__": - retcode = 0 # pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "True", __file__]) + retcode = pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "True", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" crane = _mobile_crane_fmu() # test_fmu() # test_mass_center() # test_mobilecrane_fmu( crane, show=True) # test_run_mobilecrane_static( crane, show=True) - test_run_mobilecrane_move(crane, show=True) + # test_run_mobilecrane_move(crane, show=True) From fc6f119de499166372fd3a92d5d9944ccdcd5186 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Tue, 24 Feb 2026 14:50:48 +0100 Subject: [PATCH 04/53] Adapt to changes in component_model.utils.controller.py module. --- examples/HarmonicOscillator6D.fmu | Bin 796106 -> 827588 bytes examples/MobileCrane.fmu | Bin 921686 -> 923158 bytes examples/MobileCraneStraight.fmu | Bin 1691611 -> 923275 bytes src/py_crane/boom.py | 6 +- src/py_crane/mobile_crane.py | 6 +- tests/test_animation.py | 52 +++++++++-------- tests/test_crane_configurations.py | 91 ++++++++++++++--------------- tests/test_crane_on_spring.py | 76 ++++++++++++------------ 8 files changed, 118 insertions(+), 113 deletions(-) diff --git a/examples/HarmonicOscillator6D.fmu b/examples/HarmonicOscillator6D.fmu index 5b20b34f9258f1349b8b3a088711688c33f0d51c..02b5dc8d01eaf7f4cf6b799350eaa3df51b0b64a 100644 GIT binary patch delta 121591 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

ZMT6s~E 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 Zz>;7Y$D*@Oy7DBnRm+RZ9&pRJ^glC8b&>!8 delta 6064 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 diff --git a/examples/MobileCraneStraight.fmu b/examples/MobileCraneStraight.fmu index b9e1c35a835cacbc43d0ab453b232da5466391b3..27889b4bac39c0bd2eb0aa59836d98bc343f89c1 100644 GIT binary patch delta 158050 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 t4sZ$FWTdR)a*MFKTSa(VOy&Rp delta 222624 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 diff --git a/src/py_crane/boom.py b/src/py_crane/boom.py index 2b24794..fc7f5be 100644 --- a/src/py_crane/boom.py +++ b/src/py_crane/boom.py @@ -554,7 +554,7 @@ def ivp_fun( and self.mass_center[0] * self.length > 1e-10 # pendulum length not too short ): R = self.mass_center[0] * self.length # pendulum radius wrt. center of mass - if self.additional_checks: + if self.model.current_time > 6.0: # ??self.additional_checks: e0 = Boom._energy(self.mass, self._c_m, self.r_v) lz0 = Boom._angular_momentum(self.mass, self._c_m, self.r_v) if np.allclose(self.origin, self.anchor0.end): @@ -570,6 +570,10 @@ def ivp_fun( self._new_len = None if R > 1e-6: + if self.additional_checks: + e0 = Boom._energy(self.mass, self._c_m, self.r_v) + lz0 = Boom._angular_momentum(self.mass, self._c_m, self.r_v) + r2 = R * R g = np.array((0.0, 0.0, -9.81), float) r = R * self.direction diff --git a/src/py_crane/mobile_crane.py b/src/py_crane/mobile_crane.py index f6dd114..2f7a1b2 100644 --- a/src/py_crane/mobile_crane.py +++ b/src/py_crane/mobile_crane.py @@ -36,7 +36,7 @@ def __init__( boomAngle: str = "90deg", wire_mass_range: tuple[str, str] = ("50kg", "2000 kg"), wire_mass: str | None = None, - wire_length: float = 1e-6, + wire_length: float = 0.1, **kwargs: Any, ): super().__init__(name=name, description=description, author=author, version=version, degrees=degrees, **kwargs) @@ -63,9 +63,9 @@ def __init__( mass_center=0.99, mass_rng=wire_mass_range, boom=(f"{wire_length}m", "90deg", "0 deg"), - boom_rng=(("1e-6 m", boomLength1), (), ()), + boom_rng=((f"{wire_length}m", boomLength1), (), ()), q_factor=50.0, - animationLW=2, + additional_checks=True, ) # make sure that _comSub is calculated for all booms: diff --git a/tests/test_animation.py b/tests/test_animation.py index e9d118e..4f85884 100644 --- a/tests/test_animation.py +++ b/tests/test_animation.py @@ -1,10 +1,11 @@ import logging +from functools import partial from typing import Any, Generator import matplotlib.pyplot as plt import numpy as np import pytest # noqa: F401 -from component_model.utils.controls import Controls +from component_model.utils.controls import Control, Controls from matplotlib.animation import FuncAnimation from py_crane.animation import AnimateCrane, AnimatePlayBackLines @@ -48,6 +49,14 @@ def _crane(): return crane +def _boom(b: Boom, idx: int, x: float | None = None) -> float: + """Move boom 'b', or return current setting. Used as partial() to define Control.""" + if x is not None: + arg = [None if i != idx else x for i in range(3)] + b.boom_setter(arg) + return b.boom[idx] + + def animate_sequence(crane: Crane, seq: tuple[tuple[Boom, float], ...] = (), nSteps: int = 10): """Generate animation frames for a sequence of rotations. To be used as 'update' argument in FuncAnimation. A sequence element consists of a boom and an angle, which then is rotated in nSteps. @@ -120,29 +129,24 @@ def movement(crane: Crane, dt: float = 0.01, t_end: float = 10.0) -> Generator[t """ # initial definition of controls and start values controls = Controls(limit_err=logging.WARNING) # CRITICAL) - f, p, b1, r = list(crane.booms()) - controls.append("turn", (None, (-0.31, 0.31), (-0.16, 0.16))) # free rotation, max 1 turn/20sec, 2sec to max - controls.append("luff", ((0, 1.58), (-0.18, 0.09), (-0.09, 0.05))) # 90 deg, 5/-2.5 deg/sec, 2sec to max - controls.append("boom", ((8, 50), (-0.2, 0.1), (-0.1, 0.05))) # 8m..50m, 0.1/-0.2 m/sec, 2sec to max - controls.append("wire", ((0.5, 50), (-0.1, 1.0), (-0.05, 0.1))) # 0.5m..50m, -0.1/1 m/sec, 2sec to max - controls.current[2][0] = 8.0 # b1 starts with 8m - controls.current[1][0] = np.radians(90) # b1 starts at 90 deg - controls.current[3][0] = 0.5 # wire length starts 0.5m + f, p, b1, w = list(crane.booms()) + controls.extend( + ( + Control("turn", (None, (-0.31, 0.31), (-0.16, 0.16)), rw=partial(_boom, p, 2)), + Control("luff", ((0, 1.58), (-0.18, 0.09), (-0.09, 0.05)), rw=partial(_boom, b1, 1)), + Control("boom", ((8, 50), (-0.2, 0.1), (-0.1, 0.05)), rw=partial(_boom, b1, 0)), + Control("wire", ((0.5, 50), (-0.1, 1.0), (-0.05, 0.1)), rw=partial(_boom, w, 0)), + ) + ) # From time 0 we set three goals - controls.setgoal("turn", 0, np.radians(90), 0.0) # turn pedestal 90 deg - controls.setgoal("luff", 0, np.radians(45), 0.0) # luff boom to 45 deg - controls.setgoal("boom", 1, 0.1, 0.0) # increase length 0.1m/s + controls["turn"].setgoal(0, np.radians(90)) # turn pedestal 90 deg + controls["luff"].setgoal(0, np.radians(45)) # luff boom to 45 deg + controls["boom"].setgoal(1, 0.1) # increase length 0.1m/s for time in np.linspace(0.0, t_end, int(t_end / dt) + 1): - if time > 10 and controls.goals[3] is None: # Start to increase wire length with 1m/s - controls.setgoal("wire", 1, 1.0, 10.0) + if time > 10 and not len(controls[3].goal): # Start to increase wire length with 1m/s + controls["wire"].setgoal(1, 1.0) controls.step(time, dt) - if controls.goals[3] is not None: - r.boom_setter((controls.current[3][0], None, None)) - if controls.goals[1] is not None or controls.goals[2] is not None: - b1.boom_setter((controls.current[2][0], controls.current[1][0], None)) - if controls.goals[0] is not None: - p.boom_setter((None, None, controls.current[0][0])) crane.do_step(time, dt) yield (time + dt, crane) @@ -169,7 +173,7 @@ def test_playback_lines(show: bool = False): np.array(((1, 0, 0),), float), # 'wire' ) ani = AnimatePlayBackLines( - data=data0, lw=(10, 2, 1), figsize=(10, 10), interval=1000, title="Test of AnimatePlayBackLines" + data=data0, lw=(10, 2, 1), figsize=(10, 10), interval=1000, title="Test of AnimatePlayBackLines. Static." ) if show: ani.do_animation() @@ -182,7 +186,7 @@ def test_playback_lines(show: bool = False): np.array(((1, 0, 0), (2, 1, 0), (3, 2, 0), (4, 3, 0)), float), # 'wire' ) ani = AnimatePlayBackLines( - data=data, lw=(10, 2, 1), figsize=(10, 10), interval=1000, title="Test of AnimatePlayBackLines" + data=data, lw=(10, 2, 1), figsize=(10, 10), interval=1000, title="Test of AnimatePlayBackLines. 4 time steps" ) assert np.allclose(ani.axes_lim, [[-1, 5], [-1, 5], [-1, 5]]) if show: @@ -190,7 +194,7 @@ def test_playback_lines(show: bool = False): if __name__ == "__main__": - retcode = 0 # pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) + retcode = pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" logging.basicConfig(level=logging.DEBUG) parsolog = logging.getLogger("parso") @@ -200,4 +204,4 @@ def test_playback_lines(show: bool = False): # test_animation_sequence(_crane(), show=True) # test_animation_control(_crane(), show=True) - test_playback_lines(show=True) + # test_playback_lines(show=True) diff --git a/tests/test_crane_configurations.py b/tests/test_crane_configurations.py index f078e2d..6cd294b 100644 --- a/tests/test_crane_configurations.py +++ b/tests/test_crane_configurations.py @@ -1,12 +1,14 @@ import logging +from functools import partial from math import sqrt from typing import Generator import numpy as np import pytest # noqa: F401 -from component_model.utils.controls import Controls +from component_model.utils.controls import Control, Controls from py_crane.animation import AnimateCrane +from py_crane.boom import Boom from py_crane.crane import Crane logger = logging.getLogger(__name__) @@ -56,6 +58,14 @@ def _mobile_crane(): return crane +def _boom(b: Boom, idx: int, x: float | None = None) -> float: + """Move boom 'b', or return current setting. Used as partial() to define Control.""" + if x is not None: + arg = [None if i != idx else x for i in range(3)] + b.boom_setter(arg) + return b.boom[idx] + + def move_mobile_crane( crane: Crane, dt: float = 0.01, t_end: float = 10.0 ) -> Generator[tuple[float, Crane], None, None]: @@ -65,29 +75,24 @@ def move_mobile_crane( """ # initial definition of controls and start values controls = Controls(limit_err=logging.WARNING) # CRITICAL) - f, p, b1, r = list(crane.booms()) - controls.append("turn", (None, (-0.31, 0.31), (-0.16, 0.16))) # free rotation, max 1 turn/20sec, 2sec to max - controls.append("luff", ((0, 1.58), (-0.18, 0.09), (-0.09, 0.05))) # 90 deg, 5/-2.5 deg/sec, 2sec to max - controls.append("boom", ((8, 50), (-0.2, 0.1), (-0.1, 0.05))) # 8m..50m, 0.1/-0.2 m/sec, 2sec to max - controls.append("wire", ((0.5, 50), (-0.1, 1.0), (-0.05, 0.1))) # 0.5m..50m, -0.1/1 m/sec, 2sec to max - controls.current[2][0] = 8.0 # b1 starts with 8m - controls.current[1][0] = np.radians(90) # b1 starts at 90 deg - controls.current[3][0] = 0.5 # wire length starts 0.5m + f, p, b1, w = list(crane.booms()) + controls.extend( + ( + Control("turn", (None, (-0.31, 0.31), (-0.16, 0.16)), rw=partial(_boom, p, 2)), + Control("luff", ((0, 1.58), (-0.18, 0.09), (-0.09, 0.05)), rw=partial(_boom, b1, 1)), + Control("boom", ((8, 50), (-0.2, 0.1), (-0.1, 0.05)), rw=partial(_boom, b1, 0)), + Control("wire", ((0.5, 50), (-0.1, 1.0), (-0.05, 0.1)), rw=partial(_boom, w, 0)), + ) + ) # From time 0 we set three goals - controls.setgoal("turn", 0, np.radians(90), 0.0) # turn pedestal 90 deg - controls.setgoal("luff", 0, np.radians(45), 0.0) # luff boom to 45 deg - controls.setgoal("boom", 1, 0.1, 0.0) # increase length 0.1m/s + controls["turn"].setgoal(0, np.radians(90)) # turn pedestal 90 deg + controls["luff"].setgoal(0, np.radians(45)) # luff boom to 45 deg + controls["boom"].setgoal(1, 0.1) # increase length 0.1m/s for time in np.linspace(0.0, t_end, int(t_end / dt) + 1): - if time > 10 and controls.goals[3] is None: # Start to increase wire length with 1m/s - controls.setgoal("wire", 1, 1.0, 10.0) + if time > 10 and not len(controls[3].goal): # Start to increase wire length with 1m/s + controls["wire"].setgoal(1, 1.0) controls.step(time, dt) - if controls.goals[3] is not None: - r.boom_setter((controls.current[3][0], None, None)) - if controls.goals[1] is not None or controls.goals[2] is not None: - b1.boom_setter((controls.current[2][0], controls.current[1][0], None)) - if controls.goals[0] is not None: - p.boom_setter((None, None, controls.current[0][0])) crane.do_step(time, dt) yield (time + dt, crane) @@ -96,7 +101,7 @@ def test_mobile_crane(show: bool = False): crane = _mobile_crane() if not show: # if nothing can be shown, we do not need to run it return - ani = AnimateCrane(crane, move_mobile_crane, dt=0.1, t_end=9.0) # type: ignore ## It is a Generator! + ani = AnimateCrane(crane, move_mobile_crane, dt=0.1, t_end=15.0) # type: ignore ## It is a Generator! ani.do_animation() @@ -152,19 +157,20 @@ def move_knuckle_boom_crane( # initial definition of controls and start values controls = Controls(limit_err=logging.WARNING) # CRITICAL) f, p, b1, b2, b3, w = list(crane.booms()) - controls.append("luff3", (None, (-0.09, 0.09), (-0.09, 0.05))) # 360 deg, 5/-2.5 deg/sec, 2sec to max - controls.append("luff2", (None, (-0.18, 0.09), (-0.09, 0.05))) # 360 deg, 5/-2.5 deg/sec, 2sec to max - controls.append("luff1", (None, (-0.09, 0.04), (-0.09, 0.05))) # 8m..50m, 0.1/-0.2 m/sec, 2sec to max - controls.append("wire", ((0, 50), (-0.1, 1.0), (-0.5, 0.5))) # 0m..50m, -0.1/1 m/sec, 2sec to max - controls.append("turn", (None, (-0.9, 0.9), (-0.9, 0.9))) # 360 deg, 5/-2.5 deg/sec, 2sec to max - controls.current[0][0] = b3.boom[1] - controls.current[1][0] = b2.boom[1] - controls.current[2][0] = b1.boom[1] + controls.extend( + ( + Control("luff3", (None, (-0.09, 0.09), (-0.09, 0.05)), rw=partial(_boom, b3, 1)), + Control("luff2", (None, (-0.18, 0.09), (-0.09, 0.05)), rw=partial(_boom, b2, 1)), + Control("luff1", (None, (-0.09, 0.04), (-0.09, 0.05)), rw=partial(_boom, b1, 1)), + Control("wire", ((0, 50), (-0.1, 1.0), (-0.5, 0.5)), rw=partial(_boom, w, 0)), + Control("turn", (None, (-0.9, 0.9), (-0.9, 0.9)), rw=partial(_boom, p, 2)), + ) + ) # Set initial goals - controls.setgoal("luff3", 0, np.radians(0), 0.0) - controls.setgoal("luff2", 0, np.radians(45), 0.0) # luff boom to 45 deg - controls.setgoal("luff1", 0, 0.0, 0.0) # luff to align with pedestal + controls["luff3"].setgoal(0, np.radians(0)) + controls["luff2"].setgoal(0, np.radians(45)) # luff boom to 45 deg + controls["luff1"].setgoal(0, 0.0) # luff to align with pedestal for time in np.linspace(0.0, t_end, int(t_end / dt) + 1): if abs(time - 10.0) < 1e-6: # goal2 reached assert np.allclose(p.end, (0, 0, 1)), "Pedestal unchanged (straight up)" @@ -175,27 +181,16 @@ def move_knuckle_boom_crane( _b3 = (-12 / sqrt(2), 0, 4 + 12 / sqrt(2)) assert np.allclose(b2.end, _b2, atol=0.01), f"45 deg in negative x-direction? {b2.end}!={_b2}" assert np.allclose(b3.end, _b3, atol=0.01), f"45 deg in negative x-direction? {b3.end}!={_b3}" - controls.current[3][0] = 1e-6 - controls.setgoal("wire", 0, 10.0, 35.0) + controls["wire"].setgoal(0, 10.0) if abs(time - 47.0) < 1e-6: # turn the crane (causes pendulum actions) _w = (-12 / sqrt(2), 0, 4 + 12 / sqrt(2) - 10) assert np.allclose(w.end, _w, atol=0.01), f"Load position before turn {w.end}!={_w}" w.damping(q_factor=100.0) - controls.setgoal("turn", 2, 0.09, 47.0) + controls["turn"].setgoal(2, 0.09) if abs(time - 54.0) < 1e-6: # stop turning (causes pendulum actions) - controls.setgoal("turn", 2, None, 54.0) + controls["turn"].setgoal(2, None) controls.step(time, dt) - if controls.goals[3] is not None: - w.boom_setter((controls.current[3][0], None, None)) - if controls.goals[2] is not None: - b1.boom_setter((None, controls.current[2][0], None)) - if controls.goals[1] is not None: - b2.boom_setter((None, controls.current[1][0], None)) - if controls.goals[0] is not None: - b3.boom_setter((None, controls.current[0][0], None)) - if controls.goals[4] is not None: - p.boom_setter((None, None, controls.current[4][0])) crane.do_step(time, dt) yield (time + dt, crane) @@ -217,5 +212,5 @@ def test_knuckle_boom_crane(show: bool = False): pillog = logging.getLogger("PIL") pillog.setLevel(logging.WARNING) - # test_mobile_crane(show=True) - test_knuckle_boom_crane(show=True) + test_mobile_crane(show=True) + # test_knuckle_boom_crane(show=True) diff --git a/tests/test_crane_on_spring.py b/tests/test_crane_on_spring.py index 17b1e5c..c6664b7 100644 --- a/tests/test_crane_on_spring.py +++ b/tests/test_crane_on_spring.py @@ -10,9 +10,10 @@ import numpy as np import pytest from component_model.model import Model -from component_model.utils.controls import Controls +from component_model.utils.controls import Control, Controls from py_crane.animation import AnimateCrane +from py_crane.boom import Boom from py_crane.crane import Crane sys.path.insert(0, str(Path(__file__).parent.parent / "examples")) @@ -37,11 +38,11 @@ def mobile_crane_fmu(): return _get_fmu("MobileCrane.fmu") -def make_fmu(build_path: Path, source: str, resource: Path, newargs: dict[str, Any] | None = None): +def make_fmu(build_path: Path, source: Path, resource: Path, newargs: dict[str, Any] | None = None): """Make single FMU. Non-default arguments allowed.""" build_path.mkdir(exist_ok=True) fmu = Model.build( - str(build_path / source), + str(source), project_files=[resource], dest=build_path, newargs=newargs, @@ -52,25 +53,27 @@ def make_fmu(build_path: Path, source: str, resource: Path, newargs: dict[str, A def make_fmus(): """Re-make the FMUs. Only default arguments used here.""" make_fmu( - Path(__file__).parent.parent / "examples", "mobile_crane.py", Path(__file__).parent.parent / "src" / "py_crane" + Path(__file__).parent.parent / "examples", + Path(__file__).parent.parent / "src" / "py_crane" / "mobile_crane.py", + Path(__file__).parent.parent / "src" / "py_crane", ) make_fmu( - Path(__file__).parent.parent.parent / "component-model" / "examples", - "oscillator_6dof_fmu.py", + Path(__file__).parent.parent / "examples", + Path(__file__).parent.parent.parent / "component-model" / "examples" / "oscillator_6dof_fmu.py", Path(__file__).parent.parent.parent / "component-model" / "src" / "component_model", ) shutil.copy( Path(__file__).parent.parent.parent / "component-model" / "examples" / "HarmonicOscillator6D.fmu", - Path(__file__).parent.parent / "src" / "py_crane" / "examples", + Path(__file__).parent.parent / "examples", ) def make_mobile_crane_straight(): fmu = make_fmu( Path(__file__).parent.parent / "examples", - "mobile_crane.py", + Path(__file__).parent.parent / "src" / "py_crane" / "mobile_crane.py", Path(__file__).parent.parent / "src" / "py_crane", - newargs={"pedestalCoM": (0.5, 0, 0), "boomAngle": "0 deg", "wire_length": "5 m"}, + newargs={"pedestalCoM": (0.5, 0, 0), "boomAngle": "0 deg", "wire_length": 5}, ) shutil.copy(fmu, fmu.parent / "MobileCraneStraight.fmu") @@ -86,6 +89,14 @@ def _get_fmu(fmu_file: str) -> Path: return fmu +def _boom(b: Boom, idx: int, x: float | None = None) -> float: + """Move boom 'b', or return current setting. Used as partial() to define Control.""" + if x is not None: + arg = [None if i != idx else x for i in range(3)] + b.boom_setter(arg) + return b.boom[idx] + + # def test_visual_simulation_1(): # sim = VisualSimulator() # sim.start( @@ -281,32 +292,24 @@ def movement(crane: Crane, dt: float, t_end: float) -> Generator[tuple[float, Cr """ # initial definition of controls and start values controls = Controls(limit_err=logging.WARNING) # CRITICAL) - f, p, b1, r = list(crane.booms()) - controls.append( - "turn", (None, (-0.31, 0.31), (-1, 1)) - ) # (-0.16, 0.16))) # free rotation, max 1 turn/20sec, 2sec to max - controls.append("luff", ((0, 1.58), (-0.18, 0.09), (-0.09, 0.05))) # 90 deg, 5/-2.5 deg/sec, 2sec to max - controls.append("boom", ((8, 50), (-0.2, 0.1), (-0.1, 0.05))) # 8m..50m, 0.1/-0.2 m/sec, 2sec to max - controls.append("wire", ((0.5, 50), (-0.1, 1.0), (-0.05, 0.1))) # 0.5m..50m, -0.1/1 m/sec, 2sec to max - f, p, b1, r = list(crane.booms()) - controls.current[2][0] = 8.0 # b1 starts with 8m - controls.current[1][0] = np.radians(90) # b1 starts at 90 deg - controls.current[3][0] = 0.5 # wire length starts 0.5m + f, p, b1, w = list(crane.booms()) + controls.extend( + ( + Control("turn", (None, (-0.31, 0.31), (-1, 1)), rw=partial(_boom, p, 2)), + Control("luff", ((0, 1.58), (-0.18, 0.09), (-0.09, 0.05)), rw=partial(_boom, b1, 1)), + Control("boom", ((8, 50), (-0.7, 0.5), (-0.1, 0.05)), rw=partial(_boom, b1, 0)), + Control("wire", ((0.5, 50), (-0.1, 1.0), (-0.05, 0.1)), rw=partial(_boom, w, 0)), + ) + ) # From time 0 we set three goals - controls.setgoal("turn", 0, np.radians(90), 0.0) # turn pedestal 90 deg - # controls.setgoal("luff", 0, np.radians(45), 0.0) # luff boom to 45 deg - # controls.setgoal("boom", 1, 0.1, 0.0) # increase length 0.1m/s + controls["turn"].setgoal(0, np.radians(90)) # turn pedestal 90 deg + controls["luff"].setgoal(0, np.radians(45)) # luff boom to 45 deg + # controls["boom"].setgoal(0, 12.0) # increase length to 15m (0.1m/s) for time in np.linspace(0.0, t_end, int(t_end / dt) + 1): - # if time > 10 and controls.goals[3] is None: # Start to increase wire length with 1m/s - # controls.setgoal("wire", 1, 1.0, 10.0) + if time > 10 and not len(controls[3].goal): # Start to increase wire length with 1m/s + controls["wire"].setgoal(1, 1.0) controls.step(time, dt) - if controls.goals[3] is not None: - r.boom_setter((controls.current[3][0], None, None)) - if controls.goals[1] is not None or controls.goals[2] is not None: - b1.boom_setter((controls.current[2][0], controls.current[1][0], None)) - if controls.goals[0] is not None: - p.boom_setter((None, None, controls.current[0][0])) crane.do_step(time, dt) # takes updated force, x and v and calculates updated x and v # crane.boom0.translate(-crane.boom0.origin) yield (time + dt, crane) @@ -318,7 +321,7 @@ def movement(crane: Crane, dt: float, t_end: float) -> Generator[tuple[float, Cr m=1e7, bL=8.0, wL=1.0, wM=1000.0, wQ=10000 ) # define a default crane on spring - ani = AnimateCrane(crane, movement=movement, dt=0.01, t_end=1.2, interval=10, osc=osc) # type: ignore ## It is a Generator! + ani = AnimateCrane(crane, movement=movement, dt=0.01, t_end=15, interval=10) # type: ignore ## It is a Generator! ani.do_animation() @@ -329,10 +332,9 @@ def movement(crane: Crane, dt: float, t_end: float) -> Generator[tuple[float, Cr 'test_mobile_crane_*.py' for the crane 'test_oscillator_6dof_fmu.py' from the component-model package for the oscillator """ - retcode = pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) + retcode = 0 # pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" - # make_fmus() - # make_mobile_crane_straight() - # test_mobilecrane(show=True) + make_fmus() + make_mobile_crane_straight() # test_crane_on_spring( show=True) - # test_controlled_crane_on_spring(show=True) + test_controlled_crane_on_spring(show=True) From d55811dc02a8f477eea2c8fbb2a2ceadfbc6a838 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Mon, 9 Mar 2026 15:54:02 +0100 Subject: [PATCH 05/53] Updated pendulum tests + related changes/findings on code base. Pendulum related code in separate class Wire --- examples/HarmonicOscillator6D.fmu | Bin 827588 -> 827674 bytes examples/MobileCrane.fmu | Bin 923158 -> 927868 bytes examples/MobileCraneStraight.fmu | Bin 923275 -> 961149 bytes src/py_crane/animation.py | 7 +- src/py_crane/boom.py | 335 ++++++++++++++-------- src/py_crane/boom_fmu.py | 95 ++++--- src/py_crane/crane.py | 10 +- src/py_crane/crane_fmu.py | 16 +- src/py_crane/mobile_crane.py | 6 +- tests/test_animation.py | 6 +- tests/test_crane.py | 430 ++++++++++++++++------------- tests/test_crane_configurations.py | 23 +- tests/test_crane_on_spring.py | 21 +- tests/test_mobile_crane_FMU.py | 4 +- 14 files changed, 570 insertions(+), 383 deletions(-) diff --git a/examples/HarmonicOscillator6D.fmu b/examples/HarmonicOscillator6D.fmu index 02b5dc8d01eaf7f4cf6b799350eaa3df51b0b64a..e2a8f79dcd34733c92a1d604dc9e071674e2f47b 100644 GIT binary patch delta 1071 zcmY*XOH30{6rFjm&{A%NsztFFry>@?ke1SqL=8&Bh{pI!6fM@q7DX!{;wKV>`U%Ep zP)E5ifUQyM!VLt&EL>r_z`wx-(fA?i!Wa|df*3dIn~q?atMg>+o(}Ku){mi`uXZeKF29oDDqkJaU4N6IQnG*G!_2P zk-^6kug&!FfgSBJBQM8{q_AVgt4&qjw%hF|^~*Upha4 zEsp1ryDi9$h& zT=AOJ(@9g_lxc0fKO{6C35IHl>ijHCsHv^E{%~5a{pa&m1>mN!j3+ehqrU6 zAk%l`RHgD@?Psl&LBo*3kjkKC&@rSj=o!-GvsP$yD{lO?8&)QQQXf?6nBv_xemMm% z`rw-23EDmGpsm>L3IXB-xaX$#j-t6u6J&;Ei1K`lsa7Ur9H6H znK?^^`Wp&MMqV7So6*(poIT{8QrjjZiw@EQvwsV#Cbh7Nsx)#D9MD$bBDrfDMls8!V{Dj zRKtj6n&E*(HGH!W6E2aUfLL=?<)8!({treZSVumVB(%;RaFN!4y5AolX}Gd{(@2SS7{1;3WFfDrr^7<})gJ$_PDnfh4Y8W~Hf^*9UVf)2Eo@3Ma3` z)}io7G|A6~+eyvul9aJNu+f^INy^5b1kim03h3J$`oGk<8_c-dI;9x#LNCAqWhA8d zX<-&CrXV}1cpLu=Ga^ty=j2m-Z>9Oz7J&lZi}xbn7L?54M|o?~S%!kSNtr|8)jlLq mSe%%a!YG*O86RM!4{|XS1^7RSZBfYLUm`}qnriA)y7?DjU0Le@ delta 951 zcmZ8fZAep57{2G6TAJ^)rAus9w-v3nw7YekZT4XaK`m>2Vx&!7i+)5SQG?2A23b*9 zt$gREZBgW}NRhe_eP9_u31SeUC1?;yDE24nPtmzvQ0j1>bI*u%1&^A`#C76< zvhejf6YlG$jksWdW+hKWaoqsjrhMtAYlw1bfZkwA&0T8E%9e7am12e8?-maq4OUc( zr^W5TV*$Pu4=B%`(%QMn9i2e6Tg0E2VXwg|T#}AgJIM zh!Y7gA}xn%QMTJ1t1APC?aqKh4pjQr$o2}S%jJ~)u7Ko{WNa#jqI7wWE|jV!xJdN>^aN)`ph#^27DvFunhhk}VK?E0 z2-w+vr$!#OlO!C9Kn|;IpHk+cyBVzP`zuXZ(M?kDs;YRp32uz>LVBA)rkfcS#$X~EWHkw?oX>03g3J$mM1H9)o#oP*ctmZORt82PLb$F^3G8g@8HbJmWx>%bY zhu&aEE0nO;pQgXcxeXGV^K)7V-3BMSkjx-z;oC zhT{D1x-9r04!emCNdh#N6eM`o*g-&_53uBfYZNei7i9` diff --git a/examples/MobileCrane.fmu b/examples/MobileCrane.fmu index 8046fb49156c6264909410b41db96f0fff4e31e5..c455a21bf87e4603b7600fd5a3be0d4c4a77c7e0 100644 GIT binary patch delta 8769 zcmb_hdvH`$n$NwbJCAZa*`0~4xLUX0fdGqF(5B_1Q3J}!%g??q@~l{bl-G< z7z4UB=#G|;JMvlXl!EK*xKoHSI`+(>AhoMCW35!sU3JUdVJK&2eT=I(iaI#B-#NFt z)7@mO`D0oyx##tr?|hHn_nq$^KDOwa$wieL*Ahcn9{Y*Ift|r0-q&uh=H(eqR2mDO zt1mb$2n|pdBlNlaBDVmOkBN((ilMPmJ(q9)`!>9#;rNnE%~{IDj0M9cyd_Xq_VecN0dQ-XfiC=TnBVDfRX zT=Whne*d`mp2DllmLF=s-9;Oh*Ii5S6JvsO$|41QmK)I1p0lSZcvIjSb1c zARSO>AQV&OkQ$Ix#Yg2BXWP|~sQ5xG7r^>wu?SwgB>EFS`%3H)8be`~24eaWuVUv1 zkwf00a1=LZ2X;82XAdb$T>B@{LJ|-CSsW6;|E)L(Umg@@6tVey9vu7+V$(|I8nh*r za(Mq+F=|rxMRbCWpAC{r=8BwclH?(5AuC8o8HHb1h+BK`ofhp#lM$}7k{vbEFqd4F z_=%Ognx9x#MP>`yk5aYr@z+yI@F%+}tunBg``ZqwFx79`G7@=HVRY&h9O z{vG<8NoC^kYe<4jSWVub9Mi>SzX3(>@K{Y0e+>-{tA`^&WmpNRF{%zJRQCA-Y9Jhv zgESHiN0g`MaBqJ(@S!I5E#>P8ANC-uq0kw9qB=%g-}OFQ@b zw!?oEmx`@UICxRCf%duB1I0&3ffmU(b6c5cNa?--RZe%0%F#h2)38ns`D8SqeH1%J z7_&4rkurh$!y_Rd9UW3a8J2<@b8R>1M`g!NFX~Lvc16Mw`_!hgV`?rFldHc!5DKXM z{oIJ0bU5r&f=(KehtVri#5%cAH#h6tIw);KCQycQFtA_YGG(M_KxO)b6_-R09IqgS z&~ZtuhlxvKDZKPK8nAXrq!Zz2U@#EskJC;Xin#VG(QwS(fgTeK%POZFSAt=0K;6eQ z5*vx3$YUt#DXzJ(C#lF@?_Z>YV@gno%1lvHv?it@<{kbk+3})Ulqe%M%xuk$Jo_@_ z$vHz}-^JBLE?a*M&1idnR0;O`0)D?omlU2Fb;1;Z19-$Zn)N^|92`*<%BCq%CFEtG zLenN2R6>L5(DEEXq6_1|6xEQNE9#_$_p5f+UlFT|Qr6+n=2@BweQM|Wa7b~0 zuZ%ojmbr+cX;TIjt>wfC&zMNT;cjAt<2R6Uc%qyvfeYni5zML}RWkxH++T+?oiPWj zsUS7hWP`TScB=_SD~Jm=J|!Aq<5p4x30w&i&yajLTS~!#whj6*_QT#evyLdikW?PO{RXv%;)S4kgYw$=fBf zMk46CN`Hoy>~T4ml$G}BKzUh^UPrlgWX#bn4%!%KmS*%JSzM{rpRFulOWdvn+L=F8 z!eoRLdv1&>3iV^OU|x?Vm{zP$Q57#n4Hw;%vIQj?4M#n642g|5INd61<4&0|w<`x77}94f0PiyLT0L!{cD6?| zRp~sY4SdrnVNY{&y8~LfNYzAg*1V~)3{YSKZ=`YB!tu@*R_#$|+|e3sM_O?lk>lZj z4>43$X$WyY8t`IV3dCX~7?XmEj6-1>Q=@WdP{A}|NR9`>Bhf5->n3A?(9!di}^IEtT0IJ`eg0s4{U22b%SFBbAuu(G#R0O%Y>C+Ow8a9F`E4 z?_Win29`p?=~XC*qnk;Y_UyF=D7lg3>-~E+6Ne$r773bQgY~P)7{PPFn}%&L ztRxNE`6lD)WMt$%)xr$Q?LbKPsZ43P%F199qZkW5cp{9x2x(T6R{wRSB@?K@{XL-; zdRCGe*d+=EpnHYdQr#0e(T7>0&mz#nD~SV7W7H@$z`w2}o70WjRixO+u^sl*5u2$I zb3@$E?r(r)TS$$^MWcI@9>E+J-{ZP>;3@jCg@00QS#RTkSdR;OjF^c3?`k3nCY;3_ ziJ95|ts$>-()CChDNi{X!tYqLJ;USR*K5fqh0Hrx`|vXG{PqvP(8n+2Aj2! zVN4dgb6pg9iiBf4(aj*;j6RHhlY${QVI<{<37vFbzuMd3fs5ag3hl_p1}QrdbMe{` zvyzsk2SG$Y?ZOB3HlO0>#)i7b^pz(l3~Z{vy-hG$d4j=go@EwUc2;6wsR2x^Ahk`H z&3Kp@c!w14Zp?P0)SrrX)Ef@z89SVKRjgbxO}NF}$DTY^>d;P_dSf<*thdlMmqxEC z*IxdS0S>GqYv9~EvKAWGV=}Qt6iQI#92`-~i4E&XiI8gpRz$SWU{LXlwJmbDz+-C- z72CLF`OsVVeE{{1>Se|@)z#kTaNsp>3OnT#aN^}5paX5r5_cch3%^mFok0p<_XdKX zT8GzVba@$by)=;Fol%7zQQ(;bOnfco7xG*d522lFs^mSEq}y@3W4(pRgqKf|(g`bC zC3@D;SIS}OR#L2O{Ba@Upk(U0)X$Z}m2eHu=^`#nM&#h2D-@0n>s~gMm^<(`tX-iM z3_UDty6%pFn098+#=NwU0jUYTw*nF7TUxH|`=~^l{6k@p(uUTOjX?0gs89gU_K+ry zMJykJSz9po`?!a!o$6Y-iPS=16Iqn%vcmCAq=vVCxruBh=zZYaOs?TQx4wpVlHSe4 z!dj-_{`9sC;5hK<+it8!KzVU9*)$E++AU-=)Ek5vh^)n%bmX53d9ns6PUdKGqm5~y z2oAUroW`EP6Nj&e1@JE$$U^9+*!*-0S)N#OGg=(Py75B$^R1*u;F>Gux#ccGh5*K*ExO?!w9^6Q8jJauF?7ENG#9hnDr*O4Yz{g7xW!tlWUYoze* zcCtjozq_hQHMH&^MM9%?alEX6;{o<>+(D?`RqkdBQa>daQ}7PYC&N=a$a>6R*|>hs zze$d%8~9Z+W)BbChW^Jc{Q7FV@~={Yc(G5;9SR41ENNyr9u^5yd|}vvk%T2#7++bb z#-&>>wFg)sfL;1qQ(lH=VT8LWjfM40&4+nfoL{?Qm;r=X{U)6VVD$!#T?Z+jD}yKY z`mijP8#$*S6p6M~Xz4y>&xm3lmiOX4tg|Dzmo5a33t=n}Ogk+_8sV2}XPz%(LO$9- zs*C)AC?1kQ>rd__H8;}%IfgYQyah7>ct@gmH&m0SccH9Obl9GFur|acg+u+6v0(ip zqrnzHeVw6ff`#Q6b9CC_VKE#_SxKqNaXAo_vE-FWo$VmAP>{;pXs^AE)yq`;zZtLp z4EM3J71BYJQ%6MAYXiB2a4zhU1=}D-9pSYO<3|`TkyyhBVXRh1QE2J?Vt5z%3rb_B!J}aEIT&L{X`%eh-G{Mtg4*?C z@cz_^vV;;<)RAb2K}*WI*l>zxX1{WTIU5flXbQ-Zhb!I5_VJ*(q%V1?MNGzMyIqmU z1TI9YQb(}J$DElTh`FHd&@HJnpoQzy!R}Mc57^nc>t&h8YCrXl1hD{y7fp(n2VRH^ z#IVXT9FC(C`J-6pV#~w$Pn5<2KE;;y@gz^kB6IHAF2+fg9Mab0N}iV;4`)nO!W~%4 ze(B5Wmkc)L&>U243duaMmnls5eAPiSCOMEPs7VmEH;;4FT-eZOmcXxsfs8} z4v{dN9wN1g>jR`(nBX<9?6M;3YH(1rw+8;oJVP%MqVQr0Z9wndJ(y9RWJrsKq|eby zldLOAgTLm|&=JDz+Kf1XSt5#y$(j991*jZ?sqQ4vbTgMQ5`%R?a;b!?rJt2N$6f3g z+A`Bj2n>@FUY~a9BXH&{sYyICOb#0J^#xEJBWts!T>0GUOgtPTy@DA` zH?SB3559_7!c9E0GQjw+FwJ`QNrRCWUA3c6+Msa+$FU3iBcz(WSeQ&Y2Nlfo)Tq|= z<4T?tUK$|{ut^kVKvkS{!vO~|XvfS&qT2-*s|{s^sZuQnKM-r+hkFb*BTJ~V_ZOV- zXIZF*-|QhqH-@hu;su5xRyRc89EoCV2A6rN>2^cI?T* z9L|gO5-Z#vCnu63Pb?cHMlsb`v5$Nqq#B+v5>1W3v2k*_j-?Az9|dk)cQgMkfPeL` z5%Abf;+|k)*+pA)qmn6>o4r`p#)=CTQF&f~Nx!PfnE7J$ecF#Xh$k|Q$ULYrWu=I* zoET+>D2r~z(+)n0ekM~d^HanP3Uk&8ON`UFZCiI%C7zjOe>61c&)O&U zf*I^U%x*HqPwcHs9DS1<65;kr!E7GI8L5w38{Q&S)zaT>-M;g_kz@Zhurx1kU@4p! zFdUXaTkR*W*J;? z%1HQqF=3&GFkuttv24IUA^G$o34^u-?)w!HXkzbN!+(>+)_H~( z*N~le1P+zQ4fq)G$;YPvpF(_!@G;?I#>bKv!&S zo-gw5$jf67`|7Oq{Jae29JlVK9rOJI^8AI1y$jv*=X)2q2RhoW^}81?Ru(GC{Kbpr z!!yUN%d8zO?!{Z$=6e>nJqs2lzBz7%?bfXC)^May^6)+Xr%m{7NgW7dKafY{kC+v`E*B zr)zlQa4p_Q7g%v2z%Hw_THNo{C!KVP7I+;8#LAs|Pq8F*h+}{(WfhxuT?K73!XR7tP; zj<3^BZXn$Wr6p1wyjLtag=0`sBDK=&PxN_t2p;L*3kQEU$yStuS|Zu%vlFYlyxHlA zjQf8M5ZF~BRl&<_ZWe|jj@D+)z3F~Fw+sqPrLy{~z$R`Oyef2oHhHpxv2Zb`Q8w@C}h_;|AQ0UP`FP_orohFW00(%$oC71J$ysx!zR z5JGo@S|+U!PuvYB%cM2p$8jhqms;x4=XRd?RnrPH&SpTDVsJdG^d$t0tA|-hyGETZkjfyA#}^8+ja>g0m4s{?421I zYyx}E>YQiKo%h~-_uc#5zxl_Rru{!?%3HY{j5!ANcVn=t=lsCSf6BHP4B1EWOv$E@ zI4HHeP?Y>Cm!v&fk^3s-yl^SyJ}xomOe+AWj(6=^YT3Qi!YNBF{F$iG@{QF!uPms! z-q36?G&JLrr=dWZzmu1_eI2>VgKT1>%}dwD4Tfvu$|q;|4OaQvojJh>oyuA8QS_ zvxpQFqk*`@qG9F=M*{tU5VH?SA%83w8+2IGSmLJ%8}W)hpURlwv4fDejWHXeH?IFL zGaK_uePS#aWnw5WNau$`EEot$Y)il&9ZVFGVyd@LVzED_d{!0~~l+3wEynN8?aLs`ZIMUyM{O>2i7f;^0sq)bEW31|@B0 zY*_4$WxyBoDhaH;mM|LP$ zI7O>0FwDLfCJ;!$VTswbIZ}2%0iq_Mw1oa0Y$mJMO^s;|aLH#XQdK`l!Nm?JKxX8O zc-Ra{*j^18IP9Qa|&}Ov@Ya7z4p#{HSrO6f5q>S&|^3{h-AoHX6Bgn&T z)nv~bSx~~^_C1_QyP;{G3J=AV4VR6$)dyMA=!HkaL&A1U#O>>iGi3J`6E^ii3BF%p zOv0{S$d)tr8*vjo;JNoyc0VSmclSb-?4D!9L$s8O;OSnd(dg^FP=WV7#pmoF2QxZP zLTXPqI-o)=uvPNka*_#T1wy`H%ulFdVlYTv6A1bvQpm}AV^J0ii+&edFgzS1yy<%P zc?Z42v0fG%@)IuoZIB_H||?PhBhrB7g6V@X~F$2D|}0!^^W*A|5yABEctid{M1n+ zzPSWm$L*&e9a}oUY$5iIF5MgE&qwY7n2vu~MsUBh3{1v_6xQ+jGAK>aKtUqO;`g)B z-3CeeoTm*OI$&sSQu#YjObXaqco0`W=R{yM;ge2C zOHRNOH#ZpbQzoz~ACU79my(a4SO&B2aziY8ozJ`Dinp$hWoyX@BW>_y%V)7fKhED zZtQ@IHTQs_H`?uKSWn?q$7nU-m!Y^4=BO8MBnuD%M`)3XR?2qw=Atf0aFKV6<>KoP zz;cy`3Rk+^AeaACtkFiq1el_?+VfjYPvisW;ZCOq^ppNg5+_!7(*rvU!1rBI72 zyCGAxVG1^Oz%(}jjDnAr`N;wN;i#S}H25amz)PX+7S83971_u-AUS1=7zt5yw98pd z(<}O{_eUg3y=tf=XIA4Q#jhw;N)%beDAN)%x;vn-Y5|ovp3(pFurVuz&#c$^QeDtf z%OI;<6H(Xfg!ijCQ+M*j8~orQu&NTKN_C{{d-%uL8nCNZ$iSD@z=N3C1=Yz32f|m^ zK{;;dqJ&EeEO>D(%)kR((3v&iVYfrW$6Zi~V?QSiSFeTiwBW5%Sj%k8u9kFn!~jyDLUm1mOCZaM?n9i(q!*@Holv8^ zvjK)Voa2KzIOGE>{`EX$Q{^2Z`yBEC(`e3(C--m_Xz@dP{_SSq$`h-5p1uCNZ84j{ z5VPUQ0364Qsg$Mj_YwjNeoZ?5;V!CupL>I^;%#i-s%*5Jfdxv*Cdf>~O;5lhId`i! z<&{Y_m>P$MZG&OI6!frm^PT5IaB2kg-XIloO5LJ~_Er+6b!3N36qJ}c7yrVN)yt@!; z-*ZnH)X6(USioYTz~-2wF=n?Gr~=@-HporVkHk0Pguj|!@YC>Xap+X8#i4-1>my*6 zPe4lXWHnHE?~4Sqq)UQWtqvUfh%a+aULuBk1L27Ko^oQH9j0&7w3AqRSN%{mCApZr zir}dAAysU6Kn)3M+Yyn}x`^_;p4?UEbdaB`xk&vDl2sA~#hq5984|3{#k(Ci|2;kz z-~NcNDw!<4y4mX^TS*aG>2}R_py@JSQ!y0|Y8yn;_B!fD)TpQZaX4`0Wxl9zGP{YI zUE@WE{&_20#dk-bQ7L>FzGK3Z+hDoYe~#hazgV0~>vrhoBBEk%v0Me zDMFo;#NuKkAod2ewnp^F$quAWA0=pMnAm={i;8vEBT$9U?1WbNX>%(6+qWt5Uw#CR zD=$3?Cf=-KY0RZ+!6EPammEAh4i@F&Zul9e93O`WkBwi4n}xSMc;?>KJx@44@O95K z7}Qa?|2O<}4B3Pf<n|bBOV5}@C(m+H# z`vh?H+L-W56G~FC@g382GOS(nZ;TBS_#JF0JRDHtGp@23t2(GTwud8Xw@W>8y(Upy z43g{XE%T(1rFsZe&cy6tDH2vc6G%ZRqWAg{iK2zdp30m&<*dzk)hq~C>O}uOO z=wn72CK^dJl4+#SNTrcR!%QPx8GX#Mc8>Z92j6ZibLt!Fe2rrBtXWO7nwy%N8|U=a)%(QR^^Ly9ddxj(`NH)2+WJ`!)-`x$HGAso zYTRy*+ihiW`N&|Nvh$>6%Ua8nPfVCpD0oWgXjWpEumi}huU#T!o zFQ+$Szt`Wimkag$iyF}e=0AXH`!c*(F65PcE=Wv~XZ^**S_&9u?5rT357pp~O2LW; zDg-xwV6jeIsSq0ZyiGdMQAso3jHyJk>VSBxQYhq8;`nx@u$W&S$FeG+jo8>6y#mAS}8S- zYeL$|OlYT}YI531+Yo-`2Lzn7*-R&sS81n{8cL?^wCQ7-%(Ro~G^8Pann%<0+$*h+ z978kp%(eF3dtUe4bG~zSPoBH=li!4EcW)=6vyAt4B3$ocifjZDP`#xMF+QfT2Ca4&H6#JJ~xP zw!$qJ}DAygFB9a*|Tgx+mrD}Ok?jckJEezFuk9wgs|OWQ~bX?0QW+G@bVL(x8WOlcw+{k0{CnHFP<8gx#IRPjmcke$U8W;NFDHwNw!L@g{vjz&>Xu^f6`5N;|-MVDDX zE+lNmva3Bh&JDVOoMMBb7^8~Lq>am|6m_H3vH3?tX`jokBbH&GN2uNz(G`pn3?g>o zU?9kRg-dw6j6&tnl$K26B{Bj_jFyODwUB)VRny~&o`_;ZBvPrglA=jP#yt%I zN0-$xg^ntR+t73(rq_TdooBn@`&oCtX4UyQ8um{s36G~sxq_#YZ*gb*xXMh z;JJR{9;U4wctkXIo~ebxF&sRIwFCc8`*PL8$6OMg!hg8JS=j_7U|rcn@i#ohCM_O# zYgD8aKDLBl0iVtY4RHKJ+hXu;A>SKhK3gz}I4m7RpCr_Tkw7mLBaxMH1q_0aLv(Dh zmaR<7gHf>*w4wwTEtb3_xO`Sv3BTP!riBdLvz7P>GO&l$=JQ)gufQ9OC9M>%C7MR~ zQCMk*U-pm{c7(-}08`=WMO))8bWrd{XDrl%VhfAUS%_AZx^u*=h&u0Le*Rxlk)*v? z?);N~R(AHnfB}-dGhHD7TP7ilu-pRF{}xcWL!e_X(}=+BOns#_LisKnsQI zdTWDtY#dIcU}gY|Icuz3t*io}chEW4OCg+UBFlx=Y=2v$Es}x00pfvs2Z)ce4Dt9v zdN*U^If?aoEyex;Q)k}A78${GC&pP#!19IASX*VCa?$_SUsV;wqZ+DhkPmNIj)@=0tPB z;NMQ(spM&#@nUWdeu-?rYdN}$tjg|l*RjT^mb=_aX)d=tK)({6n$S|1W15QMcxULN zx)&dcAc|L$Y7TG_V;CW>#DmrFxAVek7l#_2%n?<&FOpPLgh-gZWLpaK5H@Hp{u{G? ztq=S4E9V69(F~15Aayl}(TI{P z%rMNaQDG_gc9MV1yZ0FcHFYehgr`Ec2K`K{GKTi1-1sQ#QA1Wc=e-&re&W@Zye;7M zdpy%LbsbA(3rJbp%pe!%r7VcTEiC`u-bd=PS5}vUdlwlhG{%69aU<)78Y@i^y2j7S=C;+?VTxL6~$kE@IZS!qav!7{-zj!+srKV?r3HG1e#N;t}5~lo<6R z6s8pq?V%yXAL{bp`yJ*U-viSww(q>o-&x9!S}~30v6prZ*sSqlfS`|UD|Rh0xaJZa zJX#Oci4J(9UaU70_2TQ*@Q;s3jd`L;{8-4Io?D##YVYF8zA1*-(*X)!Y!U+y7{a%| z(=CDsQ%z!(`3)*6#G<{WKs%^mn~=*DRYqd;;6z=jibgqnW;Unfqk-ox6jpX;t%epYj3YW z{*gaC_2C#Cd%E@{jcruqcqpiBj72-wZHR?7DqV6Y6z}X>FU#HX`fg}DU)$&G@(0)L z3#|*U>k4-TJA%P*FxUbwZ?LT~pEzHeIKYKhS4y|B2Xk+wG`iUSnNK6|vpUJ$jrYMm ziUcMmcW)PL&a&JGmIdB{`G*(w@3{vyj=-#4a>3v0q>%04ySd2^hDkN_*d;HK>P7z1 z1$NGm3ivu(I}M8nT`K0Pc55e(u+^E~K08m*6#*)Ygm+9$C?PqNx^* zVWXwQs27o}u0dMcY+dr`-I$y>a&FGi9v7;&NN}h@S|$0*{AIsDF;9G7!oB|jQa}Mp delta 1438 zcmah}ZA@Eb6z=VLTS`A(g&|}#g9`|O(bY?-z-oYjFv3SF0}{cJKxr=@4yj%W29ZHW z3<=A0vBqQE(ha42`~X4XZWhOw@sG*)A<@irjxgOXjoD_@#U+yX-nK4I6TR(o?>Wys z=RD{Ay1QRHPA-+08tZ{1sgz6bwXT{j8G$_~H;q%9Mo!)|sz*MjO;T(7%n!kh2h%Fm zgK7ClJ5=S!!TV~r6Ibd;95ytAL048}6&zXRds@i|64K4wTZ#@m=74tN}+gJOTT)ivPm^-6-3F^qTjitb}9Sn5#XapoqN zkY+^vfg(ukL^|GQx&7u)e)S0&k6( z6<;24LMMl(zXwZnz9N(UaD4vsJ(2O1GNu2QvhZH1ucvPyrXF?1GV?H@inXwj4NuA0 zM?lYUS-5qEOTx!@*lM;lLE}H9xuSp~eEc@#;&uz9;rea(SPnHqP$&1?fu45~`krrX zueI-XtURMqDagky9hZjZ7N`bm=cz$9O_FZ4JoE<1*$aYIz;i*8tQhjeAlcTK_$)rx z<#};v&?kEG@cmgL;Dm;dTCe00CBC$jxACq44?pbc9}*RpW#*am{NJuT2hWPUCQ4r3 z<#vnxqU2I0pO?f}hQvW1`p>fgcCL|l49aX9cYa88=$I#o@`VuDG6Y^Nr5IWxR(Wcb z#HsQ8Cq#>&eB}mnWYDy|0p2W&$h#xci2(0hUk9SHy)$BA!>uw^_W+lNHn+} zq6Ki}8^E7K^f3H#J@PIMTjyvlSi-SLXFoCE2Xm~HpHbhPAQ^aXj;3V%YnMbmMbX;@ z|5tY-u@-EZr~B2L`8Y98n;_*diB|YMOpK^rU`0_*u*fDrS(V@-3%;>r!b^)ZJA*Op=ByKG zCf1pP%tzWZFPre&3YukYtmo88s_5tRuZ8S-L8Vd|| diff --git a/src/py_crane/animation.py b/src/py_crane/animation.py index d31003c..343dc96 100644 --- a/src/py_crane/animation.py +++ b/src/py_crane/animation.py @@ -9,7 +9,7 @@ from mpl_toolkits.mplot3d import Axes3D from mpl_toolkits.mplot3d.art3d import Line3D -from py_crane.boom import Boom +from py_crane.boom import Boom, Wire from py_crane.crane import Crane logger = logging.getLogger(__name__) @@ -182,14 +182,15 @@ def __init__( # instantiate a very simple crane self.crane = Crane() self.wire = self.crane.add_boom( - name="wire", + "wire", description="The wire fixed to the fixation. Flexible connection", mass=1.0, mass_center=1.0, boom=(length, *(np.radians(angles) if degrees else angles)), q_factor=q_factor, ) - self.wire.r_v = v0 + assert isinstance(self.wire, Wire) + self.wire.cm_v = v0 self.dt = dt self.t_end = t_end self.figsize = figsize diff --git a/src/py_crane/boom.py b/src/py_crane/boom.py index fc7f5be..84eb97d 100644 --- a/src/py_crane/boom.py +++ b/src/py_crane/boom.py @@ -150,6 +150,7 @@ def __init__( self, model: "py_crane.crane.Crane", name: str, + /, description: str = "", anchor0: Boom | None = None, mass: float | str = 1.0, @@ -157,32 +158,22 @@ def __init__( boom: tuple[float, float, float] | list[float] = (1.0, 0.0, 0.0), q_factor: float = 0.0, limits: tuple[float, float, float] | None = None, - tolerance: float = 1e-5, - additional_checks: bool = False, **kwargs: Any, # for compatibility with derived classes ): + assert q_factor == 0.0, f"A boom with a flexible joint {q_factor} shall be instantiated as Wire." self._model: py_crane.crane.Crane = model self.anchor0: Boom | None = anchor0 self.anchor1: Boom | None = None # so far. If a boom is added, this is changed self._name: str = name self.description: str = description - self.q_factor: float = q_factor - self.tolerance: float = tolerance - self.additional_checks = additional_checks self.direction: TVector = np.array((0.0, 0.0, -1.0), dtype=np.float64) # default for non-fixed booms - self.r_v: TVector = np.array( - (0.0, 0.0, 0.0), dtype=np.float64 - ) # velocity of CoM relative to .origin (pendulum) - self.r_acc: TVector = np.array((0.0, 0.0, 0.0), dtype=np.float64) # acc. of CoM relative to .origin (pendulum) - # default animation line width of boom. Can be changed, e.g. 10 pedestal and 2 for wire. - # self.animationLW: int = 5 self.origin: TVector if self.anchor0 is None: # this defines the fixation of the crane as a 'pseudo-boom' boom = (1e-10, 0.0, 0.0) # z-axis in spherical coordinates self.origin = np.array((0.0, 0.0, -1e-10), dtype=np.float64) else: self.origin = self.anchor0.end - assert self.anchor0.q_factor == 0.0, ( + assert not isinstance(self.anchor0, Wire), ( f"Trying to attach boom {self.name} to flexible boom {self.anchor0.name}. Not implemented" ) self.anchor0.anchor1 = self @@ -195,7 +186,6 @@ def __init__( # rot denotes the rotation which turns (0,0,1) into the direction self._rot: Rot = Rot.identity() if self.anchor0 is None else self.anchor0._rot * rot_from_spherical(boom[1:]) self.direction = self._rot.apply((0.0, 0.0, 1.0)) - self._new_len: float | None = None # place holder for length changes # self._c_m = np.array( (0.0, 0.0, 0.0), dtype=np.float64) # just to make _c_m known. Updated by method c_m # save the current value, running method self.c_m self._c_m: TVector = self.c_m @@ -203,10 +193,10 @@ def __init__( self.mass, self._c_m, ) # updated by calc_statics_dynamics - _ = self.damping(q_factor=self.q_factor) # pre-calculate self._damping_time for usage in pendulum # self.control = Control( ('len','polar','azimuth'), limits) # control object (without goals) - self.calc_statics_dynamics(dt=None) - logger.info(f"BOOM {self._name} {self.origin}->{self.end}. |{self.length} | {self.direction} | {self.q_factor}") + if not isinstance(self, Wire): + self.calc_statics_dynamics(dt=None) + logger.info(f"BOOM {self._name} {self.origin}->{self.end}. |{self.length} | {self.direction} | {type(self)}") def __getitem__(self, idx: int | str): """Facilitate subscripting booms. 'idx' denotes the connected boom with respect to self. @@ -271,12 +261,12 @@ def boom_setter(self, val: tuple[float | None, ...] | list[float | None], ch: in ch += Change.POS.value elif Change.ROT not in Change(ch): ch += Change.ROT.value - if i == 0 and self.q_factor != 0 and abs(self.direction[2]) < 1.0 - 1e-10: - self._new_len = v # pendulum length change while swinging. Defer to .calc_statics_dynamics() + if i == 0 and isinstance(self, Wire) and abs(self.direction[2]) < 1.0 - 1e-10: + self.newlen = v # pendulum length change while swinging else: self.boom[i] = v if Change.ROT in Change(ch): # direction change - assert self.q_factor == 0, "Attempt to directly set the angle of a wire. Does not make sense" + assert not isinstance(self, Wire), "Attempt to directly set the angle of a wire. Does not make sense" if self.anchor0 is None: self._rot = self.model.rot() else: @@ -322,30 +312,12 @@ def c_m_sub(self): """ return self._c_m_sub - def damping(self, q_factor: float | None = None, damping_time: float | None = None): - """Change/set the damping properties of a flexible boom. - Ensure that _damping_time and _new_len is set correctly. - """ - if self.q_factor != 0.0: - assert self.anchor0 is not None, "Flexible first booms are so far not implemented" - # pre-calculated loss term used in .pendulum() - length = self.length if self._new_len is None else (self.length + self._new_len) / 2 - if q_factor is not None: - self.q_factor = q_factor - self._damping_time = sqrt(length / 9.81 * (self.q_factor**2 + 0.25)) - elif damping_time is not None: # new damping time. Change q_factor - self.q_factor = sqrt(damping_time**2 * 9.81 / length - 0.25) - self._damping_time = damping_time - else: - self._damping_time = 0.0 - return self._damping_time - def update_child(self, change: Change = Change.ALL): """Update this boom after the parent boom has changed length, angles, position or rotation. change(Change) = Change.ALL: Possibility to specify which type of change is performed. Default: ALL """ - if self.q_factor > 0.0: # flexible boom. Angle or origin change. c_m stays about fixed + if isinstance(self, Wire): # flexible boom. Angle or origin change. c_m stays about fixed return # update of pendulum is deferred to calc_statics_dynamics, since step time is needed if self.anchor0 is not None: # does not apply to fixation @@ -367,19 +339,9 @@ def torque(self): The Torque is always calculated relative to the crane position. """ _p_c_m = self._c_m_sub[1] - self.model.position - if self.q_factor > 0: # pendulum action - return self.mass * np.cross( - _p_c_m, - ( - np.array((0, 0, -9.81), float) - + (self.model.velocity + self.r_v) ** 2 * self.direction - + self.model.d_velocity - ), - ) - else: # other fixed boom. Calculate including all children, but load. Assume self._c_m_sub updated - m = self._c_m_sub[0] - # print(f"Torque {self.name}: m:{m}, pos:{_p_c_m}, c_m_sub:{self.c_m_sub[1]}, acc:{self.model.d_velocity}") - return m * np.cross(_p_c_m, (np.array((0, 0, -9.81), float) + self.model.d_velocity)) + m = self._c_m_sub[0] + # print(f"Torque {self.name}: m:{m}, pos:{_p_c_m}, c_m_sub:{self.c_m_sub[1]}, acc:{self.model.d_velocity}") + return m * np.cross(_p_c_m, (np.array((0, 0, -9.81), float) + self.model.d_velocity)) @property def force(self): @@ -399,10 +361,10 @@ def calc_statics_dynamics(self, dt: float | None = None): dt (float)=None: for dynamic calculations, the time for the movement must be provided and is then used to calculate torque and force """ - if self.q_factor != 0.0: # non-fixed boom. Might be moving or there might be pending origin/length changes + if isinstance(self, Wire): # non-fixed boom. Might be moving or there might be pending origin/length changes if dt is None: self._c_m_sub = (self.mass, self.origin + self.c_m) - self._pendulum_instantaneous() + self.pendulum_instantaneous() else: self.pendulum(dt) else: @@ -420,16 +382,59 @@ def calc_statics_dynamics(self, dt: float | None = None): if self.anchor0 is not None: self.anchor0.calc_statics_dynamics(dt) - def _pendulum_instantaneous(self): + +class Wire(Boom): + """Implements the additional properties of a flexible joint (the wire), with pendulum actions, damping, ...""" + + def __init__( + self, + model: "py_crane.crane.Crane", + name: str, + /, + tolerance: float = 1e-5, + additional_checks: bool = False, + **kwargs: Any, + ): + assert "q_factor" in kwargs, "Mandatory argument q_factor not found when instantiating Wire" + q_factor = kwargs.pop("q_factor") + Boom.__init__(self, model, name, q_factor=0.0, **kwargs) # initialize the Boom itself + self.tolerance: float = tolerance + self.q_factor: float = q_factor + self.additional_checks: bool = additional_checks + self.origin_v = np.zeros(3, float) # velocity of suspension + self.origin_acc = np.zeros(3, float) # acceleration of suspension + self.cm_v = np.zeros(3, float) # velocity of center of mass (load) + self.cm_acc = np.zeros(3, float) # acceleration of center of mass (load) + self.r2_rel_diff: float = 0.0 # relative absolute difference in pendulum length + self.newlen: float = nan + self.damping_time: float = self.damping(q_factor=q_factor) + self.calc_statics_dynamics(dt=None) + + def damping(self, q_factor: float | None = None, damping_time: float | None = None): + """Change/set the damping properties of a flexible boom. + Ensure that damping_time and newlen is set correctly. + """ + assert self.anchor0 is not None, "Flexible first booms are so far not implemented" + # pre-calculated loss term used in .pendulum() + length = self.length if isnan(self.newlen) else (self.length + self.newlen) / 2 + if q_factor is not None: + self.q_factor = q_factor + self._damping_time = sqrt(length / 9.81 * (self.q_factor**2 + 0.25)) + elif damping_time is not None: # new damping time. Change q_factor + self.q_factor = sqrt(damping_time**2 * 9.81 / length - 0.25) + self._damping_time = damping_time + return self._damping_time + + def pendulum_instantaneous(self): """Move the pendulum instantaneously - origin or length changes are instantaneous and the load stays as much as possible where it was. - Note: a new length may be stored as self._new_len and the origin might need updating to .anchor0.end + Note: a new length may be stored as self.newlen and the origin might need updating to .anchor0.end """ assert self.anchor0 is not None, "Function cannot be called on fixation." - if self._new_len is not None: # a new length has been stored, but is not affectuated - self.boom[0] = self._new_len #! we assume that the direction does not change - self._new_len = None + if not isnan(self.newlen): # a new length has been stored, but is not affectuated + self.boom[0] = self.newlen #! we assume that the direction does not change + self.newlen = nan clen0 = self.boom[0] * self.mass_center[0] # distance from origin to the mass center (constant) cm0 = self.origin + clen0 * self.direction # center-of-mass before movement origin1 = self.anchor0.end @@ -516,8 +521,21 @@ def pendulum(self, dt: float): """ + def update_r2_rel_diff(val: float): + if val > self.r2_rel_diff: + self.r2_rel_diff = val + if val > 0.5: + logger.error(f"Free falling of pendulum detected. Relative error: {val}") + def ivp_fun( - t: float, y: np.ndarray, r2: float, g: np.ndarray, dr_dt: np.ndarray | None, l0: float, dl_dt: float | None + t: float, + y: np.ndarray, + r2: float, + g: np.ndarray, + s_v: np.ndarray, + s_acc: np.ndarray, + l0: float, + dl_dt: float | None, ): """Solve the initial value problem of the pendulum dr/dt = f(t,r), r(t=0) = r0. Without losses. @@ -527,126 +545,199 @@ def ivp_fun( relative to origin r2 (float): the squared pendulum radius with respect to COM g (float): gravitational acceleration as ndarray - dr_dt (ndarray): Optional movement of the origin through dt + s_v (ndarray): Velocity of thesuspension + s_acc (ndarray): Acceleration of the suspension l0 (float): start length of wire. Used only if wire length changes dl_dt (float): Optional change of wire length through dt: l(t) = l0 + dl_dt* t """ - r = y[:3] - v = y[3:] if dr_dt is None else y[3:] - dr_dt + r = y[:3] - (s_v * t + 0.5 * s_acc * t**2) + v = y[3:] - (s_v + s_acc * t) + # v -= np.dot(r,v)/r2 * v # avoid that numerical issues lead to strange behaviour if dl_dt is not None: - l0 += dl_dt * t - r2 = l0 * l0 + lt = l0 + dl_dt * t + r2 = lt * lt + v += dl_dt tangential = np.cross(r, np.cross(r, g)) centripetal = np.dot(v, v) * r - self.r_acc = -((tangential + centripetal) / r2) - return np.append(v, self.r_acc) + acc = -((tangential + centripetal) / r2) # + s_acc + # print(f"r[0]:{r[0]}, v:{v}, s_v:{s_v}, s_acc:{s_acc} => acc:{acc}") + return np.append(v, acc) assert self.anchor0 is not None, f"pendulum() called on fixation {self.name}" assert self.q_factor != 0.0, f"pendulum() called on fixed boom {self.name}" + v = np.array(self.cm_v) # initial velocity of pendulum (load) + acc = np.array(self.cm_acc) # initial acceleration of pendulum (load) if ( ( abs(self.direction[2]) < 1.0 - 1e-10 # pendulum moving or not updated - or np.linalg.norm(self.r_v[:2]) > 1e-10 - or np.linalg.norm(self.r_acc[:2]) > 1e-10 + or np.linalg.norm(v) > 1e-10 + or np.linalg.norm(acc) > 1e-10 or not np.allclose(self.origin, self.anchor0.end) - or self._new_len is not None + or isnan(self.newlen) ) and self.mass_center[0] * self.length > 1e-10 # pendulum length not too short ): R = self.mass_center[0] * self.length # pendulum radius wrt. center of mass - if self.model.current_time > 6.0: # ??self.additional_checks: - e0 = Boom._energy(self.mass, self._c_m, self.r_v) - lz0 = Boom._angular_momentum(self.mass, self._c_m, self.r_v) - if np.allclose(self.origin, self.anchor0.end): - dr_dt = None - else: - dr_dt = (self.anchor0.end - self.origin) / dt # translation in time interval - if self._new_len is None: - dl_dt = None - else: - dl_dt = (self._new_len - self.length) / dt - self.damping(q_factor=self.q_factor) # re-calculate due to new length - self.boom[0] = self._new_len - self._new_len = None - if R > 1e-6: + # calculate the velocity and acceleration of the suspension point + s_v0 = np.array(self.origin_v) # initial velocity of suspension (origin) + # s_acc0 = self.origin_acc # initial acceleration of suspension (origin) + if self.additional_checks: - e0 = Boom._energy(self.mass, self._c_m, self.r_v) - lz0 = Boom._angular_momentum(self.mass, self._c_m, self.r_v) + e0 = Wire._energy(self.mass, self._c_m, v) + lz0 = Wire._angular_momentum(self.mass, self._c_m, v) r2 = R * R g = np.array((0.0, 0.0, -9.81), float) r = R * self.direction - v = self.r_v + s_v = (self.anchor0.end - self.origin) / dt # velocity of suspension in time interval + s_acc = (s_v - s_v0) / dt # acceleration of suspension in time interval + + if isnan(self.newlen): + dl_dt = None + else: + dl_dt = (self.newlen - self.length) / dt + self.damping(q_factor=self.q_factor) # re-calculate due to new length + self.boom[0] = self.newlen + # print(f"@{self.model.current_time}. rx:{r[0]} * vx:{v[0]}, s_v_x:{s_v[0]}, s_a_x:{s_acc[0]}.") sol = solve_ivp( # type: ignore ivp_fun, t_span=[0, dt], y0=np.append(r, v), method="RK45", #'RK23' 'DOP853' 'Radau' 'LSODA' "BDF" "RK45" - args=(r2, g, dr_dt, R, dl_dt), # type: ignore[reportArgumentType] ## according to spec + args=(r2, g, s_v, s_acc, R, dl_dt), # type: ignore[reportArgumentType] ## according to spec atol=self.tolerance, ) if sol.status != 0: - logger.error(f"r:{r}, v:{v}, r2:{r2}, dr/dt:{dr_dt}, l0:{R}, dl_dt:{dl_dt}") + logger.error(f"@{self.model.current_time}. r:{r}, v:{v}, r2:{r2}, s_v:{s_v}, l0:{R}, dl_dt:{dl_dt}") raise AssertionError(f"Pendulum solver did not succeed with dt:{dt}. Status:{sol.status}") from None y_t = sol.y[:, -1] # last column - position = y_t[:3] # if dr_dt is None else y_t[:3] + dr_dt * dt # position of CoM relative to origin - self.r_v = y_t[3:] # if dr_dt is None else y_t[3:] + dr_dt + position = y_t[:3] # + s_v*dt # position of CoM relative to origin + # if self.model.current_time > 0: + # print(f"@{self.model.current_time}. x:{position}, v:{v}, r*v:{np.dot(r,v)}") + v = y_t[3:] + if not isnan(self.newlen): + r2 = self.newlen**2 # type: ignore[assignment] ## definitively a float + self.newlen = nan + + if self.additional_checks and self.model.current_time > 0.0: # check for free fall conditions + update_r2_rel_diff(abs(np.dot(position, position) / r2 - 1.0)) if dt >= self._damping_time: # pendulum stops within dt - self.r_v = np.array((0, 0, 0), float) + v = np.array((0, 0, 0), float) else: - self.r_v *= 1 - dt / self._damping_time # see note + v *= 1 - dt / self._damping_time # see note + # v += s_v self.direction = normalized(position) rel_direction = self.anchor0.rot().apply(self.direction, inverse=True) # dir. relative to previous boom self.boom[1:] = cartesian_to_spherical(rel_direction)[1:] + # print(f"@{self.model.current_time}, angle:{180-np.degrees(np.arctan2(self.direction[0], self.direction[2]))} sv:{s_v}, sa:{s_acc}, dir[0]:{self.direction[0]}") self._c_m = position + # save pendulum state + self.origin_v = s_v + self.origin_acc = s_acc + self.cm_v = v + self.cm_acc = acc + + if ( + self.additional_checks + and np.linalg.norm(self.origin_acc) == 0.0 + and dl_dt is None + and self.model.current_time > 0.0 + ): + e1 = Wire._energy(self.mass, self._c_m, v) + if e1 - e0 > 1e-7: + logger.error(f"Pendulum energy change @{self.model.current_time} {e0}->{e1}") + logger.error(f" c_m:{self._c_m}, speed:{v}, damping:{self._damping_time}") + logger.error( + f" tangential:{np.cross(self._c_m, np.cross(self._c_m, g))}, centripetal:{np.dot(v, v) * self._c_m}" + ) + + lz1 = Wire._angular_momentum(self.mass, self._c_m, v) + if abs(lz1[2]) - abs(lz0[2]) > 1e-7: # not np.allclose( lz0, lz1): + logger.error( + f"Pendulum angular momentum increase @{self.model.current_time} {lz0[2]}->{lz1[2]}" + ) + logger.error(f" c_m:{self._c_m}, speed:{v}, damping:{self._damping_time}") + self.origin = self.anchor0.end self._c_m_sub = (self.mass, self.origin + self.c_m) - if self.model.current_time > 0.0 and np.dot(self.r_acc, self._c_m) > 1e-10: - logger.warning( - f"Free fall of load detected at time {self.model.current_time}. Acc.:{np.dot(self.r_acc, self._c_m)}" - ) - if self.additional_checks and dr_dt is None and dl_dt is None and self.model.current_time > 0.0: - e1 = Boom._energy(self.mass, self._c_m, self.r_v) - if e1 - e0 > 1e-7: - logger.error(f"Pendulum energy change @{self.model.current_time} {e0}->{e1}") - logger.error(f" c_m:{self._c_m}, speed:{self.r_v}, damping:{self._damping_time}") - logger.error( - f" tangential:{np.cross(self._c_m, np.cross(self._c_m, g))}, centripetal:{np.dot(self.r_v, self.r_v) * self._c_m}" - ) - - lz1 = Boom._angular_momentum(self.mass, self._c_m, self.r_v) - if abs(lz1[2]) - abs(lz0[2]) > 1e-7: # not np.allclose( lz0, lz1): - logger.error(f"Pendulum angular momentum increase @{self.model.current_time} {lz0[2]}->{lz1[2]}") - logger.error(f" c_m:{self._c_m}, speed:{self.r_v}, damping:{self._damping_time}") - # print(f"origin:{self.origin[0]}, d_end:{self.end[0]-self.origin[0]}, speed:{self.r_v[0]}") + # print(f"origin:{self.origin[0]}, d_end:{self.end[0]-self.origin[0]}, speed:{v[0]}") def pendulum_relax(self): """Relax the pendulum movement, leading to the CoM strictly downward. Since no time is used this action is strictly unphysical and should only be used for testing purposes. """ if ( - self.q_factor != 0.0 # non-fixed connection to previous boom (wire) - and abs(self.direction[2]) > 1e-10 # pendulum not relaxed + abs(self.direction[2]) > 1e-10 # pendulum not relaxed and self.mass_center[0] * self.length > 1e-10 # pendulum length not too short ): assert self.anchor0 is not None, "anchor0 needed at this point" self.model.calc_statics_dynamics() self.direction = np.array((0, 0, -1), float) - self.r_v = np.array((0, 0, 0), float) - self.r_acc = np.array((0, 0, 0), float) + self.origin_v = np.zeros(3, float) + self.origin_acc = np.zeros(3, float) + self.cm_v = np.zeros(3, float) + self.cm_acc = np.zeros(3, float) rel_direction = self.anchor0.rot().apply(self.direction, inverse=True) # dir. relative to previous boom self.boom[1:] = cartesian_to_spherical(rel_direction)[1:] crane_dir = normalized(self.model.rot().apply((0, 0, 1))) self._rot = rot_from_vectors(crane_dir, self.direction) self.model.calc_statics_dynamics() - def _calc_decayrate(self, newLength: float) -> float: - if self.q_factor == 0.0: - return nan - elif newLength == 0.0: - return 0 - else: - return sqrt(9.81 / (newLength * self.mass_center[0])) / sqrt(4 * self.q_factor - 1) + @property + def torque(self): + """Calculate the torque from the CoM of this boom with respect to the fixation. + Note that this is in practice only calculated for the load and the rest of the crane. + These two are calculated separately, since the load exhibits a pendulum action (dealt with here). + The Torque is always calculated relative to the crane position. + """ + _p_c_m = self._c_m_sub[1] - self.model.position + return self.mass * np.cross( + _p_c_m, + ( + np.array((0, 0, -9.81), float) + + (self.model.velocity + self.cm_v) ** 2 * self.direction + + self.model.d_velocity + ), + ) + + +# def _calc_decayrate(self, newLength: float) -> float: +# if self.q_factor == 0.0: +# return nan +# elif newLength == 0.0: +# return 0 +# else: +# return sqrt(9.81 / (newLength * self.mass_center[0])) / sqrt(4 * self.q_factor - 1) + + +# class LastOrigins(object): +# """Helper class holding the last three values of boom.origin (if a wire/pendulum) +# so that derivatives can be calculated and updated. +# """ +# def __init__(self): +# self.times = deque(maxlen=3) +# self.orgs = deque(maxlen=3) +# +# def derivatives(self): +# """Calculate the first and second order derivatives, based on the current values.""" +# der1, der2 = np.zeros(3), np.zeros(3) +# if len(self.times) == 3: # all values available +# a = np.array( [(1.0, t, t*t) for t in self.times], float) +# for i in range(3): # go through the 3 dimensions +# coeff = np.linalg.solve( a, np.array( [self.orgs[k][i] for k in range(3)])) +# der1[i] = coeff[1] + 2*coeff[2]* self.times[0] # at last time point +# der2[i] = 2*coeff[2] +# +# elif len( self.times) == 2: # so far only two time points available +# der1 = (orgs[1] - orgs[0]) / (times[1] - times[0]) +# return (der1, der2) +# +# def append(self, time:float, org:np.ndarray): +# """Append a tuple of time and origin-position-array) to the two deques +# If full, one element is removed from the other side. +# """ +# self.times.append(time) +# self.orgs.append( org) diff --git a/src/py_crane/boom_fmu.py b/src/py_crane/boom_fmu.py index f7d78a9..6df5ae1 100644 --- a/src/py_crane/boom_fmu.py +++ b/src/py_crane/boom_fmu.py @@ -5,13 +5,11 @@ from typing import Any, Never, Sequence, cast import numpy as np -from component_model.model import Model -from py_crane.boom import Boom +from py_crane.boom import Boom, Wire +from py_crane.crane import Crane from py_crane.enums import Change -# from py_crane.crane import Crane - logger = logging.getLogger(__name__) @@ -60,37 +58,32 @@ class BoomFMU(Boom): def __init__( self, + model: Crane, + name: str, + /, mass_rng: tuple[str, str] | None = None, boom_rng: tuple[tuple[Any, Any] | None | Sequence[Never], ...] = tuple(), - animationLW: int = 5, **kwargs: Any, ): from py_crane.crane_fmu import CraneFMU - model: Model | None = kwargs.get("model", None) - assert model is not None, "The 'model' argument is needed when instantiating a Boom" - name: str | None = kwargs.get("name", None) - assert name is not None, "A unique Boom name is needed when instantiating a Boom" - self._name = name + # we need 'early access' to some of the properties assert isinstance(model, CraneFMU), f"BoomFMU must link to a CraneFMU. Found {type(model)}" + self._name = name model.ensure_boom(self) # ensure that the boom object is registered with the crane u_time = model.u_time u_length = model.u_length u_angle = "deg" if model.degrees else "rad" # make some super-arguments 'visible' direct for usage here - description: str = kwargs.get("description", "") - anchor0: Boom | None = kwargs.get("anchor0", None) - assert anchor0 is not None, "The 'anchor0' argument is needed here. ('fixation' shall be created through Boom)" - mass: float = kwargs.get("mass", 1.0) - mass_center: float | tuple[float, float, float] = kwargs.get("mass_center", 0.5) - boom: tuple[float, float, float] = kwargs.get("boom", (1.0, 0.0, 0.0)) - q_factor: float = kwargs.get("q_factor", 0.0) + mass = kwargs.pop("mass") if "mass" in kwargs else "1.0kg" + boom = kwargs.pop("boom") if "boom" in kwargs else (1.0, 0.0, 0.0) + q_factor = kwargs.pop("q_factor") if "q_factor" in kwargs else 0.0 # Interface specifications. When we have the start values we can instantiate the Boom _c, _v = ("parameter", "fixed") if mass_rng is None else ("input", "continuous") self._mass = model.add_variable( - f"{name}.mass", - description=f"Mass of boom {name}", + f"{self._name}.mass", + description=f"Mass of boom {self._name}", causality=_c, variability=_v, start=mass, @@ -118,8 +111,8 @@ def __init__( or (boom_rng[i][0] > float("-inf") and boom_rng[i][1] < float("inf")) # type: ignore[index] ), f"The range of {self.name}[{i}] should not be limited, as radian variables are periodic" self._boom = model.add_variable( - f"{name}.boom", - description=f"Length [m] and direction [rad] of {name} from anchor point in spherical coordinates", + f"{self._name}.boom", + description=f"Length [m] and direction [rad] of {self._name} from anchor point in spherical coordinates", causality="input", variability="continuous", start=_boom, @@ -127,19 +120,33 @@ def __init__( on_set=partial(self.boom_setter, ch=Change.ROT.value if q_factor == 0 else 0), ) assert isinstance(self._boom.owner, BoomFMU), f"Owner of variable {self._boom}: {self._boom.owner}" - super().__init__( - model, - name, - description, - anchor0, - mass=mass0, - mass_center=mass_center, # this could be made an interface variable in advanced cranes - boom=getattr(self._boom.owner, self._boom.local_name), #! not getter()! boom.py uses internal variables! - q_factor=q_factor, - ) + if q_factor == 0.0: + Boom.__init__( + self, + model, + name, + mass=mass0, + boom=getattr( + self._boom.owner, self._boom.local_name + ), #! not getter()! boom.py uses internal variables! + q_factor=0.0, + **kwargs, + ) + else: + Wire.__init__( + self, # type: ignore[arg-type] ## call sequence ensures that it is called from Wire + model, + name, + mass=mass0, + boom=getattr( + self._boom.owner, self._boom.local_name + ), #! not getter()! boom.py uses internal variables! + q_factor=q_factor, + **kwargs, + ) # additional output variables self._end = model.add_variable( # pyright: ignore[reportUnknownMemberType] # should become obsolete once component_model is updated. - f"{name}.end", + f"{self._name}.end", description="Cartesian vector of the end of the boom", causality="output", variability="continuous", @@ -148,14 +155,14 @@ def __init__( # additional derivative variables (but not for fixation, as these are Euler movements on the crane!) if self.name != "fixation": self._der1_boom = model.add_variable( # pyright: ignore[reportUnknownMemberType] # should become obsolete once component_model is updated. - f"der({name}.boom)", + f"der({self._name}.boom)", description="Continuous change to the boom (length, polar-rotation, azimuthal-rotation) wrt. origin", causality="input", variability="continuous", start=(f"0 m/{u_time}", f"0 {u_angle}/{u_time}", f"0 {u_angle}/{u_time}"), ) self._der2_boom = model.add_variable( # pyright: ignore[reportUnknownMemberType] # should become obsolete once component_model is updated. - f"der(der({name}.boom))", + f"der(der({self._name}.boom))", description="Acceleration to the boom (length, polar-rotation, azimuthal-rotation) wrt. origin", causality="input", variability="continuous", @@ -163,9 +170,27 @@ def __init__( ) if self._mass.range[0].rng[0] != self._mass.range[0].rng[1]: # mass is changeable (normally the load) self._der1_mass = model.add_variable( # pyright: ignore[reportUnknownMemberType] # should become obsolete once component_model is updated. - f"der({name}.mass)", + f"der({self._name}.mass)", description="Continuous change to the mass (i.e. load change)", causality="input", variability="continuous", start=f"0 kg/{u_time}", ) + + +class WireFMU(BoomFMU, Wire): + """The FMU definitions of a :py:class:`.Wire` - + a special stiff boom, attached with a flexible joint to a parent boom - + normally the last boom of a crane. + """ + + def __init__( + self, + model: Crane, + name: str, + /, + mass_rng: tuple[str, str] | None = None, + boom_rng: tuple[tuple[Any, Any] | None | Sequence[Never], ...] = tuple(), + **kwargs: Any, + ): + super().__init__(model, name, mass_rng, boom_rng, **kwargs) diff --git a/src/py_crane/crane.py b/src/py_crane/crane.py index 335aaa0..7aec63d 100644 --- a/src/py_crane/crane.py +++ b/src/py_crane/crane.py @@ -7,7 +7,7 @@ from component_model.utils.transform import cartesian_to_spherical from scipy.spatial.transform import Rotation as Rot -from py_crane.boom import Boom +from py_crane.boom import Boom, Wire from py_crane.enums import Change logger = logging.getLogger(__name__) @@ -151,7 +151,8 @@ def boom_by_name(self, name: str) -> Boom | None: def add_boom( self, - *args: Any, + name: str, + /, **kwargs: Any, ) -> Boom: """Add a boom to the crane. @@ -163,7 +164,10 @@ def add_boom( if "anchor0" not in kwargs: last = next(self.booms(reverse=True)) kwargs.update({"anchor0": last}) - self.boom_ = Boom(self, *args, **kwargs) + if "q_factor" in kwargs and kwargs["q_factor"] > 0.0: + self.boom_ = Wire(self, name, **kwargs) + else: + self.boom_ = Boom(self, name, **kwargs) return self.boom_ # the new last boom @property diff --git a/src/py_crane/crane_fmu.py b/src/py_crane/crane_fmu.py index b98e01e..8e2560f 100644 --- a/src/py_crane/crane_fmu.py +++ b/src/py_crane/crane_fmu.py @@ -7,7 +7,8 @@ from component_model.variable import Variable from component_model.variable_naming import VariableNamingConvention -from py_crane.boom_fmu import BoomFMU +from py_crane.boom import Boom, Wire +from py_crane.boom_fmu import BoomFMU, WireFMU from py_crane.crane import Crane logger = logging.getLogger(__name__) @@ -119,9 +120,10 @@ def __init__( def add_boom( self, - *args: Any, + name: str, + /, **kwargs: Any, - ) -> BoomFMU: + ) -> Boom: """Add a boom to the crane. Overrides :py:meth:`.Crane.add_boom` to ensure that :py:class:`BoomFMU` is added. This method represents the recommended way to contruct a crane and then add the booms. @@ -131,11 +133,13 @@ def add_boom( *args: all :py:class:`Boom` positional parameters, excluding ``model`` and ``anchor0`` **kwargs: all :py:class:`Boom` keyword parameters, excluding ``model`` and ``anchor0`` """ - kwargs.update({"model": self}) if "anchor0" not in kwargs: last = self.boom0[-1] kwargs.update({"anchor0": last}) - return BoomFMU(*args, **kwargs) + if kwargs.get("q_factor", 0.0) == 0.0: + return BoomFMU(self, name, **kwargs) + else: + return WireFMU(self, name, **kwargs) def ensure_boom(self, boom: BoomFMU): """Ensure that the boom is registered before structured variables are added to it. @@ -151,7 +155,7 @@ def exit_initialization_mode(self): """ self.dirty_do() # run on_set on all dirty variables wire = self.boom0[-1] - if wire is not None and wire.q_factor != 0: + if isinstance(wire, Wire): wire.pendulum_relax() def do_step(self, current_time: float, step_size: float) -> bool: diff --git a/src/py_crane/mobile_crane.py b/src/py_crane/mobile_crane.py index 2f7a1b2..21f0b9c 100644 --- a/src/py_crane/mobile_crane.py +++ b/src/py_crane/mobile_crane.py @@ -41,7 +41,7 @@ def __init__( ): super().__init__(name=name, description=description, author=author, version=version, degrees=degrees, **kwargs) _pedestal = self.add_boom( - name="pedestal", + "pedestal", description="The crane base, on one side fixed to the vessel and on the other side the first crane boom is fixed to it. The mass should include all additional items fixed to it, like the operator's cab", mass=pedestalMass, mass_center=pedestalCoM, @@ -49,7 +49,7 @@ def __init__( boom_rng=(None, None, ()), ) _boom = self.add_boom( - name="boom", + "boom", description="The boom. Can be lifted and length can change within the given range", mass=boomMass, mass_center=(0.5, 0, 0), @@ -57,7 +57,7 @@ def __init__( boom_rng=((boomLength0, boomLength1), (), None), ) _ = self.add_boom( - name="wire", + "wire", description="The wire fixed to the last boom. Flexible connection", mass=wire_mass_range[0] if wire_mass is None else wire_mass, mass_center=0.99, diff --git a/tests/test_animation.py b/tests/test_animation.py index 4f85884..658fce0 100644 --- a/tests/test_animation.py +++ b/tests/test_animation.py @@ -25,21 +25,21 @@ def crane(scope: str = "session", autouse: bool = True): def _crane(): crane = Crane() _ = crane.add_boom( - name="pedestal", + "pedestal", description="The crane base, on one side fixed to the vessel and on the other side the first crane boom is fixed to it. The mass should include all additional items fixed to it, like the operator's cab", mass=2000.0, mass_center=(0.5, -1, 0.8), boom=(3.0, 0, 0), ) _ = crane.add_boom( - name="boom1", + "boom1", description="The first boom. Can be lifted", mass=200.0, mass_center=0.5, boom=(10.0, np.radians(90), 0), ) _ = crane.add_boom( - name="wire", + "wire", description="The wire fixed to the last boom. Flexible connection", mass=50.0, # so far basically the hook mass_center=1.0, diff --git a/tests/test_crane.py b/tests/test_crane.py index 1677499..863e1fb 100644 --- a/tests/test_crane.py +++ b/tests/test_crane.py @@ -11,7 +11,7 @@ from scipy.spatial.transform import Rotation as Rot from py_crane.animation import AnimatePendulum -from py_crane.boom import Boom +from py_crane.boom import Boom, Wire from py_crane.crane import Crane # from mpl_toolkits.mplot3d.art3d import Line3D @@ -49,21 +49,22 @@ def do_show( plt.show() -def set_wire_direction(r: Boom, angles: tuple[float, float], degrees: bool = False): +def set_wire_direction(w: Wire, angles: tuple[float, float], degrees: bool = False): """Set the angles of a wire object. Makes only sense for preparation of test cases. Not allowed in Boom class.""" _angles = np.array(angles, float) + assert isinstance(w, Wire) if degrees: _angles = np.radians(_angles) - r.boom[1:] = _angles - assert r.anchor0 is not None - r.rot(r.anchor0.rot() * rot_from_spherical(_angles)) - r.direction = r.rot().apply(np.array((0, 0, 1), float)) - r.r_v = np.array((0, 0, 0), float) # reset also the speed + w.boom[1:] = _angles + assert w.anchor0 is not None + w.rot(w.anchor0.rot() * rot_from_spherical(_angles)) + w.direction = w.rot().apply(np.array((0, 0, 1), float)) + w.cm_v = np.zeros(3, float) # reset also the speed def mass_center(xs: tuple[tuple[float, np.ndarray], ...]): """Calculate the total mass center of a number of point masses provided as 4-tuple""" - M, c = 0.0, np.array((0, 0, 0), float) + M, c = 0.0, np.zeros(3, float) for x, v in xs: M += x c += x * v @@ -116,21 +117,21 @@ def crane(scope: str = "session", autouse: bool = True): def _crane(wire_length: float = 0.5): crane = Crane() _ = crane.add_boom( - name="pedestal", + "pedestal", description="The crane base, on one side fixed to the vessel and on the other side the first crane boom is fixed to it. The mass should include all additional items fixed to it, like the operator's cab", mass=2000.0, mass_center=(0.5, -1, 0.8), boom=(3.0, 0, 0), ) _ = crane.add_boom( - name="boom1", + "boom1", description="The first boom. Can be lifted", mass=200.0, mass_center=0.5, boom=(10.0, radians(90), 0), ) w = crane.add_boom( - name="wire", + "wire", description="The wire fixed to the last boom. Flexible connection", mass=50.0, # so far basically the hook mass_center=1.0, @@ -153,6 +154,7 @@ def test_initial(crane: Crane): booms = [b.name for b in crane.booms()] assert booms == ["fixation", "pedestal", "boom1", "wire"] fixation, pedestal, boom1, wire = [b for b in crane.booms()] + assert isinstance(wire, Wire) assert crane.boom0.name == "fixation", "Boom0 should be fixation" assert np.allclose(crane.boom0.origin, (0.0, 0.0, -1e-10)) # fixation somewhat below surface@ {crane.boom0.origin} @@ -245,26 +247,29 @@ def test_animate_pendulum(show: bool = False): def test_pendulum(show: bool = False): """Test crane with 1m wire and 50kg load at end as pendulum (in various configurations).""" - def _pendulum(q_factor: float = 20.0): + def _pendulum(q_factor: float = 20.0, length: float = 1.0, tau: float | None = None): """Defines the special crane used here: a pedestal with a wire/load hanging down.""" crane = Crane() _ = crane.add_boom( - name="pedestal", + "pedestal", description="The crane base, just a pole for the pendulum, here.", mass=2000.0, mass_center=0.5, - boom=(1.0, 0, 0), + boom=(length, 0, 0), ) w = crane.add_boom( - name="wire", + "wire", description="The wire fixed to the last boom. Flexible connection", mass=100.0, # load mass_center=1.0, - boom=(1.0, radians(180), 0), # 1m down, so that load at (0,0,0) in equilibrium + boom=(length, radians(180), 0), # 1m down, so that load at (0,0,0) in equilibrium q_factor=q_factor, - additional_checks=True, + additional_checks=False, # True, ) - assert np.allclose(w.origin, (0, 0, 1.0)), f"Origin {w.origin} != (0,0,1)" + assert isinstance(w, Wire) + if tau is not None: + w.damping(damping_time=tau) + assert np.allclose(w.origin, (0, 0, length)), f"Origin {w.origin} != (0,0,{length})" assert np.allclose(w.end, (0, 0, 0)), f"Load expected at (0,0,0). Found {w.end}" return crane, w @@ -293,19 +298,63 @@ def check_fit( assert e_y0 is None or abs(_y0 - e_y0) < eps, f"Translation {_y0} != {e_y0}" assert e_a is None or abs(_a - e_a) < eps, f"Amplitude {_a} != {e_a}" assert e_w is None or abs(_w - e_w) < eps, f"Angular frequency {_w} != {e_w}" - assert e_phi is None or abs(_phi - e_phi) < eps, f"Phase {_phi} != {e_phi}" + assert e_phi is None or abs(_phi - e_phi) < 10 * eps, f"Phase {_phi} != {e_phi}" + + def check_sin( + _t: list[float], _p: list[float], _v: list[float], ampl: float, w: float, tau: float = 20, _len: float = 1.0 + ): + # wd = np.sqrt(9.81 / _len - 1 / 4 / tau**2) + osc = ForcedOscillator1D(k=9.81 / _len, c=1 / tau, a=ampl * 9.81 / _len, wf=w) + x, dx = osc.calc(np.array(_t)) + if show: + do_show(_t, {"pos": _p, "expected": x, "diff": x - _p}, selection={"pos": 1, "expected": 1, "diff": 2}) + assert np.allclose(x, _p, atol=1e-3, rtol=1e-4) + _dx = np.array(dx, float) # np.array( [np.linalg.norm(_) for _ in dx]) + if show: + do_show( + _t, + {"|speed|": _v, "expected": _dx, "diff": (_dx - _v)}, + selection={"|speed|": 1, "expected": 1, "diff": 2}, + ) + assert np.allclose(_v, _dx, atol=1e-3, rtol=1e-3) + + def check_shock( + _t: list[float], + _p: list[float], + _v: list[float], + _p0: float, + tau: float = 20.0, + length: float = 1.0, + eps: float = 1e-4, + ): + """Check whether a short acceleration on crane movement results in the expected pendulum action.""" + wd = np.sqrt(9.81 / length - 1 / 4 / tau**2) + phi0 = np.arcsin(_p0 / length) + assert np.isclose(_p[10], 0.0, atol=1e-3), f"Position after shock {_p[10]} should be still at origo." + assert np.isclose(np.average(_p), _p0, atol=1e-4), f"Average position {np.average(_p)} != {_p0}" + expected = [_p0 * (1.0 - np.exp(-(t - 0.01) / 2 / tau) * cos(wd * (t - 0.01))) for t in _t] + maxdiff = max(np.absolute(np.array(expected[4:]) - np.array(_p[4:]))) + assert maxdiff < eps, f"Max difference between position and expected: {maxdiff}" + beta = np.arctan2(1 / 2 / tau, wd) + expected = [ + phi0 * np.exp(-t / 2 / tau) * np.sqrt(9.81 / length) * np.sqrt(np.sin(wd * t - beta) ** 2) for t in _t + ] + avgdiff = np.average(np.absolute(np.array(expected) - np.array(_v))) + assert avgdiff < 10 * eps, f"High average _v difference to expected: {avgdiff}" + # do_show(_t, {'|speed|':_v, 'expected':expected}) def sim_run( - crane_spec: tuple[Crane, Boom], - t_end: float, + crane_spec: tuple[Crane, Wire], + te: float, dt: float = 0.1, + speed_x: bool = False, min_z: float | None = None, max_z: float | None = None, max_speed: float | None = None, show_select: dict[str, int] | None = None, idx: int = 1, title: str = "test_pendulum", - crane_position: Callable[[float], float] | None = None, + crane_pos: Callable[[float], float] | None = None, crane_roll: Callable[[float], float] | None = None, crane_pitch: Callable[[float], float] | None = None, crane_yaw: Callable[[float], float] | None = None, @@ -319,26 +368,13 @@ def sim_run( y_pos: list[float] = [] z_pos: list[float] = [] speed: list[float] = [] - time = np.arange(0, t_end, dt) + time = np.arange(0, te, dt) ext = time if _ext is None else [_ext(t) for t in time] _dz0_dt = -1 - z0 = w.end[2] length = w.boom[0] for _time in time: - x0 = w.end[0] - y0 = w.end[1] - z0 = w.end[2] - v = float(np.linalg.norm(w.r_v)) - assert min_z is None or w.end[2] >= min_z, f"@{_time}. Min z {w.end[2]} < {min_z}" - assert max_z is None or w.end[2] <= max_z + 1e-6, f"@{_time}. Max z {w.end[2]} > {max_z}" - assert max_speed is None or v <= max_speed, f"@{_time}. Max speed {v} < {max_speed}" - x_pos.append(x0) - y_pos.append(y0) - z_pos.append(z0) - speed.append(v) - misc.append(w.origin[1]) - if crane_position is not None: - crane.position = np.array((crane_position(_time), 0, 0), float) + if crane_pos is not None: + crane.position = np.array((crane_pos(_time), 0, 0), float) if crane_roll is not None: crane.rotate((crane_roll(_time), 0, 0), absolute=True) if crane_pitch is not None: @@ -350,6 +386,22 @@ def sim_run( else: assert abs(length - w.boom[0]) < 1e-4, f"Pendulum length {w.boom[0]} != {length}" crane.do_step(_time, dt) + + x0 = w.end[0] + y0 = w.end[1] + z0 = w.end[2] + if speed_x: + v = w.cm_v[0] + else: + v = float(np.linalg.norm(w.cm_v)) + assert min_z is None or w.end[2] >= min_z, f"@{_time}. Min z {w.end[2]} < {min_z}" + assert max_z is None or w.end[2] <= max_z + 1e-6, f"@{_time}. Max z {w.end[2]} > {max_z}" + assert max_speed is None or v <= max_speed, f"@{_time}. Max speed {v} < {max_speed}" + x_pos.append(x0) + y_pos.append(y0) + z_pos.append(z0) + speed.append(v) + misc.append(w.origin[0]) z_max = [[0.0, z0]] z_max.extend(extremum_series(time[10:], z_pos[10:], which="max")) @@ -363,49 +415,10 @@ def sim_run( ) return (time, x_pos, y_pos, z_pos, speed, z_max, misc) - def _initial_angle_speed( - a0: float = 0.0, v0: float = 0.0, damping_time: float = 1e100, show: bool = False, idx: int = 1 - ): - crane, w = _pendulum() - set_wire_direction(w, (a0, 0)) - assert np.allclose(w.end, (-np.sin(a0), 0, 1 - np.cos(a0))), ( - f"Wire angle {np.degrees(a0)}, load: ({-np.sin(a0)},0,{1 - np.cos(a0)}). Found {w.end}" - ) - tau = w.damping(damping_time=damping_time) - - # Start the crane at maximum potential energy of load (1 deg) without damping - # we should have theta(t) = theta0* cos(w*t) with theta0= 0.0175 (1deg) and w= sqrt(g/L) = 3.132 => T = 2.006 - w.r_v = np.array((v0, 0, 0), float) - wd = np.sqrt(9.81 - 1 / 4 / tau**2) - e_a = np.sqrt(a0**2 + (a0 / 2 / tau + v0) ** 2 / wd**2) - phi = np.arctan2(1 / 2 / tau, wd) - time, x_pos, y_pos, z_pos, speed, z_max, misc = sim_run( - crane_spec=(crane, w), - t_end=10, - dt=0.01, - min_z=0, - max_z=1 - np.cos(e_a), - max_speed=np.sqrt(2 * 9.81 / (1 - np.cos(e_a))), - show_select=None if not show else {"x_pos": 1, "z_pos": 1, "speed": 2}, - title=f"test_pendulum. {np.degrees(a0)}deg through origin", - idx=idx, - ) - _y0, _a, _w, _phi, t = sine_fit(time, x_pos) - check_fit(0.0, e_a * np.exp(-t / 2 / tau), wd, phi, (_y0, _a, _w, _phi), eps=1e-4) - - eps = 1e-5 if tau > 100 else 1e-3 - for t, z in zip(time, z_pos, strict=False): - theta = a0 * np.cos(np.sqrt(9.81) * t) - assert abs(z - 1 + np.cos(theta)) < eps, f"@{t}: z={z}. Expected {1.0 - np.cos(theta)}" - e0 = 9.81 * z_pos[0] + 0.5 * speed[0] ** 2 - for t, z, v in zip(time, z_pos, speed, strict=True): - assert abs(9.81 * z + 0.5 * v**2 - e0 * np.exp(-t / tau)) <= 1e-3, ( - f"Energy @{t} {9.81 * z + 0.5 * v**2} =! {e0 * np.exp(-t / tau)}" - ) - def _move_crane( v0: float = 0.0, te: float = 100, + dt: float | None = None, tau: float = 5, c_pos: Callable[[float], float] | None = None, show: bool = False, @@ -415,57 +428,62 @@ def _move_crane( gamma = 1.0 / 2.0 / w.damping(damping_time=tau) # damping wd = np.sqrt(9.81 / w.length - gamma**2) T = 2 * np.pi / wd - if 4 <= idx <= 6: + if idx in (4, 5, 6): osc = ForcedOscillator1D(k=9.81, c=gamma, a=0.001, wf=wd) else: osc = None w.pendulum_relax() - w.r_v = np.array((v0, 0, 0), float) + w.cm_v = np.array((v0, 0, 0), float) + _dt = dt if dt is not None else (T / 100 if idx in (0, 4, 5, 6) else 0.01) time, x_pos, y_pos, z_pos, speed, z_max, misc = sim_run( crane_spec=(crane, w), - t_end=te, - dt=T / 100 if idx != 3 else 0.01, + te=te, + dt=_dt, + speed_x=True, min_z=1.0 - float(w.length), show_select={ "x_pos": 1, - "z_pos": 2, + "misc": 1, "speed": 1, - "ext": 1, + "z_pos": 2, }, title=f"test_pendulum_move[{idx}]. Movements in x-direction", - crane_position=c_pos, # movement function + crane_pos=c_pos, # movement function _ext=lambda t: ( abs((v0 if v0 > 0 else 1.0) * np.exp(-gamma * t) * np.cos(wd * t)) if osc is None else float(osc.calc(t)[0]) ), ) - if idx == 0: # v0=1.0, tau=20 - damped oscillation. Small amplitude, so that linear approximation holds! + if idx == 0: # v0=0.1, tau=20 - damped oscillation. Small amplitude, so that linear approximation holds! for t, v in zip(time, speed, strict=True): - expected = abs(v0 * np.exp(-t / tau) * np.cos(wd * t)) + expected = v0 * np.exp(-t / tau) * np.cos(wd * t) assert abs(v - expected) < 3e-2, f"Damped oscillation speed @{t}: {v}!={expected}" - if idx == 1: # origin moves with v=0.01, while load starts with same speed + elif idx == 1: # v0=0. Crane moves with speed 0.1/s + assert abs(np.average(speed) - 0.1) < 1e-4, f"Average speed {np.average(speed)} != 0.1" + assert abs(x_pos[-1] - 0.1 * te) < 1e-2, f"End position {0.1 * te}!={x_pos[-1]}" + elif idx == 2: # origin moves with v=0.01, while load starts with same speed assert all(abs(v - v0) < 1e-3 for v in speed), f"Tail speeds: {speed[0:10]}" - assert abs(x_pos[-1] - v0 * 100) < 1e-3, f"End position: {x_pos[-1]}!={v0 * 100}" - elif idx == 2: # v0=0. For t<5*T crane moves with speed 1/s and stops then - i_change = np_index(time, 5) - i_zero = np.absolute(x_pos[i_change : i_change + 200]).argmin() - assert all(abs(v) < 2e-3 for v in speed[i_change + i_zero :: 100]), ( - f"Speed zero at period. speed[{i_change + i_zero}]:{speed[i_change + i_zero]}" - ) - assert all(abs(x - 0.25) < 1e-2 for x in x_pos[-200:]), f"End position {0.25}!={x_pos[-10:]}" - elif idx == 3: + assert abs(x_pos[-1] - v0 * te) < 1e-3, f"End position: {x_pos[-1]}!={v0 * 100}" + elif idx == 3: # v0=0. Shock in x-direction with a=20 + check_shock(list(time), x_pos, speed, _p0=20 * 0.01**2, tau=20, eps=1e-3) + elif idx == 4: # low frequency sine + check_sin(list(time), x_pos, speed, 0.001, 0.1 * wd, tau=20) + elif idx == 5: # resonant oscillation + check_sin(list(time), x_pos, speed, 0.00005, wd, tau=20) + elif idx == 6: # high frequency sine + check_sin(list(time), x_pos, speed, 0.00005, 10 * wd, tau=20) + elif idx == 7: # Qualitative (visual) comparison with forced oscillator is very good, but oscillator is not harmonic # and sweep in low frequency area too fast, so that numerical comparison is difficult. i_low = np_index(time, 40) i_res = np_index(time, 167.5) - i_high = np_index(time, 300) - check_fit(0.0, 0.0, 0.66431, 1.9054, sine_fit(time[:i_low], x_pos[:i_low]), eps=1e-3) - check_fit(None, 0.0013, 3.1979, 1.8262, sine_fit(time[:i_res], x_pos[:i_res]), eps=1e-3) - check_fit(None, 3.7e-5, 5.9758, -0.7238, sine_fit(time[:i_high], x_pos[:i_high]), eps=1e-3) - elif idx == 4: # resonant oscillation - _y0, _a, _w, _phi, _ = sine_fit(time, x_pos) - logger.info(f"Amplitude:{_a}, angular_freq:{_w} - {wd}, phase:{_phi}") + i_high = np_index(time, 450) + check_fit(0.0, 0.0, 0.66, 1.92, sine_fit(time[:i_low], x_pos[:i_low]), eps=1e-2) + check_fit(None, 0.022, 3.17, 0.7, sine_fit(time[:i_res], x_pos[:i_res]), eps=1e-2) + check_fit(None, 3.7e-5, 9.05, -2.15, sine_fit(time[:i_high], x_pos[:i_high]), eps=1e-2) + elif idx == 8: + print(np.degrees(np.arctan2(z_pos[-1], misc[-1] - x_pos[-1])), np.degrees(np.arcsin(2 / 9.81)) / 2) def _turn_crane( te: float = 100, @@ -488,11 +506,12 @@ def _turn_crane( crane_roll, crane_pitch, crane_yaw = None, None, rpy time, x_pos, y_pos, z_pos, speed, z_max, misc = sim_run( crane_spec=(crane, w), - t_end=te, + te=te, dt=T / 100, min_z=float(-w.length), show_select={ "x_pos": 1, + "z_pos": 1, "y_pos": 2, "speed": 1, "misc": 2, @@ -507,21 +526,9 @@ def _turn_crane( assert all(abs(v) < 1e-8 for v in speed), "Speed should be zero" assert all(abs(x) < 1e-8 for x in x_pos), "x-position should be zero" if idx == 2: # accelerated roll, then stop (shock) - i_change = np_index(time, 5 + 2 * T) - assert all([abs(x) < 1e-10 for x in x_pos]), "x should not be affected." - assert all([abs(y + np.sin(0.25)) < 0.03 for y in y_pos[-100:]]), ( - f"y at rest with rolled crane? {y_pos[-10:]}" - ) - _y0, _a, _w, _phi, _ = sine_fit(time[:i_change], np.array(y_pos[:i_change], float) + np.sin(0.25)) - assert abs(_w - wd) < 1e-1, f"Expected angular frequency {wd} != {_w}" + check_shock(list(time), y_pos, speed, _p0=-20 * 0.01**2, tau=20, eps=1e-3) if idx == 3: # accelerated pitch, then stop (shock) - i_change = np_index(time, 5 + 2 * T) - assert all([abs(y) < 1e-10 for y in y_pos]), "y should not be affected." - assert all([abs(x + np.sin(0.25)) < 0.03 for x in x_pos[-100:]]), ( - f"x at rest with rolled crane? {x_pos[-10:]}" - ) - _y0, _a, _w, _phi, _ = sine_fit(time[:i_change], np.array(x_pos[:i_change], float) + np.sin(0.25)) - assert abs(_w - wd) < 2e-2, f"Expected angular frequency {wd} != {_w}" + check_shock(list(time), x_pos, speed, _p0=-20 * 0.01**2, tau=20, eps=1e-3) def _circular( tau: float = 1000, @@ -532,13 +539,12 @@ def _circular( idx: int = 1, ): """Start with circular motion of load and shorten wire according to wire_l function.""" - crane, w = _pendulum() - w.damping(damping_time=tau) + crane, w = _pendulum(tau=tau) set_wire_direction(w, (90 + angle, 0), degrees=True) - w.r_v = np.array((0.0, v0, 0.0), float) + w.cm_v = np.array((0.0, v0, 0.0), float) time, x_pos, y_pos, z_pos, speed, z_max, misc = sim_run( crane_spec=(crane, w), - t_end=10.0, + te=10, dt=0.001, min_z=0, show_select={"x_pos": 1, "y_pos": 1, "z_pos": 2}, @@ -552,36 +558,66 @@ def _circular( _z = np.sqrt(1 - _x**2) assert all(abs(z - 1 + _z) < 1e-5 for z in z_pos), f"Height != {1 - _z}: {z_pos[:10]}" i25 = np_index(time, 2.5) - check_fit(0.0, _x, 2 / c_angle, np.pi / 2, sine_fit(time[:i25], x_pos[:i25]), eps=1e-6) - _y0, _a, _w, _phi, _tm = sine_fit(time[: np_index(time, 2.5)], y_pos[: np_index(time, 2.5)]) - assert abs(_phi) < 1e-6, f"y-Phase {_phi} != {np.pi / 2}" + check_fit(0.0, _x, 2 / c_angle, np.pi / 2, sine_fit(time[:i25], x_pos[:i25]), eps=1e-2) + # _y0, _a, _w, _phi, _tm = sine_fit(time[: np_index(time, 2.5)], y_pos[: np_index(time, 2.5)]) + # assert abs(_phi) < 1e-6, f"y-Phase {_phi} != {np.pi / 2}" elif idx == 1: # No change of wire length. Note: curves are not completely circular => accuracy reduced. c_angle = np.sqrt(np.sqrt(_b2 + _b2**2 / 4) - _b2 / 2) # stability angle _y0, _a, _w, _phi, _tm = sine_fit(time[: np_index(time, 1.5)], z_pos[: np_index(time, 1.5)]) assert abs(1.0 - 2**2 / 9.81 - _y0) < 5e-2, "Zentripetal in balance with gravitation. y0={_y0}." assert 1.0 - _y0 - _a < 2e-2, f"Energy and angular momentum conservation: y0={_y0}, a={_a}" elif idx == 2: # wire shortened with 0.05*t => l0=1...lend=0.5. Should have wd1 = l0**2 / l1**2* wd0 - i02 = np_index(time, 0.2) - check_fit(0.0, 1.0, 50, None, sine_fit(time[:i02], x_pos[:i02], eps=0.1), eps=0.16) + i03 = np_index(time, 0.3) + check_fit(0.0, 1.0, 50.9, None, sine_fit(time[:i03], x_pos[:i03], eps=0.1), eps=0.16) _y0, _a, _w, _phi, _tm = sine_fit(time, x_pos, eps=0.1) check_fit(0.0, 0.5, None, None, (_y0, _a, _w, _phi), eps=0.1) assert abs(_w - 200) / 200 < 3e-2, f"Angular velocity {_w} != 200" + elif idx == 3: # pendulum hanging down with just enough kinetic energy to rotate through top point once + assert (np.max(y_pos) - 1.0) < 1e-5, "Max y: {np.max{y_pos}" + assert (np.min(y_pos) + 1.0) < 1e-5, "Min y: {np.min{y_pos}" + assert (np.max(z_pos) - 2.0) < 1e-5, "Max z: {np.max{z_pos}" + + # def _special(pos: np.ndarray, speed: np.ndarray, tau: float, te: float = 1.0, idx: int = 1): + # """Test special situations which have caused problems. Use the loa position vector and the speed as initial.""" + # sp = cartesian_to_spherical(pos, deg=True) + # R = float(np.linalg.norm(pos)) + # crane, w = _pendulum(length=R, tau=tau) + # set_wire_direction(w, (sp[1], sp[2]), degrees=True) + # w.cm_v = speed + # time, x_pos, y_pos, z_pos, speed, z_max, misc = sim_run( + # crane_spec=(crane, w), + # te=te, + # dt=0.01, + # min_z=0, + # show_select={"x_pos": 1, "y_pos": 1, "z_pos": 1, "speed": 2}, + # title="test_pendulum. Special({idx})", + # ) + # _special( np.array((1, -1, -1)), np.array((1, 1, 1)), tau=8000, te = 1.0, idx=1) + # _special( np.array((1, -1, -1)), np.array((1, 0, 1)), tau=8000, te = 1.0, idx=1) + + tau = 20.0 + gamma = 1.0 / 2.0 / tau + wd = np.sqrt(9.81 / 1.0 - gamma**2) _b2 = (2**2 / 9.81) ** 2 stable = np.degrees(np.arccos(np.sqrt(np.sqrt(_b2 + _b2**2 / 4) - _b2 / 2))) + _move_crane(te=50, v0=0.1, c_pos=None, tau=20, show=True, idx=0) + _move_crane(v0=0.0, c_pos=lambda t: 0.1 * t, tau=10, te=20.0, show=True, idx=1) + _move_crane(v0=0.1, c_pos=lambda t: 0.1 * t, tau=1000, te=10, show=True, idx=2) + _move_crane(v0=0.0, c_pos=lambda t: 20 * t**2 if t < 0.01 else 0.002, tau=20, te=10, dt=0.01, show=True, idx=3) + _move_crane(c_pos=lambda t: 0.001 * np.sin(0.1 * wd * t), tau=tau, te=200, dt=0.01, show=True, idx=4) + _move_crane(c_pos=lambda t: -0.00005 * np.sin(wd * t), tau=tau, te=200, show=True, idx=5) + _move_crane(c_pos=lambda t: 0.00005 * np.sin(10 * wd * t), tau=tau, te=10, dt=0.0001, show=True, idx=6) + _move_crane(v0=0.0, c_pos=lambda t: 0.001 * np.sin((0.01 + 0.01 * t) * t), tau=20, te=500, show=True, idx=7) + # _move_crane(v0=0.0, c_pos=lambda t: 1 * t**2, tau=20, te=100, dt=0.01, show=True, idx=8) + _turn_crane(_rpy="y", rpy=lambda t: 0.01 * t**2 if t <= 5 else 0.25, tau=20, show=True, idx=1) + _turn_crane(_rpy="r", rpy=lambda t: 20 * t**2 if t < 0.01 else 0.002, tau=20, te=50, show=True, idx=2) + _turn_crane(_rpy="p", rpy=lambda t: 20 * t**2 if t < 0.01 else 0.002, tau=20, te=50, show=True, idx=3) _circular(tau=1e10, v0=2.0, wire_l=None, angle=stable, show=show, idx=0) _circular(tau=1e10, v0=2.0, wire_l=None, show=show, idx=1) _circular(tau=1e10, v0=50.0, wire_l=lambda t: 1.0 - 0.05 * t, show=show, idx=2) - _move_crane(te=50, v0=0.1, tau=20, show=True, idx=0) - _move_crane(v0=0.01, c_pos=lambda t: 0.01 * t, tau=50, show=True, idx=1) - _move_crane(v0=0.0, c_pos=lambda t: 0.01 * t**2 if t <= 5 else 0.25, tau=20.0, show=True, idx=2) - _move_crane(te=500, c_pos=lambda t: 0.0001 * np.sin((0.01 + 0.01 * t) * t), show=True, idx=3) - # ??_move_crane(c_pos=lambda t: 0.001 * np.sin( wd* t), tau=tau, te=200, show=True, idx=4) - # ??_move_crane(c_pos=lambda t: 0.1 * np.sin( 0.1*wd* t), tau=tau, te=100, show=True, idx=5) - # ??_move_crane(c_pos=lambda t: 0.1 * np.sin( 10*wd* t), tau=tau, te=100, show=True, idx=6) - _turn_crane(_rpy="y", rpy=lambda t: 0.01 * t**2 if t <= 5 else 0.25, tau=20, show=True, idx=1) - _turn_crane(_rpy="r", rpy=lambda t: 0.01 * t**2 if t <= 5 else 0.25, tau=20, show=True, idx=2) - _turn_crane(_rpy="p", rpy=lambda t: 0.01 * t**2 if t <= 5 else 0.25, tau=20, show=True, idx=3) + _circular(tau=1e10, v0=50.0, wire_l=None, angle=-90, show=show, idx=3) + _circular(tau=20, v0=2.1 * np.sqrt(9.81 * 1.0), wire_l=None, angle=90, show=show, idx=3) # @pytest.mark.skip() @@ -598,33 +634,34 @@ def check_rot(r: Rot, vec: Sequence[float]): print(f"Rotate (0,0,1): {rot2.apply(np.array((0, 0, 1)))} =? {r.apply(np.array((0, 0, 1)))}") raise AssertionError("Rotation object error") from None - f, p, b1, r = [b for b in crane.booms()] + f, p, b1, w = [b for b in crane.booms()] + assert isinstance(w, Wire) if show: show_crane(crane, markCOM=True, markSubCOM=True, title="test_sequence(). Initial state. Crane folded") - assert np.allclose(r.origin + r.length * r.direction, (10, 0, 2.5)) - assert np.allclose(r.end, (10, 0, 2.5)) + assert np.allclose(w.origin + w.length * w.direction, (10, 0, 2.5)) + assert np.allclose(w.end, (10, 0, 2.5)) logger.info("boom1 up (vertical)") b1.boom_setter((None, 0, None)) crane.calc_statics_dynamics() - assert np.allclose(r.end, (0 + 0.5 / sqrt(2), 0, 3 + 10 - 0.5 / sqrt(2)), atol=0.1) # somewhat lower due to length - pendulum_relax(r, show=False) - assert np.allclose(r.end, (0, 0, 3 + 10 - 0.5), atol=0.05), f"Found equilibrium position {r.end}" + assert np.allclose(w.end, (0 + 0.5 / sqrt(2), 0, 3 + 10 - 0.5 / sqrt(2)), atol=0.1) # somewhat lower due to length + pendulum_relax(w, show=False) + assert np.allclose(w.end, (0, 0, 3 + 10 - 0.5), atol=0.05), f"Found equilibrium position {w.end}" logger.info("boom1 45 deg up") b1.boom_setter((None, radians(45), None)) - r.pendulum_relax() # includes calc_statics_dynamics() - assert np.allclose(r.end, [10 / sqrt(2), 0, 3 - 0.5 + 10 / sqrt(2)]) + w.pendulum_relax() # includes calc_statics_dynamics() + assert np.allclose(w.end, [10 / sqrt(2), 0, 3 - 0.5 + 10 / sqrt(2)]) check_rot(b1._rot, (1.0 / sqrt(2), 0.0, 1.0 / sqrt(2))) if show: show_crane(crane, markCOM=True, markSubCOM=True, title="test_sequence(). boom 1 at 45 degrees.") logger.info("wire 0.5m -> 5m") - r.boom_setter((5.0, None, None)) + w.boom_setter((5.0, None, None)) if show: show_crane(crane, markCOM=True, markSubCOM=True, title="test_sequence(). Wire 0.5m -> 5m") - r.pendulum_relax() # includes calc_statics_dynamics() - assert np.allclose(r.end, [10 / sqrt(2), 0, 3 + 10 / (sqrt(2)) - 5]), f"Found load position {r.end}" + w.pendulum_relax() # includes calc_statics_dynamics() + assert np.allclose(w.end, [10 / sqrt(2), 0, 3 + 10 / (sqrt(2)) - 5]), f"Found load position {w.end}" logger.info("turn base 360 deg in steps of 5 deg") for i in range(73): # turn in steps of 5 deg @@ -635,7 +672,7 @@ def check_rot(r: Rot, vec: Sequence[float]): check_rot(b1._rot, (1.0 / sqrt(2) * cos(radians(i * 5)), 1.0 / sqrt(2) * sin(radians(i * 5)), 1.0 / sqrt(2))) check_rot(b1._rot, (1.0 / sqrt(2), 0.0, 1.0 / sqrt(2))) # back to starting point - r.pendulum_relax() # includes calc_statics_dynamics() + w.pendulum_relax() # includes calc_statics_dynamics() if show: show_crane(crane, markCOM=True, markSubCOM=True, title="test_sequence(). Turned base 360 degrees") @@ -653,42 +690,44 @@ def check_rot(r: Rot, vec: Sequence[float]): crane.rot((0, 0, 0)) check_rot(b1._rot, (1.0 / sqrt(2), 0.0, 1.0 / sqrt(2))) # back to starting point - len_0 = r.length + len_0 = w.length # boom1 up. Dynamic for i in range(450): angle = 45 - i / 100 b1.boom_setter((None, angle, None)) crane.calc_statics_dynamics(1.0) - # print(f"angle {angle}, rope length: {r.length}, rope origin: {r.origin}. rope velocity: {r.velocity}") - assert len_0 == r.length, "Length of rope has changed!" + # print(f"angle {angle}, rope length: {w.length}, rope origin: {w.origin}. rope velocity: {w.velocity}") + assert len_0 == w.length, "Length of rope has changed!" # @pytest.mark.skip() def test_change_length(crane: Crane, show: bool = False): - f, p, b1, r = [b for b in crane.booms()] + f, p, b1, w = [b for b in crane.booms()] + assert isinstance(w, Wire) if show: show_crane(crane, title="test_change_length(). Initial") - assert r.anchor1 is None - assert np.allclose(b1.end, r.origin) - r.boom_setter((3, None, None)) # increase length - r.pendulum_relax() - assert np.allclose(r.end, (10.0, 0, 0), atol=0.001) + assert w.anchor1 is None + assert np.allclose(b1.end, w.origin) + w.boom_setter((3, None, None)) # increase length + w.pendulum_relax() + assert np.allclose(w.end, (10.0, 0, 0), atol=0.001) if show: show_crane(crane, title="test_change_length(). rope -> 3m") # @pytest.mark.skip() def test_rotation(crane: Crane, show: bool = False): - f, p, b1, r = [b for b in crane.booms()] + f, p, b1, w = [b for b in crane.booms()] + assert isinstance(w, Wire) b1.boom_setter((None, 0, None)) # b1 up crane.calc_statics_dynamics() # ensure that wire is adjusted assert np.allclose(b1.direction, (0, 0, 1)) assert b1.length == 10 assert np.allclose(b1.c_m, (0, 0, 5)) # measured relative to its origin! - assert np.allclose(r.origin, (0, 0, 3 + 10)), f"r.origin {r.origin}, != (0,0,13)" - assert abs(r.length - 0.5) < 1e-10, f"Unexpected length {r.length}" + assert np.allclose(w.origin, (0, 0, 3 + 10)), f"w.origin {w.origin}, != (0,0,13)" + assert abs(w.length - 0.5) < 1e-10, f"Unexpected length {w.length}" b1.boom_setter((None, radians(90), None)) # b1 east (as initially) - r.pendulum_relax() + w.pendulum_relax() assert np.allclose(b1.direction, (1, 0, 0)) assert b1.length == 10 assert np.allclose(b1.c_m, (5, 0, 0)) @@ -701,7 +740,7 @@ def test_rotation(crane: Crane, show: bool = False): p.boom_setter((None, None, radians(-90))) # turn p so that b1 south assert np.allclose(b1.direction, (0, -1, 0)) assert np.allclose(b1.c_m, (0, -5, 0)) - r.pendulum_relax() + w.pendulum_relax() crane.calc_statics_dynamics() if show: show_crane(crane, markCOM=True, markSubCOM=False, title="test_rotation(). Before rotation. b1 east.") @@ -714,8 +753,9 @@ def test_rotation(crane: Crane, show: bool = False): # @pytest.mark.skip() def test_c_m(crane: Crane, show: bool = False): # Note: Boom.c_m is a local measure, calculated from Boom.origin - f, p, b1, r = [b for b in crane.booms()] - r.mass -= 50 + f, p, b1, w = [b for b in crane.booms()] + assert isinstance(w, Wire) + w.mass -= 50 if show: show_crane(crane, markCOM=True, markSubCOM=False, title="test_c_m(). Initial") # initial c_m location @@ -727,7 +767,7 @@ def test_c_m(crane: Crane, show: bool = False): assert np.allclose(b1.c_m, (0, 0, 5)) # update all subsystem center of mass points. Need to do that from last boom! crane.calc_statics_dynamics() - r.pendulum_relax() + w.pendulum_relax() if show: show_crane(crane, markCOM=True, markSubCOM=False, title="test_c_m(). All booms along a line in z-direction") assert np.allclose(b1.c_m_sub[1], (0, 0, 3 + 5)), f"b1 CoM: {b1.c_m_sub[1]}" @@ -810,8 +850,8 @@ def crane_check( show_crane(crane, True, False, title=title) assert np.allclose(p.end, _p), f"Expected (p) {_p}, found {p.end}" assert np.allclose(b1.end, _b1), f"Expected (b1) {_b1}, found {b1.end}" - assert np.allclose(r.end, wire_end(b1.end, _r, 1.0, relaxed), atol=1e-4), ( - f"Expected (wire) {wire_end(b1.end, _r, 1.0, relaxed)}. Found {r.end}" + assert np.allclose(w.end, wire_end(b1.end, _r, 1.0, relaxed), atol=1e-4), ( + f"Expected (wire) {wire_end(b1.end, _r, 1.0, relaxed)}. Found {w.end}" ) def rotate_calc_check( @@ -819,19 +859,20 @@ def rotate_calc_check( absolute: bool, _p: tuple[float, ...], _b1: tuple[float, ...], - _r: tuple[float, ...], + _w: tuple[float, ...], show_it: int, title: str, relaxed: bool, ): crane.rotate(angles, absolute=absolute) # roll 90 deg crane.calc_statics_dynamics() - if relaxed: - r.pendulum_relax() - crane_check(_p, _b1, _r, show_it=show_it, title=title, relaxed=relaxed) + if relaxed and isinstance(w, Wire): + w.pendulum_relax() + crane_check(_p, _b1, _w, show_it=show_it, title=title, relaxed=relaxed) - f, p, b1, r = [b for b in crane.booms()] - r.boom_setter((1.0, None, None)) # wire 1m + f, p, b1, w = [b for b in crane.booms()] + assert isinstance(w, Wire) + w.boom_setter((1.0, None, None)) # wire 1m _a = radians(90) # the angle used in the rotations rotate_calc_check((0, 0, 0), False, (0, 0, 3), (10, 0, 3), (10, 0, 2), 1, "test_orientation(initial).", False) @@ -858,7 +899,7 @@ def rotate_calc_check( crane.d_angular = np.array((np.radians(1), 0, 0), float) for i in range(90): crane.do_step(i, 1.0) - r.pendulum_relax() + w.pendulum_relax() crane.calc_statics_dynamics() if show & 32 >= 32: show_crane(crane, True, False, title="roll, pitch, yaw successively") @@ -868,12 +909,13 @@ def rotate_calc_check( def test_force_torque(crane: Crane, show: bool = False): """Check that results for force and torque are correct with respect to crane states and movements.""" - f, p, b1, r = [b for b in crane.booms()] + f, p, b1, w = [b for b in crane.booms()] + assert isinstance(w, Wire) p.mass = 100 p.mass_center = (0.5, 0, 0) p.boom_setter((10, None, None)) b1.mass = 100 - r.mass = 0.0 + w.mass = 0.0 traces: dict[str, list[float]] # ----------------------------------------------------------------- logger.info("all booms upwards. Calculate static force and torque") @@ -906,12 +948,12 @@ def test_force_torque(crane: Crane, show: bool = False): crane.velocity = np.array((0, 0, 0), float) crane.d_velocity = np.array((0, 0, 0), float) b1.boom_setter((None, np.radians(90), 0)) - r.boom_setter((10, None, None)) - set_wire_direction(r, (90, 0), degrees=True) - r.mass = 50 - r.pendulum_relax() + w.boom_setter((10, None, None)) + set_wire_direction(w, (90, 0), degrees=True) + w.mass = 50 + w.pendulum_relax() crane.do_step(3.0, 1.0) - assert np.allclose(r.end, (10, 0, 0)) + assert np.allclose(w.end, (10, 0, 0)) assert abs(crane.c_m_sub[0] - 250) < 1e-10, f"Mass {crane.c_m_sub[0]}" assert np.allclose(crane.force, (0, 0, -9.81 * 250)) c_m = np.array(((5 * 100 + 10 * 50) / 250, 0, (5 * 100 + 10 * 100) / 250), float) @@ -930,8 +972,8 @@ def test_force_torque(crane: Crane, show: bool = False): crane.d_velocity = np.array((0.1 * np.sin(1.0 * time), 0, 0), float) _time.append(time) crane.do_step(time, dt) - _xe.append(r.origin[0]) - _load.append(r.end[0]) + _xe.append(w.origin[0]) + _load.append(w.end[0]) _force.append(crane.force[0]) _torque.append(crane.torque[1]) time += dt @@ -951,8 +993,8 @@ def test_force_torque(crane: Crane, show: bool = False): crane.d_angular = np.array((0, 0.1 * np.sin(1.0 * time), 0), float) _time.append(time) crane.do_step(time, dt) - traces["xe"].append(float(r.origin[0])) - traces["load"].append(float(r.end[0])) + traces["xe"].append(float(w.origin[0])) + traces["load"].append(float(w.end[0])) traces["force"].append(float(crane.force[0])) traces["torque"].append(float(crane.torque[1])) time += dt @@ -960,6 +1002,17 @@ def test_force_torque(crane: Crane, show: bool = False): do_show(_time, traces, {"xe": 1, "load": 1, "force": 2, "torque": 2}) +# def test_last_origins(): +# lo = LastOrigins() +# for t in (1.0, 2.5, 5.0): +# lo.append( t, np.array( (np.sin(t), np.cos(t), t**2), float)) +# assert lo.times[1] == 2.5 +# assert np.allclose( lo.orgs[1], (np.sin(2.5), np.cos(2.5), 2.5**2)) +# der1, der2 = lo.derivatives() +# print("DER1", der1, (np.cos(2.5), -np.sin(2.5), 2*2.5)) +# print("DER2", der2, (-np.sin(2.5), -np.cos(2.5), 2.0)) + + if __name__ == "__main__": retcode = pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" @@ -972,7 +1025,7 @@ def test_force_torque(crane: Crane, show: bool = False): # test_mass_center() # test_initial(_crane()) # test_orientation(_crane(), 32) - # test_pendulum( show=True) + # test_pendulum(show=True) # test_animate_pendulum(show=True) # test_sequence(_crane(), show=True) # test_change_length(_crane(), show=True) @@ -981,3 +1034,4 @@ def test_force_torque(crane: Crane, show: bool = False): # test_animation(_crane(), show=True) # test_animation_control(_crane(), show=True) # test_force_torque(_crane(),show=True) + # test_last_origins() diff --git a/tests/test_crane_configurations.py b/tests/test_crane_configurations.py index 6cd294b..a7cc1f8 100644 --- a/tests/test_crane_configurations.py +++ b/tests/test_crane_configurations.py @@ -8,7 +8,7 @@ from component_model.utils.controls import Control, Controls from py_crane.animation import AnimateCrane -from py_crane.boom import Boom +from py_crane.boom import Boom, Wire from py_crane.crane import Crane logger = logging.getLogger(__name__) @@ -19,7 +19,7 @@ def _pendulum(): crane = Crane() w = crane.add_boom( - name="wire", + "wire", description="The wire fixed to the last boom. Flexible connection", mass=50.0, # so far basically the hook mass_center=1.0, @@ -27,6 +27,7 @@ def _pendulum(): q_factor=10.0, ) crane.position = np.array((0, 0, 1), float) + assert isinstance(w, Wire) w.pendulum_relax() return crane @@ -34,21 +35,21 @@ def _pendulum(): def _mobile_crane(): crane = Crane() _p = crane.add_boom( - name="pedestal", + "pedestal", description="The crane base, on one side fixed to the vessel and on the other side the first crane boom is fixed to it. The mass should include all additional items fixed to it, like the operator's cab", mass=2000.0, mass_center=(0.5, -1, 0.8), boom=(3.0, 0, 0), ) _b = crane.add_boom( - name="boom1", + "boom1", description="The first boom. Can be lifted", mass=200.0, mass_center=0.5, boom=(10.0, np.radians(90), 0), ) _w = crane.add_boom( - name="wire", + "wire", description="The wire fixed to the last boom. Flexible connection", mass=50.0, # so far basically the hook mass_center=1.0, @@ -108,41 +109,42 @@ def test_mobile_crane(show: bool = False): def _knuckle_boom_crane(): crane = Crane() _p = crane.add_boom( - name="pedestal", + "pedestal", description="The crane base, on one side fixed to the vessel and on the other side the first crane boom is fixed to it. The mass should include all additional items fixed to it, like the operator's cab", mass=2000.0, mass_center=(0.5, -1, 0.8), boom=(1.0, 0, 0), ) _b1 = crane.add_boom( - name="boom1", + "boom1", description="The first boom. Can be lifted", mass=200.0, mass_center=0.5, boom=(3.0, np.radians(45), 0), ) _b2 = crane.add_boom( - name="boom2", + "boom2", description="The second boom. Can be lifted", mass=200.0, mass_center=0.5, boom=(5.0, np.radians(170), np.pi), ) _b3 = crane.add_boom( - name="boom3", + "boom3", description="The third boom. Can be lifted", mass=200.0, mass_center=0.5, boom=(7.0, np.radians(170), 0.0), ) _w = crane.add_boom( - name="wire", + "wire", description="The wire fixed to the last boom. Flexible connection", mass=50.0, # so far basically the hook mass_center=1.0, boom=(1e-6, 0, 0), q_factor=100.0, ) + assert isinstance(_w, Wire) _w.pendulum_relax() return crane @@ -157,6 +159,7 @@ def move_knuckle_boom_crane( # initial definition of controls and start values controls = Controls(limit_err=logging.WARNING) # CRITICAL) f, p, b1, b2, b3, w = list(crane.booms()) + assert isinstance(w, Wire) controls.extend( ( Control("luff3", (None, (-0.09, 0.09), (-0.09, 0.05)), rw=partial(_boom, b3, 1)), diff --git a/tests/test_crane_on_spring.py b/tests/test_crane_on_spring.py index c6664b7..a5e41f5 100644 --- a/tests/test_crane_on_spring.py +++ b/tests/test_crane_on_spring.py @@ -13,7 +13,7 @@ from component_model.utils.controls import Control, Controls from py_crane.animation import AnimateCrane -from py_crane.boom import Boom +from py_crane.boom import Boom, Wire from py_crane.crane import Crane sys.path.insert(0, str(Path(__file__).parent.parent / "examples")) @@ -191,21 +191,21 @@ def calc_force(t: float, x: np.ndarray, v: np.ndarray, crane: Any, dt: float | N crane = Crane() _ = crane.add_boom( - name="pedestal", + "pedestal", description="The crane base, on one side fixed to the vessel and on the other side the first crane boom is fixed to it. The mass should include all additional items fixed to it, like the operator's cab", mass=pM, mass_center=pCoM, boom=(pH, 0, 0), ) _ = crane.add_boom( - name="boom", + "boom", description="The first boom. Can be lifted", mass=bM, mass_center=0.5, boom=(bL, bA, 0), ) _ = crane.add_boom( - name="wire", + "wire", description="The wire fixed to the last boom. Flexible connection", mass=wM, # so far basically the hook mass_rng=(50, 2000), @@ -293,23 +293,28 @@ def movement(crane: Crane, dt: float, t_end: float) -> Generator[tuple[float, Cr # initial definition of controls and start values controls = Controls(limit_err=logging.WARNING) # CRITICAL) f, p, b1, w = list(crane.booms()) + assert isinstance(w, Wire) + w.additional_checks = True controls.extend( ( Control("turn", (None, (-0.31, 0.31), (-1, 1)), rw=partial(_boom, p, 2)), Control("luff", ((0, 1.58), (-0.18, 0.09), (-0.09, 0.05)), rw=partial(_boom, b1, 1)), Control("boom", ((8, 50), (-0.7, 0.5), (-0.1, 0.05)), rw=partial(_boom, b1, 0)), - Control("wire", ((0.5, 50), (-0.1, 1.0), (-0.05, 0.1)), rw=partial(_boom, w, 0)), + Control("wire", ((0.5, 50), (-0.1, 1.0), (-0.4, 0.5)), rw=partial(_boom, w, 0)), ) ) # From time 0 we set three goals + controls["wire"].setgoal(0, 7.0) # wire length increase to 9m controls["turn"].setgoal(0, np.radians(90)) # turn pedestal 90 deg controls["luff"].setgoal(0, np.radians(45)) # luff boom to 45 deg - # controls["boom"].setgoal(0, 12.0) # increase length to 15m (0.1m/s) + controls["boom"].setgoal(0, 12.0) # increase length to 15m (0.1m/s) for time in np.linspace(0.0, t_end, int(t_end / dt) + 1): - if time > 10 and not len(controls[3].goal): # Start to increase wire length with 1m/s - controls["wire"].setgoal(1, 1.0) + # if abs(time-10.0)<0.5*dt: # Stop wire length increase + # controls["wire"].setgoal(1, 0.0) # wire length increase with 1m/s controls.step(time, dt) + # if time > 7.2: + # print(f"w.additional:{w.additional_checks}") crane.do_step(time, dt) # takes updated force, x and v and calculates updated x and v # crane.boom0.translate(-crane.boom0.origin) yield (time + dt, crane) diff --git a/tests/test_mobile_crane_FMU.py b/tests/test_mobile_crane_FMU.py index 895e545..de9f163 100644 --- a/tests/test_mobile_crane_FMU.py +++ b/tests/test_mobile_crane_FMU.py @@ -240,11 +240,11 @@ def run_simulation( if __name__ == "__main__": - retcode = pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "True", __file__]) + retcode = 0 # pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "True", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" crane = _mobile_crane_fmu() # test_fmu() # test_mass_center() # test_mobilecrane_fmu( crane, show=True) # test_run_mobilecrane_static( crane, show=True) - # test_run_mobilecrane_move(crane, show=True) + test_run_mobilecrane_move(crane, show=True) From 793119a5821bcc67bb247582e6dd06d1c29eda03 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Tue, 24 Mar 2026 08:45:04 +0100 Subject: [PATCH 06/53] Updates on pendulum action. Tests are still not all activated and need more work. --- .vscode/extensions.json | 21 ------------ .vscode/launch.json | 66 -------------------------------------- .vscode/settings.json | 52 ------------------------------ examples/MobileCrane.fmu | Bin 927868 -> 928648 bytes pyproject.toml | 2 +- src/py_crane/animation.py | 3 +- src/py_crane/boom.py | 12 ++++--- src/py_crane/boom_fmu.py | 2 +- tests/test_crane.py | 36 ++++++++++++--------- 9 files changed, 31 insertions(+), 163 deletions(-) delete mode 100644 .vscode/extensions.json delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/settings.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index d6549af..0000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp - // List of extensions which should be recommended for users of this workspace. - "recommendations": [ - "ms-python.python", - "ms-python.vscode-pylance", - "github.copilot-chat", - "ms-python.debugpy", - "ms-python.vscode-python-envs", - "charliermarsh.ruff", - "sourcery.sourcery", - "njpwerner.autodocstring", - "editorconfig.editorconfig", - "ms-toolsai.jupyter", - "ms-toolsai.jupyter-renderers", - "ms-python.mypy-type-checker", - ], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. - "unwantedRecommendations": [] -} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 152bc0c..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Python: Debug Tests", - "type": "debugpy", - "request": "launch", - "program": "${file}", - "purpose": [ - "debug-test" - ], - "console": "integratedTerminal", - "env": { - "PYTEST_ADDOPTS": "--no-cov" - }, - "autoReload": { - "enable": true - }, - "justMyCode": true, - }, - { - "name": "Python: Current File, cwd = file dir", - "type": "debugpy", - "request": "launch", - "cwd": "${fileDirname}", // working dir = dir where current file is - "program": "${file}", - "console": "integratedTerminal", - "justMyCode": true, - "autoReload": { - "enable": true - }, - }, - { - "name": "Python: Current File, cwd = workspace root folder", - "type": "debugpy", - "request": "launch", - "cwd": "${workspaceFolder}", // working dir = workspace root folder - "program": "${file}", - "console": "integratedTerminal", - "autoReload": { - "enable": true - }, - "justMyCode": true, - }, - { - "name": "py_crane test_cli", - "type": "debugpy", - "request": "launch", - "cwd": "${workspaceFolder}\\tests", - "program": "${workspaceFolder}\\src\\py_crane\\cli\\__main__.py", - "args": [ - "test_config_file", - "--run", - "-v", - ], - "console": "integratedTerminal", - "autoReload": { - "enable": true - }, - "justMyCode": true, - }, - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index f48f804..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "python.languageServer": "Pylance", - "editor.formatOnSave": true, - "notebook.formatOnSave.enabled": true, - "notebook.codeActionsOnSave": { - "notebook.source.fixAll": "explicit", - "notebook.source.organizeImports": "explicit", - }, - "[python]": { - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.fixAll": "always", - "source.organizeImports": "explicit", - }, - "editor.defaultFormatter": "charliermarsh.ruff", - }, - "autoDocstring.docstringFormat": "numpy", - "python.testing.unittestEnabled": false, - "python.testing.pytestEnabled": true, - "python.analysis.logLevel": "Warning", - "python.analysis.completeFunctionParens": false, - "python.analysis.diagnosticMode": "workspace", - "python.analysis.indexing": true, - "python.analysis.autoImportCompletions": true, - "python.analysis.inlayHints.variableTypes": false, - "python.analysis.inlayHints.functionReturnTypes": false, - "python.analysis.inlayHints.pytestParameters": true, - "python.terminal.executeInFileDir": true, - "python.terminal.activateEnvironment": false, - "python.terminal.activateEnvInCurrentTerminal": false, - "python.analysis.packageIndexDepths": [ - { - "name": "pandas", - "depth": 4, - "includeAllSymbols": true, - }, - { - "name": "matplotlib", - "depth": 4, - "includeAllSymbols": true, - }, - { - "name": "mpl_toolkits", - "depth": 4, - "includeAllSymbols": true, - }, - ], - "mypy-type-checker.importStrategy": "fromEnvironment", - "mypy-type-checker.reportingScope": "custom", - "mypy-type-checker.preferDaemon": true, - "ruff.configurationPreference": "filesystemFirst", -} \ No newline at end of file diff --git a/examples/MobileCrane.fmu b/examples/MobileCrane.fmu index c455a21bf87e4603b7600fd5a3be0d4c4a77c7e0..281b1ed8f5df70834eb28a54c92b3c8b906e221e 100644 GIT binary patch delta 2425 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.3.2", + "component-model>=0.4.0", "pythonfmu>=0.7.0", "numpy>=2.3", "scipy>=1.16", diff --git a/src/py_crane/animation.py b/src/py_crane/animation.py index 343dc96..7a605b8 100644 --- a/src/py_crane/animation.py +++ b/src/py_crane/animation.py @@ -13,7 +13,6 @@ from py_crane.crane import Crane logger = logging.getLogger(__name__) -logging.basicConfig(level=logging.INFO) FrameGenerator: TypeAlias = Generator[tuple[float, Crane], None, None] @@ -171,7 +170,7 @@ def __init__( dt: float = 0.01, t_end: float = 10.0, figsize: tuple[int, int] = (8, 8), - axes_lim: tuple[tuple[int, int], ...] = ((-10, 10), (-10, 10), (-10, 10)), + axes_lim: tuple[tuple[int, int], ...] = ((-10, 10), (-10, 10), (0, 10)), interval: int = 10, repeat: bool = False, title: str = "Pendulum animation", diff --git a/src/py_crane/boom.py b/src/py_crane/boom.py index 84eb97d..86db01b 100644 --- a/src/py_crane/boom.py +++ b/src/py_crane/boom.py @@ -550,16 +550,17 @@ def ivp_fun( l0 (float): start length of wire. Used only if wire length changes dl_dt (float): Optional change of wire length through dt: l(t) = l0 + dl_dt* t """ - r = y[:3] - (s_v * t + 0.5 * s_acc * t**2) - v = y[3:] - (s_v + s_acc * t) - # v -= np.dot(r,v)/r2 * v # avoid that numerical issues lead to strange behaviour + # r = y[:3] - (s_v * t + 0.5 * s_acc * t**2) + # v = y[3:] - (s_v + s_acc * t) + r = y[:3] # - 0.5 * s_acc * t**2 + v = y[3:] # - s_acc * t if dl_dt is not None: lt = l0 + dl_dt * t r2 = lt * lt v += dl_dt tangential = np.cross(r, np.cross(r, g)) centripetal = np.dot(v, v) * r - acc = -((tangential + centripetal) / r2) # + s_acc + acc = -((tangential + centripetal) / r2) - s_acc # print(f"r[0]:{r[0]}, v:{v}, s_v:{s_v}, s_acc:{s_acc} => acc:{acc}") return np.append(v, acc) @@ -626,7 +627,8 @@ def ivp_fun( v = np.array((0, 0, 0), float) else: v *= 1 - dt / self._damping_time # see note - # v += s_v + # v -= s_v + # print("PEND", position[0], v[0], s_v[0], s_acc[0]) self.direction = normalized(position) rel_direction = self.anchor0.rot().apply(self.direction, inverse=True) # dir. relative to previous boom diff --git a/src/py_crane/boom_fmu.py b/src/py_crane/boom_fmu.py index 6df5ae1..c043d4f 100644 --- a/src/py_crane/boom_fmu.py +++ b/src/py_crane/boom_fmu.py @@ -134,7 +134,7 @@ def __init__( ) else: Wire.__init__( - self, # type: ignore[arg-type] ## call sequence ensures that it is called from Wire + self, # type: ignore[arg-type] ## call sequence ensures that it is called from Wire model, name, mass=mass0, diff --git a/tests/test_crane.py b/tests/test_crane.py index 863e1fb..2b765b3 100644 --- a/tests/test_crane.py +++ b/tests/test_crane.py @@ -16,7 +16,7 @@ # from mpl_toolkits.mplot3d.art3d import Line3D -logger = logging.getLogger() +logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) np.set_printoptions(precision=4, suppress=True) @@ -303,6 +303,7 @@ def check_fit( def check_sin( _t: list[float], _p: list[float], _v: list[float], ampl: float, w: float, tau: float = 20, _len: float = 1.0 ): + """Check whether _t, _p, _v represents a forced oscillation with parameters ampl, w,tau.""" # wd = np.sqrt(9.81 / _len - 1 / 4 / tau**2) osc = ForcedOscillator1D(k=9.81 / _len, c=1 / tau, a=ampl * 9.81 / _len, wf=w) x, dx = osc.calc(np.array(_t)) @@ -330,7 +331,7 @@ def check_shock( """Check whether a short acceleration on crane movement results in the expected pendulum action.""" wd = np.sqrt(9.81 / length - 1 / 4 / tau**2) phi0 = np.arcsin(_p0 / length) - assert np.isclose(_p[10], 0.0, atol=1e-3), f"Position after shock {_p[10]} should be still at origo." + # assert np.isclose(_p[10], 0.0, atol=1e-3), f"Position after shock {_p[10]} should be still at origo." assert np.isclose(np.average(_p), _p0, atol=1e-4), f"Average position {np.average(_p)} != {_p0}" expected = [_p0 * (1.0 - np.exp(-(t - 0.01) / 2 / tau) * cos(wd * (t - 0.01))) for t in _t] maxdiff = max(np.absolute(np.array(expected[4:]) - np.array(_p[4:]))) @@ -428,13 +429,16 @@ def _move_crane( gamma = 1.0 / 2.0 / w.damping(damping_time=tau) # damping wd = np.sqrt(9.81 / w.length - gamma**2) T = 2 * np.pi / wd + _dt = dt if dt is not None else (T / 100 if idx in (0, 4, 5, 6) else 0.01) + w.pendulum_relax() + w.cm_v = np.array((v0, 0, 0), float) + # ??w.origin_v[0] = (c_pos(_dt)-c_pos(-_dt))/2/_dt + if c_pos is not None: + w.origin_acc[0] = (c_pos(_dt) - 2 * c_pos(0.0) + c_pos(-_dt)) / _dt / _dt if idx in (4, 5, 6): osc = ForcedOscillator1D(k=9.81, c=gamma, a=0.001, wf=wd) else: osc = None - w.pendulum_relax() - w.cm_v = np.array((v0, 0, 0), float) - _dt = dt if dt is not None else (T / 100 if idx in (0, 4, 5, 6) else 0.01) time, x_pos, y_pos, z_pos, speed, z_max, misc = sim_run( crane_spec=(crane, w), te=te, @@ -460,11 +464,11 @@ def _move_crane( expected = v0 * np.exp(-t / tau) * np.cos(wd * t) assert abs(v - expected) < 3e-2, f"Damped oscillation speed @{t}: {v}!={expected}" elif idx == 1: # v0=0. Crane moves with speed 0.1/s - assert abs(np.average(speed) - 0.1) < 1e-4, f"Average speed {np.average(speed)} != 0.1" + assert abs(np.average(speed)) < 1e-3, f"Average !local! speed_x {np.average(speed)} != 0.0" assert abs(x_pos[-1] - 0.1 * te) < 1e-2, f"End position {0.1 * te}!={x_pos[-1]}" - elif idx == 2: # origin moves with v=0.01, while load starts with same speed - assert all(abs(v - v0) < 1e-3 for v in speed), f"Tail speeds: {speed[0:10]}" - assert abs(x_pos[-1] - v0 * te) < 1e-3, f"End position: {x_pos[-1]}!={v0 * 100}" + elif idx == 2: # origin moves with v=0.1, while load starts with same speed (+initial acc. causes oscillations) + assert abs(np.average(speed)) < 1e-3, f"Speed oscillations around: {np.average(speed)}" + assert abs(x_pos[-1] - v0 * te) < 1e-2, f"End position: {x_pos[-1]}!={v0 * te}" elif idx == 3: # v0=0. Shock in x-direction with a=20 check_shock(list(time), x_pos, speed, _p0=20 * 0.01**2, tau=20, eps=1e-3) elif idx == 4: # low frequency sine @@ -599,6 +603,7 @@ def _circular( gamma = 1.0 / 2.0 / tau wd = np.sqrt(9.81 / 1.0 - gamma**2) + ##?? Deactivated tests to be checked and updated _b2 = (2**2 / 9.81) ** 2 stable = np.degrees(np.arccos(np.sqrt(np.sqrt(_b2 + _b2**2 / 4) - _b2 / 2))) _move_crane(te=50, v0=0.1, c_pos=None, tau=20, show=True, idx=0) @@ -607,15 +612,16 @@ def _circular( _move_crane(v0=0.0, c_pos=lambda t: 20 * t**2 if t < 0.01 else 0.002, tau=20, te=10, dt=0.01, show=True, idx=3) _move_crane(c_pos=lambda t: 0.001 * np.sin(0.1 * wd * t), tau=tau, te=200, dt=0.01, show=True, idx=4) _move_crane(c_pos=lambda t: -0.00005 * np.sin(wd * t), tau=tau, te=200, show=True, idx=5) - _move_crane(c_pos=lambda t: 0.00005 * np.sin(10 * wd * t), tau=tau, te=10, dt=0.0001, show=True, idx=6) - _move_crane(v0=0.0, c_pos=lambda t: 0.001 * np.sin((0.01 + 0.01 * t) * t), tau=20, te=500, show=True, idx=7) + + # _move_crane(c_pos=lambda t: 0.00005 * np.sin(10 * wd * t), tau=tau, te=10, dt=0.0001, show=True, idx=6) + # _move_crane(v0=0.0, c_pos=lambda t: 0.001 * np.sin((0.01 + 0.01 * t) * t), tau=20, te=500, show=True, idx=7) # _move_crane(v0=0.0, c_pos=lambda t: 1 * t**2, tau=20, te=100, dt=0.01, show=True, idx=8) - _turn_crane(_rpy="y", rpy=lambda t: 0.01 * t**2 if t <= 5 else 0.25, tau=20, show=True, idx=1) - _turn_crane(_rpy="r", rpy=lambda t: 20 * t**2 if t < 0.01 else 0.002, tau=20, te=50, show=True, idx=2) - _turn_crane(_rpy="p", rpy=lambda t: 20 * t**2 if t < 0.01 else 0.002, tau=20, te=50, show=True, idx=3) + # _turn_crane(_rpy="y", rpy=lambda t: 0.01 * t**2 if t <= 5 else 0.25, tau=20, show=True, idx=1) + # _turn_crane(_rpy="r", rpy=lambda t: 20 * t**2 if t < 0.01 else 0.002, tau=20, te=50, show=True, idx=2) + # _turn_crane(_rpy="p", rpy=lambda t: 20 * t**2 if t < 0.01 else 0.002, tau=20, te=50, show=True, idx=3) _circular(tau=1e10, v0=2.0, wire_l=None, angle=stable, show=show, idx=0) _circular(tau=1e10, v0=2.0, wire_l=None, show=show, idx=1) - _circular(tau=1e10, v0=50.0, wire_l=lambda t: 1.0 - 0.05 * t, show=show, idx=2) + # _circular(tau=1e10, v0=50.0, wire_l=lambda t: 1.0 - 0.05 * t, show=show, idx=2) _circular(tau=1e10, v0=50.0, wire_l=None, angle=-90, show=show, idx=3) _circular(tau=20, v0=2.1 * np.sqrt(9.81 * 1.0), wire_l=None, angle=90, show=show, idx=3) From f4a45f8fcfe80d93ecd8e443d873a2898a690309 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:08:56 +0100 Subject: [PATCH 07/53] Changed 'uv sync --upgrade' to 'uv sync -U' --- .github/workflows/_code_quality.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/_code_quality.yml b/.github/workflows/_code_quality.yml index 2c61347..2b76b9c 100644 --- a/.github/workflows/_code_quality.yml +++ b/.github/workflows/_code_quality.yml @@ -18,7 +18,7 @@ jobs: with: python-version-file: "pyproject.toml" - name: Install the project - run: uv sync --upgrade + run: uv sync -U - name: Run ruff format run: uv run ruff format --diff @@ -37,7 +37,7 @@ jobs: with: python-version-file: "pyproject.toml" - name: Install the project - run: uv sync --upgrade + run: uv sync -U - name: Run ruff check run: uv run ruff check --diff @@ -56,7 +56,7 @@ jobs: # with: # python-version-file: "pyproject.toml" # - name: Install the project - # run: uv sync --upgrade --extra test + # run: uv sync -U --extra test # - name: Run pyright # run: uv run pyright @@ -75,6 +75,6 @@ jobs: with: python-version-file: "pyproject.toml" - name: Install the project - run: uv sync --upgrade --extra test + run: uv sync -U --extra test - name: Run mypy run: uv run mypy From ef71eeb7aef9070fba35833e5bf3dfe4331d4bbd Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:27 +0100 Subject: [PATCH 08/53] Removed redundant 'Set up Python' steps (no longer needed, as 'uv sync' will automatically install Python if not present). --- .../workflows/_build_and_publish_documentation.yml | 4 ---- .github/workflows/_code_quality.yml | 12 ------------ .github/workflows/_test.yml | 4 ---- .github/workflows/_test_future.yml | 4 ---- 4 files changed, 24 deletions(-) diff --git a/.github/workflows/_build_and_publish_documentation.yml b/.github/workflows/_build_and_publish_documentation.yml index 24157b7..8df022f 100644 --- a/.github/workflows/_build_and_publish_documentation.yml +++ b/.github/workflows/_build_and_publish_documentation.yml @@ -21,10 +21,6 @@ jobs: with: enable-cache: true cache-dependency-glob: 'uv.lock' - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version-file: 'pyproject.toml' - name: Install the project run: uv sync --frozen - name: Print debugging information diff --git a/.github/workflows/_code_quality.yml b/.github/workflows/_code_quality.yml index 2b76b9c..9b916e8 100644 --- a/.github/workflows/_code_quality.yml +++ b/.github/workflows/_code_quality.yml @@ -13,10 +13,6 @@ jobs: with: enable-cache: true cache-dependency-glob: "uv.lock" - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version-file: "pyproject.toml" - name: Install the project run: uv sync -U - name: Run ruff format @@ -32,10 +28,6 @@ jobs: with: enable-cache: true cache-dependency-glob: "uv.lock" - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version-file: "pyproject.toml" - name: Install the project run: uv sync -U - name: Run ruff check @@ -70,10 +62,6 @@ jobs: with: enable-cache: true cache-dependency-glob: "uv.lock" - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version-file: "pyproject.toml" - name: Install the project run: uv sync -U --extra test - name: Run mypy diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index c311319..290504e 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -25,10 +25,6 @@ jobs: with: enable-cache: true cache-dependency-glob: 'uv.lock' - - name: Install Python ${{ matrix.python.version }} - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python.version }} - name: Install the project run: uv sync -p ${{ matrix.python.version }} -U --no-dev --extra test - name: Run pytest diff --git a/.github/workflows/_test_future.yml b/.github/workflows/_test_future.yml index c7f4728..83812d3 100644 --- a/.github/workflows/_test_future.yml +++ b/.github/workflows/_test_future.yml @@ -24,10 +24,6 @@ jobs: with: enable-cache: true cache-dependency-glob: 'uv.lock' - - name: Install Python ${{ matrix.python.version }} - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python.version }} - name: Remove Python upper version constraint from pyproject.toml shell: pwsh run: | From f9e4e47d4c809888923385d76a30bea7e0424ff1 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:28 +0100 Subject: [PATCH 09/53] Removed redundant 'Set up Python' steps (no longer needed, as 'uv sync' will automatically install Python if not present). --- .github/workflows/_build_package.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/_build_package.yml b/.github/workflows/_build_package.yml index edfba50..d864c9e 100644 --- a/.github/workflows/_build_package.yml +++ b/.github/workflows/_build_package.yml @@ -15,9 +15,6 @@ jobs: with: enable-cache: true cache-dependency-glob: "uv.lock" - - uses: actions/setup-python@v6 - with: - python-version-file: "pyproject.toml" - name: Build source distribution and wheel run: uv build - name: Run twine check From cc0af6a955106a2627d85c687623201558129e64 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:29 +0100 Subject: [PATCH 10/53] Added 'name: Checkout code' to uses of 'actions/checkout', for better readability and consistency across workflow files. --- .github/workflows/_build_package.yml | 3 ++- .github/workflows/_code_quality.yml | 12 ++++++++---- .github/workflows/_merge_into_release.yml | 3 ++- .github/workflows/_test.yml | 3 ++- .github/workflows/_test_future.yml | 3 ++- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/_build_package.yml b/.github/workflows/_build_package.yml index d864c9e..5b9a203 100644 --- a/.github/workflows/_build_package.yml +++ b/.github/workflows/_build_package.yml @@ -7,7 +7,8 @@ jobs: name: Build source distribution runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - name: Checkout code + uses: actions/checkout@v5 with: lfs: true - name: Install uv diff --git a/.github/workflows/_code_quality.yml b/.github/workflows/_code_quality.yml index 9b916e8..8ec3f06 100644 --- a/.github/workflows/_code_quality.yml +++ b/.github/workflows/_code_quality.yml @@ -7,7 +7,8 @@ jobs: runs-on: ubuntu-latest name: ruff format steps: - - uses: actions/checkout@v5 + - name: Checkout code + uses: actions/checkout@v5 - name: Install uv uses: astral-sh/setup-uv@v7 with: @@ -22,7 +23,8 @@ jobs: runs-on: ubuntu-latest name: ruff check steps: - - uses: actions/checkout@v5 + - name: Checkout code + uses: actions/checkout@v5 - name: Install uv uses: astral-sh/setup-uv@v7 with: @@ -37,7 +39,8 @@ jobs: # runs-on: ubuntu-latest # name: pyright # steps: - # - uses: actions/checkout@v5 + # - name: Checkout code + uses: actions/checkout@v5 # - name: Install uv # uses: astral-sh/setup-uv@v7 # with: @@ -56,7 +59,8 @@ jobs: runs-on: ubuntu-latest name: mypy steps: - - uses: actions/checkout@v5 + - name: Checkout code + uses: actions/checkout@v5 - name: Install uv uses: astral-sh/setup-uv@v7 with: diff --git a/.github/workflows/_merge_into_release.yml b/.github/workflows/_merge_into_release.yml index 1220f6f..67348de 100644 --- a/.github/workflows/_merge_into_release.yml +++ b/.github/workflows/_merge_into_release.yml @@ -12,7 +12,8 @@ jobs: runs-on: ubuntu-latest environment: release steps: - - uses: actions/checkout@v5 + - name: Checkout code + uses: actions/checkout@v5 with: # Fetch the whole history to prevent unrelated history errors fetch-depth: 0 diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index 290504e..c2bc014 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -19,7 +19,8 @@ jobs: - version: '3.13' - version: '3.14' steps: - - uses: actions/checkout@v5 + - name: Checkout code + uses: actions/checkout@v5 - name: Install uv uses: astral-sh/setup-uv@v7 with: diff --git a/.github/workflows/_test_future.yml b/.github/workflows/_test_future.yml index 83812d3..1a883dc 100644 --- a/.github/workflows/_test_future.yml +++ b/.github/workflows/_test_future.yml @@ -18,7 +18,8 @@ jobs: - version: '3.15.0-alpha - 3.15.0' uvpy: '3.15' steps: - - uses: actions/checkout@v5 + - name: Checkout code + uses: actions/checkout@v5 - name: Install uv uses: astral-sh/setup-uv@v7 with: From 1cf892014afcce8ca8db63f29e73a5e4300a59b0 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:30 +0100 Subject: [PATCH 11/53] Added 'name: Upload build artifacts' to uses of 'actions/upload-artifact', for better readability and consistency across workflow files. --- .github/workflows/_build_package.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/_build_package.yml b/.github/workflows/_build_package.yml index 5b9a203..0582277 100644 --- a/.github/workflows/_build_package.yml +++ b/.github/workflows/_build_package.yml @@ -20,7 +20,8 @@ jobs: run: uv build - name: Run twine check run: uvx twine check --strict dist/* - - uses: actions/upload-artifact@v5 + - name: Upload build artifacts + uses: actions/upload-artifact@v5 with: path: | dist/*.tar.gz From 26b0b2c13f9bbce8d6efbaab677e58ed7b8b28f5 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:32 +0100 Subject: [PATCH 12/53] Added 'name: Download build artifacts' to uses of 'actions/download-artifact', for better readability and consistency across workflow files. --- .github/workflows/publish_release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml index ff03f35..147da09 100644 --- a/.github/workflows/publish_release.yml +++ b/.github/workflows/publish_release.yml @@ -18,7 +18,8 @@ jobs: permissions: id-token: write steps: - - uses: actions/download-artifact@v5 + - name: Download build artifacts + uses: actions/download-artifact@v5 with: name: artifact path: dist From 6268a1b5688da911d0799e3ebcd6209b0e0f5be3 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:33 +0100 Subject: [PATCH 13/53] Ensured that actions 'upload-artifact' and 'download-artifact' uniformly specify 'dist' as (file)name for the artifact uploaded (or downloaded, respectively), for consistency across workflow files. --- .github/workflows/_build_package.yml | 1 + .github/workflows/publish_release.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/_build_package.yml b/.github/workflows/_build_package.yml index 0582277..03a51ac 100644 --- a/.github/workflows/_build_package.yml +++ b/.github/workflows/_build_package.yml @@ -23,6 +23,7 @@ jobs: - name: Upload build artifacts uses: actions/upload-artifact@v5 with: + name: dist path: | dist/*.tar.gz dist/*.whl diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml index 147da09..dc434b4 100644 --- a/.github/workflows/publish_release.yml +++ b/.github/workflows/publish_release.yml @@ -21,7 +21,7 @@ jobs: - name: Download build artifacts uses: actions/download-artifact@v5 with: - name: artifact + name: dist path: dist - uses: pypa/gh-action-pypi-publish@release/v1 # with: # Uncomment this line to publish to testpypi From b038573a42c7f552d73b0b32e395574f5ea64ffd Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:34 +0100 Subject: [PATCH 14/53] Replaced 'Run twine check' with 'Check build artifacts' in workflow step names, to better reflect the purpose of the step. --- .github/workflows/_build_package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_build_package.yml b/.github/workflows/_build_package.yml index 03a51ac..6390a18 100644 --- a/.github/workflows/_build_package.yml +++ b/.github/workflows/_build_package.yml @@ -18,7 +18,7 @@ jobs: cache-dependency-glob: "uv.lock" - name: Build source distribution and wheel run: uv build - - name: Run twine check + - name: Check build artifacts run: uvx twine check --strict dist/* - name: Upload build artifacts uses: actions/upload-artifact@v5 From b37adaba9a7b56e01ac0b265e63f9044d15e2f22 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:35 +0100 Subject: [PATCH 15/53] Replaced 'Build source distribution and wheel' with 'Build source distribution and wheels' (plural) in workflow step names. --- .github/workflows/_build_package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_build_package.yml b/.github/workflows/_build_package.yml index 6390a18..c48c122 100644 --- a/.github/workflows/_build_package.yml +++ b/.github/workflows/_build_package.yml @@ -16,7 +16,7 @@ jobs: with: enable-cache: true cache-dependency-glob: "uv.lock" - - name: Build source distribution and wheel + - name: Build source distribution and wheels run: uv build - name: Check build artifacts run: uvx twine check --strict dist/* From c50954ed5c78a270a22a89a6dab558db2695a60e Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:36 +0100 Subject: [PATCH 16/53] Added 'name: Publish to PyPI' to uses of 'pypa/gh-action-pypi-publish', for better readability and consistency across workflow files. --- .github/workflows/publish_release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml index dc434b4..bd97143 100644 --- a/.github/workflows/publish_release.yml +++ b/.github/workflows/publish_release.yml @@ -23,7 +23,8 @@ jobs: with: name: dist path: dist - - uses: pypa/gh-action-pypi-publish@release/v1 + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 # with: # Uncomment this line to publish to testpypi # repository-url: https://test.pypi.org/legacy/ # Uncomment this line to publish to testpypi merge_into_release: From 57f10dab0c1ecd3f2d03eb4537f718b3dde9f165 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:37 +0100 Subject: [PATCH 17/53] Updated the syntax used for the OS and Python matrix in test workflows. (Part 1) --- .github/workflows/_test.yml | 5 +---- .github/workflows/_test_future.yml | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index c2bc014..8919d01 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -9,10 +9,7 @@ jobs: strategy: fail-fast: true matrix: - platform: - - runner: ubuntu-latest - - runner: windows-latest - # - runner: macos-latest + os: [ubuntu-latest, windows-latest] python: - version: '3.11' - version: '3.12' diff --git a/.github/workflows/_test_future.yml b/.github/workflows/_test_future.yml index 1a883dc..3bd5432 100644 --- a/.github/workflows/_test_future.yml +++ b/.github/workflows/_test_future.yml @@ -11,9 +11,7 @@ jobs: strategy: fail-fast: true matrix: - platform: - - runner: ubuntu-latest - - runner: windows-latest + os: [ubuntu-latest, windows-latest] python: - version: '3.15.0-alpha - 3.15.0' uvpy: '3.15' From 9466677af0ea5c5bb47cdba4121b699aa4c8536c Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:38 +0100 Subject: [PATCH 18/53] Updated the syntax used for the OS and Python matrix in test workflows. (Part 2) --- .github/workflows/_test.yml | 4 ++-- .github/workflows/_test_future.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index 8919d01..d288d8c 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -4,8 +4,8 @@ on: workflow_call jobs: test: - name: Test on ${{matrix.python.version}}-${{matrix.platform.runner}} - runs-on: ${{ matrix.platform.runner }} + name: Test on ${{matrix.python.version}}-${{ matrix.os }} + runs-on: ${{ matrix.os }} strategy: fail-fast: true matrix: diff --git a/.github/workflows/_test_future.yml b/.github/workflows/_test_future.yml index 3bd5432..6c7a578 100644 --- a/.github/workflows/_test_future.yml +++ b/.github/workflows/_test_future.yml @@ -5,9 +5,9 @@ on: workflow_call jobs: test315: - name: Test on ${{matrix.python.version}}-${{matrix.platform.runner}} (experimental) + name: Test on ${{matrix.python.version}}-${{ matrix.os }} (experimental) continue-on-error: true - runs-on: ${{ matrix.platform.runner }} + runs-on: ${{ matrix.os }} strategy: fail-fast: true matrix: From 108368fd660797fb973c9a0ed0f70afbe9c8e1dd Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:39 +0100 Subject: [PATCH 19/53] Updated the syntax used for the OS and Python matrix in test workflows. (Part 3) --- .github/workflows/_test.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index d288d8c..cc9f179 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -10,11 +10,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest, windows-latest] - python: - - version: '3.11' - - version: '3.12' - - version: '3.13' - - version: '3.14' + python: ['3.11', '3.12', '3.13', '3.14'] steps: - name: Checkout code uses: actions/checkout@v5 From 3df2aa0ffff1e2d75f8f20d4e64dd84200dcc8d0 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:41 +0100 Subject: [PATCH 20/53] Updated the syntax used for the OS and Python matrix in test workflows. (Part 4) --- .github/workflows/_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index cc9f179..cd3fd65 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -4,7 +4,7 @@ on: workflow_call jobs: test: - name: Test on ${{matrix.python.version}}-${{ matrix.os }} + name: Test on ${{ matrix.python }}-${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: true @@ -20,7 +20,7 @@ jobs: enable-cache: true cache-dependency-glob: 'uv.lock' - name: Install the project - run: uv sync -p ${{ matrix.python.version }} -U --no-dev --extra test + run: uv sync -p ${{ matrix.python }} -U --no-dev --extra test - name: Run pytest run: > uv run --with pytest --with pytest-cov From 994ed049c3675a07c8c94dd64af40d3d53410086 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:42 +0100 Subject: [PATCH 21/53] Updated the syntax used for the OS and Python matrix in test workflows. (Part 5) --- .github/workflows/_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index cd3fd65..e9b1332 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -4,7 +4,7 @@ on: workflow_call jobs: test: - name: Test on ${{ matrix.python }}-${{ matrix.os }} + name: Test (Python ${{ matrix.python }}, ${{ matrix.os }}) runs-on: ${{ matrix.os }} strategy: fail-fast: true From 5eb26001b20fcf87c5319308949c859c2ff78a16 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:43 +0100 Subject: [PATCH 22/53] Updated the syntax used for the OS and Python matrix in test workflows. (Part 5) --- .github/workflows/_test_future.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_test_future.yml b/.github/workflows/_test_future.yml index 6c7a578..3b629e5 100644 --- a/.github/workflows/_test_future.yml +++ b/.github/workflows/_test_future.yml @@ -5,7 +5,7 @@ on: workflow_call jobs: test315: - name: Test on ${{matrix.python.version}}-${{ matrix.os }} (experimental) + name: Test (Python ${{ matrix.python.version }}, ${{ matrix.os }}) (experimental) continue-on-error: true runs-on: ${{ matrix.os }} strategy: From 1f04bb1654f73884faef37cbe22643321a0ea164 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:45 +0100 Subject: [PATCH 23/53] pull_request_to_main.yml and nightly_build.yml: Added 'workflow_dispatch:' in selected workflows to allow manual trigger of the workflow. --- .github/workflows/nightly_build.yml | 1 + .github/workflows/pull_request_to_main.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/nightly_build.yml b/.github/workflows/nightly_build.yml index 33fb658..41eb2a2 100644 --- a/.github/workflows/nightly_build.yml +++ b/.github/workflows/nightly_build.yml @@ -4,6 +4,7 @@ run-name: Nightly Build (by @${{ github.actor }}) on: schedule: - cron: '30 5 * * *' + workflow_dispatch: # Allow manual trigger jobs: test: diff --git a/.github/workflows/pull_request_to_main.yml b/.github/workflows/pull_request_to_main.yml index 2bff438..1e73c39 100644 --- a/.github/workflows/pull_request_to_main.yml +++ b/.github/workflows/pull_request_to_main.yml @@ -11,6 +11,7 @@ on: - converted_to_draft branches: - main + workflow_dispatch: # Allow manual trigger concurrency: group: pr-${{ github.ref }}-1 From 1321419d705f63ccaaef78c362548a3a87b4360e Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:46 +0100 Subject: [PATCH 24/53] Updated CHANGELOG.md --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c49550..b144062 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,21 @@ The changelog format is based on [Keep a Changelog](https://keepachangelog.com/e * -/- +### Changed +* GitHub Workflows: + * Added 'name: Checkout code' to uses of 'actions/checkout', for better readability and consistency across workflow files. + * Added 'name: Download build artifacts' to uses of 'actions/download-artifact', for better readability and consistency across workflow files. + * Added 'name: Publish to PyPI' to uses of 'pypa/gh-action-pypi-publish', for better readability and consistency across workflow files. + * Added 'name: Upload build artifacts' to uses of 'actions/upload-artifact', for better readability and consistency across workflow files. + * Changed 'uv sync --upgrade' to 'uv sync -U' + * Ensured that actions 'upload-artifact' and 'download-artifact' uniformly specify 'dist' as (file)name for the artifact uploaded (or downloaded, respectively), for consistency across workflow files. + * pull_request_to_main.yml and nightly_build.yml: Added 'workflow_dispatch:' in selected workflows to allow manual trigger of the workflow. + * Removed redundant 'Set up Python' steps (no longer needed, as 'uv sync' will automatically install Python if not present). + * Replaced 'Build source distribution and wheel' with 'Build source distribution and wheels' (plural) in workflow step names. + * Replaced 'Run twine check' with 'Check build artifacts' in workflow step names, to better reflect the purpose of the step. + * Updated the syntax used for the OS and Python matrix in test workflows. + + ## [0.3.0] - 2026-02-02 ### Breaking Change: New package name From bb4bf5ef97ca698a0658015c6fe0f7f74b82328b Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:49 +0100 Subject: [PATCH 25/53] Sphinx conf.py: Updated year in copyright statement to 2026 --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 624ae7e..4723244 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = "py-crane" -copyright = "2025, DNV AS. All rights reserved." +copyright = "2026, DNV AS. All rights reserved." author = "Siegfried Eisinger, Jorge Luis Mendez" # The full version, including alpha/beta/rc tags From 910faa9ba7322de7e200e505e33161e88385a59b Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:50 +0100 Subject: [PATCH 26/53] Updated CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b144062..834c567 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ The changelog format is based on [Keep a Changelog](https://keepachangelog.com/e * Replaced 'Build source distribution and wheel' with 'Build source distribution and wheels' (plural) in workflow step names. * Replaced 'Run twine check' with 'Check build artifacts' in workflow step names, to better reflect the purpose of the step. * Updated the syntax used for the OS and Python matrix in test workflows. +* Sphinx Documentation: + * Sphinx conf.py: Updated year in copyright statement to 2026 ## [0.3.0] - 2026-02-02 From 4c9b3db59f285bd94db44423d0fb839c7c845909 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:09:56 +0100 Subject: [PATCH 27/53] Updated to plotly>=6.5 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0c13e84..a381b11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,7 +78,7 @@ rest = ["docutils>=0.3"] test = [ "FMPy>=0.3.21", "matplotlib>=3.10", - "plotly>=6.3", + "plotly>=6.5", "libcosimpy >= 0.0.5", ] From 55657f45340b18c3dcbceaea85f64997bba2f96d Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:10:02 +0100 Subject: [PATCH 28/53] Updated to ruff>=0.15.1 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a381b11..450c4f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,7 +94,7 @@ Changelog = "https://github.com/dnv-opensource/py-crane/blob/main/CHANGELOG.md" dev = [ "pytest>=8.4", "pytest-cov>=7.0", - "ruff>=0.14.3", + "ruff>=0.15.1", "pyright>=1.1.407", "mypy>=1.18", "sourcery>=1.40", From 51866d063708cd04bc546880147146617087ec5d Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:10:07 +0100 Subject: [PATCH 29/53] Updated to sphinx-argparse-cli>=1.20.1 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 450c4f2..640167d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -100,7 +100,7 @@ dev = [ "sourcery>=1.40", "pre-commit>=4.3", "Sphinx>=8.2", - "sphinx-argparse-cli>=1.20", + "sphinx-argparse-cli>=1.20.1", "sphinx-autodoc-typehints>=3.5", "sphinxcontrib-mermaid>=1.0.0", "myst-parser>=4.0", From 729a79c1171415134c0d9c8135b8205d27bf9a65 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:10:08 +0100 Subject: [PATCH 30/53] Updated to furo>=2025.12 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 640167d..6a36e03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -104,7 +104,7 @@ dev = [ "sphinx-autodoc-typehints>=3.5", "sphinxcontrib-mermaid>=1.0.0", "myst-parser>=4.0", - "furo>=2025.9", + "furo>=2025.12", ] [tool.uv] From e7a6421017af8d8d55bfe5dd660d8f0fe2d28d04 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:10:11 +0100 Subject: [PATCH 31/53] Updated to sphinx>=9.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6a36e03..7421d7b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,7 +99,7 @@ dev = [ "mypy>=1.18", "sourcery>=1.40", "pre-commit>=4.3", - "Sphinx>=8.2", + "Sphinx>=9.0", "sphinx-argparse-cli>=1.20.1", "sphinx-autodoc-typehints>=3.5", "sphinxcontrib-mermaid>=1.0.0", From 6f65ed71e627ffa093a15e1a4524f1bec7540659 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:10:15 +0100 Subject: [PATCH 32/53] Updated to myst-parser>=5.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7421d7b..aeaf5da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -103,7 +103,7 @@ dev = [ "sphinx-argparse-cli>=1.20.1", "sphinx-autodoc-typehints>=3.5", "sphinxcontrib-mermaid>=1.0.0", - "myst-parser>=4.0", + "myst-parser>=5.0", "furo>=2025.12", ] From ca24fb96e52d02bfbae5ed8c764a356a4291af24 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:10:25 +0100 Subject: [PATCH 33/53] Updated to sphinxcontrib-mermaid>=2.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index aeaf5da..5e3d860 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -102,7 +102,7 @@ dev = [ "Sphinx>=9.0", "sphinx-argparse-cli>=1.20.1", "sphinx-autodoc-typehints>=3.5", - "sphinxcontrib-mermaid>=1.0.0", + "sphinxcontrib-mermaid>=2.0", "myst-parser>=5.0", "furo>=2025.12", ] From 2648da2c051217b8c6a342c857edee55c6e20702 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:10:30 +0100 Subject: [PATCH 34/53] Updated to pyright>=1.1.408 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5e3d860..022e4a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,7 +95,7 @@ dev = [ "pytest>=8.4", "pytest-cov>=7.0", "ruff>=0.15.1", - "pyright>=1.1.407", + "pyright>=1.1.408", "mypy>=1.18", "sourcery>=1.40", "pre-commit>=4.3", From b90064413ea3e6037e73797a199de8c816c279e8 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:10:32 +0100 Subject: [PATCH 35/53] Updated to pytest>=9.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 022e4a7..838c584 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,7 +92,7 @@ Changelog = "https://github.com/dnv-opensource/py-crane/blob/main/CHANGELOG.md" [dependency-groups] dev = [ - "pytest>=8.4", + "pytest>=9.0", "pytest-cov>=7.0", "ruff>=0.15.1", "pyright>=1.1.408", From 0055e6da39c0fca2d7a1da4d7e39fc5d786bfb8d Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:10:36 +0100 Subject: [PATCH 36/53] Updated to pre-commit>=4.5 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 838c584..a0a7b13 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,7 +98,7 @@ dev = [ "pyright>=1.1.408", "mypy>=1.18", "sourcery>=1.40", - "pre-commit>=4.3", + "pre-commit>=4.5", "Sphinx>=9.0", "sphinx-argparse-cli>=1.20.1", "sphinx-autodoc-typehints>=3.5", From 2b853f30d79529d8aa160992a13eef8d83a94cb1 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:10:43 +0100 Subject: [PATCH 37/53] Updated to scipy>=1.17.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a0a7b13..2ebbb6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,7 +69,7 @@ dependencies = [ "component-model>=0.4.0", "pythonfmu>=0.7.0", "numpy>=2.3", - "scipy>=1.16", + "scipy>=1.17.0", ] # dynamic = ["version"] From 44c072448b95d75627c4b1d361980427ad20d4be Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:10:44 +0100 Subject: [PATCH 38/53] Updated to docutils>=0.22.4 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2ebbb6b..14bcf7c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ dependencies = [ # dynamic = ["version"] [project.optional-dependencies] -rest = ["docutils>=0.3"] +rest = ["docutils>=0.22.4"] test = [ "FMPy>=0.3.21", "matplotlib>=3.10", From d03ebcce72d194d2d1920a336e4e5a32c2caad41 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:10:48 +0100 Subject: [PATCH 39/53] Updated to mypy>=1.19.1 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 14bcf7c..3b4c8cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,7 +96,7 @@ dev = [ "pytest-cov>=7.0", "ruff>=0.15.1", "pyright>=1.1.408", - "mypy>=1.18", + "mypy>=1.19.1", "sourcery>=1.40", "pre-commit>=4.5", "Sphinx>=9.0", From d0c9b76d66a9364338b15b4809d0a86fe5bd7d11 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:10:58 +0100 Subject: [PATCH 40/53] Updated to sourcery>=1.43.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3b4c8cb..578a7f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,7 +97,7 @@ dev = [ "ruff>=0.15.1", "pyright>=1.1.408", "mypy>=1.19.1", - "sourcery>=1.40", + "sourcery>=1.43.0", "pre-commit>=4.5", "Sphinx>=9.0", "sphinx-argparse-cli>=1.20.1", From b05f21a3740a84e7f23e8f58c1aa9d969d85aad1 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:11:03 +0100 Subject: [PATCH 41/53] Updated to sphinx-autodoc-typehints>=3.6 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 578a7f4..e3f8812 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,7 +101,7 @@ dev = [ "pre-commit>=4.5", "Sphinx>=9.0", "sphinx-argparse-cli>=1.20.1", - "sphinx-autodoc-typehints>=3.5", + "sphinx-autodoc-typehints>=3.6", "sphinxcontrib-mermaid>=2.0", "myst-parser>=5.0", "furo>=2025.12", From 9ba29cfa9313d2ff1065a3035310192ae25ab3c1 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:11:05 +0100 Subject: [PATCH 42/53] .pre-commit-config.yaml: Updated rev of ruff-pre-commit to v0.15.1 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3d5f534..c7ca41c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-toml - id: check-merge-conflict - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.3 + rev: v0.15.1 hooks: - id: ruff-format - id: ruff-check From 3c201f76f0cba9ff52e52244f260ec20a7a4b17c Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:11:06 +0100 Subject: [PATCH 43/53] Updated CHANGELOG.md --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 834c567..142ebd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,25 @@ The changelog format is based on [Keep a Changelog](https://keepachangelog.com/e * Sphinx conf.py: Updated year in copyright statement to 2026 +### Dependencies +* .pre-commit-config.yaml: Updated rev of ruff-pre-commit to v0.15.1 +* Updated to docutils>=0.22.4 +* Updated to furo>=2025.12 +* Updated to mypy>=1.19.1 +* Updated to myst-parser>=5.0 +* Updated to plotly>=6.5 +* Updated to pre-commit>=4.5 +* Updated to pyright>=1.1.408 +* Updated to pytest>=9.0 +* Updated to ruff>=0.15.1 +* Updated to scipy>=1.17.0 +* Updated to sourcery>=1.43.0 +* Updated to sphinx-argparse-cli>=1.20.1 +* Updated to sphinx-autodoc-typehints>=3.6 +* Updated to sphinx>=9.0 +* Updated to sphinxcontrib-mermaid>=2.0 + + ## [0.3.0] - 2026-02-02 ### Breaking Change: New package name From f9b397c0f155110512e238a97e7ce8c8d353be6e Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:12:49 +0100 Subject: [PATCH 44/53] updated uv.lock --- uv.lock | 1489 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 751 insertions(+), 738 deletions(-) diff --git a/uv.lock b/uv.lock index 0d7b02c..a70a92b 100644 --- a/uv.lock +++ b/uv.lock @@ -33,20 +33,20 @@ wheels = [ [[package]] name = "attrs" -version = "25.4.0" +version = "26.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] [[package]] name = "babel" -version = "2.17.0" +version = "2.18.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, + { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, ] [[package]] @@ -64,11 +64,11 @@ wheels = [ [[package]] name = "certifi" -version = "2026.1.4" +version = "2026.2.25" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, ] [[package]] @@ -82,101 +82,117 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" }, - { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" }, - { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" }, - { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" }, - { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" }, - { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" }, - { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" }, - { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" }, - { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" }, - { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" }, - { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" }, - { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" }, - { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" }, - { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" }, - { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" }, - { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" }, - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, +version = "3.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e", size = 293582, upload-time = "2026-03-15T18:50:25.454Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b7/b1a117e5385cbdb3205f6055403c2a2a220c5ea80b8716c324eaf75c5c95/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9", size = 197240, upload-time = "2026-03-15T18:50:27.196Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5f/2574f0f09f3c3bc1b2f992e20bce6546cb1f17e111c5be07308dc5427956/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d", size = 217363, upload-time = "2026-03-15T18:50:28.601Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d1/0ae20ad77bc949ddd39b51bf383b6ca932f2916074c95cad34ae465ab71f/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de", size = 212994, upload-time = "2026-03-15T18:50:30.102Z" }, + { url = "https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73", size = 204697, upload-time = "2026-03-15T18:50:31.654Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/8a18fc411f085b82303cfb7154eed5bd49c77035eb7608d049468b53f87c/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c", size = 191673, upload-time = "2026-03-15T18:50:33.433Z" }, + { url = "https://files.pythonhosted.org/packages/ff/a7/11cfe61d6c5c5c7438d6ba40919d0306ed83c9ab957f3d4da2277ff67836/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc", size = 201120, upload-time = "2026-03-15T18:50:35.105Z" }, + { url = "https://files.pythonhosted.org/packages/b5/10/cf491fa1abd47c02f69687046b896c950b92b6cd7337a27e6548adbec8e4/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f", size = 200911, upload-time = "2026-03-15T18:50:36.819Z" }, + { url = "https://files.pythonhosted.org/packages/28/70/039796160b48b18ed466fde0af84c1b090c4e288fae26cd674ad04a2d703/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef", size = 192516, upload-time = "2026-03-15T18:50:38.228Z" }, + { url = "https://files.pythonhosted.org/packages/ff/34/c56f3223393d6ff3124b9e78f7de738047c2d6bc40a4f16ac0c9d7a1cb3c/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398", size = 218795, upload-time = "2026-03-15T18:50:39.664Z" }, + { url = "https://files.pythonhosted.org/packages/e8/3b/ce2d4f86c5282191a041fdc5a4ce18f1c6bd40a5bd1f74cf8625f08d51c1/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e", size = 201833, upload-time = "2026-03-15T18:50:41.552Z" }, + { url = "https://files.pythonhosted.org/packages/3b/9b/b6a9f76b0fd7c5b5ec58b228ff7e85095370282150f0bd50b3126f5506d6/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed", size = 213920, upload-time = "2026-03-15T18:50:43.33Z" }, + { url = "https://files.pythonhosted.org/packages/ae/98/7bc23513a33d8172365ed30ee3a3b3fe1ece14a395e5fc94129541fc6003/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021", size = 206951, upload-time = "2026-03-15T18:50:44.789Z" }, + { url = "https://files.pythonhosted.org/packages/32/73/c0b86f3d1458468e11aec870e6b3feac931facbe105a894b552b0e518e79/charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e", size = 143703, upload-time = "2026-03-15T18:50:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4", size = 153857, upload-time = "2026-03-15T18:50:47.563Z" }, + { url = "https://files.pythonhosted.org/packages/e2/dc/9abe19c9b27e6cd3636036b9d1b387b78c40dedbf0b47f9366737684b4b0/charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316", size = 142751, upload-time = "2026-03-15T18:50:49.234Z" }, + { url = "https://files.pythonhosted.org/packages/e5/62/c0815c992c9545347aeea7859b50dc9044d147e2e7278329c6e02ac9a616/charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab", size = 295154, upload-time = "2026-03-15T18:50:50.88Z" }, + { url = "https://files.pythonhosted.org/packages/a8/37/bdca6613c2e3c58c7421891d80cc3efa1d32e882f7c4a7ee6039c3fc951a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21", size = 199191, upload-time = "2026-03-15T18:50:52.658Z" }, + { url = "https://files.pythonhosted.org/packages/6c/92/9934d1bbd69f7f398b38c5dae1cbf9cc672e7c34a4adf7b17c0a9c17d15d/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2", size = 218674, upload-time = "2026-03-15T18:50:54.102Z" }, + { url = "https://files.pythonhosted.org/packages/af/90/25f6ab406659286be929fd89ab0e78e38aa183fc374e03aa3c12d730af8a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff", size = 215259, upload-time = "2026-03-15T18:50:55.616Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ef/79a463eb0fff7f96afa04c1d4c51f8fc85426f918db467854bfb6a569ce3/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5", size = 207276, upload-time = "2026-03-15T18:50:57.054Z" }, + { url = "https://files.pythonhosted.org/packages/f7/72/d0426afec4b71dc159fa6b4e68f868cd5a3ecd918fec5813a15d292a7d10/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0", size = 195161, upload-time = "2026-03-15T18:50:58.686Z" }, + { url = "https://files.pythonhosted.org/packages/bf/18/c82b06a68bfcb6ce55e508225d210c7e6a4ea122bfc0748892f3dc4e8e11/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a", size = 203452, upload-time = "2026-03-15T18:51:00.196Z" }, + { url = "https://files.pythonhosted.org/packages/44/d6/0c25979b92f8adafdbb946160348d8d44aa60ce99afdc27df524379875cb/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2", size = 202272, upload-time = "2026-03-15T18:51:01.703Z" }, + { url = "https://files.pythonhosted.org/packages/2e/3d/7fea3e8fe84136bebbac715dd1221cc25c173c57a699c030ab9b8900cbb7/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5", size = 195622, upload-time = "2026-03-15T18:51:03.526Z" }, + { url = "https://files.pythonhosted.org/packages/57/8a/d6f7fd5cb96c58ef2f681424fbca01264461336d2a7fc875e4446b1f1346/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6", size = 220056, upload-time = "2026-03-15T18:51:05.269Z" }, + { url = "https://files.pythonhosted.org/packages/16/50/478cdda782c8c9c3fb5da3cc72dd7f331f031e7f1363a893cdd6ca0f8de0/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d", size = 203751, upload-time = "2026-03-15T18:51:06.858Z" }, + { url = "https://files.pythonhosted.org/packages/75/fc/cc2fcac943939c8e4d8791abfa139f685e5150cae9f94b60f12520feaa9b/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2", size = 216563, upload-time = "2026-03-15T18:51:08.564Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b7/a4add1d9a5f68f3d037261aecca83abdb0ab15960a3591d340e829b37298/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923", size = 209265, upload-time = "2026-03-15T18:51:10.312Z" }, + { url = "https://files.pythonhosted.org/packages/6c/18/c094561b5d64a24277707698e54b7f67bd17a4f857bbfbb1072bba07c8bf/charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4", size = 144229, upload-time = "2026-03-15T18:51:11.694Z" }, + { url = "https://files.pythonhosted.org/packages/ab/20/0567efb3a8fd481b8f34f739ebddc098ed062a59fed41a8d193a61939e8f/charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb", size = 154277, upload-time = "2026-03-15T18:51:13.004Z" }, + { url = "https://files.pythonhosted.org/packages/15/57/28d79b44b51933119e21f65479d0864a8d5893e494cf5daab15df0247c17/charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4", size = 142817, upload-time = "2026-03-15T18:51:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f", size = 294823, upload-time = "2026-03-15T18:51:15.755Z" }, + { url = "https://files.pythonhosted.org/packages/47/7b/20e809b89c69d37be748d98e84dce6820bf663cf19cf6b942c951a3e8f41/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843", size = 198527, upload-time = "2026-03-15T18:51:17.177Z" }, + { url = "https://files.pythonhosted.org/packages/37/a6/4f8d27527d59c039dce6f7622593cdcd3d70a8504d87d09eb11e9fdc6062/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf", size = 218388, upload-time = "2026-03-15T18:51:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/f6/9b/4770ccb3e491a9bacf1c46cc8b812214fe367c86a96353ccc6daf87b01ec/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8", size = 214563, upload-time = "2026-03-15T18:51:20.374Z" }, + { url = "https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9", size = 206587, upload-time = "2026-03-15T18:51:21.807Z" }, + { url = "https://files.pythonhosted.org/packages/7e/70/3def227f1ec56f5c69dfc8392b8bd63b11a18ca8178d9211d7cc5e5e4f27/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88", size = 194724, upload-time = "2026-03-15T18:51:23.508Z" }, + { url = "https://files.pythonhosted.org/packages/58/ab/9318352e220c05efd31c2779a23b50969dc94b985a2efa643ed9077bfca5/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84", size = 202956, upload-time = "2026-03-15T18:51:25.239Z" }, + { url = "https://files.pythonhosted.org/packages/75/13/f3550a3ac25b70f87ac98c40d3199a8503676c2f1620efbf8d42095cfc40/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd", size = 201923, upload-time = "2026-03-15T18:51:26.682Z" }, + { url = "https://files.pythonhosted.org/packages/1b/db/c5c643b912740b45e8eec21de1bbab8e7fc085944d37e1e709d3dcd9d72f/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c", size = 195366, upload-time = "2026-03-15T18:51:28.129Z" }, + { url = "https://files.pythonhosted.org/packages/5a/67/3b1c62744f9b2448443e0eb160d8b001c849ec3fef591e012eda6484787c/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194", size = 219752, upload-time = "2026-03-15T18:51:29.556Z" }, + { url = "https://files.pythonhosted.org/packages/f6/98/32ffbaf7f0366ffb0445930b87d103f6b406bc2c271563644bde8a2b1093/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc", size = 203296, upload-time = "2026-03-15T18:51:30.921Z" }, + { url = "https://files.pythonhosted.org/packages/41/12/5d308c1bbe60cabb0c5ef511574a647067e2a1f631bc8634fcafaccd8293/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f", size = 215956, upload-time = "2026-03-15T18:51:32.399Z" }, + { url = "https://files.pythonhosted.org/packages/53/e9/5f85f6c5e20669dbe56b165c67b0260547dea97dba7e187938833d791687/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2", size = 208652, upload-time = "2026-03-15T18:51:34.214Z" }, + { url = "https://files.pythonhosted.org/packages/f1/11/897052ea6af56df3eef3ca94edafee410ca699ca0c7b87960ad19932c55e/charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d", size = 143940, upload-time = "2026-03-15T18:51:36.15Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389", size = 154101, upload-time = "2026-03-15T18:51:37.876Z" }, + { url = "https://files.pythonhosted.org/packages/01/a5/7abf15b4c0968e47020f9ca0935fb3274deb87cb288cd187cad92e8cdffd/charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f", size = 143109, upload-time = "2026-03-15T18:51:39.565Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, + { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, + { url = "https://files.pythonhosted.org/packages/00/50/dcfbb72a5138bbefdc3332e8d81a23494bf67998b4b100703fd15fa52d81/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", size = 218758, upload-time = "2026-03-15T18:51:44.339Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/d79a9a191bb75f5aa81f3aaaa387ef29ce7cb7a9e5074ba8ea095cc073c2/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", size = 215299, upload-time = "2026-03-15T18:51:45.871Z" }, + { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, + { url = "https://files.pythonhosted.org/packages/e2/40/c430b969d41dda0c465aa36cc7c2c068afb67177bef50905ac371b28ccc7/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", size = 193706, upload-time = "2026-03-15T18:51:48.849Z" }, + { url = "https://files.pythonhosted.org/packages/48/15/e35e0590af254f7df984de1323640ef375df5761f615b6225ba8deb9799a/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", size = 202706, upload-time = "2026-03-15T18:51:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ba/2cc9e3e7dfdf7760a6ed8da7446d22536f3d0ce114ac63dee2a5a3599e62/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", size = 193511, upload-time = "2026-03-15T18:51:53.723Z" }, + { url = "https://files.pythonhosted.org/packages/9e/cb/5be49b5f776e5613be07298c80e1b02a2d900f7a7de807230595c85a8b2e/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", size = 220133, upload-time = "2026-03-15T18:51:55.333Z" }, + { url = "https://files.pythonhosted.org/packages/83/43/99f1b5dad345accb322c80c7821071554f791a95ee50c1c90041c157ae99/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", size = 203035, upload-time = "2026-03-15T18:51:56.736Z" }, + { url = "https://files.pythonhosted.org/packages/87/9a/62c2cb6a531483b55dddff1a68b3d891a8b498f3ca555fbcf2978e804d9d/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", size = 216321, upload-time = "2026-03-15T18:51:58.17Z" }, + { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, + { url = "https://files.pythonhosted.org/packages/2a/57/4ecff6d4ec8585342f0c71bc03efaa99cb7468f7c91a57b105bcd561cea8/charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", size = 144610, upload-time = "2026-03-15T18:52:02.213Z" }, + { url = "https://files.pythonhosted.org/packages/80/94/8434a02d9d7f168c25767c64671fead8d599744a05d6a6c877144c754246/charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", size = 154962, upload-time = "2026-03-15T18:52:03.658Z" }, + { url = "https://files.pythonhosted.org/packages/46/4c/48f2cdbfd923026503dfd67ccea45c94fd8fe988d9056b468579c66ed62b/charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", size = 143595, upload-time = "2026-03-15T18:52:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, + { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, + { url = "https://files.pythonhosted.org/packages/54/57/64caf6e1bf07274a1e0b7c160a55ee9e8c9ec32c46846ce59b9c333f7008/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", size = 224679, upload-time = "2026-03-15T18:52:10.043Z" }, + { url = "https://files.pythonhosted.org/packages/aa/cb/9ff5a25b9273ef160861b41f6937f86fae18b0792fe0a8e75e06acb08f1d/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", size = 223475, upload-time = "2026-03-15T18:52:11.854Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, + { url = "https://files.pythonhosted.org/packages/cd/24/afff630feb571a13f07c8539fbb502d2ab494019492aaffc78ef41f1d1d0/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", size = 199045, upload-time = "2026-03-15T18:52:14.752Z" }, + { url = "https://files.pythonhosted.org/packages/e5/17/d1399ecdaf7e0498c327433e7eefdd862b41236a7e484355b8e0e5ebd64b/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", size = 211658, upload-time = "2026-03-15T18:52:16.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, + { url = "https://files.pythonhosted.org/packages/05/34/c531bc6ac4c21da9ddfddb3107be2287188b3ea4b53b70fc58f2a77ac8d8/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", size = 201328, upload-time = "2026-03-15T18:52:19.553Z" }, + { url = "https://files.pythonhosted.org/packages/fa/73/a5a1e9ca5f234519c1953608a03fe109c306b97fdfb25f09182babad51a7/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", size = 225302, upload-time = "2026-03-15T18:52:21.043Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f6/cd782923d112d296294dea4bcc7af5a7ae0f86ab79f8fefbda5526b6cfc0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659", size = 211127, upload-time = "2026-03-15T18:52:22.491Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c5/0b6898950627af7d6103a449b22320372c24c6feda91aa24e201a478d161/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", size = 222840, upload-time = "2026-03-15T18:52:24.113Z" }, + { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, + { url = "https://files.pythonhosted.org/packages/35/1a/05dacadb0978da72ee287b0143097db12f2e7e8d3ffc4647da07a383b0b7/charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", size = 155379, upload-time = "2026-03-15T18:52:27.05Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7a/d269d834cb3a76291651256f3b9a5945e81d0a49ab9f4a498964e83c0416/charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", size = 169043, upload-time = "2026-03-15T18:52:28.502Z" }, + { url = "https://files.pythonhosted.org/packages/23/06/28b29fba521a37a8932c6a84192175c34d49f84a6d4773fa63d05f9aff22/charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", size = 148523, upload-time = "2026-03-15T18:52:29.956Z" }, + { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, ] [[package]] name = "cmake" -version = "4.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/f5/e4f5a35864293a8605bf6e9366d406ee11565b91a22f38f8b8665096c718/cmake-4.2.1.tar.gz", hash = "sha256:a07a790ca65946667c0fb286549e8e0b5a850e2f8170ae60d3418573011ca218", size = 37060, upload-time = "2025-12-21T11:23:47.499Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/b3/51560fb74ff1f369299d2b15e7c0a1c227d534753dd779ccd45b305678a8/cmake-4.2.1-py3-none-macosx_10_10_universal2.whl", hash = "sha256:ec44fa08b6ca25a63f7356a442469840841145d7b7b6f4d65318b6bd59a0f7f6", size = 51572335, upload-time = "2025-12-21T11:22:42.116Z" }, - { url = "https://files.pythonhosted.org/packages/51/4f/8278a25e101ec1ce2a1a2ca78db61fa683495a14e66a1174fc3d97879802/cmake-4.2.1-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8bdf88f8d50b64c88ffc75fb671f3ab017d803f36589f21c3f1e9f3a1b236a7", size = 29050095, upload-time = "2025-12-21T11:22:45.955Z" }, - { url = "https://files.pythonhosted.org/packages/e6/6c/efeb22bfdcebb29baf2c8853edf7ef59ff0bce0c2adaeefde4916c2b0eea/cmake-4.2.1-py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:6ca394cdea61534f12e30f0188b19ace8ba844088105b77b9fd70e6df18ef241", size = 30088543, upload-time = "2025-12-21T11:22:49.623Z" }, - { url = "https://files.pythonhosted.org/packages/9b/83/7b6ff5b0f64f764db5e87ac4c320dfc34a783f38601b2f0c1dfe0ffcbab1/cmake-4.2.1-py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c5742041f8e641d977928207e2697e9cc3242d0d01f7cb8671f63ad45dcc447b", size = 29838727, upload-time = "2025-12-21T11:22:53.799Z" }, - { url = "https://files.pythonhosted.org/packages/64/48/81fa5fa5bf19b7be74ba83ea3eddc20210995b066e3acb2329e8f821bd4e/cmake-4.2.1-py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:ae0f51d2b8dd00a7ac1578c19364140358596e449d2ac1b978af3f0b35737d01", size = 27768477, upload-time = "2025-12-21T11:22:57.608Z" }, - { url = "https://files.pythonhosted.org/packages/28/19/b54ff2e03946beeef785e6407d965a9493d26c50dd1aa09ffc7b53fbf9a5/cmake-4.2.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6333a2b16e1d55373419b9c1572a155b315bfb9d834fbdbba0f7d3428437c785", size = 28919242, upload-time = "2025-12-21T11:23:01.177Z" }, - { url = "https://files.pythonhosted.org/packages/ff/82/b001aac0162af8524067a94005e61e23426103b9283c2525df62f0b403ea/cmake-4.2.1-py3-none-manylinux_2_31_armv7l.whl", hash = "sha256:4d7a62c462cc81a6f7a5e4db7b298b4e66d851010418c8cdc5a9de0a8701f60f", size = 26109769, upload-time = "2025-12-21T11:23:04.627Z" }, - { url = "https://files.pythonhosted.org/packages/28/62/c4e8810012175ca76bb4be565955b73354a8693a4a9e983206be7cd9144e/cmake-4.2.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:3455391ffce8a860bbbd22b83c2188f13806100a21f28b8ab2c6a785def25616", size = 26217175, upload-time = "2025-12-21T11:23:07.952Z" }, - { url = "https://files.pythonhosted.org/packages/40/2e/4f657c370fdf741dfa7c1189863a15e50ea44e35fad314ba3448ad017ef8/cmake-4.2.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4d0dfe33c993e3d58cfebe2ab1205668411aae1e6cb78430f3b9d070a97e1274", size = 37922579, upload-time = "2025-12-21T11:23:11.477Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c2/c37989f2366d700f934b6c557dfd74e078352f7535b63d35920f1b0e49fd/cmake-4.2.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:52db8740e81d10c8d103899c87e0100e6aab969295ab99ce51eb11de4c36c9ce", size = 34564791, upload-time = "2025-12-21T11:23:15.177Z" }, - { url = "https://files.pythonhosted.org/packages/0d/f4/4a3fe5399be1f4f3876762d2b583c8b2ce6e1b419692f170a8b710ca1742/cmake-4.2.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:493abf42c003034c2bb1ad58a221542174a5c0fd2a76e9fdd91709ae6e53263c", size = 40440667, upload-time = "2025-12-21T11:23:19.14Z" }, - { url = "https://files.pythonhosted.org/packages/9d/a7/91b4504199f587c11b612f7bedcb8943d6f2da679d6e627e2de962a95011/cmake-4.2.1-py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:3d8d7632bb27cf1d0ac78098f2f7dfb7019927f35fb5a8c1508b17524af70000", size = 39610499, upload-time = "2025-12-21T11:23:23Z" }, - { url = "https://files.pythonhosted.org/packages/f5/4e/6aabf0172544c6b021ef0192f3f9bd8bb0f2877ad9ae223e653982aebd62/cmake-4.2.1-py3-none-musllinux_1_2_riscv64.whl", hash = "sha256:3e89d391096fdbdaab82e28b7e1fa964a873c0ba8d77c3542260c7d115aaac1f", size = 34796816, upload-time = "2025-12-21T11:23:26.88Z" }, - { url = "https://files.pythonhosted.org/packages/0a/f1/a2ae37cc5ff4338165a322e2dc70ddd0713a2989dee1ccf1af4ddf4917da/cmake-4.2.1-py3-none-musllinux_1_2_s390x.whl", hash = "sha256:e758ae635c75aaf0258e2c46fe95a3821f01011d5dbe29b7f045976b88ce3ca8", size = 36826277, upload-time = "2025-12-21T11:23:30.702Z" }, - { url = "https://files.pythonhosted.org/packages/fe/5b/ffa3551f85fd26dddc0e5d2e5dff0cda50fce57aaf2b237f2d5210d74203/cmake-4.2.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:fecc03edef6257b2bc8784f7880e84fe8a0b0fb54c952528c61ce14a4d693e16", size = 37806089, upload-time = "2025-12-21T11:23:34.464Z" }, - { url = "https://files.pythonhosted.org/packages/d2/99/c48c152e5e002a59861fdcfeed4e8ebe5d2da7a36db6e5da86b2f3a6c4d8/cmake-4.2.1-py3-none-win32.whl", hash = "sha256:72c860dae7c0315b05f59fd8e19253861c6e42f8d391a26aa6e2b4c9bd6014b8", size = 35352971, upload-time = "2025-12-21T11:23:37.908Z" }, - { url = "https://files.pythonhosted.org/packages/ff/cd/54fe2d1fc0674d2f05bfdc0cbd8e4602a6541a8bbe9e4bd5839ff8397a65/cmake-4.2.1-py3-none-win_amd64.whl", hash = "sha256:c186e7b826978f86bcbada91845e949e1f5ce5c670d6db49f7ecf5bac1b334e3", size = 38545649, upload-time = "2025-12-21T11:23:41.21Z" }, - { url = "https://files.pythonhosted.org/packages/67/3a/3704a5110716d1abd76cb3cc968f355f697f7ee38fbb9406924135a9590b/cmake-4.2.1-py3-none-win_arm64.whl", hash = "sha256:82224245741cf389d7c9072002ae2a81b63accb42732803db9b449c9423d546d", size = 38000806, upload-time = "2025-12-21T11:23:44.779Z" }, +version = "4.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/cb/922693f3aa75ca3fde2e02aef0b765a8a37db9868f70609b508096df139f/cmake-4.3.0.tar.gz", hash = "sha256:b25ef14e4ebc16219eeb199da2199fc526c011f9973e02e7c21c69bfd46ed752", size = 36979, upload-time = "2026-03-23T19:38:06.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ee/d221c00452a31143fbdf35f52f981f2df54d0cedb26a8b7bb1c77b902369/cmake-4.3.0-py3-none-macosx_10_10_universal2.whl", hash = "sha256:e3d83af988c4ad687201a531440dafb6bd145a696433a4e202ac6e2b3524059d", size = 52487876, upload-time = "2026-03-23T19:36:58.449Z" }, + { url = "https://files.pythonhosted.org/packages/76/6e/34ae7a2375444c6a8311aa7d624d4c6c5cbed62bcb1adfdd669dd8ba4474/cmake-4.3.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3a1733fdaefa397f8ff721dcc6bf2e86f0397391841a5cfd6f77162694046b05", size = 29544958, upload-time = "2026-03-23T19:37:02.598Z" }, + { url = "https://files.pythonhosted.org/packages/5c/9a/110c442b6ebab861e3eb829fe661d6eb71d86952d0a43c0e6828f46d0a8d/cmake-4.3.0-py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2a40ee9d4ee8c150b824ef5bb2452a383c398f6b7660bc2838aa21e627debd49", size = 30640241, upload-time = "2026-03-23T19:37:05.599Z" }, + { url = "https://files.pythonhosted.org/packages/ce/29/66fd845ba8660e14603115cc2e296eaf4d77f9655e805275b3d74fc4afee/cmake-4.3.0-py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:807d337f57f44651a495667bb7b713fd33ec1d4fd0caca570651a1168e0b573b", size = 30426591, upload-time = "2026-03-23T19:37:08.962Z" }, + { url = "https://files.pythonhosted.org/packages/59/f1/582950cfa1407d1703108b082e5d637b7fb1638573c1a385e5d84d6ec1cd/cmake-4.3.0-py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:02bdf1b58e0c720d9bde7e3189e02dc1bd9be9df5f88bc6f3d615cb905050528", size = 28349255, upload-time = "2026-03-23T19:37:13.162Z" }, + { url = "https://files.pythonhosted.org/packages/59/4f/35cf245e8366651ff89db11cbefc6cae7845dfa361c2798fcdad102a33ac/cmake-4.3.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7259210f2465e602273e7e0bc45683a5b953c1dd46afb17534a63a7add9c3960", size = 29478905, upload-time = "2026-03-23T19:37:16.572Z" }, + { url = "https://files.pythonhosted.org/packages/56/06/4a1d995afd40c8c29f350173f7f343680d9f0840f7293ba2b1782aa4094e/cmake-4.3.0-py3-none-manylinux_2_31_armv7l.whl", hash = "sha256:2544b0bf54b5642c3450b716280942eae1607e09a31d28233e8aff71c146e300", size = 26598645, upload-time = "2026-03-23T19:37:19.84Z" }, + { url = "https://files.pythonhosted.org/packages/81/7f/5dace799689b28bf59015bb05e29fc9313e7575b91d6895dbb13090d8bf3/cmake-4.3.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:712e87ad09ff6f078b0bef2edc26ac1ebadc9caf195fe5e9406b090fbdf2b4f5", size = 26735981, upload-time = "2026-03-23T19:37:23.291Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ba/e881481b65eab457ec4abc0f4cd944b3710e9fe4530a617c27b49b02e008/cmake-4.3.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:8d2aa3aba4300f87c55ce47914e75db9dc927a638742b17573f619a9811bb0bf", size = 38551604, upload-time = "2026-03-23T19:37:26.71Z" }, + { url = "https://files.pythonhosted.org/packages/ab/98/d20d0caee8d2c1c2518ccb9e48a644ab163877c4573783a900f127f4ad1e/cmake-4.3.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2033c0c335f46bd01b431f5e37a528be28abf2458ca4fd17f5e7eb9cbdf19549", size = 35185242, upload-time = "2026-03-23T19:37:30.175Z" }, + { url = "https://files.pythonhosted.org/packages/ad/e2/faddd5aef68be52d998cd3480d02de7d86cd6b1e14a982e70fe939ba66aa/cmake-4.3.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d73657ee1c99cb180540d275e159268fdf05d1b866d9f438182509b020bf590f", size = 41214996, upload-time = "2026-03-23T19:37:33.85Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3d/080f5c1a32e595ceb08d15fa30d3dacab8af893a192e664c7b4c1e895f5b/cmake-4.3.0-py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:d31ea6a98f216695b80dba93251a5425cf21c622ac5390aba352cb0bb5a410ca", size = 40388309, upload-time = "2026-03-23T19:37:38.146Z" }, + { url = "https://files.pythonhosted.org/packages/f4/25/53711394dce63fc32d980f260475ee26e1fce385252728e3e4843a7a7f19/cmake-4.3.0-py3-none-musllinux_1_2_riscv64.whl", hash = "sha256:1f5d09679e62ea7b558bdf0854508df2395fb12b49e54149160e1d2e55938974", size = 35451804, upload-time = "2026-03-23T19:37:42.511Z" }, + { url = "https://files.pythonhosted.org/packages/9a/0f/6ffd1255a620cb36fe7241a5ea8e6d77f1ce9d226e55185389253745a693/cmake-4.3.0-py3-none-musllinux_1_2_s390x.whl", hash = "sha256:830f9128ab7bbbe87e67937ac75ad15ff6a4c8a0d23fb349543a03cd640b5361", size = 37682897, upload-time = "2026-03-23T19:37:46.249Z" }, + { url = "https://files.pythonhosted.org/packages/3b/23/e582355747cc635f74d9e2acceb974680fb54d30540d473c6345974f218e/cmake-4.3.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e43be697e00999a6a14f5aefb20d93e072785783e58f060ff8f767ea14e2a521", size = 38511234, upload-time = "2026-03-23T19:37:50.198Z" }, + { url = "https://files.pythonhosted.org/packages/49/e1/29225cfd35ac8e4018cca074b25b4ef1121128cac18af1af3d4e82719e6c/cmake-4.3.0-py3-none-win32.whl", hash = "sha256:5fd4bfbe40bc435202e88ff7d91c63293c5780e1f0d772cfc752880feb807e04", size = 37775210, upload-time = "2026-03-23T19:37:54.607Z" }, + { url = "https://files.pythonhosted.org/packages/41/01/2780a5e4037eaee642f36fd9d4dac22562123f1a77c9c9a9ca3f6ec029d3/cmake-4.3.0-py3-none-win_amd64.whl", hash = "sha256:92e807d39ffa54b4d8ab947a2444c42b00a3ccb3e00bd05b1ed0f728ae0c938c", size = 41216473, upload-time = "2026-03-23T19:37:59Z" }, + { url = "https://files.pythonhosted.org/packages/3d/6f/93cd7ee5aa0ef5beb3532f4f6dea364bfc604ef6cb1a30ebf0211c95dc71/cmake-4.3.0-py3-none-win_arm64.whl", hash = "sha256:9bdbdf9220226200f55a08e99faa0442f9ff2c95a93a2bb5f35a9d5196ddf87a", size = 39557495, upload-time = "2026-03-23T19:38:03.534Z" }, ] [[package]] @@ -190,7 +206,7 @@ wheels = [ [[package]] name = "component-model" -version = "0.3.2" +version = "0.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flexparser" }, @@ -198,11 +214,11 @@ dependencies = [ { name = "numpy" }, { name = "pint" }, { name = "pythonfmu" }, - { name = "scipy-stubs" }, + { name = "scipy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d3/2d/b68448396eb59ed44cee0ff1e283f14a983985f669bfd1f7260573ae8dcb/component_model-0.3.2.tar.gz", hash = "sha256:2bba64f206ebd915312801c89bdbd7cbf7a26bff8f1ad56ed2dd2e73b5a7b328", size = 1234946, upload-time = "2026-02-02T08:53:35.737Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/38/eb03cef10250f659574ae628770f8c15d02e3923331d6988c69c41849d2b/component_model-0.4.0.tar.gz", hash = "sha256:f60369cd5bedf348bf69593abbfb50d1f77a8cbd32b99d8b91c009b66c4e7ab3", size = 242402, upload-time = "2026-03-12T10:09:53.253Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/09/55/68d8d16e36042aae6cad6fdb1194311d8715cb875248faeb1ebda2e2f35c/component_model-0.3.2-py3-none-any.whl", hash = "sha256:f6c9579c7e087a272cd58522da68cafba41ccda25e43440843706e879d8bc55d", size = 1094304, upload-time = "2026-02-02T08:53:34.049Z" }, + { url = "https://files.pythonhosted.org/packages/7f/36/035a6900fb5a1c018257e47cdeafae44e6a3c3ce9c52ba9cf5c9ee209ae5/component_model-0.4.0-py3-none-any.whl", hash = "sha256:25cfb3c755083646a9cb85df11742e3d82986136b9941ca511bd1f782fb672f7", size = 80155, upload-time = "2026-03-12T10:09:51.686Z" }, ] [[package]] @@ -289,89 +305,101 @@ wheels = [ [[package]] name = "coverage" -version = "7.13.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/f9/e92df5e07f3fc8d4c7f9a0f146ef75446bf870351cd37b788cf5897f8079/coverage-7.13.1.tar.gz", hash = "sha256:b7593fe7eb5feaa3fbb461ac79aac9f9fc0387a5ca8080b0c6fe2ca27b091afd", size = 825862, upload-time = "2025-12-28T15:42:56.969Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/9b/77baf488516e9ced25fc215a6f75d803493fc3f6a1a1227ac35697910c2a/coverage-7.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a55d509a1dc5a5b708b5dad3b5334e07a16ad4c2185e27b40e4dba796ab7f88", size = 218755, upload-time = "2025-12-28T15:40:30.812Z" }, - { url = "https://files.pythonhosted.org/packages/d7/cd/7ab01154e6eb79ee2fab76bf4d89e94c6648116557307ee4ebbb85e5c1bf/coverage-7.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d010d080c4888371033baab27e47c9df7d6fb28d0b7b7adf85a4a49be9298b3", size = 219257, upload-time = "2025-12-28T15:40:32.333Z" }, - { url = "https://files.pythonhosted.org/packages/01/d5/b11ef7863ffbbdb509da0023fad1e9eda1c0eaea61a6d2ea5b17d4ac706e/coverage-7.13.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d938b4a840fb1523b9dfbbb454f652967f18e197569c32266d4d13f37244c3d9", size = 249657, upload-time = "2025-12-28T15:40:34.1Z" }, - { url = "https://files.pythonhosted.org/packages/f7/7c/347280982982383621d29b8c544cf497ae07ac41e44b1ca4903024131f55/coverage-7.13.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bf100a3288f9bb7f919b87eb84f87101e197535b9bd0e2c2b5b3179633324fee", size = 251581, upload-time = "2025-12-28T15:40:36.131Z" }, - { url = "https://files.pythonhosted.org/packages/82/f6/ebcfed11036ade4c0d75fa4453a6282bdd225bc073862766eec184a4c643/coverage-7.13.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef6688db9bf91ba111ae734ba6ef1a063304a881749726e0d3575f5c10a9facf", size = 253691, upload-time = "2025-12-28T15:40:37.626Z" }, - { url = "https://files.pythonhosted.org/packages/02/92/af8f5582787f5d1a8b130b2dcba785fa5e9a7a8e121a0bb2220a6fdbdb8a/coverage-7.13.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0b609fc9cdbd1f02e51f67f51e5aee60a841ef58a68d00d5ee2c0faf357481a3", size = 249799, upload-time = "2025-12-28T15:40:39.47Z" }, - { url = "https://files.pythonhosted.org/packages/24/aa/0e39a2a3b16eebf7f193863323edbff38b6daba711abaaf807d4290cf61a/coverage-7.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c43257717611ff5e9a1d79dce8e47566235ebda63328718d9b65dd640bc832ef", size = 251389, upload-time = "2025-12-28T15:40:40.954Z" }, - { url = "https://files.pythonhosted.org/packages/73/46/7f0c13111154dc5b978900c0ccee2e2ca239b910890e674a77f1363d483e/coverage-7.13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e09fbecc007f7b6afdfb3b07ce5bd9f8494b6856dd4f577d26c66c391b829851", size = 249450, upload-time = "2025-12-28T15:40:42.489Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ca/e80da6769e8b669ec3695598c58eef7ad98b0e26e66333996aee6316db23/coverage-7.13.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:a03a4f3a19a189919c7055098790285cc5c5b0b3976f8d227aea39dbf9f8bfdb", size = 249170, upload-time = "2025-12-28T15:40:44.279Z" }, - { url = "https://files.pythonhosted.org/packages/af/18/9e29baabdec1a8644157f572541079b4658199cfd372a578f84228e860de/coverage-7.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3820778ea1387c2b6a818caec01c63adc5b3750211af6447e8dcfb9b6f08dbba", size = 250081, upload-time = "2025-12-28T15:40:45.748Z" }, - { url = "https://files.pythonhosted.org/packages/00/f8/c3021625a71c3b2f516464d322e41636aea381018319050a8114105872ee/coverage-7.13.1-cp311-cp311-win32.whl", hash = "sha256:ff10896fa55167371960c5908150b434b71c876dfab97b69478f22c8b445ea19", size = 221281, upload-time = "2025-12-28T15:40:47.232Z" }, - { url = "https://files.pythonhosted.org/packages/27/56/c216625f453df6e0559ed666d246fcbaaa93f3aa99eaa5080cea1229aa3d/coverage-7.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:a998cc0aeeea4c6d5622a3754da5a493055d2d95186bad877b0a34ea6e6dbe0a", size = 222215, upload-time = "2025-12-28T15:40:49.19Z" }, - { url = "https://files.pythonhosted.org/packages/5c/9a/be342e76f6e531cae6406dc46af0d350586f24d9b67fdfa6daee02df71af/coverage-7.13.1-cp311-cp311-win_arm64.whl", hash = "sha256:fea07c1a39a22614acb762e3fbbb4011f65eedafcb2948feeef641ac78b4ee5c", size = 220886, upload-time = "2025-12-28T15:40:51.067Z" }, - { url = "https://files.pythonhosted.org/packages/ce/8a/87af46cccdfa78f53db747b09f5f9a21d5fc38d796834adac09b30a8ce74/coverage-7.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6f34591000f06e62085b1865c9bc5f7858df748834662a51edadfd2c3bfe0dd3", size = 218927, upload-time = "2025-12-28T15:40:52.814Z" }, - { url = "https://files.pythonhosted.org/packages/82/a8/6e22fdc67242a4a5a153f9438d05944553121c8f4ba70cb072af4c41362e/coverage-7.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b67e47c5595b9224599016e333f5ec25392597a89d5744658f837d204e16c63e", size = 219288, upload-time = "2025-12-28T15:40:54.262Z" }, - { url = "https://files.pythonhosted.org/packages/d0/0a/853a76e03b0f7c4375e2ca025df45c918beb367f3e20a0a8e91967f6e96c/coverage-7.13.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e7b8bd70c48ffb28461ebe092c2345536fb18bbbf19d287c8913699735f505c", size = 250786, upload-time = "2025-12-28T15:40:56.059Z" }, - { url = "https://files.pythonhosted.org/packages/ea/b4/694159c15c52b9f7ec7adf49d50e5f8ee71d3e9ef38adb4445d13dd56c20/coverage-7.13.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c223d078112e90dc0e5c4e35b98b9584164bea9fbbd221c0b21c5241f6d51b62", size = 253543, upload-time = "2025-12-28T15:40:57.585Z" }, - { url = "https://files.pythonhosted.org/packages/96/b2/7f1f0437a5c855f87e17cf5d0dc35920b6440ff2b58b1ba9788c059c26c8/coverage-7.13.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:794f7c05af0763b1bbd1b9e6eff0e52ad068be3b12cd96c87de037b01390c968", size = 254635, upload-time = "2025-12-28T15:40:59.443Z" }, - { url = "https://files.pythonhosted.org/packages/e9/d1/73c3fdb8d7d3bddd9473c9c6a2e0682f09fc3dfbcb9c3f36412a7368bcab/coverage-7.13.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0642eae483cc8c2902e4af7298bf886d605e80f26382124cddc3967c2a3df09e", size = 251202, upload-time = "2025-12-28T15:41:01.328Z" }, - { url = "https://files.pythonhosted.org/packages/66/3c/f0edf75dcc152f145d5598329e864bbbe04ab78660fe3e8e395f9fff010f/coverage-7.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f5e772ed5fef25b3de9f2008fe67b92d46831bd2bc5bdc5dd6bfd06b83b316f", size = 252566, upload-time = "2025-12-28T15:41:03.319Z" }, - { url = "https://files.pythonhosted.org/packages/17/b3/e64206d3c5f7dcbceafd14941345a754d3dbc78a823a6ed526e23b9cdaab/coverage-7.13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:45980ea19277dc0a579e432aef6a504fe098ef3a9032ead15e446eb0f1191aee", size = 250711, upload-time = "2025-12-28T15:41:06.411Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ad/28a3eb970a8ef5b479ee7f0c484a19c34e277479a5b70269dc652b730733/coverage-7.13.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e4f18eca6028ffa62adbd185a8f1e1dd242f2e68164dba5c2b74a5204850b4cf", size = 250278, upload-time = "2025-12-28T15:41:08.285Z" }, - { url = "https://files.pythonhosted.org/packages/54/e3/c8f0f1a93133e3e1291ca76cbb63565bd4b5c5df63b141f539d747fff348/coverage-7.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8dca5590fec7a89ed6826fce625595279e586ead52e9e958d3237821fbc750c", size = 252154, upload-time = "2025-12-28T15:41:09.969Z" }, - { url = "https://files.pythonhosted.org/packages/d0/bf/9939c5d6859c380e405b19e736321f1c7d402728792f4c752ad1adcce005/coverage-7.13.1-cp312-cp312-win32.whl", hash = "sha256:ff86d4e85188bba72cfb876df3e11fa243439882c55957184af44a35bd5880b7", size = 221487, upload-time = "2025-12-28T15:41:11.468Z" }, - { url = "https://files.pythonhosted.org/packages/fa/dc/7282856a407c621c2aad74021680a01b23010bb8ebf427cf5eacda2e876f/coverage-7.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:16cc1da46c04fb0fb128b4dc430b78fa2aba8a6c0c9f8eb391fd5103409a6ac6", size = 222299, upload-time = "2025-12-28T15:41:13.386Z" }, - { url = "https://files.pythonhosted.org/packages/10/79/176a11203412c350b3e9578620013af35bcdb79b651eb976f4a4b32044fa/coverage-7.13.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d9bc218650022a768f3775dd7fdac1886437325d8d295d923ebcfef4892ad5c", size = 220941, upload-time = "2025-12-28T15:41:14.975Z" }, - { url = "https://files.pythonhosted.org/packages/a3/a4/e98e689347a1ff1a7f67932ab535cef82eb5e78f32a9e4132e114bbb3a0a/coverage-7.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cb237bfd0ef4d5eb6a19e29f9e528ac67ac3be932ea6b44fb6cc09b9f3ecff78", size = 218951, upload-time = "2025-12-28T15:41:16.653Z" }, - { url = "https://files.pythonhosted.org/packages/32/33/7cbfe2bdc6e2f03d6b240d23dc45fdaf3fd270aaf2d640be77b7f16989ab/coverage-7.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1dcb645d7e34dcbcc96cd7c132b1fc55c39263ca62eb961c064eb3928997363b", size = 219325, upload-time = "2025-12-28T15:41:18.609Z" }, - { url = "https://files.pythonhosted.org/packages/59/f6/efdabdb4929487baeb7cb2a9f7dac457d9356f6ad1b255be283d58b16316/coverage-7.13.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3d42df8201e00384736f0df9be2ced39324c3907607d17d50d50116c989d84cd", size = 250309, upload-time = "2025-12-28T15:41:20.629Z" }, - { url = "https://files.pythonhosted.org/packages/12/da/91a52516e9d5aea87d32d1523f9cdcf7a35a3b298e6be05d6509ba3cfab2/coverage-7.13.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa3edde1aa8807de1d05934982416cb3ec46d1d4d91e280bcce7cca01c507992", size = 252907, upload-time = "2025-12-28T15:41:22.257Z" }, - { url = "https://files.pythonhosted.org/packages/75/38/f1ea837e3dc1231e086db1638947e00d264e7e8c41aa8ecacf6e1e0c05f4/coverage-7.13.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9edd0e01a343766add6817bc448408858ba6b489039eaaa2018474e4001651a4", size = 254148, upload-time = "2025-12-28T15:41:23.87Z" }, - { url = "https://files.pythonhosted.org/packages/7f/43/f4f16b881aaa34954ba446318dea6b9ed5405dd725dd8daac2358eda869a/coverage-7.13.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:985b7836931d033570b94c94713c6dba5f9d3ff26045f72c3e5dbc5fe3361e5a", size = 250515, upload-time = "2025-12-28T15:41:25.437Z" }, - { url = "https://files.pythonhosted.org/packages/84/34/8cba7f00078bd468ea914134e0144263194ce849ec3baad187ffb6203d1c/coverage-7.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ffed1e4980889765c84a5d1a566159e363b71d6b6fbaf0bebc9d3c30bc016766", size = 252292, upload-time = "2025-12-28T15:41:28.459Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a4/cffac66c7652d84ee4ac52d3ccb94c015687d3b513f9db04bfcac2ac800d/coverage-7.13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8842af7f175078456b8b17f1b73a0d16a65dcbdc653ecefeb00a56b3c8c298c4", size = 250242, upload-time = "2025-12-28T15:41:30.02Z" }, - { url = "https://files.pythonhosted.org/packages/f4/78/9a64d462263dde416f3c0067efade7b52b52796f489b1037a95b0dc389c9/coverage-7.13.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ccd7a6fca48ca9c131d9b0a2972a581e28b13416fc313fb98b6d24a03ce9a398", size = 250068, upload-time = "2025-12-28T15:41:32.007Z" }, - { url = "https://files.pythonhosted.org/packages/69/c8/a8994f5fece06db7c4a97c8fc1973684e178599b42e66280dded0524ef00/coverage-7.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0403f647055de2609be776965108447deb8e384fe4a553c119e3ff6bfbab4784", size = 251846, upload-time = "2025-12-28T15:41:33.946Z" }, - { url = "https://files.pythonhosted.org/packages/cc/f7/91fa73c4b80305c86598a2d4e54ba22df6bf7d0d97500944af7ef155d9f7/coverage-7.13.1-cp313-cp313-win32.whl", hash = "sha256:549d195116a1ba1e1ae2f5ca143f9777800f6636eab917d4f02b5310d6d73461", size = 221512, upload-time = "2025-12-28T15:41:35.519Z" }, - { url = "https://files.pythonhosted.org/packages/45/0b/0768b4231d5a044da8f75e097a8714ae1041246bb765d6b5563bab456735/coverage-7.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:5899d28b5276f536fcf840b18b61a9fce23cc3aec1d114c44c07fe94ebeaa500", size = 222321, upload-time = "2025-12-28T15:41:37.371Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b8/bdcb7253b7e85157282450262008f1366aa04663f3e3e4c30436f596c3e2/coverage-7.13.1-cp313-cp313-win_arm64.whl", hash = "sha256:868a2fae76dfb06e87291bcbd4dcbcc778a8500510b618d50496e520bd94d9b9", size = 220949, upload-time = "2025-12-28T15:41:39.553Z" }, - { url = "https://files.pythonhosted.org/packages/70/52/f2be52cc445ff75ea8397948c96c1b4ee14f7f9086ea62fc929c5ae7b717/coverage-7.13.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:67170979de0dacac3f3097d02b0ad188d8edcea44ccc44aaa0550af49150c7dc", size = 219643, upload-time = "2025-12-28T15:41:41.567Z" }, - { url = "https://files.pythonhosted.org/packages/47/79/c85e378eaa239e2edec0c5523f71542c7793fe3340954eafb0bc3904d32d/coverage-7.13.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f80e2bb21bfab56ed7405c2d79d34b5dc0bc96c2c1d2a067b643a09fb756c43a", size = 219997, upload-time = "2025-12-28T15:41:43.418Z" }, - { url = "https://files.pythonhosted.org/packages/fe/9b/b1ade8bfb653c0bbce2d6d6e90cc6c254cbb99b7248531cc76253cb4da6d/coverage-7.13.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f83351e0f7dcdb14d7326c3d8d8c4e915fa685cbfdc6281f9470d97a04e9dfe4", size = 261296, upload-time = "2025-12-28T15:41:45.207Z" }, - { url = "https://files.pythonhosted.org/packages/1f/af/ebf91e3e1a2473d523e87e87fd8581e0aa08741b96265730e2d79ce78d8d/coverage-7.13.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb3f6562e89bad0110afbe64e485aac2462efdce6232cdec7862a095dc3412f6", size = 263363, upload-time = "2025-12-28T15:41:47.163Z" }, - { url = "https://files.pythonhosted.org/packages/c4/8b/fb2423526d446596624ac7fde12ea4262e66f86f5120114c3cfd0bb2befa/coverage-7.13.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77545b5dcda13b70f872c3b5974ac64c21d05e65b1590b441c8560115dc3a0d1", size = 265783, upload-time = "2025-12-28T15:41:49.03Z" }, - { url = "https://files.pythonhosted.org/packages/9b/26/ef2adb1e22674913b89f0fe7490ecadcef4a71fa96f5ced90c60ec358789/coverage-7.13.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4d240d260a1aed814790bbe1f10a5ff31ce6c21bc78f0da4a1e8268d6c80dbd", size = 260508, upload-time = "2025-12-28T15:41:51.035Z" }, - { url = "https://files.pythonhosted.org/packages/ce/7d/f0f59b3404caf662e7b5346247883887687c074ce67ba453ea08c612b1d5/coverage-7.13.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d2287ac9360dec3837bfdad969963a5d073a09a85d898bd86bea82aa8876ef3c", size = 263357, upload-time = "2025-12-28T15:41:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/1a/b1/29896492b0b1a047604d35d6fa804f12818fa30cdad660763a5f3159e158/coverage-7.13.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0d2c11f3ea4db66b5cbded23b20185c35066892c67d80ec4be4bab257b9ad1e0", size = 260978, upload-time = "2025-12-28T15:41:54.589Z" }, - { url = "https://files.pythonhosted.org/packages/48/f2/971de1238a62e6f0a4128d37adadc8bb882ee96afbe03ff1570291754629/coverage-7.13.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:3fc6a169517ca0d7ca6846c3c5392ef2b9e38896f61d615cb75b9e7134d4ee1e", size = 259877, upload-time = "2025-12-28T15:41:56.263Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fc/0474efcbb590ff8628830e9aaec5f1831594874360e3251f1fdec31d07a3/coverage-7.13.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d10a2ed46386e850bb3de503a54f9fe8192e5917fcbb143bfef653a9355e9a53", size = 262069, upload-time = "2025-12-28T15:41:58.093Z" }, - { url = "https://files.pythonhosted.org/packages/88/4f/3c159b7953db37a7b44c0eab8a95c37d1aa4257c47b4602c04022d5cb975/coverage-7.13.1-cp313-cp313t-win32.whl", hash = "sha256:75a6f4aa904301dab8022397a22c0039edc1f51e90b83dbd4464b8a38dc87842", size = 222184, upload-time = "2025-12-28T15:41:59.763Z" }, - { url = "https://files.pythonhosted.org/packages/58/a5/6b57d28f81417f9335774f20679d9d13b9a8fb90cd6160957aa3b54a2379/coverage-7.13.1-cp313-cp313t-win_amd64.whl", hash = "sha256:309ef5706e95e62578cda256b97f5e097916a2c26247c287bbe74794e7150df2", size = 223250, upload-time = "2025-12-28T15:42:01.52Z" }, - { url = "https://files.pythonhosted.org/packages/81/7c/160796f3b035acfbb58be80e02e484548595aa67e16a6345e7910ace0a38/coverage-7.13.1-cp313-cp313t-win_arm64.whl", hash = "sha256:92f980729e79b5d16d221038dbf2e8f9a9136afa072f9d5d6ed4cb984b126a09", size = 221521, upload-time = "2025-12-28T15:42:03.275Z" }, - { url = "https://files.pythonhosted.org/packages/aa/8e/ba0e597560c6563fc0adb902fda6526df5d4aa73bb10adf0574d03bd2206/coverage-7.13.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:97ab3647280d458a1f9adb85244e81587505a43c0c7cff851f5116cd2814b894", size = 218996, upload-time = "2025-12-28T15:42:04.978Z" }, - { url = "https://files.pythonhosted.org/packages/6b/8e/764c6e116f4221dc7aa26c4061181ff92edb9c799adae6433d18eeba7a14/coverage-7.13.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8f572d989142e0908e6acf57ad1b9b86989ff057c006d13b76c146ec6a20216a", size = 219326, upload-time = "2025-12-28T15:42:06.691Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a6/6130dc6d8da28cdcbb0f2bf8865aeca9b157622f7c0031e48c6cf9a0e591/coverage-7.13.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d72140ccf8a147e94274024ff6fd8fb7811354cf7ef88b1f0a988ebaa5bc774f", size = 250374, upload-time = "2025-12-28T15:42:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/82/2b/783ded568f7cd6b677762f780ad338bf4b4750205860c17c25f7c708995e/coverage-7.13.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3c9f051b028810f5a87c88e5d6e9af3c0ff32ef62763bf15d29f740453ca909", size = 252882, upload-time = "2025-12-28T15:42:10.515Z" }, - { url = "https://files.pythonhosted.org/packages/cd/b2/9808766d082e6a4d59eb0cc881a57fc1600eb2c5882813eefff8254f71b5/coverage-7.13.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f398ba4df52d30b1763f62eed9de5620dcde96e6f491f4c62686736b155aa6e4", size = 254218, upload-time = "2025-12-28T15:42:12.208Z" }, - { url = "https://files.pythonhosted.org/packages/44/ea/52a985bb447c871cb4d2e376e401116520991b597c85afdde1ea9ef54f2c/coverage-7.13.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:132718176cc723026d201e347f800cd1a9e4b62ccd3f82476950834dad501c75", size = 250391, upload-time = "2025-12-28T15:42:14.21Z" }, - { url = "https://files.pythonhosted.org/packages/7f/1d/125b36cc12310718873cfc8209ecfbc1008f14f4f5fa0662aa608e579353/coverage-7.13.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e549d642426e3579b3f4b92d0431543b012dcb6e825c91619d4e93b7363c3f9", size = 252239, upload-time = "2025-12-28T15:42:16.292Z" }, - { url = "https://files.pythonhosted.org/packages/6a/16/10c1c164950cade470107f9f14bbac8485f8fb8515f515fca53d337e4a7f/coverage-7.13.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:90480b2134999301eea795b3a9dbf606c6fbab1b489150c501da84a959442465", size = 250196, upload-time = "2025-12-28T15:42:18.54Z" }, - { url = "https://files.pythonhosted.org/packages/2a/c6/cd860fac08780c6fd659732f6ced1b40b79c35977c1356344e44d72ba6c4/coverage-7.13.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e825dbb7f84dfa24663dd75835e7257f8882629fc11f03ecf77d84a75134b864", size = 250008, upload-time = "2025-12-28T15:42:20.365Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/a8c58d3d38f82a5711e1e0a67268362af48e1a03df27c03072ac30feefcf/coverage-7.13.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:623dcc6d7a7ba450bbdbeedbaa0c42b329bdae16491af2282f12a7e809be7eb9", size = 251671, upload-time = "2025-12-28T15:42:22.114Z" }, - { url = "https://files.pythonhosted.org/packages/f0/bc/fd4c1da651d037a1e3d53e8cb3f8182f4b53271ffa9a95a2e211bacc0349/coverage-7.13.1-cp314-cp314-win32.whl", hash = "sha256:6e73ebb44dca5f708dc871fe0b90cf4cff1a13f9956f747cc87b535a840386f5", size = 221777, upload-time = "2025-12-28T15:42:23.919Z" }, - { url = "https://files.pythonhosted.org/packages/4b/50/71acabdc8948464c17e90b5ffd92358579bd0910732c2a1c9537d7536aa6/coverage-7.13.1-cp314-cp314-win_amd64.whl", hash = "sha256:be753b225d159feb397bd0bf91ae86f689bad0da09d3b301478cd39b878ab31a", size = 222592, upload-time = "2025-12-28T15:42:25.619Z" }, - { url = "https://files.pythonhosted.org/packages/f7/c8/a6fb943081bb0cc926499c7907731a6dc9efc2cbdc76d738c0ab752f1a32/coverage-7.13.1-cp314-cp314-win_arm64.whl", hash = "sha256:228b90f613b25ba0019361e4ab81520b343b622fc657daf7e501c4ed6a2366c0", size = 221169, upload-time = "2025-12-28T15:42:27.629Z" }, - { url = "https://files.pythonhosted.org/packages/16/61/d5b7a0a0e0e40d62e59bc8c7aa1afbd86280d82728ba97f0673b746b78e2/coverage-7.13.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:60cfb538fe9ef86e5b2ab0ca8fc8d62524777f6c611dcaf76dc16fbe9b8e698a", size = 219730, upload-time = "2025-12-28T15:42:29.306Z" }, - { url = "https://files.pythonhosted.org/packages/a3/2c/8881326445fd071bb49514d1ce97d18a46a980712b51fee84f9ab42845b4/coverage-7.13.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:57dfc8048c72ba48a8c45e188d811e5efd7e49b387effc8fb17e97936dde5bf6", size = 220001, upload-time = "2025-12-28T15:42:31.319Z" }, - { url = "https://files.pythonhosted.org/packages/b5/d7/50de63af51dfa3a7f91cc37ad8fcc1e244b734232fbc8b9ab0f3c834a5cd/coverage-7.13.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3f2f725aa3e909b3c5fdb8192490bdd8e1495e85906af74fe6e34a2a77ba0673", size = 261370, upload-time = "2025-12-28T15:42:32.992Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2c/d31722f0ec918fd7453b2758312729f645978d212b410cd0f7c2aed88a94/coverage-7.13.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ee68b21909686eeb21dfcba2c3b81fee70dcf38b140dcd5aa70680995fa3aa5", size = 263485, upload-time = "2025-12-28T15:42:34.759Z" }, - { url = "https://files.pythonhosted.org/packages/fa/7a/2c114fa5c5fc08ba0777e4aec4c97e0b4a1afcb69c75f1f54cff78b073ab/coverage-7.13.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:724b1b270cb13ea2e6503476e34541a0b1f62280bc997eab443f87790202033d", size = 265890, upload-time = "2025-12-28T15:42:36.517Z" }, - { url = "https://files.pythonhosted.org/packages/65/d9/f0794aa1c74ceabc780fe17f6c338456bbc4e96bd950f2e969f48ac6fb20/coverage-7.13.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:916abf1ac5cf7eb16bc540a5bf75c71c43a676f5c52fcb9fe75a2bd75fb944e8", size = 260445, upload-time = "2025-12-28T15:42:38.646Z" }, - { url = "https://files.pythonhosted.org/packages/49/23/184b22a00d9bb97488863ced9454068c79e413cb23f472da6cbddc6cfc52/coverage-7.13.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:776483fd35b58d8afe3acbd9988d5de592ab6da2d2a865edfdbc9fdb43e7c486", size = 263357, upload-time = "2025-12-28T15:42:40.788Z" }, - { url = "https://files.pythonhosted.org/packages/7d/bd/58af54c0c9199ea4190284f389005779d7daf7bf3ce40dcd2d2b2f96da69/coverage-7.13.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b6f3b96617e9852703f5b633ea01315ca45c77e879584f283c44127f0f1ec564", size = 260959, upload-time = "2025-12-28T15:42:42.808Z" }, - { url = "https://files.pythonhosted.org/packages/4b/2a/6839294e8f78a4891bf1df79d69c536880ba2f970d0ff09e7513d6e352e9/coverage-7.13.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd63e7b74661fed317212fab774e2a648bc4bb09b35f25474f8e3325d2945cd7", size = 259792, upload-time = "2025-12-28T15:42:44.818Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c3/528674d4623283310ad676c5af7414b9850ab6d55c2300e8aa4b945ec554/coverage-7.13.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:933082f161bbb3e9f90d00990dc956120f608cdbcaeea15c4d897f56ef4fe416", size = 262123, upload-time = "2025-12-28T15:42:47.108Z" }, - { url = "https://files.pythonhosted.org/packages/06/c5/8c0515692fb4c73ac379d8dc09b18eaf0214ecb76ea6e62467ba7a1556ff/coverage-7.13.1-cp314-cp314t-win32.whl", hash = "sha256:18be793c4c87de2965e1c0f060f03d9e5aff66cfeae8e1dbe6e5b88056ec153f", size = 222562, upload-time = "2025-12-28T15:42:49.144Z" }, - { url = "https://files.pythonhosted.org/packages/05/0e/c0a0c4678cb30dac735811db529b321d7e1c9120b79bd728d4f4d6b010e9/coverage-7.13.1-cp314-cp314t-win_amd64.whl", hash = "sha256:0e42e0ec0cd3e0d851cb3c91f770c9301f48647cb2877cb78f74bdaa07639a79", size = 223670, upload-time = "2025-12-28T15:42:51.218Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5f/b177aa0011f354abf03a8f30a85032686d290fdeed4222b27d36b4372a50/coverage-7.13.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eaecf47ef10c72ece9a2a92118257da87e460e113b83cc0d2905cbbe931792b4", size = 221707, upload-time = "2025-12-28T15:42:53.034Z" }, - { url = "https://files.pythonhosted.org/packages/cc/48/d9f421cb8da5afaa1a64570d9989e00fb7955e6acddc5a12979f7666ef60/coverage-7.13.1-py3-none-any.whl", hash = "sha256:2016745cb3ba554469d02819d78958b571792bb68e31302610e898f80dd3a573", size = 210722, upload-time = "2025-12-28T15:42:54.901Z" }, +version = "7.13.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179", size = 915967, upload-time = "2026-03-17T10:33:18.341Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/37/d24c8f8220ff07b839b2c043ea4903a33b0f455abe673ae3c03bbdb7f212/coverage-7.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d", size = 219381, upload-time = "2026-03-17T10:30:14.68Z" }, + { url = "https://files.pythonhosted.org/packages/35/8b/cd129b0ca4afe886a6ce9d183c44d8301acbd4ef248622e7c49a23145605/coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587", size = 219880, upload-time = "2026-03-17T10:30:16.231Z" }, + { url = "https://files.pythonhosted.org/packages/55/2f/e0e5b237bffdb5d6c530ce87cc1d413a5b7d7dfd60fb067ad6d254c35c76/coverage-7.13.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642", size = 250303, upload-time = "2026-03-17T10:30:17.748Z" }, + { url = "https://files.pythonhosted.org/packages/92/be/b1afb692be85b947f3401375851484496134c5554e67e822c35f28bf2fbc/coverage-7.13.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b", size = 252218, upload-time = "2026-03-17T10:30:19.804Z" }, + { url = "https://files.pythonhosted.org/packages/da/69/2f47bb6fa1b8d1e3e5d0c4be8ccb4313c63d742476a619418f85740d597b/coverage-7.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686", size = 254326, upload-time = "2026-03-17T10:30:21.321Z" }, + { url = "https://files.pythonhosted.org/packages/d5/d0/79db81da58965bd29dabc8f4ad2a2af70611a57cba9d1ec006f072f30a54/coverage-7.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743", size = 256267, upload-time = "2026-03-17T10:30:23.094Z" }, + { url = "https://files.pythonhosted.org/packages/e5/32/d0d7cc8168f91ddab44c0ce4806b969df5f5fdfdbb568eaca2dbc2a04936/coverage-7.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75", size = 250430, upload-time = "2026-03-17T10:30:25.311Z" }, + { url = "https://files.pythonhosted.org/packages/4d/06/a055311d891ddbe231cd69fdd20ea4be6e3603ffebddf8704b8ca8e10a3c/coverage-7.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209", size = 252017, upload-time = "2026-03-17T10:30:27.284Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f6/d0fd2d21e29a657b5f77a2fe7082e1568158340dceb941954f776dce1b7b/coverage-7.13.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a", size = 250080, upload-time = "2026-03-17T10:30:29.481Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ab/0d7fb2efc2e9a5eb7ddcc6e722f834a69b454b7e6e5888c3a8567ecffb31/coverage-7.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e", size = 253843, upload-time = "2026-03-17T10:30:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/ba/6f/7467b917bbf5408610178f62a49c0ed4377bb16c1657f689cc61470da8ce/coverage-7.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd", size = 249802, upload-time = "2026-03-17T10:30:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/75/2c/1172fb689df92135f5bfbbd69fc83017a76d24ea2e2f3a1154007e2fb9f8/coverage-7.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8", size = 250707, upload-time = "2026-03-17T10:30:35.2Z" }, + { url = "https://files.pythonhosted.org/packages/67/21/9ac389377380a07884e3b48ba7a620fcd9dbfaf1d40565facdc6b36ec9ef/coverage-7.13.5-cp311-cp311-win32.whl", hash = "sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf", size = 221880, upload-time = "2026-03-17T10:30:36.775Z" }, + { url = "https://files.pythonhosted.org/packages/af/7f/4cd8a92531253f9d7c1bbecd9fa1b472907fb54446ca768c59b531248dc5/coverage-7.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9", size = 222816, upload-time = "2026-03-17T10:30:38.891Z" }, + { url = "https://files.pythonhosted.org/packages/12/a6/1d3f6155fb0010ca68eba7fe48ca6c9da7385058b77a95848710ecf189b1/coverage-7.13.5-cp311-cp311-win_arm64.whl", hash = "sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028", size = 221483, upload-time = "2026-03-17T10:30:40.463Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c3/a396306ba7db865bf96fc1fb3b7fd29bcbf3d829df642e77b13555163cd6/coverage-7.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01", size = 219554, upload-time = "2026-03-17T10:30:42.208Z" }, + { url = "https://files.pythonhosted.org/packages/a6/16/a68a19e5384e93f811dccc51034b1fd0b865841c390e3c931dcc4699e035/coverage-7.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422", size = 219908, upload-time = "2026-03-17T10:30:43.906Z" }, + { url = "https://files.pythonhosted.org/packages/29/72/20b917c6793af3a5ceb7fb9c50033f3ec7865f2911a1416b34a7cfa0813b/coverage-7.13.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f", size = 251419, upload-time = "2026-03-17T10:30:45.545Z" }, + { url = "https://files.pythonhosted.org/packages/8c/49/cd14b789536ac6a4778c453c6a2338bc0a2fb60c5a5a41b4008328b9acc1/coverage-7.13.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5", size = 254159, upload-time = "2026-03-17T10:30:47.204Z" }, + { url = "https://files.pythonhosted.org/packages/9d/00/7b0edcfe64e2ed4c0340dac14a52ad0f4c9bd0b8b5e531af7d55b703db7c/coverage-7.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376", size = 255270, upload-time = "2026-03-17T10:30:48.812Z" }, + { url = "https://files.pythonhosted.org/packages/93/89/7ffc4ba0f5d0a55c1e84ea7cee39c9fc06af7b170513d83fbf3bbefce280/coverage-7.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256", size = 257538, upload-time = "2026-03-17T10:30:50.77Z" }, + { url = "https://files.pythonhosted.org/packages/81/bd/73ddf85f93f7e6fa83e77ccecb6162d9415c79007b4bc124008a4995e4a7/coverage-7.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c", size = 251821, upload-time = "2026-03-17T10:30:52.5Z" }, + { url = "https://files.pythonhosted.org/packages/a0/81/278aff4e8dec4926a0bcb9486320752811f543a3ce5b602cc7a29978d073/coverage-7.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5", size = 253191, upload-time = "2026-03-17T10:30:54.543Z" }, + { url = "https://files.pythonhosted.org/packages/70/ee/fe1621488e2e0a58d7e94c4800f0d96f79671553488d401a612bebae324b/coverage-7.13.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09", size = 251337, upload-time = "2026-03-17T10:30:56.663Z" }, + { url = "https://files.pythonhosted.org/packages/37/a6/f79fb37aa104b562207cc23cb5711ab6793608e246cae1e93f26b2236ed9/coverage-7.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9", size = 255404, upload-time = "2026-03-17T10:30:58.427Z" }, + { url = "https://files.pythonhosted.org/packages/75/f0/ed15262a58ec81ce457ceb717b7f78752a1713556b19081b76e90896e8d4/coverage-7.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf", size = 250903, upload-time = "2026-03-17T10:31:00.093Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e9/9129958f20e7e9d4d56d51d42ccf708d15cac355ff4ac6e736e97a9393d2/coverage-7.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c", size = 252780, upload-time = "2026-03-17T10:31:01.916Z" }, + { url = "https://files.pythonhosted.org/packages/a4/d7/0ad9b15812d81272db94379fe4c6df8fd17781cc7671fdfa30c76ba5ff7b/coverage-7.13.5-cp312-cp312-win32.whl", hash = "sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf", size = 222093, upload-time = "2026-03-17T10:31:03.642Z" }, + { url = "https://files.pythonhosted.org/packages/29/3d/821a9a5799fac2556bcf0bd37a70d1d11fa9e49784b6d22e92e8b2f85f18/coverage-7.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810", size = 222900, upload-time = "2026-03-17T10:31:05.651Z" }, + { url = "https://files.pythonhosted.org/packages/d4/fa/2238c2ad08e35cf4f020ea721f717e09ec3152aea75d191a7faf3ef009a8/coverage-7.13.5-cp312-cp312-win_arm64.whl", hash = "sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de", size = 221515, upload-time = "2026-03-17T10:31:07.293Z" }, + { url = "https://files.pythonhosted.org/packages/74/8c/74fedc9663dcf168b0a059d4ea756ecae4da77a489048f94b5f512a8d0b3/coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1", size = 219576, upload-time = "2026-03-17T10:31:09.045Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c9/44fb661c55062f0818a6ffd2685c67aa30816200d5f2817543717d4b92eb/coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3", size = 219942, upload-time = "2026-03-17T10:31:10.708Z" }, + { url = "https://files.pythonhosted.org/packages/5f/13/93419671cee82b780bab7ea96b67c8ef448f5f295f36bf5031154ec9a790/coverage-7.13.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26", size = 250935, upload-time = "2026-03-17T10:31:12.392Z" }, + { url = "https://files.pythonhosted.org/packages/ac/68/1666e3a4462f8202d836920114fa7a5ee9275d1fa45366d336c551a162dd/coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3", size = 253541, upload-time = "2026-03-17T10:31:14.247Z" }, + { url = "https://files.pythonhosted.org/packages/4e/5e/3ee3b835647be646dcf3c65a7c6c18f87c27326a858f72ab22c12730773d/coverage-7.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b", size = 254780, upload-time = "2026-03-17T10:31:16.193Z" }, + { url = "https://files.pythonhosted.org/packages/44/b3/cb5bd1a04cfcc49ede6cd8409d80bee17661167686741e041abc7ee1b9a9/coverage-7.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a", size = 256912, upload-time = "2026-03-17T10:31:17.89Z" }, + { url = "https://files.pythonhosted.org/packages/1b/66/c1dceb7b9714473800b075f5c8a84f4588f887a90eb8645282031676e242/coverage-7.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969", size = 251165, upload-time = "2026-03-17T10:31:19.605Z" }, + { url = "https://files.pythonhosted.org/packages/b7/62/5502b73b97aa2e53ea22a39cf8649ff44827bef76d90bf638777daa27a9d/coverage-7.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161", size = 252908, upload-time = "2026-03-17T10:31:21.312Z" }, + { url = "https://files.pythonhosted.org/packages/7d/37/7792c2d69854397ca77a55c4646e5897c467928b0e27f2d235d83b5d08c6/coverage-7.13.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15", size = 250873, upload-time = "2026-03-17T10:31:23.565Z" }, + { url = "https://files.pythonhosted.org/packages/a3/23/bc866fb6163be52a8a9e5d708ba0d3b1283c12158cefca0a8bbb6e247a43/coverage-7.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1", size = 255030, upload-time = "2026-03-17T10:31:25.58Z" }, + { url = "https://files.pythonhosted.org/packages/7d/8b/ef67e1c222ef49860701d346b8bbb70881bef283bd5f6cbba68a39a086c7/coverage-7.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6", size = 250694, upload-time = "2026-03-17T10:31:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/46/0d/866d1f74f0acddbb906db212e096dee77a8e2158ca5e6bb44729f9d93298/coverage-7.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17", size = 252469, upload-time = "2026-03-17T10:31:29.472Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f5/be742fec31118f02ce42b21c6af187ad6a344fed546b56ca60caacc6a9a0/coverage-7.13.5-cp313-cp313-win32.whl", hash = "sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85", size = 222112, upload-time = "2026-03-17T10:31:31.526Z" }, + { url = "https://files.pythonhosted.org/packages/66/40/7732d648ab9d069a46e686043241f01206348e2bbf128daea85be4d6414b/coverage-7.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b", size = 222923, upload-time = "2026-03-17T10:31:33.633Z" }, + { url = "https://files.pythonhosted.org/packages/48/af/fea819c12a095781f6ccd504890aaddaf88b8fab263c4940e82c7b770124/coverage-7.13.5-cp313-cp313-win_arm64.whl", hash = "sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664", size = 221540, upload-time = "2026-03-17T10:31:35.445Z" }, + { url = "https://files.pythonhosted.org/packages/23/d2/17879af479df7fbbd44bd528a31692a48f6b25055d16482fdf5cdb633805/coverage-7.13.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d", size = 220262, upload-time = "2026-03-17T10:31:37.184Z" }, + { url = "https://files.pythonhosted.org/packages/5b/4c/d20e554f988c8f91d6a02c5118f9abbbf73a8768a3048cb4962230d5743f/coverage-7.13.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0", size = 220617, upload-time = "2026-03-17T10:31:39.245Z" }, + { url = "https://files.pythonhosted.org/packages/29/9c/f9f5277b95184f764b24e7231e166dfdb5780a46d408a2ac665969416d61/coverage-7.13.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806", size = 261912, upload-time = "2026-03-17T10:31:41.324Z" }, + { url = "https://files.pythonhosted.org/packages/d5/f6/7f1ab39393eeb50cfe4747ae8ef0e4fc564b989225aa1152e13a180d74f8/coverage-7.13.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3", size = 263987, upload-time = "2026-03-17T10:31:43.724Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d7/62c084fb489ed9c6fbdf57e006752e7c516ea46fd690e5ed8b8617c7d52e/coverage-7.13.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9", size = 266416, upload-time = "2026-03-17T10:31:45.769Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f6/df63d8660e1a0bff6125947afda112a0502736f470d62ca68b288ea762d8/coverage-7.13.5-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd", size = 267558, upload-time = "2026-03-17T10:31:48.293Z" }, + { url = "https://files.pythonhosted.org/packages/5b/02/353ca81d36779bd108f6d384425f7139ac3c58c750dcfaafe5d0bee6436b/coverage-7.13.5-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606", size = 261163, upload-time = "2026-03-17T10:31:50.125Z" }, + { url = "https://files.pythonhosted.org/packages/2c/16/2e79106d5749bcaf3aee6d309123548e3276517cd7851faa8da213bc61bf/coverage-7.13.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e", size = 263981, upload-time = "2026-03-17T10:31:51.961Z" }, + { url = "https://files.pythonhosted.org/packages/29/c7/c29e0c59ffa6942030ae6f50b88ae49988e7e8da06de7ecdbf49c6d4feae/coverage-7.13.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0", size = 261604, upload-time = "2026-03-17T10:31:53.872Z" }, + { url = "https://files.pythonhosted.org/packages/40/48/097cdc3db342f34006a308ab41c3a7c11c3f0d84750d340f45d88a782e00/coverage-7.13.5-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87", size = 265321, upload-time = "2026-03-17T10:31:55.997Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1f/4994af354689e14fd03a75f8ec85a9a68d94e0188bbdab3fc1516b55e512/coverage-7.13.5-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479", size = 260502, upload-time = "2026-03-17T10:31:58.308Z" }, + { url = "https://files.pythonhosted.org/packages/22/c6/9bb9ef55903e628033560885f5c31aa227e46878118b63ab15dc7ba87797/coverage-7.13.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2", size = 262688, upload-time = "2026-03-17T10:32:00.141Z" }, + { url = "https://files.pythonhosted.org/packages/14/4f/f5df9007e50b15e53e01edea486814783a7f019893733d9e4d6caad75557/coverage-7.13.5-cp313-cp313t-win32.whl", hash = "sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a", size = 222788, upload-time = "2026-03-17T10:32:02.246Z" }, + { url = "https://files.pythonhosted.org/packages/e1/98/aa7fccaa97d0f3192bec013c4e6fd6d294a6ed44b640e6bb61f479e00ed5/coverage-7.13.5-cp313-cp313t-win_amd64.whl", hash = "sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819", size = 223851, upload-time = "2026-03-17T10:32:04.416Z" }, + { url = "https://files.pythonhosted.org/packages/3d/8b/e5c469f7352651e5f013198e9e21f97510b23de957dd06a84071683b4b60/coverage-7.13.5-cp313-cp313t-win_arm64.whl", hash = "sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911", size = 222104, upload-time = "2026-03-17T10:32:06.65Z" }, + { url = "https://files.pythonhosted.org/packages/8e/77/39703f0d1d4b478bfd30191d3c14f53caf596fac00efb3f8f6ee23646439/coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f", size = 219621, upload-time = "2026-03-17T10:32:08.589Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3e/51dff36d99ae14639a133d9b164d63e628532e2974d8b1edb99dd1ebc733/coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e", size = 219953, upload-time = "2026-03-17T10:32:10.507Z" }, + { url = "https://files.pythonhosted.org/packages/6a/6c/1f1917b01eb647c2f2adc9962bd66c79eb978951cab61bdc1acab3290c07/coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a", size = 250992, upload-time = "2026-03-17T10:32:12.41Z" }, + { url = "https://files.pythonhosted.org/packages/22/e5/06b1f88f42a5a99df42ce61208bdec3bddb3d261412874280a19796fc09c/coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510", size = 253503, upload-time = "2026-03-17T10:32:14.449Z" }, + { url = "https://files.pythonhosted.org/packages/80/28/2a148a51e5907e504fa7b85490277734e6771d8844ebcc48764a15e28155/coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247", size = 254852, upload-time = "2026-03-17T10:32:16.56Z" }, + { url = "https://files.pythonhosted.org/packages/61/77/50e8d3d85cc0b7ebe09f30f151d670e302c7ff4a1bf6243f71dd8b0981fa/coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6", size = 257161, upload-time = "2026-03-17T10:32:19.004Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c4/b5fd1d4b7bf8d0e75d997afd3925c59ba629fc8616f1b3aae7605132e256/coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0", size = 251021, upload-time = "2026-03-17T10:32:21.344Z" }, + { url = "https://files.pythonhosted.org/packages/f8/66/6ea21f910e92d69ef0b1c3346ea5922a51bad4446c9126db2ae96ee24c4c/coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882", size = 252858, upload-time = "2026-03-17T10:32:23.506Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ea/879c83cb5d61aa2a35fb80e72715e92672daef8191b84911a643f533840c/coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740", size = 250823, upload-time = "2026-03-17T10:32:25.516Z" }, + { url = "https://files.pythonhosted.org/packages/8a/fb/616d95d3adb88b9803b275580bdeee8bd1b69a886d057652521f83d7322f/coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16", size = 255099, upload-time = "2026-03-17T10:32:27.944Z" }, + { url = "https://files.pythonhosted.org/packages/1c/93/25e6917c90ec1c9a56b0b26f6cad6408e5f13bb6b35d484a0d75c9cf000d/coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0", size = 250638, upload-time = "2026-03-17T10:32:29.914Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7b/dc1776b0464145a929deed214aef9fb1493f159b59ff3c7eeeedf91eddd0/coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0", size = 252295, upload-time = "2026-03-17T10:32:31.981Z" }, + { url = "https://files.pythonhosted.org/packages/ea/fb/99cbbc56a26e07762a2740713f3c8f9f3f3106e3a3dd8cc4474954bccd34/coverage-7.13.5-cp314-cp314-win32.whl", hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc", size = 222360, upload-time = "2026-03-17T10:32:34.233Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b7/4758d4f73fb536347cc5e4ad63662f9d60ba9118cb6785e9616b2ce5d7fa/coverage-7.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633", size = 223174, upload-time = "2026-03-17T10:32:36.369Z" }, + { url = "https://files.pythonhosted.org/packages/2c/f2/24d84e1dfe70f8ac9fdf30d338239860d0d1d5da0bda528959d0ebc9da28/coverage-7.13.5-cp314-cp314-win_arm64.whl", hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8", size = 221739, upload-time = "2026-03-17T10:32:38.736Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/4a168591057b3668c2428bff25dd3ebc21b629d666d90bcdfa0217940e84/coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b", size = 220351, upload-time = "2026-03-17T10:32:41.196Z" }, + { url = "https://files.pythonhosted.org/packages/f5/21/1fd5c4dbfe4a58b6b99649125635df46decdfd4a784c3cd6d410d303e370/coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c", size = 220612, upload-time = "2026-03-17T10:32:43.204Z" }, + { url = "https://files.pythonhosted.org/packages/d6/fe/2a924b3055a5e7e4512655a9d4609781b0d62334fa0140c3e742926834e2/coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9", size = 261985, upload-time = "2026-03-17T10:32:45.514Z" }, + { url = "https://files.pythonhosted.org/packages/d7/0d/c8928f2bd518c45990fe1a2ab8db42e914ef9b726c975facc4282578c3eb/coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29", size = 264107, upload-time = "2026-03-17T10:32:47.971Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ae/4ae35bbd9a0af9d820362751f0766582833c211224b38665c0f8de3d487f/coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607", size = 266513, upload-time = "2026-03-17T10:32:50.1Z" }, + { url = "https://files.pythonhosted.org/packages/9c/20/d326174c55af36f74eac6ae781612d9492f060ce8244b570bb9d50d9d609/coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90", size = 267650, upload-time = "2026-03-17T10:32:52.391Z" }, + { url = "https://files.pythonhosted.org/packages/7a/5e/31484d62cbd0eabd3412e30d74386ece4a0837d4f6c3040a653878bfc019/coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3", size = 261089, upload-time = "2026-03-17T10:32:54.544Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d8/49a72d6de146eebb0b7e48cc0f4bc2c0dd858e3d4790ab2b39a2872b62bd/coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab", size = 263982, upload-time = "2026-03-17T10:32:56.803Z" }, + { url = "https://files.pythonhosted.org/packages/06/3b/0351f1bd566e6e4dd39e978efe7958bde1d32f879e85589de147654f57bb/coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562", size = 261579, upload-time = "2026-03-17T10:32:59.466Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ce/796a2a2f4017f554d7810f5c573449b35b1e46788424a548d4d19201b222/coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2", size = 265316, upload-time = "2026-03-17T10:33:01.847Z" }, + { url = "https://files.pythonhosted.org/packages/3d/16/d5ae91455541d1a78bc90abf495be600588aff8f6db5c8b0dae739fa39c9/coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea", size = 260427, upload-time = "2026-03-17T10:33:03.945Z" }, + { url = "https://files.pythonhosted.org/packages/48/11/07f413dba62db21fb3fad5d0de013a50e073cc4e2dc4306e770360f6dfc8/coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a", size = 262745, upload-time = "2026-03-17T10:33:06.285Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/d792371332eb4663115becf4bad47e047d16234b1aff687b1b18c58d60ae/coverage-7.13.5-cp314-cp314t-win32.whl", hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215", size = 223146, upload-time = "2026-03-17T10:33:08.756Z" }, + { url = "https://files.pythonhosted.org/packages/db/51/37221f59a111dca5e85be7dbf09696323b5b9f13ff65e0641d535ed06ea8/coverage-7.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43", size = 224254, upload-time = "2026-03-17T10:33:11.174Z" }, + { url = "https://files.pythonhosted.org/packages/54/83/6acacc889de8987441aa7d5adfbdbf33d288dad28704a67e574f1df9bcbb/coverage-7.13.5-cp314-cp314t-win_arm64.whl", hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45", size = 222276, upload-time = "2026-03-17T10:33:13.466Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" }, ] [package.optional-dependencies] @@ -417,11 +445,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.20.3" +version = "3.25.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, ] [[package]] @@ -450,7 +478,7 @@ wheels = [ [[package]] name = "fmpy" -version = "0.3.27" +version = "0.3.29" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -463,56 +491,56 @@ dependencies = [ { name = "numpy" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/f6/524fab535e287fc4d39736602cb69b4d9622a795d6c5da397f8039ed4c38/fmpy-0.3.27-py3-none-any.whl", hash = "sha256:0c2c270dd39e4303e7200dd62626f4d80e76cda4a2ca3a8e542bd4f70522eddf", size = 4970777, upload-time = "2025-12-16T13:36:12.686Z" }, + { url = "https://files.pythonhosted.org/packages/28/d7/6a95ae6bbbf04926fe461c52c8405711cd53bb8c2c7c4b408443243df65e/fmpy-0.3.29-py3-none-any.whl", hash = "sha256:18b5d5015049dbdc76561491efabad3befc27724c0f403840f4fe9183e45cc80", size = 6453065, upload-time = "2026-03-24T14:06:08.592Z" }, ] [[package]] name = "fonttools" -version = "4.61.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/ca/cf17b88a8df95691275a3d77dc0a5ad9907f328ae53acbe6795da1b2f5ed/fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69", size = 3565756, upload-time = "2025-12-12T17:31:24.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/12/bf9f4eaa2fad039356cc627587e30ed008c03f1cebd3034376b5ee8d1d44/fonttools-4.61.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c6604b735bb12fef8e0efd5578c9fb5d3d8532d5001ea13a19cddf295673ee09", size = 2852213, upload-time = "2025-12-12T17:29:46.675Z" }, - { url = "https://files.pythonhosted.org/packages/ac/49/4138d1acb6261499bedde1c07f8c2605d1d8f9d77a151e5507fd3ef084b6/fonttools-4.61.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5ce02f38a754f207f2f06557523cd39a06438ba3aafc0639c477ac409fc64e37", size = 2401689, upload-time = "2025-12-12T17:29:48.769Z" }, - { url = "https://files.pythonhosted.org/packages/e5/fe/e6ce0fe20a40e03aef906af60aa87668696f9e4802fa283627d0b5ed777f/fonttools-4.61.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77efb033d8d7ff233385f30c62c7c79271c8885d5c9657d967ede124671bbdfb", size = 5058809, upload-time = "2025-12-12T17:29:51.701Z" }, - { url = "https://files.pythonhosted.org/packages/79/61/1ca198af22f7dd22c17ab86e9024ed3c06299cfdb08170640e9996d501a0/fonttools-4.61.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:75c1a6dfac6abd407634420c93864a1e274ebc1c7531346d9254c0d8f6ca00f9", size = 5036039, upload-time = "2025-12-12T17:29:53.659Z" }, - { url = "https://files.pythonhosted.org/packages/99/cc/fa1801e408586b5fce4da9f5455af8d770f4fc57391cd5da7256bb364d38/fonttools-4.61.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0de30bfe7745c0d1ffa2b0b7048fb7123ad0d71107e10ee090fa0b16b9452e87", size = 5034714, upload-time = "2025-12-12T17:29:55.592Z" }, - { url = "https://files.pythonhosted.org/packages/bf/aa/b7aeafe65adb1b0a925f8f25725e09f078c635bc22754f3fecb7456955b0/fonttools-4.61.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58b0ee0ab5b1fc9921eccfe11d1435added19d6494dde14e323f25ad2bc30c56", size = 5158648, upload-time = "2025-12-12T17:29:57.861Z" }, - { url = "https://files.pythonhosted.org/packages/99/f9/08ea7a38663328881384c6e7777bbefc46fd7d282adfd87a7d2b84ec9d50/fonttools-4.61.1-cp311-cp311-win32.whl", hash = "sha256:f79b168428351d11e10c5aeb61a74e1851ec221081299f4cf56036a95431c43a", size = 2280681, upload-time = "2025-12-12T17:29:59.943Z" }, - { url = "https://files.pythonhosted.org/packages/07/ad/37dd1ae5fa6e01612a1fbb954f0927681f282925a86e86198ccd7b15d515/fonttools-4.61.1-cp311-cp311-win_amd64.whl", hash = "sha256:fe2efccb324948a11dd09d22136fe2ac8a97d6c1347cf0b58a911dcd529f66b7", size = 2331951, upload-time = "2025-12-12T17:30:02.254Z" }, - { url = "https://files.pythonhosted.org/packages/6f/16/7decaa24a1bd3a70c607b2e29f0adc6159f36a7e40eaba59846414765fd4/fonttools-4.61.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f3cb4a569029b9f291f88aafc927dd53683757e640081ca8c412781ea144565e", size = 2851593, upload-time = "2025-12-12T17:30:04.225Z" }, - { url = "https://files.pythonhosted.org/packages/94/98/3c4cb97c64713a8cf499b3245c3bf9a2b8fd16a3e375feff2aed78f96259/fonttools-4.61.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41a7170d042e8c0024703ed13b71893519a1a6d6e18e933e3ec7507a2c26a4b2", size = 2400231, upload-time = "2025-12-12T17:30:06.47Z" }, - { url = "https://files.pythonhosted.org/packages/b7/37/82dbef0f6342eb01f54bca073ac1498433d6ce71e50c3c3282b655733b31/fonttools-4.61.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10d88e55330e092940584774ee5e8a6971b01fc2f4d3466a1d6c158230880796", size = 4954103, upload-time = "2025-12-12T17:30:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/6c/44/f3aeac0fa98e7ad527f479e161aca6c3a1e47bb6996b053d45226fe37bf2/fonttools-4.61.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15acc09befd16a0fb8a8f62bc147e1a82817542d72184acca9ce6e0aeda9fa6d", size = 5004295, upload-time = "2025-12-12T17:30:10.56Z" }, - { url = "https://files.pythonhosted.org/packages/14/e8/7424ced75473983b964d09f6747fa09f054a6d656f60e9ac9324cf40c743/fonttools-4.61.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e6bcdf33aec38d16508ce61fd81838f24c83c90a1d1b8c68982857038673d6b8", size = 4944109, upload-time = "2025-12-12T17:30:12.874Z" }, - { url = "https://files.pythonhosted.org/packages/c8/8b/6391b257fa3d0b553d73e778f953a2f0154292a7a7a085e2374b111e5410/fonttools-4.61.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5fade934607a523614726119164ff621e8c30e8fa1ffffbbd358662056ba69f0", size = 5093598, upload-time = "2025-12-12T17:30:15.79Z" }, - { url = "https://files.pythonhosted.org/packages/d9/71/fd2ea96cdc512d92da5678a1c98c267ddd4d8c5130b76d0f7a80f9a9fde8/fonttools-4.61.1-cp312-cp312-win32.whl", hash = "sha256:75da8f28eff26defba42c52986de97b22106cb8f26515b7c22443ebc9c2d3261", size = 2269060, upload-time = "2025-12-12T17:30:18.058Z" }, - { url = "https://files.pythonhosted.org/packages/80/3b/a3e81b71aed5a688e89dfe0e2694b26b78c7d7f39a5ffd8a7d75f54a12a8/fonttools-4.61.1-cp312-cp312-win_amd64.whl", hash = "sha256:497c31ce314219888c0e2fce5ad9178ca83fe5230b01a5006726cdf3ac9f24d9", size = 2319078, upload-time = "2025-12-12T17:30:22.862Z" }, - { url = "https://files.pythonhosted.org/packages/4b/cf/00ba28b0990982530addb8dc3e9e6f2fa9cb5c20df2abdda7baa755e8fe1/fonttools-4.61.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c56c488ab471628ff3bfa80964372fc13504ece601e0d97a78ee74126b2045c", size = 2846454, upload-time = "2025-12-12T17:30:24.938Z" }, - { url = "https://files.pythonhosted.org/packages/5a/ca/468c9a8446a2103ae645d14fee3f610567b7042aba85031c1c65e3ef7471/fonttools-4.61.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc492779501fa723b04d0ab1f5be046797fee17d27700476edc7ee9ae535a61e", size = 2398191, upload-time = "2025-12-12T17:30:27.343Z" }, - { url = "https://files.pythonhosted.org/packages/a3/4b/d67eedaed19def5967fade3297fed8161b25ba94699efc124b14fb68cdbc/fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:64102ca87e84261419c3747a0d20f396eb024bdbeb04c2bfb37e2891f5fadcb5", size = 4928410, upload-time = "2025-12-12T17:30:29.771Z" }, - { url = "https://files.pythonhosted.org/packages/b0/8d/6fb3494dfe61a46258cd93d979cf4725ded4eb46c2a4ca35e4490d84daea/fonttools-4.61.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c1b526c8d3f615a7b1867f38a9410849c8f4aef078535742198e942fba0e9bd", size = 4984460, upload-time = "2025-12-12T17:30:32.073Z" }, - { url = "https://files.pythonhosted.org/packages/f7/f1/a47f1d30b3dc00d75e7af762652d4cbc3dff5c2697a0dbd5203c81afd9c3/fonttools-4.61.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:41ed4b5ec103bd306bb68f81dc166e77409e5209443e5773cb4ed837bcc9b0d3", size = 4925800, upload-time = "2025-12-12T17:30:34.339Z" }, - { url = "https://files.pythonhosted.org/packages/a7/01/e6ae64a0981076e8a66906fab01539799546181e32a37a0257b77e4aa88b/fonttools-4.61.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b501c862d4901792adaec7c25b1ecc749e2662543f68bb194c42ba18d6eec98d", size = 5067859, upload-time = "2025-12-12T17:30:36.593Z" }, - { url = "https://files.pythonhosted.org/packages/73/aa/28e40b8d6809a9b5075350a86779163f074d2b617c15d22343fce81918db/fonttools-4.61.1-cp313-cp313-win32.whl", hash = "sha256:4d7092bb38c53bbc78e9255a59158b150bcdc115a1e3b3ce0b5f267dc35dd63c", size = 2267821, upload-time = "2025-12-12T17:30:38.478Z" }, - { url = "https://files.pythonhosted.org/packages/1a/59/453c06d1d83dc0951b69ef692d6b9f1846680342927df54e9a1ca91c6f90/fonttools-4.61.1-cp313-cp313-win_amd64.whl", hash = "sha256:21e7c8d76f62ab13c9472ccf74515ca5b9a761d1bde3265152a6dc58700d895b", size = 2318169, upload-time = "2025-12-12T17:30:40.951Z" }, - { url = "https://files.pythonhosted.org/packages/32/8f/4e7bf82c0cbb738d3c2206c920ca34ca74ef9dabde779030145d28665104/fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fff4f534200a04b4a36e7ae3cb74493afe807b517a09e99cb4faa89a34ed6ecd", size = 2846094, upload-time = "2025-12-12T17:30:43.511Z" }, - { url = "https://files.pythonhosted.org/packages/71/09/d44e45d0a4f3a651f23a1e9d42de43bc643cce2971b19e784cc67d823676/fonttools-4.61.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d9203500f7c63545b4ce3799319fe4d9feb1a1b89b28d3cb5abd11b9dd64147e", size = 2396589, upload-time = "2025-12-12T17:30:45.681Z" }, - { url = "https://files.pythonhosted.org/packages/89/18/58c64cafcf8eb677a99ef593121f719e6dcbdb7d1c594ae5a10d4997ca8a/fonttools-4.61.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa646ecec9528bef693415c79a86e733c70a4965dd938e9a226b0fc64c9d2e6c", size = 4877892, upload-time = "2025-12-12T17:30:47.709Z" }, - { url = "https://files.pythonhosted.org/packages/8a/ec/9e6b38c7ba1e09eb51db849d5450f4c05b7e78481f662c3b79dbde6f3d04/fonttools-4.61.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f35ad7805edba3aac1a3710d104592df59f4b957e30108ae0ba6c10b11dd75", size = 4972884, upload-time = "2025-12-12T17:30:49.656Z" }, - { url = "https://files.pythonhosted.org/packages/5e/87/b5339da8e0256734ba0dbbf5b6cdebb1dd79b01dc8c270989b7bcd465541/fonttools-4.61.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b931ae8f62db78861b0ff1ac017851764602288575d65b8e8ff1963fed419063", size = 4924405, upload-time = "2025-12-12T17:30:51.735Z" }, - { url = "https://files.pythonhosted.org/packages/0b/47/e3409f1e1e69c073a3a6fd8cb886eb18c0bae0ee13db2c8d5e7f8495e8b7/fonttools-4.61.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b148b56f5de675ee16d45e769e69f87623a4944f7443850bf9a9376e628a89d2", size = 5035553, upload-time = "2025-12-12T17:30:54.823Z" }, - { url = "https://files.pythonhosted.org/packages/bf/b6/1f6600161b1073a984294c6c031e1a56ebf95b6164249eecf30012bb2e38/fonttools-4.61.1-cp314-cp314-win32.whl", hash = "sha256:9b666a475a65f4e839d3d10473fad6d47e0a9db14a2f4a224029c5bfde58ad2c", size = 2271915, upload-time = "2025-12-12T17:30:57.913Z" }, - { url = "https://files.pythonhosted.org/packages/52/7b/91e7b01e37cc8eb0e1f770d08305b3655e4f002fc160fb82b3390eabacf5/fonttools-4.61.1-cp314-cp314-win_amd64.whl", hash = "sha256:4f5686e1fe5fce75d82d93c47a438a25bf0d1319d2843a926f741140b2b16e0c", size = 2323487, upload-time = "2025-12-12T17:30:59.804Z" }, - { url = "https://files.pythonhosted.org/packages/39/5c/908ad78e46c61c3e3ed70c3b58ff82ab48437faf84ec84f109592cabbd9f/fonttools-4.61.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e76ce097e3c57c4bcb67c5aa24a0ecdbd9f74ea9219997a707a4061fbe2707aa", size = 2929571, upload-time = "2025-12-12T17:31:02.574Z" }, - { url = "https://files.pythonhosted.org/packages/bd/41/975804132c6dea64cdbfbaa59f3518a21c137a10cccf962805b301ac6ab2/fonttools-4.61.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9cfef3ab326780c04d6646f68d4b4742aae222e8b8ea1d627c74e38afcbc9d91", size = 2435317, upload-time = "2025-12-12T17:31:04.974Z" }, - { url = "https://files.pythonhosted.org/packages/b0/5a/aef2a0a8daf1ebaae4cfd83f84186d4a72ee08fd6a8451289fcd03ffa8a4/fonttools-4.61.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a75c301f96db737e1c5ed5fd7d77d9c34466de16095a266509e13da09751bd19", size = 4882124, upload-time = "2025-12-12T17:31:07.456Z" }, - { url = "https://files.pythonhosted.org/packages/80/33/d6db3485b645b81cea538c9d1c9219d5805f0877fda18777add4671c5240/fonttools-4.61.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91669ccac46bbc1d09e9273546181919064e8df73488ea087dcac3e2968df9ba", size = 5100391, upload-time = "2025-12-12T17:31:09.732Z" }, - { url = "https://files.pythonhosted.org/packages/6c/d6/675ba631454043c75fcf76f0ca5463eac8eb0666ea1d7badae5fea001155/fonttools-4.61.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c33ab3ca9d3ccd581d58e989d67554e42d8d4ded94ab3ade3508455fe70e65f7", size = 4978800, upload-time = "2025-12-12T17:31:11.681Z" }, - { url = "https://files.pythonhosted.org/packages/7f/33/d3ec753d547a8d2bdaedd390d4a814e8d5b45a093d558f025c6b990b554c/fonttools-4.61.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:664c5a68ec406f6b1547946683008576ef8b38275608e1cee6c061828171c118", size = 5006426, upload-time = "2025-12-12T17:31:13.764Z" }, - { url = "https://files.pythonhosted.org/packages/b4/40/cc11f378b561a67bea850ab50063366a0d1dd3f6d0a30ce0f874b0ad5664/fonttools-4.61.1-cp314-cp314t-win32.whl", hash = "sha256:aed04cabe26f30c1647ef0e8fbb207516fd40fe9472e9439695f5c6998e60ac5", size = 2335377, upload-time = "2025-12-12T17:31:16.49Z" }, - { url = "https://files.pythonhosted.org/packages/e4/ff/c9a2b66b39f8628531ea58b320d66d951267c98c6a38684daa8f50fb02f8/fonttools-4.61.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2180f14c141d2f0f3da43f3a81bc8aa4684860f6b0e6f9e165a4831f24e6a23b", size = 2400613, upload-time = "2025-12-12T17:31:18.769Z" }, - { url = "https://files.pythonhosted.org/packages/c7/4e/ce75a57ff3aebf6fc1f4e9d508b8e5810618a33d900ad6c19eb30b290b97/fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371", size = 1148996, upload-time = "2025-12-12T17:31:21.03Z" }, +version = "4.62.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/08/7012b00a9a5874311b639c3920270c36ee0c445b69d9989a85e5c92ebcb0/fonttools-4.62.1.tar.gz", hash = "sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d", size = 3580737, upload-time = "2026-03-13T13:54:25.52Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/39/23ff32561ec8d45a4d48578b4d241369d9270dc50926c017570e60893701/fonttools-4.62.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:40975849bac44fb0b9253d77420c6d8b523ac4dcdcefeff6e4d706838a5b80f7", size = 2871039, upload-time = "2026-03-13T13:52:33.127Z" }, + { url = "https://files.pythonhosted.org/packages/24/7f/66d3f8a9338a9b67fe6e1739f47e1cd5cee78bd3bc1206ef9b0b982289a5/fonttools-4.62.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9dde91633f77fa576879a0c76b1d89de373cae751a98ddf0109d54e173b40f14", size = 2416346, upload-time = "2026-03-13T13:52:35.676Z" }, + { url = "https://files.pythonhosted.org/packages/aa/53/5276ceba7bff95da7793a07c5284e1da901cf00341ce5e2f3273056c0cca/fonttools-4.62.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6acb4109f8bee00fec985c8c7afb02299e35e9c94b57287f3ea542f28bd0b0a7", size = 5100897, upload-time = "2026-03-13T13:52:38.102Z" }, + { url = "https://files.pythonhosted.org/packages/cc/a1/40a5c4d8e28b0851d53a8eeeb46fbd73c325a2a9a165f290a5ed90e6c597/fonttools-4.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1c5c25671ce8805e0d080e2ffdeca7f1e86778c5cbfbeae86d7f866d8830517b", size = 5071078, upload-time = "2026-03-13T13:52:41.305Z" }, + { url = "https://files.pythonhosted.org/packages/e3/be/d378fca4c65ea1956fee6d90ace6e861776809cbbc5af22388a090c3c092/fonttools-4.62.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a5d8825e1140f04e6c99bb7d37a9e31c172f3bc208afbe02175339e699c710e1", size = 5076908, upload-time = "2026-03-13T13:52:44.122Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d9/ae6a1d0693a4185a84605679c8a1f719a55df87b9c6e8e817bfdd9ef5936/fonttools-4.62.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:268abb1cb221e66c014acc234e872b7870d8b5d4657a83a8f4205094c32d2416", size = 5202275, upload-time = "2026-03-13T13:52:46.591Z" }, + { url = "https://files.pythonhosted.org/packages/54/6c/af95d9c4efb15cabff22642b608342f2bd67137eea6107202d91b5b03184/fonttools-4.62.1-cp311-cp311-win32.whl", hash = "sha256:942b03094d7edbb99bdf1ae7e9090898cad7bf9030b3d21f33d7072dbcb51a53", size = 2293075, upload-time = "2026-03-13T13:52:48.711Z" }, + { url = "https://files.pythonhosted.org/packages/d3/97/bf54c5b3f2be34e1f143e6db838dfdc54f2ffa3e68c738934c82f3b2a08d/fonttools-4.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:e8514f4924375f77084e81467e63238b095abda5107620f49421c368a6017ed2", size = 2344593, upload-time = "2026-03-13T13:52:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/47/d4/dbacced3953544b9a93088cc10ef2b596d348c983d5c67a404fa41ec51ba/fonttools-4.62.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:90365821debbd7db678809c7491ca4acd1e0779b9624cdc6ddaf1f31992bf974", size = 2870219, upload-time = "2026-03-13T13:52:53.664Z" }, + { url = "https://files.pythonhosted.org/packages/66/9e/a769c8e99b81e5a87ab7e5e7236684de4e96246aae17274e5347d11ebd78/fonttools-4.62.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12859ff0b47dd20f110804c3e0d0970f7b832f561630cd879969011541a464a9", size = 2414891, upload-time = "2026-03-13T13:52:56.493Z" }, + { url = "https://files.pythonhosted.org/packages/69/64/f19a9e3911968c37e1e620e14dfc5778299e1474f72f4e57c5ec771d9489/fonttools-4.62.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c125ffa00c3d9003cdaaf7f2c79e6e535628093e14b5de1dccb08859b680936", size = 5033197, upload-time = "2026-03-13T13:52:59.179Z" }, + { url = "https://files.pythonhosted.org/packages/9b/8a/99c8b3c3888c5c474c08dbfd7c8899786de9604b727fcefb055b42c84bba/fonttools-4.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:149f7d84afca659d1a97e39a4778794a2f83bf344c5ee5134e09995086cc2392", size = 4988768, upload-time = "2026-03-13T13:53:02.761Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c6/0f904540d3e6ab463c1243a0d803504826a11604c72dd58c2949796a1762/fonttools-4.62.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0aa72c43a601cfa9273bb1ae0518f1acadc01ee181a6fc60cd758d7fdadffc04", size = 4971512, upload-time = "2026-03-13T13:53:05.678Z" }, + { url = "https://files.pythonhosted.org/packages/29/0b/5cbef6588dc9bd6b5c9ad6a4d5a8ca384d0cea089da31711bbeb4f9654a6/fonttools-4.62.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:19177c8d96c7c36359266e571c5173bcee9157b59cfc8cb0153c5673dc5a3a7d", size = 5122723, upload-time = "2026-03-13T13:53:08.662Z" }, + { url = "https://files.pythonhosted.org/packages/4a/47/b3a5342d381595ef439adec67848bed561ab7fdb1019fa522e82101b7d9c/fonttools-4.62.1-cp312-cp312-win32.whl", hash = "sha256:a24decd24d60744ee8b4679d38e88b8303d86772053afc29b19d23bb8207803c", size = 2281278, upload-time = "2026-03-13T13:53:10.998Z" }, + { url = "https://files.pythonhosted.org/packages/28/b1/0c2ab56a16f409c6c8a68816e6af707827ad5d629634691ff60a52879792/fonttools-4.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:9e7863e10b3de72376280b515d35b14f5eeed639d1aa7824f4cf06779ec65e42", size = 2331414, upload-time = "2026-03-13T13:53:13.992Z" }, + { url = "https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79", size = 2865155, upload-time = "2026-03-13T13:53:16.132Z" }, + { url = "https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe", size = 2412802, upload-time = "2026-03-13T13:53:18.878Z" }, + { url = "https://files.pythonhosted.org/packages/52/94/e6ac4b44026de7786fe46e3bfa0c87e51d5d70a841054065d49cd62bb909/fonttools-4.62.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68", size = 5013926, upload-time = "2026-03-13T13:53:21.379Z" }, + { url = "https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1", size = 4964575, upload-time = "2026-03-13T13:53:23.857Z" }, + { url = "https://files.pythonhosted.org/packages/46/76/7d051671e938b1881670528fec69cc4044315edd71a229c7fd712eaa5119/fonttools-4.62.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069", size = 4953693, upload-time = "2026-03-13T13:53:26.569Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ae/b41f8628ec0be3c1b934fc12b84f4576a5c646119db4d3bdd76a217c90b5/fonttools-4.62.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9", size = 5094920, upload-time = "2026-03-13T13:53:29.329Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f6/53a1e9469331a23dcc400970a27a4caa3d9f6edbf5baab0260285238b884/fonttools-4.62.1-cp313-cp313-win32.whl", hash = "sha256:93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24", size = 2279928, upload-time = "2026-03-13T13:53:32.352Z" }, + { url = "https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056", size = 2330514, upload-time = "2026-03-13T13:53:34.991Z" }, + { url = "https://files.pythonhosted.org/packages/36/f0/2888cdac391807d68d90dcb16ef858ddc1b5309bfc6966195a459dd326e2/fonttools-4.62.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca", size = 2864442, upload-time = "2026-03-13T13:53:37.509Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b2/e521803081f8dc35990816b82da6360fa668a21b44da4b53fc9e77efcd62/fonttools-4.62.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca", size = 2410901, upload-time = "2026-03-13T13:53:40.55Z" }, + { url = "https://files.pythonhosted.org/packages/00/a4/8c3511ff06e53110039358dbbdc1a65d72157a054638387aa2ada300a8b8/fonttools-4.62.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782", size = 4999608, upload-time = "2026-03-13T13:53:42.798Z" }, + { url = "https://files.pythonhosted.org/packages/28/63/cd0c3b26afe60995a5295f37c246a93d454023726c3261cfbb3559969bb9/fonttools-4.62.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae", size = 4912726, upload-time = "2026-03-13T13:53:45.405Z" }, + { url = "https://files.pythonhosted.org/packages/70/b9/ac677cb07c24c685cf34f64e140617d58789d67a3dd524164b63648c6114/fonttools-4.62.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7", size = 4951422, upload-time = "2026-03-13T13:53:48.326Z" }, + { url = "https://files.pythonhosted.org/packages/e6/10/11c08419a14b85b7ca9a9faca321accccc8842dd9e0b1c8a72908de05945/fonttools-4.62.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a", size = 5060979, upload-time = "2026-03-13T13:53:51.366Z" }, + { url = "https://files.pythonhosted.org/packages/4e/3c/12eea4a4cf054e7ab058ed5ceada43b46809fce2bf319017c4d63ae55bb4/fonttools-4.62.1-cp314-cp314-win32.whl", hash = "sha256:49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800", size = 2283733, upload-time = "2026-03-13T13:53:53.606Z" }, + { url = "https://files.pythonhosted.org/packages/6b/67/74b070029043186b5dd13462c958cb7c7f811be0d2e634309d9a1ffb1505/fonttools-4.62.1-cp314-cp314-win_amd64.whl", hash = "sha256:1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e", size = 2335663, upload-time = "2026-03-13T13:53:56.23Z" }, + { url = "https://files.pythonhosted.org/packages/42/c5/4d2ed3ca6e33617fc5624467da353337f06e7f637707478903c785bd8e20/fonttools-4.62.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82", size = 2947288, upload-time = "2026-03-13T13:53:59.397Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e9/7ab11ddfda48ed0f89b13380e5595ba572619c27077be0b2c447a63ff351/fonttools-4.62.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260", size = 2449023, upload-time = "2026-03-13T13:54:01.642Z" }, + { url = "https://files.pythonhosted.org/packages/b2/10/a800fa090b5e8819942e54e19b55fc7c21fe14a08757c3aa3ca8db358939/fonttools-4.62.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4", size = 5137599, upload-time = "2026-03-13T13:54:04.495Z" }, + { url = "https://files.pythonhosted.org/packages/37/dc/8ccd45033fffd74deb6912fa1ca524643f584b94c87a16036855b498a1ed/fonttools-4.62.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b", size = 4920933, upload-time = "2026-03-13T13:54:07.557Z" }, + { url = "https://files.pythonhosted.org/packages/99/eb/e618adefb839598d25ac8136cd577925d6c513dc0d931d93b8af956210f0/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87", size = 5016232, upload-time = "2026-03-13T13:54:10.611Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5f/9b5c9bfaa8ec82def8d8168c4f13615990d6ce5996fe52bd49bfb5e05134/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c", size = 5042987, upload-time = "2026-03-13T13:54:13.569Z" }, + { url = "https://files.pythonhosted.org/packages/90/aa/dfbbe24c6a6afc5c203d90cc0343e24bcbb09e76d67c4d6eef8c2558d7ba/fonttools-4.62.1-cp314-cp314t-win32.whl", hash = "sha256:b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a", size = 2348021, upload-time = "2026-03-13T13:54:16.98Z" }, + { url = "https://files.pythonhosted.org/packages/13/6f/ae9c4e4dd417948407b680855c2c7790efb52add6009aaecff1e3bc50e8e/fonttools-4.62.1-cp314-cp314t-win_amd64.whl", hash = "sha256:59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e", size = 2414147, upload-time = "2026-03-13T13:54:19.416Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ba/56147c165442cc5ba7e82ecf301c9a68353cede498185869e6e02b4c264f/fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd", size = 1152647, upload-time = "2026-03-13T13:54:22.735Z" }, ] [[package]] @@ -534,11 +562,11 @@ wheels = [ [[package]] name = "identify" -version = "2.6.16" +version = "2.6.18" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5b/8d/e8b97e6bd3fb6fb271346f7981362f1e04d6a7463abd0de79e1fda17c067/identify-2.6.16.tar.gz", hash = "sha256:846857203b5511bbe94d5a352a48ef2359532bc8f6727b5544077a0dcfb24980", size = 99360, upload-time = "2026-01-12T18:58:58.201Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/c4/7fb4db12296cdb11893d61c92048fe617ee853f8523b9b296ac03b43757e/identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd", size = 99580, upload-time = "2026-03-15T18:39:50.319Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/58/40fbbcefeda82364720eba5cf2270f98496bdfa19ea75b4cccae79c698e6/identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0", size = 99202, upload-time = "2026-01-12T18:58:56.627Z" }, + { url = "https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737", size = 99394, upload-time = "2026-03-15T18:39:48.915Z" }, ] [[package]] @@ -552,11 +580,11 @@ wheels = [ [[package]] name = "imagesize" -version = "1.4.1" +version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026, upload-time = "2022-07-01T12:21:05.687Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/e6/7bf14eeb8f8b7251141944835abd42eb20a658d89084b7e1f3e5fe394090/imagesize-2.0.0.tar.gz", hash = "sha256:8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3", size = 1773045, upload-time = "2026-03-03T14:18:29.941Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, + { url = "https://files.pythonhosted.org/packages/5f/53/fb7122b71361a0d121b669dcf3d31244ef75badbbb724af388948de543e2/imagesize-2.0.0-py2.py3-none-any.whl", hash = "sha256:5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96", size = 9441, upload-time = "2026-03-03T14:18:27.892Z" }, ] [[package]] @@ -582,14 +610,11 @@ wheels = [ [[package]] name = "jsonpath-ng" -version = "1.7.0" +version = "1.8.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ply" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/86/08646239a313f895186ff0a4573452038eed8c86f54380b3ebac34d32fb2/jsonpath-ng-1.7.0.tar.gz", hash = "sha256:f6f5f7fd4e5ff79c785f1573b394043b39849fb2bb47bcead935d12b00beab3c", size = 37838, upload-time = "2024-10-11T15:41:42.404Z" } +sdist = { url = "https://files.pythonhosted.org/packages/32/58/250751940d75c8019659e15482d548a4aa3b6ce122c515102a4bfdac50e3/jsonpath_ng-1.8.0.tar.gz", hash = "sha256:54252968134b5e549ea5b872f1df1168bd7defe1a52fed5a358c194e1943ddc3", size = 74513, upload-time = "2026-02-24T14:42:06.182Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/35/5a/73ecb3d82f8615f32ccdadeb9356726d6cae3a4bbc840b437ceb95708063/jsonpath_ng-1.7.0-py3-none-any.whl", hash = "sha256:f3d7f9e848cba1b6da28c55b1c26ff915dc9e0b1ba7e752a53d6da8d5cbd00b6", size = 30105, upload-time = "2024-11-20T17:58:30.418Z" }, + { url = "https://files.pythonhosted.org/packages/03/99/33c7d78a3fb70d545fd5411ac67a651c81602cc09c9cf0df383733f068c5/jsonpath_ng-1.8.0-py3-none-any.whl", hash = "sha256:b8dde192f8af58d646fc031fac9c99fe4d00326afc4148f1f043c601a8cfe138", size = 67844, upload-time = "2026-02-28T00:53:19.637Z" }, ] [[package]] @@ -634,92 +659,108 @@ wheels = [ [[package]] name = "kiwisolver" -version = "1.4.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/ab/c80b0d5a9d8a1a65f4f815f2afff9798b12c3b9f31f1d304dd233dd920e2/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eb14a5da6dc7642b0f3a18f13654847cd8b7a2550e2645a5bda677862b03ba16", size = 124167, upload-time = "2025-08-10T21:25:53.403Z" }, - { url = "https://files.pythonhosted.org/packages/a0/c0/27fe1a68a39cf62472a300e2879ffc13c0538546c359b86f149cc19f6ac3/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39a219e1c81ae3b103643d2aedb90f1ef22650deb266ff12a19e7773f3e5f089", size = 66579, upload-time = "2025-08-10T21:25:54.79Z" }, - { url = "https://files.pythonhosted.org/packages/31/a2/a12a503ac1fd4943c50f9822678e8015a790a13b5490354c68afb8489814/kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2405a7d98604b87f3fc28b1716783534b1b4b8510d8142adca34ee0bc3c87543", size = 65309, upload-time = "2025-08-10T21:25:55.76Z" }, - { url = "https://files.pythonhosted.org/packages/66/e1/e533435c0be77c3f64040d68d7a657771194a63c279f55573188161e81ca/kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dc1ae486f9abcef254b5618dfb4113dd49f94c68e3e027d03cf0143f3f772b61", size = 1435596, upload-time = "2025-08-10T21:25:56.861Z" }, - { url = "https://files.pythonhosted.org/packages/67/1e/51b73c7347f9aabdc7215aa79e8b15299097dc2f8e67dee2b095faca9cb0/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a1f570ce4d62d718dce3f179ee78dac3b545ac16c0c04bb363b7607a949c0d1", size = 1246548, upload-time = "2025-08-10T21:25:58.246Z" }, - { url = "https://files.pythonhosted.org/packages/21/aa/72a1c5d1e430294f2d32adb9542719cfb441b5da368d09d268c7757af46c/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb27e7b78d716c591e88e0a09a2139c6577865d7f2e152488c2cc6257f460872", size = 1263618, upload-time = "2025-08-10T21:25:59.857Z" }, - { url = "https://files.pythonhosted.org/packages/a3/af/db1509a9e79dbf4c260ce0cfa3903ea8945f6240e9e59d1e4deb731b1a40/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:15163165efc2f627eb9687ea5f3a28137217d217ac4024893d753f46bce9de26", size = 1317437, upload-time = "2025-08-10T21:26:01.105Z" }, - { url = "https://files.pythonhosted.org/packages/e0/f2/3ea5ee5d52abacdd12013a94130436e19969fa183faa1e7c7fbc89e9a42f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bdee92c56a71d2b24c33a7d4c2856bd6419d017e08caa7802d2963870e315028", size = 2195742, upload-time = "2025-08-10T21:26:02.675Z" }, - { url = "https://files.pythonhosted.org/packages/6f/9b/1efdd3013c2d9a2566aa6a337e9923a00590c516add9a1e89a768a3eb2fc/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:412f287c55a6f54b0650bd9b6dce5aceddb95864a1a90c87af16979d37c89771", size = 2290810, upload-time = "2025-08-10T21:26:04.009Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e5/cfdc36109ae4e67361f9bc5b41323648cb24a01b9ade18784657e022e65f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2c93f00dcba2eea70af2be5f11a830a742fe6b579a1d4e00f47760ef13be247a", size = 2461579, upload-time = "2025-08-10T21:26:05.317Z" }, - { url = "https://files.pythonhosted.org/packages/62/86/b589e5e86c7610842213994cdea5add00960076bef4ae290c5fa68589cac/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f117e1a089d9411663a3207ba874f31be9ac8eaa5b533787024dc07aeb74f464", size = 2268071, upload-time = "2025-08-10T21:26:06.686Z" }, - { url = "https://files.pythonhosted.org/packages/3b/c6/f8df8509fd1eee6c622febe54384a96cfaf4d43bf2ccec7a0cc17e4715c9/kiwisolver-1.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:be6a04e6c79819c9a8c2373317d19a96048e5a3f90bec587787e86a1153883c2", size = 73840, upload-time = "2025-08-10T21:26:07.94Z" }, - { url = "https://files.pythonhosted.org/packages/e2/2d/16e0581daafd147bc11ac53f032a2b45eabac897f42a338d0a13c1e5c436/kiwisolver-1.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:0ae37737256ba2de764ddc12aed4956460277f00c4996d51a197e72f62f5eec7", size = 65159, upload-time = "2025-08-10T21:26:09.048Z" }, - { url = "https://files.pythonhosted.org/packages/86/c9/13573a747838aeb1c76e3267620daa054f4152444d1f3d1a2324b78255b5/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999", size = 123686, upload-time = "2025-08-10T21:26:10.034Z" }, - { url = "https://files.pythonhosted.org/packages/51/ea/2ecf727927f103ffd1739271ca19c424d0e65ea473fbaeea1c014aea93f6/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2", size = 66460, upload-time = "2025-08-10T21:26:11.083Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/51f5464373ce2aeb5194508298a508b6f21d3867f499556263c64c621914/kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14", size = 64952, upload-time = "2025-08-10T21:26:12.058Z" }, - { url = "https://files.pythonhosted.org/packages/70/90/6d240beb0f24b74371762873e9b7f499f1e02166a2d9c5801f4dbf8fa12e/kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04", size = 1474756, upload-time = "2025-08-10T21:26:13.096Z" }, - { url = "https://files.pythonhosted.org/packages/12/42/f36816eaf465220f683fb711efdd1bbf7a7005a2473d0e4ed421389bd26c/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752", size = 1276404, upload-time = "2025-08-10T21:26:14.457Z" }, - { url = "https://files.pythonhosted.org/packages/2e/64/bc2de94800adc830c476dce44e9b40fd0809cddeef1fde9fcf0f73da301f/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77", size = 1294410, upload-time = "2025-08-10T21:26:15.73Z" }, - { url = "https://files.pythonhosted.org/packages/5f/42/2dc82330a70aa8e55b6d395b11018045e58d0bb00834502bf11509f79091/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198", size = 1343631, upload-time = "2025-08-10T21:26:17.045Z" }, - { url = "https://files.pythonhosted.org/packages/22/fd/f4c67a6ed1aab149ec5a8a401c323cee7a1cbe364381bb6c9c0d564e0e20/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d", size = 2224963, upload-time = "2025-08-10T21:26:18.737Z" }, - { url = "https://files.pythonhosted.org/packages/45/aa/76720bd4cb3713314677d9ec94dcc21ced3f1baf4830adde5bb9b2430a5f/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab", size = 2321295, upload-time = "2025-08-10T21:26:20.11Z" }, - { url = "https://files.pythonhosted.org/packages/80/19/d3ec0d9ab711242f56ae0dc2fc5d70e298bb4a1f9dfab44c027668c673a1/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2", size = 2487987, upload-time = "2025-08-10T21:26:21.49Z" }, - { url = "https://files.pythonhosted.org/packages/39/e9/61e4813b2c97e86b6fdbd4dd824bf72d28bcd8d4849b8084a357bc0dd64d/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145", size = 2291817, upload-time = "2025-08-10T21:26:22.812Z" }, - { url = "https://files.pythonhosted.org/packages/a0/41/85d82b0291db7504da3c2defe35c9a8a5c9803a730f297bd823d11d5fb77/kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54", size = 73895, upload-time = "2025-08-10T21:26:24.37Z" }, - { url = "https://files.pythonhosted.org/packages/e2/92/5f3068cf15ee5cb624a0c7596e67e2a0bb2adee33f71c379054a491d07da/kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60", size = 64992, upload-time = "2025-08-10T21:26:25.732Z" }, - { url = "https://files.pythonhosted.org/packages/31/c1/c2686cda909742ab66c7388e9a1a8521a59eb89f8bcfbee28fc980d07e24/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8", size = 123681, upload-time = "2025-08-10T21:26:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2", size = 66464, upload-time = "2025-08-10T21:26:27.733Z" }, - { url = "https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f", size = 64961, upload-time = "2025-08-10T21:26:28.729Z" }, - { url = "https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098", size = 1474607, upload-time = "2025-08-10T21:26:29.798Z" }, - { url = "https://files.pythonhosted.org/packages/d9/28/aac26d4c882f14de59041636292bc838db8961373825df23b8eeb807e198/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed", size = 1276546, upload-time = "2025-08-10T21:26:31.401Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ad/8bfc1c93d4cc565e5069162f610ba2f48ff39b7de4b5b8d93f69f30c4bed/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525", size = 1294482, upload-time = "2025-08-10T21:26:32.721Z" }, - { url = "https://files.pythonhosted.org/packages/da/f1/6aca55ff798901d8ce403206d00e033191f63d82dd708a186e0ed2067e9c/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78", size = 1343720, upload-time = "2025-08-10T21:26:34.032Z" }, - { url = "https://files.pythonhosted.org/packages/d1/91/eed031876c595c81d90d0f6fc681ece250e14bf6998c3d7c419466b523b7/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b", size = 2224907, upload-time = "2025-08-10T21:26:35.824Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ec/4d1925f2e49617b9cca9c34bfa11adefad49d00db038e692a559454dfb2e/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799", size = 2321334, upload-time = "2025-08-10T21:26:37.534Z" }, - { url = "https://files.pythonhosted.org/packages/43/cb/450cd4499356f68802750c6ddc18647b8ea01ffa28f50d20598e0befe6e9/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3", size = 2488313, upload-time = "2025-08-10T21:26:39.191Z" }, - { url = "https://files.pythonhosted.org/packages/71/67/fc76242bd99f885651128a5d4fa6083e5524694b7c88b489b1b55fdc491d/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c", size = 2291970, upload-time = "2025-08-10T21:26:40.828Z" }, - { url = "https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d", size = 73894, upload-time = "2025-08-10T21:26:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/95/38/dce480814d25b99a391abbddadc78f7c117c6da34be68ca8b02d5848b424/kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2", size = 64995, upload-time = "2025-08-10T21:26:43.889Z" }, - { url = "https://files.pythonhosted.org/packages/e2/37/7d218ce5d92dadc5ebdd9070d903e0c7cf7edfe03f179433ac4d13ce659c/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1", size = 126510, upload-time = "2025-08-10T21:26:44.915Z" }, - { url = "https://files.pythonhosted.org/packages/23/b0/e85a2b48233daef4b648fb657ebbb6f8367696a2d9548a00b4ee0eb67803/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1", size = 67903, upload-time = "2025-08-10T21:26:45.934Z" }, - { url = "https://files.pythonhosted.org/packages/44/98/f2425bc0113ad7de24da6bb4dae1343476e95e1d738be7c04d31a5d037fd/kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11", size = 66402, upload-time = "2025-08-10T21:26:47.101Z" }, - { url = "https://files.pythonhosted.org/packages/98/d8/594657886df9f34c4177cc353cc28ca7e6e5eb562d37ccc233bff43bbe2a/kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c", size = 1582135, upload-time = "2025-08-10T21:26:48.665Z" }, - { url = "https://files.pythonhosted.org/packages/5c/c6/38a115b7170f8b306fc929e166340c24958347308ea3012c2b44e7e295db/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197", size = 1389409, upload-time = "2025-08-10T21:26:50.335Z" }, - { url = "https://files.pythonhosted.org/packages/bf/3b/e04883dace81f24a568bcee6eb3001da4ba05114afa622ec9b6fafdc1f5e/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c", size = 1401763, upload-time = "2025-08-10T21:26:51.867Z" }, - { url = "https://files.pythonhosted.org/packages/9f/80/20ace48e33408947af49d7d15c341eaee69e4e0304aab4b7660e234d6288/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185", size = 1453643, upload-time = "2025-08-10T21:26:53.592Z" }, - { url = "https://files.pythonhosted.org/packages/64/31/6ce4380a4cd1f515bdda976a1e90e547ccd47b67a1546d63884463c92ca9/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748", size = 2330818, upload-time = "2025-08-10T21:26:55.051Z" }, - { url = "https://files.pythonhosted.org/packages/fa/e9/3f3fcba3bcc7432c795b82646306e822f3fd74df0ee81f0fa067a1f95668/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64", size = 2419963, upload-time = "2025-08-10T21:26:56.421Z" }, - { url = "https://files.pythonhosted.org/packages/99/43/7320c50e4133575c66e9f7dadead35ab22d7c012a3b09bb35647792b2a6d/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff", size = 2594639, upload-time = "2025-08-10T21:26:57.882Z" }, - { url = "https://files.pythonhosted.org/packages/65/d6/17ae4a270d4a987ef8a385b906d2bdfc9fce502d6dc0d3aea865b47f548c/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07", size = 2391741, upload-time = "2025-08-10T21:26:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/2a/8f/8f6f491d595a9e5912971f3f863d81baddccc8a4d0c3749d6a0dd9ffc9df/kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c", size = 68646, upload-time = "2025-08-10T21:27:00.52Z" }, - { url = "https://files.pythonhosted.org/packages/6b/32/6cc0fbc9c54d06c2969faa9c1d29f5751a2e51809dd55c69055e62d9b426/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386", size = 123806, upload-time = "2025-08-10T21:27:01.537Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dd/2bfb1d4a4823d92e8cbb420fe024b8d2167f72079b3bb941207c42570bdf/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552", size = 66605, upload-time = "2025-08-10T21:27:03.335Z" }, - { url = "https://files.pythonhosted.org/packages/f7/69/00aafdb4e4509c2ca6064646cba9cd4b37933898f426756adb2cb92ebbed/kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3", size = 64925, upload-time = "2025-08-10T21:27:04.339Z" }, - { url = "https://files.pythonhosted.org/packages/43/dc/51acc6791aa14e5cb6d8a2e28cefb0dc2886d8862795449d021334c0df20/kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58", size = 1472414, upload-time = "2025-08-10T21:27:05.437Z" }, - { url = "https://files.pythonhosted.org/packages/3d/bb/93fa64a81db304ac8a246f834d5094fae4b13baf53c839d6bb6e81177129/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4", size = 1281272, upload-time = "2025-08-10T21:27:07.063Z" }, - { url = "https://files.pythonhosted.org/packages/70/e6/6df102916960fb8d05069d4bd92d6d9a8202d5a3e2444494e7cd50f65b7a/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df", size = 1298578, upload-time = "2025-08-10T21:27:08.452Z" }, - { url = "https://files.pythonhosted.org/packages/7c/47/e142aaa612f5343736b087864dbaebc53ea8831453fb47e7521fa8658f30/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6", size = 1345607, upload-time = "2025-08-10T21:27:10.125Z" }, - { url = "https://files.pythonhosted.org/packages/54/89/d641a746194a0f4d1a3670fb900d0dbaa786fb98341056814bc3f058fa52/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5", size = 2230150, upload-time = "2025-08-10T21:27:11.484Z" }, - { url = "https://files.pythonhosted.org/packages/aa/6b/5ee1207198febdf16ac11f78c5ae40861b809cbe0e6d2a8d5b0b3044b199/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf", size = 2325979, upload-time = "2025-08-10T21:27:12.917Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ff/b269eefd90f4ae14dcc74973d5a0f6d28d3b9bb1afd8c0340513afe6b39a/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5", size = 2491456, upload-time = "2025-08-10T21:27:14.353Z" }, - { url = "https://files.pythonhosted.org/packages/fc/d4/10303190bd4d30de547534601e259a4fbf014eed94aae3e5521129215086/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce", size = 2294621, upload-time = "2025-08-10T21:27:15.808Z" }, - { url = "https://files.pythonhosted.org/packages/28/e0/a9a90416fce5c0be25742729c2ea52105d62eda6c4be4d803c2a7be1fa50/kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7", size = 75417, upload-time = "2025-08-10T21:27:17.436Z" }, - { url = "https://files.pythonhosted.org/packages/1f/10/6949958215b7a9a264299a7db195564e87900f709db9245e4ebdd3c70779/kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c", size = 66582, upload-time = "2025-08-10T21:27:18.436Z" }, - { url = "https://files.pythonhosted.org/packages/ec/79/60e53067903d3bc5469b369fe0dfc6b3482e2133e85dae9daa9527535991/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548", size = 126514, upload-time = "2025-08-10T21:27:19.465Z" }, - { url = "https://files.pythonhosted.org/packages/25/d1/4843d3e8d46b072c12a38c97c57fab4608d36e13fe47d47ee96b4d61ba6f/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d", size = 67905, upload-time = "2025-08-10T21:27:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ae/29ffcbd239aea8b93108de1278271ae764dfc0d803a5693914975f200596/kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c", size = 66399, upload-time = "2025-08-10T21:27:21.496Z" }, - { url = "https://files.pythonhosted.org/packages/a1/ae/d7ba902aa604152c2ceba5d352d7b62106bedbccc8e95c3934d94472bfa3/kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122", size = 1582197, upload-time = "2025-08-10T21:27:22.604Z" }, - { url = "https://files.pythonhosted.org/packages/f2/41/27c70d427eddb8bc7e4f16420a20fefc6f480312122a59a959fdfe0445ad/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64", size = 1390125, upload-time = "2025-08-10T21:27:24.036Z" }, - { url = "https://files.pythonhosted.org/packages/41/42/b3799a12bafc76d962ad69083f8b43b12bf4fe78b097b12e105d75c9b8f1/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134", size = 1402612, upload-time = "2025-08-10T21:27:25.773Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b5/a210ea073ea1cfaca1bb5c55a62307d8252f531beb364e18aa1e0888b5a0/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370", size = 1453990, upload-time = "2025-08-10T21:27:27.089Z" }, - { url = "https://files.pythonhosted.org/packages/5f/ce/a829eb8c033e977d7ea03ed32fb3c1781b4fa0433fbadfff29e39c676f32/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21", size = 2331601, upload-time = "2025-08-10T21:27:29.343Z" }, - { url = "https://files.pythonhosted.org/packages/e0/4b/b5e97eb142eb9cd0072dacfcdcd31b1c66dc7352b0f7c7255d339c0edf00/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a", size = 2422041, upload-time = "2025-08-10T21:27:30.754Z" }, - { url = "https://files.pythonhosted.org/packages/40/be/8eb4cd53e1b85ba4edc3a9321666f12b83113a178845593307a3e7891f44/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f", size = 2594897, upload-time = "2025-08-10T21:27:32.803Z" }, - { url = "https://files.pythonhosted.org/packages/99/dd/841e9a66c4715477ea0abc78da039832fbb09dac5c35c58dc4c41a407b8a/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369", size = 2391835, upload-time = "2025-08-10T21:27:34.23Z" }, - { url = "https://files.pythonhosted.org/packages/0c/28/4b2e5c47a0da96896fdfdb006340ade064afa1e63675d01ea5ac222b6d52/kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891", size = 79988, upload-time = "2025-08-10T21:27:35.587Z" }, - { url = "https://files.pythonhosted.org/packages/80/be/3578e8afd18c88cdf9cb4cffde75a96d2be38c5a903f1ed0ceec061bd09e/kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32", size = 70260, upload-time = "2025-08-10T21:27:36.606Z" }, - { url = "https://files.pythonhosted.org/packages/a3/0f/36d89194b5a32c054ce93e586d4049b6c2c22887b0eb229c61c68afd3078/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:720e05574713db64c356e86732c0f3c5252818d05f9df320f0ad8380641acea5", size = 60104, upload-time = "2025-08-10T21:27:43.287Z" }, - { url = "https://files.pythonhosted.org/packages/52/ba/4ed75f59e4658fd21fe7dde1fee0ac397c678ec3befba3fe6482d987af87/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:17680d737d5335b552994a2008fab4c851bcd7de33094a82067ef3a576ff02fa", size = 58592, upload-time = "2025-08-10T21:27:44.314Z" }, - { url = "https://files.pythonhosted.org/packages/33/01/a8ea7c5ea32a9b45ceeaee051a04c8ed4320f5add3c51bfa20879b765b70/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85b5352f94e490c028926ea567fc569c52ec79ce131dadb968d3853e809518c2", size = 80281, upload-time = "2025-08-10T21:27:45.369Z" }, - { url = "https://files.pythonhosted.org/packages/da/e3/dbd2ecdce306f1d07a1aaf324817ee993aab7aee9db47ceac757deabafbe/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:464415881e4801295659462c49461a24fb107c140de781d55518c4b80cb6790f", size = 78009, upload-time = "2025-08-10T21:27:46.376Z" }, - { url = "https://files.pythonhosted.org/packages/da/e9/0d4add7873a73e462aeb45c036a2dead2562b825aa46ba326727b3f31016/kiwisolver-1.4.9-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fb940820c63a9590d31d88b815e7a3aa5915cad3ce735ab45f0c730b39547de1", size = 73929, upload-time = "2025-08-10T21:27:48.236Z" }, +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", size = 122798, upload-time = "2026-03-09T13:12:58.963Z" }, + { url = "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", size = 66216, upload-time = "2026-03-09T13:13:00.155Z" }, + { url = "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", size = 63911, upload-time = "2026-03-09T13:13:01.671Z" }, + { url = "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", size = 1438209, upload-time = "2026-03-09T13:13:03.385Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398", size = 1248888, upload-time = "2026-03-09T13:13:05.231Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", size = 1266304, upload-time = "2026-03-09T13:13:06.743Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", size = 1319650, upload-time = "2026-03-09T13:13:08.607Z" }, + { url = "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", size = 970949, upload-time = "2026-03-09T13:13:10.299Z" }, + { url = "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", size = 2199125, upload-time = "2026-03-09T13:13:11.841Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", size = 2293783, upload-time = "2026-03-09T13:13:14.399Z" }, + { url = "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", size = 1960726, upload-time = "2026-03-09T13:13:16.047Z" }, + { url = "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", size = 2464738, upload-time = "2026-03-09T13:13:17.897Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", size = 2270718, upload-time = "2026-03-09T13:13:19.421Z" }, + { url = "https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b", size = 73480, upload-time = "2026-03-09T13:13:20.818Z" }, + { url = "https://files.pythonhosted.org/packages/d8/0e/2ee5debc4f77a625778fec5501ff3e8036fe361b7ee28ae402a485bb9694/kiwisolver-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac", size = 64930, upload-time = "2026-03-09T13:13:21.997Z" }, + { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" }, + { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" }, + { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" }, + { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" }, + { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" }, + { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" }, + { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" }, + { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" }, + { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" }, + { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" }, + { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" }, + { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" }, + { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" }, + { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166, upload-time = "2026-03-09T13:13:48.032Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395, upload-time = "2026-03-09T13:13:49.365Z" }, + { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065, upload-time = "2026-03-09T13:13:50.562Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903, upload-time = "2026-03-09T13:13:52.084Z" }, + { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751, upload-time = "2026-03-09T13:13:54.673Z" }, + { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793, upload-time = "2026-03-09T13:13:56.287Z" }, + { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041, upload-time = "2026-03-09T13:13:58.269Z" }, + { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292, upload-time = "2026-03-09T13:13:59.871Z" }, + { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865, upload-time = "2026-03-09T13:14:01.401Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369, upload-time = "2026-03-09T13:14:02.972Z" }, + { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989, upload-time = "2026-03-09T13:14:04.503Z" }, + { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645, upload-time = "2026-03-09T13:14:06.106Z" }, + { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237, upload-time = "2026-03-09T13:14:08.891Z" }, + { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573, upload-time = "2026-03-09T13:14:12.327Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998, upload-time = "2026-03-09T13:14:13.469Z" }, + { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700, upload-time = "2026-03-09T13:14:14.636Z" }, + { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537, upload-time = "2026-03-09T13:14:15.808Z" }, + { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514, upload-time = "2026-03-09T13:14:18.035Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848, upload-time = "2026-03-09T13:14:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542, upload-time = "2026-03-09T13:14:21.54Z" }, + { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447, upload-time = "2026-03-09T13:14:23.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918, upload-time = "2026-03-09T13:14:24.74Z" }, + { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856, upload-time = "2026-03-09T13:14:26.597Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580, upload-time = "2026-03-09T13:14:28.237Z" }, + { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018, upload-time = "2026-03-09T13:14:30.018Z" }, + { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804, upload-time = "2026-03-09T13:14:32.888Z" }, + { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482, upload-time = "2026-03-09T13:14:34.971Z" }, + { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328, upload-time = "2026-03-09T13:14:36.816Z" }, + { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410, upload-time = "2026-03-09T13:14:38.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231, upload-time = "2026-03-09T13:14:41.323Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489, upload-time = "2026-03-09T13:14:42.534Z" }, + { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063, upload-time = "2026-03-09T13:14:44.759Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913, upload-time = "2026-03-09T13:14:46.247Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782, upload-time = "2026-03-09T13:14:48.458Z" }, + { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815, upload-time = "2026-03-09T13:14:50.721Z" }, + { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925, upload-time = "2026-03-09T13:14:52.304Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322, upload-time = "2026-03-09T13:14:54.637Z" }, + { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857, upload-time = "2026-03-09T13:14:56.471Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376, upload-time = "2026-03-09T13:14:58.072Z" }, + { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549, upload-time = "2026-03-09T13:14:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680, upload-time = "2026-03-09T13:15:01.364Z" }, + { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905, upload-time = "2026-03-09T13:15:03.925Z" }, + { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086, upload-time = "2026-03-09T13:15:07.775Z" }, + { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577, upload-time = "2026-03-09T13:15:09.139Z" }, + { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794, upload-time = "2026-03-09T13:15:10.525Z" }, + { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646, upload-time = "2026-03-09T13:15:12.016Z" }, + { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511, upload-time = "2026-03-09T13:15:13.311Z" }, + { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858, upload-time = "2026-03-09T13:15:15.103Z" }, + { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539, upload-time = "2026-03-09T13:15:16.661Z" }, + { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310, upload-time = "2026-03-09T13:15:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244, upload-time = "2026-03-09T13:15:20.444Z" }, + { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154, upload-time = "2026-03-09T13:15:22.039Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377, upload-time = "2026-03-09T13:15:23.741Z" }, + { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288, upload-time = "2026-03-09T13:15:25.789Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158, upload-time = "2026-03-09T13:15:27.63Z" }, + { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260, upload-time = "2026-03-09T13:15:29.421Z" }, + { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403, upload-time = "2026-03-09T13:15:31.517Z" }, + { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687, upload-time = "2026-03-09T13:15:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032, upload-time = "2026-03-09T13:15:34.411Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" }, + { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" }, + { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" }, + { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" }, + { url = "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", size = 59532, upload-time = "2026-03-09T13:15:47.047Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", size = 57420, upload-time = "2026-03-09T13:15:48.199Z" }, + { url = "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", size = 79892, upload-time = "2026-03-09T13:15:49.694Z" }, + { url = "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", size = 77603, upload-time = "2026-03-09T13:15:50.945Z" }, + { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558, upload-time = "2026-03-09T13:15:52.112Z" }, ] [[package]] @@ -748,65 +789,75 @@ wheels = [ [[package]] name = "librt" -version = "0.7.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/24/5f3646ff414285e0f7708fa4e946b9bf538345a41d1c375c439467721a5e/librt-0.7.8.tar.gz", hash = "sha256:1a4ede613941d9c3470b0368be851df6bb78ab218635512d0370b27a277a0862", size = 148323, upload-time = "2026-01-14T12:56:16.876Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/a3/87ea9c1049f2c781177496ebee29430e4631f439b8553a4969c88747d5d8/librt-0.7.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3e9c11aa260c31493d4b3197d1e28dd07768594a4f92bec4506849d736248f", size = 56507, upload-time = "2026-01-14T12:54:54.156Z" }, - { url = "https://files.pythonhosted.org/packages/5e/4a/23bcef149f37f771ad30203d561fcfd45b02bc54947b91f7a9ac34815747/librt-0.7.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddb52499d0b3ed4aa88746aaf6f36a08314677d5c346234c3987ddc506404eac", size = 58455, upload-time = "2026-01-14T12:54:55.978Z" }, - { url = "https://files.pythonhosted.org/packages/22/6e/46eb9b85c1b9761e0f42b6e6311e1cc544843ac897457062b9d5d0b21df4/librt-0.7.8-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e9c0afebbe6ce177ae8edba0c7c4d626f2a0fc12c33bb993d163817c41a7a05c", size = 164956, upload-time = "2026-01-14T12:54:57.311Z" }, - { url = "https://files.pythonhosted.org/packages/7a/3f/aa7c7f6829fb83989feb7ba9aa11c662b34b4bd4bd5b262f2876ba3db58d/librt-0.7.8-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:631599598e2c76ded400c0a8722dec09217c89ff64dc54b060f598ed68e7d2a8", size = 174364, upload-time = "2026-01-14T12:54:59.089Z" }, - { url = "https://files.pythonhosted.org/packages/3f/2d/d57d154b40b11f2cb851c4df0d4c4456bacd9b1ccc4ecb593ddec56c1a8b/librt-0.7.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c1ba843ae20db09b9d5c80475376168feb2640ce91cd9906414f23cc267a1ff", size = 188034, upload-time = "2026-01-14T12:55:00.141Z" }, - { url = "https://files.pythonhosted.org/packages/59/f9/36c4dad00925c16cd69d744b87f7001792691857d3b79187e7a673e812fb/librt-0.7.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b5b007bb22ea4b255d3ee39dfd06d12534de2fcc3438567d9f48cdaf67ae1ae3", size = 186295, upload-time = "2026-01-14T12:55:01.303Z" }, - { url = "https://files.pythonhosted.org/packages/23/9b/8a9889d3df5efb67695a67785028ccd58e661c3018237b73ad081691d0cb/librt-0.7.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dbd79caaf77a3f590cbe32dc2447f718772d6eea59656a7dcb9311161b10fa75", size = 181470, upload-time = "2026-01-14T12:55:02.492Z" }, - { url = "https://files.pythonhosted.org/packages/43/64/54d6ef11afca01fef8af78c230726a9394759f2addfbf7afc5e3cc032a45/librt-0.7.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:87808a8d1e0bd62a01cafc41f0fd6818b5a5d0ca0d8a55326a81643cdda8f873", size = 201713, upload-time = "2026-01-14T12:55:03.919Z" }, - { url = "https://files.pythonhosted.org/packages/2d/29/73e7ed2991330b28919387656f54109139b49e19cd72902f466bd44415fd/librt-0.7.8-cp311-cp311-win32.whl", hash = "sha256:31724b93baa91512bd0a376e7cf0b59d8b631ee17923b1218a65456fa9bda2e7", size = 43803, upload-time = "2026-01-14T12:55:04.996Z" }, - { url = "https://files.pythonhosted.org/packages/3f/de/66766ff48ed02b4d78deea30392ae200bcbd99ae61ba2418b49fd50a4831/librt-0.7.8-cp311-cp311-win_amd64.whl", hash = "sha256:978e8b5f13e52cf23a9e80f3286d7546baa70bc4ef35b51d97a709d0b28e537c", size = 50080, upload-time = "2026-01-14T12:55:06.489Z" }, - { url = "https://files.pythonhosted.org/packages/6f/e3/33450438ff3a8c581d4ed7f798a70b07c3206d298cf0b87d3806e72e3ed8/librt-0.7.8-cp311-cp311-win_arm64.whl", hash = "sha256:20e3946863d872f7cabf7f77c6c9d370b8b3d74333d3a32471c50d3a86c0a232", size = 43383, upload-time = "2026-01-14T12:55:07.49Z" }, - { url = "https://files.pythonhosted.org/packages/56/04/79d8fcb43cae376c7adbab7b2b9f65e48432c9eced62ac96703bcc16e09b/librt-0.7.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b6943885b2d49c48d0cff23b16be830ba46b0152d98f62de49e735c6e655a63", size = 57472, upload-time = "2026-01-14T12:55:08.528Z" }, - { url = "https://files.pythonhosted.org/packages/b4/ba/60b96e93043d3d659da91752689023a73981336446ae82078cddf706249e/librt-0.7.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46ef1f4b9b6cc364b11eea0ecc0897314447a66029ee1e55859acb3dd8757c93", size = 58986, upload-time = "2026-01-14T12:55:09.466Z" }, - { url = "https://files.pythonhosted.org/packages/7c/26/5215e4cdcc26e7be7eee21955a7e13cbf1f6d7d7311461a6014544596fac/librt-0.7.8-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:907ad09cfab21e3c86e8f1f87858f7049d1097f77196959c033612f532b4e592", size = 168422, upload-time = "2026-01-14T12:55:10.499Z" }, - { url = "https://files.pythonhosted.org/packages/0f/84/e8d1bc86fa0159bfc24f3d798d92cafd3897e84c7fea7fe61b3220915d76/librt-0.7.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2991b6c3775383752b3ca0204842743256f3ad3deeb1d0adc227d56b78a9a850", size = 177478, upload-time = "2026-01-14T12:55:11.577Z" }, - { url = "https://files.pythonhosted.org/packages/57/11/d0268c4b94717a18aa91df1100e767b010f87b7ae444dafaa5a2d80f33a6/librt-0.7.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03679b9856932b8c8f674e87aa3c55ea11c9274301f76ae8dc4d281bda55cf62", size = 192439, upload-time = "2026-01-14T12:55:12.7Z" }, - { url = "https://files.pythonhosted.org/packages/8d/56/1e8e833b95fe684f80f8894ae4d8b7d36acc9203e60478fcae599120a975/librt-0.7.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3968762fec1b2ad34ce57458b6de25dbb4142713e9ca6279a0d352fa4e9f452b", size = 191483, upload-time = "2026-01-14T12:55:13.838Z" }, - { url = "https://files.pythonhosted.org/packages/17/48/f11cf28a2cb6c31f282009e2208312aa84a5ee2732859f7856ee306176d5/librt-0.7.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bb7a7807523a31f03061288cc4ffc065d684c39db7644c676b47d89553c0d714", size = 185376, upload-time = "2026-01-14T12:55:15.017Z" }, - { url = "https://files.pythonhosted.org/packages/b8/6a/d7c116c6da561b9155b184354a60a3d5cdbf08fc7f3678d09c95679d13d9/librt-0.7.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad64a14b1e56e702e19b24aae108f18ad1bf7777f3af5fcd39f87d0c5a814449", size = 206234, upload-time = "2026-01-14T12:55:16.571Z" }, - { url = "https://files.pythonhosted.org/packages/61/de/1975200bb0285fc921c5981d9978ce6ce11ae6d797df815add94a5a848a3/librt-0.7.8-cp312-cp312-win32.whl", hash = "sha256:0241a6ed65e6666236ea78203a73d800dbed896cf12ae25d026d75dc1fcd1dac", size = 44057, upload-time = "2026-01-14T12:55:18.077Z" }, - { url = "https://files.pythonhosted.org/packages/8e/cd/724f2d0b3461426730d4877754b65d39f06a41ac9d0a92d5c6840f72b9ae/librt-0.7.8-cp312-cp312-win_amd64.whl", hash = "sha256:6db5faf064b5bab9675c32a873436b31e01d66ca6984c6f7f92621656033a708", size = 50293, upload-time = "2026-01-14T12:55:19.179Z" }, - { url = "https://files.pythonhosted.org/packages/bd/cf/7e899acd9ee5727ad8160fdcc9994954e79fab371c66535c60e13b968ffc/librt-0.7.8-cp312-cp312-win_arm64.whl", hash = "sha256:57175aa93f804d2c08d2edb7213e09276bd49097611aefc37e3fa38d1fb99ad0", size = 43574, upload-time = "2026-01-14T12:55:20.185Z" }, - { url = "https://files.pythonhosted.org/packages/a1/fe/b1f9de2829cf7fc7649c1dcd202cfd873837c5cc2fc9e526b0e7f716c3d2/librt-0.7.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4c3995abbbb60b3c129490fa985dfe6cac11d88fc3c36eeb4fb1449efbbb04fc", size = 57500, upload-time = "2026-01-14T12:55:21.219Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d4/4a60fbe2e53b825f5d9a77325071d61cd8af8506255067bf0c8527530745/librt-0.7.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44e0c2cbc9bebd074cf2cdbe472ca185e824be4e74b1c63a8e934cea674bebf2", size = 59019, upload-time = "2026-01-14T12:55:22.256Z" }, - { url = "https://files.pythonhosted.org/packages/6a/37/61ff80341ba5159afa524445f2d984c30e2821f31f7c73cf166dcafa5564/librt-0.7.8-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d2f1e492cae964b3463a03dc77a7fe8742f7855d7258c7643f0ee32b6651dd3", size = 169015, upload-time = "2026-01-14T12:55:23.24Z" }, - { url = "https://files.pythonhosted.org/packages/1c/86/13d4f2d6a93f181ebf2fc953868826653ede494559da8268023fe567fca3/librt-0.7.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:451e7ffcef8f785831fdb791bd69211f47e95dc4c6ddff68e589058806f044c6", size = 178161, upload-time = "2026-01-14T12:55:24.826Z" }, - { url = "https://files.pythonhosted.org/packages/88/26/e24ef01305954fc4d771f1f09f3dd682f9eb610e1bec188ffb719374d26e/librt-0.7.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3469e1af9f1380e093ae06bedcbdd11e407ac0b303a56bbe9afb1d6824d4982d", size = 193015, upload-time = "2026-01-14T12:55:26.04Z" }, - { url = "https://files.pythonhosted.org/packages/88/a0/92b6bd060e720d7a31ed474d046a69bd55334ec05e9c446d228c4b806ae3/librt-0.7.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f11b300027ce19a34f6d24ebb0a25fd0e24a9d53353225a5c1e6cadbf2916b2e", size = 192038, upload-time = "2026-01-14T12:55:27.208Z" }, - { url = "https://files.pythonhosted.org/packages/06/bb/6f4c650253704279c3a214dad188101d1b5ea23be0606628bc6739456624/librt-0.7.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4adc73614f0d3c97874f02f2c7fd2a27854e7e24ad532ea6b965459c5b757eca", size = 186006, upload-time = "2026-01-14T12:55:28.594Z" }, - { url = "https://files.pythonhosted.org/packages/dc/00/1c409618248d43240cadf45f3efb866837fa77e9a12a71481912135eb481/librt-0.7.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:60c299e555f87e4c01b2eca085dfccda1dde87f5a604bb45c2906b8305819a93", size = 206888, upload-time = "2026-01-14T12:55:30.214Z" }, - { url = "https://files.pythonhosted.org/packages/d9/83/b2cfe8e76ff5c1c77f8a53da3d5de62d04b5ebf7cf913e37f8bca43b5d07/librt-0.7.8-cp313-cp313-win32.whl", hash = "sha256:b09c52ed43a461994716082ee7d87618096851319bf695d57ec123f2ab708951", size = 44126, upload-time = "2026-01-14T12:55:31.44Z" }, - { url = "https://files.pythonhosted.org/packages/a9/0b/c59d45de56a51bd2d3a401fc63449c0ac163e4ef7f523ea8b0c0dee86ec5/librt-0.7.8-cp313-cp313-win_amd64.whl", hash = "sha256:f8f4a901a3fa28969d6e4519deceab56c55a09d691ea7b12ca830e2fa3461e34", size = 50262, upload-time = "2026-01-14T12:55:33.01Z" }, - { url = "https://files.pythonhosted.org/packages/fc/b9/973455cec0a1ec592395250c474164c4a58ebf3e0651ee920fef1a2623f1/librt-0.7.8-cp313-cp313-win_arm64.whl", hash = "sha256:43d4e71b50763fcdcf64725ac680d8cfa1706c928b844794a7aa0fa9ac8e5f09", size = 43600, upload-time = "2026-01-14T12:55:34.054Z" }, - { url = "https://files.pythonhosted.org/packages/1a/73/fa8814c6ce2d49c3827829cadaa1589b0bf4391660bd4510899393a23ebc/librt-0.7.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:be927c3c94c74b05128089a955fba86501c3b544d1d300282cc1b4bd370cb418", size = 57049, upload-time = "2026-01-14T12:55:35.056Z" }, - { url = "https://files.pythonhosted.org/packages/53/fe/f6c70956da23ea235fd2e3cc16f4f0b4ebdfd72252b02d1164dd58b4e6c3/librt-0.7.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7b0803e9008c62a7ef79058233db7ff6f37a9933b8f2573c05b07ddafa226611", size = 58689, upload-time = "2026-01-14T12:55:36.078Z" }, - { url = "https://files.pythonhosted.org/packages/1f/4d/7a2481444ac5fba63050d9abe823e6bc16896f575bfc9c1e5068d516cdce/librt-0.7.8-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:79feb4d00b2a4e0e05c9c56df707934f41fcb5fe53fd9efb7549068d0495b758", size = 166808, upload-time = "2026-01-14T12:55:37.595Z" }, - { url = "https://files.pythonhosted.org/packages/ac/3c/10901d9e18639f8953f57c8986796cfbf4c1c514844a41c9197cf87cb707/librt-0.7.8-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9122094e3f24aa759c38f46bd8863433820654927370250f460ae75488b66ea", size = 175614, upload-time = "2026-01-14T12:55:38.756Z" }, - { url = "https://files.pythonhosted.org/packages/db/01/5cbdde0951a5090a80e5ba44e6357d375048123c572a23eecfb9326993a7/librt-0.7.8-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e03bea66af33c95ce3addf87a9bf1fcad8d33e757bc479957ddbc0e4f7207ac", size = 189955, upload-time = "2026-01-14T12:55:39.939Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b4/e80528d2f4b7eaf1d437fcbd6fc6ba4cbeb3e2a0cb9ed5a79f47c7318706/librt-0.7.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1ade7f31675db00b514b98f9ab9a7698c7282dad4be7492589109471852d398", size = 189370, upload-time = "2026-01-14T12:55:41.057Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ab/938368f8ce31a9787ecd4becb1e795954782e4312095daf8fd22420227c8/librt-0.7.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a14229ac62adcf1b90a15992f1ab9c69ae8b99ffb23cb64a90878a6e8a2f5b81", size = 183224, upload-time = "2026-01-14T12:55:42.328Z" }, - { url = "https://files.pythonhosted.org/packages/3c/10/559c310e7a6e4014ac44867d359ef8238465fb499e7eb31b6bfe3e3f86f5/librt-0.7.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5bcaaf624fd24e6a0cb14beac37677f90793a96864c67c064a91458611446e83", size = 203541, upload-time = "2026-01-14T12:55:43.501Z" }, - { url = "https://files.pythonhosted.org/packages/f8/db/a0db7acdb6290c215f343835c6efda5b491bb05c3ddc675af558f50fdba3/librt-0.7.8-cp314-cp314-win32.whl", hash = "sha256:7aa7d5457b6c542ecaed79cec4ad98534373c9757383973e638ccced0f11f46d", size = 40657, upload-time = "2026-01-14T12:55:44.668Z" }, - { url = "https://files.pythonhosted.org/packages/72/e0/4f9bdc2a98a798511e81edcd6b54fe82767a715e05d1921115ac70717f6f/librt-0.7.8-cp314-cp314-win_amd64.whl", hash = "sha256:3d1322800771bee4a91f3b4bd4e49abc7d35e65166821086e5afd1e6c0d9be44", size = 46835, upload-time = "2026-01-14T12:55:45.655Z" }, - { url = "https://files.pythonhosted.org/packages/f9/3d/59c6402e3dec2719655a41ad027a7371f8e2334aa794ed11533ad5f34969/librt-0.7.8-cp314-cp314-win_arm64.whl", hash = "sha256:5363427bc6a8c3b1719f8f3845ea53553d301382928a86e8fab7984426949bce", size = 39885, upload-time = "2026-01-14T12:55:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/4e/9c/2481d80950b83085fb14ba3c595db56330d21bbc7d88a19f20165f3538db/librt-0.7.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ca916919793a77e4a98d4a1701e345d337ce53be4a16620f063191f7322ac80f", size = 59161, upload-time = "2026-01-14T12:55:48.45Z" }, - { url = "https://files.pythonhosted.org/packages/96/79/108df2cfc4e672336765d54e3ff887294c1cc36ea4335c73588875775527/librt-0.7.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:54feb7b4f2f6706bb82325e836a01be805770443e2400f706e824e91f6441dde", size = 61008, upload-time = "2026-01-14T12:55:49.527Z" }, - { url = "https://files.pythonhosted.org/packages/46/f2/30179898f9994a5637459d6e169b6abdc982012c0a4b2d4c26f50c06f911/librt-0.7.8-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39a4c76fee41007070f872b648cc2f711f9abf9a13d0c7162478043377b52c8e", size = 187199, upload-time = "2026-01-14T12:55:50.587Z" }, - { url = "https://files.pythonhosted.org/packages/b4/da/f7563db55cebdc884f518ba3791ad033becc25ff68eb70902b1747dc0d70/librt-0.7.8-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac9c8a458245c7de80bc1b9765b177055efff5803f08e548dd4bb9ab9a8d789b", size = 198317, upload-time = "2026-01-14T12:55:51.991Z" }, - { url = "https://files.pythonhosted.org/packages/b3/6c/4289acf076ad371471fa86718c30ae353e690d3de6167f7db36f429272f1/librt-0.7.8-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b67aa7eff150f075fda09d11f6bfb26edffd300f6ab1666759547581e8f666", size = 210334, upload-time = "2026-01-14T12:55:53.682Z" }, - { url = "https://files.pythonhosted.org/packages/4a/7f/377521ac25b78ac0a5ff44127a0360ee6d5ddd3ce7327949876a30533daa/librt-0.7.8-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:535929b6eff670c593c34ff435d5440c3096f20fa72d63444608a5aef64dd581", size = 211031, upload-time = "2026-01-14T12:55:54.827Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b1/e1e96c3e20b23d00cf90f4aad48f0deb4cdfec2f0ed8380d0d85acf98bbf/librt-0.7.8-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63937bd0f4d1cb56653dc7ae900d6c52c41f0015e25aaf9902481ee79943b33a", size = 204581, upload-time = "2026-01-14T12:55:56.811Z" }, - { url = "https://files.pythonhosted.org/packages/43/71/0f5d010e92ed9747e14bef35e91b6580533510f1e36a8a09eb79ee70b2f0/librt-0.7.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf243da9e42d914036fd362ac3fa77d80a41cadcd11ad789b1b5eec4daaf67ca", size = 224731, upload-time = "2026-01-14T12:55:58.175Z" }, - { url = "https://files.pythonhosted.org/packages/22/f0/07fb6ab5c39a4ca9af3e37554f9d42f25c464829254d72e4ebbd81da351c/librt-0.7.8-cp314-cp314t-win32.whl", hash = "sha256:171ca3a0a06c643bd0a2f62a8944e1902c94aa8e5da4db1ea9a8daf872685365", size = 41173, upload-time = "2026-01-14T12:55:59.315Z" }, - { url = "https://files.pythonhosted.org/packages/24/d4/7e4be20993dc6a782639625bd2f97f3c66125c7aa80c82426956811cfccf/librt-0.7.8-cp314-cp314t-win_amd64.whl", hash = "sha256:445b7304145e24c60288a2f172b5ce2ca35c0f81605f5299f3fa567e189d2e32", size = 47668, upload-time = "2026-01-14T12:56:00.261Z" }, - { url = "https://files.pythonhosted.org/packages/fc/85/69f92b2a7b3c0f88ffe107c86b952b397004b5b8ea5a81da3d9c04c04422/librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06", size = 40550, upload-time = "2026-01-14T12:56:01.542Z" }, +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73", size = 177471, upload-time = "2026-02-17T16:13:06.101Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/01/0e748af5e4fee180cf7cd12bd12b0513ad23b045dccb2a83191bde82d168/librt-0.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd", size = 65315, upload-time = "2026-02-17T16:11:25.152Z" }, + { url = "https://files.pythonhosted.org/packages/9d/4d/7184806efda571887c798d573ca4134c80ac8642dcdd32f12c31b939c595/librt-0.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965", size = 68021, upload-time = "2026-02-17T16:11:26.129Z" }, + { url = "https://files.pythonhosted.org/packages/ae/88/c3c52d2a5d5101f28d3dc89298444626e7874aa904eed498464c2af17627/librt-0.8.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da", size = 194500, upload-time = "2026-02-17T16:11:27.177Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5d/6fb0a25b6a8906e85b2c3b87bee1d6ed31510be7605b06772f9374ca5cb3/librt-0.8.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0", size = 205622, upload-time = "2026-02-17T16:11:28.242Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a6/8006ae81227105476a45691f5831499e4d936b1c049b0c1feb17c11b02d1/librt-0.8.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e", size = 218304, upload-time = "2026-02-17T16:11:29.344Z" }, + { url = "https://files.pythonhosted.org/packages/ee/19/60e07886ad16670aae57ef44dada41912c90906a6fe9f2b9abac21374748/librt-0.8.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3", size = 211493, upload-time = "2026-02-17T16:11:30.445Z" }, + { url = "https://files.pythonhosted.org/packages/9c/cf/f666c89d0e861d05600438213feeb818c7514d3315bae3648b1fc145d2b6/librt-0.8.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac", size = 219129, upload-time = "2026-02-17T16:11:32.021Z" }, + { url = "https://files.pythonhosted.org/packages/8f/ef/f1bea01e40b4a879364c031476c82a0dc69ce068daad67ab96302fed2d45/librt-0.8.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596", size = 213113, upload-time = "2026-02-17T16:11:33.192Z" }, + { url = "https://files.pythonhosted.org/packages/9b/80/cdab544370cc6bc1b72ea369525f547a59e6938ef6863a11ab3cd24759af/librt-0.8.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99", size = 212269, upload-time = "2026-02-17T16:11:34.373Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9c/48d6ed8dac595654f15eceab2035131c136d1ae9a1e3548e777bb6dbb95d/librt-0.8.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe", size = 234673, upload-time = "2026-02-17T16:11:36.063Z" }, + { url = "https://files.pythonhosted.org/packages/16/01/35b68b1db517f27a01be4467593292eb5315def8900afad29fabf56304ba/librt-0.8.1-cp311-cp311-win32.whl", hash = "sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb", size = 54597, upload-time = "2026-02-17T16:11:37.544Z" }, + { url = "https://files.pythonhosted.org/packages/71/02/796fe8f02822235966693f257bf2c79f40e11337337a657a8cfebba5febc/librt-0.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b", size = 61733, upload-time = "2026-02-17T16:11:38.691Z" }, + { url = "https://files.pythonhosted.org/packages/28/ad/232e13d61f879a42a4e7117d65e4984bb28371a34bb6fb9ca54ec2c8f54e/librt-0.8.1-cp311-cp311-win_arm64.whl", hash = "sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9", size = 52273, upload-time = "2026-02-17T16:11:40.308Z" }, + { url = "https://files.pythonhosted.org/packages/95/21/d39b0a87ac52fc98f621fb6f8060efb017a767ebbbac2f99fbcbc9ddc0d7/librt-0.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a", size = 66516, upload-time = "2026-02-17T16:11:41.604Z" }, + { url = "https://files.pythonhosted.org/packages/69/f1/46375e71441c43e8ae335905e069f1c54febee63a146278bcee8782c84fd/librt-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9", size = 68634, upload-time = "2026-02-17T16:11:43.268Z" }, + { url = "https://files.pythonhosted.org/packages/0a/33/c510de7f93bf1fa19e13423a606d8189a02624a800710f6e6a0a0f0784b3/librt-0.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb", size = 198941, upload-time = "2026-02-17T16:11:44.28Z" }, + { url = "https://files.pythonhosted.org/packages/dd/36/e725903416409a533d92398e88ce665476f275081d0d7d42f9c4951999e5/librt-0.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d", size = 209991, upload-time = "2026-02-17T16:11:45.462Z" }, + { url = "https://files.pythonhosted.org/packages/30/7a/8d908a152e1875c9f8eac96c97a480df425e657cdb47854b9efaa4998889/librt-0.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7", size = 224476, upload-time = "2026-02-17T16:11:46.542Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b8/a22c34f2c485b8903a06f3fe3315341fe6876ef3599792344669db98fcff/librt-0.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440", size = 217518, upload-time = "2026-02-17T16:11:47.746Z" }, + { url = "https://files.pythonhosted.org/packages/79/6f/5c6fea00357e4f82ba44f81dbfb027921f1ab10e320d4a64e1c408d035d9/librt-0.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9", size = 225116, upload-time = "2026-02-17T16:11:49.298Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a0/95ced4e7b1267fe1e2720a111685bcddf0e781f7e9e0ce59d751c44dcfe5/librt-0.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972", size = 217751, upload-time = "2026-02-17T16:11:50.49Z" }, + { url = "https://files.pythonhosted.org/packages/93/c2/0517281cb4d4101c27ab59472924e67f55e375bc46bedae94ac6dc6e1902/librt-0.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921", size = 218378, upload-time = "2026-02-17T16:11:51.783Z" }, + { url = "https://files.pythonhosted.org/packages/43/e8/37b3ac108e8976888e559a7b227d0ceac03c384cfd3e7a1c2ee248dbae79/librt-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0", size = 241199, upload-time = "2026-02-17T16:11:53.561Z" }, + { url = "https://files.pythonhosted.org/packages/4b/5b/35812d041c53967fedf551a39399271bbe4257e681236a2cf1a69c8e7fa1/librt-0.8.1-cp312-cp312-win32.whl", hash = "sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a", size = 54917, upload-time = "2026-02-17T16:11:54.758Z" }, + { url = "https://files.pythonhosted.org/packages/de/d1/fa5d5331b862b9775aaf2a100f5ef86854e5d4407f71bddf102f4421e034/librt-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444", size = 62017, upload-time = "2026-02-17T16:11:55.748Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7c/c614252f9acda59b01a66e2ddfd243ed1c7e1deab0293332dfbccf862808/librt-0.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d", size = 52441, upload-time = "2026-02-17T16:11:56.801Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3c/f614c8e4eaac7cbf2bbdf9528790b21d89e277ee20d57dc6e559c626105f/librt-0.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35", size = 66529, upload-time = "2026-02-17T16:11:57.809Z" }, + { url = "https://files.pythonhosted.org/packages/ab/96/5836544a45100ae411eda07d29e3d99448e5258b6e9c8059deb92945f5c2/librt-0.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583", size = 68669, upload-time = "2026-02-17T16:11:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/06/53/f0b992b57af6d5531bf4677d75c44f095f2366a1741fb695ee462ae04b05/librt-0.8.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c", size = 199279, upload-time = "2026-02-17T16:11:59.862Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ad/4848cc16e268d14280d8168aee4f31cea92bbd2b79ce33d3e166f2b4e4fc/librt-0.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04", size = 210288, upload-time = "2026-02-17T16:12:00.954Z" }, + { url = "https://files.pythonhosted.org/packages/52/05/27fdc2e95de26273d83b96742d8d3b7345f2ea2bdbd2405cc504644f2096/librt-0.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363", size = 224809, upload-time = "2026-02-17T16:12:02.108Z" }, + { url = "https://files.pythonhosted.org/packages/7a/d0/78200a45ba3240cb042bc597d6f2accba9193a2c57d0356268cbbe2d0925/librt-0.8.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0", size = 218075, upload-time = "2026-02-17T16:12:03.631Z" }, + { url = "https://files.pythonhosted.org/packages/af/72/a210839fa74c90474897124c064ffca07f8d4b347b6574d309686aae7ca6/librt-0.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012", size = 225486, upload-time = "2026-02-17T16:12:04.725Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c1/a03cc63722339ddbf087485f253493e2b013039f5b707e8e6016141130fa/librt-0.8.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb", size = 218219, upload-time = "2026-02-17T16:12:05.828Z" }, + { url = "https://files.pythonhosted.org/packages/58/f5/fff6108af0acf941c6f274a946aea0e484bd10cd2dc37610287ce49388c5/librt-0.8.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b", size = 218750, upload-time = "2026-02-17T16:12:07.09Z" }, + { url = "https://files.pythonhosted.org/packages/71/67/5a387bfef30ec1e4b4f30562c8586566faf87e47d696768c19feb49e3646/librt-0.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d", size = 241624, upload-time = "2026-02-17T16:12:08.43Z" }, + { url = "https://files.pythonhosted.org/packages/d4/be/24f8502db11d405232ac1162eb98069ca49c3306c1d75c6ccc61d9af8789/librt-0.8.1-cp313-cp313-win32.whl", hash = "sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a", size = 54969, upload-time = "2026-02-17T16:12:09.633Z" }, + { url = "https://files.pythonhosted.org/packages/5c/73/c9fdf6cb2a529c1a092ce769a12d88c8cca991194dfe641b6af12fa964d2/librt-0.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79", size = 62000, upload-time = "2026-02-17T16:12:10.632Z" }, + { url = "https://files.pythonhosted.org/packages/d3/97/68f80ca3ac4924f250cdfa6e20142a803e5e50fca96ef5148c52ee8c10ea/librt-0.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0", size = 52495, upload-time = "2026-02-17T16:12:11.633Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6a/907ef6800f7bca71b525a05f1839b21f708c09043b1c6aa77b6b827b3996/librt-0.8.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f", size = 66081, upload-time = "2026-02-17T16:12:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/1b/18/25e991cd5640c9fb0f8d91b18797b29066b792f17bf8493da183bf5caabe/librt-0.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c", size = 68309, upload-time = "2026-02-17T16:12:13.756Z" }, + { url = "https://files.pythonhosted.org/packages/a4/36/46820d03f058cfb5a9de5940640ba03165ed8aded69e0733c417bb04df34/librt-0.8.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc", size = 196804, upload-time = "2026-02-17T16:12:14.818Z" }, + { url = "https://files.pythonhosted.org/packages/59/18/5dd0d3b87b8ff9c061849fbdb347758d1f724b9a82241aa908e0ec54ccd0/librt-0.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c", size = 206907, upload-time = "2026-02-17T16:12:16.513Z" }, + { url = "https://files.pythonhosted.org/packages/d1/96/ef04902aad1424fd7299b62d1890e803e6ab4018c3044dca5922319c4b97/librt-0.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3", size = 221217, upload-time = "2026-02-17T16:12:17.906Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ff/7e01f2dda84a8f5d280637a2e5827210a8acca9a567a54507ef1c75b342d/librt-0.8.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14", size = 214622, upload-time = "2026-02-17T16:12:19.108Z" }, + { url = "https://files.pythonhosted.org/packages/1e/8c/5b093d08a13946034fed57619742f790faf77058558b14ca36a6e331161e/librt-0.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7", size = 221987, upload-time = "2026-02-17T16:12:20.331Z" }, + { url = "https://files.pythonhosted.org/packages/d3/cc/86b0b3b151d40920ad45a94ce0171dec1aebba8a9d72bb3fa00c73ab25dd/librt-0.8.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6", size = 215132, upload-time = "2026-02-17T16:12:21.54Z" }, + { url = "https://files.pythonhosted.org/packages/fc/be/8588164a46edf1e69858d952654e216a9a91174688eeefb9efbb38a9c799/librt-0.8.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071", size = 215195, upload-time = "2026-02-17T16:12:23.073Z" }, + { url = "https://files.pythonhosted.org/packages/f5/f2/0b9279bea735c734d69344ecfe056c1ba211694a72df10f568745c899c76/librt-0.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78", size = 237946, upload-time = "2026-02-17T16:12:24.275Z" }, + { url = "https://files.pythonhosted.org/packages/e9/cc/5f2a34fbc8aeb35314a3641f9956fa9051a947424652fad9882be7a97949/librt-0.8.1-cp314-cp314-win32.whl", hash = "sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023", size = 50689, upload-time = "2026-02-17T16:12:25.766Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/cd4d010ab2147339ca2b93e959c3686e964edc6de66ddacc935c325883d7/librt-0.8.1-cp314-cp314-win_amd64.whl", hash = "sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730", size = 57875, upload-time = "2026-02-17T16:12:27.465Z" }, + { url = "https://files.pythonhosted.org/packages/84/0f/2143cb3c3ca48bd3379dcd11817163ca50781927c4537345d608b5045998/librt-0.8.1-cp314-cp314-win_arm64.whl", hash = "sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3", size = 48058, upload-time = "2026-02-17T16:12:28.556Z" }, + { url = "https://files.pythonhosted.org/packages/d2/0e/9b23a87e37baf00311c3efe6b48d6b6c168c29902dfc3f04c338372fd7db/librt-0.8.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1", size = 68313, upload-time = "2026-02-17T16:12:29.659Z" }, + { url = "https://files.pythonhosted.org/packages/db/9a/859c41e5a4f1c84200a7d2b92f586aa27133c8243b6cac9926f6e54d01b9/librt-0.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee", size = 70994, upload-time = "2026-02-17T16:12:31.516Z" }, + { url = "https://files.pythonhosted.org/packages/4c/28/10605366ee599ed34223ac2bf66404c6fb59399f47108215d16d5ad751a8/librt-0.8.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7", size = 220770, upload-time = "2026-02-17T16:12:33.294Z" }, + { url = "https://files.pythonhosted.org/packages/af/8d/16ed8fd452dafae9c48d17a6bc1ee3e818fd40ef718d149a8eff2c9f4ea2/librt-0.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040", size = 235409, upload-time = "2026-02-17T16:12:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/89/1b/7bdf3e49349c134b25db816e4a3db6b94a47ac69d7d46b1e682c2c4949be/librt-0.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e", size = 246473, upload-time = "2026-02-17T16:12:36.656Z" }, + { url = "https://files.pythonhosted.org/packages/4e/8a/91fab8e4fd2a24930a17188c7af5380eb27b203d72101c9cc000dbdfd95a/librt-0.8.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732", size = 238866, upload-time = "2026-02-17T16:12:37.849Z" }, + { url = "https://files.pythonhosted.org/packages/b9/e0/c45a098843fc7c07e18a7f8a24ca8496aecbf7bdcd54980c6ca1aaa79a8e/librt-0.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624", size = 250248, upload-time = "2026-02-17T16:12:39.445Z" }, + { url = "https://files.pythonhosted.org/packages/82/30/07627de23036640c952cce0c1fe78972e77d7d2f8fd54fa5ef4554ff4a56/librt-0.8.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4", size = 240629, upload-time = "2026-02-17T16:12:40.889Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c1/55bfe1ee3542eba055616f9098eaf6eddb966efb0ca0f44eaa4aba327307/librt-0.8.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382", size = 239615, upload-time = "2026-02-17T16:12:42.446Z" }, + { url = "https://files.pythonhosted.org/packages/2b/39/191d3d28abc26c9099b19852e6c99f7f6d400b82fa5a4e80291bd3803e19/librt-0.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994", size = 263001, upload-time = "2026-02-17T16:12:43.627Z" }, + { url = "https://files.pythonhosted.org/packages/b9/eb/7697f60fbe7042ab4e88f4ee6af496b7f222fffb0a4e3593ef1f29f81652/librt-0.8.1-cp314-cp314t-win32.whl", hash = "sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a", size = 51328, upload-time = "2026-02-17T16:12:45.148Z" }, + { url = "https://files.pythonhosted.org/packages/7c/72/34bf2eb7a15414a23e5e70ecb9440c1d3179f393d9349338a91e2781c0fb/librt-0.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4", size = 58722, upload-time = "2026-02-17T16:12:46.85Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c8/d148e041732d631fc76036f8b30fae4e77b027a1e95b7a84bb522481a940/librt-0.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61", size = 48755, upload-time = "2026-02-17T16:12:47.943Z" }, ] [[package]] @@ -1203,11 +1254,11 @@ wheels = [ [[package]] name = "narwhals" -version = "2.15.0" +version = "2.18.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/6d/b57c64e5038a8cf071bce391bb11551657a74558877ac961e7fa905ece27/narwhals-2.15.0.tar.gz", hash = "sha256:a9585975b99d95084268445a1fdd881311fa26ef1caa18020d959d5b2ff9a965", size = 603479, upload-time = "2026-01-06T08:10:13.27Z" } +sdist = { url = "https://files.pythonhosted.org/packages/59/96/45218c2fdec4c9f22178f905086e85ef1a6d63862dcc3cd68eb60f1867f5/narwhals-2.18.1.tar.gz", hash = "sha256:652a1fcc9d432bbf114846688884c215f17eb118aa640b7419295d2f910d2a8b", size = 620578, upload-time = "2026-03-24T15:11:25.456Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.whl", hash = "sha256:cbfe21ca19d260d9fd67f995ec75c44592d1f106933b03ddd375df7ac841f9d6", size = 432856, upload-time = "2026-01-06T08:10:11.511Z" }, + { url = "https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl", hash = "sha256:a0a8bb80205323851338888ba3a12b4f65d352362c8a94be591244faf36504ad", size = 444952, upload-time = "2026-03-24T15:11:23.801Z" }, ] [[package]] @@ -1236,111 +1287,81 @@ wheels = [ [[package]] name = "numpy" -version = "2.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/62/ae72ff66c0f1fd959925b4c11f8c2dea61f47f6acaea75a08512cdfe3fed/numpy-2.4.1.tar.gz", hash = "sha256:a1ceafc5042451a858231588a104093474c6a5c57dcc724841f5c888d237d690", size = 20721320, upload-time = "2026-01-10T06:44:59.619Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/34/2b1bc18424f3ad9af577f6ce23600319968a70575bd7db31ce66731bbef9/numpy-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0cce2a669e3c8ba02ee563c7835f92c153cf02edff1ae05e1823f1dde21b16a5", size = 16944563, upload-time = "2026-01-10T06:42:14.615Z" }, - { url = "https://files.pythonhosted.org/packages/2c/57/26e5f97d075aef3794045a6ca9eada6a4ed70eb9a40e7a4a93f9ac80d704/numpy-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:899d2c18024984814ac7e83f8f49d8e8180e2fbe1b2e252f2e7f1d06bea92425", size = 12645658, upload-time = "2026-01-10T06:42:17.298Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ba/80fc0b1e3cb2fd5c6143f00f42eb67762aa043eaa05ca924ecc3222a7849/numpy-2.4.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:09aa8a87e45b55a1c2c205d42e2808849ece5c484b2aab11fecabec3841cafba", size = 5474132, upload-time = "2026-01-10T06:42:19.637Z" }, - { url = "https://files.pythonhosted.org/packages/40/ae/0a5b9a397f0e865ec171187c78d9b57e5588afc439a04ba9cab1ebb2c945/numpy-2.4.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:edee228f76ee2dab4579fad6f51f6a305de09d444280109e0f75df247ff21501", size = 6804159, upload-time = "2026-01-10T06:42:21.44Z" }, - { url = "https://files.pythonhosted.org/packages/86/9c/841c15e691c7085caa6fd162f063eff494099c8327aeccd509d1ab1e36ab/numpy-2.4.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a92f227dbcdc9e4c3e193add1a189a9909947d4f8504c576f4a732fd0b54240a", size = 14708058, upload-time = "2026-01-10T06:42:23.546Z" }, - { url = "https://files.pythonhosted.org/packages/5d/9d/7862db06743f489e6a502a3b93136d73aea27d97b2cf91504f70a27501d6/numpy-2.4.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:538bf4ec353709c765ff75ae616c34d3c3dca1a68312727e8f2676ea644f8509", size = 16651501, upload-time = "2026-01-10T06:42:25.909Z" }, - { url = "https://files.pythonhosted.org/packages/a6/9c/6fc34ebcbd4015c6e5f0c0ce38264010ce8a546cb6beacb457b84a75dfc8/numpy-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ac08c63cb7779b85e9d5318e6c3518b424bc1f364ac4cb2c6136f12e5ff2dccc", size = 16492627, upload-time = "2026-01-10T06:42:28.938Z" }, - { url = "https://files.pythonhosted.org/packages/aa/63/2494a8597502dacda439f61b3c0db4da59928150e62be0e99395c3ad23c5/numpy-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f9c360ecef085e5841c539a9a12b883dff005fbd7ce46722f5e9cef52634d82", size = 18585052, upload-time = "2026-01-10T06:42:31.312Z" }, - { url = "https://files.pythonhosted.org/packages/6a/93/098e1162ae7522fc9b618d6272b77404c4656c72432ecee3abc029aa3de0/numpy-2.4.1-cp311-cp311-win32.whl", hash = "sha256:0f118ce6b972080ba0758c6087c3617b5ba243d806268623dc34216d69099ba0", size = 6236575, upload-time = "2026-01-10T06:42:33.872Z" }, - { url = "https://files.pythonhosted.org/packages/8c/de/f5e79650d23d9e12f38a7bc6b03ea0835b9575494f8ec94c11c6e773b1b1/numpy-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:18e14c4d09d55eef39a6ab5b08406e84bc6869c1e34eef45564804f90b7e0574", size = 12604479, upload-time = "2026-01-10T06:42:35.778Z" }, - { url = "https://files.pythonhosted.org/packages/dd/65/e1097a7047cff12ce3369bd003811516b20ba1078dbdec135e1cd7c16c56/numpy-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:6461de5113088b399d655d45c3897fa188766415d0f568f175ab071c8873bd73", size = 10578325, upload-time = "2026-01-10T06:42:38.518Z" }, - { url = "https://files.pythonhosted.org/packages/78/7f/ec53e32bf10c813604edf07a3682616bd931d026fcde7b6d13195dfb684a/numpy-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d3703409aac693fa82c0aee023a1ae06a6e9d065dba10f5e8e80f642f1e9d0a2", size = 16656888, upload-time = "2026-01-10T06:42:40.913Z" }, - { url = "https://files.pythonhosted.org/packages/b8/e0/1f9585d7dae8f14864e948fd7fa86c6cb72dee2676ca2748e63b1c5acfe0/numpy-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7211b95ca365519d3596a1d8688a95874cc94219d417504d9ecb2df99fa7bfa8", size = 12373956, upload-time = "2026-01-10T06:42:43.091Z" }, - { url = "https://files.pythonhosted.org/packages/8e/43/9762e88909ff2326f5e7536fa8cb3c49fb03a7d92705f23e6e7f553d9cb3/numpy-2.4.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5adf01965456a664fc727ed69cc71848f28d063217c63e1a0e200a118d5eec9a", size = 5202567, upload-time = "2026-01-10T06:42:45.107Z" }, - { url = "https://files.pythonhosted.org/packages/4b/ee/34b7930eb61e79feb4478800a4b95b46566969d837546aa7c034c742ef98/numpy-2.4.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:26f0bcd9c79a00e339565b303badc74d3ea2bd6d52191eeca5f95936cad107d0", size = 6549459, upload-time = "2026-01-10T06:42:48.152Z" }, - { url = "https://files.pythonhosted.org/packages/79/e3/5f115fae982565771be994867c89bcd8d7208dbfe9469185497d70de5ddf/numpy-2.4.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0093e85df2960d7e4049664b26afc58b03236e967fb942354deef3208857a04c", size = 14404859, upload-time = "2026-01-10T06:42:49.947Z" }, - { url = "https://files.pythonhosted.org/packages/d9/7d/9c8a781c88933725445a859cac5d01b5871588a15969ee6aeb618ba99eee/numpy-2.4.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ad270f438cbdd402c364980317fb6b117d9ec5e226fff5b4148dd9aa9fc6e02", size = 16371419, upload-time = "2026-01-10T06:42:52.409Z" }, - { url = "https://files.pythonhosted.org/packages/a6/d2/8aa084818554543f17cf4162c42f162acbd3bb42688aefdba6628a859f77/numpy-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:297c72b1b98100c2e8f873d5d35fb551fce7040ade83d67dd51d38c8d42a2162", size = 16182131, upload-time = "2026-01-10T06:42:54.694Z" }, - { url = "https://files.pythonhosted.org/packages/60/db/0425216684297c58a8df35f3284ef56ec4a043e6d283f8a59c53562caf1b/numpy-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf6470d91d34bf669f61d515499859fa7a4c2f7c36434afb70e82df7217933f9", size = 18295342, upload-time = "2026-01-10T06:42:56.991Z" }, - { url = "https://files.pythonhosted.org/packages/31/4c/14cb9d86240bd8c386c881bafbe43f001284b7cce3bc01623ac9475da163/numpy-2.4.1-cp312-cp312-win32.whl", hash = "sha256:b6bcf39112e956594b3331316d90c90c90fb961e39696bda97b89462f5f3943f", size = 5959015, upload-time = "2026-01-10T06:42:59.631Z" }, - { url = "https://files.pythonhosted.org/packages/51/cf/52a703dbeb0c65807540d29699fef5fda073434ff61846a564d5c296420f/numpy-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:e1a27bb1b2dee45a2a53f5ca6ff2d1a7f135287883a1689e930d44d1ff296c87", size = 12310730, upload-time = "2026-01-10T06:43:01.627Z" }, - { url = "https://files.pythonhosted.org/packages/69/80/a828b2d0ade5e74a9fe0f4e0a17c30fdc26232ad2bc8c9f8b3197cf7cf18/numpy-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:0e6e8f9d9ecf95399982019c01223dc130542960a12edfa8edd1122dfa66a8a8", size = 10312166, upload-time = "2026-01-10T06:43:03.673Z" }, - { url = "https://files.pythonhosted.org/packages/04/68/732d4b7811c00775f3bd522a21e8dd5a23f77eb11acdeb663e4a4ebf0ef4/numpy-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d797454e37570cfd61143b73b8debd623c3c0952959adb817dd310a483d58a1b", size = 16652495, upload-time = "2026-01-10T06:43:06.283Z" }, - { url = "https://files.pythonhosted.org/packages/20/ca/857722353421a27f1465652b2c66813eeeccea9d76d5f7b74b99f298e60e/numpy-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c55962006156aeef1629b953fd359064aa47e4d82cfc8e67f0918f7da3344f", size = 12368657, upload-time = "2026-01-10T06:43:09.094Z" }, - { url = "https://files.pythonhosted.org/packages/81/0d/2377c917513449cc6240031a79d30eb9a163d32a91e79e0da47c43f2c0c8/numpy-2.4.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:71abbea030f2cfc3092a0ff9f8c8fdefdc5e0bf7d9d9c99663538bb0ecdac0b9", size = 5197256, upload-time = "2026-01-10T06:43:13.634Z" }, - { url = "https://files.pythonhosted.org/packages/17/39/569452228de3f5de9064ac75137082c6214be1f5c532016549a7923ab4b5/numpy-2.4.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5b55aa56165b17aaf15520beb9cbd33c9039810e0d9643dd4379e44294c7303e", size = 6545212, upload-time = "2026-01-10T06:43:15.661Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a4/77333f4d1e4dac4395385482557aeecf4826e6ff517e32ca48e1dafbe42a/numpy-2.4.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0faba4a331195bfa96f93dd9dfaa10b2c7aa8cda3a02b7fd635e588fe821bf5", size = 14402871, upload-time = "2026-01-10T06:43:17.324Z" }, - { url = "https://files.pythonhosted.org/packages/ba/87/d341e519956273b39d8d47969dd1eaa1af740615394fe67d06f1efa68773/numpy-2.4.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e3087f53e2b4428766b54932644d148613c5a595150533ae7f00dab2f319a8", size = 16359305, upload-time = "2026-01-10T06:43:19.376Z" }, - { url = "https://files.pythonhosted.org/packages/32/91/789132c6666288eaa20ae8066bb99eba1939362e8f1a534949a215246e97/numpy-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:49e792ec351315e16da54b543db06ca8a86985ab682602d90c60ef4ff4db2a9c", size = 16181909, upload-time = "2026-01-10T06:43:21.808Z" }, - { url = "https://files.pythonhosted.org/packages/cf/b8/090b8bd27b82a844bb22ff8fdf7935cb1980b48d6e439ae116f53cdc2143/numpy-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79e9e06c4c2379db47f3f6fc7a8652e7498251789bf8ff5bd43bf478ef314ca2", size = 18284380, upload-time = "2026-01-10T06:43:23.957Z" }, - { url = "https://files.pythonhosted.org/packages/67/78/722b62bd31842ff029412271556a1a27a98f45359dea78b1548a3a9996aa/numpy-2.4.1-cp313-cp313-win32.whl", hash = "sha256:3d1a100e48cb266090a031397863ff8a30050ceefd798f686ff92c67a486753d", size = 5957089, upload-time = "2026-01-10T06:43:27.535Z" }, - { url = "https://files.pythonhosted.org/packages/da/a6/cf32198b0b6e18d4fbfa9a21a992a7fca535b9bb2b0cdd217d4a3445b5ca/numpy-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:92a0e65272fd60bfa0d9278e0484c2f52fe03b97aedc02b357f33fe752c52ffb", size = 12307230, upload-time = "2026-01-10T06:43:29.298Z" }, - { url = "https://files.pythonhosted.org/packages/44/6c/534d692bfb7d0afe30611320c5fb713659dcb5104d7cc182aff2aea092f5/numpy-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:20d4649c773f66cc2fc36f663e091f57c3b7655f936a4c681b4250855d1da8f5", size = 10313125, upload-time = "2026-01-10T06:43:31.782Z" }, - { url = "https://files.pythonhosted.org/packages/da/a1/354583ac5c4caa566de6ddfbc42744409b515039e085fab6e0ff942e0df5/numpy-2.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f93bc6892fe7b0663e5ffa83b61aab510aacffd58c16e012bb9352d489d90cb7", size = 12496156, upload-time = "2026-01-10T06:43:34.237Z" }, - { url = "https://files.pythonhosted.org/packages/51/b0/42807c6e8cce58c00127b1dc24d365305189991f2a7917aa694a109c8d7d/numpy-2.4.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:178de8f87948163d98a4c9ab5bee4ce6519ca918926ec8df195af582de28544d", size = 5324663, upload-time = "2026-01-10T06:43:36.211Z" }, - { url = "https://files.pythonhosted.org/packages/fe/55/7a621694010d92375ed82f312b2f28017694ed784775269115323e37f5e2/numpy-2.4.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:98b35775e03ab7f868908b524fc0a84d38932d8daf7b7e1c3c3a1b6c7a2c9f15", size = 6645224, upload-time = "2026-01-10T06:43:37.884Z" }, - { url = "https://files.pythonhosted.org/packages/50/96/9fa8635ed9d7c847d87e30c834f7109fac5e88549d79ef3324ab5c20919f/numpy-2.4.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941c2a93313d030f219f3a71fd3d91a728b82979a5e8034eb2e60d394a2b83f9", size = 14462352, upload-time = "2026-01-10T06:43:39.479Z" }, - { url = "https://files.pythonhosted.org/packages/03/d1/8cf62d8bb2062da4fb82dd5d49e47c923f9c0738032f054e0a75342faba7/numpy-2.4.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:529050522e983e00a6c1c6b67411083630de8b57f65e853d7b03d9281b8694d2", size = 16407279, upload-time = "2026-01-10T06:43:41.93Z" }, - { url = "https://files.pythonhosted.org/packages/86/1c/95c86e17c6b0b31ce6ef219da00f71113b220bcb14938c8d9a05cee0ff53/numpy-2.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2302dc0224c1cbc49bb94f7064f3f923a971bfae45c33870dcbff63a2a550505", size = 16248316, upload-time = "2026-01-10T06:43:44.121Z" }, - { url = "https://files.pythonhosted.org/packages/30/b4/e7f5ff8697274c9d0fa82398b6a372a27e5cef069b37df6355ccb1f1db1a/numpy-2.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9171a42fcad32dcf3fa86f0a4faa5e9f8facefdb276f54b8b390d90447cff4e2", size = 18329884, upload-time = "2026-01-10T06:43:46.613Z" }, - { url = "https://files.pythonhosted.org/packages/37/a4/b073f3e9d77f9aec8debe8ca7f9f6a09e888ad1ba7488f0c3b36a94c03ac/numpy-2.4.1-cp313-cp313t-win32.whl", hash = "sha256:382ad67d99ef49024f11d1ce5dcb5ad8432446e4246a4b014418ba3a1175a1f4", size = 6081138, upload-time = "2026-01-10T06:43:48.854Z" }, - { url = "https://files.pythonhosted.org/packages/16/16/af42337b53844e67752a092481ab869c0523bc95c4e5c98e4dac4e9581ac/numpy-2.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:62fea415f83ad8fdb6c20840578e5fbaf5ddd65e0ec6c3c47eda0f69da172510", size = 12447478, upload-time = "2026-01-10T06:43:50.476Z" }, - { url = "https://files.pythonhosted.org/packages/6c/f8/fa85b2eac68ec631d0b631abc448552cb17d39afd17ec53dcbcc3537681a/numpy-2.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a7870e8c5fc11aef57d6fea4b4085e537a3a60ad2cdd14322ed531fdca68d261", size = 10382981, upload-time = "2026-01-10T06:43:52.575Z" }, - { url = "https://files.pythonhosted.org/packages/1b/a7/ef08d25698e0e4b4efbad8d55251d20fe2a15f6d9aa7c9b30cd03c165e6f/numpy-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3869ea1ee1a1edc16c29bbe3a2f2a4e515cc3a44d43903ad41e0cacdbaf733dc", size = 16652046, upload-time = "2026-01-10T06:43:54.797Z" }, - { url = "https://files.pythonhosted.org/packages/8f/39/e378b3e3ca13477e5ac70293ec027c438d1927f18637e396fe90b1addd72/numpy-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e867df947d427cdd7a60e3e271729090b0f0df80f5f10ab7dd436f40811699c3", size = 12378858, upload-time = "2026-01-10T06:43:57.099Z" }, - { url = "https://files.pythonhosted.org/packages/c3/74/7ec6154f0006910ed1fdbb7591cf4432307033102b8a22041599935f8969/numpy-2.4.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:e3bd2cb07841166420d2fa7146c96ce00cb3410664cbc1a6be028e456c4ee220", size = 5207417, upload-time = "2026-01-10T06:43:59.037Z" }, - { url = "https://files.pythonhosted.org/packages/f7/b7/053ac11820d84e42f8feea5cb81cc4fcd1091499b45b1ed8c7415b1bf831/numpy-2.4.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:f0a90aba7d521e6954670550e561a4cb925713bd944445dbe9e729b71f6cabee", size = 6542643, upload-time = "2026-01-10T06:44:01.852Z" }, - { url = "https://files.pythonhosted.org/packages/c0/c4/2e7908915c0e32ca636b92e4e4a3bdec4cb1e7eb0f8aedf1ed3c68a0d8cd/numpy-2.4.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d558123217a83b2d1ba316b986e9248a1ed1971ad495963d555ccd75dcb1556", size = 14418963, upload-time = "2026-01-10T06:44:04.047Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c0/3ed5083d94e7ffd7c404e54619c088e11f2e1939a9544f5397f4adb1b8ba/numpy-2.4.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f44de05659b67d20499cbc96d49f2650769afcb398b79b324bb6e297bfe3844", size = 16363811, upload-time = "2026-01-10T06:44:06.207Z" }, - { url = "https://files.pythonhosted.org/packages/0e/68/42b66f1852bf525050a67315a4fb94586ab7e9eaa541b1bef530fab0c5dd/numpy-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:69e7419c9012c4aaf695109564e3387f1259f001b4326dfa55907b098af082d3", size = 16197643, upload-time = "2026-01-10T06:44:08.33Z" }, - { url = "https://files.pythonhosted.org/packages/d2/40/e8714fc933d85f82c6bfc7b998a0649ad9769a32f3494ba86598aaf18a48/numpy-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2ffd257026eb1b34352e749d7cc1678b5eeec3e329ad8c9965a797e08ccba205", size = 18289601, upload-time = "2026-01-10T06:44:10.841Z" }, - { url = "https://files.pythonhosted.org/packages/80/9a/0d44b468cad50315127e884802351723daca7cf1c98d102929468c81d439/numpy-2.4.1-cp314-cp314-win32.whl", hash = "sha256:727c6c3275ddefa0dc078524a85e064c057b4f4e71ca5ca29a19163c607be745", size = 6005722, upload-time = "2026-01-10T06:44:13.332Z" }, - { url = "https://files.pythonhosted.org/packages/7e/bb/c6513edcce5a831810e2dddc0d3452ce84d208af92405a0c2e58fd8e7881/numpy-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:7d5d7999df434a038d75a748275cd6c0094b0ecdb0837342b332a82defc4dc4d", size = 12438590, upload-time = "2026-01-10T06:44:15.006Z" }, - { url = "https://files.pythonhosted.org/packages/e9/da/a598d5cb260780cf4d255102deba35c1d072dc028c4547832f45dd3323a8/numpy-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:ce9ce141a505053b3c7bce3216071f3bf5c182b8b28930f14cd24d43932cd2df", size = 10596180, upload-time = "2026-01-10T06:44:17.386Z" }, - { url = "https://files.pythonhosted.org/packages/de/bc/ea3f2c96fcb382311827231f911723aeff596364eb6e1b6d1d91128aa29b/numpy-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4e53170557d37ae404bf8d542ca5b7c629d6efa1117dac6a83e394142ea0a43f", size = 12498774, upload-time = "2026-01-10T06:44:19.467Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ab/ef9d939fe4a812648c7a712610b2ca6140b0853c5efea361301006c02ae5/numpy-2.4.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:a73044b752f5d34d4232f25f18160a1cc418ea4507f5f11e299d8ac36875f8a0", size = 5327274, upload-time = "2026-01-10T06:44:23.189Z" }, - { url = "https://files.pythonhosted.org/packages/bd/31/d381368e2a95c3b08b8cf7faac6004849e960f4a042d920337f71cef0cae/numpy-2.4.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:fb1461c99de4d040666ca0444057b06541e5642f800b71c56e6ea92d6a853a0c", size = 6648306, upload-time = "2026-01-10T06:44:25.012Z" }, - { url = "https://files.pythonhosted.org/packages/c8/e5/0989b44ade47430be6323d05c23207636d67d7362a1796ccbccac6773dd2/numpy-2.4.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423797bdab2eeefbe608d7c1ec7b2b4fd3c58d51460f1ee26c7500a1d9c9ee93", size = 14464653, upload-time = "2026-01-10T06:44:26.706Z" }, - { url = "https://files.pythonhosted.org/packages/10/a7/cfbe475c35371cae1358e61f20c5f075badc18c4797ab4354140e1d283cf/numpy-2.4.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:52b5f61bdb323b566b528899cc7db2ba5d1015bda7ea811a8bcf3c89c331fa42", size = 16405144, upload-time = "2026-01-10T06:44:29.378Z" }, - { url = "https://files.pythonhosted.org/packages/f8/a3/0c63fe66b534888fa5177cc7cef061541064dbe2b4b60dcc60ffaf0d2157/numpy-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42d7dd5fa36d16d52a84f821eb96031836fd405ee6955dd732f2023724d0aa01", size = 16247425, upload-time = "2026-01-10T06:44:31.721Z" }, - { url = "https://files.pythonhosted.org/packages/6b/2b/55d980cfa2c93bd40ff4c290bf824d792bd41d2fe3487b07707559071760/numpy-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7b6b5e28bbd47b7532698e5db2fe1db693d84b58c254e4389d99a27bb9b8f6b", size = 18330053, upload-time = "2026-01-10T06:44:34.617Z" }, - { url = "https://files.pythonhosted.org/packages/23/12/8b5fc6b9c487a09a7957188e0943c9ff08432c65e34567cabc1623b03a51/numpy-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:5de60946f14ebe15e713a6f22850c2372fa72f4ff9a432ab44aa90edcadaa65a", size = 6152482, upload-time = "2026-01-10T06:44:36.798Z" }, - { url = "https://files.pythonhosted.org/packages/00/a5/9f8ca5856b8940492fc24fbe13c1bc34d65ddf4079097cf9e53164d094e1/numpy-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8f085da926c0d491ffff3096f91078cc97ea67e7e6b65e490bc8dcda65663be2", size = 12627117, upload-time = "2026-01-10T06:44:38.828Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0d/eca3d962f9eef265f01a8e0d20085c6dd1f443cbffc11b6dede81fd82356/numpy-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:6436cffb4f2bf26c974344439439c95e152c9a527013f26b3577be6c2ca64295", size = 10667121, upload-time = "2026-01-10T06:44:41.644Z" }, - { url = "https://files.pythonhosted.org/packages/1e/48/d86f97919e79314a1cdee4c832178763e6e98e623e123d0bada19e92c15a/numpy-2.4.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8ad35f20be147a204e28b6a0575fbf3540c5e5f802634d4258d55b1ff5facce1", size = 16822202, upload-time = "2026-01-10T06:44:43.738Z" }, - { url = "https://files.pythonhosted.org/packages/51/e9/1e62a7f77e0f37dcfb0ad6a9744e65df00242b6ea37dfafb55debcbf5b55/numpy-2.4.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8097529164c0f3e32bb89412a0905d9100bf434d9692d9fc275e18dcf53c9344", size = 12569985, upload-time = "2026-01-10T06:44:45.945Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7e/914d54f0c801342306fdcdce3e994a56476f1b818c46c47fc21ae968088c/numpy-2.4.1-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:ea66d2b41ca4a1630aae5507ee0a71647d3124d1741980138aa8f28f44dac36e", size = 5398484, upload-time = "2026-01-10T06:44:48.012Z" }, - { url = "https://files.pythonhosted.org/packages/1c/d8/9570b68584e293a33474e7b5a77ca404f1dcc655e40050a600dee81d27fb/numpy-2.4.1-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d3f8f0df9f4b8be57b3bf74a1d087fec68f927a2fab68231fdb442bf2c12e426", size = 6713216, upload-time = "2026-01-10T06:44:49.725Z" }, - { url = "https://files.pythonhosted.org/packages/33/9b/9dd6e2db8d49eb24f86acaaa5258e5f4c8ed38209a4ee9de2d1a0ca25045/numpy-2.4.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2023ef86243690c2791fd6353e5b4848eedaa88ca8a2d129f462049f6d484696", size = 14538937, upload-time = "2026-01-10T06:44:51.498Z" }, - { url = "https://files.pythonhosted.org/packages/53/87/d5bd995b0f798a37105b876350d346eea5838bd8f77ea3d7a48392f3812b/numpy-2.4.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8361ea4220d763e54cff2fbe7d8c93526b744f7cd9ddab47afeff7e14e8503be", size = 16479830, upload-time = "2026-01-10T06:44:53.931Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c7/b801bf98514b6ae6475e941ac05c58e6411dd863ea92916bfd6d510b08c1/numpy-2.4.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:4f1b68ff47680c2925f8063402a693ede215f0257f02596b1318ecdfb1d79e33", size = 12492579, upload-time = "2026-01-10T06:44:57.094Z" }, -] - -[[package]] -name = "numpy-typing-compat" -version = "20251206.2.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/5f/29fd5f29b0a5d96e2def96ecba3112fc330ecd16e8c97c2b332563c5e201/numpy_typing_compat-20251206.2.4.tar.gz", hash = "sha256:59882d23aaff054a2536da80564012cdce33487657be4d79c5925bb8705fcabc", size = 5011, upload-time = "2025-12-06T20:02:04.942Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/7c/5c2892e6bc0628a2ccf4e938e1e2db22794657ccb374672d66e20d73839e/numpy_typing_compat-20251206.2.4-py3-none-any.whl", hash = "sha256:a82e723bd20efaa4cf2886709d4264c144f1f2b609bda83d1545113b7e47a5b5", size = 6300, upload-time = "2025-12-06T20:01:57.578Z" }, -] - -[[package]] -name = "optype" -version = "0.15.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d7/93/6b9e43138ce36fbad134bd1a50460a7bbda61105b5a964e4cf773fe4d845/optype-0.15.0.tar.gz", hash = "sha256:457d6ca9e7da19967ec16d42bdf94e240b33b5d70a56fbbf5b427e5ea39cf41e", size = 99978, upload-time = "2025-12-08T12:32:41.422Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/8b/93f6c496fc5da062fd7e7c4745b5a8dd09b7b576c626075844fe97951a7d/optype-0.15.0-py3-none-any.whl", hash = "sha256:caba40ece9ea39b499fa76c036a82e0d452a432dd4dd3e8e0d30892be2e8c76c", size = 88716, upload-time = "2025-12-08T12:32:39.669Z" }, -] - -[package.optional-dependencies] -numpy = [ - { name = "numpy" }, - { name = "numpy-typing-compat" }, +version = "2.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/8b/c265f4823726ab832de836cdd184d0986dcf94480f81e8739692a7ac7af2/numpy-2.4.3.tar.gz", hash = "sha256:483a201202b73495f00dbc83796c6ae63137a9bdade074f7648b3e32613412dd", size = 20727743, upload-time = "2026-03-09T07:58:53.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/51/5093a2df15c4dc19da3f79d1021e891f5dcf1d9d1db6ba38891d5590f3fe/numpy-2.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:33b3bf58ee84b172c067f56aeadc7ee9ab6de69c5e800ab5b10295d54c581adb", size = 16957183, upload-time = "2026-03-09T07:55:57.774Z" }, + { url = "https://files.pythonhosted.org/packages/b5/7c/c061f3de0630941073d2598dc271ac2f6cbcf5c83c74a5870fea07488333/numpy-2.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ba7b51e71c05aa1f9bc3641463cd82308eab40ce0d5c7e1fd4038cbf9938147", size = 14968734, upload-time = "2026-03-09T07:56:00.494Z" }, + { url = "https://files.pythonhosted.org/packages/ef/27/d26c85cbcd86b26e4f125b0668e7a7c0542d19dd7d23ee12e87b550e95b5/numpy-2.4.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a1988292870c7cb9d0ebb4cc96b4d447513a9644801de54606dc7aabf2b7d920", size = 5475288, upload-time = "2026-03-09T07:56:02.857Z" }, + { url = "https://files.pythonhosted.org/packages/2b/09/3c4abbc1dcd8010bf1a611d174c7aa689fc505585ec806111b4406f6f1b1/numpy-2.4.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:23b46bb6d8ecb68b58c09944483c135ae5f0e9b8d8858ece5e4ead783771d2a9", size = 6805253, upload-time = "2026-03-09T07:56:04.53Z" }, + { url = "https://files.pythonhosted.org/packages/21/bc/e7aa3f6817e40c3f517d407742337cbb8e6fc4b83ce0b55ab780c829243b/numpy-2.4.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a016db5c5dba78fa8fe9f5d80d6708f9c42ab087a739803c0ac83a43d686a470", size = 15969479, upload-time = "2026-03-09T07:56:06.638Z" }, + { url = "https://files.pythonhosted.org/packages/78/51/9f5d7a41f0b51649ddf2f2320595e15e122a40610b233d51928dd6c92353/numpy-2.4.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:715de7f82e192e8cae5a507a347d97ad17598f8e026152ca97233e3666daaa71", size = 16901035, upload-time = "2026-03-09T07:56:09.405Z" }, + { url = "https://files.pythonhosted.org/packages/64/6e/b221dd847d7181bc5ee4857bfb026182ef69499f9305eb1371cbb1aea626/numpy-2.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2ddb7919366ee468342b91dea2352824c25b55814a987847b6c52003a7c97f15", size = 17325657, upload-time = "2026-03-09T07:56:12.067Z" }, + { url = "https://files.pythonhosted.org/packages/eb/b8/8f3fd2da596e1063964b758b5e3c970aed1949a05200d7e3d46a9d46d643/numpy-2.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a315e5234d88067f2d97e1f2ef670a7569df445d55400f1e33d117418d008d52", size = 18635512, upload-time = "2026-03-09T07:56:14.629Z" }, + { url = "https://files.pythonhosted.org/packages/5c/24/2993b775c37e39d2f8ab4125b44337ab0b2ba106c100980b7c274a22bee7/numpy-2.4.3-cp311-cp311-win32.whl", hash = "sha256:2b3f8d2c4589b1a2028d2a770b0fc4d1f332fb5e01521f4de3199a896d158ddd", size = 6238100, upload-time = "2026-03-09T07:56:17.243Z" }, + { url = "https://files.pythonhosted.org/packages/76/1d/edccf27adedb754db7c4511d5eac8b83f004ae948fe2d3509e8b78097d4c/numpy-2.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:77e76d932c49a75617c6d13464e41203cd410956614d0a0e999b25e9e8d27eec", size = 12609816, upload-time = "2026-03-09T07:56:19.089Z" }, + { url = "https://files.pythonhosted.org/packages/92/82/190b99153480076c8dce85f4cfe7d53ea84444145ffa54cb58dcd460d66b/numpy-2.4.3-cp311-cp311-win_arm64.whl", hash = "sha256:eb610595dd91560905c132c709412b512135a60f1851ccbd2c959e136431ff67", size = 10485757, upload-time = "2026-03-09T07:56:21.753Z" }, + { url = "https://files.pythonhosted.org/packages/a9/ed/6388632536f9788cea23a3a1b629f25b43eaacd7d7377e5d6bc7b9deb69b/numpy-2.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:61b0cbabbb6126c8df63b9a3a0c4b1f44ebca5e12ff6997b80fcf267fb3150ef", size = 16669628, upload-time = "2026-03-09T07:56:24.252Z" }, + { url = "https://files.pythonhosted.org/packages/74/1b/ee2abfc68e1ce728b2958b6ba831d65c62e1b13ce3017c13943f8f9b5b2e/numpy-2.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7395e69ff32526710748f92cd8c9849b361830968ea3e24a676f272653e8983e", size = 14696872, upload-time = "2026-03-09T07:56:26.991Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d1/780400e915ff5638166f11ca9dc2c5815189f3d7cf6f8759a1685e586413/numpy-2.4.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:abdce0f71dcb4a00e4e77f3faf05e4616ceccfe72ccaa07f47ee79cda3b7b0f4", size = 5203489, upload-time = "2026-03-09T07:56:29.414Z" }, + { url = "https://files.pythonhosted.org/packages/0b/bb/baffa907e9da4cc34a6e556d6d90e032f6d7a75ea47968ea92b4858826c4/numpy-2.4.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:48da3a4ee1336454b07497ff7ec83903efa5505792c4e6d9bf83d99dc07a1e18", size = 6550814, upload-time = "2026-03-09T07:56:32.225Z" }, + { url = "https://files.pythonhosted.org/packages/7b/12/8c9f0c6c95f76aeb20fc4a699c33e9f827fa0d0f857747c73bb7b17af945/numpy-2.4.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32e3bef222ad6b052280311d1d60db8e259e4947052c3ae7dd6817451fc8a4c5", size = 15666601, upload-time = "2026-03-09T07:56:34.461Z" }, + { url = "https://files.pythonhosted.org/packages/bd/79/cc665495e4d57d0aa6fbcc0aa57aa82671dfc78fbf95fe733ed86d98f52a/numpy-2.4.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7dd01a46700b1967487141a66ac1a3cf0dd8ebf1f08db37d46389401512ca97", size = 16621358, upload-time = "2026-03-09T07:56:36.852Z" }, + { url = "https://files.pythonhosted.org/packages/a8/40/b4ecb7224af1065c3539f5ecfff879d090de09608ad1008f02c05c770cb3/numpy-2.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:76f0f283506c28b12bba319c0fab98217e9f9b54e6160e9c79e9f7348ba32e9c", size = 17016135, upload-time = "2026-03-09T07:56:39.337Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b1/6a88e888052eed951afed7a142dcdf3b149a030ca59b4c71eef085858e43/numpy-2.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737f630a337364665aba3b5a77e56a68cc42d350edd010c345d65a3efa3addcc", size = 18345816, upload-time = "2026-03-09T07:56:42.31Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8f/103a60c5f8c3d7fc678c19cd7b2476110da689ccb80bc18050efbaeae183/numpy-2.4.3-cp312-cp312-win32.whl", hash = "sha256:26952e18d82a1dbbc2f008d402021baa8d6fc8e84347a2072a25e08b46d698b9", size = 5960132, upload-time = "2026-03-09T07:56:44.851Z" }, + { url = "https://files.pythonhosted.org/packages/d7/7c/f5ee1bf6ed888494978046a809df2882aad35d414b622893322df7286879/numpy-2.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:65f3c2455188f09678355f5cae1f959a06b778bc66d535da07bf2ef20cd319d5", size = 12316144, upload-time = "2026-03-09T07:56:47.057Z" }, + { url = "https://files.pythonhosted.org/packages/71/46/8d1cb3f7a00f2fb6394140e7e6623696e54c6318a9d9691bb4904672cf42/numpy-2.4.3-cp312-cp312-win_arm64.whl", hash = "sha256:2abad5c7fef172b3377502bde47892439bae394a71bc329f31df0fd829b41a9e", size = 10220364, upload-time = "2026-03-09T07:56:49.849Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d0/1fe47a98ce0df229238b77611340aff92d52691bcbc10583303181abf7fc/numpy-2.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b346845443716c8e542d54112966383b448f4a3ba5c66409771b8c0889485dd3", size = 16665297, upload-time = "2026-03-09T07:56:52.296Z" }, + { url = "https://files.pythonhosted.org/packages/27/d9/4e7c3f0e68dfa91f21c6fb6cf839bc829ec920688b1ce7ec722b1a6202fb/numpy-2.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2629289168f4897a3c4e23dc98d6f1731f0fc0fe52fb9db19f974041e4cc12b9", size = 14691853, upload-time = "2026-03-09T07:56:54.992Z" }, + { url = "https://files.pythonhosted.org/packages/3a/66/bd096b13a87549683812b53ab211e6d413497f84e794fb3c39191948da97/numpy-2.4.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bb2e3cf95854233799013779216c57e153c1ee67a0bf92138acca0e429aefaee", size = 5198435, upload-time = "2026-03-09T07:56:57.184Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2f/687722910b5a5601de2135c891108f51dfc873d8e43c8ed9f4ebb440b4a2/numpy-2.4.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:7f3408ff897f8ab07a07fbe2823d7aee6ff644c097cc1f90382511fe982f647f", size = 6546347, upload-time = "2026-03-09T07:56:59.531Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ec/7971c4e98d86c564750393fab8d7d83d0a9432a9d78bb8a163a6dc59967a/numpy-2.4.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:decb0eb8a53c3b009b0962378065589685d66b23467ef5dac16cbe818afde27f", size = 15664626, upload-time = "2026-03-09T07:57:01.385Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/7daecbea84ec935b7fc732e18f532073064a3816f0932a40a17f3349185f/numpy-2.4.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5f51900414fc9204a0e0da158ba2ac52b75656e7dce7e77fb9f84bfa343b4cc", size = 16608916, upload-time = "2026-03-09T07:57:04.008Z" }, + { url = "https://files.pythonhosted.org/packages/df/58/2a2b4a817ffd7472dca4421d9f0776898b364154e30c95f42195041dc03b/numpy-2.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6bd06731541f89cdc01b261ba2c9e037f1543df7472517836b78dfb15bd6e476", size = 17015824, upload-time = "2026-03-09T07:57:06.347Z" }, + { url = "https://files.pythonhosted.org/packages/4a/ca/627a828d44e78a418c55f82dd4caea8ea4a8ef24e5144d9e71016e52fb40/numpy-2.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:22654fe6be0e5206f553a9250762c653d3698e46686eee53b399ab90da59bd92", size = 18334581, upload-time = "2026-03-09T07:57:09.114Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c0/76f93962fc79955fcba30a429b62304332345f22d4daec1cb33653425643/numpy-2.4.3-cp313-cp313-win32.whl", hash = "sha256:d71e379452a2f670ccb689ec801b1218cd3983e253105d6e83780967e899d687", size = 5958618, upload-time = "2026-03-09T07:57:11.432Z" }, + { url = "https://files.pythonhosted.org/packages/b1/3c/88af0040119209b9b5cb59485fa48b76f372c73068dbf9254784b975ac53/numpy-2.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:0a60e17a14d640f49146cb38e3f105f571318db7826d9b6fef7e4dce758faecd", size = 12312824, upload-time = "2026-03-09T07:57:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/58/ce/3d07743aced3d173f877c3ef6a454c2174ba42b584ab0b7e6d99374f51ed/numpy-2.4.3-cp313-cp313-win_arm64.whl", hash = "sha256:c9619741e9da2059cd9c3f206110b97583c7152c1dc9f8aafd4beb450ac1c89d", size = 10221218, upload-time = "2026-03-09T07:57:16.183Z" }, + { url = "https://files.pythonhosted.org/packages/62/09/d96b02a91d09e9d97862f4fc8bfebf5400f567d8eb1fe4b0cc4795679c15/numpy-2.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7aa4e54f6469300ebca1d9eb80acd5253cdfa36f2c03d79a35883687da430875", size = 14819570, upload-time = "2026-03-09T07:57:18.564Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ca/0b1aba3905fdfa3373d523b2b15b19029f4f3031c87f4066bd9d20ef6c6b/numpy-2.4.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d1b90d840b25874cf5cd20c219af10bac3667db3876d9a495609273ebe679070", size = 5326113, upload-time = "2026-03-09T07:57:21.052Z" }, + { url = "https://files.pythonhosted.org/packages/c0/63/406e0fd32fcaeb94180fd6a4c41e55736d676c54346b7efbce548b94a914/numpy-2.4.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a749547700de0a20a6718293396ec237bb38218049cfce788e08fcb716e8cf73", size = 6646370, upload-time = "2026-03-09T07:57:22.804Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d0/10f7dc157d4b37af92720a196be6f54f889e90dcd30dce9dc657ed92c257/numpy-2.4.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94f3c4a151a2e529adf49c1d54f0f57ff8f9b233ee4d44af623a81553ab86368", size = 15723499, upload-time = "2026-03-09T07:57:24.693Z" }, + { url = "https://files.pythonhosted.org/packages/66/f1/d1c2bf1161396629701bc284d958dc1efa3a5a542aab83cf11ee6eb4cba5/numpy-2.4.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22c31dc07025123aedf7f2db9e91783df13f1776dc52c6b22c620870dc0fab22", size = 16657164, upload-time = "2026-03-09T07:57:27.676Z" }, + { url = "https://files.pythonhosted.org/packages/1a/be/cca19230b740af199ac47331a21c71e7a3d0ba59661350483c1600d28c37/numpy-2.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:148d59127ac95979d6f07e4d460f934ebdd6eed641db9c0db6c73026f2b2101a", size = 17081544, upload-time = "2026-03-09T07:57:30.664Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c5/9602b0cbb703a0936fb40f8a95407e8171935b15846de2f0776e08af04c7/numpy-2.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a97cbf7e905c435865c2d939af3d93f99d18eaaa3cabe4256f4304fb51604349", size = 18380290, upload-time = "2026-03-09T07:57:33.763Z" }, + { url = "https://files.pythonhosted.org/packages/ed/81/9f24708953cd30be9ee36ec4778f4b112b45165812f2ada4cc5ea1c1f254/numpy-2.4.3-cp313-cp313t-win32.whl", hash = "sha256:be3b8487d725a77acccc9924f65fd8bce9af7fac8c9820df1049424a2115af6c", size = 6082814, upload-time = "2026-03-09T07:57:36.491Z" }, + { url = "https://files.pythonhosted.org/packages/e2/9e/52f6eaa13e1a799f0ab79066c17f7016a4a8ae0c1aefa58c82b4dab690b4/numpy-2.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1ec84fd7c8e652b0f4aaaf2e6e9cc8eaa9b1b80a537e06b2e3a2fb176eedcb26", size = 12452673, upload-time = "2026-03-09T07:57:38.281Z" }, + { url = "https://files.pythonhosted.org/packages/c4/04/b8cece6ead0b30c9fbd99bb835ad7ea0112ac5f39f069788c5558e3b1ab2/numpy-2.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:120df8c0a81ebbf5b9020c91439fccd85f5e018a927a39f624845be194a2be02", size = 10290907, upload-time = "2026-03-09T07:57:40.747Z" }, + { url = "https://files.pythonhosted.org/packages/70/ae/3936f79adebf8caf81bd7a599b90a561334a658be4dcc7b6329ebf4ee8de/numpy-2.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:5884ce5c7acfae1e4e1b6fde43797d10aa506074d25b531b4f54bde33c0c31d4", size = 16664563, upload-time = "2026-03-09T07:57:43.817Z" }, + { url = "https://files.pythonhosted.org/packages/9b/62/760f2b55866b496bb1fa7da2a6db076bef908110e568b02fcfc1422e2a3a/numpy-2.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:297837823f5bc572c5f9379b0c9f3a3365f08492cbdc33bcc3af174372ebb168", size = 14702161, upload-time = "2026-03-09T07:57:46.169Z" }, + { url = "https://files.pythonhosted.org/packages/32/af/a7a39464e2c0a21526fb4fb76e346fb172ebc92f6d1c7a07c2c139cc17b1/numpy-2.4.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:a111698b4a3f8dcbe54c64a7708f049355abd603e619013c346553c1fd4ca90b", size = 5208738, upload-time = "2026-03-09T07:57:48.506Z" }, + { url = "https://files.pythonhosted.org/packages/29/8c/2a0cf86a59558fa078d83805589c2de490f29ed4fb336c14313a161d358a/numpy-2.4.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:4bd4741a6a676770e0e97fe9ab2e51de01183df3dcbcec591d26d331a40de950", size = 6543618, upload-time = "2026-03-09T07:57:50.591Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b8/612ce010c0728b1c363fa4ea3aa4c22fe1c5da1de008486f8c2f5cb92fae/numpy-2.4.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54f29b877279d51e210e0c80709ee14ccbbad647810e8f3d375561c45ef613dd", size = 15680676, upload-time = "2026-03-09T07:57:52.34Z" }, + { url = "https://files.pythonhosted.org/packages/a9/7e/4f120ecc54ba26ddf3dc348eeb9eb063f421de65c05fc961941798feea18/numpy-2.4.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:679f2a834bae9020f81534671c56fd0cc76dd7e5182f57131478e23d0dc59e24", size = 16613492, upload-time = "2026-03-09T07:57:54.91Z" }, + { url = "https://files.pythonhosted.org/packages/2c/86/1b6020db73be330c4b45d5c6ee4295d59cfeef0e3ea323959d053e5a6909/numpy-2.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d84f0f881cb2225c2dfd7f78a10a5645d487a496c6668d6cc39f0f114164f3d0", size = 17031789, upload-time = "2026-03-09T07:57:57.641Z" }, + { url = "https://files.pythonhosted.org/packages/07/3a/3b90463bf41ebc21d1b7e06079f03070334374208c0f9a1f05e4ae8455e7/numpy-2.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d213c7e6e8d211888cc359bab7199670a00f5b82c0978b9d1c75baf1eddbeac0", size = 18339941, upload-time = "2026-03-09T07:58:00.577Z" }, + { url = "https://files.pythonhosted.org/packages/a8/74/6d736c4cd962259fd8bae9be27363eb4883a2f9069763747347544c2a487/numpy-2.4.3-cp314-cp314-win32.whl", hash = "sha256:52077feedeff7c76ed7c9f1a0428558e50825347b7545bbb8523da2cd55c547a", size = 6007503, upload-time = "2026-03-09T07:58:03.331Z" }, + { url = "https://files.pythonhosted.org/packages/48/39/c56ef87af669364356bb011922ef0734fc49dad51964568634c72a009488/numpy-2.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:0448e7f9caefb34b4b7dd2b77f21e8906e5d6f0365ad525f9f4f530b13df2afc", size = 12444915, upload-time = "2026-03-09T07:58:06.353Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1f/ab8528e38d295fd349310807496fabb7cf9fe2e1f70b97bc20a483ea9d4a/numpy-2.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:b44fd60341c4d9783039598efadd03617fa28d041fc37d22b62d08f2027fa0e7", size = 10494875, upload-time = "2026-03-09T07:58:08.734Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ef/b7c35e4d5ef141b836658ab21a66d1a573e15b335b1d111d31f26c8ef80f/numpy-2.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0a195f4216be9305a73c0e91c9b026a35f2161237cf1c6de9b681637772ea657", size = 14822225, upload-time = "2026-03-09T07:58:11.034Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8d/7730fa9278cf6648639946cc816e7cc89f0d891602584697923375f801ed/numpy-2.4.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:cd32fbacb9fd1bf041bf8e89e4576b6f00b895f06d00914820ae06a616bdfef7", size = 5328769, upload-time = "2026-03-09T07:58:13.67Z" }, + { url = "https://files.pythonhosted.org/packages/47/01/d2a137317c958b074d338807c1b6a383406cdf8b8e53b075d804cc3d211d/numpy-2.4.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:2e03c05abaee1f672e9d67bc858f300b5ccba1c21397211e8d77d98350972093", size = 6649461, upload-time = "2026-03-09T07:58:15.912Z" }, + { url = "https://files.pythonhosted.org/packages/5c/34/812ce12bc0f00272a4b0ec0d713cd237cb390666eb6206323d1cc9cedbb2/numpy-2.4.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d1ce23cce91fcea443320a9d0ece9b9305d4368875bab09538f7a5b4131938a", size = 15725809, upload-time = "2026-03-09T07:58:17.787Z" }, + { url = "https://files.pythonhosted.org/packages/25/c0/2aed473a4823e905e765fee3dc2cbf504bd3e68ccb1150fbdabd5c39f527/numpy-2.4.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c59020932feb24ed49ffd03704fbab89f22aa9c0d4b180ff45542fe8918f5611", size = 16655242, upload-time = "2026-03-09T07:58:20.476Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c8/7e052b2fc87aa0e86de23f20e2c42bd261c624748aa8efd2c78f7bb8d8c6/numpy-2.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9684823a78a6cd6ad7511fc5e25b07947d1d5b5e2812c93fe99d7d4195130720", size = 17080660, upload-time = "2026-03-09T07:58:23.067Z" }, + { url = "https://files.pythonhosted.org/packages/f3/3d/0876746044db2adcb11549f214d104f2e1be00f07a67edbb4e2812094847/numpy-2.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0200b25c687033316fb39f0ff4e3e690e8957a2c3c8d22499891ec58c37a3eb5", size = 18380384, upload-time = "2026-03-09T07:58:25.839Z" }, + { url = "https://files.pythonhosted.org/packages/07/12/8160bea39da3335737b10308df4f484235fd297f556745f13092aa039d3b/numpy-2.4.3-cp314-cp314t-win32.whl", hash = "sha256:5e10da9e93247e554bb1d22f8edc51847ddd7dde52d85ce31024c1b4312bfba0", size = 6154547, upload-time = "2026-03-09T07:58:28.289Z" }, + { url = "https://files.pythonhosted.org/packages/42/f3/76534f61f80d74cc9cdf2e570d3d4eeb92c2280a27c39b0aaf471eda7b48/numpy-2.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:45f003dbdffb997a03da2d1d0cb41fbd24a87507fb41605c0420a3db5bd4667b", size = 12633645, upload-time = "2026-03-09T07:58:30.384Z" }, + { url = "https://files.pythonhosted.org/packages/1f/b6/7c0d4334c15983cec7f92a69e8ce9b1e6f31857e5ee3a413ac424e6bd63d/numpy-2.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:4d382735cecd7bcf090172489a525cd7d4087bc331f7df9f60ddc9a296cf208e", size = 10565454, upload-time = "2026-03-09T07:58:33.031Z" }, + { url = "https://files.pythonhosted.org/packages/64/e4/4dab9fb43c83719c29241c535d9e07be73bea4bc0c6686c5816d8e1b6689/numpy-2.4.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c6b124bfcafb9e8d3ed09130dbee44848c20b3e758b6bbf006e641778927c028", size = 16834892, upload-time = "2026-03-09T07:58:35.334Z" }, + { url = "https://files.pythonhosted.org/packages/c9/29/f8b6d4af90fed3dfda84ebc0df06c9833d38880c79ce954e5b661758aa31/numpy-2.4.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:76dbb9d4e43c16cf9aa711fcd8de1e2eeb27539dcefb60a1d5e9f12fae1d1ed8", size = 14893070, upload-time = "2026-03-09T07:58:37.7Z" }, + { url = "https://files.pythonhosted.org/packages/9a/04/a19b3c91dbec0a49269407f15d5753673a09832daed40c45e8150e6fa558/numpy-2.4.3-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:29363fbfa6f8ee855d7569c96ce524845e3d726d6c19b29eceec7dd555dab152", size = 5399609, upload-time = "2026-03-09T07:58:39.853Z" }, + { url = "https://files.pythonhosted.org/packages/79/34/4d73603f5420eab89ea8a67097b31364bf7c30f811d4dd84b1659c7476d9/numpy-2.4.3-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:bc71942c789ef415a37f0d4eab90341425a00d538cd0642445d30b41023d3395", size = 6714355, upload-time = "2026-03-09T07:58:42.365Z" }, + { url = "https://files.pythonhosted.org/packages/58/ad/1100d7229bb248394939a12a8074d485b655e8ed44207d328fdd7fcebc7b/numpy-2.4.3-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e58765ad74dcebd3ef0208a5078fba32dc8ec3578fe84a604432950cd043d79", size = 15800434, upload-time = "2026-03-09T07:58:44.837Z" }, + { url = "https://files.pythonhosted.org/packages/0c/fd/16d710c085d28ba4feaf29ac60c936c9d662e390344f94a6beaa2ac9899b/numpy-2.4.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e236dbda4e1d319d681afcbb136c0c4a8e0f1a5c58ceec2adebb547357fe857", size = 16729409, upload-time = "2026-03-09T07:58:47.972Z" }, + { url = "https://files.pythonhosted.org/packages/57/a7/b35835e278c18b85206834b3aa3abe68e77a98769c59233d1f6300284781/numpy-2.4.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:4b42639cdde6d24e732ff823a3fa5b701d8acad89c4142bc1d0bd6dc85200ba5", size = 12504685, upload-time = "2026-03-09T07:58:50.525Z" }, ] [[package]] @@ -1354,103 +1375,103 @@ wheels = [ [[package]] name = "pathspec" -version = "1.0.3" +version = "1.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/b2/bb8e495d5262bfec41ab5cb18f522f1012933347fb5d9e62452d446baca2/pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d", size = 130841, upload-time = "2026-01-09T15:46:46.009Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/2b/121e912bd60eebd623f873fd090de0e84f322972ab25a7f9044c056804ed/pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c", size = 55021, upload-time = "2026-01-09T15:46:44.652Z" }, + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, ] [[package]] name = "pillow" -version = "12.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/02/d52c733a2452ef1ffcc123b68e6606d07276b0e358db70eabad7e40042b7/pillow-12.1.0.tar.gz", hash = "sha256:5c5ae0a06e9ea030ab786b0251b32c7e4ce10e58d983c0d5c56029455180b5b9", size = 46977283, upload-time = "2026-01-02T09:13:29.892Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/c4/bf8328039de6cc22182c3ef007a2abfbbdab153661c0a9aa78af8d706391/pillow-12.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:a83e0850cb8f5ac975291ebfc4170ba481f41a28065277f7f735c202cd8e0af3", size = 5304057, upload-time = "2026-01-02T09:10:46.627Z" }, - { url = "https://files.pythonhosted.org/packages/43/06/7264c0597e676104cc22ca73ee48f752767cd4b1fe084662620b17e10120/pillow-12.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6e53e82ec2db0717eabb276aa56cf4e500c9a7cec2c2e189b55c24f65a3e8c0", size = 4657811, upload-time = "2026-01-02T09:10:49.548Z" }, - { url = "https://files.pythonhosted.org/packages/72/64/f9189e44474610daf83da31145fa56710b627b5c4c0b9c235e34058f6b31/pillow-12.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:40a8e3b9e8773876d6e30daed22f016509e3987bab61b3b7fe309d7019a87451", size = 6232243, upload-time = "2026-01-02T09:10:51.62Z" }, - { url = "https://files.pythonhosted.org/packages/ef/30/0df458009be6a4caca4ca2c52975e6275c387d4e5c95544e34138b41dc86/pillow-12.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:800429ac32c9b72909c671aaf17ecd13110f823ddb7db4dfef412a5587c2c24e", size = 8037872, upload-time = "2026-01-02T09:10:53.446Z" }, - { url = "https://files.pythonhosted.org/packages/e4/86/95845d4eda4f4f9557e25381d70876aa213560243ac1a6d619c46caaedd9/pillow-12.1.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b022eaaf709541b391ee069f0022ee5b36c709df71986e3f7be312e46f42c84", size = 6345398, upload-time = "2026-01-02T09:10:55.426Z" }, - { url = "https://files.pythonhosted.org/packages/5c/1f/8e66ab9be3aaf1435bc03edd1ebdf58ffcd17f7349c1d970cafe87af27d9/pillow-12.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f345e7bc9d7f368887c712aa5054558bad44d2a301ddf9248599f4161abc7c0", size = 7034667, upload-time = "2026-01-02T09:10:57.11Z" }, - { url = "https://files.pythonhosted.org/packages/f9/f6/683b83cb9b1db1fb52b87951b1c0b99bdcfceaa75febf11406c19f82cb5e/pillow-12.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d70347c8a5b7ccd803ec0c85c8709f036e6348f1e6a5bf048ecd9c64d3550b8b", size = 6458743, upload-time = "2026-01-02T09:10:59.331Z" }, - { url = "https://files.pythonhosted.org/packages/9a/7d/de833d63622538c1d58ce5395e7c6cb7e7dce80decdd8bde4a484e095d9f/pillow-12.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1fcc52d86ce7a34fd17cb04e87cfdb164648a3662a6f20565910a99653d66c18", size = 7159342, upload-time = "2026-01-02T09:11:01.82Z" }, - { url = "https://files.pythonhosted.org/packages/8c/40/50d86571c9e5868c42b81fe7da0c76ca26373f3b95a8dd675425f4a92ec1/pillow-12.1.0-cp311-cp311-win32.whl", hash = "sha256:3ffaa2f0659e2f740473bcf03c702c39a8d4b2b7ffc629052028764324842c64", size = 6328655, upload-time = "2026-01-02T09:11:04.556Z" }, - { url = "https://files.pythonhosted.org/packages/6c/af/b1d7e301c4cd26cd45d4af884d9ee9b6fab893b0ad2450d4746d74a6968c/pillow-12.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:806f3987ffe10e867bab0ddad45df1148a2b98221798457fa097ad85d6e8bc75", size = 7031469, upload-time = "2026-01-02T09:11:06.538Z" }, - { url = "https://files.pythonhosted.org/packages/48/36/d5716586d887fb2a810a4a61518a327a1e21c8b7134c89283af272efe84b/pillow-12.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:9f5fefaca968e700ad1a4a9de98bf0869a94e397fe3524c4c9450c1445252304", size = 2452515, upload-time = "2026-01-02T09:11:08.226Z" }, - { url = "https://files.pythonhosted.org/packages/20/31/dc53fe21a2f2996e1b7d92bf671cdb157079385183ef7c1ae08b485db510/pillow-12.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a332ac4ccb84b6dde65dbace8431f3af08874bf9770719d32a635c4ef411b18b", size = 5262642, upload-time = "2026-01-02T09:11:10.138Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c1/10e45ac9cc79419cedf5121b42dcca5a50ad2b601fa080f58c22fb27626e/pillow-12.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:907bfa8a9cb790748a9aa4513e37c88c59660da3bcfffbd24a7d9e6abf224551", size = 4657464, upload-time = "2026-01-02T09:11:12.319Z" }, - { url = "https://files.pythonhosted.org/packages/ad/26/7b82c0ab7ef40ebede7a97c72d473bda5950f609f8e0c77b04af574a0ddb/pillow-12.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:efdc140e7b63b8f739d09a99033aa430accce485ff78e6d311973a67b6bf3208", size = 6234878, upload-time = "2026-01-02T09:11:14.096Z" }, - { url = "https://files.pythonhosted.org/packages/76/25/27abc9792615b5e886ca9411ba6637b675f1b77af3104710ac7353fe5605/pillow-12.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bef9768cab184e7ae6e559c032e95ba8d07b3023c289f79a2bd36e8bf85605a5", size = 8044868, upload-time = "2026-01-02T09:11:15.903Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ea/f200a4c36d836100e7bc738fc48cd963d3ba6372ebc8298a889e0cfc3359/pillow-12.1.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:742aea052cf5ab5034a53c3846165bc3ce88d7c38e954120db0ab867ca242661", size = 6349468, upload-time = "2026-01-02T09:11:17.631Z" }, - { url = "https://files.pythonhosted.org/packages/11/8f/48d0b77ab2200374c66d344459b8958c86693be99526450e7aee714e03e4/pillow-12.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6dfc2af5b082b635af6e08e0d1f9f1c4e04d17d4e2ca0ef96131e85eda6eb17", size = 7041518, upload-time = "2026-01-02T09:11:19.389Z" }, - { url = "https://files.pythonhosted.org/packages/1d/23/c281182eb986b5d31f0a76d2a2c8cd41722d6fb8ed07521e802f9bba52de/pillow-12.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:609e89d9f90b581c8d16358c9087df76024cf058fa693dd3e1e1620823f39670", size = 6462829, upload-time = "2026-01-02T09:11:21.28Z" }, - { url = "https://files.pythonhosted.org/packages/25/ef/7018273e0faac099d7b00982abdcc39142ae6f3bd9ceb06de09779c4a9d6/pillow-12.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43b4899cfd091a9693a1278c4982f3e50f7fb7cff5153b05174b4afc9593b616", size = 7166756, upload-time = "2026-01-02T09:11:23.559Z" }, - { url = "https://files.pythonhosted.org/packages/8f/c8/993d4b7ab2e341fe02ceef9576afcf5830cdec640be2ac5bee1820d693d4/pillow-12.1.0-cp312-cp312-win32.whl", hash = "sha256:aa0c9cc0b82b14766a99fbe6084409972266e82f459821cd26997a488a7261a7", size = 6328770, upload-time = "2026-01-02T09:11:25.661Z" }, - { url = "https://files.pythonhosted.org/packages/a7/87/90b358775a3f02765d87655237229ba64a997b87efa8ccaca7dd3e36e7a7/pillow-12.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:d70534cea9e7966169ad29a903b99fc507e932069a881d0965a1a84bb57f6c6d", size = 7033406, upload-time = "2026-01-02T09:11:27.474Z" }, - { url = "https://files.pythonhosted.org/packages/5d/cf/881b457eccacac9e5b2ddd97d5071fb6d668307c57cbf4e3b5278e06e536/pillow-12.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:65b80c1ee7e14a87d6a068dd3b0aea268ffcabfe0498d38661b00c5b4b22e74c", size = 2452612, upload-time = "2026-01-02T09:11:29.309Z" }, - { url = "https://files.pythonhosted.org/packages/dd/c7/2530a4aa28248623e9d7f27316b42e27c32ec410f695929696f2e0e4a778/pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7b5dd7cbae20285cdb597b10eb5a2c13aa9de6cde9bb64a3c1317427b1db1ae1", size = 4062543, upload-time = "2026-01-02T09:11:31.566Z" }, - { url = "https://files.pythonhosted.org/packages/8f/1f/40b8eae823dc1519b87d53c30ed9ef085506b05281d313031755c1705f73/pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:29a4cef9cb672363926f0470afc516dbf7305a14d8c54f7abbb5c199cd8f8179", size = 4138373, upload-time = "2026-01-02T09:11:33.367Z" }, - { url = "https://files.pythonhosted.org/packages/d4/77/6fa60634cf06e52139fd0e89e5bbf055e8166c691c42fb162818b7fda31d/pillow-12.1.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:681088909d7e8fa9e31b9799aaa59ba5234c58e5e4f1951b4c4d1082a2e980e0", size = 3601241, upload-time = "2026-01-02T09:11:35.011Z" }, - { url = "https://files.pythonhosted.org/packages/4f/bf/28ab865de622e14b747f0cd7877510848252d950e43002e224fb1c9ababf/pillow-12.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:983976c2ab753166dc66d36af6e8ec15bb511e4a25856e2227e5f7e00a160587", size = 5262410, upload-time = "2026-01-02T09:11:36.682Z" }, - { url = "https://files.pythonhosted.org/packages/1c/34/583420a1b55e715937a85bd48c5c0991598247a1fd2eb5423188e765ea02/pillow-12.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:db44d5c160a90df2d24a24760bbd37607d53da0b34fb546c4c232af7192298ac", size = 4657312, upload-time = "2026-01-02T09:11:38.535Z" }, - { url = "https://files.pythonhosted.org/packages/1d/fd/f5a0896839762885b3376ff04878f86ab2b097c2f9a9cdccf4eda8ba8dc0/pillow-12.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6b7a9d1db5dad90e2991645874f708e87d9a3c370c243c2d7684d28f7e133e6b", size = 6232605, upload-time = "2026-01-02T09:11:40.602Z" }, - { url = "https://files.pythonhosted.org/packages/98/aa/938a09d127ac1e70e6ed467bd03834350b33ef646b31edb7452d5de43792/pillow-12.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6258f3260986990ba2fa8a874f8b6e808cf5abb51a94015ca3dc3c68aa4f30ea", size = 8041617, upload-time = "2026-01-02T09:11:42.721Z" }, - { url = "https://files.pythonhosted.org/packages/17/e8/538b24cb426ac0186e03f80f78bc8dc7246c667f58b540bdd57c71c9f79d/pillow-12.1.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e115c15e3bc727b1ca3e641a909f77f8ca72a64fff150f666fcc85e57701c26c", size = 6346509, upload-time = "2026-01-02T09:11:44.955Z" }, - { url = "https://files.pythonhosted.org/packages/01/9a/632e58ec89a32738cabfd9ec418f0e9898a2b4719afc581f07c04a05e3c9/pillow-12.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6741e6f3074a35e47c77b23a4e4f2d90db3ed905cb1c5e6e0d49bff2045632bc", size = 7038117, upload-time = "2026-01-02T09:11:46.736Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a2/d40308cf86eada842ca1f3ffa45d0ca0df7e4ab33c83f81e73f5eaed136d/pillow-12.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:935b9d1aed48fcfb3f838caac506f38e29621b44ccc4f8a64d575cb1b2a88644", size = 6460151, upload-time = "2026-01-02T09:11:48.625Z" }, - { url = "https://files.pythonhosted.org/packages/f1/88/f5b058ad6453a085c5266660a1417bdad590199da1b32fb4efcff9d33b05/pillow-12.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5fee4c04aad8932da9f8f710af2c1a15a83582cfb884152a9caa79d4efcdbf9c", size = 7164534, upload-time = "2026-01-02T09:11:50.445Z" }, - { url = "https://files.pythonhosted.org/packages/19/ce/c17334caea1db789163b5d855a5735e47995b0b5dc8745e9a3605d5f24c0/pillow-12.1.0-cp313-cp313-win32.whl", hash = "sha256:a786bf667724d84aa29b5db1c61b7bfdde380202aaca12c3461afd6b71743171", size = 6332551, upload-time = "2026-01-02T09:11:52.234Z" }, - { url = "https://files.pythonhosted.org/packages/e5/07/74a9d941fa45c90a0d9465098fe1ec85de3e2afbdc15cc4766622d516056/pillow-12.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:461f9dfdafa394c59cd6d818bdfdbab4028b83b02caadaff0ffd433faf4c9a7a", size = 7040087, upload-time = "2026-01-02T09:11:54.822Z" }, - { url = "https://files.pythonhosted.org/packages/88/09/c99950c075a0e9053d8e880595926302575bc742b1b47fe1bbcc8d388d50/pillow-12.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:9212d6b86917a2300669511ed094a9406888362e085f2431a7da985a6b124f45", size = 2452470, upload-time = "2026-01-02T09:11:56.522Z" }, - { url = "https://files.pythonhosted.org/packages/b5/ba/970b7d85ba01f348dee4d65412476321d40ee04dcb51cd3735b9dc94eb58/pillow-12.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:00162e9ca6d22b7c3ee8e61faa3c3253cd19b6a37f126cad04f2f88b306f557d", size = 5264816, upload-time = "2026-01-02T09:11:58.227Z" }, - { url = "https://files.pythonhosted.org/packages/10/60/650f2fb55fdba7a510d836202aa52f0baac633e50ab1cf18415d332188fb/pillow-12.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7d6daa89a00b58c37cb1747ec9fb7ac3bc5ffd5949f5888657dfddde6d1312e0", size = 4660472, upload-time = "2026-01-02T09:12:00.798Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c0/5273a99478956a099d533c4f46cbaa19fd69d606624f4334b85e50987a08/pillow-12.1.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e2479c7f02f9d505682dc47df8c0ea1fc5e264c4d1629a5d63fe3e2334b89554", size = 6268974, upload-time = "2026-01-02T09:12:02.572Z" }, - { url = "https://files.pythonhosted.org/packages/b4/26/0bf714bc2e73d5267887d47931d53c4ceeceea6978148ed2ab2a4e6463c4/pillow-12.1.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f188d580bd870cda1e15183790d1cc2fa78f666e76077d103edf048eed9c356e", size = 8073070, upload-time = "2026-01-02T09:12:04.75Z" }, - { url = "https://files.pythonhosted.org/packages/43/cf/1ea826200de111a9d65724c54f927f3111dc5ae297f294b370a670c17786/pillow-12.1.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fde7ec5538ab5095cc02df38ee99b0443ff0e1c847a045554cf5f9af1f4aa82", size = 6380176, upload-time = "2026-01-02T09:12:06.626Z" }, - { url = "https://files.pythonhosted.org/packages/03/e0/7938dd2b2013373fd85d96e0f38d62b7a5a262af21ac274250c7ca7847c9/pillow-12.1.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ed07dca4a8464bada6139ab38f5382f83e5f111698caf3191cb8dbf27d908b4", size = 7067061, upload-time = "2026-01-02T09:12:08.624Z" }, - { url = "https://files.pythonhosted.org/packages/86/ad/a2aa97d37272a929a98437a8c0ac37b3cf012f4f8721e1bd5154699b2518/pillow-12.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f45bd71d1fa5e5749587613037b172e0b3b23159d1c00ef2fc920da6f470e6f0", size = 6491824, upload-time = "2026-01-02T09:12:10.488Z" }, - { url = "https://files.pythonhosted.org/packages/a4/44/80e46611b288d51b115826f136fb3465653c28f491068a72d3da49b54cd4/pillow-12.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:277518bf4fe74aa91489e1b20577473b19ee70fb97c374aa50830b279f25841b", size = 7190911, upload-time = "2026-01-02T09:12:12.772Z" }, - { url = "https://files.pythonhosted.org/packages/86/77/eacc62356b4cf81abe99ff9dbc7402750044aed02cfd6a503f7c6fc11f3e/pillow-12.1.0-cp313-cp313t-win32.whl", hash = "sha256:7315f9137087c4e0ee73a761b163fc9aa3b19f5f606a7fc08d83fd3e4379af65", size = 6336445, upload-time = "2026-01-02T09:12:14.775Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3c/57d81d0b74d218706dafccb87a87ea44262c43eef98eb3b164fd000e0491/pillow-12.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:0ddedfaa8b5f0b4ffbc2fa87b556dc59f6bb4ecb14a53b33f9189713ae8053c0", size = 7045354, upload-time = "2026-01-02T09:12:16.599Z" }, - { url = "https://files.pythonhosted.org/packages/ac/82/8b9b97bba2e3576a340f93b044a3a3a09841170ab4c1eb0d5c93469fd32f/pillow-12.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:80941e6d573197a0c28f394753de529bb436b1ca990ed6e765cf42426abc39f8", size = 2454547, upload-time = "2026-01-02T09:12:18.704Z" }, - { url = "https://files.pythonhosted.org/packages/8c/87/bdf971d8bbcf80a348cc3bacfcb239f5882100fe80534b0ce67a784181d8/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:5cb7bc1966d031aec37ddb9dcf15c2da5b2e9f7cc3ca7c54473a20a927e1eb91", size = 4062533, upload-time = "2026-01-02T09:12:20.791Z" }, - { url = "https://files.pythonhosted.org/packages/ff/4f/5eb37a681c68d605eb7034c004875c81f86ec9ef51f5be4a63eadd58859a/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:97e9993d5ed946aba26baf9c1e8cf18adbab584b99f452ee72f7ee8acb882796", size = 4138546, upload-time = "2026-01-02T09:12:23.664Z" }, - { url = "https://files.pythonhosted.org/packages/11/6d/19a95acb2edbace40dcd582d077b991646b7083c41b98da4ed7555b59733/pillow-12.1.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:414b9a78e14ffeb98128863314e62c3f24b8a86081066625700b7985b3f529bd", size = 3601163, upload-time = "2026-01-02T09:12:26.338Z" }, - { url = "https://files.pythonhosted.org/packages/fc/36/2b8138e51cb42e4cc39c3297713455548be855a50558c3ac2beebdc251dd/pillow-12.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e6bdb408f7c9dd2a5ff2b14a3b0bb6d4deb29fb9961e6eb3ae2031ae9a5cec13", size = 5266086, upload-time = "2026-01-02T09:12:28.782Z" }, - { url = "https://files.pythonhosted.org/packages/53/4b/649056e4d22e1caa90816bf99cef0884aed607ed38075bd75f091a607a38/pillow-12.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3413c2ae377550f5487991d444428f1a8ae92784aac79caa8b1e3b89b175f77e", size = 4657344, upload-time = "2026-01-02T09:12:31.117Z" }, - { url = "https://files.pythonhosted.org/packages/6c/6b/c5742cea0f1ade0cd61485dc3d81f05261fc2276f537fbdc00802de56779/pillow-12.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e5dcbe95016e88437ecf33544ba5db21ef1b8dd6e1b434a2cb2a3d605299e643", size = 6232114, upload-time = "2026-01-02T09:12:32.936Z" }, - { url = "https://files.pythonhosted.org/packages/bf/8f/9f521268ce22d63991601aafd3d48d5ff7280a246a1ef62d626d67b44064/pillow-12.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d0a7735df32ccbcc98b98a1ac785cc4b19b580be1bdf0aeb5c03223220ea09d5", size = 8042708, upload-time = "2026-01-02T09:12:34.78Z" }, - { url = "https://files.pythonhosted.org/packages/1a/eb/257f38542893f021502a1bbe0c2e883c90b5cff26cc33b1584a841a06d30/pillow-12.1.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c27407a2d1b96774cbc4a7594129cc027339fd800cd081e44497722ea1179de", size = 6347762, upload-time = "2026-01-02T09:12:36.748Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5a/8ba375025701c09b309e8d5163c5a4ce0102fa86bbf8800eb0d7ac87bc51/pillow-12.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15c794d74303828eaa957ff8070846d0efe8c630901a1c753fdc63850e19ecd9", size = 7039265, upload-time = "2026-01-02T09:12:39.082Z" }, - { url = "https://files.pythonhosted.org/packages/cf/dc/cf5e4cdb3db533f539e88a7bbf9f190c64ab8a08a9bc7a4ccf55067872e4/pillow-12.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c990547452ee2800d8506c4150280757f88532f3de2a58e3022e9b179107862a", size = 6462341, upload-time = "2026-01-02T09:12:40.946Z" }, - { url = "https://files.pythonhosted.org/packages/d0/47/0291a25ac9550677e22eda48510cfc4fa4b2ef0396448b7fbdc0a6946309/pillow-12.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b63e13dd27da389ed9475b3d28510f0f954bca0041e8e551b2a4eb1eab56a39a", size = 7165395, upload-time = "2026-01-02T09:12:42.706Z" }, - { url = "https://files.pythonhosted.org/packages/4f/4c/e005a59393ec4d9416be06e6b45820403bb946a778e39ecec62f5b2b991e/pillow-12.1.0-cp314-cp314-win32.whl", hash = "sha256:1a949604f73eb07a8adab38c4fe50791f9919344398bdc8ac6b307f755fc7030", size = 6431413, upload-time = "2026-01-02T09:12:44.944Z" }, - { url = "https://files.pythonhosted.org/packages/1c/af/f23697f587ac5f9095d67e31b81c95c0249cd461a9798a061ed6709b09b5/pillow-12.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:4f9f6a650743f0ddee5593ac9e954ba1bdbc5e150bc066586d4f26127853ab94", size = 7176779, upload-time = "2026-01-02T09:12:46.727Z" }, - { url = "https://files.pythonhosted.org/packages/b3/36/6a51abf8599232f3e9afbd16d52829376a68909fe14efe29084445db4b73/pillow-12.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:808b99604f7873c800c4840f55ff389936ef1948e4e87645eaf3fccbc8477ac4", size = 2543105, upload-time = "2026-01-02T09:12:49.243Z" }, - { url = "https://files.pythonhosted.org/packages/82/54/2e1dd20c8749ff225080d6ba465a0cab4387f5db0d1c5fb1439e2d99923f/pillow-12.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc11908616c8a283cf7d664f77411a5ed2a02009b0097ff8abbba5e79128ccf2", size = 5268571, upload-time = "2026-01-02T09:12:51.11Z" }, - { url = "https://files.pythonhosted.org/packages/57/61/571163a5ef86ec0cf30d265ac2a70ae6fc9e28413d1dc94fa37fae6bda89/pillow-12.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:896866d2d436563fa2a43a9d72f417874f16b5545955c54a64941e87c1376c61", size = 4660426, upload-time = "2026-01-02T09:12:52.865Z" }, - { url = "https://files.pythonhosted.org/packages/5e/e1/53ee5163f794aef1bf84243f755ee6897a92c708505350dd1923f4afec48/pillow-12.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8e178e3e99d3c0ea8fc64b88447f7cac8ccf058af422a6cedc690d0eadd98c51", size = 6269908, upload-time = "2026-01-02T09:12:54.884Z" }, - { url = "https://files.pythonhosted.org/packages/bc/0b/b4b4106ff0ee1afa1dc599fde6ab230417f800279745124f6c50bcffed8e/pillow-12.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:079af2fb0c599c2ec144ba2c02766d1b55498e373b3ac64687e43849fbbef5bc", size = 8074733, upload-time = "2026-01-02T09:12:56.802Z" }, - { url = "https://files.pythonhosted.org/packages/19/9f/80b411cbac4a732439e629a26ad3ef11907a8c7fc5377b7602f04f6fe4e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdec5e43377761c5dbca620efb69a77f6855c5a379e32ac5b158f54c84212b14", size = 6381431, upload-time = "2026-01-02T09:12:58.823Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b7/d65c45db463b66ecb6abc17c6ba6917a911202a07662247e1355ce1789e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:565c986f4b45c020f5421a4cea13ef294dde9509a8577f29b2fc5edc7587fff8", size = 7068529, upload-time = "2026-01-02T09:13:00.885Z" }, - { url = "https://files.pythonhosted.org/packages/50/96/dfd4cd726b4a45ae6e3c669fc9e49deb2241312605d33aba50499e9d9bd1/pillow-12.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:43aca0a55ce1eefc0aefa6253661cb54571857b1a7b2964bd8a1e3ef4b729924", size = 6492981, upload-time = "2026-01-02T09:13:03.314Z" }, - { url = "https://files.pythonhosted.org/packages/4d/1c/b5dc52cf713ae46033359c5ca920444f18a6359ce1020dd3e9c553ea5bc6/pillow-12.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0deedf2ea233722476b3a81e8cdfbad786f7adbed5d848469fa59fe52396e4ef", size = 7191878, upload-time = "2026-01-02T09:13:05.276Z" }, - { url = "https://files.pythonhosted.org/packages/53/26/c4188248bd5edaf543864fe4834aebe9c9cb4968b6f573ce014cc42d0720/pillow-12.1.0-cp314-cp314t-win32.whl", hash = "sha256:b17fbdbe01c196e7e159aacb889e091f28e61020a8abeac07b68079b6e626988", size = 6438703, upload-time = "2026-01-02T09:13:07.491Z" }, - { url = "https://files.pythonhosted.org/packages/b8/0e/69ed296de8ea05cb03ee139cee600f424ca166e632567b2d66727f08c7ed/pillow-12.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27b9baecb428899db6c0de572d6d305cfaf38ca1596b5c0542a5182e3e74e8c6", size = 7182927, upload-time = "2026-01-02T09:13:09.841Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f5/68334c015eed9b5cff77814258717dec591ded209ab5b6fb70e2ae873d1d/pillow-12.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f61333d817698bdcdd0f9d7793e365ac3d2a21c1f1eb02b32ad6aefb8d8ea831", size = 2545104, upload-time = "2026-01-02T09:13:12.068Z" }, - { url = "https://files.pythonhosted.org/packages/8b/bc/224b1d98cffd7164b14707c91aac83c07b047fbd8f58eba4066a3e53746a/pillow-12.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ca94b6aac0d7af2a10ba08c0f888b3d5114439b6b3ef39968378723622fed377", size = 5228605, upload-time = "2026-01-02T09:13:14.084Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ca/49ca7769c4550107de049ed85208240ba0f330b3f2e316f24534795702ce/pillow-12.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:351889afef0f485b84078ea40fe33727a0492b9af3904661b0abbafee0355b72", size = 4622245, upload-time = "2026-01-02T09:13:15.964Z" }, - { url = "https://files.pythonhosted.org/packages/73/48/fac807ce82e5955bcc2718642b94b1bd22a82a6d452aea31cbb678cddf12/pillow-12.1.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb0984b30e973f7e2884362b7d23d0a348c7143ee559f38ef3eaab640144204c", size = 5247593, upload-time = "2026-01-02T09:13:17.913Z" }, - { url = "https://files.pythonhosted.org/packages/d2/95/3e0742fe358c4664aed4fd05d5f5373dcdad0b27af52aa0972568541e3f4/pillow-12.1.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84cabc7095dd535ca934d57e9ce2a72ffd216e435a84acb06b2277b1de2689bd", size = 6989008, upload-time = "2026-01-02T09:13:20.083Z" }, - { url = "https://files.pythonhosted.org/packages/5a/74/fe2ac378e4e202e56d50540d92e1ef4ff34ed687f3c60f6a121bcf99437e/pillow-12.1.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53d8b764726d3af1a138dd353116f774e3862ec7e3794e0c8781e30db0f35dfc", size = 5313824, upload-time = "2026-01-02T09:13:22.405Z" }, - { url = "https://files.pythonhosted.org/packages/f3/77/2a60dee1adee4e2655ac328dd05c02a955c1cd683b9f1b82ec3feb44727c/pillow-12.1.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5da841d81b1a05ef940a8567da92decaa15bc4d7dedb540a8c219ad83d91808a", size = 5963278, upload-time = "2026-01-02T09:13:24.706Z" }, - { url = "https://files.pythonhosted.org/packages/2d/71/64e9b1c7f04ae0027f788a248e6297d7fcc29571371fe7d45495a78172c0/pillow-12.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:75af0b4c229ac519b155028fa1be632d812a519abba9b46b20e50c6caa184f19", size = 7029809, upload-time = "2026-01-02T09:13:26.541Z" }, +version = "12.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/42/5c74462b4fd957fcd7b13b04fb3205ff8349236ea74c7c375766d6c82288/pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4", size = 46980264, upload-time = "2026-02-11T04:23:07.146Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/46/5da1ec4a5171ee7bf1a0efa064aba70ba3d6e0788ce3f5acd1375d23c8c0/pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e879bb6cd5c73848ef3b2b48b8af9ff08c5b71ecda8048b7dd22d8a33f60be32", size = 5304084, upload-time = "2026-02-11T04:20:27.501Z" }, + { url = "https://files.pythonhosted.org/packages/78/93/a29e9bc02d1cf557a834da780ceccd54e02421627200696fcf805ebdc3fb/pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:365b10bb9417dd4498c0e3b128018c4a624dc11c7b97d8cc54effe3b096f4c38", size = 4657866, upload-time = "2026-02-11T04:20:29.827Z" }, + { url = "https://files.pythonhosted.org/packages/13/84/583a4558d492a179d31e4aae32eadce94b9acf49c0337c4ce0b70e0a01f2/pillow-12.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d4ce8e329c93845720cd2014659ca67eac35f6433fd3050393d85f3ecef0dad5", size = 6232148, upload-time = "2026-02-11T04:20:31.329Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e2/53c43334bbbb2d3b938978532fbda8e62bb6e0b23a26ce8592f36bcc4987/pillow-12.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc354a04072b765eccf2204f588a7a532c9511e8b9c7f900e1b64e3e33487090", size = 8038007, upload-time = "2026-02-11T04:20:34.225Z" }, + { url = "https://files.pythonhosted.org/packages/b8/a6/3d0e79c8a9d58150dd98e199d7c1c56861027f3829a3a60b3c2784190180/pillow-12.1.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e7976bf1910a8116b523b9f9f58bf410f3e8aa330cd9a2bb2953f9266ab49af", size = 6345418, upload-time = "2026-02-11T04:20:35.858Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c8/46dfeac5825e600579157eea177be43e2f7ff4a99da9d0d0a49533509ac5/pillow-12.1.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:597bd9c8419bc7c6af5604e55847789b69123bbe25d65cc6ad3012b4f3c98d8b", size = 7034590, upload-time = "2026-02-11T04:20:37.91Z" }, + { url = "https://files.pythonhosted.org/packages/af/bf/e6f65d3db8a8bbfeaf9e13cc0417813f6319863a73de934f14b2229ada18/pillow-12.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c1fc0f2ca5f96a3c8407e41cca26a16e46b21060fe6d5b099d2cb01412222f5", size = 6458655, upload-time = "2026-02-11T04:20:39.496Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c2/66091f3f34a25894ca129362e510b956ef26f8fb67a0e6417bc5744e56f1/pillow-12.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:578510d88c6229d735855e1f278aa305270438d36a05031dfaae5067cc8eb04d", size = 7159286, upload-time = "2026-02-11T04:20:41.139Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5a/24bc8eb526a22f957d0cec6243146744966d40857e3d8deb68f7902ca6c1/pillow-12.1.1-cp311-cp311-win32.whl", hash = "sha256:7311c0a0dcadb89b36b7025dfd8326ecfa36964e29913074d47382706e516a7c", size = 6328663, upload-time = "2026-02-11T04:20:43.184Z" }, + { url = "https://files.pythonhosted.org/packages/31/03/bef822e4f2d8f9d7448c133d0a18185d3cce3e70472774fffefe8b0ed562/pillow-12.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:fbfa2a7c10cc2623f412753cddf391c7f971c52ca40a3f65dc5039b2939e8563", size = 7031448, upload-time = "2026-02-11T04:20:44.696Z" }, + { url = "https://files.pythonhosted.org/packages/49/70/f76296f53610bd17b2e7d31728b8b7825e3ac3b5b3688b51f52eab7c0818/pillow-12.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:b81b5e3511211631b3f672a595e3221252c90af017e399056d0faabb9538aa80", size = 2453651, upload-time = "2026-02-11T04:20:46.243Z" }, + { url = "https://files.pythonhosted.org/packages/07/d3/8df65da0d4df36b094351dce696f2989bec731d4f10e743b1c5f4da4d3bf/pillow-12.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab323b787d6e18b3d91a72fc99b1a2c28651e4358749842b8f8dfacd28ef2052", size = 5262803, upload-time = "2026-02-11T04:20:47.653Z" }, + { url = "https://files.pythonhosted.org/packages/d6/71/5026395b290ff404b836e636f51d7297e6c83beceaa87c592718747e670f/pillow-12.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:adebb5bee0f0af4909c30db0d890c773d1a92ffe83da908e2e9e720f8edf3984", size = 4657601, upload-time = "2026-02-11T04:20:49.328Z" }, + { url = "https://files.pythonhosted.org/packages/b1/2e/1001613d941c67442f745aff0f7cc66dd8df9a9c084eb497e6a543ee6f7e/pillow-12.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb66b7cc26f50977108790e2456b7921e773f23db5630261102233eb355a3b79", size = 6234995, upload-time = "2026-02-11T04:20:51.032Z" }, + { url = "https://files.pythonhosted.org/packages/07/26/246ab11455b2549b9233dbd44d358d033a2f780fa9007b61a913c5b2d24e/pillow-12.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee2810642b2898bb187ced9b349e95d2a7272930796e022efaf12e99dccd293", size = 8045012, upload-time = "2026-02-11T04:20:52.882Z" }, + { url = "https://files.pythonhosted.org/packages/b2/8b/07587069c27be7535ac1fe33874e32de118fbd34e2a73b7f83436a88368c/pillow-12.1.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0b1cd6232e2b618adcc54d9882e4e662a089d5768cd188f7c245b4c8c44a397", size = 6349638, upload-time = "2026-02-11T04:20:54.444Z" }, + { url = "https://files.pythonhosted.org/packages/ff/79/6df7b2ee763d619cda2fb4fea498e5f79d984dae304d45a8999b80d6cf5c/pillow-12.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7aac39bcf8d4770d089588a2e1dd111cbaa42df5a94be3114222057d68336bd0", size = 7041540, upload-time = "2026-02-11T04:20:55.97Z" }, + { url = "https://files.pythonhosted.org/packages/2c/5e/2ba19e7e7236d7529f4d873bdaf317a318896bac289abebd4bb00ef247f0/pillow-12.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ab174cd7d29a62dd139c44bf74b698039328f45cb03b4596c43473a46656b2f3", size = 6462613, upload-time = "2026-02-11T04:20:57.542Z" }, + { url = "https://files.pythonhosted.org/packages/03/03/31216ec124bb5c3dacd74ce8efff4cc7f52643653bad4825f8f08c697743/pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35", size = 7166745, upload-time = "2026-02-11T04:20:59.196Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e7/7c4552d80052337eb28653b617eafdef39adfb137c49dd7e831b8dc13bc5/pillow-12.1.1-cp312-cp312-win32.whl", hash = "sha256:5d1f9575a12bed9e9eedd9a4972834b08c97a352bd17955ccdebfeca5913fa0a", size = 6328823, upload-time = "2026-02-11T04:21:01.385Z" }, + { url = "https://files.pythonhosted.org/packages/3d/17/688626d192d7261bbbf98846fc98995726bddc2c945344b65bec3a29d731/pillow-12.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:21329ec8c96c6e979cd0dfd29406c40c1d52521a90544463057d2aaa937d66a6", size = 7033367, upload-time = "2026-02-11T04:21:03.536Z" }, + { url = "https://files.pythonhosted.org/packages/ed/fe/a0ef1f73f939b0eca03ee2c108d0043a87468664770612602c63266a43c4/pillow-12.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:af9a332e572978f0218686636610555ae3defd1633597be015ed50289a03c523", size = 2453811, upload-time = "2026-02-11T04:21:05.116Z" }, + { url = "https://files.pythonhosted.org/packages/d5/11/6db24d4bd7685583caeae54b7009584e38da3c3d4488ed4cd25b439de486/pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d242e8ac078781f1de88bf823d70c1a9b3c7950a44cdf4b7c012e22ccbcd8e4e", size = 4062689, upload-time = "2026-02-11T04:21:06.804Z" }, + { url = "https://files.pythonhosted.org/packages/33/c0/ce6d3b1fe190f0021203e0d9b5b99e57843e345f15f9ef22fcd43842fd21/pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:02f84dfad02693676692746df05b89cf25597560db2857363a208e393429f5e9", size = 4138535, upload-time = "2026-02-11T04:21:08.452Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c6/d5eb6a4fb32a3f9c21a8c7613ec706534ea1cf9f4b3663e99f0d83f6fca8/pillow-12.1.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e65498daf4b583091ccbb2556c7000abf0f3349fcd57ef7adc9a84a394ed29f6", size = 3601364, upload-time = "2026-02-11T04:21:10.194Z" }, + { url = "https://files.pythonhosted.org/packages/14/a1/16c4b823838ba4c9c52c0e6bbda903a3fe5a1bdbf1b8eb4fff7156f3e318/pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60", size = 5262561, upload-time = "2026-02-11T04:21:11.742Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ad/ad9dc98ff24f485008aa5cdedaf1a219876f6f6c42a4626c08bc4e80b120/pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2", size = 4657460, upload-time = "2026-02-11T04:21:13.786Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f1a4ea9a895b5732152789326202a82464d5254759fbacae4deea3069334/pillow-12.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5bddd742a44b7e6b1e773ab5db102bd7a94c32555ba656e76d319d19c3850", size = 6232698, upload-time = "2026-02-11T04:21:15.949Z" }, + { url = "https://files.pythonhosted.org/packages/95/f4/86f51b8745070daf21fd2e5b1fe0eb35d4db9ca26e6d58366562fb56a743/pillow-12.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc44ef1f3de4f45b50ccf9136999d71abb99dca7706bc75d222ed350b9fd2289", size = 8041706, upload-time = "2026-02-11T04:21:17.723Z" }, + { url = "https://files.pythonhosted.org/packages/29/9b/d6ecd956bb1266dd1045e995cce9b8d77759e740953a1c9aad9502a0461e/pillow-12.1.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a8eb7ed8d4198bccbd07058416eeec51686b498e784eda166395a23eb99138e", size = 6346621, upload-time = "2026-02-11T04:21:19.547Z" }, + { url = "https://files.pythonhosted.org/packages/71/24/538bff45bde96535d7d998c6fed1a751c75ac7c53c37c90dc2601b243893/pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717", size = 7038069, upload-time = "2026-02-11T04:21:21.378Z" }, + { url = "https://files.pythonhosted.org/packages/94/0e/58cb1a6bc48f746bc4cb3adb8cabff73e2742c92b3bf7a220b7cf69b9177/pillow-12.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:518a48c2aab7ce596d3bf79d0e275661b846e86e4d0e7dec34712c30fe07f02a", size = 6460040, upload-time = "2026-02-11T04:21:23.148Z" }, + { url = "https://files.pythonhosted.org/packages/6c/57/9045cb3ff11eeb6c1adce3b2d60d7d299d7b273a2e6c8381a524abfdc474/pillow-12.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a550ae29b95c6dc13cf69e2c9dc5747f814c54eeb2e32d683e5e93af56caa029", size = 7164523, upload-time = "2026-02-11T04:21:25.01Z" }, + { url = "https://files.pythonhosted.org/packages/73/f2/9be9cb99f2175f0d4dbadd6616ce1bf068ee54a28277ea1bf1fbf729c250/pillow-12.1.1-cp313-cp313-win32.whl", hash = "sha256:a003d7422449f6d1e3a34e3dd4110c22148336918ddbfc6a32581cd54b2e0b2b", size = 6332552, upload-time = "2026-02-11T04:21:27.238Z" }, + { url = "https://files.pythonhosted.org/packages/3f/eb/b0834ad8b583d7d9d42b80becff092082a1c3c156bb582590fcc973f1c7c/pillow-12.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:344cf1e3dab3be4b1fa08e449323d98a2a3f819ad20f4b22e77a0ede31f0faa1", size = 7040108, upload-time = "2026-02-11T04:21:29.462Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7d/fc09634e2aabdd0feabaff4a32f4a7d97789223e7c2042fd805ea4b4d2c2/pillow-12.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c0dd1636633e7e6a0afe7bf6a51a14992b7f8e60de5789018ebbdfae55b040a", size = 2453712, upload-time = "2026-02-11T04:21:31.072Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/b9d62794fc8a0dd14c1943df68347badbd5511103e0d04c035ffe5cf2255/pillow-12.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0330d233c1a0ead844fc097a7d16c0abff4c12e856c0b325f231820fee1f39da", size = 5264880, upload-time = "2026-02-11T04:21:32.865Z" }, + { url = "https://files.pythonhosted.org/packages/26/9d/e03d857d1347fa5ed9247e123fcd2a97b6220e15e9cb73ca0a8d91702c6e/pillow-12.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dae5f21afb91322f2ff791895ddd8889e5e947ff59f71b46041c8ce6db790bc", size = 4660616, upload-time = "2026-02-11T04:21:34.97Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ec/8a6d22afd02570d30954e043f09c32772bfe143ba9285e2fdb11284952cd/pillow-12.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e0c664be47252947d870ac0d327fea7e63985a08794758aa8af5b6cb6ec0c9c", size = 6269008, upload-time = "2026-02-11T04:21:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/3d/1d/6d875422c9f28a4a361f495a5f68d9de4a66941dc2c619103ca335fa6446/pillow-12.1.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:691ab2ac363b8217f7d31b3497108fb1f50faab2f75dfb03284ec2f217e87bf8", size = 8073226, upload-time = "2026-02-11T04:21:38.585Z" }, + { url = "https://files.pythonhosted.org/packages/a1/cd/134b0b6ee5eda6dc09e25e24b40fdafe11a520bc725c1d0bbaa5e00bf95b/pillow-12.1.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9e8064fb1cc019296958595f6db671fba95209e3ceb0c4734c9baf97de04b20", size = 6380136, upload-time = "2026-02-11T04:21:40.562Z" }, + { url = "https://files.pythonhosted.org/packages/7a/a9/7628f013f18f001c1b98d8fffe3452f306a70dc6aba7d931019e0492f45e/pillow-12.1.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:472a8d7ded663e6162dafdf20015c486a7009483ca671cece7a9279b512fcb13", size = 7067129, upload-time = "2026-02-11T04:21:42.521Z" }, + { url = "https://files.pythonhosted.org/packages/1e/f8/66ab30a2193b277785601e82ee2d49f68ea575d9637e5e234faaa98efa4c/pillow-12.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:89b54027a766529136a06cfebeecb3a04900397a3590fd252160b888479517bf", size = 6491807, upload-time = "2026-02-11T04:21:44.22Z" }, + { url = "https://files.pythonhosted.org/packages/da/0b/a877a6627dc8318fdb84e357c5e1a758c0941ab1ddffdafd231983788579/pillow-12.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:86172b0831b82ce4f7877f280055892b31179e1576aa00d0df3bb1bbf8c3e524", size = 7190954, upload-time = "2026-02-11T04:21:46.114Z" }, + { url = "https://files.pythonhosted.org/packages/83/43/6f732ff85743cf746b1361b91665d9f5155e1483817f693f8d57ea93147f/pillow-12.1.1-cp313-cp313t-win32.whl", hash = "sha256:44ce27545b6efcf0fdbdceb31c9a5bdea9333e664cda58a7e674bb74608b3986", size = 6336441, upload-time = "2026-02-11T04:21:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/3b/44/e865ef3986611bb75bfabdf94a590016ea327833f434558801122979cd0e/pillow-12.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a285e3eb7a5a45a2ff504e31f4a8d1b12ef62e84e5411c6804a42197c1cf586c", size = 7045383, upload-time = "2026-02-11T04:21:50.015Z" }, + { url = "https://files.pythonhosted.org/packages/a8/c6/f4fb24268d0c6908b9f04143697ea18b0379490cb74ba9e8d41b898bd005/pillow-12.1.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cc7d296b5ea4d29e6570dabeaed58d31c3fea35a633a69679fb03d7664f43fb3", size = 2456104, upload-time = "2026-02-11T04:21:51.633Z" }, + { url = "https://files.pythonhosted.org/packages/03/d0/bebb3ffbf31c5a8e97241476c4cf8b9828954693ce6744b4a2326af3e16b/pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:417423db963cb4be8bac3fc1204fe61610f6abeed1580a7a2cbb2fbda20f12af", size = 4062652, upload-time = "2026-02-11T04:21:53.19Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c0/0e16fb0addda4851445c28f8350d8c512f09de27bbb0d6d0bbf8b6709605/pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:b957b71c6b2387610f556a7eb0828afbe40b4a98036fc0d2acfa5a44a0c2036f", size = 4138823, upload-time = "2026-02-11T04:22:03.088Z" }, + { url = "https://files.pythonhosted.org/packages/6b/fb/6170ec655d6f6bb6630a013dd7cf7bc218423d7b5fa9071bf63dc32175ae/pillow-12.1.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:097690ba1f2efdeb165a20469d59d8bb03c55fb6621eb2041a060ae8ea3e9642", size = 3601143, upload-time = "2026-02-11T04:22:04.909Z" }, + { url = "https://files.pythonhosted.org/packages/59/04/dc5c3f297510ba9a6837cbb318b87dd2b8f73eb41a43cc63767f65cb599c/pillow-12.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2815a87ab27848db0321fb78c7f0b2c8649dee134b7f2b80c6a45c6831d75ccd", size = 5266254, upload-time = "2026-02-11T04:22:07.656Z" }, + { url = "https://files.pythonhosted.org/packages/05/30/5db1236b0d6313f03ebf97f5e17cda9ca060f524b2fcc875149a8360b21c/pillow-12.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7ed2c6543bad5a7d5530eb9e78c53132f93dfa44a28492db88b41cdab885202", size = 4657499, upload-time = "2026-02-11T04:22:09.613Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/008d2ca0eb612e81968e8be0bbae5051efba24d52debf930126d7eaacbba/pillow-12.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:652a2c9ccfb556235b2b501a3a7cf3742148cd22e04b5625c5fe057ea3e3191f", size = 6232137, upload-time = "2026-02-11T04:22:11.434Z" }, + { url = "https://files.pythonhosted.org/packages/70/f1/f14d5b8eeb4b2cd62b9f9f847eb6605f103df89ef619ac68f92f748614ea/pillow-12.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6e4571eedf43af33d0fc233a382a76e849badbccdf1ac438841308652a08e1f", size = 8042721, upload-time = "2026-02-11T04:22:13.321Z" }, + { url = "https://files.pythonhosted.org/packages/5a/d6/17824509146e4babbdabf04d8171491fa9d776f7061ff6e727522df9bd03/pillow-12.1.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b574c51cf7d5d62e9be37ba446224b59a2da26dc4c1bb2ecbe936a4fb1a7cb7f", size = 6347798, upload-time = "2026-02-11T04:22:15.449Z" }, + { url = "https://files.pythonhosted.org/packages/d1/ee/c85a38a9ab92037a75615aba572c85ea51e605265036e00c5b67dfafbfe2/pillow-12.1.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a37691702ed687799de29a518d63d4682d9016932db66d4e90c345831b02fb4e", size = 7039315, upload-time = "2026-02-11T04:22:17.24Z" }, + { url = "https://files.pythonhosted.org/packages/ec/f3/bc8ccc6e08a148290d7523bde4d9a0d6c981db34631390dc6e6ec34cacf6/pillow-12.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f95c00d5d6700b2b890479664a06e754974848afaae5e21beb4d83c106923fd0", size = 6462360, upload-time = "2026-02-11T04:22:19.111Z" }, + { url = "https://files.pythonhosted.org/packages/f6/ab/69a42656adb1d0665ab051eec58a41f169ad295cf81ad45406963105408f/pillow-12.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:559b38da23606e68681337ad74622c4dbba02254fc9cb4488a305dd5975c7eeb", size = 7165438, upload-time = "2026-02-11T04:22:21.041Z" }, + { url = "https://files.pythonhosted.org/packages/02/46/81f7aa8941873f0f01d4b55cc543b0a3d03ec2ee30d617a0448bf6bd6dec/pillow-12.1.1-cp314-cp314-win32.whl", hash = "sha256:03edcc34d688572014ff223c125a3f77fb08091e4607e7745002fc214070b35f", size = 6431503, upload-time = "2026-02-11T04:22:22.833Z" }, + { url = "https://files.pythonhosted.org/packages/40/72/4c245f7d1044b67affc7f134a09ea619d4895333d35322b775b928180044/pillow-12.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:50480dcd74fa63b8e78235957d302d98d98d82ccbfac4c7e12108ba9ecbdba15", size = 7176748, upload-time = "2026-02-11T04:22:24.64Z" }, + { url = "https://files.pythonhosted.org/packages/e4/ad/8a87bdbe038c5c698736e3348af5c2194ffb872ea52f11894c95f9305435/pillow-12.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:5cb1785d97b0c3d1d1a16bc1d710c4a0049daefc4935f3a8f31f827f4d3d2e7f", size = 2544314, upload-time = "2026-02-11T04:22:26.685Z" }, + { url = "https://files.pythonhosted.org/packages/6c/9d/efd18493f9de13b87ede7c47e69184b9e859e4427225ea962e32e56a49bc/pillow-12.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1f90cff8aa76835cba5769f0b3121a22bd4eb9e6884cfe338216e557a9a548b8", size = 5268612, upload-time = "2026-02-11T04:22:29.884Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f1/4f42eb2b388eb2ffc660dcb7f7b556c1015c53ebd5f7f754965ef997585b/pillow-12.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1f1be78ce9466a7ee64bfda57bdba0f7cc499d9794d518b854816c41bf0aa4e9", size = 4660567, upload-time = "2026-02-11T04:22:31.799Z" }, + { url = "https://files.pythonhosted.org/packages/01/54/df6ef130fa43e4b82e32624a7b821a2be1c5653a5fdad8469687a7db4e00/pillow-12.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42fc1f4677106188ad9a55562bbade416f8b55456f522430fadab3cef7cd4e60", size = 6269951, upload-time = "2026-02-11T04:22:33.921Z" }, + { url = "https://files.pythonhosted.org/packages/a9/48/618752d06cc44bb4aae8ce0cd4e6426871929ed7b46215638088270d9b34/pillow-12.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98edb152429ab62a1818039744d8fbb3ccab98a7c29fc3d5fcef158f3f1f68b7", size = 8074769, upload-time = "2026-02-11T04:22:35.877Z" }, + { url = "https://files.pythonhosted.org/packages/c3/bd/f1d71eb39a72fa088d938655afba3e00b38018d052752f435838961127d8/pillow-12.1.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d470ab1178551dd17fdba0fef463359c41aaa613cdcd7ff8373f54be629f9f8f", size = 6381358, upload-time = "2026-02-11T04:22:37.698Z" }, + { url = "https://files.pythonhosted.org/packages/64/ef/c784e20b96674ed36a5af839305f55616f8b4f8aa8eeccf8531a6e312243/pillow-12.1.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6408a7b064595afcab0a49393a413732a35788f2a5092fdc6266952ed67de586", size = 7068558, upload-time = "2026-02-11T04:22:39.597Z" }, + { url = "https://files.pythonhosted.org/packages/73/cb/8059688b74422ae61278202c4e1ad992e8a2e7375227be0a21c6b87ca8d5/pillow-12.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5d8c41325b382c07799a3682c1c258469ea2ff97103c53717b7893862d0c98ce", size = 6493028, upload-time = "2026-02-11T04:22:42.73Z" }, + { url = "https://files.pythonhosted.org/packages/c6/da/e3c008ed7d2dd1f905b15949325934510b9d1931e5df999bb15972756818/pillow-12.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7697918b5be27424e9ce568193efd13d925c4481dd364e43f5dff72d33e10f8", size = 7191940, upload-time = "2026-02-11T04:22:44.543Z" }, + { url = "https://files.pythonhosted.org/packages/01/4a/9202e8d11714c1fc5951f2e1ef362f2d7fbc595e1f6717971d5dd750e969/pillow-12.1.1-cp314-cp314t-win32.whl", hash = "sha256:d2912fd8114fc5545aa3a4b5576512f64c55a03f3ebcca4c10194d593d43ea36", size = 6438736, upload-time = "2026-02-11T04:22:46.347Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ca/cbce2327eb9885476b3957b2e82eb12c866a8b16ad77392864ad601022ce/pillow-12.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4ceb838d4bd9dab43e06c363cab2eebf63846d6a4aeaea283bbdfd8f1a8ed58b", size = 7182894, upload-time = "2026-02-11T04:22:48.114Z" }, + { url = "https://files.pythonhosted.org/packages/ec/d2/de599c95ba0a973b94410477f8bf0b6f0b5e67360eb89bcb1ad365258beb/pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334", size = 2546446, upload-time = "2026-02-11T04:22:50.342Z" }, + { url = "https://files.pythonhosted.org/packages/56/11/5d43209aa4cb58e0cc80127956ff1796a68b928e6324bbf06ef4db34367b/pillow-12.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:600fd103672b925fe62ed08e0d874ea34d692474df6f4bf7ebe148b30f89f39f", size = 5228606, upload-time = "2026-02-11T04:22:52.106Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d5/3b005b4e4fda6698b371fa6c21b097d4707585d7db99e98d9b0b87ac612a/pillow-12.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:665e1b916b043cef294bc54d47bf02d87e13f769bc4bc5fa225a24b3a6c5aca9", size = 4622321, upload-time = "2026-02-11T04:22:53.827Z" }, + { url = "https://files.pythonhosted.org/packages/df/36/ed3ea2d594356fd8037e5a01f6156c74bc8d92dbb0fa60746cc96cabb6e8/pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:495c302af3aad1ca67420ddd5c7bd480c8867ad173528767d906428057a11f0e", size = 5247579, upload-time = "2026-02-11T04:22:56.094Z" }, + { url = "https://files.pythonhosted.org/packages/54/9a/9cc3e029683cf6d20ae5085da0dafc63148e3252c2f13328e553aaa13cfb/pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fd420ef0c52c88b5a035a0886f367748c72147b2b8f384c9d12656678dfdfa9", size = 6989094, upload-time = "2026-02-11T04:22:58.288Z" }, + { url = "https://files.pythonhosted.org/packages/00/98/fc53ab36da80b88df0967896b6c4b4cd948a0dc5aa40a754266aa3ae48b3/pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f975aa7ef9684ce7e2c18a3aa8f8e2106ce1e46b94ab713d156b2898811651d3", size = 5313850, upload-time = "2026-02-11T04:23:00.554Z" }, + { url = "https://files.pythonhosted.org/packages/30/02/00fa585abfd9fe9d73e5f6e554dc36cc2b842898cbfc46d70353dae227f8/pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8089c852a56c2966cf18835db62d9b34fef7ba74c726ad943928d494fa7f4735", size = 5963343, upload-time = "2026-02-11T04:23:02.934Z" }, + { url = "https://files.pythonhosted.org/packages/f2/26/c56ce33ca856e358d27fda9676c055395abddb82c35ac0f593877ed4562e/pillow-12.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cb9bb857b2d057c6dfc72ac5f3b44836924ba15721882ef103cecb40d002d80e", size = 7029880, upload-time = "2026-02-11T04:23:04.783Z" }, ] [[package]] name = "pint" -version = "0.25.2" +version = "0.25.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flexcache" }, @@ -1458,31 +1479,31 @@ dependencies = [ { name = "platformdirs" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5f/74/bc3f671997158aef171194c3c4041e549946f4784b8690baa0626a0a164b/pint-0.25.2.tar.gz", hash = "sha256:85a45d1da8fe9c9f7477fed8aef59ad2b939af3d6611507e1a9cbdacdcd3450a", size = 254467, upload-time = "2025-11-06T22:08:09.184Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/9d/b1379cdbd33a49d17d627bc24e2b63cca06a1c5343b38072d2889499e82e/pint-0.25.3.tar.gz", hash = "sha256:f8f5df6cf65314d74da1ade1bf96f8e3e4d0c41b51577ac53c49e7d44ca5acee", size = 255106, upload-time = "2026-03-19T21:57:08.72Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/88/550d41e81e6d43335603a960cd9c75c1d88f9cf01bc9d4ee8e86290aba7d/pint-0.25.2-py3-none-any.whl", hash = "sha256:ca35ab1d8eeeb6f7d9942b3cb5f34ca42b61cdd5fb3eae79531553dcca04dda7", size = 306762, upload-time = "2025-11-06T22:08:07.745Z" }, + { url = "https://files.pythonhosted.org/packages/1b/dd/a9fe6a0a09512da23951c68bf36466aeecd89def3183dc095edbc807ddc5/pint-0.25.3-py3-none-any.whl", hash = "sha256:27eb25143bd5de9fcc4d5a4b484f16faf6b4615aa93ece6b3373a8c1a3c1b97d", size = 307488, upload-time = "2026-03-19T21:57:07.022Z" }, ] [[package]] name = "platformdirs" -version = "4.5.1" +version = "4.9.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, + { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, ] [[package]] name = "plotly" -version = "6.5.2" +version = "6.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "narwhals" }, { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e3/4f/8a10a9b9f5192cb6fdef62f1d77fa7d834190b2c50c0cd256bd62879212b/plotly-6.5.2.tar.gz", hash = "sha256:7478555be0198562d1435dee4c308268187553cc15516a2f4dd034453699e393", size = 7015695, upload-time = "2026-01-14T21:26:51.222Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/fb/41efe84970cfddefd4ccf025e2cbfafe780004555f583e93dba3dac2cdef/plotly-6.6.0.tar.gz", hash = "sha256:b897f15f3b02028d69f755f236be890ba950d0a42d7dfc619b44e2d8cea8748c", size = 7027956, upload-time = "2026-03-02T21:10:25.321Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl", hash = "sha256:91757653bd9c550eeea2fa2404dba6b85d1e366d54804c340b2c874e5a7eb4a4", size = 9895973, upload-time = "2026-01-14T21:26:47.135Z" }, + { url = "https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl", hash = "sha256:8d6daf0f87412e0c0bfe72e809d615217ab57cc715899a1e5145135a7800d1d0", size = 9910315, upload-time = "2026-03-02T21:10:18.131Z" }, ] [[package]] @@ -1494,15 +1515,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] -[[package]] -name = "ply" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/69/882ee5c9d017149285cab114ebeab373308ef0f874fcdac9beb90e0ac4da/ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3", size = 159130, upload-time = "2018-02-15T19:01:31.097Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce", size = 49567, upload-time = "2018-02-15T19:01:27.172Z" }, -] - [[package]] name = "pre-commit" version = "4.5.1" @@ -1556,39 +1568,39 @@ dev = [ { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-argparse-cli" }, { name = "sphinx-autodoc-typehints", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx-autodoc-typehints", version = "3.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "sphinx-autodoc-typehints", version = "3.9.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-mermaid" }, ] [package.metadata] requires-dist = [ - { name = "component-model", specifier = ">=0.3.2" }, - { name = "docutils", marker = "extra == 'rest'", specifier = ">=0.3" }, + { name = "component-model", specifier = ">=0.4.0" }, + { name = "docutils", marker = "extra == 'rest'", specifier = ">=0.22.4" }, { name = "fmpy", marker = "extra == 'test'", specifier = ">=0.3.21" }, { name = "libcosimpy", marker = "extra == 'test'", specifier = ">=0.0.5" }, { name = "matplotlib", marker = "extra == 'test'", specifier = ">=3.10" }, { name = "numpy", specifier = ">=2.3" }, - { name = "plotly", marker = "extra == 'test'", specifier = ">=6.3" }, + { name = "plotly", marker = "extra == 'test'", specifier = ">=6.5" }, { name = "pythonfmu", specifier = ">=0.7.0" }, - { name = "scipy", specifier = ">=1.16" }, + { name = "scipy", specifier = ">=1.17.0" }, ] provides-extras = ["rest", "test"] [package.metadata.requires-dev] dev = [ - { name = "furo", specifier = ">=2025.9" }, - { name = "mypy", specifier = ">=1.18" }, - { name = "myst-parser", specifier = ">=4.0" }, - { name = "pre-commit", specifier = ">=4.3" }, - { name = "pyright", specifier = ">=1.1.407" }, - { name = "pytest", specifier = ">=8.4" }, + { name = "furo", specifier = ">=2025.12" }, + { name = "mypy", specifier = ">=1.19.1" }, + { name = "myst-parser", specifier = ">=5.0" }, + { name = "pre-commit", specifier = ">=4.5" }, + { name = "pyright", specifier = ">=1.1.408" }, + { name = "pytest", specifier = ">=9.0" }, { name = "pytest-cov", specifier = ">=7.0" }, - { name = "ruff", specifier = ">=0.14.3" }, - { name = "sourcery", specifier = ">=1.40" }, - { name = "sphinx", specifier = ">=8.2" }, - { name = "sphinx-argparse-cli", specifier = ">=1.20" }, - { name = "sphinx-autodoc-typehints", specifier = ">=3.5" }, - { name = "sphinxcontrib-mermaid", specifier = ">=1.0.0" }, + { name = "ruff", specifier = ">=0.15.1" }, + { name = "sourcery", specifier = ">=1.43.0" }, + { name = "sphinx", specifier = ">=9.0" }, + { name = "sphinx-argparse-cli", specifier = ">=1.20.1" }, + { name = "sphinx-autodoc-typehints", specifier = ">=3.6" }, + { name = "sphinxcontrib-mermaid", specifier = ">=2.0" }, ] [[package]] @@ -1640,16 +1652,16 @@ wheels = [ [[package]] name = "pytest-cov" -version = "7.0.0" +version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, ] [[package]] @@ -1664,6 +1676,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] +[[package]] +name = "python-discovery" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/90/bcce6b46823c9bec1757c964dc37ed332579be512e17a30e9698095dcae4/python_discovery-1.2.0.tar.gz", hash = "sha256:7d33e350704818b09e3da2bd419d37e21e7c30db6e0977bb438916e06b41b5b1", size = 58055, upload-time = "2026-03-19T01:43:08.248Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl", hash = "sha256:1e108f1bbe2ed0ef089823d28805d5ad32be8e734b86a5f212bf89b71c266e4a", size = 31524, upload-time = "2026-03-19T01:43:07.045Z" }, +] + [[package]] name = "pythonfmu" version = "0.7.0" @@ -1744,7 +1769,7 @@ wheels = [ [[package]] name = "requests" -version = "2.32.5" +version = "2.33.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -1752,9 +1777,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, + { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" }, ] [[package]] @@ -1876,111 +1901,98 @@ wheels = [ [[package]] name = "ruff" -version = "0.14.14" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/06/f71e3a86b2df0dfa2d2f72195941cd09b44f87711cb7fa5193732cb9a5fc/ruff-0.14.14.tar.gz", hash = "sha256:2d0f819c9a90205f3a867dbbd0be083bee9912e170fd7d9704cc8ae45824896b", size = 4515732, upload-time = "2026-01-22T22:30:17.527Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/89/20a12e97bc6b9f9f68343952da08a8099c57237aef953a56b82711d55edd/ruff-0.14.14-py3-none-linux_armv6l.whl", hash = "sha256:7cfe36b56e8489dee8fbc777c61959f60ec0f1f11817e8f2415f429552846aed", size = 10467650, upload-time = "2026-01-22T22:30:08.578Z" }, - { url = "https://files.pythonhosted.org/packages/a3/b1/c5de3fd2d5a831fcae21beda5e3589c0ba67eec8202e992388e4b17a6040/ruff-0.14.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6006a0082336e7920b9573ef8a7f52eec837add1265cc74e04ea8a4368cd704c", size = 10883245, upload-time = "2026-01-22T22:30:04.155Z" }, - { url = "https://files.pythonhosted.org/packages/b8/7c/3c1db59a10e7490f8f6f8559d1db8636cbb13dccebf18686f4e3c9d7c772/ruff-0.14.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:026c1d25996818f0bf498636686199d9bd0d9d6341c9c2c3b62e2a0198b758de", size = 10231273, upload-time = "2026-01-22T22:30:34.642Z" }, - { url = "https://files.pythonhosted.org/packages/a1/6e/5e0e0d9674be0f8581d1f5e0f0a04761203affce3232c1a1189d0e3b4dad/ruff-0.14.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f666445819d31210b71e0a6d1c01e24447a20b85458eea25a25fe8142210ae0e", size = 10585753, upload-time = "2026-01-22T22:30:31.781Z" }, - { url = "https://files.pythonhosted.org/packages/23/09/754ab09f46ff1884d422dc26d59ba18b4e5d355be147721bb2518aa2a014/ruff-0.14.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c0f18b922c6d2ff9a5e6c3ee16259adc513ca775bcf82c67ebab7cbd9da5bc8", size = 10286052, upload-time = "2026-01-22T22:30:24.827Z" }, - { url = "https://files.pythonhosted.org/packages/c8/cc/e71f88dd2a12afb5f50733851729d6b571a7c3a35bfdb16c3035132675a0/ruff-0.14.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1629e67489c2dea43e8658c3dba659edbfd87361624b4040d1df04c9740ae906", size = 11043637, upload-time = "2026-01-22T22:30:13.239Z" }, - { url = "https://files.pythonhosted.org/packages/67/b2/397245026352494497dac935d7f00f1468c03a23a0c5db6ad8fc49ca3fb2/ruff-0.14.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:27493a2131ea0f899057d49d303e4292b2cae2bb57253c1ed1f256fbcd1da480", size = 12194761, upload-time = "2026-01-22T22:30:22.542Z" }, - { url = "https://files.pythonhosted.org/packages/5b/06/06ef271459f778323112c51b7587ce85230785cd64e91772034ddb88f200/ruff-0.14.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ff589aab3f5b539e35db38425da31a57521efd1e4ad1ae08fc34dbe30bd7df", size = 12005701, upload-time = "2026-01-22T22:30:20.499Z" }, - { url = "https://files.pythonhosted.org/packages/41/d6/99364514541cf811ccc5ac44362f88df66373e9fec1b9d1c4cc830593fe7/ruff-0.14.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc12d74eef0f29f51775f5b755913eb523546b88e2d733e1d701fe65144e89b", size = 11282455, upload-time = "2026-01-22T22:29:59.679Z" }, - { url = "https://files.pythonhosted.org/packages/ca/71/37daa46f89475f8582b7762ecd2722492df26421714a33e72ccc9a84d7a5/ruff-0.14.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb8481604b7a9e75eff53772496201690ce2687067e038b3cc31aaf16aa0b974", size = 11215882, upload-time = "2026-01-22T22:29:57.032Z" }, - { url = "https://files.pythonhosted.org/packages/2c/10/a31f86169ec91c0705e618443ee74ede0bdd94da0a57b28e72db68b2dbac/ruff-0.14.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:14649acb1cf7b5d2d283ebd2f58d56b75836ed8c6f329664fa91cdea19e76e66", size = 11180549, upload-time = "2026-01-22T22:30:27.175Z" }, - { url = "https://files.pythonhosted.org/packages/fd/1e/c723f20536b5163adf79bdd10c5f093414293cdf567eed9bdb7b83940f3f/ruff-0.14.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8058d2145566510790eab4e2fad186002e288dec5e0d343a92fe7b0bc1b3e13", size = 10543416, upload-time = "2026-01-22T22:30:01.964Z" }, - { url = "https://files.pythonhosted.org/packages/3e/34/8a84cea7e42c2d94ba5bde1d7a4fae164d6318f13f933d92da6d7c2041ff/ruff-0.14.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e651e977a79e4c758eb807f0481d673a67ffe53cfa92209781dfa3a996cf8412", size = 10285491, upload-time = "2026-01-22T22:30:29.51Z" }, - { url = "https://files.pythonhosted.org/packages/55/ef/b7c5ea0be82518906c978e365e56a77f8de7678c8bb6651ccfbdc178c29f/ruff-0.14.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cc8b22da8d9d6fdd844a68ae937e2a0adf9b16514e9a97cc60355e2d4b219fc3", size = 10733525, upload-time = "2026-01-22T22:30:06.499Z" }, - { url = "https://files.pythonhosted.org/packages/6a/5b/aaf1dfbcc53a2811f6cc0a1759de24e4b03e02ba8762daabd9b6bd8c59e3/ruff-0.14.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:16bc890fb4cc9781bb05beb5ab4cd51be9e7cb376bf1dd3580512b24eb3fda2b", size = 11315626, upload-time = "2026-01-22T22:30:36.848Z" }, - { url = "https://files.pythonhosted.org/packages/2c/aa/9f89c719c467dfaf8ad799b9bae0df494513fb21d31a6059cb5870e57e74/ruff-0.14.14-py3-none-win32.whl", hash = "sha256:b530c191970b143375b6a68e6f743800b2b786bbcf03a7965b06c4bf04568167", size = 10502442, upload-time = "2026-01-22T22:30:38.93Z" }, - { url = "https://files.pythonhosted.org/packages/87/44/90fa543014c45560cae1fffc63ea059fb3575ee6e1cb654562197e5d16fb/ruff-0.14.14-py3-none-win_amd64.whl", hash = "sha256:3dde1435e6b6fe5b66506c1dff67a421d0b7f6488d466f651c07f4cab3bf20fd", size = 11630486, upload-time = "2026-01-22T22:30:10.852Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6a/40fee331a52339926a92e17ae748827270b288a35ef4a15c9c8f2ec54715/ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c", size = 10920448, upload-time = "2026-01-22T22:30:15.417Z" }, +version = "0.15.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/22/9e4f66ee588588dc6c9af6a994e12d26e19efbe874d1a909d09a6dac7a59/ruff-0.15.7.tar.gz", hash = "sha256:04f1ae61fc20fe0b148617c324d9d009b5f63412c0b16474f3d5f1a1a665f7ac", size = 4601277, upload-time = "2026-03-19T16:26:22.605Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/2f/0b08ced94412af091807b6119ca03755d651d3d93a242682bf020189db94/ruff-0.15.7-py3-none-linux_armv6l.whl", hash = "sha256:a81cc5b6910fb7dfc7c32d20652e50fa05963f6e13ead3c5915c41ac5d16668e", size = 10489037, upload-time = "2026-03-19T16:26:32.47Z" }, + { url = "https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:722d165bd52403f3bdabc0ce9e41fc47070ac56d7a91b4e0d097b516a53a3477", size = 10955433, upload-time = "2026-03-19T16:27:00.205Z" }, + { url = "https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fbc2448094262552146cbe1b9643a92f66559d3761f1ad0656d4991491af49e", size = 10269302, upload-time = "2026-03-19T16:26:26.183Z" }, + { url = "https://files.pythonhosted.org/packages/eb/5d/32b5c44ccf149a26623671df49cbfbd0a0ae511ff3df9d9d2426966a8d57/ruff-0.15.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b39329b60eba44156d138275323cc726bbfbddcec3063da57caa8a8b1d50adf", size = 10607625, upload-time = "2026-03-19T16:27:03.263Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f1/f0001cabe86173aaacb6eb9bb734aa0605f9a6aa6fa7d43cb49cbc4af9c9/ruff-0.15.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87768c151808505f2bfc93ae44e5f9e7c8518943e5074f76ac21558ef5627c85", size = 10324743, upload-time = "2026-03-19T16:27:09.791Z" }, + { url = "https://files.pythonhosted.org/packages/7a/87/b8a8f3d56b8d848008559e7c9d8bf367934d5367f6d932ba779456e2f73b/ruff-0.15.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb0511670002c6c529ec66c0e30641c976c8963de26a113f3a30456b702468b0", size = 11138536, upload-time = "2026-03-19T16:27:06.101Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f2/4fd0d05aab0c5934b2e1464784f85ba2eab9d54bffc53fb5430d1ed8b829/ruff-0.15.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0d19644f801849229db8345180a71bee5407b429dd217f853ec515e968a6912", size = 11994292, upload-time = "2026-03-19T16:26:48.718Z" }, + { url = "https://files.pythonhosted.org/packages/64/22/fc4483871e767e5e95d1622ad83dad5ebb830f762ed0420fde7dfa9d9b08/ruff-0.15.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4806d8e09ef5e84eb19ba833d0442f7e300b23fe3f0981cae159a248a10f0036", size = 11398981, upload-time = "2026-03-19T16:26:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce0896488562f09a27b9c91b1f58a097457143931f3c4d519690dea54e624c5", size = 11242422, upload-time = "2026-03-19T16:26:29.277Z" }, + { url = "https://files.pythonhosted.org/packages/5d/3a/a7060f145bfdcce4c987ea27788b30c60e2c81d6e9a65157ca8afe646328/ruff-0.15.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1852ce241d2bc89e5dc823e03cff4ce73d816b5c6cdadd27dbfe7b03217d2a12", size = 11232158, upload-time = "2026-03-19T16:26:42.321Z" }, + { url = "https://files.pythonhosted.org/packages/a7/53/90fbb9e08b29c048c403558d3cdd0adf2668b02ce9d50602452e187cd4af/ruff-0.15.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5f3e4b221fb4bd293f79912fc5e93a9063ebd6d0dcbd528f91b89172a9b8436c", size = 10577861, upload-time = "2026-03-19T16:26:57.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/aa/5f486226538fe4d0f0439e2da1716e1acf895e2a232b26f2459c55f8ddad/ruff-0.15.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b15e48602c9c1d9bdc504b472e90b90c97dc7d46c7028011ae67f3861ceba7b4", size = 10327310, upload-time = "2026-03-19T16:26:35.909Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/271afdffb81fe7bfc8c43ba079e9d96238f674380099457a74ccb3863857/ruff-0.15.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b4705e0e85cedc74b0a23cf6a179dbb3df184cb227761979cc76c0440b5ab0d", size = 10840752, upload-time = "2026-03-19T16:26:45.723Z" }, + { url = "https://files.pythonhosted.org/packages/bf/29/a4ae78394f76c7759953c47884eb44de271b03a66634148d9f7d11e721bd/ruff-0.15.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:112c1fa316a558bb34319282c1200a8bf0495f1b735aeb78bfcb2991e6087580", size = 11336961, upload-time = "2026-03-19T16:26:39.076Z" }, + { url = "https://files.pythonhosted.org/packages/26/6b/8786ba5736562220d588a2f6653e6c17e90c59ced34a2d7b512ef8956103/ruff-0.15.7-py3-none-win32.whl", hash = "sha256:6d39e2d3505b082323352f733599f28169d12e891f7dd407f2d4f54b4c2886de", size = 10582538, upload-time = "2026-03-19T16:26:15.992Z" }, + { url = "https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl", hash = "sha256:4d53d712ddebcd7dace1bc395367aec12c057aacfe9adbb6d832302575f4d3a1", size = 11755839, upload-time = "2026-03-19T16:26:19.897Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e8/726643a3ea68c727da31570bde48c7a10f1aa60eddd628d94078fec586ff/ruff-0.15.7-py3-none-win_arm64.whl", hash = "sha256:18e8d73f1c3fdf27931497972250340f92e8c861722161a9caeb89a58ead6ed2", size = 11023304, upload-time = "2026-03-19T16:26:51.669Z" }, ] [[package]] name = "scipy" -version = "1.17.0" +version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/3e/9cca699f3486ce6bc12ff46dc2031f1ec8eb9ccc9a320fdaf925f1417426/scipy-1.17.0.tar.gz", hash = "sha256:2591060c8e648d8b96439e111ac41fd8342fdeff1876be2e19dea3fe8930454e", size = 30396830, upload-time = "2026-01-10T21:34:23.009Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/4b/c89c131aa87cad2b77a54eb0fb94d633a842420fa7e919dc2f922037c3d8/scipy-1.17.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:2abd71643797bd8a106dff97894ff7869eeeb0af0f7a5ce02e4227c6a2e9d6fd", size = 31381316, upload-time = "2026-01-10T21:24:33.42Z" }, - { url = "https://files.pythonhosted.org/packages/5e/5f/a6b38f79a07d74989224d5f11b55267714707582908a5f1ae854cf9a9b84/scipy-1.17.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:ef28d815f4d2686503e5f4f00edc387ae58dfd7a2f42e348bb53359538f01558", size = 27966760, upload-time = "2026-01-10T21:24:38.911Z" }, - { url = "https://files.pythonhosted.org/packages/c1/20/095ad24e031ee8ed3c5975954d816b8e7e2abd731e04f8be573de8740885/scipy-1.17.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:272a9f16d6bb4667e8b50d25d71eddcc2158a214df1b566319298de0939d2ab7", size = 20138701, upload-time = "2026-01-10T21:24:43.249Z" }, - { url = "https://files.pythonhosted.org/packages/89/11/4aad2b3858d0337756f3323f8960755704e530b27eb2a94386c970c32cbe/scipy-1.17.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:7204fddcbec2fe6598f1c5fdf027e9f259106d05202a959a9f1aecf036adc9f6", size = 22480574, upload-time = "2026-01-10T21:24:47.266Z" }, - { url = "https://files.pythonhosted.org/packages/85/bd/f5af70c28c6da2227e510875cadf64879855193a687fb19951f0f44cfd6b/scipy-1.17.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc02c37a5639ee67d8fb646ffded6d793c06c5622d36b35cfa8fe5ececb8f042", size = 32862414, upload-time = "2026-01-10T21:24:52.566Z" }, - { url = "https://files.pythonhosted.org/packages/ef/df/df1457c4df3826e908879fe3d76bc5b6e60aae45f4ee42539512438cfd5d/scipy-1.17.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dac97a27520d66c12a34fd90a4fe65f43766c18c0d6e1c0a80f114d2260080e4", size = 35112380, upload-time = "2026-01-10T21:24:58.433Z" }, - { url = "https://files.pythonhosted.org/packages/5f/bb/88e2c16bd1dd4de19d80d7c5e238387182993c2fb13b4b8111e3927ad422/scipy-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ebb7446a39b3ae0fe8f416a9a3fdc6fba3f11c634f680f16a239c5187bc487c0", size = 34922676, upload-time = "2026-01-10T21:25:04.287Z" }, - { url = "https://files.pythonhosted.org/packages/02/ba/5120242cc735f71fc002cff0303d536af4405eb265f7c60742851e7ccfe9/scipy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:474da16199f6af66601a01546144922ce402cb17362e07d82f5a6cf8f963e449", size = 37507599, upload-time = "2026-01-10T21:25:09.851Z" }, - { url = "https://files.pythonhosted.org/packages/52/c8/08629657ac6c0da198487ce8cd3de78e02cfde42b7f34117d56a3fe249dc/scipy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:255c0da161bd7b32a6c898e7891509e8a9289f0b1c6c7d96142ee0d2b114c2ea", size = 36380284, upload-time = "2026-01-10T21:25:15.632Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4a/465f96d42c6f33ad324a40049dfd63269891db9324aa66c4a1c108c6f994/scipy-1.17.0-cp311-cp311-win_arm64.whl", hash = "sha256:85b0ac3ad17fa3be50abd7e69d583d98792d7edc08367e01445a1e2076005379", size = 24370427, upload-time = "2026-01-10T21:25:20.514Z" }, - { url = "https://files.pythonhosted.org/packages/0b/11/7241a63e73ba5a516f1930ac8d5b44cbbfabd35ac73a2d08ca206df007c4/scipy-1.17.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:0d5018a57c24cb1dd828bcf51d7b10e65986d549f52ef5adb6b4d1ded3e32a57", size = 31364580, upload-time = "2026-01-10T21:25:25.717Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1d/5057f812d4f6adc91a20a2d6f2ebcdb517fdbc87ae3acc5633c9b97c8ba5/scipy-1.17.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:88c22af9e5d5a4f9e027e26772cc7b5922fab8bcc839edb3ae33de404feebd9e", size = 27969012, upload-time = "2026-01-10T21:25:30.921Z" }, - { url = "https://files.pythonhosted.org/packages/e3/21/f6ec556c1e3b6ec4e088da667d9987bb77cc3ab3026511f427dc8451187d/scipy-1.17.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f3cd947f20fe17013d401b64e857c6b2da83cae567adbb75b9dcba865abc66d8", size = 20140691, upload-time = "2026-01-10T21:25:34.802Z" }, - { url = "https://files.pythonhosted.org/packages/7a/fe/5e5ad04784964ba964a96f16c8d4676aa1b51357199014dce58ab7ec5670/scipy-1.17.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e8c0b331c2c1f531eb51f1b4fc9ba709521a712cce58f1aa627bc007421a5306", size = 22463015, upload-time = "2026-01-10T21:25:39.277Z" }, - { url = "https://files.pythonhosted.org/packages/4a/69/7c347e857224fcaf32a34a05183b9d8a7aca25f8f2d10b8a698b8388561a/scipy-1.17.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5194c445d0a1c7a6c1a4a4681b6b7c71baad98ff66d96b949097e7513c9d6742", size = 32724197, upload-time = "2026-01-10T21:25:44.084Z" }, - { url = "https://files.pythonhosted.org/packages/d1/fe/66d73b76d378ba8cc2fe605920c0c75092e3a65ae746e1e767d9d020a75a/scipy-1.17.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9eeb9b5f5997f75507814ed9d298ab23f62cf79f5a3ef90031b1ee2506abdb5b", size = 35009148, upload-time = "2026-01-10T21:25:50.591Z" }, - { url = "https://files.pythonhosted.org/packages/af/07/07dec27d9dc41c18d8c43c69e9e413431d20c53a0339c388bcf72f353c4b/scipy-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:40052543f7bbe921df4408f46003d6f01c6af109b9e2c8a66dd1cf6cf57f7d5d", size = 34798766, upload-time = "2026-01-10T21:25:59.41Z" }, - { url = "https://files.pythonhosted.org/packages/81/61/0470810c8a093cdacd4ba7504b8a218fd49ca070d79eca23a615f5d9a0b0/scipy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0cf46c8013fec9d3694dc572f0b54100c28405d55d3e2cb15e2895b25057996e", size = 37405953, upload-time = "2026-01-10T21:26:07.75Z" }, - { url = "https://files.pythonhosted.org/packages/92/ce/672ed546f96d5d41ae78c4b9b02006cedd0b3d6f2bf5bb76ea455c320c28/scipy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:0937a0b0d8d593a198cededd4c439a0ea216a3f36653901ea1f3e4be949056f8", size = 36328121, upload-time = "2026-01-10T21:26:16.509Z" }, - { url = "https://files.pythonhosted.org/packages/9d/21/38165845392cae67b61843a52c6455d47d0cc2a40dd495c89f4362944654/scipy-1.17.0-cp312-cp312-win_arm64.whl", hash = "sha256:f603d8a5518c7426414d1d8f82e253e454471de682ce5e39c29adb0df1efb86b", size = 24314368, upload-time = "2026-01-10T21:26:23.087Z" }, - { url = "https://files.pythonhosted.org/packages/0c/51/3468fdfd49387ddefee1636f5cf6d03ce603b75205bf439bbf0e62069bfd/scipy-1.17.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:65ec32f3d32dfc48c72df4291345dae4f048749bc8d5203ee0a3f347f96c5ce6", size = 31344101, upload-time = "2026-01-10T21:26:30.25Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9a/9406aec58268d437636069419e6977af953d1e246df941d42d3720b7277b/scipy-1.17.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:1f9586a58039d7229ce77b52f8472c972448cded5736eaf102d5658bbac4c269", size = 27950385, upload-time = "2026-01-10T21:26:36.801Z" }, - { url = "https://files.pythonhosted.org/packages/4f/98/e7342709e17afdfd1b26b56ae499ef4939b45a23a00e471dfb5375eea205/scipy-1.17.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9fad7d3578c877d606b1150135c2639e9de9cecd3705caa37b66862977cc3e72", size = 20122115, upload-time = "2026-01-10T21:26:42.107Z" }, - { url = "https://files.pythonhosted.org/packages/fd/0e/9eeeb5357a64fd157cbe0302c213517c541cc16b8486d82de251f3c68ede/scipy-1.17.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:423ca1f6584fc03936972b5f7c06961670dbba9f234e71676a7c7ccf938a0d61", size = 22442402, upload-time = "2026-01-10T21:26:48.029Z" }, - { url = "https://files.pythonhosted.org/packages/c9/10/be13397a0e434f98e0c79552b2b584ae5bb1c8b2be95db421533bbca5369/scipy-1.17.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe508b5690e9eaaa9467fc047f833af58f1152ae51a0d0aed67aa5801f4dd7d6", size = 32696338, upload-time = "2026-01-10T21:26:55.521Z" }, - { url = "https://files.pythonhosted.org/packages/63/1e/12fbf2a3bb240161651c94bb5cdd0eae5d4e8cc6eaeceb74ab07b12a753d/scipy-1.17.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6680f2dfd4f6182e7d6db161344537da644d1cf85cf293f015c60a17ecf08752", size = 34977201, upload-time = "2026-01-10T21:27:03.501Z" }, - { url = "https://files.pythonhosted.org/packages/19/5b/1a63923e23ccd20bd32156d7dd708af5bbde410daa993aa2500c847ab2d2/scipy-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eec3842ec9ac9de5917899b277428886042a93db0b227ebbe3a333b64ec7643d", size = 34777384, upload-time = "2026-01-10T21:27:11.423Z" }, - { url = "https://files.pythonhosted.org/packages/39/22/b5da95d74edcf81e540e467202a988c50fef41bd2011f46e05f72ba07df6/scipy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d7425fcafbc09a03731e1bc05581f5fad988e48c6a861f441b7ab729a49a55ea", size = 37379586, upload-time = "2026-01-10T21:27:20.171Z" }, - { url = "https://files.pythonhosted.org/packages/b9/b6/8ac583d6da79e7b9e520579f03007cb006f063642afd6b2eeb16b890bf93/scipy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:87b411e42b425b84777718cc41516b8a7e0795abfa8e8e1d573bf0ef014f0812", size = 36287211, upload-time = "2026-01-10T21:28:43.122Z" }, - { url = "https://files.pythonhosted.org/packages/55/fb/7db19e0b3e52f882b420417644ec81dd57eeef1bd1705b6f689d8ff93541/scipy-1.17.0-cp313-cp313-win_arm64.whl", hash = "sha256:357ca001c6e37601066092e7c89cca2f1ce74e2a520ca78d063a6d2201101df2", size = 24312646, upload-time = "2026-01-10T21:28:49.893Z" }, - { url = "https://files.pythonhosted.org/packages/20/b6/7feaa252c21cc7aff335c6c55e1b90ab3e3306da3f048109b8b639b94648/scipy-1.17.0-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:ec0827aa4d36cb79ff1b81de898e948a51ac0b9b1c43e4a372c0508c38c0f9a3", size = 31693194, upload-time = "2026-01-10T21:27:27.454Z" }, - { url = "https://files.pythonhosted.org/packages/76/bb/bbb392005abce039fb7e672cb78ac7d158700e826b0515cab6b5b60c26fb/scipy-1.17.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:819fc26862b4b3c73a60d486dbb919202f3d6d98c87cf20c223511429f2d1a97", size = 28365415, upload-time = "2026-01-10T21:27:34.26Z" }, - { url = "https://files.pythonhosted.org/packages/37/da/9d33196ecc99fba16a409c691ed464a3a283ac454a34a13a3a57c0d66f3a/scipy-1.17.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:363ad4ae2853d88ebcde3ae6ec46ccca903ea9835ee8ba543f12f575e7b07e4e", size = 20537232, upload-time = "2026-01-10T21:27:40.306Z" }, - { url = "https://files.pythonhosted.org/packages/56/9d/f4b184f6ddb28e9a5caea36a6f98e8ecd2a524f9127354087ce780885d83/scipy-1.17.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:979c3a0ff8e5ba254d45d59ebd38cde48fce4f10b5125c680c7a4bfe177aab07", size = 22791051, upload-time = "2026-01-10T21:27:46.539Z" }, - { url = "https://files.pythonhosted.org/packages/9b/9d/025cccdd738a72140efc582b1641d0dd4caf2e86c3fb127568dc80444e6e/scipy-1.17.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:130d12926ae34399d157de777472bf82e9061c60cc081372b3118edacafe1d00", size = 32815098, upload-time = "2026-01-10T21:27:54.389Z" }, - { url = "https://files.pythonhosted.org/packages/48/5f/09b879619f8bca15ce392bfc1894bd9c54377e01d1b3f2f3b595a1b4d945/scipy-1.17.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e886000eb4919eae3a44f035e63f0fd8b651234117e8f6f29bad1cd26e7bc45", size = 35031342, upload-time = "2026-01-10T21:28:03.012Z" }, - { url = "https://files.pythonhosted.org/packages/f2/9a/f0f0a9f0aa079d2f106555b984ff0fbb11a837df280f04f71f056ea9c6e4/scipy-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:13c4096ac6bc31d706018f06a49abe0485f96499deb82066b94d19b02f664209", size = 34893199, upload-time = "2026-01-10T21:28:10.832Z" }, - { url = "https://files.pythonhosted.org/packages/90/b8/4f0f5cf0c5ea4d7548424e6533e6b17d164f34a6e2fb2e43ffebb6697b06/scipy-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cacbaddd91fcffde703934897c5cd2c7cb0371fac195d383f4e1f1c5d3f3bd04", size = 37438061, upload-time = "2026-01-10T21:28:19.684Z" }, - { url = "https://files.pythonhosted.org/packages/f9/cc/2bd59140ed3b2fa2882fb15da0a9cb1b5a6443d67cfd0d98d4cec83a57ec/scipy-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:edce1a1cf66298cccdc48a1bdf8fb10a3bf58e8b58d6c3883dd1530e103f87c0", size = 36328593, upload-time = "2026-01-10T21:28:28.007Z" }, - { url = "https://files.pythonhosted.org/packages/13/1b/c87cc44a0d2c7aaf0f003aef2904c3d097b422a96c7e7c07f5efd9073c1b/scipy-1.17.0-cp313-cp313t-win_arm64.whl", hash = "sha256:30509da9dbec1c2ed8f168b8d8aa853bc6723fede1dbc23c7d43a56f5ab72a67", size = 24625083, upload-time = "2026-01-10T21:28:35.188Z" }, - { url = "https://files.pythonhosted.org/packages/1a/2d/51006cd369b8e7879e1c630999a19d1fbf6f8b5ed3e33374f29dc87e53b3/scipy-1.17.0-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:c17514d11b78be8f7e6331b983a65a7f5ca1fd037b95e27b280921fe5606286a", size = 31346803, upload-time = "2026-01-10T21:28:57.24Z" }, - { url = "https://files.pythonhosted.org/packages/d6/2e/2349458c3ce445f53a6c93d4386b1c4c5c0c540917304c01222ff95ff317/scipy-1.17.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:4e00562e519c09da34c31685f6acc3aa384d4d50604db0f245c14e1b4488bfa2", size = 27967182, upload-time = "2026-01-10T21:29:04.107Z" }, - { url = "https://files.pythonhosted.org/packages/5e/7c/df525fbfa77b878d1cfe625249529514dc02f4fd5f45f0f6295676a76528/scipy-1.17.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f7df7941d71314e60a481e02d5ebcb3f0185b8d799c70d03d8258f6c80f3d467", size = 20139125, upload-time = "2026-01-10T21:29:10.179Z" }, - { url = "https://files.pythonhosted.org/packages/33/11/fcf9d43a7ed1234d31765ec643b0515a85a30b58eddccc5d5a4d12b5f194/scipy-1.17.0-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:aabf057c632798832f071a8dde013c2e26284043934f53b00489f1773b33527e", size = 22443554, upload-time = "2026-01-10T21:29:15.888Z" }, - { url = "https://files.pythonhosted.org/packages/80/5c/ea5d239cda2dd3d31399424967a24d556cf409fbea7b5b21412b0fd0a44f/scipy-1.17.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a38c3337e00be6fd8a95b4ed66b5d988bac4ec888fd922c2ea9fe5fb1603dd67", size = 32757834, upload-time = "2026-01-10T21:29:23.406Z" }, - { url = "https://files.pythonhosted.org/packages/b8/7e/8c917cc573310e5dc91cbeead76f1b600d3fb17cf0969db02c9cf92e3cfa/scipy-1.17.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00fb5f8ec8398ad90215008d8b6009c9db9fa924fd4c7d6be307c6f945f9cd73", size = 34995775, upload-time = "2026-01-10T21:29:31.915Z" }, - { url = "https://files.pythonhosted.org/packages/c5/43/176c0c3c07b3f7df324e7cdd933d3e2c4898ca202b090bd5ba122f9fe270/scipy-1.17.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f2a4942b0f5f7c23c7cd641a0ca1955e2ae83dedcff537e3a0259096635e186b", size = 34841240, upload-time = "2026-01-10T21:29:39.995Z" }, - { url = "https://files.pythonhosted.org/packages/44/8c/d1f5f4b491160592e7f084d997de53a8e896a3ac01cd07e59f43ca222744/scipy-1.17.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf133ced83889583156566d2bdf7a07ff89228fe0c0cb727f777de92092ec6b", size = 37394463, upload-time = "2026-01-10T21:29:48.723Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ec/42a6657f8d2d087e750e9a5dde0b481fd135657f09eaf1cf5688bb23c338/scipy-1.17.0-cp314-cp314-win_amd64.whl", hash = "sha256:3625c631a7acd7cfd929e4e31d2582cf00f42fcf06011f59281271746d77e061", size = 37053015, upload-time = "2026-01-10T21:30:51.418Z" }, - { url = "https://files.pythonhosted.org/packages/27/58/6b89a6afd132787d89a362d443a7bddd511b8f41336a1ae47f9e4f000dc4/scipy-1.17.0-cp314-cp314-win_arm64.whl", hash = "sha256:9244608d27eafe02b20558523ba57f15c689357c85bdcfe920b1828750aa26eb", size = 24951312, upload-time = "2026-01-10T21:30:56.771Z" }, - { url = "https://files.pythonhosted.org/packages/e9/01/f58916b9d9ae0112b86d7c3b10b9e685625ce6e8248df139d0fcb17f7397/scipy-1.17.0-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:2b531f57e09c946f56ad0b4a3b2abee778789097871fc541e267d2eca081cff1", size = 31706502, upload-time = "2026-01-10T21:29:56.326Z" }, - { url = "https://files.pythonhosted.org/packages/59/8e/2912a87f94a7d1f8b38aabc0faf74b82d3b6c9e22be991c49979f0eceed8/scipy-1.17.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:13e861634a2c480bd237deb69333ac79ea1941b94568d4b0efa5db5e263d4fd1", size = 28380854, upload-time = "2026-01-10T21:30:01.554Z" }, - { url = "https://files.pythonhosted.org/packages/bd/1c/874137a52dddab7d5d595c1887089a2125d27d0601fce8c0026a24a92a0b/scipy-1.17.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:eb2651271135154aa24f6481cbae5cc8af1f0dd46e6533fb7b56aa9727b6a232", size = 20552752, upload-time = "2026-01-10T21:30:05.93Z" }, - { url = "https://files.pythonhosted.org/packages/3f/f0/7518d171cb735f6400f4576cf70f756d5b419a07fe1867da34e2c2c9c11b/scipy-1.17.0-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:c5e8647f60679790c2f5c76be17e2e9247dc6b98ad0d3b065861e082c56e078d", size = 22803972, upload-time = "2026-01-10T21:30:10.651Z" }, - { url = "https://files.pythonhosted.org/packages/7c/74/3498563a2c619e8a3ebb4d75457486c249b19b5b04a30600dfd9af06bea5/scipy-1.17.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fb10d17e649e1446410895639f3385fd2bf4c3c7dfc9bea937bddcbc3d7b9ba", size = 32829770, upload-time = "2026-01-10T21:30:16.359Z" }, - { url = "https://files.pythonhosted.org/packages/48/d1/7b50cedd8c6c9d6f706b4b36fa8544d829c712a75e370f763b318e9638c1/scipy-1.17.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8547e7c57f932e7354a2319fab613981cde910631979f74c9b542bb167a8b9db", size = 35051093, upload-time = "2026-01-10T21:30:22.987Z" }, - { url = "https://files.pythonhosted.org/packages/e2/82/a2d684dfddb87ba1b3ea325df7c3293496ee9accb3a19abe9429bce94755/scipy-1.17.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33af70d040e8af9d5e7a38b5ed3b772adddd281e3062ff23fec49e49681c38cf", size = 34909905, upload-time = "2026-01-10T21:30:28.704Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5e/e565bd73991d42023eb82bb99e51c5b3d9e2c588ca9d4b3e2cc1d3ca62a6/scipy-1.17.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb55bb97d00f8b7ab95cb64f873eb0bf54d9446264d9f3609130381233483f", size = 37457743, upload-time = "2026-01-10T21:30:34.819Z" }, - { url = "https://files.pythonhosted.org/packages/58/a8/a66a75c3d8f1fb2b83f66007d6455a06a6f6cf5618c3dc35bc9b69dd096e/scipy-1.17.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1ff269abf702f6c7e67a4b7aad981d42871a11b9dd83c58d2d2ea624efbd1088", size = 37098574, upload-time = "2026-01-10T21:30:40.782Z" }, - { url = "https://files.pythonhosted.org/packages/56/a5/df8f46ef7da168f1bc52cd86e09a9de5c6f19cc1da04454d51b7d4f43408/scipy-1.17.0-cp314-cp314t-win_arm64.whl", hash = "sha256:031121914e295d9791319a1875444d55079885bbae5bdc9c5e0f2ee5f09d34ff", size = 25246266, upload-time = "2026-01-10T21:30:45.923Z" }, -] - -[[package]] -name = "scipy-stubs" -version = "1.17.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "optype", extra = ["numpy"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/40/fe/5fa7da49821ea94d60629ae71277fa8d7e16eb20602f720062b6c30a644c/scipy_stubs-1.17.0.2.tar.gz", hash = "sha256:3981bd7fa4c189a8493307afadaee1a830d9a0de8e3ae2f4603f192b6260ef2a", size = 379897, upload-time = "2026-01-22T19:17:08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/e3/20233497e4a27956e7392c3f7879e6ee7f767f268079f24f4b089b70f563/scipy_stubs-1.17.0.2-py3-none-any.whl", hash = "sha256:99d1aa75b7d72a7ee36a68d18bcf1149f62ab577bbd1236c65c471b3b465d824", size = 586137, upload-time = "2026-01-22T19:17:05.802Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" }, + { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" }, + { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" }, + { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" }, + { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" }, + { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" }, + { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" }, + { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" }, + { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" }, + { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, + { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, + { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" }, + { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" }, + { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" }, + { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" }, + { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" }, + { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" }, + { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" }, + { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" }, + { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" }, + { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" }, + { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" }, + { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" }, + { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" }, + { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" }, + { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" }, + { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" }, + { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" }, + { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" }, + { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" }, + { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" }, + { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" }, + { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" }, + { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" }, + { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" }, + { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" }, + { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" }, + { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" }, + { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" }, ] [[package]] @@ -2085,15 +2097,15 @@ wheels = [ [[package]] name = "sphinx-argparse-cli" -version = "1.20.1" +version = "1.21.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/86/e64bef5ee47bbb0bdb2415b9cabcbf03cbc270eca4ba46c6060fa0977313/sphinx_argparse_cli-1.20.1.tar.gz", hash = "sha256:ce21821c5961de24f52d4f78b01be02b693a5bd3d7df7ce920facb9708f983a5", size = 13080, upload-time = "2025-10-08T22:03:10.411Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/41/c9f23756680b5333adfa026df6ca38f5db8d29c6781f7f6d65134bc08f46/sphinx_argparse_cli-1.21.3.tar.gz", hash = "sha256:3ac2f25e7a3b30a382b9baaa4ce651b3fa5e1c09379e0c9929235720d61f2f4b", size = 14241, upload-time = "2026-03-19T19:53:51.01Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/40/da6d8044c0f27f0bffd0656f77eceef6ac7cd29b1bea21222f34c3c54784/sphinx_argparse_cli-1.20.1-py3-none-any.whl", hash = "sha256:a4592b1cc2c07dcdd6d6b58e41b79d032bd48588e68a6b057272f75f87f904ed", size = 10368, upload-time = "2025-10-08T22:03:09.051Z" }, + { url = "https://files.pythonhosted.org/packages/ee/5e/816d9b68824f118a4ff62afdd88568073b478b00274eb6007ca83c0a71c6/sphinx_argparse_cli-1.21.3-py3-none-any.whl", hash = "sha256:31d78d4ad926e8f06c065475b030d7d7393095e345d84fa3d29266e46ca91987", size = 11000, upload-time = "2026-03-19T19:53:49.87Z" }, ] [[package]] @@ -2113,7 +2125,7 @@ wheels = [ [[package]] name = "sphinx-autodoc-typehints" -version = "3.6.2" +version = "3.9.11" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.12'", @@ -2121,9 +2133,9 @@ resolution-markers = [ dependencies = [ { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/51/6603ed3786a2d52366c66f49bc8afb31ae5c0e33d4a156afcb38d2bac62c/sphinx_autodoc_typehints-3.6.2.tar.gz", hash = "sha256:3d37709a21b7b765ad6e20a04ecefcb229b9eb0007cb24f6ebaa8a4576ea7f06", size = 37574, upload-time = "2026-01-02T21:25:28.216Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/e9/d29ae58dd12971d2cbb872884676a70d1a5e4719b4d82e197264cdf0431a/sphinx_autodoc_typehints-3.9.11.tar.gz", hash = "sha256:28516c916b41fa83271ee2ab9191b73807e4113d3bfb94222ac87d8d9795b6e7", size = 70261, upload-time = "2026-03-24T16:57:28.462Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/6a/877e8a6ea52fc86d88ce110ebcfe4f8474ff590d8a8d322909673af3da7b/sphinx_autodoc_typehints-3.6.2-py3-none-any.whl", hash = "sha256:9e70bee1f487b087c83ba0f4949604a4630bee396e263a324aae1dc4268d2c0f", size = 20853, upload-time = "2026-01-02T21:25:26.853Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e3/ff212b51c16717681792eaf18691e6b5affbbb3d4290147c457fa9127372/sphinx_autodoc_typehints-3.9.11-py3-none-any.whl", hash = "sha256:b5cbc7a56a9338021ab7a4e6aa132aa7829fa2f8b64eca927faab64cd3971b80", size = 37279, upload-time = "2026-03-24T16:57:27.147Z" }, ] [[package]] @@ -2177,7 +2189,7 @@ wheels = [ [[package]] name = "sphinxcontrib-mermaid" -version = "2.0.0" +version = "2.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jinja2" }, @@ -2185,9 +2197,9 @@ dependencies = [ { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/a5/65a5c439cc14ba80483b9891e9350f11efb80cd3bdccb222f0c738068c78/sphinxcontrib_mermaid-2.0.0.tar.gz", hash = "sha256:cf4f7d453d001132eaba5d1fdf53d42049f02e913213cf8337427483bfca26f4", size = 18194, upload-time = "2026-01-13T17:13:42.563Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/ae/999891de292919b66ea34f2c22fc22c9be90ab3536fbc0fca95716277351/sphinxcontrib_mermaid-2.0.1.tar.gz", hash = "sha256:a21a385a059a6cafd192aa3a586b14bf5c42721e229db67b459dc825d7f0a497", size = 19839, upload-time = "2026-03-05T14:10:41.901Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/de/bd96c69b62e967bffd02c6d89dfca9471b04e761c466725fc39746abf41d/sphinxcontrib_mermaid-2.0.0-py3-none-any.whl", hash = "sha256:59a73249bbee2c74b1a4db036f8e8899ade65982bdda6712cf22b4f4e9874bb5", size = 14055, upload-time = "2026-01-13T17:13:41.481Z" }, + { url = "https://files.pythonhosted.org/packages/03/46/25d64bcd7821c8d6f1080e1c43d5fcdfc442a18f759a230b5ccdc891093e/sphinxcontrib_mermaid-2.0.1-py3-none-any.whl", hash = "sha256:9dca7fbe827bad5e7e2b97c4047682cfd26e3e07398cfdc96c7a8842ae7f06e7", size = 14064, upload-time = "2026-03-05T14:10:40.533Z" }, ] [[package]] @@ -2291,14 +2303,15 @@ wheels = [ [[package]] name = "virtualenv" -version = "20.36.1" +version = "21.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, + { name = "python-discovery" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba", size = 6032239, upload-time = "2026-01-09T18:21:01.296Z" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618, upload-time = "2026-03-09T17:24:38.013Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f", size = 6008258, upload-time = "2026-01-09T18:20:59.425Z" }, + { url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084, upload-time = "2026-03-09T17:24:35.378Z" }, ] From f6357db82c937b2bb1adeb1034d3ba94587d9348 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:20:03 +0100 Subject: [PATCH 45/53] pyproject.toml: Added default directories to the 'exclude' list for pyright, in section [tool.pyright] (Ref note in pyright [docs](https://github.com/microsoft/pylance-release/blob/main/docs/settings/python_analysis_exclude.md#default-behavior)). --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index e3f8812..d37ad4b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -145,6 +145,10 @@ include = [ "examples", ] exclude = [ + "**/node_modules", + "**/__pycache__", + "**/.*", + ".venv", "tests/test_working_directory", ] From b85344038984079ab0ac09ef40f6c4aa80bc0ff6 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 19:27:07 +0100 Subject: [PATCH 46/53] .github/workflows/_code_quality.yml: Added missing '#' in a commented out line --- .github/workflows/_code_quality.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_code_quality.yml b/.github/workflows/_code_quality.yml index 8ec3f06..5a577cf 100644 --- a/.github/workflows/_code_quality.yml +++ b/.github/workflows/_code_quality.yml @@ -40,7 +40,7 @@ jobs: # name: pyright # steps: # - name: Checkout code - uses: actions/checkout@v5 + # uses: actions/checkout@v5 # - name: Install uv # uses: astral-sh/setup-uv@v7 # with: From 5f9086e93252642b55d0392a48ca0d8f6a9adcb1 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 20:39:06 +0100 Subject: [PATCH 47/53] tests/test_working_directory/.gitignore: Added *.xml, *.fmu and *.dat as ignores --- tests/test_working_directory/.gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_working_directory/.gitignore b/tests/test_working_directory/.gitignore index 1be5c5b..a604d11 100644 --- a/tests/test_working_directory/.gitignore +++ b/tests/test_working_directory/.gitignore @@ -1 +1,4 @@ *test*.pdf +*.xml +*.fmu +*.dat From 2bec9a8753a78c96d62582bd512d6bfbbe481049 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 20:40:04 +0100 Subject: [PATCH 48/53] tests/conftest.py, tests/test_crane_on_spring.py: Change destination folder for the call to Model.build(), so that MobileCrane.fmu gets built not in `/examples` but in `/tests/test_working_directory`. This avoids that we commit an updated binary in the repository (/examples/MobileCrane.fmu), just because we ran tests. --- tests/conftest.py | 9 +++++---- tests/test_crane_on_spring.py | 6 +++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 8b9e54f..1534eb9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,13 +11,14 @@ def mobile_crane_fmu(): """Make the (updated) MobileCrane.fmu available for all tests.""" from component_model.model import Model - + src_folder: Path = Path(__file__).parent.parent / "src" / "py_crane" + src: Path = src_folder / "mobile_crane.py" build_path = Path(__file__).parent.parent / "examples" # together with other crane files build_path.mkdir(exist_ok=True) fmu_path = Model.build( # MobileCrane.build( - str(Path(__file__).parent.parent / "src" / "py_crane" / "mobile_crane.py"), - project_files=[Path(__file__).parent.parent / "src" / "py_crane"], - dest=build_path, + script=str(src), + project_files=[src_folder], + dest=Path.cwd() / "MobileCrane.fmu", # was: dest=build_path ) return fmu_path diff --git a/tests/test_crane_on_spring.py b/tests/test_crane_on_spring.py index a5e41f5..e0378ae 100644 --- a/tests/test_crane_on_spring.py +++ b/tests/test_crane_on_spring.py @@ -84,7 +84,11 @@ def oscillator_fmu(): def _get_fmu(fmu_file: str) -> Path: - fmu = Path(__file__).parent.parent / "examples" / fmu_file + # First assume that the FMU is in the current working directory, which is expected to be the 'test_working_directory' folder. + # If not found there, look in the 'examples' folder. + fmu = Path.cwd() / fmu_file + if not fmu.exists(): + fmu = Path(__file__).parent.parent / "examples" / fmu_file assert fmu.exists(), f"{fmu_file} file expected at {fmu}. Not found." return fmu From 271fca34996038d4a54cf1cf415c5a17731ff5c5 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 20:47:05 +0100 Subject: [PATCH 49/53] updated CHANGELOG.md --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 142ebd4..21a34e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ The changelog format is based on [Keep a Changelog](https://keepachangelog.com/e * -/- +## [0.3.1] - 2026-03-26 + ### Changed * GitHub Workflows: * Added 'name: Checkout code' to uses of 'actions/checkout', for better readability and consistency across workflow files. @@ -23,7 +25,11 @@ The changelog format is based on [Keep a Changelog](https://keepachangelog.com/e * Updated the syntax used for the OS and Python matrix in test workflows. * Sphinx Documentation: * Sphinx conf.py: Updated year in copyright statement to 2026 - +* pyproject.toml: + * Added default directories to the 'exclude' list for pyright, in section [tool.pyright] + (Ref note in pyright [docs](https://github.com/microsoft/pylance-release/blob/main/docs/settings/python_analysis_exclude.md#default-behavior)). +* tests/conftest.py, tests/test_crane_on_spring.py: Change destination folder for the call to Model.build(), so that MobileCrane.fmu gets built not in `/examples` but in `/tests/test_working_directory`. +This avoids the need to commit an updated binary in the repository (/examples/MobileCrane.fmu), just because we ran tests. ### Dependencies * .pre-commit-config.yaml: Updated rev of ruff-pre-commit to v0.15.1 From c706a195d1e9bca594b247ecb0417c352ea99617 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 20:54:39 +0100 Subject: [PATCH 50/53] Bumped version number to 0.3.1 --- CITATION.cff | 2 +- docs/source/conf.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index a56bf01..fd2a428 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,5 +1,5 @@ title: py-crane -version: 0.3.0 +version: 0.3.1 abstract: >- Basic crane simulation model including publication as FMU, for usage in the SEACo project. type: software diff --git a/docs/source/conf.py b/docs/source/conf.py index 4723244..1f75087 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -26,7 +26,7 @@ author = "Siegfried Eisinger, Jorge Luis Mendez" # The full version, including alpha/beta/rc tags -release = "0.3.0" +release = "0.3.1" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/pyproject.toml b/pyproject.toml index d37ad4b..6f70a6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ allow-direct-references = true [project] name = "py-crane" -version = "0.3.0" +version = "0.3.1" description = "Basic crane simulation model including publication as FMU, for usage in the SEACo project." readme = "README.rst" requires-python = ">= 3.11" From a7a216bdde82bd7d5ad463606dfb15af03209142 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 20:55:58 +0100 Subject: [PATCH 51/53] updated uv.lock --- uv.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uv.lock b/uv.lock index a70a92b..a4e7b4e 100644 --- a/uv.lock +++ b/uv.lock @@ -1533,7 +1533,7 @@ wheels = [ [[package]] name = "py-crane" -version = "0.3.0" +version = "0.3.1" source = { editable = "." } dependencies = [ { name = "component-model" }, From fcb1c99c3192f363c23b68f7fd1dca42c76b1b9d Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 21:22:08 +0100 Subject: [PATCH 52/53] docs/source/py_crane.rst: Adapted to changed name of one module (py_crane/enum.py was renamed to py_crane/enums.py) --- docs/source/py_crane.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/py_crane.rst b/docs/source/py_crane.rst index f78f7a0..149cb83 100644 --- a/docs/source/py_crane.rst +++ b/docs/source/py_crane.rst @@ -25,5 +25,5 @@ Modules py_crane.boom py_crane.boom_fmu py_crane.mobile_crane - py_crane.enum + py_crane.enums py_crane.animation From cd9ebb5b01b907d41486bd08f721db36f2c304a0 Mon Sep 17 00:00:00 2001 From: Claas Date: Wed, 25 Mar 2026 21:24:13 +0100 Subject: [PATCH 53/53] ruff format --- tests/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/conftest.py b/tests/conftest.py index 1534eb9..4c93728 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,6 +11,7 @@ def mobile_crane_fmu(): """Make the (updated) MobileCrane.fmu available for all tests.""" from component_model.model import Model + src_folder: Path = Path(__file__).parent.parent / "src" / "py_crane" src: Path = src_folder / "mobile_crane.py" build_path = Path(__file__).parent.parent / "examples" # together with other crane files