diff --git a/examples/nnunet_example/client.py b/examples/nnunet_example/client.py index 3d5943a94..29e7f33ba 100644 --- a/examples/nnunet_example/client.py +++ b/examples/nnunet_example/client.py @@ -5,8 +5,8 @@ from os.path import exists, join from pathlib import Path -from fl4health.checkpointing.checkpointer import PerRoundStateCheckpointer from fl4health.checkpointing.client_module import ClientCheckpointAndStateModule +from fl4health.checkpointing.state_checkpointer import ClientStateCheckpointer with warnings.catch_warnings(): # Silence deprecation warnings from sentry sdk due to flwr and wandb @@ -73,7 +73,7 @@ def main( if intermediate_client_state_dir is not None: checkpoint_and_state_module = ClientCheckpointAndStateModule( - state_checkpointer=PerRoundStateCheckpointer(Path(intermediate_client_state_dir)) + state_checkpointer=ClientStateCheckpointer(Path(intermediate_client_state_dir)) ) else: checkpoint_and_state_module = None @@ -147,7 +147,7 @@ def main( even if the preprocessed data is found to already exist", ) parser.add_argument( - "--server_address", + "--server-address", type=str, required=False, default="0.0.0.0:8080", diff --git a/examples/nnunet_example/server.py b/examples/nnunet_example/server.py index a336b39f0..ad3e19f9d 100644 --- a/examples/nnunet_example/server.py +++ b/examples/nnunet_example/server.py @@ -12,8 +12,8 @@ from flwr.server.client_manager import SimpleClientManager from flwr.server.strategy import FedAvg -from fl4health.checkpointing.checkpointer import PerRoundStateCheckpointer from fl4health.checkpointing.server_module import NnUnetServerCheckpointAndStateModule +from fl4health.checkpointing.state_checkpointer import NnUnetServerStateCheckpointer from fl4health.metrics.metric_aggregation import evaluate_metrics_aggregation_fn, fit_metrics_aggregation_fn from fl4health.parameter_exchange.full_exchanger import FullParameterExchanger from fl4health.servers.nnunet_server import NnunetServer @@ -85,7 +85,7 @@ def main( ) state_checkpointer = ( - PerRoundStateCheckpointer(Path(intermediate_server_state_dir)) + NnUnetServerStateCheckpointer(Path(intermediate_server_state_dir)) if intermediate_server_state_dir is not None else None ) @@ -132,7 +132,7 @@ def main( ) parser.add_argument( - "--intermediate-server-state-dir", + "--intermediate-server-state_dir", type=str, required=False, default=None, diff --git a/fl4health/checkpointing/checkpointer.py b/fl4health/checkpointing/checkpointer.py index d1ee3631a..98da33706 100644 --- a/fl4health/checkpointing/checkpointer.py +++ b/fl4health/checkpointing/checkpointer.py @@ -2,8 +2,6 @@ from abc import ABC, abstractmethod from collections.abc import Callable from logging import ERROR, INFO, WARNING -from pathlib import Path -from typing import Any import torch import torch.nn as nn @@ -276,7 +274,6 @@ def __init__( maximize: bool = False, ) -> None: """Checkpointer that checkpoints based on the value of a user defined metric. - Args: checkpoint_dir (str): Directory to which the model is saved. This directory should already exist. The checkpointer will not create it if it does not. @@ -310,83 +307,3 @@ def metric_score_function(_: float, metrics: dict[str, Scalar]) -> float: checkpoint_score_name=metric, maximize=maximize, ) - - -class PerRoundStateCheckpointer: - def __init__(self, checkpoint_dir: Path) -> None: - """ - Base class that provides a uniform interface for loading, saving and checking if checkpoints exists. - - Args: - checkpoint_dir (Path): Base directory to store checkpoints. This checkpoint directory MUST already exist. - It will not be created by this state checkpointer. - """ - log( - WARNING, - "Creating PerRoundCheckpointer. Currently, this functionality is still experimental and only supported " - "for BasicClient and NnunetClient, along with their associated servers.", - ) - self.checkpoint_dir = checkpoint_dir - - def save_checkpoint(self, checkpoint_name: str, checkpoint_dict: dict[str, Any]) -> None: - """ - Saves ``checkpoint_dict`` to checkpoint path form from this classes checkpointer dir and the provided - checkpoint name. - - Args: - checkpoint_name (str): Name of the state checkpoint file. - checkpoint_dict (dict[str, Any]): A dictionary with string keys and values of type Any representing the - state to checkpoint. - - Raises: - e: Will throw an error if there is an issue saving the model. ``Torch.save`` seems to swallow errors in - this context, so we explicitly surface the error with a try/except. - """ - - checkpoint_path = os.path.join(self.checkpoint_dir, checkpoint_name) - try: - log(INFO, f"Saving state as {checkpoint_path}") - torch.save(checkpoint_dict, checkpoint_path) - except Exception as e: - log(ERROR, f"Encountered the following error while saving the checkpoint: {e}") - raise e - - def load_checkpoint(self, checkpoint_name: str) -> dict[str, Any]: - """ - Loads and returns the checkpoint stored in ``checkpoint_dir`` under the provided name if it exists. If it - does not exist, an assertion error will be thrown. - - Args: - checkpoint_name (str): Name of the state checkpoint to be loaded. - - Returns: - dict[str, Any]: A dictionary representing the checkpointed state, as loaded by ``torch.load``. - """ - - assert self.checkpoint_exists(checkpoint_name) - checkpoint_path = os.path.join(self.checkpoint_dir, checkpoint_name) - log(INFO, f"Loading state from checkpoint at {checkpoint_path}") - - return torch.load(checkpoint_path) - - def checkpoint_exists(self, checkpoint_name: str, **kwargs: Any) -> bool: - """ - Checks if a checkpoint exists at the ``checkpoint_dir`` constructed at initialization + ``checkpoint_name``. - - Args: - checkpoint_name (str): Name of checkpoint for existence test. Directory of checkpoint is held internally - as state by the checkpointer - - Raises: - ValueError: Previously this function supported sending a path, but now requires ``checkpoint_name``. Will - raise an error is ``checkpoint_path`` provided. - - Returns: - bool: True if checkpoint exists, otherwise false. - """ - if "checkpoint_path" in kwargs: - raise ValueError( - "Previously this checkpoint supported sending a path, but it now requires a checkpoint_name only" - ) - checkpoint_path = os.path.join(self.checkpoint_dir, checkpoint_name) - return os.path.exists(checkpoint_path) diff --git a/fl4health/checkpointing/client_module.py b/fl4health/checkpointing/client_module.py index 103238e90..2fc095b2f 100644 --- a/fl4health/checkpointing/client_module.py +++ b/fl4health/checkpointing/client_module.py @@ -1,13 +1,19 @@ +from __future__ import annotations + from collections.abc import Sequence from enum import Enum from logging import INFO -from typing import Any +from typing import TYPE_CHECKING import torch.nn as nn from flwr.common.logger import log from flwr.common.typing import Scalar -from fl4health.checkpointing.checkpointer import PerRoundStateCheckpointer, TorchModuleCheckpointer +if TYPE_CHECKING: + from fl4health.clients.basic_client import BasicClient + +from fl4health.checkpointing.checkpointer import TorchModuleCheckpointer +from fl4health.checkpointing.state_checkpointer import ClientStateCheckpointer ModelCheckpointers = TorchModuleCheckpointer | Sequence[TorchModuleCheckpointer] | None @@ -18,11 +24,12 @@ class CheckpointMode(Enum): class ClientCheckpointAndStateModule: + def __init__( self, pre_aggregation: ModelCheckpointers = None, post_aggregation: ModelCheckpointers = None, - state_checkpointer: PerRoundStateCheckpointer | None = None, + state_checkpointer: ClientStateCheckpointer | None = None, ) -> None: """ This module is meant to hold up three to major components that determine how clients handle model and state @@ -44,9 +51,9 @@ def __init__( post_aggregation (ModelCheckpointers, optional): If defined, this checkpointer (or sequence of checkpointers) is used to checkpoint models based on their validation metrics/losses **AFTER** server-side aggregation. Defaults to None. - state_checkpointer (PerRoundStateCheckpointer | None, optional): If defined, this checkpointer is used to - preserve client state (not just models), in the event one wants to restart federated training. - Defaults to None. + state_checkpointer (ClientStateCheckpointer | None, optional): If defined, this checkpointer + is used to preserve client state (not just models), in the event one wants to restart + federated training. Defaults to None. """ self.pre_aggregation = ( [pre_aggregation] if isinstance(pre_aggregation, TorchModuleCheckpointer) else pre_aggregation @@ -119,52 +126,42 @@ def maybe_checkpoint( else: raise ValueError(f"Unrecognized mode for checkpointing: {str(mode)}") - def save_state(self, state_checkpoint_name: str, state: dict[str, Any]) -> None: + def save_state(self, client: BasicClient) -> None: """ This function is meant to facilitate saving state required to restart an FL process on the client side. This - function will simply save whatever information is passed in the state variable using the file name in - ``state_checkpoint_name``. This function should only be called if a ``state_checkpointer`` exists in this - module + function will simply save all the attributes stated in ``ClientStateCheckpointer.snapshot_attrs``. + This function should only be called if a ``state_checkpointer`` exists in this module. Args: - state_checkpoint_name (str): Name of the state checkpoint file. The checkpointer itself will have a - directory to which state will be saved. - state (dict[str, Any]): State to be saved so that training might be resumed on the client if federated - training is interrupted. For example, this might contain things like optimizer states, learning rate - scheduler states, etc. + client (BasicClient): The client object from which state will be saved. Raises: ValueError: Throws an error if this function is called, but no state checkpointer has been provided """ if self.state_checkpointer is not None: - self.state_checkpointer.save_checkpoint(state_checkpoint_name, state) + self.state_checkpointer.save_client_state(client) else: raise ValueError("Attempting to save state but no state checkpointer is specified") - def maybe_load_state(self, state_checkpoint_name: str) -> dict[str, Any] | None: + def maybe_load_state(self, client: BasicClient) -> bool: """ - This function facilitates loading of any pre-existing state (with the name ``state_checkpoint_name``) in the - directory of the ``state_checkpointer``. If the state already exists at the proper path, the state is loaded - and returned. If it doesn't exist, we return None. + This function facilitates loading of any pre-existing state (with the name ``checkpoint_name``) in the + directory of the ``checkpoint_dir``. If the state already exists at the proper path, the state is loaded + and will be automatically saved into client's attributes. If it doesn't exist, we return False. Args: - state_checkpoint_name (str): Name of the state checkpoint file. The checkpointer itself will have a - directory from which state will be loaded (if it exists). + client (BasicClient): client object into which state will be loaded if a checkpoint exists Raises: ValueError: Throws an error if this function is called, but no state checkpointer has been provided Returns: - dict[str, Any] | None: If the state checkpoint properly exists and is loaded correctly, this dictionary - carries that state. Otherwise, we return a None (or throw an exception). + bool : If the state checkpoint properly exists and is loaded correctly, client's attributes + are set to the loaded values, and True is returned. Otherwise, we return False (or throw an exception). """ if self.state_checkpointer is not None: - if self.state_checkpointer.checkpoint_exists(state_checkpoint_name): - return self.state_checkpointer.load_checkpoint(state_checkpoint_name) - else: - log(INFO, "State checkpointer is defined but no state checkpoint exists.") - return None + return self.state_checkpointer.maybe_load_client_state(client) else: raise ValueError("Attempting to load state, but no state checkpointer is specified") diff --git a/fl4health/checkpointing/server_module.py b/fl4health/checkpointing/server_module.py index adf2984a6..4e959a075 100644 --- a/fl4health/checkpointing/server_module.py +++ b/fl4health/checkpointing/server_module.py @@ -1,15 +1,21 @@ +from __future__ import annotations + from collections.abc import Sequence from logging import INFO -from typing import Any +from typing import TYPE_CHECKING import torch.nn as nn from flwr.common import Parameters from flwr.common.logger import log -from flwr.common.parameter import parameters_to_ndarrays +from flwr.common.parameter import ndarrays_to_parameters, parameters_to_ndarrays from flwr.common.typing import Scalar -from fl4health.checkpointing.checkpointer import PerRoundStateCheckpointer, TorchModuleCheckpointer +if TYPE_CHECKING: + from fl4health.servers.base_server import FlServer + +from fl4health.checkpointing.checkpointer import TorchModuleCheckpointer from fl4health.checkpointing.opacus_checkpointer import OpacusCheckpointer +from fl4health.checkpointing.state_checkpointer import NnUnetServerStateCheckpointer, ServerStateCheckpointer from fl4health.parameter_exchange.packing_exchanger import FullParameterExchangerWithPacking from fl4health.parameter_exchange.parameter_exchanger_base import ExchangerType from fl4health.parameter_exchange.parameter_packer import ( @@ -24,12 +30,13 @@ class BaseServerCheckpointAndStateModule: + def __init__( self, model: nn.Module | None = None, parameter_exchanger: ExchangerType | None = None, model_checkpointers: ModelCheckpointers = None, - state_checkpointer: PerRoundStateCheckpointer | None = None, + state_checkpointer: ServerStateCheckpointer | None = None, ) -> None: """ This module is meant to handle basic model and state checkpointing on the server-side of an FL process. Unlike @@ -51,9 +58,9 @@ def __init__( parameters go to the right places. Defaults to None. model_checkpointers (ModelCheckpointers, optional): If defined, this checkpointer (or sequence of checkpointers) is used to checkpoint models based on their defined scoring function. Defaults to None. - state_checkpointer (PerRoundStateCheckpointer | None, optional): If defined, this checkpointer will be - used to preserve FL training state to facilitate restarting training if interrupted. Generally, this - checkpointer will save much more than just the model being trained. Defaults to None. + state_checkpointer (ServerStateCheckpointer | None, optional): If defined, this checkpointer + will be used to preserve FL training state to facilitate restarting training if interrupted. + Generally, this checkpointer will save much more than just the model being trained. Defaults to None. """ self.model = model self.parameter_exchanger = parameter_exchanger @@ -142,75 +149,68 @@ def _hydrate_model_for_checkpointing(self, server_parameters: Parameters) -> Non model_ndarrays = parameters_to_ndarrays(server_parameters) self.parameter_exchanger.pull_parameters(model_ndarrays, self.model) - def save_state( - self, state_checkpoint_name: str, server_parameters: Parameters, other_state: dict[str, Any] - ) -> None: + def save_state(self, server: FlServer, server_parameters: Parameters) -> None: """ - This function is meant to facilitate saving state required to restart on FL process on the server side. By - default, this function will always at least preserve the model being trained. However, it may be desirable to - save additional information, like the current server round etc. So the ``other_state`` dictionary may be - provided to preserve this additional state. - - **NOTE:** This function will throw an error if you attempt to save the model under the 'model' key in - ``other_state`` + Facilitates saving state required to restart the FL process on the server side. By default, this function + will preserve the state of the server as defined by ``snapshot_attrs`` in ``ServerStateCheckpointer`` . + Note that ``server_parameters`` will be hydrated and passed to the state checkpointer module to facilitate + saving the state of the server's parameters. Args: - state_checkpoint_name (str): Name of the state checkpoint file. The checkpointer itself will have a - directory to which state will be saved. + server (FlServer): Server object from which state will be extracted and saved. server_parameters (Parameters): Like model checkpointers, these are the aggregated Parameters stored by the server representing model state. They are mapped to a torch model architecture via the ``_hydrate_model_for_checkpointing`` function. - other_state (dict[str, Any]): Any additional state (such as current server round) to be checkpointed in - order to allow FL to restart from where it left off. Raises: - ValueError: Throws an error if ``other_state`` already has a key called "model" ValueError: Throws an error if this function is called, but no state checkpointer has been provided """ if self.state_checkpointer is not None: self._hydrate_model_for_checkpointing(server_parameters) - if "model" in other_state: - raise ValueError("Key 'model' already exists in the other_state dictionary.") - - checkpoint_dict = other_state | {"model": self.model} - self.state_checkpointer.save_checkpoint(state_checkpoint_name, checkpoint_dict=checkpoint_dict) + assert self.model is not None + self.state_checkpointer.save_server_state(server, self.model) else: raise ValueError("Attempting to save state but no state checkpointer is specified") - def maybe_load_state(self, state_checkpoint_name: str) -> dict[str, Any] | None: + def maybe_load_state(self, server: FlServer) -> Parameters | None: """ - This function facilitates loading of any pre-existing state (with the name ``state_checkpoint_name``) in the - directory of the state_checkpointer. If the state already exists at the proper path, the state is loaded - and returned. If it doesn't exist, we return None. + Facilitates loading of any pre-existing state in the directory of the state_checkpointer. + If a ``state_checkpointer`` is defined and a checkpoint exists at its checkpoint_path, this method hydrates + the model with the saved state and returns the corresponding server Parameters. If no checkpoint exists, + it logs this information and returns None. Args: - state_checkpoint_name (str): Name of the state checkpoint file. The checkpointer itself will have a - directory from which state will be loaded (if it exists). + server (FlServer): server into which checkpointed state will be loaded if a checkpoint exists Raises: ValueError: Throws an error if this function is called, but no state checkpointer has been provided Returns: - dict[str, Any] | None: If the state checkpoint properly exists and is loaded correctly, this dictionary - carries that state. Otherwise, we return a None (or throw an exception). + Parameters | None: If the state checkpoint properly exists and is loaded correctly, server_parameters + is returned. Otherwise, we return a None (or throw an exception). """ if self.state_checkpointer is not None: - if self.state_checkpointer.checkpoint_exists(state_checkpoint_name): - return self.state_checkpointer.load_checkpoint(state_checkpoint_name) - else: - log(INFO, "State checkpointer is defined but no state checkpoint exists.") - return None + assert self.model is not None, ( + "Attempting to load state but self.model is None, make sure to pass the model architecture" + " to checkpointing module" + ) + server_model = self.state_checkpointer.maybe_load_server_state(server, self.model) + if server_model: + assert self.parameter_exchanger is not None + return ndarrays_to_parameters(self.parameter_exchanger.push_parameters(server_model)) + return None else: raise ValueError("Attempting to load state, but no state checkpointer is specified") class PackingServerCheckpointAndAndStateModule(BaseServerCheckpointAndStateModule): + def __init__( self, model: nn.Module | None = None, parameter_exchanger: FullParameterExchangerWithPacking | None = None, model_checkpointers: ModelCheckpointers = None, - state_checkpointer: PerRoundStateCheckpointer | None = None, + state_checkpointer: ServerStateCheckpointer | None = None, ) -> None: """ @@ -232,9 +232,9 @@ def __init__( places. Defaults to None. model_checkpointers (ModelCheckpointers, optional): If defined, this checkpointer (or sequence of checkpointers) is used to checkpoint models based on their defined scoring function. Defaults to None. - state_checkpointer (PerRoundStateCheckpointer | None, optional): If defined, this checkpointer will be - used to preserve FL training state to facilitate restarting training if interrupted. Generally, this - checkpointer will save much more than just the model being trained. Defaults to None. + state_checkpointer (ServerStateCheckpointer | None, optional): If defined, this checkpointer + will be used to preserve FL training state to facilitate restarting training if interrupted. + Generally, this checkpointer will save much more than just the model being trained. Defaults to None. """ if parameter_exchanger is not None: assert isinstance( @@ -275,7 +275,7 @@ def __init__( self, model: nn.Module | None = None, model_checkpointers: ModelCheckpointers = None, - state_checkpointer: PerRoundStateCheckpointer | None = None, + state_checkpointer: ServerStateCheckpointer | None = None, ) -> None: """ This module is meant to handle SCAFFOLD model and state checkpointing on the server-side of an FL process. @@ -293,9 +293,9 @@ def __init__( these parameters to allow for real models to be saved. Defaults to None. model_checkpointers (ModelCheckpointers, optional): If defined, this checkpointer (or sequence of checkpointers) is used to checkpoint models based on their defined scoring function. Defaults to None. - state_checkpointer (PerRoundStateCheckpointer | None, optional): If defined, this checkpointer will be - used to preserve FL training state to facilitate restarting training if interrupted. Generally, this - checkpointer will save much more than just the model being trained. Defaults to None. + state_checkpointer (ServerStateCheckpointer | None, optional): If defined, this checkpointer + will be used to preserve FL training state to facilitate restarting training if interrupted. + Generally, this checkpointer will save much more than just the model being trained. Defaults to None. """ if model is not None: model_size = len(model.state_dict()) @@ -310,7 +310,7 @@ def __init__( self, model: nn.Module | None = None, model_checkpointers: ModelCheckpointers = None, - state_checkpointer: PerRoundStateCheckpointer | None = None, + state_checkpointer: ServerStateCheckpointer | None = None, ) -> None: """ This module is meant to handle FL flows with adaptive constraints, where the server and client communicate @@ -328,9 +328,9 @@ def __init__( these parameters to allow for real models to be saved. Defaults to None. model_checkpointers (ModelCheckpointers, optional): If defined, this checkpointer (or sequence of checkpointers) is used to checkpoint models based on their defined scoring function. Defaults to None. - state_checkpointer (PerRoundStateCheckpointer | None, optional): If defined, this checkpointer will be - used to preserve FL training state to facilitate restarting training if interrupted. Generally, this - checkpointer will save much more than just the model being trained. Defaults to None. + state_checkpointer (ServerStateCheckpointer | None, optional): If defined, this checkpointer + will be used to preserve FL training state to facilitate restarting training if interrupted. + Generally, this checkpointer will save much more than just the model being trained. Defaults to None. """ if model is not None: parameter_exchanger = FullParameterExchangerWithPacking(ParameterPackerAdaptiveConstraint()) @@ -344,7 +344,7 @@ def __init__( self, model: nn.Module | None = None, model_checkpointers: ModelCheckpointers = None, - state_checkpointer: PerRoundStateCheckpointer | None = None, + state_checkpointer: ServerStateCheckpointer | None = None, ) -> None: """ This module is meant to handle FL flows with clipping bits being passed to the server along with the model @@ -362,9 +362,9 @@ def __init__( these parameters to allow for real models to be saved. Defaults to None. model_checkpointers (ModelCheckpointers, optional): If defined, this checkpointer (or sequence of checkpointers) is used to checkpoint models based on their defined scoring function. Defaults to None. - state_checkpointer (PerRoundStateCheckpointer | None, optional): If defined, this checkpointer will be - used to preserve FL training state to facilitate restarting training if interrupted. Generally, this - checkpointer will save much more than just the model being trained. Defaults to None. + state_checkpointer (ServerStateCheckpointer | None, optional): If defined, this checkpointer + will be used to preserve FL training state to facilitate restarting training if interrupted. + Generally, this checkpointer will save much more than just the model being trained. Defaults to None. """ if model is not None: parameter_exchanger = FullParameterExchangerWithPacking(ParameterPackerWithClippingBit()) @@ -378,7 +378,7 @@ def __init__( self, model: nn.Module | None = None, model_checkpointers: ModelCheckpointers = None, - state_checkpointer: PerRoundStateCheckpointer | None = None, + state_checkpointer: ServerStateCheckpointer | None = None, ) -> None: """ This module is meant to handle FL flows with layer names being passed to the server along with the model @@ -396,9 +396,9 @@ def __init__( these parameters to allow for real models to be saved. Defaults to None. model_checkpointers (ModelCheckpointers, optional): If defined, this checkpointer (or sequence of checkpointers) is used to checkpoint models based on their defined scoring function. Defaults to None. - state_checkpointer (PerRoundStateCheckpointer | None, optional): If defined, this checkpointer will be - used to preserve FL training state to facilitate restarting training if interrupted. Generally, this - checkpointer will save much more than just the model being trained. Defaults to None. + state_checkpointer (ServerStateCheckpointer | None, optional): If defined, this checkpointer + will be used to preserve FL training state to facilitate restarting training if interrupted. + Generally, this checkpointer will save much more than just the model being trained. Defaults to None. """ if model is not None: parameter_exchanger = FullParameterExchangerWithPacking(ParameterPackerWithLayerNames()) @@ -412,7 +412,7 @@ def __init__( self, model: nn.Module | None = None, model_checkpointers: ModelCheckpointers = None, - state_checkpointer: PerRoundStateCheckpointer | None = None, + state_checkpointer: ServerStateCheckpointer | None = None, ) -> None: """ This module is meant to handle FL flows with parameters encoded in a sparse COO format being passed to the @@ -431,9 +431,10 @@ def __init__( these parameters to allow for real models to be saved. Defaults to None. model_checkpointers (ModelCheckpointers, optional): If defined, this checkpointer (or sequence of checkpointers) is used to checkpoint models based on their defined scoring function. Defaults to None. - state_checkpointer (PerRoundStateCheckpointer | None, optional): If defined, this checkpointer will be - used to preserve FL training state to facilitate restarting training if interrupted. Generally, this - checkpointer will save much more than just the model being trained. Defaults to None. + state_checkpointer (ServerStateCheckpointer | None, optional): If defined, this checkpointer + will be used to preserve FL training state to facilitate restarting training if interrupted. + Generally, this checkpointer will save much more than just the model being trained. + Defaults to None. """ if model is not None: parameter_exchanger = FullParameterExchangerWithPacking(SparseCooParameterPacker()) @@ -448,7 +449,7 @@ def __init__( model: nn.Module | None = None, parameter_exchanger: ExchangerType | None = None, model_checkpointers: ModelCheckpointers = None, - state_checkpointer: PerRoundStateCheckpointer | None = None, + state_checkpointer: ServerStateCheckpointer | None = None, ) -> None: """ This module is meant to handle FL flows with Opacus models where special treatment by the checkpointers is @@ -471,9 +472,9 @@ def __init__( parameters go to the right places. Defaults to None. model_checkpointers (ModelCheckpointers, optional): If defined, this checkpointer (or sequence of checkpointers) is used to checkpoint models based on their defined scoring function. Defaults to None. - state_checkpointer (PerRoundStateCheckpointer | None, optional): If defined, this checkpointer will be - used to preserve FL training state to facilitate restarting training if interrupted. Generally, this - checkpointer will save much more than just the model being trained. Defaults to None. + state_checkpointer (ServerStateCheckpointer | None, optional): If defined, this checkpointer + will be used to preserve FL training state to facilitate restarting training if interrupted. + Generally, this checkpointer will save much more than just the model being trained. Defaults to None. """ super().__init__(model, parameter_exchanger, model_checkpointers, state_checkpointer) @@ -491,12 +492,13 @@ def _ensure_checkpointers_are_of_opacus_type(self) -> None: class NnUnetServerCheckpointAndStateModule(BaseServerCheckpointAndStateModule): + def __init__( self, model: nn.Module | None = None, parameter_exchanger: ExchangerType | None = None, model_checkpointers: ModelCheckpointers = None, - state_checkpointer: PerRoundStateCheckpointer | None = None, + state_checkpointer: NnUnetServerStateCheckpointer | None = None, ) -> None: """ This module is meant to be used with the ``NnUnetServer`` class to handle model and state checkpointing on the @@ -528,9 +530,9 @@ def __init__( parameters go to the right places. Defaults to None. model_checkpointers (ModelCheckpointers, optional): If defined, this checkpointer (or sequence of checkpointers) is used to checkpoint models based on their defined scoring function. Defaults to None. - state_checkpointer (PerRoundStateCheckpointer | None, optional): If defined, this checkpointer will be - used to preserve FL training state to facilitate restarting training if interrupted. Generally, this - checkpointer will save much more than just the model being trained. Defaults to None. + state_checkpointer (NnUnetServerStateCheckpointer | None, optional): If defined, this checkpointer + will be used to preserve FL training state to facilitate restarting training if interrupted. + Generally, this checkpointer will save much more than just the model being trained. Defaults to None. """ super().__init__(model, parameter_exchanger, model_checkpointers, state_checkpointer) @@ -548,7 +550,7 @@ def __init__( self, model: nn.Module | None = None, model_checkpointers: ModelCheckpointers = None, - state_checkpointer: PerRoundStateCheckpointer | None = None, + state_checkpointer: ServerStateCheckpointer | None = None, ) -> None: """ This module is meant to handle DP SCAFFOLD model and state checkpointing on the server-side of an FL process. @@ -566,9 +568,9 @@ def __init__( these parameters to allow for real models to be saved. Defaults to None. model_checkpointers (ModelCheckpointers, optional): If defined, this checkpointer (or sequence of checkpointers) is used to checkpoint models based on their defined scoring function. Defaults to None. - state_checkpointer (PerRoundStateCheckpointer | None, optional): If defined, this checkpointer will be - used to preserve FL training state to facilitate restarting training if interrupted. Generally, this - checkpointer will save much more than just the model being trained. Defaults to None. + state_checkpointer (ServerStateCheckpointer | None, optional): If defined, this checkpointer + will be used to preserve FL training state to facilitate restarting training if interrupted. + Generally, this checkpointer will save much more than just the model being trained. Defaults to None. """ super().__init__(model, model_checkpointers, state_checkpointer) self._ensure_checkpointers_are_of_opacus_type() diff --git a/fl4health/checkpointing/state_checkpointer.py b/fl4health/checkpointing/state_checkpointer.py new file mode 100644 index 000000000..d28639bc1 --- /dev/null +++ b/fl4health/checkpointing/state_checkpointer.py @@ -0,0 +1,590 @@ +from __future__ import annotations + +import os +from abc import ABC, abstractmethod +from enum import Enum +from logging import ERROR, INFO, WARNING +from pathlib import Path +from typing import TYPE_CHECKING, Any + +import torch +import torch.nn as nn +from flwr.common.logger import log +from torch.optim import Optimizer +from torch.optim.lr_scheduler import LRScheduler + +if TYPE_CHECKING: + from fl4health.clients.basic_client import BasicClient + from fl4health.servers.base_server import FlServer + +from flwr.server.history import History + +from fl4health.metrics.metric_managers import MetricManager +from fl4health.reporting.reports_manager import ReportsManager +from fl4health.utils.losses import LossMeter +from fl4health.utils.snapshotter import ( + AbstractSnapshotter, + BytesSnapshotter, + EnumSnapshotter, + HistorySnapshotter, + LRSchedulerSnapshotter, + OptimizerSnapshotter, + SerializableObjectSnapshotter, + SingletonSnapshotter, + StringSnapshotter, + T, + TorchModuleSnapshotter, +) + + +class StateCheckpointer(ABC): + + def __init__( + self, + checkpoint_dir: Path, + checkpoint_name: str | None, + snapshot_attrs: dict[str, tuple[AbstractSnapshotter, Any]], + ) -> None: + """ + Class for saving and loading the state of the client or server attributes. Attributes are stored in a + dictionary to assist saving and are loaded in a dictionary. Checkpointing can be done after client or + server round to facilitate restarting federated training if interrupted, or during the client's training + loop to facilitate early stopping. + + Server and client state checkpointers will save to disk in the provided directory. A default name for the + state checkpoint will be derived if checkpoint name remains none at the time of saving. + + Args: + checkpoint_dir (Path): Directory to which checkpoints are saved. This can be modified later with + `set_checkpoint_path` + checkpoint_name (str): Name of the checkpoint to be saved. If None at time of state saving, a default name + will be given to the checkpoint. This can be changed later with `set_checkpoint_path` + snapshot_attrs (dict[str, tuple[AbstractSnapshotter, Any]]): Attributes that we need to save in order + to allow for restarting of training. + """ + self.checkpoint_dir = checkpoint_dir + self.checkpoint_name = checkpoint_name + self.checkpoint_path: str | None = None + if self.checkpoint_name is not None: + self.checkpoint_path = os.path.join(self.checkpoint_dir, self.checkpoint_name) + + self.snapshot_attrs = snapshot_attrs + self.snapshot_ckpt: dict[str, Any] = {} + + def set_checkpoint_path(self, checkpoint_dir: Path, checkpoint_name: str) -> None: + """ + Set or update the checkpoint path based on the provided checkpoint name and directory. + + Args: + checkpoint_dir (Path): The directory where the checkpoint will be saved. + checkpoint_name (str): The name of the checkpoint file. + """ + self.checkpoint_dir = checkpoint_dir + self.checkpoint_name = checkpoint_name + self.checkpoint_path = os.path.join(self.checkpoint_dir, self.checkpoint_name) + + def save_checkpoint(self, checkpoint_dict: dict[str, Any]) -> None: + """ + Save ``checkpoint_dict`` to checkpoint path defined based on checkpointer dir and checkpoint name. + + Args: + checkpoint_dict (dict[str, Any]): A dictionary with string keys and values of type Any representing the + state to be saved. + + Raises: + e: Will throw an error if there is an issue saving the model. ``Torch.save`` seems to swallow errors in + this context, so we explicitly surface the error with a try/except. + """ + assert self.checkpoint_path is not None, "Checkpoint path is not set but save_checkpoint has been called." + try: + log(INFO, f"Saving the state as {self.checkpoint_path}") + torch.save(checkpoint_dict, self.checkpoint_path) + except Exception as e: + log(ERROR, f"Encountered the following error while saving the checkpoint: {e}") + raise e + + def load_checkpoint(self) -> dict[str, Any]: + """ + Load and return the checkpoint stored in ``checkpoint_dir`` under the ``checkpoint_name`` if it exists. If + it does not exist, an assertion error will be thrown. + + Returns: + dict[str, Any]: A dictionary representing the checkpointed state, as loaded by ``torch.load``. + """ + assert self.checkpoint_path is not None, "Checkpoint path is not set but load_checkpoint has been called." + assert self.checkpoint_exists(), f"Could not verify existence of checkpoint file at {self.checkpoint_path}" + log(INFO, f"Loading state from checkpoint at {self.checkpoint_path}") + + return torch.load(self.checkpoint_path) + + def checkpoint_exists(self) -> bool: + """ + Check if a checkpoint exists at the checkpoint_path constructed as ``checkpoint_dir`` + ``checkpoint_name`` + + Returns: + bool: True if checkpoint exists, otherwise false. + """ + assert self.checkpoint_path is not None, "A checkpoint_path should be set but is no" + return os.path.exists(self.checkpoint_path) + + def add_to_snapshot_attr(self, name: str, snapshotter: AbstractSnapshotter, input_type: type[T]) -> None: + """ + Add new attribute to the default ``snapshot_attrs`` dictionary. For this, we need a snapshotter that + provides functionality for loading and saving the state of the attribute based on the type of the attribute. + + Args: + name (str): Name of the attribute to be added. + snapshotter (AbstractSnapshotter): Snapshotter object to be used for saving and loading the attribute. + input_type (type[T]): Expected type of the attribute. + """ + self.snapshot_attrs.update({name: (snapshotter, input_type)}) + + def delete_from_snapshot_attr(self, name: str) -> None: + """ + Delete the attribute from the default ``snapshot_attrs`` dictionary. This is useful for removing attributes + that are no longer needed or to avoid saving/loading them. + + Args: + name (str): Name of the attribute to be removed from the ``snapshot_attrs`` dictionary. + """ + del self.snapshot_attrs[name] + + def save_state(self) -> None: + """ + Create a snapshot of the state as defined in ``self.snapshot_attrs``. It is saved at ``self.checkpoint_path`` + """ + for attr_name, (snapshotter, expected_type) in self.snapshot_attrs.items(): + self.snapshot_ckpt.update(self._save_snapshot(snapshotter, attr_name, expected_type)) + + assert self.checkpoint_path is not None, "Attempting to save state but checkpoint_path is None" + log(INFO, f"Saving the state to checkpoint at {self.checkpoint_path}") + self.save_checkpoint(self.snapshot_ckpt) + # Release snapshot memory after disk persistence + self.snapshot_ckpt.clear() + + def load_state(self, attributes: list[str] | None = None) -> None: + """ + Load checkpointed state dictionary from the checkpoint, potentially restricting the attributes to load. + + Args: + attributes (list[str] | None): List of attributes to load from the checkpoint. If None, all attributes + specified in ``snapshot_attrs`` are loaded. Defaults to None. + """ + assert ( + self.checkpoint_exists() + ), f"No state checkpoint to load. Checkpoint at {self.checkpoint_path} does not exist" + + if attributes is None: + attributes = list(self.snapshot_attrs.keys()) + if not attributes: + log(WARNING, "self.snapshot_attrs is empty, which may be undesired behavior.") + + # If the checkpoint exists, load it into snapshot_ckpt + self.snapshot_ckpt = self.load_checkpoint() + + # Load components into target object + for attr in attributes: + snapshotter, expected_type = self.snapshot_attrs[attr] + self._load_snapshot(snapshotter, attr, expected_type) + log(INFO, f"Loaded the checkpointed state from {self.checkpoint_path}") + + # Release snapshot memory after loading + self.snapshot_ckpt.clear() + + @abstractmethod + def get_attribute(self, name: str) -> Any: + """ + Get the attribute from the client or server. + + Args: + name (str): Name of the attribute. + + Returns: + Any: The attribute value. + """ + raise NotImplementedError("get_attribute must be implemented by inheriting classes") + + @abstractmethod + def set_attribute(self, name: str, value: Any) -> None: + """ + Set the attribute on the client or server. + + Args: + name (str): Name of the attribute. + value (Any): Value to set for the attribute. + """ + raise NotImplementedError("set_attribute must be implemented by inheriting classes") + + def _dict_wrap_attr(self, name: str, expected_type: type[T]) -> dict[str, T]: + """ + Wrap the attribute in a dictionary if it is not already a dictionary. + + Args: + name (str): Name of the attribute. + expected_type (type[T]): Expected type of the attribute. + + Returns: + dict[str, T]: Wrapped attribute as a dictionary. + """ + attribute = self.get_attribute(name) + if isinstance(attribute, expected_type): + return {"None": attribute} + elif isinstance(attribute, dict): + for key, value in attribute.items(): + if not isinstance(value, expected_type): + raise ValueError(f"Incompatible type of attribute {type(attribute)} for key {key}") + return attribute + else: + raise ValueError(f"Incompatible type of attribute {type(attribute)}, expected {expected_type}") + + def _save_snapshot(self, snapshotter: AbstractSnapshotter, name: str, expected_type: type[T]) -> dict[str, Any]: + """ + Save the state of the attribute using the snapshotter's save_attribute functionality. + + Args: + snapshotter (AbstractSnapshotter): Snapshotter object to save the attribute. + name (str): Name of the attribute. + expected_type (type[T]): Expected type of the attribute. + + Returns: + dict[str, Any]: A dictionary containing the state of the attribute. + """ + attribute = self._dict_wrap_attr(name, expected_type) + return {name: snapshotter.save_attribute(attribute)} + + def _load_snapshot(self, snapshotter: AbstractSnapshotter, name: str, expected_type: type[T]) -> None: + """ + Load the state of the attribute using the snapshotter's ``load_attribute`` functionality. + + NOTE: This function assumes that ``snapshot_ckpt`` has been populated with the right data loaded from disk. + + Args: + snapshotter (dict[str, Any]): Snapshotter object to return the state of the attribute. + name (str): Name of the attribute. + expected_type (type[T]): Expected type of the attribute. + """ + attribute = self._dict_wrap_attr(name, expected_type) + snapshotter.load_attribute(self.snapshot_ckpt[name], attribute) + if list(attribute.keys()) == ["None"]: + self.set_attribute(name, attribute["None"]) + else: + self.set_attribute(name, attribute) + + +class ClientStateCheckpointer(StateCheckpointer): + + def __init__( + self, + checkpoint_dir: Path, + checkpoint_name: str | None = None, + snapshot_attrs: dict[str, tuple[AbstractSnapshotter, Any]] | None = None, + ) -> None: + """ + Class for saving and loading the state of a client's attributes as specified in ``snapshot_attrs``. + + Args: + checkpoint_dir (Path): Directory to which checkpoints are saved. This can be modified later with + `set_checkpoint_path` + checkpoint_name (str | None, optional): Name of the checkpoint to be saved. If None, but ``checkpoint_dir`` + is set then a default ``checkpoint_name`` based on the underlying name of the client to be + checkpointed will be set of the form ``f"client_{client.client_name}_state.pt"``. This can be changed + later with `set_checkpoint_path`. Defaults to None. + snapshot_attrs (dict[str, tuple[AbstractSnapshotter, Any]] | None, optional): Attributes that we need to + save in order to allow for restarting of training. If None, a sensible default set of attributes and + their associated snapshotters for an FL client are set. Defaults to None. + """ + # If snapshot_attrs is None, we set a sensible default set of attributes to be saved. These are a minimal + # set of attributes that can be used for per round checkpointing or early stopping. + # NOTE: These default attributes are useful for state checkpointing a BasicClient. More sophisticated clients + # may require more attributes to fully support training restarts and early stopping. For a server example, see + # NnUnetServerStateCheckpointer. + if snapshot_attrs is None: + snapshot_attrs = { + "model": (TorchModuleSnapshotter(), nn.Module), + "optimizers": (OptimizerSnapshotter(), Optimizer), + "lr_schedulers": ( + LRSchedulerSnapshotter(), + LRScheduler, + ), + "total_steps": (SingletonSnapshotter(), int), + "total_epochs": (SingletonSnapshotter(), int), + "reports_manager": ( + SerializableObjectSnapshotter(), + ReportsManager, + ), + "train_loss_meter": ( + SerializableObjectSnapshotter(), + LossMeter, + ), + "train_metric_manager": ( + SerializableObjectSnapshotter(), + MetricManager, + ), + } + + super().__init__(checkpoint_dir, checkpoint_name, snapshot_attrs) + self.client: BasicClient | None = None + + def maybe_set_default_checkpoint_name(self) -> None: + """ + Potentially sets a default name for the checkpoint to be saved. If ``checkpoint_dir`` is set but + ``checkpoint_name`` is None then a default ``checkpoint_name`` based on the underlying name of the client to + be checkpointed will be set of the form ``f"client_{self.client.client_name}_state.pt"``. + """ + assert self.client is not None, "Attempting to save client state but client is None" + # Set the checkpoint name based on client's name if not already provided. + if self.checkpoint_name is None: + # If checkpoint_name is not provided, we set it based on the client name. + self.checkpoint_name = f"client_{self.client.client_name}_state.pt" + self.set_checkpoint_path(self.checkpoint_dir, self.checkpoint_name) + + def save_client_state(self, client: BasicClient) -> None: + """ + Save the state of the client that is provided. + + Args: + client (BasicClient): Client object with state to be saved. + """ + # Store client for access in functions + self.client = client + # Potentially set a default checkpoint name + self.maybe_set_default_checkpoint_name() + # Saves everything in self.snapshot_attrs + self.save_state() + # Clear the client after being checkpointed. + self.client = None + + def maybe_load_client_state(self, client: BasicClient, attributes: list[str] | None = None) -> bool: + """ + Load the state into the client that is being provided. + + Args: + client (BasicClient): Target client object into which state will be loaded + attributes (list[str] | None, optional): List of attributes to load from the checkpoint. If None, all + attributes specified in ``snapshot_attrs`` are loaded. Defaults to None. + + Returns: + bool: True if a checkpoint is successfully loaded. False otherwise + """ + # Store client for access in functions + self.client = client + # Setting default name if one doesn't exist. If we're here and it doesn't exist yet, user is expecting + # a default name for loading anyway. + self.maybe_set_default_checkpoint_name() + if self.checkpoint_exists(): + self.load_state(attributes) + log(INFO, f"State checkpoint successfully loaded from: {self.checkpoint_path}") + # Clear the client after we are done updating its attributes. + self.client = None + return True + + log(INFO, f"No state checkpoint found at: {self.checkpoint_path}") + # Clear the client since no checkpoint exists. + self.client = None + return False + + def get_attribute(self, name: str) -> Any: + """ + Get the attribute from the client. + + Args: + name (str): Name of the attribute. + + Returns: + Any: The attribute value. + """ + assert self.client is not None, "Client is not set." + attribute = getattr(self.client, name) + return attribute + + def set_attribute(self, name: str, value: Any) -> None: + """ + Set the attribute on the client. + + Args: + name (str): Name of the attribute. + value (Any): Value to set for the attribute. + """ + assert self.client is not None, "Client is not set." + setattr(self.client, name, value) + + +class ServerStateCheckpointer(StateCheckpointer): + + def __init__( + self, + checkpoint_dir: Path, + checkpoint_name: str | None = None, + snapshot_attrs: dict[str, tuple[AbstractSnapshotter, Any]] | None = None, + ) -> None: + """ + Class for saving and loading the state of a server's attributes as specified in ``snapshot_attrs``. + + Args: + checkpoint_dir (Path): Directory to which checkpoints are saved. This can be modified later with + `set_checkpoint_path` + checkpoint_name (str | None, optional): Name of the checkpoint to be saved. If None, but ``checkpoint_dir`` + is set then a default ``checkpoint_name`` based on the underlying name of the client to be + checkpointed will be set of the form ``f"f"server_{self.server.server_name}_state.pt""``. This can be + updated later with `set_checkpoint_path`. Defaults to None. + snapshot_attrs (dict[str, tuple[AbstractSnapshotter, Any]] | None, optional): Attributes that we need to + save in order to allow for restarting of training. If None, a sensible default set of attributes and + their associated snapshotters for an FL client are set. Defaults to None. + """ + # If snapshot_attrs is None, we set a sensible default set of attributes to be saved. These are a minimal + # set of attributes that can be used for per round checkpointing or early stopping. + # NOTE: These default attributes are useful for state checkpointing a FlServer. More sophisticated servers + # may require more attributes to fully support training restarts and early stopping. For an example, see + # NnUnetServerStateCheckpointer. + if snapshot_attrs is None: + snapshot_attrs = { + "model": (TorchModuleSnapshotter(), nn.Module), + "current_round": (SingletonSnapshotter(), int), + "reports_manager": ( + SerializableObjectSnapshotter(), + ReportsManager, + ), + "server_name": (StringSnapshotter(), str), + "history": (HistorySnapshotter(), History), + } + super().__init__(checkpoint_dir, checkpoint_name, snapshot_attrs) + self.server: FlServer | None = None + self.server_model: nn.Module | None = None + + def maybe_set_default_checkpoint_name(self) -> None: + """ + Potentially sets a default name for the checkpoint to be saved. If ``checkpoint_dir`` is set but + ``checkpoint_name`` is None then a default ``checkpoint_name`` based on the underlying name of the server to + be checkpointed will be set of the form ``f"server_{self.server.server_name}_state.pt"``. + """ + + assert self.server is not None, "Attempting to save server state but server is None" + # Set the checkpoint name based on server's name if not already provided. + if self.checkpoint_name is None: + # If checkpoint_name is not provided, we set it based on the server's name. + self.checkpoint_name = f"server_{self.server.server_name}_state.pt" + self.set_checkpoint_path(self.checkpoint_dir, self.checkpoint_name) + + def save_server_state(self, server: FlServer, model: nn.Module) -> None: + """ + Save the state of the server, including a torch model, which is not a required component of the server class + + Args: + server (FlServer): Server with state to be saved + model (nn.Module): The model to be saved as part of the server state. + """ + # Store server and model for access in functions + self.server = server + # Server object does not have a model attribute, so we handle it separately. + self.server_model = model + # Potentially set a default checkpoint name + self.maybe_set_default_checkpoint_name() + # Saves everything in self.snapshot_attrs + self.save_state() + # Clear the server objects after checkpointing. + self.server = None + self.server_model = None + + def maybe_load_server_state( + self, server: FlServer, model: nn.Module, attributes: list[str] | None = None + ) -> nn.Module | None: + """ + Load the state of the server from checkpoint. + + Args: + server (FlServer): server into which the attributes will be loaded + model nn.Module: The model structure to be loaded as part of the server state. + attributes (list[str] | None): List of attributes to load from the checkpoint. If None, all attributes + specified in ``snapshot_attrs`` are loaded. Defaults to None. + + Returns: + nn.Module | None: Returns a model if a checkpoint exists to load from. Otherwise returns None + """ + # Store server for access in functions + self.server = server + # Server object does not have a model attribute, so we handle it separately. + self.server_model = model + # Setting default name if one doesn't exist. If we're here and it doesn't exist yet, user is expecting + self.maybe_set_default_checkpoint_name() + if self.checkpoint_exists(): + self.load_state(attributes) + log(INFO, f"State checkpoint successfully loaded from: {self.checkpoint_path}") + # Clear the server after we are done updating its attributes. + self.server = None + # Server model is saved and returned separately for parameter extraction. + return self.server_model + + log(INFO, f"No state checkpoint found at: {self.checkpoint_path}") + # Clear the server object since checkpoint is not found. + self.server = None + self.server_model = None + return None + + def get_attribute(self, name: str) -> Any: + """ + Get the attribute from the server. + + Args: + name (str): Name of the attribute. + + Returns: + Any: The attribute value. + """ + assert self.server is not None, "Server is not set." + if name == "model": + return self.server_model + return getattr(self.server, name) + + def set_attribute(self, name: str, value: Any) -> None: + """ + Set the attribute on the server. + + Args: + name (str): Name of the attribute. + value (Any): Value to set for the attribute. + """ + assert self.server is not None, "Server is not set." + if name == "model": + self.server_model = value + else: + setattr(self.server, name, value) + + +class NnUnetServerStateCheckpointer(ServerStateCheckpointer): + + def __init__( + self, + checkpoint_dir: Path, + checkpoint_name: str | None = None, + ) -> None: + """ + Class for saving and loading the state of the server's attributes based on the ``snapshot_attrs`` defined + specifically for the nnUNet server. + + Args: + checkpoint_dir (Path): Directory to which checkpoints are saved. This can be modified later with + `set_checkpoint_path` + checkpoint_name (str | None, optional): Name of the checkpoint to be saved. If None, but ``checkpoint_dir`` + is set then a default ``checkpoint_name`` based on the underlying name of the client to be + checkpointed will be set of the form ``f"f"server_{self.server.server_name}_state.pt""``. This can be + updated later with `set_checkpoint_path`. Defaults to None. + """ + + # Go beyond default snapshot_attrs with nnUNet-specific attributes. + nnunet_snapshot_attrs: dict[str, tuple[AbstractSnapshotter, Any]] = { + "model": (TorchModuleSnapshotter(), nn.Module), + "current_round": (SingletonSnapshotter(), int), + "reports_manager": ( + SerializableObjectSnapshotter(), + ReportsManager, + ), + "server_name": (StringSnapshotter(), str), + "history": (HistorySnapshotter(), History), + "nnunet_plans_bytes": (BytesSnapshotter(), bytes), + "num_segmentation_heads": (SingletonSnapshotter(), int), + "num_input_channels": (SingletonSnapshotter(), int), + "global_deep_supervision": (EnumSnapshotter(), bool), + "nnunet_config": (EnumSnapshotter(), Enum), + } + + super().__init__(checkpoint_dir, checkpoint_name, snapshot_attrs=nnunet_snapshot_attrs) diff --git a/fl4health/clients/basic_client.py b/fl4health/clients/basic_client.py index eaff0df1d..a71f50f10 100644 --- a/fl4health/clients/basic_client.py +++ b/fl4health/clients/basic_client.py @@ -29,7 +29,7 @@ process_and_check_validation_steps, set_pack_losses_with_val_metrics, ) -from fl4health.utils.config import narrow_dict_type, narrow_dict_type_and_set_attribute +from fl4health.utils.config import narrow_dict_type from fl4health.utils.early_stopper import EarlyStopper from fl4health.utils.logging import LoggingMode from fl4health.utils.losses import EvaluationLosses, LossMeter, LossMeterType, TrainingLosses @@ -82,8 +82,6 @@ def __init__( self.client_name = client_name if client_name is not None else generate_hash() log(INFO, f"Client Name: {self.client_name}") - self.state_checkpoint_name = f"client_{self.client_name}_state.pt" - if checkpoint_and_state_module is not None: self.checkpoint_and_state_module = checkpoint_and_state_module else: @@ -1292,49 +1290,19 @@ def transform_gradients(self, losses: TrainingLosses) -> None: def _save_client_state(self) -> None: """ - Saves checkpoint dict consisting of client name, total steps, lr schedulers, metrics reporter and - optimizers state. Method can be overridden to augment saved checkpointed state. + Save a checkpoint of the client's state as defined by the state_checkpointer's snapshot_attrs. + By default, snapshot_attrs includes attributes such as client name, total steps, lr schedulers, + metrics reporter, and optimizer states. You can override snapshot_attrs in the state_checkpointer to + customize which attributes are saved in the checkpoint. """ - - state = { - "lr_schedulers_state": {key: scheduler.state_dict() for key, scheduler in self.lr_schedulers.items()}, - "total_steps": self.total_steps, - "client_name": self.client_name, - "reports_manager": self.reports_manager, - "optimizers_state": {key: optimizer.state_dict()["state"] for key, optimizer in self.optimizers.items()}, - } - - self.checkpoint_and_state_module.save_state(self.state_checkpoint_name, state) + assert self.checkpoint_and_state_module.state_checkpointer is not None + self.checkpoint_and_state_module.save_state(self) def _load_client_state(self) -> bool: """ Load checkpoint dict consisting of client name, total steps, lr schedulers, metrics reporter and optimizers state. Method can be overridden to augment loaded checkpointed state. """ - client_state = self.checkpoint_and_state_module.maybe_load_state(self.state_checkpoint_name) - - if client_state is None: - return False - - narrow_dict_type_and_set_attribute(self, client_state, "client_name", "client_name", str) - narrow_dict_type_and_set_attribute(self, client_state, "total_steps", "total_steps", int) - narrow_dict_type_and_set_attribute(self, client_state, "reports_manager", "reports_manager", ReportsManager) - - assert "lr_schedulers_state" in client_state and isinstance(client_state["lr_schedulers_state"], dict) - assert "optimizers_state" in client_state and isinstance(client_state["optimizers_state"], dict) - - # Optimizer is updated in setup_client to reference model weights from server - # Thus, only optimizer state (per parameter values such as momentum) - # should be loaded - for key, optimizer in self.optimizers.items(): - optimizer_state = client_state["optimizers_state"][key] - optimizer_state_dict = optimizer.state_dict() - optimizer_state_dict["state"] = optimizer_state - optimizer.load_state_dict(optimizer_state_dict) - - # Schedulers initialized in setup_client to reference correct optimizers - # Here we load in all other aspects of the scheduler state - for key in self.lr_schedulers: - self.lr_schedulers[key].load_state_dict(client_state["lr_schedulers_state"][key]) - - return True + assert self.checkpoint_and_state_module.state_checkpointer is not None + log(INFO, "Loading client state from checkpoint") + return self.checkpoint_and_state_module.maybe_load_state(self) diff --git a/fl4health/servers/base_server.py b/fl4health/servers/base_server.py index f2c6d6199..09e8d7e9f 100644 --- a/fl4health/servers/base_server.py +++ b/fl4health/servers/base_server.py @@ -2,7 +2,6 @@ from collections.abc import Callable, Sequence from logging import DEBUG, ERROR, INFO, WARNING -import torch.nn as nn from flwr.common import EvaluateRes, Parameters from flwr.common.logger import log from flwr.common.typing import Code, Config, GetParametersIns, Scalar @@ -18,8 +17,6 @@ from fl4health.reporting.reports_manager import ReportsManager from fl4health.servers.polling import poll_clients from fl4health.strategies.strategy_with_poll import StrategyWithPolling -from fl4health.utils.config import narrow_dict_type_and_set_attribute -from fl4health.utils.parameter_extraction import get_all_model_parameters from fl4health.utils.random import generate_hash from fl4health.utils.typing import EvaluateFailures, FitFailures @@ -83,7 +80,6 @@ def __init__( self.server_name = server_name if server_name is not None else generate_hash() log(INFO, f"Server Name: {self.server_name}") - self.state_checkpoint_name = f"server_{self.server_name}_state.pt" self.accept_failures = accept_failures self.current_round: int @@ -140,17 +136,17 @@ def fit_with_per_round_checkpointing(self, num_rounds: int, timeout: float | Non seconds. """ - # Attempt to load the server state if it exists. If the state checkpoint exists, update history, server - # round and model accordingly + log(INFO, "Initializing server state and global parameters") + self.parameters = self._get_initial_parameters(server_round=0, timeout=timeout) + self.history = History() + self.current_round = 1 + # Attempt to load the server state if it exists. If the state checkpoint exists, update the initiated + # attributes like history, server round and model accordingly state_load_success = self._load_server_state() if state_load_success: log(INFO, "Server state checkpoint successfully loaded.") else: - log(INFO, "Initializing server state and global parameters") - self.parameters = self._get_initial_parameters(server_round=0, timeout=timeout) - self.history = History() - self.current_round = 1 - + log(INFO, "No server state checkpoint found. Starting from scratch.") if self.current_round == 1: log(INFO, "Evaluating initial parameters") res = self.strategy.evaluate(0, parameters=self.parameters) @@ -408,44 +404,24 @@ def _save_server_state(self) -> None: method can be overridden to add any necessary state to the checkpoint. The model will be injected into the ckpt state by the checkpoint module """ - other_state_to_save = { - "history": self.history, - "current_round": self.current_round, - "reports_manager": self.reports_manager, - "server_name": self.server_name, - } - - self.checkpoint_and_state_module.save_state( - state_checkpoint_name=self.state_checkpoint_name, - server_parameters=self.parameters, - other_state=other_state_to_save, - ) + assert self.checkpoint_and_state_module.state_checkpointer is not None + self.checkpoint_and_state_module.save_state(self, self.parameters) def _load_server_state(self) -> bool: """ Load server checkpoint consisting of model, history, server name, current round and metrics reporter. The method can be overridden to add any necessary state when loading the checkpoint. """ - - # Attempt to load the server state if it exists. This variable will be None if it does not. - server_state = self.checkpoint_and_state_module.maybe_load_state(self.state_checkpoint_name) - - if server_state is None: + assert self.checkpoint_and_state_module.state_checkpointer is not None + # Attempt to load the server state if it exists. + server_parameters = self.checkpoint_and_state_module.maybe_load_state(self) + if server_parameters: + self.parameters = server_parameters + log(INFO, "Loaded server state from checkpoint") + return True + else: return False - narrow_dict_type_and_set_attribute(self, server_state, "server_name", "server_name", str) - narrow_dict_type_and_set_attribute(self, server_state, "current_round", "current_round", int) - narrow_dict_type_and_set_attribute(self, server_state, "reports_manager", "reports_manager", ReportsManager) - narrow_dict_type_and_set_attribute(self, server_state, "history", "history", History) - narrow_dict_type_and_set_attribute( - self, server_state, "model", "parameters", nn.Module, func=get_all_model_parameters - ) - # Needed for when _hydrate_model_for_checkpointing is called - narrow_dict_type_and_set_attribute(self, server_state, "model", "server_model", nn.Module) - - self.parameters = get_all_model_parameters(server_state["model"]) - return True - def _terminate_after_unacceptable_failures(self, timeout: float | None) -> None: assert not self.accept_failures # First we shutdown all clients involved in the FL training/evaluation if they can be. diff --git a/fl4health/servers/nnunet_server.py b/fl4health/servers/nnunet_server.py index ca73cf7c6..f5580ef6f 100644 --- a/fl4health/servers/nnunet_server.py +++ b/fl4health/servers/nnunet_server.py @@ -4,22 +4,18 @@ from logging import INFO from typing import Any -import torch.nn as nn from flwr.common import Parameters from flwr.common.logger import log from flwr.common.typing import Code, Config, EvaluateIns, FitIns, GetPropertiesIns, Scalar from flwr.server.client_manager import ClientManager from flwr.server.client_proxy import ClientProxy -from flwr.server.history import History from flwr.server.strategy import Strategy from fl4health.checkpointing.server_module import NnUnetServerCheckpointAndStateModule from fl4health.reporting.base_reporter import BaseReporter -from fl4health.reporting.reports_manager import ReportsManager from fl4health.servers.base_server import FlServer -from fl4health.utils.config import narrow_dict_type, narrow_dict_type_and_set_attribute +from fl4health.utils.config import narrow_dict_type from fl4health.utils.nnunet_utils import NnunetConfig -from fl4health.utils.parameter_extraction import get_all_model_parameters with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=DeprecationWarning) @@ -154,7 +150,6 @@ def initialize_server_model(self) -> None: self.num_segmentation_heads, self.global_deep_supervision, ) - self.checkpoint_and_state_module.model = model def update_before_fit(self, num_rounds: int, timeout: float | None) -> None: @@ -191,18 +186,7 @@ def update_before_fit(self, num_rounds: int, timeout: float | None) -> None: or self.checkpoint_and_state_module.model_checkpointers is not None ) - # If the state_checkpointer has been specified and a state checkpoint exists, we load state - # NOTE: Inherent assumption that if checkpoint exists for server that it also will exist for client. - if ( - self.checkpoint_and_state_module.state_checkpointer is not None - and self.checkpoint_and_state_module.state_checkpointer.checkpoint_exists( - self.state_checkpoint_name - ) # self.state_checkpoint_name initialized in base FLServer Class - ): - self._load_server_state() - - # Otherwise, we're starting training from "scratch" - elif checkpointer_exists or plans_bytes is None: + if checkpointer_exists or plans_bytes is None: log(INFO, "") log(INFO, "[PRE-INIT]") log(INFO, "Requesting properties from one random client via get_properties") @@ -245,6 +229,10 @@ def update_before_fit(self, num_rounds: int, timeout: float | None) -> None: if checkpointer_exists: self.initialize_server_model() + # If the state_checkpointer has been specified and a state checkpoint exists, the state + # will be loaded when executing ``fit_with_per_round_checkpointing`` of the base_server. + # NOTE: Inherent assumption that if checkpoint exists for server that it also will exist for client. + # Wrap config functions so that we are sure the nnunet_plans are included new_fit_cfg_fn = add_items_to_config_fn( self.strategy.configure_fit, {"nnunet_plans": self.nnunet_plans_bytes} @@ -254,12 +242,9 @@ def update_before_fit(self, num_rounds: int, timeout: float | None) -> None: ) setattr(self.strategy, "configure_fit", new_fit_cfg_fn) setattr(self.strategy, "configure_evaluate", new_eval_cfg_fn) - # Finish log(INFO, "") - # TODO: We should have a get server state method - # subclass could call parent method and not have to copy entire state. def _save_server_state(self) -> None: """ Save server checkpoint consisting of model, history, server round, metrics reporter and server name. This @@ -275,52 +260,4 @@ def _save_server_state(self) -> None: and self.nnunet_config is not None ) - other_state_to_save = { - "history": self.history, - "current_round": self.current_round, - "reports_manager": self.reports_manager, - "server_name": self.server_name, - "nnunet_plans_bytes": self.nnunet_plans_bytes, - "num_input_channels": self.num_input_channels, - "num_segmentation_heads": self.num_segmentation_heads, - "global_deep_supervision": self.global_deep_supervision, - "nnunet_config": self.nnunet_config, - } - - self.checkpoint_and_state_module.save_state( - state_checkpoint_name=self.state_checkpoint_name, - server_parameters=self.parameters, - other_state=other_state_to_save, - ) - - def _load_server_state(self) -> bool: - """ - Load server checkpoint consisting of model, history, server name, current round and metrics reporter. - The method overrides parent to add any necessary state when loading the checkpoint. - """ - # Attempt to load the server state if it exists. This variable will be None if it does not. - server_state = self.checkpoint_and_state_module.maybe_load_state(self.state_checkpoint_name) - - if server_state is None: - return False - - # Standard attributes to load - narrow_dict_type_and_set_attribute(self, server_state, "current_round", "current_round", int) - narrow_dict_type_and_set_attribute(self, server_state, "server_name", "server_name", str) - narrow_dict_type_and_set_attribute(self, server_state, "reports_manager", "reports_manager", ReportsManager) - narrow_dict_type_and_set_attribute(self, server_state, "history", "history", History) - narrow_dict_type_and_set_attribute( - self, server_state, "model", "parameters", nn.Module, func=get_all_model_parameters - ) - # Needed for when _hydrate_model_for_checkpointing is called - narrow_dict_type_and_set_attribute(self, server_state, "model", "server_model", nn.Module) - - # NnunetServer specific attributes to load - narrow_dict_type_and_set_attribute(self, server_state, "nnunet_plans_bytes", "nnunet_plans_bytes", bytes) - narrow_dict_type_and_set_attribute(self, server_state, "num_segmentation_heads", "num_segmentation_heads", int) - narrow_dict_type_and_set_attribute(self, server_state, "num_input_channels", "num_input_channels", int) - narrow_dict_type_and_set_attribute( - self, server_state, "global_deep_supervision", "global_deep_supervision", bool - ) - narrow_dict_type_and_set_attribute(self, server_state, "nnunet_config", "nnunet_config", NnunetConfig) - return True + super()._save_server_state() diff --git a/fl4health/utils/early_stopper.py b/fl4health/utils/early_stopper.py index bbf5f755a..30f3ea9ed 100644 --- a/fl4health/utils/early_stopper.py +++ b/fl4health/utils/early_stopper.py @@ -1,42 +1,22 @@ from __future__ import annotations -import copy -from collections.abc import Callable -from logging import INFO, WARNING from pathlib import Path -from typing import TYPE_CHECKING, Any - -import torch.nn as nn -from flwr.common.logger import log -from torch.optim import Optimizer -from torch.optim.lr_scheduler import LRScheduler - -from fl4health.checkpointing.checkpointer import PerRoundStateCheckpointer -from fl4health.metrics.metric_managers import MetricManager -from fl4health.reporting.reports_manager import ReportsManager -from fl4health.utils.logging import LoggingMode -from fl4health.utils.losses import TrainingLosses -from fl4health.utils.snapshotter import ( - AbstractSnapshotter, - LRSchedulerSnapshotter, - NumberSnapshotter, - OptimizerSnapshotter, - SerializableObjectSnapshotter, - T, - TorchModuleSnapshotter, -) +from typing import TYPE_CHECKING if TYPE_CHECKING: from fl4health.clients.basic_client import BasicClient +from fl4health.checkpointing.state_checkpointer import ClientStateCheckpointer +from fl4health.utils.logging import LoggingMode + class EarlyStopper: def __init__( self, client: BasicClient, + train_loop_checkpoint_dir: Path, patience: int | None = 1, interval_steps: int = 5, - snapshot_dir: Path | None = None, ) -> None: """ Early stopping class is a plugin for the client that allows to stop local training based on the validation @@ -46,13 +26,12 @@ def __init__( Args: client (BasicClient): The client to be monitored. + train_loop_checkpoint_dir (Path): Directory to checkpoint the "best" state seen so far. patience (int, optional): Number of validation cycles to wait before stopping the training. If it is equal to None client never stops, but still loads the best state before sending the model to the server. Defaults to 1. interval_steps (int): Specifies the frequency, in terms of training intervals, at which the early stopping mechanism should evaluate the validation loss. Defaults to 5. - snapshot_dir (Path | None, optional): Rather than keeping best state in the memory we can checkpoint it to - the given directory. If it is not given, the best state is kept in the memory. Defaults to None. """ self.client = client @@ -61,94 +40,25 @@ def __init__( self.count_down = patience self.interval_steps = interval_steps - self.best_score: float | None = None - self.snapshot_ckpt: dict[str, tuple[AbstractSnapshotter, Any]] = {} - - self.snapshot_attrs: dict = { - "model": (TorchModuleSnapshotter(self.client), nn.Module), - "optimizers": (OptimizerSnapshotter(self.client), Optimizer), - "lr_schedulers": ( - LRSchedulerSnapshotter(self.client), - LRScheduler, - ), - "learning_rate": (NumberSnapshotter(self.client), float), - "total_steps": (NumberSnapshotter(self.client), int), - "total_epochs": (NumberSnapshotter(self.client), int), - "reports_manager": ( - SerializableObjectSnapshotter(self.client), - ReportsManager, - ), - "train_loss_meter": ( - SerializableObjectSnapshotter(self.client), - TrainingLosses, - ), - "train_metric_manager": ( - SerializableObjectSnapshotter(self.client), - MetricManager, - ), - } - - if snapshot_dir is not None: - # TODO: Move to generic checkpointer - self.checkpointer = PerRoundStateCheckpointer(snapshot_dir) - self.checkpoint_name = f"temp_{self.client.client_name}.pt" - else: - log(INFO, "Snapshot is being persisted in memory") - - def add_default_snapshot_attr( - self, name: str, snapshot_class: Callable[[BasicClient], AbstractSnapshotter], input_type: type[T] - ) -> None: - self.snapshot_attrs.update({name: (snapshot_class(self.client), input_type)}) + # Early stopper uses a default name for the state + checkpoint_name = f"temp_{self.client.client_name}.pt" - def delete_default_snapshot_attr(self, name: str) -> None: - del self.snapshot_attrs[name] + self.state_checkpointer = ClientStateCheckpointer( + checkpoint_dir=train_loop_checkpoint_dir, checkpoint_name=checkpoint_name + ) - def save_snapshot(self) -> None: - """ - Creates a snapshot of the client state and if ``snapshot_ckpt`` is given, saves it to the checkpoint. - """ - for attr, (snapshotter_function, expected_type) in self.snapshot_attrs.items(): - self.snapshot_ckpt.update(snapshotter_function.save(attr, expected_type)) - - if self.checkpointer is not None: - log( - INFO, - f"Saving client best state to checkpoint at {self.checkpointer.checkpoint_dir} " - f"with name {self.checkpoint_name}.", - ) - self.checkpointer.save_checkpoint(self.checkpoint_name, self.snapshot_ckpt) - self.snapshot_ckpt.clear() - - else: - log( - WARNING, - "Checkpointing directory is not provided. Client best state will be kept in the memory.", - ) - self.snapshot_ckpt = copy.deepcopy(self.snapshot_ckpt) + self.best_score: float | None = None def load_snapshot(self, attributes: list[str] | None = None) -> None: """ - Load checkpointed snapshot dict consisting to the respective model attributes. + Load the best snapshot of the client state from the checkpoint directory. Args: - attributes (list[str] | None): List of attributes to load from the checkpoint. If None, all attributes - are loaded. Defaults to None. + attributes (list[str] | None, optional): List of attributes to load from the checkpoint. + If None, all attributes as defined in ``state_checkpointer`` are loaded. Defaults to None. """ - assert ( - self.checkpointer.checkpoint_exists(self.checkpoint_name) or self.snapshot_ckpt != {} - ), "No checkpoint to load" - - if attributes is None: - attributes = list(self.snapshot_attrs.keys()) - - log(INFO, f"Loading client best state {attributes} from checkpoint at {self.checkpointer.checkpoint_dir}") - - if self.checkpointer.checkpoint_exists(self.checkpoint_name): - self.snapshot_ckpt = self.checkpointer.load_checkpoint(self.checkpoint_name) - - for attr in attributes: - snapshotter, expected_type = self.snapshot_attrs[attr] - snapshotter.load(self.snapshot_ckpt, attr, expected_type) + # Load the best snapshot, and update self.client with the values + self.state_checkpointer.maybe_load_client_state(self.client, attributes) def should_stop(self, steps: int) -> bool: """ @@ -177,7 +87,7 @@ def should_stop(self, steps: int) -> bool: if self.best_score is None or val_loss < self.best_score: self.best_score = val_loss self.count_down = self.patience - self.save_snapshot() + self.state_checkpointer.save_client_state(self.client) return False if self.count_down is not None: diff --git a/fl4health/utils/snapshotter.py b/fl4health/utils/snapshotter.py index 5b4524859..358ca5f0a 100644 --- a/fl4health/utils/snapshotter.py +++ b/fl4health/utils/snapshotter.py @@ -1,15 +1,14 @@ from __future__ import annotations from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, Generic, TypeVar +from enum import Enum +from typing import Any, Generic, TypeVar import torch.nn as nn +from flwr.server.history import History from torch.optim import Optimizer from torch.optim.lr_scheduler import LRScheduler -if TYPE_CHECKING: - from fl4health.clients.basic_client import BasicClient - from fl4health.metrics.metric_managers import MetricManager from fl4health.reporting.reports_manager import ReportsManager from fl4health.utils.losses import LossMeter @@ -18,71 +17,11 @@ class AbstractSnapshotter(ABC, Generic[T]): - def __init__(self, client: BasicClient) -> None: - """ - Abstract class for saving and loading the state of the client's attributes. - - Args: - client (BasicClient): The client to be monitored. - """ - self.client = client - - def dict_wrap_attr(self, name: str, expected_type: type[T]) -> dict[str, T]: - """ - Wrap the attribute in a dictionary if it is not already a dictionary. - - Args: - name (str): Name of the attribute. - expected_type (type[T]): Expected type of the attribute. - - Returns: - dict[str, T]: Wrapped attribute as a dictionary. - """ - attribute = getattr(self.client, name) - if isinstance(attribute, expected_type): - return {"None": attribute} - elif isinstance(attribute, dict): - for key, value in attribute.items(): - if not isinstance(value, expected_type): - raise ValueError(f"Incompatible type of attribute {type(attribute)} for key {key}") - return attribute - else: - raise ValueError(f"Incompatible type of attribute {type(attribute)}") - - def save(self, name: str, expected_type: type[T]) -> dict[str, Any]: - """ - Save the state of the attribute. - - Args: - name (str): Name of the attribute. - expected_type (type[T]): Expected type of the attribute. - - Returns: - dict[str, Any]: A dictionary containing the state of the attribute. - """ - attribute = self.dict_wrap_attr(name, expected_type) - return {name: self.save_attribute(attribute)} - - def load(self, snapshot: dict[str, Any], name: str, expected_type: type[T]) -> None: - """ - Load the state of the attribute to the client. - - Args: - snapshot (dict[str, Any]): Snapshot containing the state of the attribute. - name (str): Name of the attribute. - expected_type (type[T]): Expected type of the attribute. - """ - attribute = self.dict_wrap_attr(name, expected_type) - self.load_attribute(snapshot[name], attribute) - if list(attribute.keys()) == ["None"]: - setattr(self.client, name, attribute["None"]) - else: - setattr(self.client, name, attribute) @abstractmethod def save_attribute(self, attribute: dict[str, T]) -> dict[str, Any]: """ - Abstract method to save the state of the attribute. This method should be implemented based on the type of + Abstract method used to save the state of the attribute. This method should be implemented based on the type of the attribute and the way it should be saved. Args: @@ -108,7 +47,7 @@ class OptimizerSnapshotter(AbstractSnapshotter[Optimizer]): def save_attribute(self, attribute: dict[str, Optimizer]) -> dict[str, Any]: """ - Save the state of the optimizers by saving "state" attribute of the optimizer. + Save the state of the optimizers by saving "state" attribute of the optimizers. Args: attribute (dict[str, Optimizer]): The optimizers to be saved. @@ -123,7 +62,7 @@ def save_attribute(self, attribute: dict[str, Optimizer]) -> dict[str, Any]: def load_attribute(self, attribute_snapshot: dict[str, Any], attribute: dict[str, Optimizer]) -> None: """ - Load the state of the optimizers by loading "state" attribute of the optimizer + Load the state of the optimizers by loading "state" attribute of the optimizers. Args: attribute_snapshot (dict[str, Any]): The snapshot containing the state of the optimizers. @@ -220,26 +159,128 @@ def load_attribute( attribute[key] = attribute_snapshot[key] -class NumberSnapshotter(AbstractSnapshotter[int | float]): - def save_attribute(self, attribute: dict[str, int | float]) -> dict[str, Any]: +class SingletonSnapshotter(AbstractSnapshotter[int | float | bool]): + def save_attribute(self, attribute: dict[str, int | float | bool]) -> dict[str, Any]: + """ + Save the state of a singleton which could be a number or a boolean (either single or dictionary of them). + + Args: + attribute (dict[str, int | float | bool]): The singleton to be saved. + + Returns: + dict[str, Any]: A dictionary containing the state of the singletons. + """ + return attribute + + def load_attribute(self, attribute_snapshot: dict[str, Any], attribute: dict[str, int | float | bool]) -> None: + """ + Load the state of the singleton (either single or dictionary of them). + + Args: + attribute_snapshot (dict[str, Any]): The snapshot containing the state of the singleton. + attribute (dict[str, int | float | bool]): The singletons to be loaded + """ + for key in attribute: + attribute[key] = attribute_snapshot[key] + + +class HistorySnapshotter(AbstractSnapshotter[History]): + def save_attribute(self, attribute: dict[str, History]) -> dict[str, Any]: + """ + Save the state of the history objects (either single or dictionary of them). + + Args: + attribute (dict[str, History]): The history to be saved. + + Returns: + dict[str, Any]: A dictionary containing the state of the history. + """ + return attribute + + def load_attribute(self, attribute_snapshot: dict[str, Any], attribute: dict[str, History]) -> None: + """ + Load the state of the history (either single or dictionary of them). + + Args: + attribute_snapshot (dict[str, Any]): The snapshot containing the state of the history. + attribute (dict[str, History]): The history to be loaded + """ + for key in attribute: + attribute[key] = attribute_snapshot[key] + + +class StringSnapshotter(AbstractSnapshotter[str]): + def save_attribute(self, attribute: dict[str, str]) -> dict[str, Any]: + """ + Save the state of the strings (either single or dictionary of them). + + Args: + attribute (dict[str, str]): The string to be saved. + + Returns: + dict[str, Any]: A dictionary containing the state of the strings. + """ + return attribute + + def load_attribute(self, attribute_snapshot: dict[str, Any], attribute: dict[str, str]) -> None: + """ + Load the state of the strings (either single or dictionary of them). + + Args: + attribute_snapshot (dict[str, Any]): The snapshot containing the state of the strings. + attribute (dict[str, str]): The strings to be loaded + """ + for key in attribute: + attribute[key] = attribute_snapshot[key] + + +class BytesSnapshotter(AbstractSnapshotter[bytes]): + + def save_attribute(self, attribute: dict[str, bytes]) -> dict[str, Any]: + """ + Save the state of the bytes (either single or dictionary of them). + + Args: + attribute (dict[str, str]): The string to be saved. + + Returns: + dict[str, Any]: A dictionary containing the state of the bytes. + """ + return attribute + + def load_attribute(self, attribute_snapshot: dict[str, Any], attribute: dict[str, bytes]) -> None: + """ + Load the state of the bytes (either single or dictionary of them). + + Args: + attribute_snapshot (dict[str, Any]): The snapshot containing the state of the bytes. + attribute (dict[str, str]): The bytes to be loaded + """ + for key in attribute: + attribute[key] = attribute_snapshot[key] + + +class EnumSnapshotter(AbstractSnapshotter[Enum]): + + def save_attribute(self, attribute: dict[str, Enum]) -> dict[str, Any]: """ - Save the state of the numbers (either single or dictionary of them). + Save the state of the Enum (either single or dictionary of them). Args: - attribute (dict[str, int | float]): The numbers to be saved. + attribute (dict[str, Enum]): The enum to be saved. Returns: - dict[str, Any]: A dictionary containing the state of the numbers. + dict[str, Any]: A dictionary containing the state of the enum. """ return attribute - def load_attribute(self, attribute_snapshot: dict[str, Any], attribute: dict[str, int | float]) -> None: + def load_attribute(self, attribute_snapshot: dict[str, Any], attribute: dict[str, Enum]) -> None: """ - Load the state of the numbers (either single or dictionary of them). + Load the state of the num (either single or dictionary of them). Args: - attribute_snapshot (dict[str, Any]): The snapshot containing the state of the numbers. - attribute (dict[str, int | float]): The numbers to be loaded + attribute_snapshot (dict[str, Any]): The snapshot containing the state of the enum. + attribute (dict[str, Enum]): The enum to be loaded """ for key in attribute: attribute[key] = attribute_snapshot[key] diff --git a/poetry.lock b/poetry.lock index 8a0c83941..e5fc78ba0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -13,17 +13,17 @@ files = [ [[package]] name = "accelerate" -version = "1.7.0" +version = "1.8.0" description = "Accelerate" optional = false python-versions = ">=3.9.0" files = [ - {file = "accelerate-1.7.0-py3-none-any.whl", hash = "sha256:cf57165cca28769c6cf2650812371c81b18e05743dfa3c748524b1bb4f2b272f"}, - {file = "accelerate-1.7.0.tar.gz", hash = "sha256:e8a2a5503d6237b9eee73cc8d36cf543f9c2d8dd2c6713450b322f5e6d53a610"}, + {file = "accelerate-1.8.0-py3-none-any.whl", hash = "sha256:4c3460a8052e122fb124d92ec15618229951febbef1d7336a4c35371f78b72a1"}, + {file = "accelerate-1.8.0.tar.gz", hash = "sha256:8b56553d570f4787fd34a33dc4613f5444fe1ba3d0e81fedb9ce27bd81e91423"}, ] [package.dependencies] -huggingface-hub = ">=0.21.0" +huggingface_hub = ">=0.21.0" numpy = ">=1.17,<3.0.0" packaging = ">=20.0" psutil = "*" @@ -40,7 +40,7 @@ sagemaker = ["sagemaker"] test-dev = ["bitsandbytes", "datasets", "diffusers", "evaluate", "scikit-learn", "scipy", "timm", "torchdata (>=0.8.0)", "torchpippy (>=0.2.0)", "tqdm", "transformers"] test-fp8 = ["torchao"] test-prod = ["parameterized", "pytest (>=7.2.0,<=8.0.0)", "pytest-order", "pytest-subtests", "pytest-xdist"] -test-trackers = ["comet-ml", "dvclive", "matplotlib", "mlflow", "tensorboard", "wandb"] +test-trackers = ["comet-ml", "dvclive", "matplotlib", "mlflow", "swanlab", "tensorboard", "wandb"] testing = ["bitsandbytes", "datasets", "diffusers", "evaluate", "parameterized", "pytest (>=7.2.0,<=8.0.0)", "pytest-order", "pytest-subtests", "pytest-xdist", "scikit-learn", "scipy", "timm", "torchdata (>=0.8.0)", "torchpippy (>=0.2.0)", "tqdm", "transformers"] [[package]] @@ -74,97 +74,97 @@ files = [ [[package]] name = "aiohttp" -version = "3.12.12" +version = "3.12.13" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" files = [ - {file = "aiohttp-3.12.12-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6f25e9d274d6abbb15254f76f100c3984d6b9ad6e66263cc60a465dd5c7e48f5"}, - {file = "aiohttp-3.12.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b8ec3c1a1c13d24941b5b913607e57b9364e4c0ea69d5363181467492c4b2ba6"}, - {file = "aiohttp-3.12.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81ef2f9253c327c211cb7b06ea2edd90e637cf21c347b894d540466b8d304e08"}, - {file = "aiohttp-3.12.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28ded835c3663fd41c9ad44685811b11e34e6ac9a7516a30bfce13f6abba4496"}, - {file = "aiohttp-3.12.12-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a4b78ccf254fc10605b263996949a94ca3f50e4f9100e05137d6583e266b711e"}, - {file = "aiohttp-3.12.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f4a5af90d5232c41bb857568fe7d11ed84408653ec9da1ff999cc30258b9bd1"}, - {file = "aiohttp-3.12.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffa5205c2f53f1120e93fdf2eca41b0f6344db131bc421246ee82c1e1038a14a"}, - {file = "aiohttp-3.12.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f68301660f0d7a3eddfb84f959f78a8f9db98c76a49b5235508fa16edaad0f7c"}, - {file = "aiohttp-3.12.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db874d3b0c92fdbb553751af9d2733b378c25cc83cd9dfba87f12fafd2dc9cd5"}, - {file = "aiohttp-3.12.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5e53cf9c201b45838a2d07b1f2d5f7fec9666db7979240002ce64f9b8a1e0cf2"}, - {file = "aiohttp-3.12.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:8687cc5f32b4e328c233acd387d09a1b477007896b2f03c1c823a0fd05f63883"}, - {file = "aiohttp-3.12.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5ee537ad29de716a3d8dc46c609908de0c25ffeebf93cd94a03d64cdc07d66d0"}, - {file = "aiohttp-3.12.12-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:411f821be5af6af11dc5bed6c6c1dc6b6b25b91737d968ec2756f9baa75e5f9b"}, - {file = "aiohttp-3.12.12-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f90319d94cf5f9786773237f24bd235a7b5959089f1af8ec1154580a3434b503"}, - {file = "aiohttp-3.12.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73b148e606f34e9d513c451fd65efe1091772659ca5703338a396a99f60108ff"}, - {file = "aiohttp-3.12.12-cp310-cp310-win32.whl", hash = "sha256:d40e7bfd577fdc8a92b72f35dfbdd3ec90f1bc8a72a42037fefe34d4eca2d4a1"}, - {file = "aiohttp-3.12.12-cp310-cp310-win_amd64.whl", hash = "sha256:65c7804a2343893d6dea9fce69811aea0a9ac47f68312cf2e3ee1668cd9a387f"}, - {file = "aiohttp-3.12.12-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:38823fe0d8bc059b3eaedb263fe427d887c7032e72b4ef92c472953285f0e658"}, - {file = "aiohttp-3.12.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:10237f2c34711215d04ed21da63852ce023608299554080a45c576215d9df81c"}, - {file = "aiohttp-3.12.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:563ec477c0dc6d56fc7f943a3475b5acdb399c7686c30f5a98ada24bb7562c7a"}, - {file = "aiohttp-3.12.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3d05c46a61aca7c47df74afff818bc06a251ab95d95ff80b53665edfe1e0bdf"}, - {file = "aiohttp-3.12.12-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:277c882916759b4a6b6dc7e2ceb124aad071b3c6456487808d9ab13e1b448d57"}, - {file = "aiohttp-3.12.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:216abf74b324b0f4e67041dd4fb2819613909a825904f8a51701fbcd40c09cd7"}, - {file = "aiohttp-3.12.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65d6cefad286459b68e7f867b9586a821fb7f121057b88f02f536ef570992329"}, - {file = "aiohttp-3.12.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:feaaaff61966b5f4b4eae0b79fc79427f49484e4cfa5ab7d138ecd933ab540a8"}, - {file = "aiohttp-3.12.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a05917780b7cad1755784b16cfaad806bc16029a93d15f063ca60185b7d9ba05"}, - {file = "aiohttp-3.12.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:082c5ec6d262c1b2ee01c63f4fb9152c17f11692bf16f0f100ad94a7a287d456"}, - {file = "aiohttp-3.12.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:b265a3a8b379b38696ac78bdef943bdc4f4a5d6bed1a3fb5c75c6bab1ecea422"}, - {file = "aiohttp-3.12.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2e0f2e208914ecbc4b2a3b7b4daa759d0c587d9a0b451bb0835ac47fae7fa735"}, - {file = "aiohttp-3.12.12-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9923b025845b72f64d167bca221113377c8ffabd0a351dc18fb839d401ee8e22"}, - {file = "aiohttp-3.12.12-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1ebb213445900527831fecc70e185bf142fdfe5f2a691075f22d63c65ee3c35a"}, - {file = "aiohttp-3.12.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6fc369fb273a8328077d37798b77c1e65676709af5c182cb74bd169ca9defe81"}, - {file = "aiohttp-3.12.12-cp311-cp311-win32.whl", hash = "sha256:58ecd10fda6a44c311cd3742cfd2aea8c4c600338e9f27cb37434d9f5ca9ddaa"}, - {file = "aiohttp-3.12.12-cp311-cp311-win_amd64.whl", hash = "sha256:b0066e88f30be00badffb5ef8f2281532b9a9020863d873ae15f7c147770b6ec"}, - {file = "aiohttp-3.12.12-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:98451ce9ce229d092f278a74a7c2a06b3aa72984673c87796126d7ccade893e9"}, - {file = "aiohttp-3.12.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:adbac7286d89245e1aff42e948503fdc6edf6d5d65c8e305a67c40f6a8fb95f4"}, - {file = "aiohttp-3.12.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0728882115bfa85cbd8d0f664c8ccc0cfd5bd3789dd837596785450ae52fac31"}, - {file = "aiohttp-3.12.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf3b9d9e767f9d0e09fb1a31516410fc741a62cc08754578c40abc497d09540"}, - {file = "aiohttp-3.12.12-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c944860e86b9f77a462321a440ccf6fa10f5719bb9d026f6b0b11307b1c96c7b"}, - {file = "aiohttp-3.12.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b1979e1f0c98c06fd0cd940988833b102fa3aa56751f6c40ffe85cabc51f6fd"}, - {file = "aiohttp-3.12.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:120b7dd084e96cfdad85acea2ce1e7708c70a26db913eabb8d7b417c728f5d84"}, - {file = "aiohttp-3.12.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e58f5ae79649ffa247081c2e8c85e31d29623cf2a3137dda985ae05c9478aae"}, - {file = "aiohttp-3.12.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aa5f049e3e2745b0141f13e5a64e7c48b1a1427ed18bbb7957b348f282fee56"}, - {file = "aiohttp-3.12.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7163cc9cf3722d90f1822f8a38b211e3ae2fc651c63bb55449f03dc1b3ff1d44"}, - {file = "aiohttp-3.12.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ef97c4d035b721de6607f3980fa3e4ef0ec3aca76474b5789b7fac286a8c4e23"}, - {file = "aiohttp-3.12.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1c14448d6a86acadc3f7b2f4cc385d1fb390acb6f37dce27f86fe629410d92e3"}, - {file = "aiohttp-3.12.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a1b6df6255cfc493454c79221183d64007dd5080bcda100db29b7ff181b8832c"}, - {file = "aiohttp-3.12.12-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:60fc7338dfb0626c2927bfbac4785de3ea2e2bbe3d328ba5f3ece123edda4977"}, - {file = "aiohttp-3.12.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2afc72207ef4c9d4ca9fcd00689a6a37ef2d625600c3d757b5c2b80c9d0cf9a"}, - {file = "aiohttp-3.12.12-cp312-cp312-win32.whl", hash = "sha256:8098a48f93b2cbcdb5778e7c9a0e0375363e40ad692348e6e65c3b70d593b27c"}, - {file = "aiohttp-3.12.12-cp312-cp312-win_amd64.whl", hash = "sha256:d1c1879b2e0fc337d7a1b63fe950553c2b9e93c071cf95928aeea1902d441403"}, - {file = "aiohttp-3.12.12-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ea5d604318234427929d486954e3199aded65f41593ac57aa0241ab93dda3d15"}, - {file = "aiohttp-3.12.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e03ff38250b8b572dce6fcd7b6fb6ee398bb8a59e6aa199009c5322d721df4fc"}, - {file = "aiohttp-3.12.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:71125b1fc2b6a94bccc63bbece620906a4dead336d2051f8af9cbf04480bc5af"}, - {file = "aiohttp-3.12.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:784a66f9f853a22c6b8c2bd0ff157f9b879700f468d6d72cfa99167df08c5c9c"}, - {file = "aiohttp-3.12.12-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a5be0b58670b54301404bd1840e4902570a1c3be00358e2700919cb1ea73c438"}, - {file = "aiohttp-3.12.12-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8f13566fc7bf5a728275b434bc3bdea87a7ed3ad5f734102b02ca59d9b510f"}, - {file = "aiohttp-3.12.12-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d736e57d1901683bc9be648aa308cb73e646252c74b4c639c35dcd401ed385ea"}, - {file = "aiohttp-3.12.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2007eaa7aae9102f211c519d1ec196bd3cecb1944a095db19eeaf132b798738"}, - {file = "aiohttp-3.12.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a813e61583cab6d5cdbaa34bc28863acdb92f9f46e11de1b3b9251a1e8238f6"}, - {file = "aiohttp-3.12.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e408293aa910b0aea48b86a28eace41d497a85ba16c20f619f0c604597ef996c"}, - {file = "aiohttp-3.12.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:f3d31faf290f5a30acba46b388465b67c6dbe8655d183e9efe2f6a1d594e6d9d"}, - {file = "aiohttp-3.12.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0b84731697325b023902aa643bd1726d999f5bc7854bc28b17ff410a81151d4b"}, - {file = "aiohttp-3.12.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a324c6852b6e327811748446e56cc9bb6eaa58710557922183175816e82a4234"}, - {file = "aiohttp-3.12.12-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:22fd867fbd72612dcf670c90486dbcbaf702cb807fb0b42bc0b7a142a573574a"}, - {file = "aiohttp-3.12.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e092f1a970223794a4bf620a26c0e4e4e8e36bccae9b0b5da35e6d8ee598a03"}, - {file = "aiohttp-3.12.12-cp313-cp313-win32.whl", hash = "sha256:7f5f5eb8717ef8ba15ab35fcde5a70ad28bbdc34157595d1cddd888a985f5aae"}, - {file = "aiohttp-3.12.12-cp313-cp313-win_amd64.whl", hash = "sha256:ace2499bdd03c329c054dc4b47361f2b19d5aa470f7db5c7e0e989336761b33c"}, - {file = "aiohttp-3.12.12-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0d0b1c27c05a7d39a50e946ec5f94c3af4ffadd33fa5f20705df42fb0a72ca14"}, - {file = "aiohttp-3.12.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e5928847e6f7b7434921fbabf73fa5609d1f2bf4c25d9d4522b1fcc3b51995cb"}, - {file = "aiohttp-3.12.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7678147c3c85a7ae61559b06411346272ed40a08f54bc05357079a63127c9718"}, - {file = "aiohttp-3.12.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f50057f36f2a1d8e750b273bb966bec9f69ee1e0a20725ae081610501f25d555"}, - {file = "aiohttp-3.12.12-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e834f0f11ff5805d11f0f22b627c75eadfaf91377b457875e4e3affd0b924f"}, - {file = "aiohttp-3.12.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f94b2e2dea19d09745ef02ed483192260750f18731876a5c76f1c254b841443a"}, - {file = "aiohttp-3.12.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b434bfb49564dc1c318989a0ab1d3000d23e5cfd00d8295dc9d5a44324cdd42d"}, - {file = "aiohttp-3.12.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ed76bc80177ddb7c5c93e1a6440b115ed2c92a3063420ac55206fd0832a6459"}, - {file = "aiohttp-3.12.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1282a9acd378f2aed8dc79c01e702b1d5fd260ad083926a88ec7e987c4e0ade"}, - {file = "aiohttp-3.12.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:09a213c13fba321586edab1528b530799645b82bd64d79b779eb8d47ceea155a"}, - {file = "aiohttp-3.12.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:72eae16a9233561d315e72ae78ed9fc65ab3db0196e56cb2d329c755d694f137"}, - {file = "aiohttp-3.12.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f25990c507dbbeefd5a6a17df32a4ace634f7b20a38211d1b9609410c7f67a24"}, - {file = "aiohttp-3.12.12-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:3a2aa255417c8ccf1b39359cd0a3d63ae3b5ced83958dbebc4d9113327c0536a"}, - {file = "aiohttp-3.12.12-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4c53b89b3f838e9c25f943d1257efff10b348cb56895f408ddbcb0ec953a2ad"}, - {file = "aiohttp-3.12.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b5a49c2dcb32114455ad503e8354624d85ab311cbe032da03965882492a9cb98"}, - {file = "aiohttp-3.12.12-cp39-cp39-win32.whl", hash = "sha256:74fddc0ba8cea6b9c5bd732eb9d97853543586596b86391f8de5d4f6c2a0e068"}, - {file = "aiohttp-3.12.12-cp39-cp39-win_amd64.whl", hash = "sha256:ddf40ba4a1d0b4d232dc47d2b98ae7e937dcbc40bb5f2746bce0af490a64526f"}, - {file = "aiohttp-3.12.12.tar.gz", hash = "sha256:05875595d2483d96cb61fa9f64e75262d7ac6251a7e3c811d8e26f7d721760bd"}, + {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5421af8f22a98f640261ee48aae3a37f0c41371e99412d55eaf2f8a46d5dad29"}, + {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fcda86f6cb318ba36ed8f1396a6a4a3fd8f856f84d426584392083d10da4de0"}, + {file = "aiohttp-3.12.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cd71c9fb92aceb5a23c4c39d8ecc80389c178eba9feab77f19274843eb9412d"}, + {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34ebf1aca12845066c963016655dac897651e1544f22a34c9b461ac3b4b1d3aa"}, + {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:893a4639694c5b7edd4bdd8141be296042b6806e27cc1d794e585c43010cc294"}, + {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:663d8ee3ffb3494502ebcccb49078faddbb84c1d870f9c1dd5a29e85d1f747ce"}, + {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0f8f6a85a0006ae2709aa4ce05749ba2cdcb4b43d6c21a16c8517c16593aabe"}, + {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1582745eb63df267c92d8b61ca655a0ce62105ef62542c00a74590f306be8cb5"}, + {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d59227776ee2aa64226f7e086638baa645f4b044f2947dbf85c76ab11dcba073"}, + {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06b07c418bde1c8e737d8fa67741072bd3f5b0fb66cf8c0655172188c17e5fa6"}, + {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9445c1842680efac0f81d272fd8db7163acfcc2b1436e3f420f4c9a9c5a50795"}, + {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:09c4767af0b0b98c724f5d47f2bf33395c8986995b0a9dab0575ca81a554a8c0"}, + {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f3854fbde7a465318ad8d3fc5bef8f059e6d0a87e71a0d3360bb56c0bf87b18a"}, + {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2332b4c361c05ecd381edb99e2a33733f3db906739a83a483974b3df70a51b40"}, + {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1561db63fa1b658cd94325d303933553ea7d89ae09ff21cc3bcd41b8521fbbb6"}, + {file = "aiohttp-3.12.13-cp310-cp310-win32.whl", hash = "sha256:a0be857f0b35177ba09d7c472825d1b711d11c6d0e8a2052804e3b93166de1ad"}, + {file = "aiohttp-3.12.13-cp310-cp310-win_amd64.whl", hash = "sha256:fcc30ad4fb5cb41a33953292d45f54ef4066746d625992aeac33b8c681173178"}, + {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c229b1437aa2576b99384e4be668af1db84b31a45305d02f61f5497cfa6f60c"}, + {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04076d8c63471e51e3689c93940775dc3d12d855c0c80d18ac5a1c68f0904358"}, + {file = "aiohttp-3.12.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55683615813ce3601640cfaa1041174dc956d28ba0511c8cbd75273eb0587014"}, + {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:921bc91e602d7506d37643e77819cb0b840d4ebb5f8d6408423af3d3bf79a7b7"}, + {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e72d17fe0974ddeae8ed86db297e23dba39c7ac36d84acdbb53df2e18505a013"}, + {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0653d15587909a52e024a261943cf1c5bdc69acb71f411b0dd5966d065a51a47"}, + {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a77b48997c66722c65e157c06c74332cdf9c7ad00494b85ec43f324e5c5a9b9a"}, + {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6946bae55fd36cfb8e4092c921075cde029c71c7cb571d72f1079d1e4e013bc"}, + {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f95db8c8b219bcf294a53742c7bda49b80ceb9d577c8e7aa075612b7f39ffb7"}, + {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:03d5eb3cfb4949ab4c74822fb3326cd9655c2b9fe22e4257e2100d44215b2e2b"}, + {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6383dd0ffa15515283c26cbf41ac8e6705aab54b4cbb77bdb8935a713a89bee9"}, + {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6548a411bc8219b45ba2577716493aa63b12803d1e5dc70508c539d0db8dbf5a"}, + {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:81b0fcbfe59a4ca41dc8f635c2a4a71e63f75168cc91026c61be665945739e2d"}, + {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a83797a0174e7995e5edce9dcecc517c642eb43bc3cba296d4512edf346eee2"}, + {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5734d8469a5633a4e9ffdf9983ff7cdb512524645c7a3d4bc8a3de45b935ac3"}, + {file = "aiohttp-3.12.13-cp311-cp311-win32.whl", hash = "sha256:fef8d50dfa482925bb6b4c208b40d8e9fa54cecba923dc65b825a72eed9a5dbd"}, + {file = "aiohttp-3.12.13-cp311-cp311-win_amd64.whl", hash = "sha256:9a27da9c3b5ed9d04c36ad2df65b38a96a37e9cfba6f1381b842d05d98e6afe9"}, + {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0aa580cf80558557285b49452151b9c69f2fa3ad94c5c9e76e684719a8791b73"}, + {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b103a7e414b57e6939cc4dece8e282cfb22043efd0c7298044f6594cf83ab347"}, + {file = "aiohttp-3.12.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f64e748e9e741d2eccff9597d09fb3cd962210e5b5716047cbb646dc8fe06f"}, + {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c955989bf4c696d2ededc6b0ccb85a73623ae6e112439398935362bacfaaf6"}, + {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d640191016763fab76072c87d8854a19e8e65d7a6fcfcbf017926bdbbb30a7e5"}, + {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dc507481266b410dede95dd9f26c8d6f5a14315372cc48a6e43eac652237d9b"}, + {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a94daa873465d518db073bd95d75f14302e0208a08e8c942b2f3f1c07288a75"}, + {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f52420cde4ce0bb9425a375d95577fe082cb5721ecb61da3049b55189e4e6"}, + {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f7df1f620ec40f1a7fbcb99ea17d7326ea6996715e78f71a1c9a021e31b96b8"}, + {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3062d4ad53b36e17796dce1c0d6da0ad27a015c321e663657ba1cc7659cfc710"}, + {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:8605e22d2a86b8e51ffb5253d9045ea73683d92d47c0b1438e11a359bdb94462"}, + {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:54fbbe6beafc2820de71ece2198458a711e224e116efefa01b7969f3e2b3ddae"}, + {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:050bd277dfc3768b606fd4eae79dd58ceda67d8b0b3c565656a89ae34525d15e"}, + {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2637a60910b58f50f22379b6797466c3aa6ae28a6ab6404e09175ce4955b4e6a"}, + {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e986067357550d1aaa21cfe9897fa19e680110551518a5a7cf44e6c5638cb8b5"}, + {file = "aiohttp-3.12.13-cp312-cp312-win32.whl", hash = "sha256:ac941a80aeea2aaae2875c9500861a3ba356f9ff17b9cb2dbfb5cbf91baaf5bf"}, + {file = "aiohttp-3.12.13-cp312-cp312-win_amd64.whl", hash = "sha256:671f41e6146a749b6c81cb7fd07f5a8356d46febdaaaf07b0e774ff04830461e"}, + {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d4a18e61f271127465bdb0e8ff36e8f02ac4a32a80d8927aa52371e93cd87938"}, + {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:532542cb48691179455fab429cdb0d558b5e5290b033b87478f2aa6af5d20ace"}, + {file = "aiohttp-3.12.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d7eea18b52f23c050ae9db5d01f3d264ab08f09e7356d6f68e3f3ac2de9dfabb"}, + {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad7c8e5c25f2a26842a7c239de3f7b6bfb92304593ef997c04ac49fb703ff4d7"}, + {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6af355b483e3fe9d7336d84539fef460120c2f6e50e06c658fe2907c69262d6b"}, + {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a95cf9f097498f35c88e3609f55bb47b28a5ef67f6888f4390b3d73e2bac6177"}, + {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8ed8c38a1c584fe99a475a8f60eefc0b682ea413a84c6ce769bb19a7ff1c5ef"}, + {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0b9170d5d800126b5bc89d3053a2363406d6e327afb6afaeda2d19ee8bb103"}, + {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:372feeace612ef8eb41f05ae014a92121a512bd5067db8f25101dd88a8db11da"}, + {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a946d3702f7965d81f7af7ea8fb03bb33fe53d311df48a46eeca17e9e0beed2d"}, + {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a0c4725fae86555bbb1d4082129e21de7264f4ab14baf735278c974785cd2041"}, + {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b28ea2f708234f0a5c44eb6c7d9eb63a148ce3252ba0140d050b091b6e842d1"}, + {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d4f5becd2a5791829f79608c6f3dc745388162376f310eb9c142c985f9441cc1"}, + {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:60f2ce6b944e97649051d5f5cc0f439360690b73909230e107fd45a359d3e911"}, + {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69fc1909857401b67bf599c793f2183fbc4804717388b0b888f27f9929aa41f3"}, + {file = "aiohttp-3.12.13-cp313-cp313-win32.whl", hash = "sha256:7d7e68787a2046b0e44ba5587aa723ce05d711e3a3665b6b7545328ac8e3c0dd"}, + {file = "aiohttp-3.12.13-cp313-cp313-win_amd64.whl", hash = "sha256:5a178390ca90419bfd41419a809688c368e63c86bd725e1186dd97f6b89c2706"}, + {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:36f6c973e003dc9b0bb4e8492a643641ea8ef0e97ff7aaa5c0f53d68839357b4"}, + {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6cbfc73179bd67c229eb171e2e3745d2afd5c711ccd1e40a68b90427f282eab1"}, + {file = "aiohttp-3.12.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e8b27b2d414f7e3205aa23bb4a692e935ef877e3a71f40d1884f6e04fd7fa74"}, + {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eabded0c2b2ef56243289112c48556c395d70150ce4220d9008e6b4b3dd15690"}, + {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:003038e83f1a3ff97409999995ec02fe3008a1d675478949643281141f54751d"}, + {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b6f46613031dbc92bdcaad9c4c22c7209236ec501f9c0c5f5f0b6a689bf50f3"}, + {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c332c6bb04650d59fb94ed96491f43812549a3ba6e7a16a218e612f99f04145e"}, + {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fea41a2c931fb582cb15dc86a3037329e7b941df52b487a9f8b5aa960153cbd"}, + {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:846104f45d18fb390efd9b422b27d8f3cf8853f1218c537f36e71a385758c896"}, + {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d6c85ac7dd350f8da2520bac8205ce99df4435b399fa7f4dc4a70407073e390"}, + {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5a1ecce0ed281bec7da8550da052a6b89552db14d0a0a45554156f085a912f48"}, + {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5304d74867028cca8f64f1cc1215eb365388033c5a691ea7aa6b0dc47412f495"}, + {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:64d1f24ee95a2d1e094a4cd7a9b7d34d08db1bbcb8aa9fb717046b0a884ac294"}, + {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:119c79922a7001ca6a9e253228eb39b793ea994fd2eccb79481c64b5f9d2a055"}, + {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bb18f00396d22e2f10cd8825d671d9f9a3ba968d708a559c02a627536b36d91c"}, + {file = "aiohttp-3.12.13-cp39-cp39-win32.whl", hash = "sha256:0022de47ef63fd06b065d430ac79c6b0bd24cdae7feaf0e8c6bac23b805a23a8"}, + {file = "aiohttp-3.12.13-cp39-cp39-win_amd64.whl", hash = "sha256:29e08111ccf81b2734ae03f1ad1cb03b9615e7d8f616764f22f71209c094f122"}, + {file = "aiohttp-3.12.13.tar.gz", hash = "sha256:47e2da578528264a12e4e3dd8dd72a7289e5f812758fe086473fab037a10fcce"}, ] [package.dependencies] @@ -207,13 +207,13 @@ files = [ [[package]] name = "alembic" -version = "1.16.1" +version = "1.16.2" description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.9" files = [ - {file = "alembic-1.16.1-py3-none-any.whl", hash = "sha256:0cdd48acada30d93aa1035767d67dff25702f8de74d7c3919f2e8492c8db2e67"}, - {file = "alembic-1.16.1.tar.gz", hash = "sha256:43d37ba24b3d17bc1eb1024fe0f51cd1dc95aeb5464594a02c6bb9ca9864bfa4"}, + {file = "alembic-1.16.2-py3-none-any.whl", hash = "sha256:5f42e9bd0afdbd1d5e3ad856c01754530367debdebf21ed6894e34af52b3bb03"}, + {file = "alembic-1.16.2.tar.gz", hash = "sha256:e53c38ff88dadb92eb22f8b150708367db731d58ad7e9d417c9168ab516cbed8"}, ] [package.dependencies] @@ -861,13 +861,13 @@ files = [ [[package]] name = "certifi" -version = "2025.4.26" +version = "2025.6.15" description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"}, - {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, + {file = "certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057"}, + {file = "certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b"}, ] [[package]] @@ -1063,17 +1063,17 @@ files = [ [[package]] name = "clarabel" -version = "0.11.0" +version = "0.11.1" description = "Clarabel Conic Interior Point Solver for Rust / Python" optional = false python-versions = ">=3.9" files = [ - {file = "clarabel-0.11.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e7cdc7ff5bebf04999fb990b740b7a9ec6e266382af406c8aa75aa3031ed219a"}, - {file = "clarabel-0.11.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:85bb474ba3c590fac47985d3dd19519ea247b77b62f1e1afb93c728799a594a2"}, - {file = "clarabel-0.11.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:763cb448ba337e593bab1d442b671c854b85981ae551e6a64209fc864985098a"}, - {file = "clarabel-0.11.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a08bb648b2cddce8ff80acb065da54aa80a76cf90ae1f2176f15a640f042cc2c"}, - {file = "clarabel-0.11.0-cp39-abi3-win_amd64.whl", hash = "sha256:6b804f99741719531b7a8ce7e44c8162b4cac7782334ea48dad0c48d6904f7d7"}, - {file = "clarabel-0.11.0.tar.gz", hash = "sha256:7a8ee6fe74eb7e7ba457b664e312e6953dec4bd1651d4725f5b5bcff5ef4dbc1"}, + {file = "clarabel-0.11.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c39160e4222040f051f2a0598691c4f9126b4d17f5b9e7678f76c71d611e12d8"}, + {file = "clarabel-0.11.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8963687ee250d27310d139eea5a6816f9c3ae31f33691b56579ca4f0f0b64b63"}, + {file = "clarabel-0.11.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4837b9d0db01e98239f04b1e3526a6cf568529d3c19a8b3f591befdc467f9bb"}, + {file = "clarabel-0.11.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8c41aaa6f3f8c0f3bd9d86c3e568dcaee079562c075bd2ec9fb3a80287380ef"}, + {file = "clarabel-0.11.1-cp39-abi3-win_amd64.whl", hash = "sha256:557d5148a4377ae1980b65d00605ae870a8f34f95f0f6a41e04aa6d3edf67148"}, + {file = "clarabel-0.11.1.tar.gz", hash = "sha256:e7c41c47f0e59aeab99aefff9e58af4a8753ee5269bbeecbd5526fc6f41b9598"}, ] [package.dependencies] @@ -1179,53 +1179,50 @@ test = ["pytest"] [[package]] name = "connected-components-3d" -version = "3.23.0" +version = "3.24.0" description = "Connected components on discrete and continuous multilabel 3D and 2D images. Handles 26, 18, and 6 connected variants; periodic boundaries (4, 8, & 6)." optional = false -python-versions = "<4.0,>=3.8" +python-versions = "<4.0,>=3.9" files = [ - {file = "connected_components_3d-3.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2242b6e4ace3cb0622cfc9dcc8daacb7697277314bc9d83d0696f7b478580689"}, - {file = "connected_components_3d-3.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7836f6dc16ac41e17bec510803e9294ad820bbe2b098af678d4e5182b692a052"}, - {file = "connected_components_3d-3.23.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f85223aee2c81cfc88d44a36b0e203d6016578c69f817d27cc628b08abfdf1ff"}, - {file = "connected_components_3d-3.23.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:473b15cc9f41ac47f08a0b900e3560cbf00d349b8f1f72af85be7ca9d62a3fe6"}, - {file = "connected_components_3d-3.23.0-cp310-cp310-win32.whl", hash = "sha256:313307fb32063a141f96f7d2127ea6900b97cef4902d1499ead78d6cfa865eb8"}, - {file = "connected_components_3d-3.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:4c954d6c270fddaf571b26843cf0168276f45689577425dd6cf26bf7ca90ada2"}, - {file = "connected_components_3d-3.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:660f7579746708cc3da6af9287342a71bffa85f0ceb2d53d335593778147bcf9"}, - {file = "connected_components_3d-3.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6684ffa981ebc32f0f3787eec8fb16d7afd7d6549c2a0273a4a9f8e5352d2f61"}, - {file = "connected_components_3d-3.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:197495c886690ad178070c0d58eeffbfdcb622d3feaa478d7b661c9ac2cd36da"}, - {file = "connected_components_3d-3.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6397aa205e28dfd99f6bee45c3cfc13be749c14c670df420705a29baa7d70c5"}, - {file = "connected_components_3d-3.23.0-cp311-cp311-win32.whl", hash = "sha256:a886ee7e41ea6b416cfdea3193280d66e9d91ba9a1aec225530c32e65b6a7c13"}, - {file = "connected_components_3d-3.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:902137d12e8b31807159348525aad76ce9bed77edde6dd5b292c9f43ca33964d"}, - {file = "connected_components_3d-3.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:071b7f2b999ac2bcbd77ca6ce6e8d8e58731e851b6bab7f074d50040cc8c46c0"}, - {file = "connected_components_3d-3.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fd758007abf9879abe1545ab9fad9254fa590878187e948253fa730f7a2c5575"}, - {file = "connected_components_3d-3.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58f134d698da06985b90a7594af3cca27772d15ba18bc6df841dc28f58681128"}, - {file = "connected_components_3d-3.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9019fa1bac0884a93f05b30bff187876c9f8cbb0a065734987d1bb0e1a0aedfa"}, - {file = "connected_components_3d-3.23.0-cp312-cp312-win32.whl", hash = "sha256:ac086129c393d383f26d67b6e035f32343e5ff0ed7859f3a65b38badc0504747"}, - {file = "connected_components_3d-3.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:abffeb05faa31df92dc08d4a692eaa33073a04ccb8ec6b856a56a0adef6034a9"}, - {file = "connected_components_3d-3.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bce17b71efbe1ad375e0ab25e568063316a6b36b1e3aaa09df4defb1e0d01d9d"}, - {file = "connected_components_3d-3.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:650dc439273a4863e3d0a2f692e4dcf11ec49839319508dd0a0603621e0eace6"}, - {file = "connected_components_3d-3.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d0d4a12e4503876c5490cfa58b9ea44d3e1d8b4da0dc97faebd118810b9b3e2"}, - {file = "connected_components_3d-3.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2160b6d7676d01b3c0cbb756450020d94eea7f141f688e24c5d5c9d56d10c316"}, - {file = "connected_components_3d-3.23.0-cp313-cp313-win32.whl", hash = "sha256:328925cf8c1cbc558451d8dbe9e99ed03da0b94f5ff219fa41c367b979db97a3"}, - {file = "connected_components_3d-3.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:52f685c54f9f69ceae6a22b1ed421405c9063ea6b8da48d314ece666d6ac70b5"}, - {file = "connected_components_3d-3.23.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cc088ae74453ae2f5dd3049e48519f54990a0d6a5dc008edaf5c92f3300e620f"}, - {file = "connected_components_3d-3.23.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d2e477566a730a35ec178956bcd9b46329d726425799458dd3e9f99b8ef1926"}, - {file = "connected_components_3d-3.23.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:babd00050685263873e984fc9b1f2f1b4ad8fa22adfc1a1c9d74ed6a56d1ba4b"}, - {file = "connected_components_3d-3.23.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38d129d5f57333d41eda1b6900098bf2448eb0dda7dd9bea4a1a09b55da44f8f"}, - {file = "connected_components_3d-3.23.0-cp38-cp38-win32.whl", hash = "sha256:458e7903da5b7a4a531dadf9826696d15f669a45d247a03fa6e04cc8e5ac1491"}, - {file = "connected_components_3d-3.23.0-cp38-cp38-win_amd64.whl", hash = "sha256:0626ea822cb0412beac1ae802ee65d813de786f440646611a26be882748ed5c7"}, - {file = "connected_components_3d-3.23.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c1918253469ca70cb6e96212d5e1c382655825294fc429ff5a2ad63f7cc212b4"}, - {file = "connected_components_3d-3.23.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6ff7052ba4bd1032eaec12ea3f55061cc4353df535573d1776d0f71f1dadaac"}, - {file = "connected_components_3d-3.23.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3952775ee0b352302ca8418bcd203f04f86ad2648778886f407f4063b82a52b3"}, - {file = "connected_components_3d-3.23.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81bfc73b5c800e2a0733785576cec200621ae3f63803c3f5a02c2c5ed9267615"}, - {file = "connected_components_3d-3.23.0-cp39-cp39-win32.whl", hash = "sha256:3d58dba103a88d7ca162a84a7f860d2573e0ed40a9b23dd3efa5c6921d25dac9"}, - {file = "connected_components_3d-3.23.0-cp39-cp39-win_amd64.whl", hash = "sha256:8e4bd826d81604182310d2818f78b73212b1f82e32341b2b8fe28c4f9c45fdb5"}, - {file = "connected_components_3d-3.23.0.tar.gz", hash = "sha256:011c814b55d006acd14d0621f658452b6c42fc91980dcce315e1e3164dd97190"}, + {file = "connected_components_3d-3.24.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf2c00d0e27f1b6b95f9b72b2fc98732301d5160dc9ee2ea648060ff7f67ceca"}, + {file = "connected_components_3d-3.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9d90d03156adc272dbb13cbdab61bb1d5005ec1c2accaa5ece3d5eb996fd9619"}, + {file = "connected_components_3d-3.24.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4def2957701881a6663815c4d75c6678cb7dceee473162cc40f0282617270ac5"}, + {file = "connected_components_3d-3.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fd5afa0ff4d7c452ffa46095d8cd689727c35fc372581bc8ca0906e2f09b289"}, + {file = "connected_components_3d-3.24.0-cp310-cp310-win32.whl", hash = "sha256:9c044d28d3509807f81f888581d16f814a514ece2e4ed0168f26afa4a1bc9bce"}, + {file = "connected_components_3d-3.24.0-cp310-cp310-win_amd64.whl", hash = "sha256:6478ff6b09b031b42fb59863872d9fb58ebce6bddb984e78a2145e5cdcb04414"}, + {file = "connected_components_3d-3.24.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f3e68df68533439957a32f953b3f0f5586f28087320a5305c97d5121f1de82f"}, + {file = "connected_components_3d-3.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9b79517b8ad49e5433ccf9242bf9784813b132d46ea655d335037ce1e6dc940e"}, + {file = "connected_components_3d-3.24.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05d541d10ffd5d09a8b6db84b770f4d23c534516860bd162eaa3291a359d2431"}, + {file = "connected_components_3d-3.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e650e1dfb1c1a4f1c1e489708086e6a03a01adfe14d541ade1a1043ec28bf36"}, + {file = "connected_components_3d-3.24.0-cp311-cp311-win32.whl", hash = "sha256:517d0eeaa3151668ff0c26d1cb0aa5939ecf6ad541a368c4742f4e8730f601f2"}, + {file = "connected_components_3d-3.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:a83cf2be95562bdedca509a362d0cce983ca35493381e5bf47926ec4f4bd06a2"}, + {file = "connected_components_3d-3.24.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cc9e882944bdb37be44f4cd4e030b17eaf5c8b3de4b76c68fed2c57e753b9479"}, + {file = "connected_components_3d-3.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:94fce3d81b11aa050d93a52f10ec6da1d6fc70330d5046a064633c8314755108"}, + {file = "connected_components_3d-3.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4797e904ec623b6fec2c6a7dec46a4689e3ffe88bbe503803e288305610983e3"}, + {file = "connected_components_3d-3.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1d51c8492a883e93664ee476a9173ec65de16c7cd4684f44b57c817ace95a46"}, + {file = "connected_components_3d-3.24.0-cp312-cp312-win32.whl", hash = "sha256:10073997c8c37bd9b5c27776a43f047b261d6bf9df7475c403a4ca6e13cad03f"}, + {file = "connected_components_3d-3.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:96cc26066262776bbba0befb4ceee0ab38d085d0b6bcc4070325c1260694cdb9"}, + {file = "connected_components_3d-3.24.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:19d5b311072e4932c53515e82e0478648772b5d4ba9fe2d9f1bb04a1f12466d7"}, + {file = "connected_components_3d-3.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c4b5db80c07292ce19f0b227468e306401936da584e9ac82420a6459a50f4707"}, + {file = "connected_components_3d-3.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55c429038530290e7479602fb80b03cadd5a31b18c5d9d66c005eb7adba7249"}, + {file = "connected_components_3d-3.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f11295ac7b9f0c4cbbd79009786b75590ee45f1673edf97f1f54c188c6b1833"}, + {file = "connected_components_3d-3.24.0-cp313-cp313-win32.whl", hash = "sha256:accefec81c645ae73d5e596f560a8d1ebd7eae52db242cc5cece64c382f70402"}, + {file = "connected_components_3d-3.24.0-cp313-cp313-win_amd64.whl", hash = "sha256:e5b5dfed48449490691267611540a7ec1f528c1ca853542a5348728e06e5f691"}, + {file = "connected_components_3d-3.24.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:887a6648a5698c695de29a302e5dc586709a10f53ace0c9bd9237544713aa810"}, + {file = "connected_components_3d-3.24.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:289530bbcf4c9b099fe26acfdb7b25c8da180f2c1d2254f1346b853bfea18fa8"}, + {file = "connected_components_3d-3.24.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa4e0a3b92bdc733f3306f7b225f223ce791f920e94eda02a1e0c58a80670a5b"}, + {file = "connected_components_3d-3.24.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01187b4649d189d2b0cd75cce414e78f7f88566603c60f92922912d62db108f4"}, + {file = "connected_components_3d-3.24.0-cp39-cp39-win32.whl", hash = "sha256:814fe4ddf3cb09d1c8f01b99d10f5c35990d6df904f516892914746fb2424f84"}, + {file = "connected_components_3d-3.24.0-cp39-cp39-win_amd64.whl", hash = "sha256:c392284424d124ec053bf83b76232961836e629a51bffeaa6dc453845e2eb855"}, + {file = "connected_components_3d-3.24.0.tar.gz", hash = "sha256:a77efece9e042d030b984b67560ff9ebe1fa46f3d0a9a2b093a275bf0722eaa0"}, ] [package.dependencies] numpy = "*" +[package.extras] +stack = ["crackle-codec", "fastremap"] + [[package]] name = "contourpy" version = "1.3.2" @@ -1304,78 +1301,78 @@ test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist" [[package]] name = "coverage" -version = "7.8.2" +version = "7.9.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd8ec21e1443fd7a447881332f7ce9d35b8fbd2849e761bb290b584535636b0a"}, - {file = "coverage-7.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26c2396674816deaeae7ded0e2b42c26537280f8fe313335858ffff35019be"}, - {file = "coverage-7.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1aec326ed237e5880bfe69ad41616d333712c7937bcefc1343145e972938f9b3"}, - {file = "coverage-7.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e818796f71702d7a13e50c70de2a1924f729228580bcba1607cccf32eea46e6"}, - {file = "coverage-7.8.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:546e537d9e24efc765c9c891328f30f826e3e4808e31f5d0f87c4ba12bbd1622"}, - {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab9b09a2349f58e73f8ebc06fac546dd623e23b063e5398343c5270072e3201c"}, - {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd51355ab8a372d89fb0e6a31719e825cf8df8b6724bee942fb5b92c3f016ba3"}, - {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0774df1e093acb6c9e4d58bce7f86656aeed6c132a16e2337692c12786b32404"}, - {file = "coverage-7.8.2-cp310-cp310-win32.whl", hash = "sha256:00f2e2f2e37f47e5f54423aeefd6c32a7dbcedc033fcd3928a4f4948e8b96af7"}, - {file = "coverage-7.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:145b07bea229821d51811bf15eeab346c236d523838eda395ea969d120d13347"}, - {file = "coverage-7.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b99058eef42e6a8dcd135afb068b3d53aff3921ce699e127602efff9956457a9"}, - {file = "coverage-7.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5feb7f2c3e6ea94d3b877def0270dff0947b8d8c04cfa34a17be0a4dc1836879"}, - {file = "coverage-7.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:670a13249b957bb9050fab12d86acef7bf8f6a879b9d1a883799276e0d4c674a"}, - {file = "coverage-7.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bdc8bf760459a4a4187b452213e04d039990211f98644c7292adf1e471162b5"}, - {file = "coverage-7.8.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07a989c867986c2a75f158f03fdb413128aad29aca9d4dbce5fc755672d96f11"}, - {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2db10dedeb619a771ef0e2949ccba7b75e33905de959c2643a4607bef2f3fb3a"}, - {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e6ea7dba4e92926b7b5f0990634b78ea02f208d04af520c73a7c876d5a8d36cb"}, - {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ef2f22795a7aca99fc3c84393a55a53dd18ab8c93fb431004e4d8f0774150f54"}, - {file = "coverage-7.8.2-cp311-cp311-win32.whl", hash = "sha256:641988828bc18a6368fe72355df5f1703e44411adbe49bba5644b941ce6f2e3a"}, - {file = "coverage-7.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8ab4a51cb39dc1933ba627e0875046d150e88478dbe22ce145a68393e9652975"}, - {file = "coverage-7.8.2-cp311-cp311-win_arm64.whl", hash = "sha256:8966a821e2083c74d88cca5b7dcccc0a3a888a596a04c0b9668a891de3a0cc53"}, - {file = "coverage-7.8.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2f6fe3654468d061942591aef56686131335b7a8325684eda85dacdf311356c"}, - {file = "coverage-7.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76090fab50610798cc05241bf83b603477c40ee87acd358b66196ab0ca44ffa1"}, - {file = "coverage-7.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd0a0a5054be160777a7920b731a0570284db5142abaaf81bcbb282b8d99279"}, - {file = "coverage-7.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da23ce9a3d356d0affe9c7036030b5c8f14556bd970c9b224f9c8205505e3b99"}, - {file = "coverage-7.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9392773cffeb8d7e042a7b15b82a414011e9d2b5fdbbd3f7e6a6b17d5e21b20"}, - {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:876cbfd0b09ce09d81585d266c07a32657beb3eaec896f39484b631555be0fe2"}, - {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3da9b771c98977a13fbc3830f6caa85cae6c9c83911d24cb2d218e9394259c57"}, - {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a990f6510b3292686713bfef26d0049cd63b9c7bb17e0864f133cbfd2e6167f"}, - {file = "coverage-7.8.2-cp312-cp312-win32.whl", hash = "sha256:bf8111cddd0f2b54d34e96613e7fbdd59a673f0cf5574b61134ae75b6f5a33b8"}, - {file = "coverage-7.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:86a323a275e9e44cdf228af9b71c5030861d4d2610886ab920d9945672a81223"}, - {file = "coverage-7.8.2-cp312-cp312-win_arm64.whl", hash = "sha256:820157de3a589e992689ffcda8639fbabb313b323d26388d02e154164c57b07f"}, - {file = "coverage-7.8.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ea561010914ec1c26ab4188aef8b1567272ef6de096312716f90e5baa79ef8ca"}, - {file = "coverage-7.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cb86337a4fcdd0e598ff2caeb513ac604d2f3da6d53df2c8e368e07ee38e277d"}, - {file = "coverage-7.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a4636ddb666971345541b59899e969f3b301143dd86b0ddbb570bd591f1e85"}, - {file = "coverage-7.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5040536cf9b13fb033f76bcb5e1e5cb3b57c4807fef37db9e0ed129c6a094257"}, - {file = "coverage-7.8.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc67994df9bcd7e0150a47ef41278b9e0a0ea187caba72414b71dc590b99a108"}, - {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e6c86888fd076d9e0fe848af0a2142bf606044dc5ceee0aa9eddb56e26895a0"}, - {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:684ca9f58119b8e26bef860db33524ae0365601492e86ba0b71d513f525e7050"}, - {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8165584ddedb49204c4e18da083913bdf6a982bfb558632a79bdaadcdafd0d48"}, - {file = "coverage-7.8.2-cp313-cp313-win32.whl", hash = "sha256:34759ee2c65362163699cc917bdb2a54114dd06d19bab860725f94ef45a3d9b7"}, - {file = "coverage-7.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:2f9bc608fbafaee40eb60a9a53dbfb90f53cc66d3d32c2849dc27cf5638a21e3"}, - {file = "coverage-7.8.2-cp313-cp313-win_arm64.whl", hash = "sha256:9fe449ee461a3b0c7105690419d0b0aba1232f4ff6d120a9e241e58a556733f7"}, - {file = "coverage-7.8.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8369a7c8ef66bded2b6484053749ff220dbf83cba84f3398c84c51a6f748a008"}, - {file = "coverage-7.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:159b81df53a5fcbc7d45dae3adad554fdbde9829a994e15227b3f9d816d00b36"}, - {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6fcbbd35a96192d042c691c9e0c49ef54bd7ed865846a3c9d624c30bb67ce46"}, - {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05364b9cc82f138cc86128dc4e2e1251c2981a2218bfcd556fe6b0fbaa3501be"}, - {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46d532db4e5ff3979ce47d18e2fe8ecad283eeb7367726da0e5ef88e4fe64740"}, - {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4000a31c34932e7e4fa0381a3d6deb43dc0c8f458e3e7ea6502e6238e10be625"}, - {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:43ff5033d657cd51f83015c3b7a443287250dc14e69910577c3e03bd2e06f27b"}, - {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94316e13f0981cbbba132c1f9f365cac1d26716aaac130866ca812006f662199"}, - {file = "coverage-7.8.2-cp313-cp313t-win32.whl", hash = "sha256:3f5673888d3676d0a745c3d0e16da338c5eea300cb1f4ada9c872981265e76d8"}, - {file = "coverage-7.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:2c08b05ee8d7861e45dc5a2cc4195c8c66dca5ac613144eb6ebeaff2d502e73d"}, - {file = "coverage-7.8.2-cp313-cp313t-win_arm64.whl", hash = "sha256:1e1448bb72b387755e1ff3ef1268a06617afd94188164960dba8d0245a46004b"}, - {file = "coverage-7.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:496948261eaac5ac9cf43f5d0a9f6eb7a6d4cb3bedb2c5d294138142f5c18f2a"}, - {file = "coverage-7.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eacd2de0d30871eff893bab0b67840a96445edcb3c8fd915e6b11ac4b2f3fa6d"}, - {file = "coverage-7.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b039ffddc99ad65d5078ef300e0c7eed08c270dc26570440e3ef18beb816c1ca"}, - {file = "coverage-7.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e49824808d4375ede9dd84e9961a59c47f9113039f1a525e6be170aa4f5c34d"}, - {file = "coverage-7.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b069938961dfad881dc2f8d02b47645cd2f455d3809ba92a8a687bf513839787"}, - {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:de77c3ba8bb686d1c411e78ee1b97e6e0b963fb98b1637658dd9ad2c875cf9d7"}, - {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1676628065a498943bd3f64f099bb573e08cf1bc6088bbe33cf4424e0876f4b3"}, - {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8e1a26e7e50076e35f7afafde570ca2b4d7900a491174ca357d29dece5aacee7"}, - {file = "coverage-7.8.2-cp39-cp39-win32.whl", hash = "sha256:6782a12bf76fa61ad9350d5a6ef5f3f020b57f5e6305cbc663803f2ebd0f270a"}, - {file = "coverage-7.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:1efa4166ba75ccefd647f2d78b64f53f14fb82622bc94c5a5cb0a622f50f1c9e"}, - {file = "coverage-7.8.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:ec455eedf3ba0bbdf8f5a570012617eb305c63cb9f03428d39bf544cb2b94837"}, - {file = "coverage-7.8.2-py3-none-any.whl", hash = "sha256:726f32ee3713f7359696331a18daf0c3b3a70bb0ae71141b9d3c52be7c595e32"}, - {file = "coverage-7.8.2.tar.gz", hash = "sha256:a886d531373a1f6ff9fad2a2ba4a045b68467b779ae729ee0b3b10ac20033b27"}, + {file = "coverage-7.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cc94d7c5e8423920787c33d811c0be67b7be83c705f001f7180c7b186dcf10ca"}, + {file = "coverage-7.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16aa0830d0c08a2c40c264cef801db8bc4fc0e1892782e45bcacbd5889270509"}, + {file = "coverage-7.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf95981b126f23db63e9dbe4cf65bd71f9a6305696fa5e2262693bc4e2183f5b"}, + {file = "coverage-7.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f05031cf21699785cd47cb7485f67df619e7bcdae38e0fde40d23d3d0210d3c3"}, + {file = "coverage-7.9.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb4fbcab8764dc072cb651a4bcda4d11fb5658a1d8d68842a862a6610bd8cfa3"}, + {file = "coverage-7.9.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0f16649a7330ec307942ed27d06ee7e7a38417144620bb3d6e9a18ded8a2d3e5"}, + {file = "coverage-7.9.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cea0a27a89e6432705fffc178064503508e3c0184b4f061700e771a09de58187"}, + {file = "coverage-7.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e980b53a959fa53b6f05343afbd1e6f44a23ed6c23c4b4c56c6662bbb40c82ce"}, + {file = "coverage-7.9.1-cp310-cp310-win32.whl", hash = "sha256:70760b4c5560be6ca70d11f8988ee6542b003f982b32f83d5ac0b72476607b70"}, + {file = "coverage-7.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a66e8f628b71f78c0e0342003d53b53101ba4e00ea8dabb799d9dba0abbbcebe"}, + {file = "coverage-7.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95c765060e65c692da2d2f51a9499c5e9f5cf5453aeaf1420e3fc847cc060582"}, + {file = "coverage-7.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ba383dc6afd5ec5b7a0d0c23d38895db0e15bcba7fb0fa8901f245267ac30d86"}, + {file = "coverage-7.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37ae0383f13cbdcf1e5e7014489b0d71cc0106458878ccde52e8a12ced4298ed"}, + {file = "coverage-7.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69aa417a030bf11ec46149636314c24c8d60fadb12fc0ee8f10fda0d918c879d"}, + {file = "coverage-7.9.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a4be2a28656afe279b34d4f91c3e26eccf2f85500d4a4ff0b1f8b54bf807338"}, + {file = "coverage-7.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:382e7ddd5289f140259b610e5f5c58f713d025cb2f66d0eb17e68d0a94278875"}, + {file = "coverage-7.9.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e5532482344186c543c37bfad0ee6069e8ae4fc38d073b8bc836fc8f03c9e250"}, + {file = "coverage-7.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a39d18b3f50cc121d0ce3838d32d58bd1d15dab89c910358ebefc3665712256c"}, + {file = "coverage-7.9.1-cp311-cp311-win32.whl", hash = "sha256:dd24bd8d77c98557880def750782df77ab2b6885a18483dc8588792247174b32"}, + {file = "coverage-7.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:6b55ad10a35a21b8015eabddc9ba31eb590f54adc9cd39bcf09ff5349fd52125"}, + {file = "coverage-7.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:6ad935f0016be24c0e97fc8c40c465f9c4b85cbbe6eac48934c0dc4d2568321e"}, + {file = "coverage-7.9.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8de12b4b87c20de895f10567639c0797b621b22897b0af3ce4b4e204a743626"}, + {file = "coverage-7.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5add197315a054e92cee1b5f686a2bcba60c4c3e66ee3de77ace6c867bdee7cb"}, + {file = "coverage-7.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:600a1d4106fe66f41e5d0136dfbc68fe7200a5cbe85610ddf094f8f22e1b0300"}, + {file = "coverage-7.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a876e4c3e5a2a1715a6608906aa5a2e0475b9c0f68343c2ada98110512ab1d8"}, + {file = "coverage-7.9.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81f34346dd63010453922c8e628a52ea2d2ccd73cb2487f7700ac531b247c8a5"}, + {file = "coverage-7.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:888f8eee13f2377ce86d44f338968eedec3291876b0b8a7289247ba52cb984cd"}, + {file = "coverage-7.9.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9969ef1e69b8c8e1e70d591f91bbc37fc9a3621e447525d1602801a24ceda898"}, + {file = "coverage-7.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:60c458224331ee3f1a5b472773e4a085cc27a86a0b48205409d364272d67140d"}, + {file = "coverage-7.9.1-cp312-cp312-win32.whl", hash = "sha256:5f646a99a8c2b3ff4c6a6e081f78fad0dde275cd59f8f49dc4eab2e394332e74"}, + {file = "coverage-7.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:30f445f85c353090b83e552dcbbdad3ec84c7967e108c3ae54556ca69955563e"}, + {file = "coverage-7.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:af41da5dca398d3474129c58cb2b106a5d93bbb196be0d307ac82311ca234342"}, + {file = "coverage-7.9.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:31324f18d5969feef7344a932c32428a2d1a3e50b15a6404e97cba1cc9b2c631"}, + {file = "coverage-7.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0c804506d624e8a20fb3108764c52e0eef664e29d21692afa375e0dd98dc384f"}, + {file = "coverage-7.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef64c27bc40189f36fcc50c3fb8f16ccda73b6a0b80d9bd6e6ce4cffcd810bbd"}, + {file = "coverage-7.9.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4fe2348cc6ec372e25adec0219ee2334a68d2f5222e0cba9c0d613394e12d86"}, + {file = "coverage-7.9.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34ed2186fe52fcc24d4561041979a0dec69adae7bce2ae8d1c49eace13e55c43"}, + {file = "coverage-7.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:25308bd3d00d5eedd5ae7d4357161f4df743e3c0240fa773ee1b0f75e6c7c0f1"}, + {file = "coverage-7.9.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73e9439310f65d55a5a1e0564b48e34f5369bee943d72c88378f2d576f5a5751"}, + {file = "coverage-7.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:37ab6be0859141b53aa89412a82454b482c81cf750de4f29223d52268a86de67"}, + {file = "coverage-7.9.1-cp313-cp313-win32.whl", hash = "sha256:64bdd969456e2d02a8b08aa047a92d269c7ac1f47e0c977675d550c9a0863643"}, + {file = "coverage-7.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:be9e3f68ca9edb897c2184ad0eee815c635565dbe7a0e7e814dc1f7cbab92c0a"}, + {file = "coverage-7.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:1c503289ffef1d5105d91bbb4d62cbe4b14bec4d13ca225f9c73cde9bb46207d"}, + {file = "coverage-7.9.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0b3496922cb5f4215bf5caaef4cf12364a26b0be82e9ed6d050f3352cf2d7ef0"}, + {file = "coverage-7.9.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9565c3ab1c93310569ec0d86b017f128f027cab0b622b7af288696d7ed43a16d"}, + {file = "coverage-7.9.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2241ad5dbf79ae1d9c08fe52b36d03ca122fb9ac6bca0f34439e99f8327ac89f"}, + {file = "coverage-7.9.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bb5838701ca68b10ebc0937dbd0eb81974bac54447c55cd58dea5bca8451029"}, + {file = "coverage-7.9.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b30a25f814591a8c0c5372c11ac8967f669b97444c47fd794926e175c4047ece"}, + {file = "coverage-7.9.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2d04b16a6062516df97969f1ae7efd0de9c31eb6ebdceaa0d213b21c0ca1a683"}, + {file = "coverage-7.9.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7931b9e249edefb07cd6ae10c702788546341d5fe44db5b6108a25da4dca513f"}, + {file = "coverage-7.9.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52e92b01041151bf607ee858e5a56c62d4b70f4dac85b8c8cb7fb8a351ab2c10"}, + {file = "coverage-7.9.1-cp313-cp313t-win32.whl", hash = "sha256:684e2110ed84fd1ca5f40e89aa44adf1729dc85444004111aa01866507adf363"}, + {file = "coverage-7.9.1-cp313-cp313t-win_amd64.whl", hash = "sha256:437c576979e4db840539674e68c84b3cda82bc824dd138d56bead1435f1cb5d7"}, + {file = "coverage-7.9.1-cp313-cp313t-win_arm64.whl", hash = "sha256:18a0912944d70aaf5f399e350445738a1a20b50fbea788f640751c2ed9208b6c"}, + {file = "coverage-7.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f424507f57878e424d9a95dc4ead3fbdd72fd201e404e861e465f28ea469951"}, + {file = "coverage-7.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:535fde4001b2783ac80865d90e7cc7798b6b126f4cd8a8c54acfe76804e54e58"}, + {file = "coverage-7.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02532fd3290bb8fa6bec876520842428e2a6ed6c27014eca81b031c2d30e3f71"}, + {file = "coverage-7.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56f5eb308b17bca3bbff810f55ee26d51926d9f89ba92707ee41d3c061257e55"}, + {file = "coverage-7.9.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfa447506c1a52271f1b0de3f42ea0fa14676052549095e378d5bff1c505ff7b"}, + {file = "coverage-7.9.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9ca8e220006966b4a7b68e8984a6aee645a0384b0769e829ba60281fe61ec4f7"}, + {file = "coverage-7.9.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:49f1d0788ba5b7ba65933f3a18864117c6506619f5ca80326b478f72acf3f385"}, + {file = "coverage-7.9.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:68cd53aec6f45b8e4724c0950ce86eacb775c6be01ce6e3669fe4f3a21e768ed"}, + {file = "coverage-7.9.1-cp39-cp39-win32.whl", hash = "sha256:95335095b6c7b1cc14c3f3f17d5452ce677e8490d101698562b2ffcacc304c8d"}, + {file = "coverage-7.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:e1b5191d1648acc439b24721caab2fd0c86679d8549ed2c84d5a7ec1bedcc244"}, + {file = "coverage-7.9.1-pp39.pp310.pp311-none-any.whl", hash = "sha256:db0f04118d1db74db6c9e1cb1898532c7dcc220f1d2718f058601f7c3f499514"}, + {file = "coverage-7.9.1-py3-none-any.whl", hash = "sha256:66b974b145aa189516b6bf2d8423e888b742517d37872f6ee4c5be0073bd9a3c"}, + {file = "coverage-7.9.1.tar.gz", hash = "sha256:6cf43c78c4282708a28e466316935ec7489a9c487518a77fa68f716c67909cec"}, ] [package.dependencies] @@ -1456,37 +1453,37 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "cvxpy" -version = "1.6.5" +version = "1.6.6" description = "A domain-specific language for modeling convex optimization problems in Python." optional = false python-versions = ">=3.9" files = [ - {file = "cvxpy-1.6.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:26ac571ffed3f98ad59c2ddfb88eaad7280003faa96b5f509c353e46fb79e787"}, - {file = "cvxpy-1.6.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b35a3b5c6c7b43b85179ee77d895ac7a49afdccbe7ebcf60f5f74cd9186cedd8"}, - {file = "cvxpy-1.6.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:374b18b023a9266d888bf753b6f012d1e58199bb9ba7d2e71bcbbd1f2c677ddf"}, - {file = "cvxpy-1.6.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e39e83fab103c049209d93e38a439c9ce0d09b59874c1b026c82b655aad2d9"}, - {file = "cvxpy-1.6.5-cp310-cp310-win_amd64.whl", hash = "sha256:ff94cad9bcb0897bdecadd0b34836fba7d44b5c95237837132f09f979dadafe0"}, - {file = "cvxpy-1.6.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9dfc3fda013b7387e4d8820d01c81739330519231254b73dfaa79bebdd509937"}, - {file = "cvxpy-1.6.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7a7debf1bf36550c74bc6b5625592a9bec92d9f3a884b7e0a9d49f9e302ce6e4"}, - {file = "cvxpy-1.6.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93d898722b772438ae7e41b043d89d5896ff33c0ba764429c0282dc7e7db80e6"}, - {file = "cvxpy-1.6.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de121b64c4a876a0d1b005fbd68a893f0fc8640d2d02f58f2f3f0941f48a6742"}, - {file = "cvxpy-1.6.5-cp311-cp311-win_amd64.whl", hash = "sha256:9752baf15c2339c24d4d8bd974563c14f7cc8020a338d31dbcb80149fcbb7971"}, - {file = "cvxpy-1.6.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4c3365de01866f3f3a14f2c754d52f1aa361184c4f5f004b7257622b2c177237"}, - {file = "cvxpy-1.6.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9f70e39a41e1691783a4e55a73440f9e68b852fe0e0498c4d0c5a1505f3a2640"}, - {file = "cvxpy-1.6.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:579b0039fa097e2e20272028bd2d4b592de7c67e60fd8eb6991629b5d53204a2"}, - {file = "cvxpy-1.6.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a82c6de45a39065dd8c5ff5b30bee21b09ca85eac6b6dcbd3f5ed1b19986bec1"}, - {file = "cvxpy-1.6.5-cp312-cp312-win_amd64.whl", hash = "sha256:436aed23d0ca84df81944018d971cf8bda8f19bfa2362ae3c540313d5183eca6"}, - {file = "cvxpy-1.6.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c16478107e3bd8bbda85d7a2d12ae737151e44a5fc43f695a84d387311c3cce9"}, - {file = "cvxpy-1.6.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5f5ca05999a61b7eed4fc3369e19368d0241538f1890d5510c31f35f7daff021"}, - {file = "cvxpy-1.6.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13eb878e89029d00c0569c6f151df31d136959b0ac1a9f11d567e77579a9a108"}, - {file = "cvxpy-1.6.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47c0edd990a200de5115427fdc5526cf8cd462951318af55800e6d5abcf72d32"}, - {file = "cvxpy-1.6.5-cp313-cp313-win_amd64.whl", hash = "sha256:51161b0e8b0d83dc07355bab938bd0734dd5531c98dca8d6faaa8b847c651339"}, - {file = "cvxpy-1.6.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:aed6510cc589ee2836cfe53cbb323a86328101996ee7ec60f81797d07aafd528"}, - {file = "cvxpy-1.6.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45a9fa33eb26abc6f62d0458f0971ea1dcb704a2dd4143687e68916bd8506c7e"}, - {file = "cvxpy-1.6.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7240f3befd12a12b164809ca4af30de6cb3513672d52806cd9f5858177e32617"}, - {file = "cvxpy-1.6.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:245a3b84a72b0156c2dd0f0e3a3de7699a46da83d308a738c53d65a63999846c"}, - {file = "cvxpy-1.6.5-cp39-cp39-win_amd64.whl", hash = "sha256:2cfc2069b7c9e0c77f789c14500a91134b352e36671da2b1174d2ea7bdb31f73"}, - {file = "cvxpy-1.6.5.tar.gz", hash = "sha256:666081b9c1f6db8947bcfc3c6f250174f934fa1ba8e30b38e3d32eba779ff785"}, + {file = "cvxpy-1.6.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5a23a18a1b88b008996b3cb696b8b64c315c44bd875b449658784f8a0b70aa02"}, + {file = "cvxpy-1.6.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:101fb9b433cc2e334d3596ad611fcc3e2fa5d484bb0c65eda9ee2d7213de05b2"}, + {file = "cvxpy-1.6.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed5a3b2da9c47eddad2113327ac81689524ebb4ba174ea1f52117921541fd705"}, + {file = "cvxpy-1.6.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0f3c6bd27f62036b5a23e24beab0491d2e1af330bafa2872484658323ed3ece"}, + {file = "cvxpy-1.6.6-cp310-cp310-win_amd64.whl", hash = "sha256:5d2aecceeac9f5b9297c26ea0080a8de0da94f2660103c6b2c996f6900fb9dab"}, + {file = "cvxpy-1.6.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d9046f1481d0b518efe82c4c8fd29ad1e7878bd1c426a29a1d4b770e620dd12c"}, + {file = "cvxpy-1.6.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ad3abd3dabe5ae5d4d88b53d62d13d53c3515dbbd4ffb3348df91eef6b23171"}, + {file = "cvxpy-1.6.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca252631ff112684a9f06fbedeb4517148646e5a26577077130d35c69a3e8a61"}, + {file = "cvxpy-1.6.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb67053bb3ffb886af17f198fe73f9f0c963865de47ed716af8728a1a2763507"}, + {file = "cvxpy-1.6.6-cp311-cp311-win_amd64.whl", hash = "sha256:200f969a171e7b7f6d682e51c9c595e7fec7014de551355a915aad16e9a824b2"}, + {file = "cvxpy-1.6.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73b82adb9a32ac75d98b52b0520dfcea846e5d5fe7d84c539b101043d51d532b"}, + {file = "cvxpy-1.6.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b3e2b5db609434608c7f535ab483b055c3012ca02bb97b1cc76a97ef0ea9fef"}, + {file = "cvxpy-1.6.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bf2c311becaac48ea566692c53dba9bd39f0c4dd10534945191e39fd398e7ee"}, + {file = "cvxpy-1.6.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:863535ba8d89806a8cfcd5cb7939aef7ec7c45a7109780af72ceda6090410887"}, + {file = "cvxpy-1.6.6-cp312-cp312-win_amd64.whl", hash = "sha256:0bc83872e7054434c9c242a1a154daacae4c57f2818c7edfefcbf69a0f628748"}, + {file = "cvxpy-1.6.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9d7aa7ecf5409459aaa5f39ec4889a472314d5790f478c093b6ebe733b4e4bd3"}, + {file = "cvxpy-1.6.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:23eb8dad835c425630a5d6b249c8685bd5d9038ad564cf6317e9ae259046b654"}, + {file = "cvxpy-1.6.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091bf83fb4b7d58ea73380325f654239be6e7f556f2546c1cb767c41bf6815e3"}, + {file = "cvxpy-1.6.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b461bd6cb9dd975ca5bf1ac86a3602036158eca7cdff8d824e8a7c30efe43f66"}, + {file = "cvxpy-1.6.6-cp313-cp313-win_amd64.whl", hash = "sha256:7790b15365411778acbe25d23368f4e2776f28f44c7cbb6059101072e40fc028"}, + {file = "cvxpy-1.6.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:060e09cdc0cb7044ee8b4fd736168ecda86edcf64af3f39ab579f59a36336ba3"}, + {file = "cvxpy-1.6.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220711c4cef001804613eccdbe1d104ba2be705b47ba1f8dc073ebc542bc5ca5"}, + {file = "cvxpy-1.6.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d011ca756ea13632a739431a59b3a0d17e36e8dab280059763c09c3d615662f"}, + {file = "cvxpy-1.6.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:845bb27ad6d7c8069e1fa769b638a0f07dfb4cbaa2f1e92dbc3c4ef5eb6cdeab"}, + {file = "cvxpy-1.6.6-cp39-cp39-win_amd64.whl", hash = "sha256:5d8a7a567823ea43b5e24f390bb298ebd905cfb3570b4feeed0bcb80815f1400"}, + {file = "cvxpy-1.6.6.tar.gz", hash = "sha256:b424f2416b2d8935628e1291e97d532ec34ae046246fe9d2d2d69115ff1ba701"}, ] [package.dependencies] @@ -1558,13 +1555,13 @@ xml-validation = ["lxml (>=4,<5)"] [[package]] name = "databricks-sdk" -version = "0.56.0" +version = "0.57.0" description = "Databricks SDK for Python (Beta)" optional = false python-versions = ">=3.7" files = [ - {file = "databricks_sdk-0.56.0-py3-none-any.whl", hash = "sha256:0b0036a3b48b59a70f07968658379ceecd47a7cd55646d519fe1a1c3cbc60c02"}, - {file = "databricks_sdk-0.56.0.tar.gz", hash = "sha256:3d701eba806f07a0203f97634789195495a1104f5061d2d7b76dc1669fe3c20c"}, + {file = "databricks_sdk-0.57.0-py3-none-any.whl", hash = "sha256:a253bb4c7e00e43654af8b6e29ac79bee72d310e342ec73e148e4e591b75915f"}, + {file = "databricks_sdk-0.57.0.tar.gz", hash = "sha256:62c012001b6bd4d46e5a117b90e286c2c5a77e4f53f31985f253ce21ce986837"}, ] [package.dependencies] @@ -2075,13 +2072,13 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "fastapi" -version = "0.115.12" +version = "0.115.13" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d"}, - {file = "fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681"}, + {file = "fastapi-0.115.13-py3-none-any.whl", hash = "sha256:0a0cab59afa7bab22f5eb347f8c9864b681558c278395e94035a741fc10cd865"}, + {file = "fastapi-0.115.13.tar.gz", hash = "sha256:55d1d25c2e1e0a0a50aceb1c8705cd932def273c102bff0b1c1da88b3c6eb307"}, ] [package.dependencies] @@ -2259,53 +2256,53 @@ vision = ["pillow (>=6.2.1)"] [[package]] name = "fonttools" -version = "4.58.2" +version = "4.58.4" description = "Tools to manipulate font files" optional = false python-versions = ">=3.9" files = [ - {file = "fonttools-4.58.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4baaf34f07013ba9c2c3d7a95d0c391fcbb30748cb86c36c094fab8f168e49bb"}, - {file = "fonttools-4.58.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2e26e4a4920d57f04bb2c3b6e9a68b099c7ef2d70881d4fee527896fa4f7b5aa"}, - {file = "fonttools-4.58.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0bb956d9d01ea51368415515f664f58abf96557ba3c1aae4e26948ae7c86f29"}, - {file = "fonttools-4.58.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d40af8493c80ec17a1133ef429d42f1a97258dd9213b917daae9d8cafa6e0e6c"}, - {file = "fonttools-4.58.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:60b5cde1c76f6ded198da5608dddb1ee197faad7d2f0f6d3348ca0cda0c756c4"}, - {file = "fonttools-4.58.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f8df6dc80ecc9033ca25a944ee5db7564fecca28e96383043fd92d9df861a159"}, - {file = "fonttools-4.58.2-cp310-cp310-win32.whl", hash = "sha256:25728e980f5fbb67f52c5311b90fae4aaec08c3d3b78dce78ab564784df1129c"}, - {file = "fonttools-4.58.2-cp310-cp310-win_amd64.whl", hash = "sha256:d6997ee7c2909a904802faf44b0d0208797c4d751f7611836011ace165308165"}, - {file = "fonttools-4.58.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:024faaf20811296fd2f83ebdac7682276362e726ed5fea4062480dd36aff2fd9"}, - {file = "fonttools-4.58.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2faec6e7f2abd80cd9f2392dfa28c02cfd5b1125be966ea6eddd6ca684deaa40"}, - {file = "fonttools-4.58.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520792629a938c14dd7fe185794b156cfc159c609d07b31bbb5f51af8dc7918a"}, - {file = "fonttools-4.58.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12fbc6e0bf0c75ce475ef170f2c065be6abc9e06ad19a13b56b02ec2acf02427"}, - {file = "fonttools-4.58.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:44a39cf856d52109127d55576c7ec010206a8ba510161a7705021f70d1649831"}, - {file = "fonttools-4.58.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5390a67c55a835ad5a420da15b3d88b75412cbbd74450cb78c4916b0bd7f0a34"}, - {file = "fonttools-4.58.2-cp311-cp311-win32.whl", hash = "sha256:f7e10f4e7160bcf6a240d7560e9e299e8cb585baed96f6a616cef51180bf56cb"}, - {file = "fonttools-4.58.2-cp311-cp311-win_amd64.whl", hash = "sha256:29bdf52bfafdae362570d3f0d3119a3b10982e1ef8cb3a9d3ebb72da81cb8d5e"}, - {file = "fonttools-4.58.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c6eeaed9c54c1d33c1db928eb92b4e180c7cb93b50b1ee3e79b2395cb01f25e9"}, - {file = "fonttools-4.58.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbe1d9c72b7f981bed5c2a61443d5e3127c1b3aca28ca76386d1ad93268a803f"}, - {file = "fonttools-4.58.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85babe5b3ce2cbe57fc0d09c0ee92bbd4d594fd7ea46a65eb43510a74a4ce773"}, - {file = "fonttools-4.58.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:918a2854537fcdc662938057ad58b633bc9e0698f04a2f4894258213283a7932"}, - {file = "fonttools-4.58.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b379cf05bf776c336a0205632596b1c7d7ab5f7135e3935f2ca2a0596d2d092"}, - {file = "fonttools-4.58.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99ab3547a15a5d168c265e139e21756bbae1de04782ac9445c9ef61b8c0a32ce"}, - {file = "fonttools-4.58.2-cp312-cp312-win32.whl", hash = "sha256:6764e7a3188ce36eea37b477cdeca602ae62e63ae9fc768ebc176518072deb04"}, - {file = "fonttools-4.58.2-cp312-cp312-win_amd64.whl", hash = "sha256:41f02182a1d41b79bae93c1551855146868b04ec3e7f9c57d6fef41a124e6b29"}, - {file = "fonttools-4.58.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:829048ef29dbefec35d95cc6811014720371c95bdc6ceb0afd2f8e407c41697c"}, - {file = "fonttools-4.58.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:64998c5993431e45b474ed5f579f18555f45309dd1cf8008b594d2fe0a94be59"}, - {file = "fonttools-4.58.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b887a1cf9fbcb920980460ee4a489c8aba7e81341f6cdaeefa08c0ab6529591c"}, - {file = "fonttools-4.58.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27d74b9f6970cefbcda33609a3bee1618e5e57176c8b972134c4e22461b9c791"}, - {file = "fonttools-4.58.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec26784610056a770e15a60f9920cee26ae10d44d1e43271ea652dadf4e7a236"}, - {file = "fonttools-4.58.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ed0a71d57dd427c0fb89febd08cac9b925284d2a8888e982a6c04714b82698d7"}, - {file = "fonttools-4.58.2-cp313-cp313-win32.whl", hash = "sha256:994e362b01460aa863ef0cb41a29880bc1a498c546952df465deff7abf75587a"}, - {file = "fonttools-4.58.2-cp313-cp313-win_amd64.whl", hash = "sha256:f95dec862d7c395f2d4efe0535d9bdaf1e3811e51b86432fa2a77e73f8195756"}, - {file = "fonttools-4.58.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f6ca4337e37d287535fd0089b4520cedc5666023fe4176a74e3415f917b570"}, - {file = "fonttools-4.58.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b269c7a783ec3be40809dc0dc536230a3d2d2c08e3fb9538d4e0213872b1a762"}, - {file = "fonttools-4.58.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1902d9b2b84cc9485663f1a72882890cd240f4464e8443af93faa34b095a4444"}, - {file = "fonttools-4.58.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a94a00ffacbb044729c6a5b29e02bf6f0e80681e9275cd374a1d25db3061328"}, - {file = "fonttools-4.58.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:25d22628f8b6b49b78666415f7cfa60c88138c24d66f3e5818d09ca001810cc5"}, - {file = "fonttools-4.58.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4bacb925a045e964a44bdeb9790b8778ce659605c7a2a39ef4f12e06c323406b"}, - {file = "fonttools-4.58.2-cp39-cp39-win32.whl", hash = "sha256:eb4bc19a3ab45d2b4bb8f4f7c60e55bec53016e402af0b6ff4ef0c0129193671"}, - {file = "fonttools-4.58.2-cp39-cp39-win_amd64.whl", hash = "sha256:c8d16973f8ab02a5a960afe1cae4db72220ef628bf397499aba8e3caa0c10e33"}, - {file = "fonttools-4.58.2-py3-none-any.whl", hash = "sha256:84f4b0bcfa046254a65ee7117094b4907e22dc98097a220ef108030eb3c15596"}, - {file = "fonttools-4.58.2.tar.gz", hash = "sha256:4b491ddbfd50b856e84b0648b5f7941af918f6d32f938f18e62b58426a8d50e2"}, + {file = "fonttools-4.58.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:834542f13fee7625ad753b2db035edb674b07522fcbdd0ed9e9a9e2a1034467f"}, + {file = "fonttools-4.58.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2e6c61ce330142525296170cd65666e46121fc0d44383cbbcfa39cf8f58383df"}, + {file = "fonttools-4.58.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9c75f8faa29579c0fbf29b56ae6a3660c6c025f3b671803cb6a9caa7e4e3a98"}, + {file = "fonttools-4.58.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:88dedcedbd5549e35b2ea3db3de02579c27e62e51af56779c021e7b33caadd0e"}, + {file = "fonttools-4.58.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ae80a895adab43586f4da1521d58fd4f4377cef322ee0cc205abcefa3a5effc3"}, + {file = "fonttools-4.58.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0d3acc7f0d151da116e87a182aefb569cf0a3c8e0fd4c9cd0a7c1e7d3e7adb26"}, + {file = "fonttools-4.58.4-cp310-cp310-win32.whl", hash = "sha256:1244f69686008e7e8d2581d9f37eef330a73fee3843f1107993eb82c9d306577"}, + {file = "fonttools-4.58.4-cp310-cp310-win_amd64.whl", hash = "sha256:2a66c0af8a01eb2b78645af60f3b787de5fe5eb1fd8348163715b80bdbfbde1f"}, + {file = "fonttools-4.58.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3841991c9ee2dc0562eb7f23d333d34ce81e8e27c903846f0487da21e0028eb"}, + {file = "fonttools-4.58.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c98f91b6a9604e7ffb5ece6ea346fa617f967c2c0944228801246ed56084664"}, + {file = "fonttools-4.58.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab9f891eb687ddf6a4e5f82901e00f992e18012ca97ab7acd15f13632acd14c1"}, + {file = "fonttools-4.58.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:891c5771e8f0094b7c0dc90eda8fc75e72930b32581418f2c285a9feedfd9a68"}, + {file = "fonttools-4.58.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:43ba4d9646045c375d22e3473b7d82b18b31ee2ac715cd94220ffab7bc2d5c1d"}, + {file = "fonttools-4.58.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33d19f16e6d2ffd6669bda574a6589941f6c99a8d5cfb9f464038244c71555de"}, + {file = "fonttools-4.58.4-cp311-cp311-win32.whl", hash = "sha256:b59e5109b907da19dc9df1287454821a34a75f2632a491dd406e46ff432c2a24"}, + {file = "fonttools-4.58.4-cp311-cp311-win_amd64.whl", hash = "sha256:3d471a5b567a0d1648f2e148c9a8bcf00d9ac76eb89e976d9976582044cc2509"}, + {file = "fonttools-4.58.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:462211c0f37a278494e74267a994f6be9a2023d0557aaa9ecbcbfce0f403b5a6"}, + {file = "fonttools-4.58.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0c7a12fb6f769165547f00fcaa8d0df9517603ae7e04b625e5acb8639809b82d"}, + {file = "fonttools-4.58.4-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d42c63020a922154add0a326388a60a55504629edc3274bc273cd3806b4659f"}, + {file = "fonttools-4.58.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f2b4e6fd45edc6805f5f2c355590b092ffc7e10a945bd6a569fc66c1d2ae7aa"}, + {file = "fonttools-4.58.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f155b927f6efb1213a79334e4cb9904d1e18973376ffc17a0d7cd43d31981f1e"}, + {file = "fonttools-4.58.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e38f687d5de97c7fb7da3e58169fb5ba349e464e141f83c3c2e2beb91d317816"}, + {file = "fonttools-4.58.4-cp312-cp312-win32.whl", hash = "sha256:636c073b4da9db053aa683db99580cac0f7c213a953b678f69acbca3443c12cc"}, + {file = "fonttools-4.58.4-cp312-cp312-win_amd64.whl", hash = "sha256:82e8470535743409b30913ba2822e20077acf9ea70acec40b10fcf5671dceb58"}, + {file = "fonttools-4.58.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5f4a64846495c543796fa59b90b7a7a9dff6839bd852741ab35a71994d685c6d"}, + {file = "fonttools-4.58.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e80661793a5d4d7ad132a2aa1eae2e160fbdbb50831a0edf37c7c63b2ed36574"}, + {file = "fonttools-4.58.4-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fe5807fc64e4ba5130f1974c045a6e8d795f3b7fb6debfa511d1773290dbb76b"}, + {file = "fonttools-4.58.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b610b9bef841cb8f4b50472494158b1e347d15cad56eac414c722eda695a6cfd"}, + {file = "fonttools-4.58.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2daa7f0e213c38f05f054eb5e1730bd0424aebddbeac094489ea1585807dd187"}, + {file = "fonttools-4.58.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:66cccb6c0b944496b7f26450e9a66e997739c513ffaac728d24930df2fd9d35b"}, + {file = "fonttools-4.58.4-cp313-cp313-win32.whl", hash = "sha256:94d2aebb5ca59a5107825520fde596e344652c1f18170ef01dacbe48fa60c889"}, + {file = "fonttools-4.58.4-cp313-cp313-win_amd64.whl", hash = "sha256:b554bd6e80bba582fd326ddab296e563c20c64dca816d5e30489760e0c41529f"}, + {file = "fonttools-4.58.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca773fe7812e4e1197ee4e63b9691e89650ab55f679e12ac86052d2fe0d152cd"}, + {file = "fonttools-4.58.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e31289101221910f44245472e02b1a2f7d671c6d06a45c07b354ecb25829ad92"}, + {file = "fonttools-4.58.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90c9e3c01475bb9602cb617f69f02c4ba7ab7784d93f0b0d685e84286f4c1a10"}, + {file = "fonttools-4.58.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e00a826f2bc745a010341ac102082fe5e3fb9f0861b90ed9ff32277598813711"}, + {file = "fonttools-4.58.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc75e72e9d2a4ad0935c59713bd38679d51c6fefab1eadde80e3ed4c2a11ea84"}, + {file = "fonttools-4.58.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f57a795e540059ce3de68508acfaaf177899b39c36ef0a2833b2308db98c71f1"}, + {file = "fonttools-4.58.4-cp39-cp39-win32.whl", hash = "sha256:a7d04f64c88b48ede655abcf76f2b2952f04933567884d99be7c89e0a4495131"}, + {file = "fonttools-4.58.4-cp39-cp39-win_amd64.whl", hash = "sha256:5a8bc5dfd425c89b1c38380bc138787b0a830f761b82b37139aa080915503b69"}, + {file = "fonttools-4.58.4-py3-none-any.whl", hash = "sha256:a10ce13a13f26cbb9f37512a4346bb437ad7e002ff6fa966a7ce7ff5ac3528bd"}, + {file = "fonttools-4.58.4.tar.gz", hash = "sha256:928a8009b9884ed3aae17724b960987575155ca23c6f0b8146e400cc9e0d44ba"}, ] [package.extras] @@ -2669,18 +2666,18 @@ graphql-core = ">=3.2,<3.3" [[package]] name = "graphviz" -version = "0.20.3" +version = "0.21" description = "Simple Python interface for Graphviz" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "graphviz-0.20.3-py3-none-any.whl", hash = "sha256:81f848f2904515d8cd359cc611faba817598d2feaac4027b266aa3eda7b3dde5"}, - {file = "graphviz-0.20.3.zip", hash = "sha256:09d6bc81e6a9fa392e7ba52135a9d49f1ed62526f96499325930e87ca1b5925d"}, + {file = "graphviz-0.21-py3-none-any.whl", hash = "sha256:54f33de9f4f911d7e84e4191749cac8cc5653f815b06738c54db9a15ab8b1e42"}, + {file = "graphviz-0.21.tar.gz", hash = "sha256:20743e7183be82aaaa8ad6c93f8893c923bd6658a04c32ee115edb3c8a835f78"}, ] [package.extras] -dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"] -docs = ["sphinx (>=5,<7)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] +dev = ["Flake8-pyproject", "build", "flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"] +docs = ["sphinx (>=5,<7)", "sphinx-autodoc-typehints", "sphinx-rtd-theme (>=0.2.5)"] test = ["coverage", "pytest (>=7,<8.1)", "pytest-cov", "pytest-mock (>=3)"] [[package]] @@ -2885,19 +2882,19 @@ numpy = ">=1.19.3" [[package]] name = "hf-xet" -version = "1.1.3" +version = "1.1.4" description = "Fast transfer of large files with the Hugging Face Hub." optional = false python-versions = ">=3.8" files = [ - {file = "hf_xet-1.1.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c3b508b5f583a75641aebf732853deb058953370ce8184f5dabc49f803b0819b"}, - {file = "hf_xet-1.1.3-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:b788a61977fbe6b5186e66239e2a329a3f0b7e7ff50dad38984c0c74f44aeca1"}, - {file = "hf_xet-1.1.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd2da210856444a34aad8ada2fc12f70dabed7cc20f37e90754d1d9b43bc0534"}, - {file = "hf_xet-1.1.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8203f52827e3df65981984936654a5b390566336956f65765a8aa58c362bb841"}, - {file = "hf_xet-1.1.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:30c575a5306f8e6fda37edb866762140a435037365eba7a17ce7bd0bc0216a8b"}, - {file = "hf_xet-1.1.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7c1a6aa6abed1f696f8099aa9796ca04c9ee778a58728a115607de9cc4638ff1"}, - {file = "hf_xet-1.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:b578ae5ac9c056296bb0df9d018e597c8dc6390c5266f35b5c44696003cde9f3"}, - {file = "hf_xet-1.1.3.tar.gz", hash = "sha256:a5f09b1dd24e6ff6bcedb4b0ddab2d81824098bb002cf8b4ffa780545fa348c3"}, + {file = "hf_xet-1.1.4-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:6591ab9f61ea82d261107ed90237e2ece972f6a7577d96f5f071208bbf255d1c"}, + {file = "hf_xet-1.1.4-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:071b0b4d4698990f746edd666c7cc42555833d22035d88db0df936677fb57d29"}, + {file = "hf_xet-1.1.4-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5b610831e92e41182d4c028653978b844d332d492cdcba1b920d3aca4a0207e"}, + {file = "hf_xet-1.1.4-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f6578bcd71393abfd60395279cc160ca808b61f5f9d535b922fcdcd3f77a708d"}, + {file = "hf_xet-1.1.4-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fb2bbfa2aae0e4f0baca988e7ba8d8c1a39a25adf5317461eb7069ad00505b3e"}, + {file = "hf_xet-1.1.4-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:73346ba3e2e15ea8909a26b0862b458f15b003e6277935e3fba5bf273508d698"}, + {file = "hf_xet-1.1.4-cp37-abi3-win_amd64.whl", hash = "sha256:52e8f8bc2029d8b911493f43cea131ac3fa1f0dc6a13c50b593c4516f02c6fc3"}, + {file = "hf_xet-1.1.4.tar.gz", hash = "sha256:875158df90cb13547752532ed73cad9dfaad3b29e203143838f67178418d08a4"}, ] [package.extras] @@ -2916,13 +2913,13 @@ files = [ [[package]] name = "huggingface-hub" -version = "0.32.4" +version = "0.33.0" description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" optional = false python-versions = ">=3.8.0" files = [ - {file = "huggingface_hub-0.32.4-py3-none-any.whl", hash = "sha256:37abf8826b38d971f60d3625229221c36e53fe58060286db9baf619cfbf39767"}, - {file = "huggingface_hub-0.32.4.tar.gz", hash = "sha256:f61d45cd338736f59fb0e97550b74c24ee771bcc92c05ae0766b9116abe720be"}, + {file = "huggingface_hub-0.33.0-py3-none-any.whl", hash = "sha256:e8668875b40c68f9929150d99727d39e5ebb8a05a98e4191b908dc7ded9074b3"}, + {file = "huggingface_hub-0.33.0.tar.gz", hash = "sha256:aa31f70d29439d00ff7a33837c03f1f9dd83971ce4e29ad664d63ffb17d3bb97"}, ] [package.dependencies] @@ -3030,7 +3027,6 @@ files = [ {file = "imagecodecs-2025.3.30-cp313-cp313-win32.whl", hash = "sha256:66b614488d85d91f456b949fde4ad678dbe95cde38861043122237de086308c1"}, {file = "imagecodecs-2025.3.30-cp313-cp313-win_amd64.whl", hash = "sha256:1c51fef75fec66b4ea5e98b4ab47889942049389278749e1f96329c38f31c377"}, {file = "imagecodecs-2025.3.30-cp313-cp313-win_arm64.whl", hash = "sha256:eda70c0b9d2bcf225f7ae12dbefd0e3ab92ea7db30cdb56b292517fb61357ad7"}, - {file = "imagecodecs-2025.3.30-cp313-cp313t-win_amd64.whl", hash = "sha256:8861d76ca85b823e88604e58ee31131dd5133bfc30147147368d335c5b0e42e1"}, {file = "imagecodecs-2025.3.30.tar.gz", hash = "sha256:29256f44a7fcfb8f235a3e9b3bae72b06ea2112e63bcc892267a8c01b7097f90"}, ] @@ -3643,13 +3639,13 @@ files = [ [[package]] name = "latexcodec" -version = "3.0.0" +version = "3.0.1" description = "A lexer and codec to work with LaTeX code in Python." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "latexcodec-3.0.0-py3-none-any.whl", hash = "sha256:6f3477ad5e61a0a99bd31a6a370c34e88733a6bad9c921a3ffcfacada12f41a7"}, - {file = "latexcodec-3.0.0.tar.gz", hash = "sha256:917dc5fe242762cc19d963e6548b42d63a118028cdd3361d62397e3b638b6bc5"}, + {file = "latexcodec-3.0.1-py3-none-any.whl", hash = "sha256:a9eb8200bff693f0437a69581f7579eb6bca25c4193515c09900ce76451e452e"}, + {file = "latexcodec-3.0.1.tar.gz", hash = "sha256:e78a6911cd72f9dec35031c6ec23584de6842bfbc4610a9678868d14cdfb0357"}, ] [[package]] @@ -3939,13 +3935,13 @@ testing = ["pytest"] [[package]] name = "markdown" -version = "3.8" +version = "3.8.1" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.9" files = [ - {file = "markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc"}, - {file = "markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f"}, + {file = "markdown-3.8.1-py3-none-any.whl", hash = "sha256:46cc0c0f1e5211ab2e9d453582f0b28a1bfaf058a9f7d5c50386b99b588d8811"}, + {file = "markdown-3.8.1.tar.gz", hash = "sha256:a2e2f01cead4828ee74ecca9623045f62216aef2212a7685d6eb9163f590b8c1"}, ] [package.extras] @@ -4333,21 +4329,21 @@ test = ["pytest", "pytest-cov"] [[package]] name = "monai" -version = "1.4.0" +version = "1.5.0" description = "AI Toolkit for Healthcare Imaging" optional = false python-versions = ">=3.9" files = [ - {file = "monai-1.4.0-py3-none-any.whl", hash = "sha256:a715ca9ff8a068e36efdd147420f0ff02ead744909e7ea0998f31129c4997c9b"}, - {file = "monai-1.4.0.tar.gz", hash = "sha256:2fff631dd78afc166ccbafb89d7dde06f3d3b287860fb6f2d6cddd6bcc72caa8"}, + {file = "monai-1.5.0-py3-none-any.whl", hash = "sha256:93259cfa8b68fbf006dea7c78376a46ce38f369bf8b20b36f74ab1d3f484d37b"}, + {file = "monai-1.5.0.tar.gz", hash = "sha256:8c5e4555839812fe060e13da34d9b3342d750d8ad5934f1b79f9b08eb6b35d64"}, ] [package.dependencies] -numpy = ">=1.24,<2.0" -torch = ">=1.9" +numpy = ">=1.24,<3.0" +torch = ">=2.4.1,<2.7.0" [package.extras] -all = ["clearml (>=1.10.0rc0)", "cucim-cu12", "einops", "fire", "gdown (>=4.7.3)", "h5py", "huggingface-hub", "imagecodecs", "itk (>=5.2)", "jsonschema", "lmdb", "lpips (==0.1.4)", "matplotlib (>=3.6.3)", "mlflow (>=2.12.2)", "nibabel", "ninja", "nni", "nvidia-ml-py", "onnx (>=1.13.0)", "onnxruntime", "openslide-python", "optuna", "pandas", "pillow", "psutil", "pyamg (>=5.0.0)", "pydicom", "pynrrd", "pytorch-ignite (==0.4.11)", "pyyaml", "scikit-image (>=0.14.2)", "scipy (>=1.12.0)", "tensorboard", "tensorboardX", "tifffile", "torchvision", "tqdm (>=4.47.0)", "transformers (>=4.36.0,<4.41.0)", "zarr"] +all = ["clearml (>=1.10.0rc0)", "cucim-cu12", "einops", "fire", "gdown (>=4.7.3)", "h5py", "huggingface-hub", "imagecodecs", "itk (>=5.2)", "jsonschema", "lmdb", "lpips (==0.1.4)", "matplotlib (>=3.6.3)", "mlflow (>=2.12.2)", "nibabel", "ninja", "nni", "nvidia-ml-py", "onnx (>=1.13.0)", "onnxruntime", "openslide-bin", "openslide-python", "optuna", "pandas", "pillow", "psutil", "pyamg (>=5.0.0)", "pydicom", "pynrrd", "pytorch-ignite (==0.4.11)", "pyyaml", "scikit-image (>=0.14.2)", "scipy (>=1.12.0)", "tensorboard", "tensorboardX", "tifffile", "torchio", "torchvision", "tqdm (>=4.47.0)", "transformers (>=4.36.0,<4.41.0)", "zarr"] clearml = ["clearml"] cucim = ["cucim-cu12"] einops = ["einops"] @@ -4367,7 +4363,7 @@ nibabel = ["nibabel"] ninja = ["ninja"] nni = ["nni"] onnx = ["onnx (>=1.13.0)", "onnxruntime"] -openslide = ["openslide-python"] +openslide = ["openslide-bin", "openslide-python"] optuna = ["optuna"] packaging = ["packaging"] pandas = ["pandas"] @@ -4384,6 +4380,7 @@ skimage = ["scikit-image (>=0.14.2)"] tensorboard = ["tensorboard"] tensorboardx = ["tensorboardX"] tifffile = ["tifffile"] +torchio = ["torchio"] torchvision = ["torchvision"] tqdm = ["tqdm (>=4.47.0)"] transformers = ["transformers (>=4.36.0,<4.41.0)"] @@ -4419,188 +4416,189 @@ tests = ["pytest (>=4.6)"] [[package]] name = "msgpack" -version = "1.1.0" +version = "1.1.1" description = "MessagePack serializer" optional = false python-versions = ">=3.8" files = [ - {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, - {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, - {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"}, - {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"}, - {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"}, - {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"}, - {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"}, - {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"}, - {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"}, - {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"}, - {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"}, - {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, - {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, - {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"}, - {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"}, - {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, - {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"}, - {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"}, - {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"}, - {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"}, - {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"}, - {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"}, - {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"}, - {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"}, - {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"}, - {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"}, - {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"}, - {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"}, - {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"}, - {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"}, - {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"}, - {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"}, - {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"}, - {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"}, - {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"}, - {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"}, - {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"}, - {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"}, - {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"}, - {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"}, - {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"}, - {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"}, - {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"}, - {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"}, - {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"}, - {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"}, - {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"}, - {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"}, - {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"}, - {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"}, - {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"}, - {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"}, - {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"}, - {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"}, - {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"}, - {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"}, - {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"}, - {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"}, - {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"}, - {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"}, - {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"}, - {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"}, - {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"}, - {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, + {file = "msgpack-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:353b6fc0c36fde68b661a12949d7d49f8f51ff5fa019c1e47c87c4ff34b080ed"}, + {file = "msgpack-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:79c408fcf76a958491b4e3b103d1c417044544b68e96d06432a189b43d1215c8"}, + {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78426096939c2c7482bf31ef15ca219a9e24460289c00dd0b94411040bb73ad2"}, + {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b17ba27727a36cb73aabacaa44b13090feb88a01d012c0f4be70c00f75048b4"}, + {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a17ac1ea6ec3c7687d70201cfda3b1e8061466f28f686c24f627cae4ea8efd0"}, + {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:88d1e966c9235c1d4e2afac21ca83933ba59537e2e2727a999bf3f515ca2af26"}, + {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f6d58656842e1b2ddbe07f43f56b10a60f2ba5826164910968f5933e5178af75"}, + {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:96decdfc4adcbc087f5ea7ebdcfd3dee9a13358cae6e81d54be962efc38f6338"}, + {file = "msgpack-1.1.1-cp310-cp310-win32.whl", hash = "sha256:6640fd979ca9a212e4bcdf6eb74051ade2c690b862b679bfcb60ae46e6dc4bfd"}, + {file = "msgpack-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:8b65b53204fe1bd037c40c4148d00ef918eb2108d24c9aaa20bc31f9810ce0a8"}, + {file = "msgpack-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:71ef05c1726884e44f8b1d1773604ab5d4d17729d8491403a705e649116c9558"}, + {file = "msgpack-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:36043272c6aede309d29d56851f8841ba907a1a3d04435e43e8a19928e243c1d"}, + {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a32747b1b39c3ac27d0670122b57e6e57f28eefb725e0b625618d1b59bf9d1e0"}, + {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a8b10fdb84a43e50d38057b06901ec9da52baac6983d3f709d8507f3889d43f"}, + {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0c325c3f485dc54ec298d8b024e134acf07c10d494ffa24373bea729acf704"}, + {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:88daaf7d146e48ec71212ce21109b66e06a98e5e44dca47d853cbfe171d6c8d2"}, + {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8b55ea20dc59b181d3f47103f113e6f28a5e1c89fd5b67b9140edb442ab67f2"}, + {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4a28e8072ae9779f20427af07f53bbb8b4aa81151054e882aee333b158da8752"}, + {file = "msgpack-1.1.1-cp311-cp311-win32.whl", hash = "sha256:7da8831f9a0fdb526621ba09a281fadc58ea12701bc709e7b8cbc362feabc295"}, + {file = "msgpack-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fd1b58e1431008a57247d6e7cc4faa41c3607e8e7d4aaf81f7c29ea013cb458"}, + {file = "msgpack-1.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae497b11f4c21558d95de9f64fff7053544f4d1a17731c866143ed6bb4591238"}, + {file = "msgpack-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:33be9ab121df9b6b461ff91baac6f2731f83d9b27ed948c5b9d1978ae28bf157"}, + {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f64ae8fe7ffba251fecb8408540c34ee9df1c26674c50c4544d72dbf792e5ce"}, + {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a494554874691720ba5891c9b0b39474ba43ffb1aaf32a5dac874effb1619e1a"}, + {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb643284ab0ed26f6957d969fe0dd8bb17beb567beb8998140b5e38a90974f6c"}, + {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d275a9e3c81b1093c060c3837e580c37f47c51eca031f7b5fb76f7b8470f5f9b"}, + {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fd6b577e4541676e0cc9ddc1709d25014d3ad9a66caa19962c4f5de30fc09ef"}, + {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb29aaa613c0a1c40d1af111abf025f1732cab333f96f285d6a93b934738a68a"}, + {file = "msgpack-1.1.1-cp312-cp312-win32.whl", hash = "sha256:870b9a626280c86cff9c576ec0d9cbcc54a1e5ebda9cd26dab12baf41fee218c"}, + {file = "msgpack-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:5692095123007180dca3e788bb4c399cc26626da51629a31d40207cb262e67f4"}, + {file = "msgpack-1.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3765afa6bd4832fc11c3749be4ba4b69a0e8d7b728f78e68120a157a4c5d41f0"}, + {file = "msgpack-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8ddb2bcfd1a8b9e431c8d6f4f7db0773084e107730ecf3472f1dfe9ad583f3d9"}, + {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:196a736f0526a03653d829d7d4c5500a97eea3648aebfd4b6743875f28aa2af8"}, + {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d592d06e3cc2f537ceeeb23d38799c6ad83255289bb84c2e5792e5a8dea268a"}, + {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4df2311b0ce24f06ba253fda361f938dfecd7b961576f9be3f3fbd60e87130ac"}, + {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e4141c5a32b5e37905b5940aacbc59739f036930367d7acce7a64e4dec1f5e0b"}, + {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b1ce7f41670c5a69e1389420436f41385b1aa2504c3b0c30620764b15dded2e7"}, + {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4147151acabb9caed4e474c3344181e91ff7a388b888f1e19ea04f7e73dc7ad5"}, + {file = "msgpack-1.1.1-cp313-cp313-win32.whl", hash = "sha256:500e85823a27d6d9bba1d057c871b4210c1dd6fb01fbb764e37e4e8847376323"}, + {file = "msgpack-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:6d489fba546295983abd142812bda76b57e33d0b9f5d5b71c09a583285506f69"}, + {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bba1be28247e68994355e028dcd668316db30c1f758d3241a7b903ac78dcd285"}, + {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8f93dcddb243159c9e4109c9750ba5b335ab8d48d9522c5308cd05d7e3ce600"}, + {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fbbc0b906a24038c9958a1ba7ae0918ad35b06cb449d398b76a7d08470b0ed9"}, + {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:61e35a55a546a1690d9d09effaa436c25ae6130573b6ee9829c37ef0f18d5e78"}, + {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:1abfc6e949b352dadf4bce0eb78023212ec5ac42f6abfd469ce91d783c149c2a"}, + {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:996f2609ddf0142daba4cefd767d6db26958aac8439ee41db9cc0db9f4c4c3a6"}, + {file = "msgpack-1.1.1-cp38-cp38-win32.whl", hash = "sha256:4d3237b224b930d58e9d83c81c0dba7aacc20fcc2f89c1e5423aa0529a4cd142"}, + {file = "msgpack-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:da8f41e602574ece93dbbda1fab24650d6bf2a24089f9e9dbb4f5730ec1e58ad"}, + {file = "msgpack-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5be6b6bc52fad84d010cb45433720327ce886009d862f46b26d4d154001994b"}, + {file = "msgpack-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a89cd8c087ea67e64844287ea52888239cbd2940884eafd2dcd25754fb72232"}, + {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d75f3807a9900a7d575d8d6674a3a47e9f227e8716256f35bc6f03fc597ffbf"}, + {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d182dac0221eb8faef2e6f44701812b467c02674a322c739355c39e94730cdbf"}, + {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b13fe0fb4aac1aa5320cd693b297fe6fdef0e7bea5518cbc2dd5299f873ae90"}, + {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:435807eeb1bc791ceb3247d13c79868deb22184e1fc4224808750f0d7d1affc1"}, + {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4835d17af722609a45e16037bb1d4d78b7bdf19d6c0128116d178956618c4e88"}, + {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8ef6e342c137888ebbfb233e02b8fbd689bb5b5fcc59b34711ac47ebd504478"}, + {file = "msgpack-1.1.1-cp39-cp39-win32.whl", hash = "sha256:61abccf9de335d9efd149e2fff97ed5974f2481b3353772e8e2dd3402ba2bd57"}, + {file = "msgpack-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:40eae974c873b2992fd36424a5d9407f93e97656d999f43fca9d29f820899084"}, + {file = "msgpack-1.1.1.tar.gz", hash = "sha256:77b79ce34a2bdab2594f490c8e80dd62a02d650b91a75159a63ec413b8d104cd"}, ] [[package]] name = "multidict" -version = "6.4.4" +version = "6.5.0" description = "multidict implementation" optional = false python-versions = ">=3.9" files = [ - {file = "multidict-6.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8adee3ac041145ffe4488ea73fa0a622b464cc25340d98be76924d0cda8545ff"}, - {file = "multidict-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b61e98c3e2a861035aaccd207da585bdcacef65fe01d7a0d07478efac005e028"}, - {file = "multidict-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75493f28dbadecdbb59130e74fe935288813301a8554dc32f0c631b6bdcdf8b0"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffc3c6a37e048b5395ee235e4a2a0d639c2349dffa32d9367a42fc20d399772"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87cb72263946b301570b0f63855569a24ee8758aaae2cd182aae7d95fbc92ca7"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bbf7bd39822fd07e3609b6b4467af4c404dd2b88ee314837ad1830a7f4a8299"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1f7cbd4f1f44ddf5fd86a8675b7679176eae770f2fc88115d6dddb6cefb59bc"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5ac9e5bfce0e6282e7f59ff7b7b9a74aa8e5c60d38186a4637f5aa764046ad"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4efc31dfef8c4eeb95b6b17d799eedad88c4902daba39ce637e23a17ea078915"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9fcad2945b1b91c29ef2b4050f590bfcb68d8ac8e0995a74e659aa57e8d78e01"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d877447e7368c7320832acb7159557e49b21ea10ffeb135c1077dbbc0816b598"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:33a12ebac9f380714c298cbfd3e5b9c0c4e89c75fe612ae496512ee51028915f"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0f14ea68d29b43a9bf37953881b1e3eb75b2739e896ba4a6aa4ad4c5b9ffa145"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0327ad2c747a6600e4797d115d3c38a220fdb28e54983abe8964fd17e95ae83c"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d1a20707492db9719a05fc62ee215fd2c29b22b47c1b1ba347f9abc831e26683"}, - {file = "multidict-6.4.4-cp310-cp310-win32.whl", hash = "sha256:d83f18315b9fca5db2452d1881ef20f79593c4aa824095b62cb280019ef7aa3d"}, - {file = "multidict-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:9c17341ee04545fd962ae07330cb5a39977294c883485c8d74634669b1f7fe04"}, - {file = "multidict-6.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4f5f29794ac0e73d2a06ac03fd18870adc0135a9d384f4a306a951188ed02f95"}, - {file = "multidict-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c04157266344158ebd57b7120d9b0b35812285d26d0e78193e17ef57bfe2979a"}, - {file = "multidict-6.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb61ffd3ab8310d93427e460f565322c44ef12769f51f77277b4abad7b6f7223"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e0ba18a9afd495f17c351d08ebbc4284e9c9f7971d715f196b79636a4d0de44"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9faf1b1dcaadf9f900d23a0e6d6c8eadd6a95795a0e57fcca73acce0eb912065"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a4d1cb1327c6082c4fce4e2a438483390964c02213bc6b8d782cf782c9b1471f"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:941f1bec2f5dbd51feeb40aea654c2747f811ab01bdd3422a48a4e4576b7d76a"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5f8a146184da7ea12910a4cec51ef85e44f6268467fb489c3caf0cd512f29c2"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:232b7237e57ec3c09be97206bfb83a0aa1c5d7d377faa019c68a210fa35831f1"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:55ae0721c1513e5e3210bca4fc98456b980b0c2c016679d3d723119b6b202c42"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:51d662c072579f63137919d7bb8fc250655ce79f00c82ecf11cab678f335062e"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0e05c39962baa0bb19a6b210e9b1422c35c093b651d64246b6c2e1a7e242d9fd"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5b1cc3ab8c31d9ebf0faa6e3540fb91257590da330ffe6d2393d4208e638925"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:93ec84488a384cd7b8a29c2c7f467137d8a73f6fe38bb810ecf29d1ade011a7c"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b308402608493638763abc95f9dc0030bbd6ac6aff784512e8ac3da73a88af08"}, - {file = "multidict-6.4.4-cp311-cp311-win32.whl", hash = "sha256:343892a27d1a04d6ae455ecece12904d242d299ada01633d94c4f431d68a8c49"}, - {file = "multidict-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:73484a94f55359780c0f458bbd3c39cb9cf9c182552177d2136e828269dee529"}, - {file = "multidict-6.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dc388f75a1c00000824bf28b7633e40854f4127ede80512b44c3cfeeea1839a2"}, - {file = "multidict-6.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:98af87593a666f739d9dba5d0ae86e01b0e1a9cfcd2e30d2d361fbbbd1a9162d"}, - {file = "multidict-6.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aff4cafea2d120327d55eadd6b7f1136a8e5a0ecf6fb3b6863e8aca32cd8e50a"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:169c4ba7858176b797fe551d6e99040c531c775d2d57b31bcf4de6d7a669847f"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b9eb4c59c54421a32b3273d4239865cb14ead53a606db066d7130ac80cc8ec93"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cf3bd54c56aa16fdb40028d545eaa8d051402b61533c21e84046e05513d5780"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f682c42003c7264134bfe886376299db4cc0c6cd06a3295b41b347044bcb5482"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920f9cf2abdf6e493c519492d892c362007f113c94da4c239ae88429835bad1"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:530d86827a2df6504526106b4c104ba19044594f8722d3e87714e847c74a0275"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ecde56ea2439b96ed8a8d826b50c57364612ddac0438c39e473fafad7ae1c23b"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:dc8c9736d8574b560634775ac0def6bdc1661fc63fa27ffdfc7264c565bcb4f2"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7f3d3b3c34867579ea47cbd6c1f2ce23fbfd20a273b6f9e3177e256584f1eacc"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:87a728af265e08f96b6318ebe3c0f68b9335131f461efab2fc64cc84a44aa6ed"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9f193eeda1857f8e8d3079a4abd258f42ef4a4bc87388452ed1e1c4d2b0c8740"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be06e73c06415199200e9a2324a11252a3d62030319919cde5e6950ffeccf72e"}, - {file = "multidict-6.4.4-cp312-cp312-win32.whl", hash = "sha256:622f26ea6a7e19b7c48dd9228071f571b2fbbd57a8cd71c061e848f281550e6b"}, - {file = "multidict-6.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:5e2bcda30d5009996ff439e02a9f2b5c3d64a20151d34898c000a6281faa3781"}, - {file = "multidict-6.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:82ffabefc8d84c2742ad19c37f02cde5ec2a1ee172d19944d380f920a340e4b9"}, - {file = "multidict-6.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6a2f58a66fe2c22615ad26156354005391e26a2f3721c3621504cd87c1ea87bf"}, - {file = "multidict-6.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5883d6ee0fd9d8a48e9174df47540b7545909841ac82354c7ae4cbe9952603bd"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9abcf56a9511653fa1d052bfc55fbe53dbee8f34e68bd6a5a038731b0ca42d15"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6ed5ae5605d4ad5a049fad2a28bb7193400700ce2f4ae484ab702d1e3749c3f9"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbfcb60396f9bcfa63e017a180c3105b8c123a63e9d1428a36544e7d37ca9e20"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0f1987787f5f1e2076b59692352ab29a955b09ccc433c1f6b8e8e18666f608b"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d0121ccce8c812047d8d43d691a1ad7641f72c4f730474878a5aeae1b8ead8c"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83ec4967114295b8afd120a8eec579920c882831a3e4c3331d591a8e5bfbbc0f"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:995f985e2e268deaf17867801b859a282e0448633f1310e3704b30616d269d69"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d832c608f94b9f92a0ec8b7e949be7792a642b6e535fcf32f3e28fab69eeb046"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d21c1212171cf7da703c5b0b7a0e85be23b720818aef502ad187d627316d5645"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:cbebaa076aaecad3d4bb4c008ecc73b09274c952cf6a1b78ccfd689e51f5a5b0"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c93a6fb06cc8e5d3628b2b5fda215a5db01e8f08fc15fadd65662d9b857acbe4"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8cd8f81f1310182362fb0c7898145ea9c9b08a71081c5963b40ee3e3cac589b1"}, - {file = "multidict-6.4.4-cp313-cp313-win32.whl", hash = "sha256:3e9f1cd61a0ab857154205fb0b1f3d3ace88d27ebd1409ab7af5096e409614cd"}, - {file = "multidict-6.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:8ffb40b74400e4455785c2fa37eba434269149ec525fc8329858c862e4b35373"}, - {file = "multidict-6.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6a602151dbf177be2450ef38966f4be3467d41a86c6a845070d12e17c858a156"}, - {file = "multidict-6.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d2b9712211b860d123815a80b859075d86a4d54787e247d7fbee9db6832cf1c"}, - {file = "multidict-6.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d2fa86af59f8fc1972e121ade052145f6da22758f6996a197d69bb52f8204e7e"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50855d03e9e4d66eab6947ba688ffb714616f985838077bc4b490e769e48da51"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5bce06b83be23225be1905dcdb6b789064fae92499fbc458f59a8c0e68718601"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66ed0731f8e5dfd8369a883b6e564aca085fb9289aacabd9decd70568b9a30de"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:329ae97fc2f56f44d91bc47fe0972b1f52d21c4b7a2ac97040da02577e2daca2"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c27e5dcf520923d6474d98b96749e6805f7677e93aaaf62656005b8643f907ab"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:058cc59b9e9b143cc56715e59e22941a5d868c322242278d28123a5d09cdf6b0"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:69133376bc9a03f8c47343d33f91f74a99c339e8b58cea90433d8e24bb298031"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:d6b15c55721b1b115c5ba178c77104123745b1417527ad9641a4c5e2047450f0"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a887b77f51d3d41e6e1a63cf3bc7ddf24de5939d9ff69441387dfefa58ac2e26"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:632a3bf8f1787f7ef7d3c2f68a7bde5be2f702906f8b5842ad6da9d974d0aab3"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a145c550900deb7540973c5cdb183b0d24bed6b80bf7bddf33ed8f569082535e"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc5d83c6619ca5c9672cb78b39ed8542f1975a803dee2cda114ff73cbb076edd"}, - {file = "multidict-6.4.4-cp313-cp313t-win32.whl", hash = "sha256:3312f63261b9df49be9d57aaa6abf53a6ad96d93b24f9cc16cf979956355ce6e"}, - {file = "multidict-6.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:ba852168d814b2c73333073e1c7116d9395bea69575a01b0b3c89d2d5a87c8fb"}, - {file = "multidict-6.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:603f39bd1cf85705c6c1ba59644b480dfe495e6ee2b877908de93322705ad7cf"}, - {file = "multidict-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc60f91c02e11dfbe3ff4e1219c085695c339af72d1641800fe6075b91850c8f"}, - {file = "multidict-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:496bcf01c76a70a31c3d746fd39383aad8d685ce6331e4c709e9af4ced5fa221"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4219390fb5bf8e548e77b428bb36a21d9382960db5321b74d9d9987148074d6b"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef4e9096ff86dfdcbd4a78253090ba13b1d183daa11b973e842465d94ae1772"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49a29d7133b1fc214e818bbe025a77cc6025ed9a4f407d2850373ddde07fd04a"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e32053d6d3a8b0dfe49fde05b496731a0e6099a4df92154641c00aa76786aef5"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc403092a49509e8ef2d2fd636a8ecefc4698cc57bbe894606b14579bc2a955"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5363f9b2a7f3910e5c87d8b1855c478c05a2dc559ac57308117424dfaad6805c"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e543a40e4946cf70a88a3be87837a3ae0aebd9058ba49e91cacb0b2cd631e2b"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:60d849912350da557fe7de20aa8cf394aada6980d0052cc829eeda4a0db1c1db"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:19d08b4f22eae45bb018b9f06e2838c1e4b853c67628ef8ae126d99de0da6395"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d693307856d1ef08041e8b6ff01d5b4618715007d288490ce2c7e29013c12b9a"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fad6daaed41021934917f4fb03ca2db8d8a4d79bf89b17ebe77228eb6710c003"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c10d17371bff801af0daf8b073c30b6cf14215784dc08cd5c43ab5b7b8029bbc"}, - {file = "multidict-6.4.4-cp39-cp39-win32.whl", hash = "sha256:7e23f2f841fcb3ebd4724a40032d32e0892fbba4143e43d2a9e7695c5e50e6bd"}, - {file = "multidict-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:4d7b50b673ffb4ff4366e7ab43cf1f0aef4bd3608735c5fbdf0bdb6f690da411"}, - {file = "multidict-6.4.4-py3-none-any.whl", hash = "sha256:bd4557071b561a8b3b6075c3ce93cf9bfb6182cb241805c3d66ced3b75eff4ac"}, - {file = "multidict-6.4.4.tar.gz", hash = "sha256:69ee9e6ba214b5245031b76233dd95408a0fd57fdb019ddcc1ead4790932a8e8"}, + {file = "multidict-6.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e118a202904623b1d2606d1c8614e14c9444b59d64454b0c355044058066469"}, + {file = "multidict-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a42995bdcaff4e22cb1280ae7752c3ed3fbb398090c6991a2797a4a0e5ed16a9"}, + {file = "multidict-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2261b538145723ca776e55208640fffd7ee78184d223f37c2b40b9edfe0e818a"}, + {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e5b19f8cd67235fab3e195ca389490415d9fef5a315b1fa6f332925dc924262"}, + {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:177b081e4dec67c3320b16b3aa0babc178bbf758553085669382c7ec711e1ec8"}, + {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d30a2cc106a7d116b52ee046207614db42380b62e6b1dd2a50eba47c5ca5eb1"}, + {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a72933bc308d7a64de37f0d51795dbeaceebdfb75454f89035cdfc6a74cfd129"}, + {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96d109e663d032280ef8ef62b50924b2e887d5ddf19e301844a6cb7e91a172a6"}, + {file = "multidict-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b555329c9894332401f03b9a87016f0b707b6fccd4706793ec43b4a639e75869"}, + {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6994bad9d471ef2156f2b6850b51e20ee409c6b9deebc0e57be096be9faffdce"}, + {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:b15f817276c96cde9060569023808eec966bd8da56a97e6aa8116f34ddab6534"}, + {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b4bf507c991db535a935b2127cf057a58dbc688c9f309c72080795c63e796f58"}, + {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:60c3f8f13d443426c55f88cf3172547bbc600a86d57fd565458b9259239a6737"}, + {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a10227168a24420c158747fc201d4279aa9af1671f287371597e2b4f2ff21879"}, + {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e3b1425fe54ccfde66b8cfb25d02be34d5dfd2261a71561ffd887ef4088b4b69"}, + {file = "multidict-6.5.0-cp310-cp310-win32.whl", hash = "sha256:b4e47ef51237841d1087e1e1548071a6ef22e27ed0400c272174fa585277c4b4"}, + {file = "multidict-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:63b3b24fadc7067282c88fae5b2f366d5b3a7c15c021c2838de8c65a50eeefb4"}, + {file = "multidict-6.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:8b2d61afbafc679b7eaf08e9de4fa5d38bd5dc7a9c0a577c9f9588fb49f02dbb"}, + {file = "multidict-6.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8b4bf6bb15a05796a07a248084e3e46e032860c899c7a9b981030e61368dba95"}, + {file = "multidict-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46bb05d50219655c42a4b8fcda9c7ee658a09adbb719c48e65a20284e36328ea"}, + {file = "multidict-6.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:54f524d73f4d54e87e03c98f6af601af4777e4668a52b1bd2ae0a4d6fc7b392b"}, + {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529b03600466480ecc502000d62e54f185a884ed4570dee90d9a273ee80e37b5"}, + {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69ad681ad7c93a41ee7005cc83a144b5b34a3838bcf7261e2b5356057b0f78de"}, + {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fe9fada8bc0839466b09fa3f6894f003137942984843ec0c3848846329a36ae"}, + {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f94c6ea6405fcf81baef1e459b209a78cda5442e61b5b7a57ede39d99b5204a0"}, + {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca75ad8a39ed75f079a8931435a5b51ee4c45d9b32e1740f99969a5d1cc2ee"}, + {file = "multidict-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be4c08f3a2a6cc42b414496017928d95898964fed84b1b2dace0c9ee763061f9"}, + {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:046a7540cfbb4d5dc846a1fd9843f3ba980c6523f2e0c5b8622b4a5c94138ae6"}, + {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:64306121171d988af77d74be0d8c73ee1a69cf6f96aea7fa6030c88f32a152dd"}, + {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b4ac1dd5eb0ecf6f7351d5a9137f30a83f7182209c5d37f61614dfdce5714853"}, + {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bab4a8337235365f4111a7011a1f028826ca683834ebd12de4b85e2844359c36"}, + {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a05b5604c5a75df14a63eeeca598d11b2c3745b9008539b70826ea044063a572"}, + {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:67c4a640952371c9ca65b6a710598be246ef3be5ca83ed38c16a7660d3980877"}, + {file = "multidict-6.5.0-cp311-cp311-win32.whl", hash = "sha256:fdeae096ca36c12d8aca2640b8407a9d94e961372c68435bef14e31cce726138"}, + {file = "multidict-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:e2977ef8b7ce27723ee8c610d1bd1765da4f3fbe5a64f9bf1fd3b4770e31fbc0"}, + {file = "multidict-6.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:82d0cf0ea49bae43d9e8c3851e21954eff716259ff42da401b668744d1760bcb"}, + {file = "multidict-6.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1bb986c8ea9d49947bc325c51eced1ada6d8d9b4c5b15fd3fcdc3c93edef5a74"}, + {file = "multidict-6.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:03c0923da300120830fc467e23805d63bbb4e98b94032bd863bc7797ea5fa653"}, + {file = "multidict-6.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4c78d5ec00fdd35c91680ab5cf58368faad4bd1a8721f87127326270248de9bc"}, + {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadc3cb78be90a887f8f6b73945b840da44b4a483d1c9750459ae69687940c97"}, + {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5b02e1ca495d71e07e652e4cef91adae3bf7ae4493507a263f56e617de65dafc"}, + {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7fe92a62326eef351668eec4e2dfc494927764a0840a1895cff16707fceffcd3"}, + {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7673ee4f63879ecd526488deb1989041abcb101b2d30a9165e1e90c489f3f7fb"}, + {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa097ae2a29f573de7e2d86620cbdda5676d27772d4ed2669cfa9961a0d73955"}, + {file = "multidict-6.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:300da0fa4f8457d9c4bd579695496116563409e676ac79b5e4dca18e49d1c308"}, + {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9a19bd108c35877b57393243d392d024cfbfdefe759fd137abb98f6fc910b64c"}, + {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f32a1777465a35c35ddbbd7fc1293077938a69402fcc59e40b2846d04a120dd"}, + {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9cc1e10c14ce8112d1e6d8971fe3cdbe13e314f68bea0e727429249d4a6ce164"}, + {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e95c5e07a06594bdc288117ca90e89156aee8cb2d7c330b920d9c3dd19c05414"}, + {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:40ff26f58323795f5cd2855e2718a1720a1123fb90df4553426f0efd76135462"}, + {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:76803a29fd71869a8b59c2118c9dcfb3b8f9c8723e2cce6baeb20705459505cf"}, + {file = "multidict-6.5.0-cp312-cp312-win32.whl", hash = "sha256:df7ecbc65a53a2ce1b3a0c82e6ad1a43dcfe7c6137733f9176a92516b9f5b851"}, + {file = "multidict-6.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ec1c3fbbb0b655a6540bce408f48b9a7474fd94ed657dcd2e890671fefa7743"}, + {file = "multidict-6.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:2d24a00d34808b22c1f15902899b9d82d0faeca9f56281641c791d8605eacd35"}, + {file = "multidict-6.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:53d92df1752df67a928fa7f884aa51edae6f1cf00eeb38cbcf318cf841c17456"}, + {file = "multidict-6.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:680210de2c38eef17ce46b8df8bf2c1ece489261a14a6e43c997d49843a27c99"}, + {file = "multidict-6.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e279259bcb936732bfa1a8eec82b5d2352b3df69d2fa90d25808cfc403cee90a"}, + {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1c185fc1069781e3fc8b622c4331fb3b433979850392daa5efbb97f7f9959bb"}, + {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6bb5f65ff91daf19ce97f48f63585e51595539a8a523258b34f7cef2ec7e0617"}, + {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8646b4259450c59b9286db280dd57745897897284f6308edbdf437166d93855"}, + {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d245973d4ecc04eea0a8e5ebec7882cf515480036e1b48e65dffcfbdf86d00be"}, + {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a133e7ddc9bc7fb053733d0ff697ce78c7bf39b5aec4ac12857b6116324c8d75"}, + {file = "multidict-6.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80d696fa38d738fcebfd53eec4d2e3aeb86a67679fd5e53c325756682f152826"}, + {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:20d30c9410ac3908abbaa52ee5967a754c62142043cf2ba091e39681bd51d21a"}, + {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6c65068cc026f217e815fa519d8e959a7188e94ec163ffa029c94ca3ef9d4a73"}, + {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e355ac668a8c3e49c2ca8daa4c92f0ad5b705d26da3d5af6f7d971e46c096da7"}, + {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:08db204213d0375a91a381cae0677ab95dd8c67a465eb370549daf6dbbf8ba10"}, + {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ffa58e3e215af8f6536dc837a990e456129857bb6fd546b3991be470abd9597a"}, + {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e86eb90015c6f21658dbd257bb8e6aa18bdb365b92dd1fba27ec04e58cdc31b"}, + {file = "multidict-6.5.0-cp313-cp313-win32.whl", hash = "sha256:f34a90fbd9959d0f857323bd3c52b3e6011ed48f78d7d7b9e04980b8a41da3af"}, + {file = "multidict-6.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:fcb2aa79ac6aef8d5b709bbfc2fdb1d75210ba43038d70fbb595b35af470ce06"}, + {file = "multidict-6.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:6dcee5e7e92060b4bb9bb6f01efcbb78c13d0e17d9bc6eec71660dd71dc7b0c2"}, + {file = "multidict-6.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:cbbc88abea2388fde41dd574159dec2cda005cb61aa84950828610cb5010f21a"}, + {file = "multidict-6.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70b599f70ae6536e5976364d3c3cf36f40334708bd6cebdd1e2438395d5e7676"}, + {file = "multidict-6.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:828bab777aa8d29d59700018178061854e3a47727e0611cb9bec579d3882de3b"}, + {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9695fc1462f17b131c111cf0856a22ff154b0480f86f539d24b2778571ff94d"}, + {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b5ac6ebaf5d9814b15f399337ebc6d3a7f4ce9331edd404e76c49a01620b68d"}, + {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84a51e3baa77ded07be4766a9e41d977987b97e49884d4c94f6d30ab6acaee14"}, + {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8de67f79314d24179e9b1869ed15e88d6ba5452a73fc9891ac142e0ee018b5d6"}, + {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17f78a52c214481d30550ec18208e287dfc4736f0c0148208334b105fd9e0887"}, + {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2966d0099cb2e2039f9b0e73e7fd5eb9c85805681aa2a7f867f9d95b35356921"}, + {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:86fb42ed5ed1971c642cc52acc82491af97567534a8e381a8d50c02169c4e684"}, + {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:4e990cbcb6382f9eae4ec720bcac6a1351509e6fc4a5bb70e4984b27973934e6"}, + {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d99a59d64bb1f7f2117bec837d9e534c5aeb5dcedf4c2b16b9753ed28fdc20a3"}, + {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:e8ef15cc97c9890212e1caf90f0d63f6560e1e101cf83aeaf63a57556689fb34"}, + {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:b8a09aec921b34bd8b9f842f0bcfd76c6a8c033dc5773511e15f2d517e7e1068"}, + {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ff07b504c23b67f2044533244c230808a1258b3493aaf3ea2a0785f70b7be461"}, + {file = "multidict-6.5.0-cp313-cp313t-win32.whl", hash = "sha256:9232a117341e7e979d210e41c04e18f1dc3a1d251268df6c818f5334301274e1"}, + {file = "multidict-6.5.0-cp313-cp313t-win_amd64.whl", hash = "sha256:44cb5c53fb2d4cbcee70a768d796052b75d89b827643788a75ea68189f0980a1"}, + {file = "multidict-6.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:51d33fafa82640c0217391d4ce895d32b7e84a832b8aee0dcc1b04d8981ec7f4"}, + {file = "multidict-6.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c0078358470da8dc90c37456f4a9cde9f86200949a048d53682b9cd21e5bbf2b"}, + {file = "multidict-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5cc7968b7d1bf8b973c307d38aa3a2f2c783f149bcac855944804252f1df5105"}, + {file = "multidict-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad73a60e11aa92f1f2c9330efdeaac4531b719fc568eb8d312fd4112f34cc18"}, + {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3233f21abdcd180b2624eb6988a1e1287210e99bca986d8320afca5005d85844"}, + {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bee5c0b79fca78fd2ab644ca4dc831ecf793eb6830b9f542ee5ed2c91bc35a0e"}, + {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e053a4d690f4352ce46583080fefade9a903ce0fa9d820db1be80bdb9304fa2f"}, + {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42bdee30424c1f4dcda96e07ac60e2a4ede8a89f8ae2f48b5e4ccc060f294c52"}, + {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58b2ded1a7982cf7b8322b0645713a0086b2b3cf5bb9f7c01edfc1a9f98d20dc"}, + {file = "multidict-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f805b8b951d1fadc5bc18c3c93e509608ac5a883045ee33bc22e28806847c20"}, + {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2540395b63723da748f850568357a39cd8d8d4403ca9439f9fcdad6dd423c780"}, + {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:c96aedff25f4e47b6697ba048b2c278f7caa6df82c7c3f02e077bcc8d47b4b76"}, + {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e80de5ad995de210fd02a65c2350649b8321d09bd2e44717eaefb0f5814503e8"}, + {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6cb9bcedd9391b313e5ec2fb3aa07c03e050550e7b9e4646c076d5c24ba01532"}, + {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a7d130ed7a112e25ab47309962ecafae07d073316f9d158bc7b3936b52b80121"}, + {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:95750a9a9741cd1855d1b6cb4c6031ae01c01ad38d280217b64bfae986d39d56"}, + {file = "multidict-6.5.0-cp39-cp39-win32.whl", hash = "sha256:7f78caf409914f108f4212b53a9033abfdc2cbab0647e9ac3a25bb0f21ab43d2"}, + {file = "multidict-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:220c74009507e847a3a6fc5375875f2a2e05bd9ce28cf607be0e8c94600f4472"}, + {file = "multidict-6.5.0-cp39-cp39-win_arm64.whl", hash = "sha256:d98f4ac9c1ede7e9d04076e2e6d967e15df0079a6381b297270f6bcab661195e"}, + {file = "multidict-6.5.0-py3-none-any.whl", hash = "sha256:5634b35f225977605385f56153bd95a7133faffc0ffe12ad26e10517537e8dfc"}, + {file = "multidict-6.5.0.tar.gz", hash = "sha256:942bd8002492ba819426a8d7aefde3189c1b87099cdf18aaaefefcf7f3f7b6d2"}, ] [package.dependencies] @@ -4648,43 +4646,43 @@ type = ["mypy", "mypy-extensions"] [[package]] name = "mypy" -version = "1.16.0" +version = "1.16.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" files = [ - {file = "mypy-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7909541fef256527e5ee9c0a7e2aeed78b6cda72ba44298d1334fe7881b05c5c"}, - {file = "mypy-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e71d6f0090c2256c713ed3d52711d01859c82608b5d68d4fa01a3fe30df95571"}, - {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:936ccfdd749af4766be824268bfe22d1db9eb2f34a3ea1d00ffbe5b5265f5491"}, - {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4086883a73166631307fdd330c4a9080ce24913d4f4c5ec596c601b3a4bdd777"}, - {file = "mypy-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:feec38097f71797da0231997e0de3a58108c51845399669ebc532c815f93866b"}, - {file = "mypy-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a8da6a0ee9a9770b8ff61b39c0bb07971cda90e7297f4213741b48a0cc8d93"}, - {file = "mypy-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f826aaa7ff8443bac6a494cf743f591488ea940dd360e7dd330e30dd772a5ab"}, - {file = "mypy-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82d056e6faa508501af333a6af192c700b33e15865bda49611e3d7d8358ebea2"}, - {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089bedc02307c2548eb51f426e085546db1fa7dd87fbb7c9fa561575cf6eb1ff"}, - {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a2322896003ba66bbd1318c10d3afdfe24e78ef12ea10e2acd985e9d684a666"}, - {file = "mypy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:021a68568082c5b36e977d54e8f1de978baf401a33884ffcea09bd8e88a98f4c"}, - {file = "mypy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:54066fed302d83bf5128632d05b4ec68412e1f03ef2c300434057d66866cea4b"}, - {file = "mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13"}, - {file = "mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090"}, - {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1"}, - {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8"}, - {file = "mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730"}, - {file = "mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec"}, - {file = "mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b"}, - {file = "mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0"}, - {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b"}, - {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d"}, - {file = "mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52"}, - {file = "mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb"}, - {file = "mypy-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f56236114c425620875c7cf71700e3d60004858da856c6fc78998ffe767b73d3"}, - {file = "mypy-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15486beea80be24ff067d7d0ede673b001d0d684d0095803b3e6e17a886a2a92"}, - {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2ed0e0847a80655afa2c121835b848ed101cc7b8d8d6ecc5205aedc732b1436"}, - {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb5fbc8063cb4fde7787e4c0406aa63094a34a2daf4673f359a1fb64050e9cb2"}, - {file = "mypy-1.16.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a5fcfdb7318c6a8dd127b14b1052743b83e97a970f0edb6c913211507a255e20"}, - {file = "mypy-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:2e7e0ad35275e02797323a5aa1be0b14a4d03ffdb2e5f2b0489fa07b89c67b21"}, - {file = "mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031"}, - {file = "mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab"}, + {file = "mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a"}, + {file = "mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72"}, + {file = "mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea"}, + {file = "mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574"}, + {file = "mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d"}, + {file = "mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6"}, + {file = "mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc"}, + {file = "mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782"}, + {file = "mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507"}, + {file = "mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca"}, + {file = "mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4"}, + {file = "mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6"}, + {file = "mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d"}, + {file = "mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9"}, + {file = "mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79"}, + {file = "mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15"}, + {file = "mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd"}, + {file = "mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b"}, + {file = "mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438"}, + {file = "mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536"}, + {file = "mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f"}, + {file = "mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359"}, + {file = "mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be"}, + {file = "mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee"}, + {file = "mypy-1.16.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7fc688329af6a287567f45cc1cefb9db662defeb14625213a5b7da6e692e2069"}, + {file = "mypy-1.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e198ab3f55924c03ead626ff424cad1732d0d391478dfbf7bb97b34602395da"}, + {file = "mypy-1.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09aa4f91ada245f0a45dbc47e548fd94e0dd5a8433e0114917dc3b526912a30c"}, + {file = "mypy-1.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13c7cd5b1cb2909aa318a90fd1b7e31f17c50b242953e7dd58345b2a814f6383"}, + {file = "mypy-1.16.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:58e07fb958bc5d752a280da0e890c538f1515b79a65757bbdc54252ba82e0b40"}, + {file = "mypy-1.16.1-cp39-cp39-win_amd64.whl", hash = "sha256:f895078594d918f93337a505f8add9bd654d1a24962b4c6ed9390e12531eb31b"}, + {file = "mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37"}, + {file = "mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab"}, ] [package.dependencies] @@ -5295,13 +5293,13 @@ files = [ [[package]] name = "oauthlib" -version = "3.2.2" +version = "3.3.0" description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, - {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, + {file = "oauthlib-3.3.0-py3-none-any.whl", hash = "sha256:a2b3a0a2a4ec2feb4b9110f56674a39b2cc2f23e14713f4ed20441dfba14e934"}, + {file = "oauthlib-3.3.0.tar.gz", hash = "sha256:4e707cf88d7dfc22a8cce22ca736a2eef9967c1dd3845efc0703fc922353eeb2"}, ] [package.extras] @@ -6827,13 +6825,13 @@ typing-inspect = "*" [[package]] name = "pytest" -version = "8.4.0" +version = "8.4.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" files = [ - {file = "pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e"}, - {file = "pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6"}, + {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, + {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, ] [package.dependencies] @@ -7061,104 +7059,90 @@ files = [ [[package]] name = "pyzmq" -version = "26.4.0" +version = "27.0.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.8" files = [ - {file = "pyzmq-26.4.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:0329bdf83e170ac133f44a233fc651f6ed66ef8e66693b5af7d54f45d1ef5918"}, - {file = "pyzmq-26.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:398a825d2dea96227cf6460ce0a174cf7657d6f6827807d4d1ae9d0f9ae64315"}, - {file = "pyzmq-26.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d52d62edc96787f5c1dfa6c6ccff9b581cfae5a70d94ec4c8da157656c73b5b"}, - {file = "pyzmq-26.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1410c3a3705db68d11eb2424d75894d41cff2f64d948ffe245dd97a9debfebf4"}, - {file = "pyzmq-26.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7dacb06a9c83b007cc01e8e5277f94c95c453c5851aac5e83efe93e72226353f"}, - {file = "pyzmq-26.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6bab961c8c9b3a4dc94d26e9b2cdf84de9918931d01d6ff38c721a83ab3c0ef5"}, - {file = "pyzmq-26.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7a5c09413b924d96af2aa8b57e76b9b0058284d60e2fc3730ce0f979031d162a"}, - {file = "pyzmq-26.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7d489ac234d38e57f458fdbd12a996bfe990ac028feaf6f3c1e81ff766513d3b"}, - {file = "pyzmq-26.4.0-cp310-cp310-win32.whl", hash = "sha256:dea1c8db78fb1b4b7dc9f8e213d0af3fc8ecd2c51a1d5a3ca1cde1bda034a980"}, - {file = "pyzmq-26.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:fa59e1f5a224b5e04dc6c101d7186058efa68288c2d714aa12d27603ae93318b"}, - {file = "pyzmq-26.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:a651fe2f447672f4a815e22e74630b6b1ec3a1ab670c95e5e5e28dcd4e69bbb5"}, - {file = "pyzmq-26.4.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:bfcf82644c9b45ddd7cd2a041f3ff8dce4a0904429b74d73a439e8cab1bd9e54"}, - {file = "pyzmq-26.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9bcae3979b2654d5289d3490742378b2f3ce804b0b5fd42036074e2bf35b030"}, - {file = "pyzmq-26.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccdff8ac4246b6fb60dcf3982dfaeeff5dd04f36051fe0632748fc0aa0679c01"}, - {file = "pyzmq-26.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4550af385b442dc2d55ab7717837812799d3674cb12f9a3aa897611839c18e9e"}, - {file = "pyzmq-26.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f7ffe9db1187a253fca95191854b3fda24696f086e8789d1d449308a34b88"}, - {file = "pyzmq-26.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3709c9ff7ba61589b7372923fd82b99a81932b592a5c7f1a24147c91da9a68d6"}, - {file = "pyzmq-26.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f8f3c30fb2d26ae5ce36b59768ba60fb72507ea9efc72f8f69fa088450cff1df"}, - {file = "pyzmq-26.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:382a4a48c8080e273427fc692037e3f7d2851959ffe40864f2db32646eeb3cef"}, - {file = "pyzmq-26.4.0-cp311-cp311-win32.whl", hash = "sha256:d56aad0517d4c09e3b4f15adebba8f6372c5102c27742a5bdbfc74a7dceb8fca"}, - {file = "pyzmq-26.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:963977ac8baed7058c1e126014f3fe58b3773f45c78cce7af5c26c09b6823896"}, - {file = "pyzmq-26.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0c8e8cadc81e44cc5088fcd53b9b3b4ce9344815f6c4a03aec653509296fae3"}, - {file = "pyzmq-26.4.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:5227cb8da4b6f68acfd48d20c588197fd67745c278827d5238c707daf579227b"}, - {file = "pyzmq-26.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1c07a7fa7f7ba86554a2b1bef198c9fed570c08ee062fd2fd6a4dcacd45f905"}, - {file = "pyzmq-26.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae775fa83f52f52de73183f7ef5395186f7105d5ed65b1ae65ba27cb1260de2b"}, - {file = "pyzmq-26.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c760d0226ebd52f1e6b644a9e839b5db1e107a23f2fcd46ec0569a4fdd4e63"}, - {file = "pyzmq-26.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ef8c6ecc1d520debc147173eaa3765d53f06cd8dbe7bd377064cdbc53ab456f5"}, - {file = "pyzmq-26.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3150ef4084e163dec29ae667b10d96aad309b668fac6810c9e8c27cf543d6e0b"}, - {file = "pyzmq-26.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4448c9e55bf8329fa1dcedd32f661bf611214fa70c8e02fee4347bc589d39a84"}, - {file = "pyzmq-26.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e07dde3647afb084d985310d067a3efa6efad0621ee10826f2cb2f9a31b89d2f"}, - {file = "pyzmq-26.4.0-cp312-cp312-win32.whl", hash = "sha256:ba034a32ecf9af72adfa5ee383ad0fd4f4e38cdb62b13624278ef768fe5b5b44"}, - {file = "pyzmq-26.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:056a97aab4064f526ecb32f4343917a4022a5d9efb6b9df990ff72e1879e40be"}, - {file = "pyzmq-26.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f23c750e485ce1eb639dbd576d27d168595908aa2d60b149e2d9e34c9df40e0"}, - {file = "pyzmq-26.4.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:c43fac689880f5174d6fc864857d1247fe5cfa22b09ed058a344ca92bf5301e3"}, - {file = "pyzmq-26.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:902aca7eba477657c5fb81c808318460328758e8367ecdd1964b6330c73cae43"}, - {file = "pyzmq-26.4.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5e48a830bfd152fe17fbdeaf99ac5271aa4122521bf0d275b6b24e52ef35eb6"}, - {file = "pyzmq-26.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31be2b6de98c824c06f5574331f805707c667dc8f60cb18580b7de078479891e"}, - {file = "pyzmq-26.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6332452034be001bbf3206ac59c0d2a7713de5f25bb38b06519fc6967b7cf771"}, - {file = "pyzmq-26.4.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:da8c0f5dd352136853e6a09b1b986ee5278dfddfebd30515e16eae425c872b30"}, - {file = "pyzmq-26.4.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f4ccc1a0a2c9806dda2a2dd118a3b7b681e448f3bb354056cad44a65169f6d86"}, - {file = "pyzmq-26.4.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1c0b5fceadbab461578daf8d1dcc918ebe7ddd2952f748cf30c7cf2de5d51101"}, - {file = "pyzmq-26.4.0-cp313-cp313-win32.whl", hash = "sha256:28e2b0ff5ba4b3dd11062d905682bad33385cfa3cc03e81abd7f0822263e6637"}, - {file = "pyzmq-26.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:23ecc9d241004c10e8b4f49d12ac064cd7000e1643343944a10df98e57bc544b"}, - {file = "pyzmq-26.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:1edb0385c7f025045d6e0f759d4d3afe43c17a3d898914ec6582e6f464203c08"}, - {file = "pyzmq-26.4.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:93a29e882b2ba1db86ba5dd5e88e18e0ac6b627026c5cfbec9983422011b82d4"}, - {file = "pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45684f276f57110bb89e4300c00f1233ca631f08f5f42528a5c408a79efc4a"}, - {file = "pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f72073e75260cb301aad4258ad6150fa7f57c719b3f498cb91e31df16784d89b"}, - {file = "pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be37e24b13026cfedd233bcbbccd8c0bcd2fdd186216094d095f60076201538d"}, - {file = "pyzmq-26.4.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:237b283044934d26f1eeff4075f751b05d2f3ed42a257fc44386d00df6a270cf"}, - {file = "pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:b30f862f6768b17040929a68432c8a8be77780317f45a353cb17e423127d250c"}, - {file = "pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:c80fcd3504232f13617c6ab501124d373e4895424e65de8b72042333316f64a8"}, - {file = "pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:26a2a7451606b87f67cdeca2c2789d86f605da08b4bd616b1a9981605ca3a364"}, - {file = "pyzmq-26.4.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:831cc53bf6068d46d942af52fa8b0b9d128fb39bcf1f80d468dc9a3ae1da5bfb"}, - {file = "pyzmq-26.4.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:51d18be6193c25bd229524cfac21e39887c8d5e0217b1857998dfbef57c070a4"}, - {file = "pyzmq-26.4.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:445c97854204119ae2232503585ebb4fa7517142f71092cb129e5ee547957a1f"}, - {file = "pyzmq-26.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:807b8f4ad3e6084412c0f3df0613269f552110fa6fb91743e3e306223dbf11a6"}, - {file = "pyzmq-26.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c01d109dd675ac47fa15c0a79d256878d898f90bc10589f808b62d021d2e653c"}, - {file = "pyzmq-26.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0a294026e28679a8dd64c922e59411cb586dad307661b4d8a5c49e7bbca37621"}, - {file = "pyzmq-26.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:22c8dd677274af8dfb1efd05006d6f68fb2f054b17066e308ae20cb3f61028cf"}, - {file = "pyzmq-26.4.0-cp38-cp38-win32.whl", hash = "sha256:14fc678b696bc42c14e2d7f86ac4e97889d5e6b94d366ebcb637a768d2ad01af"}, - {file = "pyzmq-26.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:d1ef0a536662bbbdc8525f7e2ef19e74123ec9c4578e0582ecd41aedc414a169"}, - {file = "pyzmq-26.4.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:a88643de8abd000ce99ca72056a1a2ae15881ee365ecb24dd1d9111e43d57842"}, - {file = "pyzmq-26.4.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a744ce209ecb557406fb928f3c8c55ce79b16c3eeb682da38ef5059a9af0848"}, - {file = "pyzmq-26.4.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9434540f333332224ecb02ee6278b6c6f11ea1266b48526e73c903119b2f420f"}, - {file = "pyzmq-26.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6c6f0a23e55cd38d27d4c89add963294ea091ebcb104d7fdab0f093bc5abb1c"}, - {file = "pyzmq-26.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6145df55dc2309f6ef72d70576dcd5aabb0fd373311613fe85a5e547c722b780"}, - {file = "pyzmq-26.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2ea81823840ef8c56e5d2f9918e4d571236294fea4d1842b302aebffb9e40997"}, - {file = "pyzmq-26.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc2abc385dc37835445abe206524fbc0c9e3fce87631dfaa90918a1ba8f425eb"}, - {file = "pyzmq-26.4.0-cp39-cp39-win32.whl", hash = "sha256:41a2508fe7bed4c76b4cf55aacfb8733926f59d440d9ae2b81ee8220633b4d12"}, - {file = "pyzmq-26.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:d4000e8255d6cbce38982e5622ebb90823f3409b7ffe8aeae4337ef7d6d2612a"}, - {file = "pyzmq-26.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f6919d9c120488246bdc2a2f96662fa80d67b35bd6d66218f457e722b3ff64"}, - {file = "pyzmq-26.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:98d948288ce893a2edc5ec3c438fe8de2daa5bbbd6e2e865ec5f966e237084ba"}, - {file = "pyzmq-26.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9f34f5c9e0203ece706a1003f1492a56c06c0632d86cb77bcfe77b56aacf27b"}, - {file = "pyzmq-26.4.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80c9b48aef586ff8b698359ce22f9508937c799cc1d2c9c2f7c95996f2300c94"}, - {file = "pyzmq-26.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3f2a5b74009fd50b53b26f65daff23e9853e79aa86e0aa08a53a7628d92d44a"}, - {file = "pyzmq-26.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:61c5f93d7622d84cb3092d7f6398ffc77654c346545313a3737e266fc11a3beb"}, - {file = "pyzmq-26.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4478b14cb54a805088299c25a79f27eaf530564a7a4f72bf432a040042b554eb"}, - {file = "pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a28ac29c60e4ba84b5f58605ace8ad495414a724fe7aceb7cf06cd0598d04e1"}, - {file = "pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b03c1ceea27c6520124f4fb2ba9c647409b9abdf9a62388117148a90419494"}, - {file = "pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7731abd23a782851426d4e37deb2057bf9410848a4459b5ede4fe89342e687a9"}, - {file = "pyzmq-26.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a222ad02fbe80166b0526c038776e8042cd4e5f0dec1489a006a1df47e9040e0"}, - {file = "pyzmq-26.4.0-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:91c3ffaea475ec8bb1a32d77ebc441dcdd13cd3c4c284a6672b92a0f5ade1917"}, - {file = "pyzmq-26.4.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d9a78a52668bf5c9e7b0da36aa5760a9fc3680144e1445d68e98df78a25082ed"}, - {file = "pyzmq-26.4.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b70cab356ff8c860118b89dc86cd910c73ce2127eb986dada4fbac399ef644cf"}, - {file = "pyzmq-26.4.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acae207d4387780838192326b32d373bb286da0b299e733860e96f80728eb0af"}, - {file = "pyzmq-26.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f928eafd15794aa4be75463d537348b35503c1e014c5b663f206504ec1a90fe4"}, - {file = "pyzmq-26.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:552b0d2e39987733e1e9e948a0ced6ff75e0ea39ab1a1db2fc36eb60fd8760db"}, - {file = "pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd670a8aa843f2ee637039bbd412e0d7294a5e588e1ecc9ad98b0cdc050259a4"}, - {file = "pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d367b7b775a0e1e54a59a2ba3ed4d5e0a31566af97cc9154e34262777dab95ed"}, - {file = "pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112af16c406e4a93df2caef49f884f4c2bb2b558b0b5577ef0b2465d15c1abc"}, - {file = "pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c76c298683f82669cab0b6da59071f55238c039738297c69f187a542c6d40099"}, - {file = "pyzmq-26.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:49b6ca2e625b46f499fb081aaf7819a177f41eeb555acb05758aa97f4f95d147"}, - {file = "pyzmq-26.4.0.tar.gz", hash = "sha256:4bd13f85f80962f91a651a7356fe0472791a5f7a92f227822b5acf44795c626d"}, + {file = "pyzmq-27.0.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:b973ee650e8f442ce482c1d99ca7ab537c69098d53a3d046676a484fd710c87a"}, + {file = "pyzmq-27.0.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:661942bc7cd0223d569d808f2e5696d9cc120acc73bf3e88a1f1be7ab648a7e4"}, + {file = "pyzmq-27.0.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50360fb2a056ffd16e5f4177eee67f1dd1017332ea53fb095fe7b5bf29c70246"}, + {file = "pyzmq-27.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf209a6dc4b420ed32a7093642843cbf8703ed0a7d86c16c0b98af46762ebefb"}, + {file = "pyzmq-27.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c2dace4a7041cca2fba5357a2d7c97c5effdf52f63a1ef252cfa496875a3762d"}, + {file = "pyzmq-27.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:63af72b2955fc77caf0a77444baa2431fcabb4370219da38e1a9f8d12aaebe28"}, + {file = "pyzmq-27.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e8c4adce8e37e75c4215297d7745551b8dcfa5f728f23ce09bf4e678a9399413"}, + {file = "pyzmq-27.0.0-cp310-cp310-win32.whl", hash = "sha256:5d5ef4718ecab24f785794e0e7536436698b459bfbc19a1650ef55280119d93b"}, + {file = "pyzmq-27.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:e40609380480b3d12c30f841323f42451c755b8fece84235236f5fe5ffca8c1c"}, + {file = "pyzmq-27.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:6b0397b0be277b46762956f576e04dc06ced265759e8c2ff41a0ee1aa0064198"}, + {file = "pyzmq-27.0.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:21457825249b2a53834fa969c69713f8b5a79583689387a5e7aed880963ac564"}, + {file = "pyzmq-27.0.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1958947983fef513e6e98eff9cb487b60bf14f588dc0e6bf35fa13751d2c8251"}, + {file = "pyzmq-27.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0dc628b5493f9a8cd9844b8bee9732ef587ab00002157c9329e4fc0ef4d3afa"}, + {file = "pyzmq-27.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7bbe9e1ed2c8d3da736a15694d87c12493e54cc9dc9790796f0321794bbc91f"}, + {file = "pyzmq-27.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dc1091f59143b471d19eb64f54bae4f54bcf2a466ffb66fe45d94d8d734eb495"}, + {file = "pyzmq-27.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7011ade88c8e535cf140f8d1a59428676fbbce7c6e54fefce58bf117aefb6667"}, + {file = "pyzmq-27.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c386339d7e3f064213aede5d03d054b237937fbca6dd2197ac8cf3b25a6b14e"}, + {file = "pyzmq-27.0.0-cp311-cp311-win32.whl", hash = "sha256:0546a720c1f407b2172cb04b6b094a78773491497e3644863cf5c96c42df8cff"}, + {file = "pyzmq-27.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:15f39d50bd6c9091c67315ceb878a4f531957b121d2a05ebd077eb35ddc5efed"}, + {file = "pyzmq-27.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c5817641eebb391a2268c27fecd4162448e03538387093cdbd8bf3510c316b38"}, + {file = "pyzmq-27.0.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:cbabc59dcfaac66655c040dfcb8118f133fb5dde185e5fc152628354c1598e52"}, + {file = "pyzmq-27.0.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:cb0ac5179cba4b2f94f1aa208fbb77b62c4c9bf24dd446278b8b602cf85fcda3"}, + {file = "pyzmq-27.0.0-cp312-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53a48f0228eab6cbf69fde3aa3c03cbe04e50e623ef92ae395fce47ef8a76152"}, + {file = "pyzmq-27.0.0-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:111db5f395e09f7e775f759d598f43cb815fc58e0147623c4816486e1a39dc22"}, + {file = "pyzmq-27.0.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c8878011653dcdc27cc2c57e04ff96f0471e797f5c19ac3d7813a245bcb24371"}, + {file = "pyzmq-27.0.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:c0ed2c1f335ba55b5fdc964622254917d6b782311c50e138863eda409fbb3b6d"}, + {file = "pyzmq-27.0.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e918d70862d4cfd4b1c187310015646a14e1f5917922ab45b29f28f345eeb6be"}, + {file = "pyzmq-27.0.0-cp312-abi3-win32.whl", hash = "sha256:88b4e43cab04c3c0f0d55df3b1eef62df2b629a1a369b5289a58f6fa8b07c4f4"}, + {file = "pyzmq-27.0.0-cp312-abi3-win_amd64.whl", hash = "sha256:dce4199bf5f648a902ce37e7b3afa286f305cd2ef7a8b6ec907470ccb6c8b371"}, + {file = "pyzmq-27.0.0-cp312-abi3-win_arm64.whl", hash = "sha256:56e46bbb85d52c1072b3f809cc1ce77251d560bc036d3a312b96db1afe76db2e"}, + {file = "pyzmq-27.0.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c36ad534c0c29b4afa088dc53543c525b23c0797e01b69fef59b1a9c0e38b688"}, + {file = "pyzmq-27.0.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:67855c14173aec36395d7777aaba3cc527b393821f30143fd20b98e1ff31fd38"}, + {file = "pyzmq-27.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8617c7d43cd8ccdb62aebe984bfed77ca8f036e6c3e46dd3dddda64b10f0ab7a"}, + {file = "pyzmq-27.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:67bfbcbd0a04c575e8103a6061d03e393d9f80ffdb9beb3189261e9e9bc5d5e9"}, + {file = "pyzmq-27.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5cd11d46d7b7e5958121b3eaf4cd8638eff3a720ec527692132f05a57f14341d"}, + {file = "pyzmq-27.0.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:b801c2e40c5aa6072c2f4876de8dccd100af6d9918d4d0d7aa54a1d982fd4f44"}, + {file = "pyzmq-27.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:20d5cb29e8c5f76a127c75b6e7a77e846bc4b655c373baa098c26a61b7ecd0ef"}, + {file = "pyzmq-27.0.0-cp313-cp313t-win32.whl", hash = "sha256:a20528da85c7ac7a19b7384e8c3f8fa707841fd85afc4ed56eda59d93e3d98ad"}, + {file = "pyzmq-27.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d8229f2efece6a660ee211d74d91dbc2a76b95544d46c74c615e491900dc107f"}, + {file = "pyzmq-27.0.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:f4162dbbd9c5c84fb930a36f290b08c93e35fce020d768a16fc8891a2f72bab8"}, + {file = "pyzmq-27.0.0-cp38-cp38-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4e7d0a8d460fba526cc047333bdcbf172a159b8bd6be8c3eb63a416ff9ba1477"}, + {file = "pyzmq-27.0.0-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:29f44e3c26b9783816ba9ce274110435d8f5b19bbd82f7a6c7612bb1452a3597"}, + {file = "pyzmq-27.0.0-cp38-cp38-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e435540fa1da54667f0026cf1e8407fe6d8a11f1010b7f06b0b17214ebfcf5e"}, + {file = "pyzmq-27.0.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:51f5726de3532b8222e569990c8aa34664faa97038304644679a51d906e60c6e"}, + {file = "pyzmq-27.0.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:42c7555123679637c99205b1aa9e8f7d90fe29d4c243c719e347d4852545216c"}, + {file = "pyzmq-27.0.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a979b7cf9e33d86c4949df527a3018767e5f53bc3b02adf14d4d8db1db63ccc0"}, + {file = "pyzmq-27.0.0-cp38-cp38-win32.whl", hash = "sha256:26b72c5ae20bf59061c3570db835edb81d1e0706ff141747055591c4b41193f8"}, + {file = "pyzmq-27.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:55a0155b148fe0428285a30922f7213539aa84329a5ad828bca4bbbc665c70a4"}, + {file = "pyzmq-27.0.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:100f6e5052ba42b2533011d34a018a5ace34f8cac67cb03cfa37c8bdae0ca617"}, + {file = "pyzmq-27.0.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:bf6c6b061efd00404b9750e2cfbd9507492c8d4b3721ded76cb03786131be2ed"}, + {file = "pyzmq-27.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee05728c0b0b2484a9fc20466fa776fffb65d95f7317a3419985b8c908563861"}, + {file = "pyzmq-27.0.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7cdf07fe0a557b131366f80727ec8ccc4b70d89f1e3f920d94a594d598d754f0"}, + {file = "pyzmq-27.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:90252fa2ff3a104219db1f5ced7032a7b5fc82d7c8d2fec2b9a3e6fd4e25576b"}, + {file = "pyzmq-27.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ea6d441c513bf18c578c73c323acf7b4184507fc244762193aa3a871333c9045"}, + {file = "pyzmq-27.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ae2b34bcfaae20c064948a4113bf8709eee89fd08317eb293ae4ebd69b4d9740"}, + {file = "pyzmq-27.0.0-cp39-cp39-win32.whl", hash = "sha256:5b10bd6f008937705cf6e7bf8b6ece5ca055991e3eb130bca8023e20b86aa9a3"}, + {file = "pyzmq-27.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:00387d12a8af4b24883895f7e6b9495dc20a66027b696536edac35cb988c38f3"}, + {file = "pyzmq-27.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:4c19d39c04c29a6619adfeb19e3735c421b3bfee082f320662f52e59c47202ba"}, + {file = "pyzmq-27.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:656c1866505a5735d0660b7da6d7147174bbf59d4975fc2b7f09f43c9bc25745"}, + {file = "pyzmq-27.0.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:74175b9e12779382432dd1d1f5960ebe7465d36649b98a06c6b26be24d173fab"}, + {file = "pyzmq-27.0.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8c6de908465697a8708e4d6843a1e884f567962fc61eb1706856545141d0cbb"}, + {file = "pyzmq-27.0.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c644aaacc01d0df5c7072826df45e67301f191c55f68d7b2916d83a9ddc1b551"}, + {file = "pyzmq-27.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:10f70c1d9a446a85013a36871a296007f6fe4232b530aa254baf9da3f8328bc0"}, + {file = "pyzmq-27.0.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd1dc59763effd1576f8368047c9c31468fce0af89d76b5067641137506792ae"}, + {file = "pyzmq-27.0.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:60e8cc82d968174650c1860d7b716366caab9973787a1c060cf8043130f7d0f7"}, + {file = "pyzmq-27.0.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14fe7aaac86e4e93ea779a821967360c781d7ac5115b3f1a171ced77065a0174"}, + {file = "pyzmq-27.0.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6ad0562d4e6abb785be3e4dd68599c41be821b521da38c402bc9ab2a8e7ebc7e"}, + {file = "pyzmq-27.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:9df43a2459cd3a3563404c1456b2c4c69564daa7dbaf15724c09821a3329ce46"}, + {file = "pyzmq-27.0.0-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8c86ea8fe85e2eb0ffa00b53192c401477d5252f6dd1db2e2ed21c1c30d17e5e"}, + {file = "pyzmq-27.0.0-pp38-pypy38_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:c45fee3968834cd291a13da5fac128b696c9592a9493a0f7ce0b47fa03cc574d"}, + {file = "pyzmq-27.0.0-pp38-pypy38_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cae73bb6898c4e045fbed5024cb587e4110fddb66f6163bcab5f81f9d4b9c496"}, + {file = "pyzmq-27.0.0-pp38-pypy38_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:26d542258c7a1f35a9cff3d887687d3235006134b0ac1c62a6fe1ad3ac10440e"}, + {file = "pyzmq-27.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:04cd50ef3b28e35ced65740fb9956a5b3f77a6ff32fcd887e3210433f437dd0f"}, + {file = "pyzmq-27.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:39ddd3ba0a641f01d8f13a3cfd4c4924eb58e660d8afe87e9061d6e8ca6f7ac3"}, + {file = "pyzmq-27.0.0-pp39-pypy39_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:8ca7e6a0388dd9e1180b14728051068f4efe83e0d2de058b5ff92c63f399a73f"}, + {file = "pyzmq-27.0.0-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2524c40891be6a3106885a3935d58452dd83eb7a5742a33cc780a1ad4c49dec0"}, + {file = "pyzmq-27.0.0-pp39-pypy39_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a56e3e5bd2d62a01744fd2f1ce21d760c7c65f030e9522738d75932a14ab62a"}, + {file = "pyzmq-27.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:096af9e133fec3a72108ddefba1e42985cb3639e9de52cfd336b6fc23aa083e9"}, + {file = "pyzmq-27.0.0.tar.gz", hash = "sha256:b1f08eeb9ce1510e6939b6e5dcd46a17765e2333daae78ecf4606808442e52cf"}, ] [package.dependencies] @@ -8154,13 +8138,13 @@ files = [ [[package]] name = "sentry-sdk" -version = "2.29.1" +version = "2.30.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = ">=3.6" files = [ - {file = "sentry_sdk-2.29.1-py2.py3-none-any.whl", hash = "sha256:90862fe0616ded4572da6c9dadb363121a1ae49a49e21c418f0634e9d10b4c19"}, - {file = "sentry_sdk-2.29.1.tar.gz", hash = "sha256:8d4a0206b95fa5fe85e5e7517ed662e3888374bdc342c00e435e10e6d831aa6d"}, + {file = "sentry_sdk-2.30.0-py2.py3-none-any.whl", hash = "sha256:59391db1550662f746ea09b483806a631c3ae38d6340804a1a4c0605044f6877"}, + {file = "sentry_sdk-2.30.0.tar.gz", hash = "sha256:436369b02afef7430efb10300a344fb61a11fe6db41c2b11f41ee037d2dd7f45"}, ] [package.dependencies] @@ -8350,27 +8334,26 @@ files = [ [[package]] name = "simpleitk" -version = "2.5.0" +version = "2.5.2" description = "SimpleITK is a simplified interface to the Insight Toolkit (ITK) for image registration and segmentation" optional = false python-versions = "*" files = [ - {file = "simpleitk-2.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3e53bdc12949b34edf6be1526bc63d6a0b81930ffb7430d77cff427052df04e8"}, - {file = "simpleitk-2.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d34ada59c6944a53e581a7bebe97980c1401f3a5ad86cf67dd96d26e70d152a2"}, - {file = "simpleitk-2.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e854b76ddd4d77b3b57cf5d8d5d5ddcbe49f147a0108cd22391766d108b3203f"}, - {file = "simpleitk-2.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f090a4ece132d6b83613c91b6631a1dc7d7530232092231cac5dec2f3e52b35d"}, - {file = "simpleitk-2.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:093020866c2486ae208663ed057f7535616c97e9478e04a7ce53924fa7613195"}, - {file = "simpleitk-2.5.0-cp311-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c4039e136fa7aee3367c0c3380721ae423ed99995ee7d1a16cbba8ca1f741589"}, - {file = "simpleitk-2.5.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:9039e1bc7988594ef73cfa911925eee1a647d1ced81613565607bc56b16a7fbc"}, - {file = "simpleitk-2.5.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a96296e33661a8911d4f73d14a8d26eed387beb681327706bbe7e4a53893d48"}, - {file = "simpleitk-2.5.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5bb0e50e9fa5e3102d8bd7f243376743592bc25ecdd5aa738f63e6bfb11dd76"}, - {file = "simpleitk-2.5.0-cp311-abi3-win_amd64.whl", hash = "sha256:348ee9ae6b81ea9173701f726308ac2dac2ff22dcae7afdbdf67d037c0bd431f"}, - {file = "simpleitk-2.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db6d28785de2a97f3bc71fdecb6ef65894f7378c63c64e1791085b30d127a222"}, - {file = "simpleitk-2.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e1660d054d01d2b422f05b36b0d1c8c3d458ee5c86d7833169c73b791951b1c5"}, - {file = "simpleitk-2.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c73dbd1786042ad2667004bc3e24e847cdec11e23ee0635f438cafcb98dc0e5a"}, - {file = "simpleitk-2.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80ac24ced63a6e66fc14b988f74a31a1472918aca2aa8901b285bd6f4788003d"}, - {file = "simpleitk-2.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:0883784ea6a01985adec39847a73a2acee0fc985ab0300ae4d4b85ea753b06ef"}, - {file = "simpleitk-2.5.0.tar.gz", hash = "sha256:222546fc8ddef6a9e2cfae95253bfc4b77f7b574dfd292194f221685bd5a2ae0"}, + {file = "simpleitk-2.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ddfae02f6fc67829fcf13cffb22e8ff9d5cc79d6453d546c9937c1abafa7d257"}, + {file = "simpleitk-2.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76ec652d30762adfedb167fc8fe3918099f0b87397c7656a0bab1ba21cb2dfcd"}, + {file = "simpleitk-2.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:99739c3238539ff5f342f5a5eec2960c778c122e56f893c3dbf889cacc500bd7"}, + {file = "simpleitk-2.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9e15f7daff43ff85268f4ef48e97dcb1fb4285608811cad36cec739407581104"}, + {file = "simpleitk-2.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:637d92aecc5e2de6b424ed8866ef0415dd71b6256cd12618be68f1b510c0a256"}, + {file = "simpleitk-2.5.2-cp311-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c0227e1365472dd2bd60ea265d016bb82c5b245ad6ab3fcf56ab9be052e3fd22"}, + {file = "simpleitk-2.5.2-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:d91b602a13554ae0a1961076166bd0243c7a0ccbd9c1589ba5afbe8554409371"}, + {file = "simpleitk-2.5.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ce7ef67d22f15632c7f2bbec9d5b40fb18889289df4d04337e2b4021e7e5b444"}, + {file = "simpleitk-2.5.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4a673bdb4ccb7609905f4700fd242591c2273b2ba1604490d79d530661210942"}, + {file = "simpleitk-2.5.2-cp311-abi3-win_amd64.whl", hash = "sha256:c3884c377c2c5053e15460c6f724a8efe71f4c4ff780418030711b1234114036"}, + {file = "simpleitk-2.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e29a6b3cb02b43358514c39578afe66f64f2cfe59adc983922a324f44b6c0a59"}, + {file = "simpleitk-2.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f477ada24a384775fdecc510238e5ca1e37897976fc5ae1f17f082ca654d5cfa"}, + {file = "simpleitk-2.5.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5411273aec1a9768ccb2ab396d20ca9bdb953603734f3cb62ade5e885eb75ec8"}, + {file = "simpleitk-2.5.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07686eb40cdc2febdb31bd8efba7655fd1ff4b7faa3b99569b43498f4ea9f5f9"}, + {file = "simpleitk-2.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:a1054627f92b2a23c788facc8d4bf47eea9bb5e5b92aed6d5b9fa6652a63e31e"}, ] [[package]] @@ -9394,13 +9377,13 @@ files = [ [[package]] name = "torchmetrics" -version = "1.7.2" +version = "1.7.3" description = "PyTorch native Metrics" optional = false python-versions = ">=3.9" files = [ - {file = "torchmetrics-1.7.2-py3-none-any.whl", hash = "sha256:9cc3bff07a715fcb37fb04d2a1a5ae36267c36066c097578020056653a94f2a8"}, - {file = "torchmetrics-1.7.2.tar.gz", hash = "sha256:ba401cd01aeaa268e809c0e4f42ef8f95669bf9b485e1d93d54dc765e012338a"}, + {file = "torchmetrics-1.7.3-py3-none-any.whl", hash = "sha256:7b6fd43e92f0a1071c8bcb029637f252b0630699140a93ed8817ce7afe9db34e"}, + {file = "torchmetrics-1.7.3.tar.gz", hash = "sha256:08450a19cdb67ba1608aac0b213e5dc73033e11b60ad4719696ebcede591621e"}, ] [package.dependencies] @@ -9410,15 +9393,15 @@ packaging = ">17.1" torch = ">=2.0.0" [package.extras] -all = ["SciencePlots (>=2.0.0)", "einops (>=0.7.0)", "gammatone (>=1.0.0)", "ipadic (>=1.0.0)", "librosa (>=0.10.0)", "matplotlib (>=3.6.0)", "mecab-python3 (>=1.0.6)", "mypy (==1.15.0)", "nltk (>3.8.1)", "onnxruntime (>=1.12.0)", "pesq (>=0.0.4)", "piq (<=0.8.0)", "pycocotools (>2.0.0)", "pystoi (>=0.4.0)", "regex (>=2021.9.24)", "requests (>=2.19.0)", "scipy (>1.0.0)", "sentencepiece (>=0.2.0)", "timm (>=0.9.0)", "torch (==2.7.0)", "torch-fidelity (<=0.4.0)", "torch_linear_assignment (>=0.0.2)", "torchaudio (>=2.0.1)", "torchvision (>=0.15.1)", "torchvision (>=0.15.1)", "tqdm (<4.68.0)", "transformers (>4.4.0)", "transformers (>=4.42.3)", "types-PyYAML", "types-emoji", "types-protobuf", "types-requests", "types-setuptools", "types-six", "types-tabulate"] +all = ["SciencePlots (>=2.0.0)", "einops (>=0.7.0)", "gammatone (>=1.0.0)", "ipadic (>=1.0.0)", "librosa (>=0.10.0)", "matplotlib (>=3.6.0)", "mecab-python3 (>=1.0.6)", "mypy (==1.16.0)", "nltk (>3.8.1)", "onnxruntime (>=1.12.0)", "pesq (>=0.0.4)", "piq (<=0.8.0)", "pycocotools (>2.0.0)", "pystoi (>=0.4.0)", "regex (>=2021.9.24)", "requests (>=2.19.0)", "scipy (>1.0.0)", "sentencepiece (>=0.2.0)", "timm (>=0.9.0)", "torch (==2.7.1)", "torch-fidelity (<=0.4.0)", "torch_linear_assignment (>=0.0.2)", "torchaudio (>=2.0.1)", "torchvision (>=0.15.1)", "torchvision (>=0.15.1)", "tqdm (<4.68.0)", "transformers (>4.4.0)", "transformers (>=4.42.3)", "types-PyYAML", "types-emoji", "types-protobuf", "types-requests", "types-setuptools", "types-six", "types-tabulate"] audio = ["gammatone (>=1.0.0)", "librosa (>=0.10.0)", "onnxruntime (>=1.12.0)", "pesq (>=0.0.4)", "pystoi (>=0.4.0)", "requests (>=2.19.0)", "torchaudio (>=2.0.1)"] clustering = ["torch_linear_assignment (>=0.0.2)"] detection = ["pycocotools (>2.0.0)", "torchvision (>=0.15.1)"] -dev = ["PyTDC (==0.4.1)", "SciencePlots (>=2.0.0)", "aeon (>=1.0.0)", "bert_score (==0.3.13)", "dists-pytorch (==0.1)", "dython (==0.7.9)", "einops (>=0.7.0)", "fairlearn", "fast-bss-eval (>=0.1.0)", "faster-coco-eval (>=1.6.3)", "gammatone (>=1.0.0)", "huggingface-hub (<0.33)", "ipadic (>=1.0.0)", "jiwer (>=2.3.0)", "kornia (>=0.6.7)", "librosa (>=0.10.0)", "lpips (<=0.1.4)", "matplotlib (>=3.6.0)", "mecab-ko (>=1.0.0,<1.1.0)", "mecab-ko-dic (>=1.0.0)", "mecab-python3 (>=1.0.6)", "mir-eval (>=0.6)", "monai (==1.4.0)", "mypy (==1.15.0)", "netcal (>1.0.0)", "nltk (>3.8.1)", "numpy (<2.3.0)", "onnxruntime (>=1.12.0)", "pandas (>1.4.0)", "permetrics (==2.0.0)", "pesq (>=0.0.4)", "piq (<=0.8.0)", "pycocotools (>2.0.0)", "pystoi (>=0.4.0)", "pytorch-msssim (==1.0.0)", "regex (>=2021.9.24)", "requests (>=2.19.0)", "rouge-score (>0.1.0)", "sacrebleu (>=2.3.0)", "scikit-image (>=0.19.0)", "scipy (>1.0.0)", "scipy (>1.0.0)", "sentencepiece (>=0.2.0)", "sewar (>=0.4.4)", "statsmodels (>0.13.5)", "timm (>=0.9.0)", "torch (==2.7.0)", "torch-fidelity (<=0.4.0)", "torch_complex (<0.5.0)", "torch_linear_assignment (>=0.0.2)", "torchaudio (>=2.0.1)", "torchvision (>=0.15.1)", "torchvision (>=0.15.1)", "tqdm (<4.68.0)", "transformers (>4.4.0)", "transformers (>=4.42.3)", "types-PyYAML", "types-emoji", "types-protobuf", "types-requests", "types-setuptools", "types-six", "types-tabulate"] +dev = ["PyTDC (==0.4.1)", "SciencePlots (>=2.0.0)", "aeon (>=1.0.0)", "bert_score (==0.3.13)", "dists-pytorch (==0.1)", "dython (==0.7.9)", "einops (>=0.7.0)", "fairlearn", "fast-bss-eval (>=0.1.0)", "faster-coco-eval (>=1.6.3)", "gammatone (>=1.0.0)", "huggingface-hub (<0.33)", "ipadic (>=1.0.0)", "jiwer (>=2.3.0)", "kornia (>=0.6.7)", "librosa (>=0.10.0)", "lpips (<=0.1.4)", "matplotlib (>=3.6.0)", "mecab-ko (>=1.0.0,<1.1.0)", "mecab-ko-dic (>=1.0.0)", "mecab-python3 (>=1.0.6)", "mir-eval (>=0.6)", "monai (==1.4.0)", "mypy (==1.16.0)", "netcal (>1.0.0)", "nltk (>3.8.1)", "numpy (<2.3.0)", "onnxruntime (>=1.12.0)", "pandas (>1.4.0)", "permetrics (==2.0.0)", "pesq (>=0.0.4)", "piq (<=0.8.0)", "pycocotools (>2.0.0)", "pystoi (>=0.4.0)", "pytorch-msssim (==1.0.0)", "regex (>=2021.9.24)", "requests (>=2.19.0)", "rouge-score (>0.1.0)", "sacrebleu (>=2.3.0)", "scikit-image (>=0.19.0)", "scipy (>1.0.0)", "scipy (>1.0.0)", "sentencepiece (>=0.2.0)", "sewar (>=0.4.4)", "statsmodels (>0.13.5)", "timm (>=0.9.0)", "torch (==2.7.1)", "torch-fidelity (<=0.4.0)", "torch_complex (<0.5.0)", "torch_linear_assignment (>=0.0.2)", "torchaudio (>=2.0.1)", "torchvision (>=0.15.1)", "torchvision (>=0.15.1)", "tqdm (<4.68.0)", "transformers (>4.4.0)", "transformers (>=4.42.3)", "types-PyYAML", "types-emoji", "types-protobuf", "types-requests", "types-setuptools", "types-six", "types-tabulate"] image = ["scipy (>1.0.0)", "torch-fidelity (<=0.4.0)", "torchvision (>=0.15.1)"] multimodal = ["einops (>=0.7.0)", "piq (<=0.8.0)", "timm (>=0.9.0)", "transformers (>=4.42.3)"] text = ["ipadic (>=1.0.0)", "mecab-python3 (>=1.0.6)", "nltk (>3.8.1)", "regex (>=2021.9.24)", "sentencepiece (>=0.2.0)", "tqdm (<4.68.0)", "transformers (>4.4.0)"] -typing = ["mypy (==1.15.0)", "torch (==2.7.0)", "types-PyYAML", "types-emoji", "types-protobuf", "types-requests", "types-setuptools", "types-six", "types-tabulate"] +typing = ["mypy (==1.16.0)", "torch (==2.7.1)", "types-PyYAML", "types-emoji", "types-protobuf", "types-requests", "types-setuptools", "types-six", "types-tabulate"] visual = ["SciencePlots (>=2.0.0)", "matplotlib (>=3.6.0)"] [[package]] @@ -9778,13 +9761,13 @@ files = [ [[package]] name = "types-requests" -version = "2.32.0.20250602" +version = "2.32.4.20250611" description = "Typing stubs for requests" optional = false python-versions = ">=3.9" files = [ - {file = "types_requests-2.32.0.20250602-py3-none-any.whl", hash = "sha256:f4f335f87779b47ce10b8b8597b409130299f6971ead27fead4fe7ba6ea3e726"}, - {file = "types_requests-2.32.0.20250602.tar.gz", hash = "sha256:ee603aeefec42051195ae62ca7667cd909a2f8128fdf8aad9e8a5219ecfab3bf"}, + {file = "types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072"}, + {file = "types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826"}, ] [package.dependencies] @@ -9892,13 +9875,13 @@ files = [ [[package]] name = "urllib3" -version = "2.4.0" +version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" files = [ - {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, - {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] @@ -10009,82 +9992,117 @@ workspaces = ["wandb-workspaces"] [[package]] name = "watchfiles" -version = "1.0.5" +version = "1.1.0" description = "Simple, modern and high performance file watching and code reload in python." optional = false python-versions = ">=3.9" files = [ - {file = "watchfiles-1.0.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5c40fe7dd9e5f81e0847b1ea64e1f5dd79dd61afbedb57759df06767ac719b40"}, - {file = "watchfiles-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c0db396e6003d99bb2d7232c957b5f0b5634bbd1b24e381a5afcc880f7373fb"}, - {file = "watchfiles-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b551d4fb482fc57d852b4541f911ba28957d051c8776e79c3b4a51eb5e2a1b11"}, - {file = "watchfiles-1.0.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:830aa432ba5c491d52a15b51526c29e4a4b92bf4f92253787f9726fe01519487"}, - {file = "watchfiles-1.0.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a16512051a822a416b0d477d5f8c0e67b67c1a20d9acecb0aafa3aa4d6e7d256"}, - {file = "watchfiles-1.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe0cbc787770e52a96c6fda6726ace75be7f840cb327e1b08d7d54eadc3bc85"}, - {file = "watchfiles-1.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d363152c5e16b29d66cbde8fa614f9e313e6f94a8204eaab268db52231fe5358"}, - {file = "watchfiles-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ee32c9a9bee4d0b7bd7cbeb53cb185cf0b622ac761efaa2eba84006c3b3a614"}, - {file = "watchfiles-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29c7fd632ccaf5517c16a5188e36f6612d6472ccf55382db6c7fe3fcccb7f59f"}, - {file = "watchfiles-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e637810586e6fe380c8bc1b3910accd7f1d3a9a7262c8a78d4c8fb3ba6a2b3d"}, - {file = "watchfiles-1.0.5-cp310-cp310-win32.whl", hash = "sha256:cd47d063fbeabd4c6cae1d4bcaa38f0902f8dc5ed168072874ea11d0c7afc1ff"}, - {file = "watchfiles-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:86c0df05b47a79d80351cd179893f2f9c1b1cae49d96e8b3290c7f4bd0ca0a92"}, - {file = "watchfiles-1.0.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:237f9be419e977a0f8f6b2e7b0475ababe78ff1ab06822df95d914a945eac827"}, - {file = "watchfiles-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0da39ff917af8b27a4bdc5a97ac577552a38aac0d260a859c1517ea3dc1a7c4"}, - {file = "watchfiles-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cfcb3952350e95603f232a7a15f6c5f86c5375e46f0bd4ae70d43e3e063c13d"}, - {file = "watchfiles-1.0.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68b2dddba7a4e6151384e252a5632efcaa9bc5d1c4b567f3cb621306b2ca9f63"}, - {file = "watchfiles-1.0.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95cf944fcfc394c5f9de794ce581914900f82ff1f855326f25ebcf24d5397418"}, - {file = "watchfiles-1.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf6cd9f83d7c023b1aba15d13f705ca7b7d38675c121f3cc4a6e25bd0857ee9"}, - {file = "watchfiles-1.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:852de68acd6212cd6d33edf21e6f9e56e5d98c6add46f48244bd479d97c967c6"}, - {file = "watchfiles-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5730f3aa35e646103b53389d5bc77edfbf578ab6dab2e005142b5b80a35ef25"}, - {file = "watchfiles-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:18b3bd29954bc4abeeb4e9d9cf0b30227f0f206c86657674f544cb032296acd5"}, - {file = "watchfiles-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ba5552a1b07c8edbf197055bc9d518b8f0d98a1c6a73a293bc0726dce068ed01"}, - {file = "watchfiles-1.0.5-cp311-cp311-win32.whl", hash = "sha256:2f1fefb2e90e89959447bc0420fddd1e76f625784340d64a2f7d5983ef9ad246"}, - {file = "watchfiles-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:b6e76ceb1dd18c8e29c73f47d41866972e891fc4cc7ba014f487def72c1cf096"}, - {file = "watchfiles-1.0.5-cp311-cp311-win_arm64.whl", hash = "sha256:266710eb6fddc1f5e51843c70e3bebfb0f5e77cf4f27129278c70554104d19ed"}, - {file = "watchfiles-1.0.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b5eb568c2aa6018e26da9e6c86f3ec3fd958cee7f0311b35c2630fa4217d17f2"}, - {file = "watchfiles-1.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0a04059f4923ce4e856b4b4e5e783a70f49d9663d22a4c3b3298165996d1377f"}, - {file = "watchfiles-1.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e380c89983ce6e6fe2dd1e1921b9952fb4e6da882931abd1824c092ed495dec"}, - {file = "watchfiles-1.0.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fe43139b2c0fdc4a14d4f8d5b5d967f7a2777fd3d38ecf5b1ec669b0d7e43c21"}, - {file = "watchfiles-1.0.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee0822ce1b8a14fe5a066f93edd20aada932acfe348bede8aa2149f1a4489512"}, - {file = "watchfiles-1.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a0dbcb1c2d8f2ab6e0a81c6699b236932bd264d4cef1ac475858d16c403de74d"}, - {file = "watchfiles-1.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a2014a2b18ad3ca53b1f6c23f8cd94a18ce930c1837bd891262c182640eb40a6"}, - {file = "watchfiles-1.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f6ae86d5cb647bf58f9f655fcf577f713915a5d69057a0371bc257e2553234"}, - {file = "watchfiles-1.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1a7bac2bde1d661fb31f4d4e8e539e178774b76db3c2c17c4bb3e960a5de07a2"}, - {file = "watchfiles-1.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ab626da2fc1ac277bbf752446470b367f84b50295264d2d313e28dc4405d663"}, - {file = "watchfiles-1.0.5-cp312-cp312-win32.whl", hash = "sha256:9f4571a783914feda92018ef3901dab8caf5b029325b5fe4558c074582815249"}, - {file = "watchfiles-1.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:360a398c3a19672cf93527f7e8d8b60d8275119c5d900f2e184d32483117a705"}, - {file = "watchfiles-1.0.5-cp312-cp312-win_arm64.whl", hash = "sha256:1a2902ede862969077b97523987c38db28abbe09fb19866e711485d9fbf0d417"}, - {file = "watchfiles-1.0.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0b289572c33a0deae62daa57e44a25b99b783e5f7aed81b314232b3d3c81a11d"}, - {file = "watchfiles-1.0.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a056c2f692d65bf1e99c41045e3bdcaea3cb9e6b5a53dcaf60a5f3bd95fc9763"}, - {file = "watchfiles-1.0.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9dca99744991fc9850d18015c4f0438865414e50069670f5f7eee08340d8b40"}, - {file = "watchfiles-1.0.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:894342d61d355446d02cd3988a7326af344143eb33a2fd5d38482a92072d9563"}, - {file = "watchfiles-1.0.5-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab44e1580924d1ffd7b3938e02716d5ad190441965138b4aa1d1f31ea0877f04"}, - {file = "watchfiles-1.0.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6f9367b132078b2ceb8d066ff6c93a970a18c3029cea37bfd7b2d3dd2e5db8f"}, - {file = "watchfiles-1.0.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2e55a9b162e06e3f862fb61e399fe9f05d908d019d87bf5b496a04ef18a970a"}, - {file = "watchfiles-1.0.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0125f91f70e0732a9f8ee01e49515c35d38ba48db507a50c5bdcad9503af5827"}, - {file = "watchfiles-1.0.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:13bb21f8ba3248386337c9fa51c528868e6c34a707f729ab041c846d52a0c69a"}, - {file = "watchfiles-1.0.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:839ebd0df4a18c5b3c1b890145b5a3f5f64063c2a0d02b13c76d78fe5de34936"}, - {file = "watchfiles-1.0.5-cp313-cp313-win32.whl", hash = "sha256:4a8ec1e4e16e2d5bafc9ba82f7aaecfeec990ca7cd27e84fb6f191804ed2fcfc"}, - {file = "watchfiles-1.0.5-cp313-cp313-win_amd64.whl", hash = "sha256:f436601594f15bf406518af922a89dcaab416568edb6f65c4e5bbbad1ea45c11"}, - {file = "watchfiles-1.0.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2cfb371be97d4db374cba381b9f911dd35bb5f4c58faa7b8b7106c8853e5d225"}, - {file = "watchfiles-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a3904d88955fda461ea2531fcf6ef73584ca921415d5cfa44457a225f4a42bc1"}, - {file = "watchfiles-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b7a21715fb12274a71d335cff6c71fe7f676b293d322722fe708a9ec81d91f5"}, - {file = "watchfiles-1.0.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dfd6ae1c385ab481766b3c61c44aca2b3cd775f6f7c0fa93d979ddec853d29d5"}, - {file = "watchfiles-1.0.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b659576b950865fdad31fa491d31d37cf78b27113a7671d39f919828587b429b"}, - {file = "watchfiles-1.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1909e0a9cd95251b15bff4261de5dd7550885bd172e3536824bf1cf6b121e200"}, - {file = "watchfiles-1.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:832ccc221927c860e7286c55c9b6ebcc0265d5e072f49c7f6456c7798d2b39aa"}, - {file = "watchfiles-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fbb6102b3296926d0c62cfc9347f6237fb9400aecd0ba6bbda94cae15f2b3b"}, - {file = "watchfiles-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:15ac96dd567ad6c71c71f7b2c658cb22b7734901546cd50a475128ab557593ca"}, - {file = "watchfiles-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b6227351e11c57ae997d222e13f5b6f1f0700d84b8c52304e8675d33a808382"}, - {file = "watchfiles-1.0.5-cp39-cp39-win32.whl", hash = "sha256:974866e0db748ebf1eccab17862bc0f0303807ed9cda465d1324625b81293a18"}, - {file = "watchfiles-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:9848b21ae152fe79c10dd0197304ada8f7b586d3ebc3f27f43c506e5a52a863c"}, - {file = "watchfiles-1.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f59b870db1f1ae5a9ac28245707d955c8721dd6565e7f411024fa374b5362d1d"}, - {file = "watchfiles-1.0.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9475b0093767e1475095f2aeb1d219fb9664081d403d1dff81342df8cd707034"}, - {file = "watchfiles-1.0.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc533aa50664ebd6c628b2f30591956519462f5d27f951ed03d6c82b2dfd9965"}, - {file = "watchfiles-1.0.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed1cd825158dcaae36acce7b2db33dcbfd12b30c34317a88b8ed80f0541cc57"}, - {file = "watchfiles-1.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:554389562c29c2c182e3908b149095051f81d28c2fec79ad6c8997d7d63e0009"}, - {file = "watchfiles-1.0.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a74add8d7727e6404d5dc4dcd7fac65d4d82f95928bbee0cf5414c900e86773e"}, - {file = "watchfiles-1.0.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb1489f25b051a89fae574505cc26360c8e95e227a9500182a7fe0afcc500ce0"}, - {file = "watchfiles-1.0.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0901429650652d3f0da90bad42bdafc1f9143ff3605633c455c999a2d786cac"}, - {file = "watchfiles-1.0.5.tar.gz", hash = "sha256:b7529b5dcc114679d43827d8c35a07c493ad6f083633d573d81c660abc5979e9"}, + {file = "watchfiles-1.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc"}, + {file = "watchfiles-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df"}, + {file = "watchfiles-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8412eacef34cae2836d891836a7fff7b754d6bcac61f6c12ba5ca9bc7e427b68"}, + {file = "watchfiles-1.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df670918eb7dd719642e05979fc84704af913d563fd17ed636f7c4783003fdcc"}, + {file = "watchfiles-1.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7642b9bc4827b5518ebdb3b82698ada8c14c7661ddec5fe719f3e56ccd13c97"}, + {file = "watchfiles-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:199207b2d3eeaeb80ef4411875a6243d9ad8bc35b07fc42daa6b801cc39cc41c"}, + {file = "watchfiles-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a479466da6db5c1e8754caee6c262cd373e6e6c363172d74394f4bff3d84d7b5"}, + {file = "watchfiles-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:935f9edd022ec13e447e5723a7d14456c8af254544cefbc533f6dd276c9aa0d9"}, + {file = "watchfiles-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8076a5769d6bdf5f673a19d51da05fc79e2bbf25e9fe755c47595785c06a8c72"}, + {file = "watchfiles-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86b1e28d4c37e89220e924305cd9f82866bb0ace666943a6e4196c5df4d58dcc"}, + {file = "watchfiles-1.1.0-cp310-cp310-win32.whl", hash = "sha256:d1caf40c1c657b27858f9774d5c0e232089bca9cb8ee17ce7478c6e9264d2587"}, + {file = "watchfiles-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a89c75a5b9bc329131115a409d0acc16e8da8dfd5867ba59f1dd66ae7ea8fa82"}, + {file = "watchfiles-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2"}, + {file = "watchfiles-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c"}, + {file = "watchfiles-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d"}, + {file = "watchfiles-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7"}, + {file = "watchfiles-1.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c"}, + {file = "watchfiles-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575"}, + {file = "watchfiles-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8"}, + {file = "watchfiles-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f"}, + {file = "watchfiles-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4"}, + {file = "watchfiles-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d"}, + {file = "watchfiles-1.1.0-cp311-cp311-win32.whl", hash = "sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2"}, + {file = "watchfiles-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12"}, + {file = "watchfiles-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a"}, + {file = "watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179"}, + {file = "watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5"}, + {file = "watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297"}, + {file = "watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0"}, + {file = "watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e"}, + {file = "watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee"}, + {file = "watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd"}, + {file = "watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f"}, + {file = "watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4"}, + {file = "watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f"}, + {file = "watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd"}, + {file = "watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47"}, + {file = "watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6"}, + {file = "watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30"}, + {file = "watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a"}, + {file = "watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc"}, + {file = "watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b"}, + {file = "watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895"}, + {file = "watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a"}, + {file = "watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b"}, + {file = "watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c"}, + {file = "watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b"}, + {file = "watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb"}, + {file = "watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9"}, + {file = "watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7"}, + {file = "watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5"}, + {file = "watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1"}, + {file = "watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339"}, + {file = "watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633"}, + {file = "watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011"}, + {file = "watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670"}, + {file = "watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf"}, + {file = "watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4"}, + {file = "watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20"}, + {file = "watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef"}, + {file = "watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb"}, + {file = "watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297"}, + {file = "watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018"}, + {file = "watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0"}, + {file = "watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12"}, + {file = "watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb"}, + {file = "watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77"}, + {file = "watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92"}, + {file = "watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e"}, + {file = "watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b"}, + {file = "watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259"}, + {file = "watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f"}, + {file = "watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e"}, + {file = "watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa"}, + {file = "watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8"}, + {file = "watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f"}, + {file = "watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e"}, + {file = "watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb"}, + {file = "watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147"}, + {file = "watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8"}, + {file = "watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db"}, + {file = "watchfiles-1.1.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:865c8e95713744cf5ae261f3067861e9da5f1370ba91fc536431e29b418676fa"}, + {file = "watchfiles-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42f92befc848bb7a19658f21f3e7bae80d7d005d13891c62c2cd4d4d0abb3433"}, + {file = "watchfiles-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa0cc8365ab29487eb4f9979fd41b22549853389e22d5de3f134a6796e1b05a4"}, + {file = "watchfiles-1.1.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:90ebb429e933645f3da534c89b29b665e285048973b4d2b6946526888c3eb2c7"}, + {file = "watchfiles-1.1.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c588c45da9b08ab3da81d08d7987dae6d2a3badd63acdb3e206a42dbfa7cb76f"}, + {file = "watchfiles-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c55b0f9f68590115c25272b06e63f0824f03d4fc7d6deed43d8ad5660cabdbf"}, + {file = "watchfiles-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd17a1e489f02ce9117b0de3c0b1fab1c3e2eedc82311b299ee6b6faf6c23a29"}, + {file = "watchfiles-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da71945c9ace018d8634822f16cbc2a78323ef6c876b1d34bbf5d5222fd6a72e"}, + {file = "watchfiles-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:51556d5004887045dba3acdd1fdf61dddea2be0a7e18048b5e853dcd37149b86"}, + {file = "watchfiles-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04e4ed5d1cd3eae68c89bcc1a485a109f39f2fd8de05f705e98af6b5f1861f1f"}, + {file = "watchfiles-1.1.0-cp39-cp39-win32.whl", hash = "sha256:c600e85f2ffd9f1035222b1a312aff85fd11ea39baff1d705b9b047aad2ce267"}, + {file = "watchfiles-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:3aba215958d88182e8d2acba0fdaf687745180974946609119953c0e112397dc"}, + {file = "watchfiles-1.1.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a6fd40bbb50d24976eb275ccb55cd1951dfb63dbc27cae3066a6ca5f4beabd5"}, + {file = "watchfiles-1.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f811079d2f9795b5d48b55a37aa7773680a5659afe34b54cc1d86590a51507d"}, + {file = "watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2726d7bfd9f76158c84c10a409b77a320426540df8c35be172444394b17f7ea"}, + {file = "watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df32d59cb9780f66d165a9a7a26f19df2c7d24e3bd58713108b41d0ff4f929c6"}, + {file = "watchfiles-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3"}, + {file = "watchfiles-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c"}, + {file = "watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432"}, + {file = "watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792"}, + {file = "watchfiles-1.1.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7b3443f4ec3ba5aa00b0e9fa90cf31d98321cbff8b925a7c7b84161619870bc9"}, + {file = "watchfiles-1.1.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7049e52167fc75fc3cc418fc13d39a8e520cbb60ca08b47f6cedb85e181d2f2a"}, + {file = "watchfiles-1.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54062ef956807ba806559b3c3d52105ae1827a0d4ab47b621b31132b6b7e2866"}, + {file = "watchfiles-1.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a7bd57a1bb02f9d5c398c0c1675384e7ab1dd39da0ca50b7f09af45fa435277"}, + {file = "watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575"}, ] [package.dependencies] @@ -10687,4 +10705,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.10.0,<3.11" -content-hash = "141759837c136d298f317941aed09d28d1e371625ea4c1dbf6616bccb4d720e3" +content-hash = "2e5f49d53ecf8f00a119892940f8d68b789117099dcce5cd98698ce7f044ef7a" diff --git a/pyproject.toml b/pyproject.toml index 1ad733c76..a4214d5c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ torchmetrics = "^1.3.0" aiohttp = "^3.9.3" ecos = "^2.0.7.post1" qpth = "^0.0.16" -urllib3 = "^2.2.2" +urllib3 = "^2.4.0" grpcio = "^1.60.0,!=1.64.2,!=1.65.1,!=1.65.2,!=1.65.4" monai = "^1.3.0" nnunetv2 = "^2.3.1" diff --git a/research/picai/fedavg/client.py b/research/picai/fedavg/client.py index de339fa18..3aa632dc9 100644 --- a/research/picai/fedavg/client.py +++ b/research/picai/fedavg/client.py @@ -13,8 +13,8 @@ from torch.optim import Optimizer from torchmetrics.classification import MultilabelAveragePrecision -from fl4health.checkpointing.checkpointer import PerRoundStateCheckpointer from fl4health.checkpointing.client_module import ClientCheckpointAndStateModule +from fl4health.checkpointing.state_checkpointer import ClientStateCheckpointer from fl4health.clients.basic_client import BasicClient from fl4health.metrics import TorchMetric from fl4health.metrics.base_metrics import Metric @@ -166,7 +166,7 @@ def get_optimizer(self, config: Config) -> Optimizer: checkpoint_and_state_module: ClientCheckpointAndStateModule | None if args.artifact_dir is not None: checkpoint_and_state_module = ClientCheckpointAndStateModule( - state_checkpointer=PerRoundStateCheckpointer(Path(args.artifact_dir)) + state_checkpointer=ClientStateCheckpointer(Path(args.artifact_dir)) ) else: checkpoint_and_state_module = None diff --git a/research/picai/fedavg/server.py b/research/picai/fedavg/server.py index a27bbc094..11f39ee32 100644 --- a/research/picai/fedavg/server.py +++ b/research/picai/fedavg/server.py @@ -10,8 +10,8 @@ from flwr.server.client_manager import SimpleClientManager from flwr.server.strategy import FedAvg -from fl4health.checkpointing.checkpointer import PerRoundStateCheckpointer from fl4health.checkpointing.server_module import BaseServerCheckpointAndStateModule +from fl4health.checkpointing.state_checkpointer import ServerStateCheckpointer from fl4health.metrics.metric_aggregation import evaluate_metrics_aggregation_fn, fit_metrics_aggregation_fn from fl4health.parameter_exchange.full_exchanger import FullParameterExchanger from fl4health.servers.base_server import FlServer @@ -53,7 +53,7 @@ def main(config: dict[str, Any], server_address: str, n_clients: int, artifact_d model = get_model() parameter_exchanger = FullParameterExchanger() - state_checkpointer = PerRoundStateCheckpointer(checkpoint_dir=Path(artifact_dir)) + state_checkpointer = ServerStateCheckpointer(checkpoint_dir=Path(artifact_dir)) checkpoint_and_state_module = BaseServerCheckpointAndStateModule( model=model, parameter_exchanger=parameter_exchanger, diff --git a/research/picai/fl_nnunet/start_client.py b/research/picai/fl_nnunet/start_client.py index d6643536e..1edad62c0 100644 --- a/research/picai/fl_nnunet/start_client.py +++ b/research/picai/fl_nnunet/start_client.py @@ -5,8 +5,8 @@ from logging import INFO from pathlib import Path -from fl4health.checkpointing.checkpointer import PerRoundStateCheckpointer from fl4health.checkpointing.client_module import ClientCheckpointAndStateModule +from fl4health.checkpointing.state_checkpointer import ClientStateCheckpointer with warnings.catch_warnings(): # Silence deprecation warnings from sentry sdk due to flwr and wandb @@ -62,7 +62,7 @@ def main( if intermediate_client_state_dir is not None: checkpoint_and_state_module = ClientCheckpointAndStateModule( - state_checkpointer=PerRoundStateCheckpointer(Path(intermediate_client_state_dir)) + state_checkpointer=ClientStateCheckpointer(Path(intermediate_client_state_dir)) ) else: checkpoint_and_state_module = None diff --git a/research/picai/fl_nnunet/start_server.py b/research/picai/fl_nnunet/start_server.py index 5f329db1f..3d4aa2efe 100644 --- a/research/picai/fl_nnunet/start_server.py +++ b/research/picai/fl_nnunet/start_server.py @@ -5,8 +5,8 @@ from functools import partial from pathlib import Path -from fl4health.checkpointing.checkpointer import PerRoundStateCheckpointer from fl4health.checkpointing.server_module import NnUnetServerCheckpointAndStateModule +from fl4health.checkpointing.state_checkpointer import NnUnetServerStateCheckpointer with warnings.catch_warnings(): # Silence deprecation warnings from sentry sdk due to flwr and wandb @@ -101,7 +101,7 @@ def main( checkpoint_and_state_model = NnUnetServerCheckpointAndStateModule( model=None, parameter_exchanger=FullParameterExchanger(), - state_checkpointer=PerRoundStateCheckpointer(Path(intermediate_server_state_dir)), + state_checkpointer=NnUnetServerStateCheckpointer(Path(intermediate_server_state_dir)), ) server = NnunetServer( diff --git a/research/picai/single_node_trainer.py b/research/picai/single_node_trainer.py index 0c5f44321..38b97657c 100644 --- a/research/picai/single_node_trainer.py +++ b/research/picai/single_node_trainer.py @@ -10,8 +10,9 @@ from torch.nn.modules.loss import _Loss from torch.optim import Optimizer -from fl4health.checkpointing.checkpointer import BestLossTorchModuleCheckpointer, PerRoundStateCheckpointer +from fl4health.checkpointing.checkpointer import BestLossTorchModuleCheckpointer from fl4health.metrics.metric_managers import MetricManager +from research.picai.utils import SimpleDictionaryCheckpointer class SingleNodeTrainer: @@ -33,7 +34,7 @@ def __init__( os.mkdir(checkpoint_dir) self.state_checkpoint_name = "ckpt.pkl" - self.per_epoch_checkpointer = PerRoundStateCheckpointer(Path(checkpoint_dir)) + self.per_epoch_checkpointer = SimpleDictionaryCheckpointer(Path(checkpoint_dir), self.state_checkpoint_name) best_metric_checkpoint_name = "best_ckpt.pkl" self.checkpointer = BestLossTorchModuleCheckpointer(checkpoint_dir, best_metric_checkpoint_name) @@ -45,12 +46,10 @@ def __init__( self.device = device self.epoch: int - if not self.per_epoch_checkpointer.checkpoint_exists(self.state_checkpoint_name): - self.per_epoch_checkpointer.save_checkpoint( - self.state_checkpoint_name, {"model": self.model, "optimizer": self.optimizer, "epoch": 0} - ) + if not self.per_epoch_checkpointer.checkpoint_exists(): + self.per_epoch_checkpointer.save_checkpoint({"model": self.model, "optimizer": self.optimizer, "epoch": 0}) - ckpt = self.per_epoch_checkpointer.load_checkpoint(self.state_checkpoint_name) + ckpt = self.per_epoch_checkpointer.load_checkpoint() self.model, self.optimizer, self.epoch = ckpt["model"], ckpt["optimizer"], ckpt["epoch"] def _maybe_checkpoint(self, loss: float, metrics: dict[str, Scalar]) -> None: @@ -104,7 +103,7 @@ def train_by_epochs(self, epochs: int, train_metric_mngr: MetricManager, val_met # Save checkpoint in case run gets pre-empted self.per_epoch_checkpointer.save_checkpoint( - self.state_checkpoint_name, {"model": self.model, "optimizer": self.optimizer, "epoch": epoch + 1} + {"model": self.model, "optimizer": self.optimizer, "epoch": epoch + 1} ) def validate(self, val_metric_mngr: MetricManager) -> None: diff --git a/research/picai/utils.py b/research/picai/utils.py index b1c462224..93a147145 100644 --- a/research/picai/utils.py +++ b/research/picai/utils.py @@ -1,6 +1,73 @@ +import os from enum import Enum +from logging import ERROR, INFO +from pathlib import Path from typing import Any +import torch +from flwr.common.logger import log + + +class SimpleDictionaryCheckpointer: + def __init__( + self, + checkpoint_dir: Path, + checkpoint_name: str, + ) -> None: + """ + A simple state checkpointer that saves and loads an object's attribute state (stored in a dictionary) to and + from a file. + + Args: + checkpoint_dir (Path): Directory to which checkpoints are saved + checkpoint_name (str): Name of the checkpoint to be saved + """ + self.checkpoint_dir = checkpoint_dir + self.checkpoint_name = checkpoint_name + self.checkpoint_path = os.path.join(self.checkpoint_dir, self.checkpoint_name) + + def save_checkpoint(self, checkpoint_dict: dict[str, Any]) -> None: + """ + Save ``checkpoint_dict`` to checkpoint path defined based on checkpointer dir and checkpoint name. + + Args: + checkpoint_dict (dict[str, Any]): A dictionary with string keys and values of type Any representing the + state to be saved. + + Raises: + e: Will throw an error if there is an issue saving the model. ``Torch.save`` seems to swallow errors in + this context, so we explicitly surface the error with a try/except. + """ + try: + log(INFO, f"Saving the state as {self.checkpoint_path}") + torch.save(checkpoint_dict, self.checkpoint_path) + except Exception as e: + log(ERROR, f"Encountered the following error while saving the checkpoint: {e}") + raise e + + def load_checkpoint(self) -> dict[str, Any]: + """ + Load and return the checkpoint stored in ``checkpoint_dir`` under the ``checkpoint_name`` if it exists. If + it does not exist, an assertion error will be thrown. + + Returns: + dict[str, Any]: A dictionary representing the checkpointed state, as loaded by ``torch.load``. + """ + assert self.checkpoint_exists() + log(INFO, f"Loading state from checkpoint at {self.checkpoint_path}") + + return torch.load(self.checkpoint_path) + + def checkpoint_exists(self) -> bool: + """ + Check if a checkpoint exists at the checkpoint_path constructed as + ``checkpoint_dir`` + ``checkpoint_name`` during initialization. + + Returns: + bool: True if checkpoint exists, otherwise false. + """ + return os.path.exists(self.checkpoint_path) + class MultiAttributeEnum(Enum): def __init__(self, attributes: Any) -> None: diff --git a/tests/checkpointing/test_per_round_checkpointer.py b/tests/checkpointing/test_per_round_checkpointer.py deleted file mode 100644 index 0572832d6..000000000 --- a/tests/checkpointing/test_per_round_checkpointer.py +++ /dev/null @@ -1,33 +0,0 @@ -import os -import tempfile -from pathlib import Path - -import torch -from torch.optim import Optimizer - -from fl4health.checkpointing.checkpointer import PerRoundStateCheckpointer -from tests.test_utils.models_for_test import LinearModel - - -def test_per_round_checkpointer() -> None: - model: torch.nn.Module = LinearModel() - optimizer: Optimizer = torch.optim.SGD(model.parameters(), lr=0.01) - with tempfile.TemporaryDirectory() as results_dir: - checkpoint_name = "ckpt.pt" - checkpoint_path = os.path.join(results_dir, checkpoint_name) - checkpointer = PerRoundStateCheckpointer(checkpoint_dir=Path(results_dir)) - - assert not checkpointer.checkpoint_exists(checkpoint_name) - - checkpointer.save_checkpoint( - checkpoint_name=checkpoint_name, - checkpoint_dict={"model": model, "optimizer": optimizer, "current_round": 0}, - ) - - assert checkpointer.checkpoint_exists(checkpoint_name) - - ckpt = checkpointer.load_checkpoint(checkpoint_path) - - assert "model" in ckpt and isinstance(ckpt["model"], torch.nn.Module) - assert "optimizer" in ckpt and isinstance(ckpt["optimizer"], torch.optim.Optimizer) - assert "current_round" in ckpt and isinstance(ckpt["current_round"], int) diff --git a/tests/checkpointing/test_state_checkpointer.py b/tests/checkpointing/test_state_checkpointer.py new file mode 100644 index 000000000..c92904d3e --- /dev/null +++ b/tests/checkpointing/test_state_checkpointer.py @@ -0,0 +1,415 @@ +import copy +import pickle +from pathlib import Path +from typing import Any + +import torch +from flwr.server.client_manager import SimpleClientManager +from flwr.server.history import History +from torch.optim import Optimizer + +from fl4health.checkpointing.state_checkpointer import ( + ClientStateCheckpointer, + NnUnetServerStateCheckpointer, + ServerStateCheckpointer, +) +from fl4health.clients.basic_client import BasicClient +from fl4health.reporting import JsonReporter +from fl4health.servers.base_server import FlServer +from fl4health.servers.nnunet_server import NnunetServer +from fl4health.utils.metrics import Accuracy +from fl4health.utils.snapshotter import ( + AbstractSnapshotter, + OptimizerSnapshotter, + SingletonSnapshotter, + StringSnapshotter, +) +from tests.test_utils.models_for_test import SingleLayerWithSeed + + +def create_fl_client() -> BasicClient: + metrics = [Accuracy("accuracy")] + reporter = JsonReporter() + fl_client = BasicClient( + data_path=Path(""), + metrics=metrics, + device=torch.device(0), + reporters=[reporter], + client_name="original_client", + ) + fl_client.model = SingleLayerWithSeed(seed=42, output_size=1) + fl_client.optimizers = {"global": torch.optim.SGD(fl_client.model.parameters(), lr=0.001, momentum=0.1)} + fl_client.lr_schedulers = { + "global": torch.optim.lr_scheduler.StepLR(fl_client.optimizers["global"], step_size=30, gamma=0.1) + } + fl_client.criterion = torch.nn.CrossEntropyLoss() + return fl_client + + +def test_client_state_works_for_per_round_checkpointing(tmp_path: Path) -> None: + fl_client = create_fl_client() + # Temporary path to save the state to, will be cleaned up at the end of the test. + checkpoint_dir = tmp_path.joinpath("client_state") + checkpoint_dir.mkdir() + # checkpoint_dir is required for ClientStateCheckpointer to assist saving the state to the disk. + # This is useful for resuming training after an interruption. + checkpointer = ClientStateCheckpointer(checkpoint_dir=Path(checkpoint_dir), checkpoint_name="client_state.pt") + copy_client = copy.deepcopy(fl_client) + + assert not checkpointer.checkpoint_exists() + + # Train the client with random data for one step, this updates the model and the optimizer state. + input_data = torch.randn(32, 100) + target_data = torch.randn(32, 1) + fl_client.train_step(input_data, target_data) + # Update lr_schedulers + fl_client.update_lr_schedulers(step=0) + # Update the step count + fl_client.total_steps += 1 + + # Save the state of the trained client + checkpointer.save_client_state(fl_client) + + # Reset the checkpointer object + # This is similar to the situation where we have to restart FL after an interruption. + checkpointer = ClientStateCheckpointer(checkpoint_dir=Path(checkpoint_dir), checkpoint_name="client_state.pt") + + # Checkpoint should now exist + assert checkpointer.checkpoint_exists() + + snapshot_ckpt = checkpointer.load_checkpoint() + assert "lr_schedulers" in snapshot_ckpt + assert "optimizers" in snapshot_ckpt + assert "total_steps" in snapshot_ckpt + assert "total_epochs" in snapshot_ckpt + assert "reports_manager" in snapshot_ckpt + + # Copy client should have a different model state than the original client + assert copy_client.total_steps == 0 + default_optimizer_state = copy_client.optimizers["global"].state_dict()["state"] + updated_optimizer_state = fl_client.optimizers["global"].state_dict()["state"] + assert default_optimizer_state == {} + assert updated_optimizer_state != default_optimizer_state + assert copy_client.lr_schedulers["global"].state_dict() != fl_client.lr_schedulers["global"].state_dict() + + # Loads fl_client checkpoint (trained client) into the copy client + checkpointer.maybe_load_client_state(copy_client) + + # Check that state is loaded correctly. + # Check the state of the loaded optimizer. + loaded_optimizer_state = copy_client.optimizers["global"].state_dict()["state"] + saved_optimizer_state = fl_client.optimizers["global"].state_dict()["state"] + assert all( + torch.equal(loaded_optimizer_state[0][key], saved_optimizer_state[0][key]) + for key in loaded_optimizer_state[0].keys() + ) + # Check the state of the loaded lr_scheduler. + assert copy_client.lr_schedulers["global"].state_dict() == fl_client.lr_schedulers["global"].state_dict() + # Check the loaded total steps. + assert copy_client.total_steps == 1 + + +def test_client_state_works_for_training_loop_checkpointing(tmp_path: Path) -> None: + """ + Train loop checkpointing requires more information than per round as it needs to be able to restart a training + process where it left off. This is important for early stopping. + """ + fl_client = create_fl_client() + # Create a copy of the client for later. + copy_client = copy.deepcopy(fl_client) + # Temporary path to save the state to, will be cleaned up at the end of the test. + checkpoint_dir = tmp_path.joinpath("client_state") + checkpoint_dir.mkdir() + checkpointer = ClientStateCheckpointer(checkpoint_dir=Path(checkpoint_dir), checkpoint_name="client_state.pt") + + # Simulate updates inside the training loop (one step). + input_data = torch.randn(32, 100) + target_data = torch.zeros(32, 1) + report_data: dict[str, Any] = {"round": 0} + losses, preds = fl_client.train_step(input_data, target_data) + fl_client.train_loss_meter.update(losses) + fl_client.update_metric_manager(preds, target_data, fl_client.train_metric_manager) + fl_client.update_lr_schedulers(step=0) + report_data.update({"fit_step_losses": losses.as_dict(), "fit_step": 0}) + report_data.update(fl_client.get_client_specific_reports()) + fl_client.reports_manager.report(report_data, 0, None, 0) + fl_client.total_steps += 1 + + # Save the state of the client. + checkpointer.save_client_state(fl_client) + + assert checkpointer.checkpoint_exists() + snapshot_ckpt = checkpointer.load_checkpoint() + # Check that all the attributes defined in snapshot_attrs are present in the checkpoint. + assert "model" in snapshot_ckpt + assert "lr_schedulers" in snapshot_ckpt + assert "optimizers" in snapshot_ckpt + assert "total_steps" in snapshot_ckpt + assert "total_epochs" in snapshot_ckpt + assert "reports_manager" in snapshot_ckpt + assert "train_loss_meter" in snapshot_ckpt + assert "train_metric_manager" in snapshot_ckpt + + # Use the copy_client to load the state. + checkpointer.maybe_load_client_state(copy_client) + + # Check that the state is loaded correctly. + # Check the state of the loaded optimizer. + loaded_optimizer_state = copy_client.optimizers["global"].state_dict()["state"] + saved_optimizer_state = fl_client.optimizers["global"].state_dict()["state"] + assert all( + torch.equal(loaded_optimizer_state[0][key], saved_optimizer_state[0][key]) + for key in loaded_optimizer_state[0].keys() + ) + # Check the state of the loaded lr_scheduler. + assert copy_client.lr_schedulers["global"].state_dict() == fl_client.lr_schedulers["global"].state_dict() + # Check the loaded total steps. + assert copy_client.total_steps == 1 + # Check the loaded train loss meter. + assert all( + loss1.as_dict() == loss2.as_dict() + for loss1, loss2 in zip(copy_client.train_loss_meter.losses_list, fl_client.train_loss_meter.losses_list) + ) + # Check the loaded train metric manager. + print(fl_client.train_metric_manager.compute()) + assert copy_client.train_metric_manager.compute() == fl_client.train_metric_manager.compute() + # Check the loaded model. + for key, value in copy_client.model.state_dict().items(): + assert torch.equal(value, fl_client.model.state_dict()[key]) + # Check the loaded reports manager. + assert isinstance(copy_client.reports_manager.reporters[0], JsonReporter) # Added for type checking + assert isinstance(fl_client.reports_manager.reporters[0], JsonReporter) # Added for type checking + assert copy_client.reports_manager.reporters[0].metrics == fl_client.reports_manager.reporters[0].metrics + + +def test_client_state_checkpointing_with_custom_attrs(tmp_path: Path) -> None: + """ + Test that the client can save and load its state based a custom set of attributes rather than the + default. + """ + checkpoint_dir = tmp_path.joinpath("client_state") + checkpoint_dir.mkdir() + + fl_client = create_fl_client() + # Create a copy of the client for later. + copy_client = copy.deepcopy(fl_client) + + snapshot_attrs: dict[str, tuple[AbstractSnapshotter[Any], Any]] = { + "total_steps": (SingletonSnapshotter(), int), + "optimizers": (OptimizerSnapshotter(), Optimizer), + "client_name": (StringSnapshotter(), str), + } + + checkpointer = ClientStateCheckpointer(checkpoint_dir, None, snapshot_attrs) # ignore: typing + + # Perform one training step. + input_data = torch.randn(32, 100) + target_data = torch.zeros(32, 1) + fl_client.train_step(input_data, target_data) + fl_client.total_steps += 2 + # Change the name of the client for testing purposes. + fl_client.client_name = "updated_client" + + checkpointer.save_client_state(fl_client) + assert checkpointer.checkpoint_exists() + + # Use the copy_client to load the state. + checkpointer.maybe_load_client_state(copy_client) + + assert "reports_manager" not in checkpointer.snapshot_ckpt + + # Check that the state is loaded correctly. + # Check the state of the loaded optimizer. + loaded_optimizer_state = copy_client.optimizers["global"].state_dict()["state"] + saved_optimizer_state = fl_client.optimizers["global"].state_dict()["state"] + assert all( + torch.equal(loaded_optimizer_state[0][key], saved_optimizer_state[0][key]) + for key in loaded_optimizer_state[0].keys() + ) + + # Check the loaded total steps. + assert copy_client.total_steps == 2 + # Check the loaded client name. + assert copy_client.client_name == "updated_client" + + +def test_server_state_checkpointer(tmp_path: Path) -> None: + """ + Test the server state checkpointer. + """ + fl_server = FlServer( + client_manager=SimpleClientManager(), + fl_config={"": ""}, + reporters=[JsonReporter()], + server_name="original_server", + ) + fl_server_model = SingleLayerWithSeed(seed=42, output_size=1) + fl_server.history = History() + # Create some history. + fl_server.history.add_loss_distributed(0, 0.6) + fl_server.history.add_loss_distributed(1, 0.4) + fl_server.history.add_loss_centralized(0, 0.3) + fl_server.history.add_loss_centralized(1, 0.2) + fl_server.reports_manager.report( + { + "fit_start": "10.10", + "host_type": "server", + } + ) + fl_server.current_round = 1 + # Temporary path to save the state to, will be cleaned up at the end of the test. + checkpoint_dir = tmp_path.joinpath("server_state") + checkpoint_dir.mkdir() + + # Create a checkpointer for the server. + checkpointer = ServerStateCheckpointer(checkpoint_dir=Path(checkpoint_dir), checkpoint_name="server_state.pt") + + # Check that the checkpoint does not exist initially. + assert not checkpointer.checkpoint_exists() + + # Save the state with the model. + checkpointer.save_server_state(server=fl_server, model=fl_server_model) + + # Check that the checkpoint now exists. + assert checkpointer.checkpoint_exists() + + # Create a new server to load the state into. + new_server = FlServer(client_manager=SimpleClientManager(), fl_config={"": ""}, server_name="new_server") + new_server_model = SingleLayerWithSeed(seed=12, output_size=1) + new_server.history = History() + new_server.current_round = 0 + + # Check that new server is different from the original server. + assert not all( + torch.equal(value, fl_server_model.state_dict()[key]) for key, value in new_server_model.state_dict().items() + ) + assert new_server.current_round != fl_server.current_round + assert not all( + getattr(new_server.history, key) == getattr(fl_server.history, key) for key in vars(fl_server.history) + ) + assert new_server.server_name != fl_server.server_name + # No reporters are set up in the new server. + assert len(new_server.reports_manager.reporters) == 0 + + # Now load the state into the new server. + # We don't need to create a new checkpointer here, + # but we create one to simulate the situation where the program is + # restarted due to an interruption. + new_checkpointer = ServerStateCheckpointer(checkpoint_dir=Path(checkpoint_dir), checkpoint_name="server_state.pt") + new_checkpointer.maybe_load_server_state(server=new_server, model=new_server_model) + + # Check that the state is loaded correctly. + assert all( + torch.equal(value, fl_server_model.state_dict()[key]) for key, value in new_server_model.state_dict().items() + ) + assert new_server.current_round == fl_server.current_round + # Check that the history is loaded correctly. + assert all(getattr(new_server.history, key) == getattr(fl_server.history, key) for key in vars(fl_server.history)) + assert new_server.server_name == fl_server.server_name + # After loading, new_server now has reporters, which were not present at initialization. + assert isinstance(new_server.reports_manager.reporters[0], JsonReporter) # Added for type checking + assert isinstance(fl_server.reports_manager.reporters[0], JsonReporter) # Added for type checking + assert ( + new_server.reports_manager.reporters[0].metrics + == fl_server.reports_manager.reporters[0].metrics + == { + "fit_start": "10.10", + "host_type": "server", + } + ) + + +def test_nnunet_server_state_checkpointer(tmp_path: Path) -> None: + """ + Test the nnunet server per round state checkpointer. + """ + + def dummy_get_config(current_server_round: int) -> dict[str, Any]: + return { + "current_server_round": current_server_round, + } + + nnunet_server = NnunetServer( + client_manager=SimpleClientManager(), + fl_config={"nnunet_config": "2d"}, + on_init_parameters_config_fn=dummy_get_config, + server_name="nnunet_original_server", + ) + nnunet_server_model = SingleLayerWithSeed(seed=42, output_size=1) + dummy_plans = {"dataset_name": "x", "plans_name": "y"} + nnunet_server.nnunet_plans_bytes = pickle.dumps(dummy_plans) + nnunet_server.num_segmentation_heads = 2 + nnunet_server.num_input_channels = 3 + nnunet_server.current_round = 1 + nnunet_server.history = History() + nnunet_server.global_deep_supervision = True + # Temporary path to save the state to, will be cleaned up at the end of the test. + checkpoint_dir = tmp_path.joinpath("nnunet_server_state") + checkpoint_dir.mkdir() + + # Create a checkpointer for the server. + checkpointer = NnUnetServerStateCheckpointer( + checkpoint_dir=Path(checkpoint_dir), checkpoint_name="server_state.pt" + ) + + # Check that the checkpoint does not exist initially. + assert not checkpointer.checkpoint_exists() + + # Save the state with the model. + # In real usage, we should use parameter_exchanger to hydrate the model for checkpointing first. + checkpointer.save_server_state(server=nnunet_server, model=nnunet_server_model) + + # Check that the checkpoint now exists. + assert checkpointer.checkpoint_exists() + + # Create a new server to load the state into. + new_server = NnunetServer( + client_manager=SimpleClientManager(), + fl_config={"nnunet_config": "3d_fullres"}, + on_init_parameters_config_fn=dummy_get_config, + server_name="new_nnunet_server", + ) + new_server_model = SingleLayerWithSeed(seed=12, output_size=1) + dummy_plans = {"dataset_name": "x2", "plans_name": "y2"} + new_server.nnunet_plans_bytes = pickle.dumps(dummy_plans) + new_server.num_segmentation_heads = 1 + new_server.num_input_channels = 2 + new_server.history = History() + new_server.current_round = 0 + + # Check that new server is different from the original server. + assert not all( + torch.equal(value, nnunet_server_model.state_dict()[key]) + for key, value in new_server_model.state_dict().items() + ) + # Check that new_server is different from nnunet_server. + assert new_server.current_round != nnunet_server.current_round + assert new_server.server_name != nnunet_server.server_name + assert new_server.nnunet_plans_bytes != nnunet_server.nnunet_plans_bytes + assert new_server.num_segmentation_heads != nnunet_server.num_segmentation_heads + assert new_server.num_input_channels != nnunet_server.num_input_channels + assert new_server.nnunet_config != nnunet_server.nnunet_config + assert new_server.global_deep_supervision != nnunet_server.global_deep_supervision + + # Now load the state into the new server. + # We don't need to create a new checkpointer here, + # but we create one to simulate the situation where the program is + # restarted due to an interruption. Note that checkpoint name and path should point to the saved checkpoint. + new_checkpointer = NnUnetServerStateCheckpointer( + checkpoint_dir=Path(checkpoint_dir), checkpoint_name="server_state.pt" + ) + + new_checkpointer.maybe_load_server_state(server=new_server, model=new_server_model) + + # Check that the state is loaded correctly. + assert all( + torch.equal(value, nnunet_server_model.state_dict()[key]) + for key, value in new_server_model.state_dict().items() + ) + assert new_server.current_round == nnunet_server.current_round + assert new_server.server_name == nnunet_server.server_name + assert new_server.nnunet_plans_bytes == nnunet_server.nnunet_plans_bytes + assert new_server.num_segmentation_heads == nnunet_server.num_segmentation_heads + assert new_server.num_input_channels == nnunet_server.num_input_channels + assert new_server.nnunet_config == nnunet_server.nnunet_config + assert new_server.global_deep_supervision == nnunet_server.global_deep_supervision diff --git a/tests/servers/test_base_server.py b/tests/servers/test_base_server.py index cb78d5cec..726d52912 100644 --- a/tests/servers/test_base_server.py +++ b/tests/servers/test_base_server.py @@ -12,8 +12,9 @@ from freezegun import freeze_time from peft import LoraConfig, get_peft_model -from fl4health.checkpointing.checkpointer import BestLossTorchModuleCheckpointer, PerRoundStateCheckpointer +from fl4health.checkpointing.checkpointer import BestLossTorchModuleCheckpointer from fl4health.checkpointing.server_module import BaseServerCheckpointAndStateModule +from fl4health.checkpointing.state_checkpointer import ServerStateCheckpointer from fl4health.client_managers.base_sampling_manager import SimpleClientManager from fl4health.client_managers.poisson_sampling_manager import PoissonSamplingClientManager from fl4health.metrics.base_metrics import TEST_LOSS_KEY, TEST_NUM_EXAMPLES_KEY, MetricPrefix @@ -36,7 +37,7 @@ def test_hydration_no_model_with_checkpointer(tmp_path: Path) -> None: checkpoint_dir = tmp_path.joinpath("resources") checkpoint_dir.mkdir() checkpointer = BestLossTorchModuleCheckpointer(str(checkpoint_dir), "best_model.pkl") - state_checkpointer = PerRoundStateCheckpointer(checkpoint_dir=checkpoint_dir) + state_checkpointer = ServerStateCheckpointer(checkpoint_dir=checkpoint_dir) # Checkpointer is defined but there is no server-side model defined to produce a model from the server state. # An assertion error should be throw stating this with pytest.raises(AssertionError) as assertion_error: diff --git a/tests/smoke_tests/load_from_checkpoint_example/client.py b/tests/smoke_tests/load_from_checkpoint_example/client.py index 698c047f5..d54e656c6 100644 --- a/tests/smoke_tests/load_from_checkpoint_example/client.py +++ b/tests/smoke_tests/load_from_checkpoint_example/client.py @@ -11,8 +11,8 @@ from torch.utils.data import DataLoader from examples.models.cnn_model import Net -from fl4health.checkpointing.checkpointer import PerRoundStateCheckpointer from fl4health.checkpointing.client_module import ClientCheckpointAndStateModule +from fl4health.checkpointing.state_checkpointer import ClientStateCheckpointer from fl4health.clients.basic_client import BasicClient from fl4health.metrics import Accuracy from fl4health.metrics.base_metrics import Metric @@ -107,7 +107,7 @@ def fit(self, parameters: NDArrays, config: Config) -> tuple[NDArrays, int, dict checkpoint_and_state_module: ClientCheckpointAndStateModule | None if args.intermediate_client_state_dir is not None: checkpoint_and_state_module = ClientCheckpointAndStateModule( - state_checkpointer=PerRoundStateCheckpointer(Path(args.intermediate_client_state_dir)) + state_checkpointer=ClientStateCheckpointer(Path(args.intermediate_client_state_dir)) ) else: checkpoint_and_state_module = None diff --git a/tests/smoke_tests/load_from_checkpoint_example/server.py b/tests/smoke_tests/load_from_checkpoint_example/server.py index e7aa9a31f..c04b8fc4d 100644 --- a/tests/smoke_tests/load_from_checkpoint_example/server.py +++ b/tests/smoke_tests/load_from_checkpoint_example/server.py @@ -9,12 +9,9 @@ from flwr.server.strategy import FedAvg from examples.models.cnn_model import Net -from fl4health.checkpointing.checkpointer import ( - BestLossTorchModuleCheckpointer, - LatestTorchModuleCheckpointer, - PerRoundStateCheckpointer, -) +from fl4health.checkpointing.checkpointer import BestLossTorchModuleCheckpointer, LatestTorchModuleCheckpointer from fl4health.checkpointing.server_module import BaseServerCheckpointAndStateModule +from fl4health.checkpointing.state_checkpointer import ServerStateCheckpointer from fl4health.metrics.metric_aggregation import evaluate_metrics_aggregation_fn, fit_metrics_aggregation_fn from fl4health.parameter_exchange.full_exchanger import FullParameterExchanger from fl4health.reporting import JsonReporter @@ -54,7 +51,7 @@ def main(config: dict[str, Any], intermediate_server_state_dir: str, server_name BestLossTorchModuleCheckpointer(config["checkpoint_path"], "best_model.pkl"), LatestTorchModuleCheckpointer(config["checkpoint_path"], "latest_model.pkl"), ] - state_checkpointer = PerRoundStateCheckpointer(Path(intermediate_server_state_dir)) + state_checkpointer = ServerStateCheckpointer(Path(intermediate_server_state_dir)) checkpoint_and_state_module = BaseServerCheckpointAndStateModule( model=model, parameter_exchanger=parameter_exchanger, diff --git a/tests/smoke_tests/run_smoke_test.py b/tests/smoke_tests/run_smoke_test.py index cc8b946af..b3ef06b46 100644 --- a/tests/smoke_tests/run_smoke_test.py +++ b/tests/smoke_tests/run_smoke_test.py @@ -529,8 +529,7 @@ async def run_fault_tolerance_smoke_test( # client assertions client_errors = [] - for i in range(len(client_processes)): - client_errors.extend(_assert_metrics(MetricType.CLIENT, client_metrics, tolerance)) + client_errors.extend(_assert_metrics(MetricType.CLIENT, client_metrics, tolerance)) return server_errors, client_errors except Exception: diff --git a/tests/test_utils/models_for_test.py b/tests/test_utils/models_for_test.py index 9830e6e36..25906f912 100644 --- a/tests/test_utils/models_for_test.py +++ b/tests/test_utils/models_for_test.py @@ -33,10 +33,10 @@ def __init__(self) -> None: class SingleLayerWithSeed(nn.Module): - def __init__(self, seed: int = 42) -> None: + def __init__(self, seed: int = 42, output_size: int = 2) -> None: super().__init__() torch.manual_seed(seed) - self.linear = nn.Linear(100, 2, bias=False) + self.linear = nn.Linear(100, output_size, bias=False) def forward(self, x: torch.Tensor) -> torch.Tensor: return self.linear(x) diff --git a/tests/utils/snapshotter_test.py b/tests/utils/snapshotter_test.py index 4bc3dc2d2..4f62814dd 100644 --- a/tests/utils/snapshotter_test.py +++ b/tests/utils/snapshotter_test.py @@ -1,183 +1,138 @@ import copy -from pathlib import Path import torch +import torch.nn as nn +from torch.optim import Optimizer +from torch.optim.lr_scheduler import LRScheduler -from fl4health.clients.basic_client import BasicClient -from fl4health.metrics import Accuracy -from fl4health.metrics.metric_managers import MetricManager -from fl4health.reporting import JsonReporter -from fl4health.reporting.reports_manager import ReportsManager -from fl4health.utils.losses import LossMeter, TrainingLosses +from fl4health.utils.config import narrow_dict_type from fl4health.utils.snapshotter import ( LRSchedulerSnapshotter, - NumberSnapshotter, OptimizerSnapshotter, - SerializableObjectSnapshotter, TorchModuleSnapshotter, ) -from fl4health.utils.typing import TorchPredType, TorchTargetType from tests.test_utils.models_for_test import SingleLayerWithSeed -def test_number_snapshotter() -> None: - metrics = [Accuracy("accuracy")] - reporter = JsonReporter() - fl_client = BasicClient(data_path=Path(""), metrics=metrics, device=torch.device(0), reporters=[reporter]) - old_total_steps = fl_client.total_steps - number_snapshotter = NumberSnapshotter(fl_client) - sp = number_snapshotter.save("total_steps", int) - fl_client.total_steps += 1 - assert sp["total_steps"] == {"None": old_total_steps} - assert fl_client.total_steps != old_total_steps - number_snapshotter.load(sp, "total_steps", int) - assert fl_client.total_steps == old_total_steps - - -def test_optimizer_scheduler_model_snapshotter() -> None: - metrics = [Accuracy("accuracy")] - reporter = JsonReporter() - fl_client = BasicClient(data_path=Path(""), metrics=metrics, device=torch.device(0), reporters=[reporter]) - fl_client.model = SingleLayerWithSeed() - fl_client.criterion = torch.nn.CrossEntropyLoss() - +def compare_mixed_dictionaries( + dict1: dict[str, torch.Tensor | float | int | list | dict], + dict2: dict[str, torch.Tensor | float | int | list | dict], +) -> bool: + if dict1.keys() != dict2.keys(): + return False + + for key, dict1_value in dict1.items(): + if isinstance(dict1_value, torch.Tensor): + if not torch.equal(dict1_value, narrow_dict_type(dict2, key, torch.Tensor)): + return False + elif isinstance(dict1_value, float): + if dict1_value != narrow_dict_type(dict2, key, float): + return False + elif isinstance(dict1_value, int): + if dict1_value != narrow_dict_type(dict2, key, int): + return False + elif isinstance(dict1_value, list): + if dict1_value != narrow_dict_type(dict2, key, list): + return False + elif isinstance(dict1_value, dict): + if not compare_mixed_dictionaries(dict1_value, narrow_dict_type(dict2, key, dict)): + return False + return True + else: + raise TypeError(f"Unsupported type in dictionary: {type(dict1_value)}") + + return True + + +def test_optimizer_lr_model_snapshotters() -> None: + # Define several optimizers for a client + local_model = SingleLayerWithSeed() + global_model = SingleLayerWithSeed(seed=36) + optimizers: dict[str, Optimizer] = { + "local": torch.optim.Adam(local_model.parameters(), lr=0.001), + "global": torch.optim.Adam(global_model.parameters(), lr=0.01), + } + lr_schedulers: dict[str, LRScheduler] = { + "local": torch.optim.lr_scheduler.StepLR(optimizers["local"], step_size=30, gamma=0.1), + "global": torch.optim.lr_scheduler.StepLR(optimizers["global"], step_size=30, gamma=0.1), + } + models: dict[str, nn.Module] = { + "local": local_model, + "global": global_model, + } input_data = torch.randn(32, 100) target_data = torch.randn(32, 2) - - fl_client.optimizers = {"global": torch.optim.SGD(fl_client.model.parameters(), lr=0.001, momentum=0.1)} - fl_client.lr_schedulers = { - "global": torch.optim.lr_scheduler.StepLR(fl_client.optimizers["global"], step_size=30, gamma=0.1) + local_output = local_model(input_data) + global_output = global_model(input_data) + + criterion = torch.nn.BCEWithLogitsLoss() + local_loss = criterion(local_output, target_data) + global_loss = criterion(global_output, target_data) + + local_loss.backward() + global_loss.backward() + + optimizers["global"].step() + optimizers["local"].step() + + lr_schedulers["local"].step() + lr_schedulers["global"].step() + # Keep a copy of the client state as reference + old_optimizers = copy.deepcopy(optimizers) + old_lr_schedulers = copy.deepcopy(lr_schedulers) + old_models = copy.deepcopy(models) + + # snapshot client attribute that we want to save + optimizer_snapshotter = OptimizerSnapshotter() + optimizer_dict_to_be_saved = optimizer_snapshotter.save_attribute(attribute=optimizers) + model_snapshotter = TorchModuleSnapshotter() + model_dict_to_be_saved = model_snapshotter.save_attribute(attribute=models) + lr_scheduler_snapshotter = LRSchedulerSnapshotter() + lr_scheduler_dict_to_be_saved = lr_scheduler_snapshotter.save_attribute(attribute=lr_schedulers) + + # Now create new models, optimizers, and rl_schedulers + local_model_new = SingleLayerWithSeed(seed=4) + global_model_new = SingleLayerWithSeed(seed=5) + # New optimizers + new_optimizers: dict[str, Optimizer] = { + "local": torch.optim.Adam(local_model_new.parameters(), lr=0.001), + "global": torch.optim.Adam(global_model_new.parameters(), lr=0.01), + } + # New lr_schedulers + new_lr_schedulers: dict[str, LRScheduler] = { + "local": torch.optim.lr_scheduler.StepLR(optimizers["local"], step_size=30, gamma=0.1), + "global": torch.optim.lr_scheduler.StepLR(optimizers["global"], step_size=30, gamma=0.1), + } + new_models: dict[str, nn.Module] = { + "local": local_model_new, + "global": global_model_new, } - old_optimizers = copy.deepcopy(fl_client.optimizers) - old_lr_schedulers = copy.deepcopy(fl_client.lr_schedulers) - old_model = copy.deepcopy(fl_client.model) - - optimizer_snapshotter = OptimizerSnapshotter(fl_client) - lr_scheduler_snapshotter = LRSchedulerSnapshotter(fl_client) - model_snapshotter = TorchModuleSnapshotter(fl_client) - - snapshots = {} - snapshots.update(optimizer_snapshotter.save("optimizers", torch.optim.Optimizer)) - snapshots.update(lr_scheduler_snapshotter.save("lr_schedulers", torch.optim.lr_scheduler.LRScheduler)) - snapshots.update(model_snapshotter.save("model", torch.nn.Module)) - snapshots = copy.deepcopy(snapshots) - - fl_client.train_step(input_data, target_data) - - fl_client.optimizers["global"].step() - fl_client.lr_schedulers["global"].step() - - for key, value in fl_client.model.state_dict().items(): - assert not torch.equal(value, old_model.state_dict()[key]) - - for key, optimizers in fl_client.optimizers.items(): - assert optimizers.state_dict()["state"] != old_optimizers[key].state_dict()["state"] - - for key, schedulers in fl_client.lr_schedulers.items(): - assert schedulers.state_dict() != old_lr_schedulers[key].state_dict() - - optimizer_snapshotter.load(snapshots, "optimizers", torch.optim.Optimizer) - lr_scheduler_snapshotter.load(snapshots, "lr_schedulers", torch.optim.lr_scheduler.LRScheduler) - model_snapshotter.load(snapshots, "model", torch.nn.Module) - - for key, value in fl_client.model.state_dict().items(): - assert torch.equal(value, old_model.state_dict()[key]) - - for key, optimizers in fl_client.optimizers.items(): - assert optimizers.state_dict()["state"] == old_optimizers[key].state_dict()["state"] - - for key, schedulers in fl_client.lr_schedulers.items(): - assert schedulers.state_dict() == old_lr_schedulers[key].state_dict() - - -def test_loss_meter_snapshotter() -> None: - metrics = [Accuracy("accuracy")] - reporter = JsonReporter() - fl_client = BasicClient(data_path=Path(""), metrics=metrics, device=torch.device(0), reporters=[reporter]) - snapshots = {} - - fl_client.train_loss_meter.update(TrainingLosses(backward=torch.Tensor([35]), additional_losses=None)) - snapshotter = SerializableObjectSnapshotter(fl_client) - snapshots.update(snapshotter.save("train_loss_meter", LossMeter)) - snapshots = copy.deepcopy(snapshots) - old_loss_meter = copy.deepcopy(fl_client.train_loss_meter) - fl_client.train_loss_meter.update(TrainingLosses(backward=torch.Tensor([10]), additional_losses=None)) - assert len(old_loss_meter.losses_list) != len(fl_client.train_loss_meter.losses_list) - snapshotter.load(snapshots, "train_loss_meter", LossMeter) + for key, value in new_models.items(): + assert not compare_mixed_dictionaries(value.state_dict(), old_models[key].state_dict()) - assert len(old_loss_meter.losses_list) == len(fl_client.train_loss_meter.losses_list) - for i in range(len(fl_client.train_loss_meter.losses_list)): - assert old_loss_meter.losses_list[i].backward == fl_client.train_loss_meter.losses_list[i].backward - assert ( - old_loss_meter.losses_list[i].additional_losses - == fl_client.train_loss_meter.losses_list[i].additional_losses + for key, optimizer in new_optimizers.items(): + assert not compare_mixed_dictionaries( + optimizer.state_dict()["state"], old_optimizers[key].state_dict()["state"] ) + for key, schedulers in new_lr_schedulers.items(): + assert not compare_mixed_dictionaries(schedulers.state_dict(), old_lr_schedulers[key].state_dict()) -def test_reports_manager_snapshotter() -> None: - metrics = [Accuracy("accuracy")] - reporter = JsonReporter() - fl_client = BasicClient(data_path=Path(""), metrics=metrics, device=torch.device(0), reporters=[reporter]) - snapshots = {} - - fl_client.reports_manager.report({"start": "2012-12-12 12:12:10"}) - snapshotter = SerializableObjectSnapshotter(fl_client) - snapshots.update(snapshotter.save("reports_manager", ReportsManager)) - snapshots = copy.deepcopy(snapshots) - old_reports_manager = copy.deepcopy(fl_client.reports_manager) - fl_client.reports_manager.report({"shutdown": "2012-12-12 12:12:12"}) + # Load the state + optimizer_snapshotter.load_attribute(optimizer_dict_to_be_saved, new_optimizers) + model_snapshotter.load_attribute(model_dict_to_be_saved, new_models) + lr_scheduler_snapshotter.load_attribute(lr_scheduler_dict_to_be_saved, new_lr_schedulers) - assert isinstance(old_reports_manager.reporters[0], JsonReporter) and isinstance( - fl_client.reports_manager.reporters[0], JsonReporter - ) - - assert old_reports_manager.reporters[0].metrics != fl_client.reports_manager.reporters[0].metrics - - snapshotter.load(snapshots, "reports_manager", ReportsManager) - assert old_reports_manager.reporters[0].metrics == fl_client.reports_manager.reporters[0].metrics - - -def test_metric_manager_snapshotter() -> None: - metrics = [Accuracy("accuracy")] - reporter = JsonReporter() - fl_client = BasicClient(data_path=Path(""), metrics=metrics, device=torch.device(0), reporters=[reporter]) - snapshots = {} - preds: TorchPredType = { - "1": torch.tensor([0.7369, 0.5121, 0.2674, 0.5847, 0.4032, 0.7458, 0.9274, 0.3258, 0.7095, 0.0513]) - } - target: TorchTargetType = {"1": torch.tensor([0, 1, 0, 1, 1, 0, 1, 1, 0, 1])} - - fl_client.train_metric_manager.update(preds, target) - snapshotter = SerializableObjectSnapshotter(fl_client) - snapshots.update(snapshotter.save("train_metric_manager", MetricManager)) - snapshots = copy.deepcopy(snapshots) - old_train_metric_manager = copy.deepcopy(fl_client.train_metric_manager) - fl_client.train_metric_manager.update(preds, target) - assert isinstance(fl_client.train_metric_manager.metrics_per_prediction_type["1"][0], Accuracy) and isinstance( - old_train_metric_manager.metrics_per_prediction_type["1"][0], Accuracy - ) - assert len(fl_client.train_metric_manager.metrics_per_prediction_type["1"][0].accumulated_inputs) != len( - old_train_metric_manager.metrics_per_prediction_type["1"][0].accumulated_inputs - ) - assert len(fl_client.train_metric_manager.metrics_per_prediction_type["1"][0].accumulated_targets) != len( - old_train_metric_manager.metrics_per_prediction_type["1"][0].accumulated_targets - ) - - snapshotter.load(snapshots, "train_metric_manager", MetricManager) - assert len(fl_client.train_metric_manager.metrics_per_prediction_type["1"][0].accumulated_inputs) == len( - old_train_metric_manager.metrics_per_prediction_type["1"][0].accumulated_inputs - ) - assert len(fl_client.train_metric_manager.metrics_per_prediction_type["1"][0].accumulated_targets) == len( - old_train_metric_manager.metrics_per_prediction_type["1"][0].accumulated_targets - ) - - for i in range(len(fl_client.train_metric_manager.metrics_per_prediction_type["1"][0].accumulated_inputs)): - assert torch.all( - fl_client.train_metric_manager.metrics_per_prediction_type["1"][0].accumulated_inputs[i] - == old_train_metric_manager.metrics_per_prediction_type["1"][0].accumulated_inputs[i] - ) - assert torch.all( - fl_client.train_metric_manager.metrics_per_prediction_type["1"][0].accumulated_targets[i] - == old_train_metric_manager.metrics_per_prediction_type["1"][0].accumulated_targets[i] + # Check that the state of the new optimizers are the same as the ones saved. + for optimizer_type, new_optimizer in new_optimizers.items(): + assert compare_mixed_dictionaries( + new_optimizer.state_dict()["state"], old_optimizers[optimizer_type].state_dict()["state"] ) + # Check that the state of the new models are the same as the ones saved. + for model_type, new_model in new_models.items(): + assert compare_mixed_dictionaries(new_model.state_dict(), old_models[model_type].state_dict()) + + # Check that the state of the new lr_schedulers are the same as the ones saved. + for scheduler_type, new_scheduler in new_lr_schedulers.items(): + assert compare_mixed_dictionaries(new_scheduler.state_dict(), old_lr_schedulers[scheduler_type].state_dict()) diff --git a/tests/utils/test_early_stopper.py b/tests/utils/test_early_stopper.py new file mode 100644 index 000000000..5220773d0 --- /dev/null +++ b/tests/utils/test_early_stopper.py @@ -0,0 +1,87 @@ +from pathlib import Path +from unittest.mock import MagicMock + +from fl4health.clients.basic_client import BasicClient +from fl4health.utils.early_stopper import EarlyStopper +from fl4health.utils.snapshotter import SingletonSnapshotter + + +class MockBasicClient(BasicClient): + + def __init__(self): # type: ignore + # Val loss first goes down, then up (for less than interval steps), then down and up again + val_loss_values = [ + (0.5, None), + (0.4, None), + (0.3, None), + (0.1, None), + (0.15, None), + (0.2, None), + (0.05, None), + (0.15, None), + (0.2, None), + (0.3, None), + (0.01, None), + ] + self._fully_validate_or_test = MagicMock(side_effect=iter(val_loss_values)) # type: ignore + self.total_steps = 0 # Tracking the total training steps before early stopping. # type: ignore + self.client_name = "mock_client" # type: ignore + self.val_loader = None # type: ignore + self.val_loss_meter = None # type: ignore + self.val_metric_manager = None # type: ignore + + +def test_early_stopper_patience_3(tmp_path: Path) -> None: + mock_client = MockBasicClient() + early_stopper = EarlyStopper( + client=mock_client, + train_loop_checkpoint_dir=tmp_path, + patience=3, + interval_steps=1, + ) + # Override the snapshot_attrs of early stopper's state_checkpointer for test simplicity. + early_stopper.state_checkpointer.snapshot_attrs = { + "total_steps": (SingletonSnapshotter(), int), + } + + # Simulate training loop + for step in range(10): + mock_client.total_steps += 1 + if early_stopper.should_stop(step): + assert mock_client.total_steps == 10 + early_stopper.load_snapshot() + break + assert step == 9 + # Patience is 3, so we never get to see the actual best loss of 0.01 + assert early_stopper.best_score == 0.05 + # early stopper continues for 3 steps after the best score + assert early_stopper.count_down == 0 + assert mock_client.total_steps == 7 + + +def test_early_stopper_patience_4(tmp_path: Path) -> None: + mock_client = MockBasicClient() + early_stopper = EarlyStopper( + client=mock_client, + train_loop_checkpoint_dir=tmp_path, + patience=4, + interval_steps=1, + ) + # Override the snapshot_attrs of early stopper's state_checkpointer for test simplicity. + early_stopper.state_checkpointer.snapshot_attrs = { + "total_steps": (SingletonSnapshotter(), int), + } + + # Simulate training loop + for step in range(11): + mock_client.total_steps += 1 + if early_stopper.should_stop(step): + assert mock_client.total_steps == 11 + early_stopper.load_snapshot() + break + assert step == 10 + # Patience is 4, so we get to see the actual best loss of 0.01 + assert early_stopper.best_score == 0.01 + # early stopper's count_down is just restored to the max value + assert early_stopper.count_down == 4 + assert mock_client.total_steps == 11