From 0eadc3bccfe38066d90d0aff73ce48f8506723fd Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Wed, 11 Dec 2024 11:58:41 -0500 Subject: [PATCH 01/57] added upload data to miner hf utils. Add upload data whenever miner recieves a request. --- neurons/miner.py | 12 +++++ predictionnet/utils/miner_hf.py | 82 ++++++++++++++++++++++++++++++--- 2 files changed, 88 insertions(+), 6 deletions(-) diff --git a/neurons/miner.py b/neurons/miner.py index 35aae185..2019ac67 100644 --- a/neurons/miner.py +++ b/neurons/miner.py @@ -210,6 +210,18 @@ async def forward(self, synapse: predictionnet.protocol.Challenge) -> prediction model = load_model(model_path) data = prep_data() + + hf_interface = MinerHfInterface(self.config) + data_dict_list = data.to_dict("records") + success, metadata = hf_interface.upload_data( + hotkey=self.wallet.hotkey.ss58_address, data=data_dict_list, repo_id=self.config.hf_repo_id + ) + + if success: + bt.logging.success(f"Data uploaded successfully to {metadata['data_path']}") + else: + bt.logging.error(f"Data upload failed: {metadata['error']}") + scaler, _, _ = scale_data(data) # mse = create_and_save_base_model_lstm(scaler, X, y) diff --git a/predictionnet/utils/miner_hf.py b/predictionnet/utils/miner_hf.py index a5355d26..983310e7 100644 --- a/predictionnet/utils/miner_hf.py +++ b/predictionnet/utils/miner_hf.py @@ -1,4 +1,6 @@ +import csv import os +from datetime import datetime import bittensor as bt from dotenv import load_dotenv @@ -52,8 +54,11 @@ def upload_model(self, repo_id=None, model_path=None, hotkey=None): if not extension: raise ValueError(f"Could not determine file extension from model path: {model_path}") - model_name = f"{hotkey}{extension}" - bt.logging.debug(f"Generated model name: {model_name} from path: {model_path}") + model_name = f"model{extension}" + hotkey_path = f"{hotkey}/models" + model_full_path = f"{hotkey_path}/{model_name}" + + bt.logging.debug(f"Generated model path: {model_full_path}") bt.logging.debug(f"Checking if repo exists: {repo_id}") @@ -63,16 +68,81 @@ def upload_model(self, repo_id=None, model_path=None, hotkey=None): else: bt.logging.debug("Using existing repo") - bt.logging.debug(f"Uploading file as: {model_name}") + bt.logging.debug(f"Uploading file as: {model_full_path}") self.api.upload_file( - path_or_fileobj=model_path, path_in_repo=model_name, repo_id=repo_id, repo_type="model" + path_or_fileobj=model_path, path_in_repo=model_full_path, repo_id=repo_id, repo_type="model" ) commits = self.api.list_repo_commits(repo_id=repo_id, repo_type="model") if commits: - return True, {"hotkey": hotkey, "timestamp": commits[0].created_at.timestamp()} - return True, {} + return True, { + "hotkey": hotkey, + "timestamp": commits[0].created_at.timestamp(), + "model_path": model_full_path, + } + return True, {"model_path": model_full_path} except Exception as e: bt.logging.debug(f"Error in upload_model: {str(e)}") return False, {"error": str(e)} + + def upload_data(self, repo_id=None, data=None, hotkey=None): + """Upload training/validation data to HuggingFace Hub. + + Args: + repo_id (str, optional): Target repository ID. Defaults to config value. + data (list): List of dictionaries containing the data rows + hotkey (str, optional): Hotkey for data identification + + Returns: + tuple: (success, result) + - success (bool): Whether upload was successful + - result (dict): Contains 'hotkey', 'timestamp', and 'data_path' if successful, + 'error' if failed + + Raises: + ValueError: If required parameters are missing or data format is invalid + """ + if not repo_id: + repo_id = self.config.hf_repo_id + + if not data or not isinstance(data, list) or not all(isinstance(row, dict) for row in data): + raise ValueError("Data must be provided as a list of dictionaries") + + try: + # Create unique filename using timestamp + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + data_filename = f"data_{timestamp}.csv" + hotkey_path = f"{hotkey}/data" + data_full_path = f"{hotkey_path}/{data_filename}" + + bt.logging.debug(f"Preparing to upload data: {data_full_path}") + + # Create temporary CSV file + temp_data_path = f"/tmp/{data_filename}" + if data: + fieldnames = data[0].keys() + with open(temp_data_path, "w", newline="") as f: + writer = csv.DictWriter(f, fieldnames=fieldnames) + writer.writeheader() + writer.writerows(data) + + # Ensure repository exists + if not self.api.repo_exists(repo_id=repo_id, repo_type="model"): + self.api.create_repo(repo_id=repo_id, private=False) + bt.logging.debug("Created new repo") + + # Upload file + bt.logging.debug(f"Uploading data file: {data_full_path}") + self.api.upload_file( + path_or_fileobj=temp_data_path, path_in_repo=data_full_path, repo_id=repo_id, repo_type="model" + ) + + # Clean up temporary file + os.remove(temp_data_path) + + return True, {"hotkey": hotkey, "timestamp": timestamp, "data_path": data_full_path} + + except Exception as e: + bt.logging.debug(f"Error in upload_data: {str(e)}") + return False, {"error": str(e)} From d1d98ce4fb0514fbf20e5743fdb1178704afb589 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Wed, 11 Dec 2024 13:39:13 -0500 Subject: [PATCH 02/57] update challenge to accept a decrytion key. Data should now be encrypted --- neurons/miner.py | 14 +++++- predictionnet/protocol.py | 55 ++++++++++++++------- predictionnet/utils/miner_hf.py | 88 ++++++++++++++++----------------- 3 files changed, 91 insertions(+), 66 deletions(-) diff --git a/neurons/miner.py b/neurons/miner.py index 2019ac67..a986fc28 100644 --- a/neurons/miner.py +++ b/neurons/miner.py @@ -22,6 +22,7 @@ import typing import bittensor as bt +from cryptography.fernet import Fernet # ML imports from dotenv import load_dotenv @@ -211,14 +212,23 @@ async def forward(self, synapse: predictionnet.protocol.Challenge) -> prediction model = load_model(model_path) data = prep_data() + # Generate encryption key for this request + encryption_key = Fernet.generate_key() + + # Upload encrypted data to HuggingFace hf_interface = MinerHfInterface(self.config) data_dict_list = data.to_dict("records") success, metadata = hf_interface.upload_data( - hotkey=self.wallet.hotkey.ss58_address, data=data_dict_list, repo_id=self.config.hf_repo_id + hotkey=self.wallet.hotkey.ss58_address, + data=data_dict_list, + repo_id=self.config.hf_repo_id, + encryption_key=encryption_key, ) if success: - bt.logging.success(f"Data uploaded successfully to {metadata['data_path']}") + bt.logging.success(f"Encrypted data uploaded successfully to {metadata['data_path']}") + synapse.data = metadata["data_path"] # Store the data path in synapse + synapse.decryption_key = encryption_key.decode() # Provide key to validator else: bt.logging.error(f"Data upload failed: {metadata['error']}") diff --git a/predictionnet/protocol.py b/predictionnet/protocol.py index 652460f6..8b728eb7 100644 --- a/predictionnet/protocol.py +++ b/predictionnet/protocol.py @@ -21,6 +21,7 @@ import bittensor as bt import pydantic +from pydantic import SecretStr # TODO(developer): Rewrite with your protocol definition. @@ -44,13 +45,16 @@ class Challenge(bt.Synapse): """ - A simple dummy protocol representation which uses bt.Synapse as its base. - This protocol helps in handling dummy request and response communication between - the miner and the validator. + Protocol for handling encrypted prediction challenges between miners and validators. + Includes secure handling of decryption keys and manages model/data references. Attributes: - - dummy_input: An integer value representing the input request sent by the validator. - - dummy_output: An optional integer value which, when filled, represents the response from the miner. + repo_id: Repository identifier where the model is stored + model: Identifier for the specific model to use + decryption_key: Securely stored key for decrypting data/models + data: Identifier for the data to be used + timestamp: Time at which the validation is taking place + prediction: List of predicted values for next 6 5m candles """ repo_id: Optional[str] = pydantic.Field( @@ -65,34 +69,47 @@ class Challenge(bt.Synapse): description="Which model to use", ) - # Required request input, filled by sending dendrite caller. + decryption_key: Optional[SecretStr] = pydantic.Field( + default=None, + title="Decryption Key", + description="Secure key for decrypting sensitive data/models", + ) + + data: Optional[str] = pydantic.Field( + default=None, + title="Data ID", + description="Which data to use", + ) + timestamp: str = pydantic.Field( ..., title="Timestamp", description="The time stamp at which the validation is taking place for", allow_mutation=False, ) - # Optional request output, filled by recieving axon. + prediction: Optional[List[float]] = pydantic.Field( default=None, title="Predictions", description="Next 6 5m candles' predictions for closing price of S&P 500", ) - def deserialize(self) -> int: + def deserialize(self) -> List[float]: """ - Deserialize the dummy output. This method retrieves the response from - the miner in the form of dummy_output, deserializes it and returns it - as the output of the dendrite.query() call. + Deserialize the prediction output from the miner. Returns: - - int: The deserialized response, which in this case is the value of dummy_output. - - Example: - Assuming a Dummy instance has a dummy_output value of 5: - >>> dummy_instance = Dummy(dummy_input=4) - >>> dummy_instance.dummy_output = 5 - >>> dummy_instance.deserialize() - 5 + List[float]: The deserialized predictions for the next 6 5m candles. """ return self.prediction + + def get_decryption_key(self) -> Optional[str]: + """ + Safely retrieve the decryption key when needed. + + Returns: + Optional[str]: The decryption key value if set, None otherwise. + """ + if self.decryption_key is not None: + return self.decryption_key.get_secret_value() + return None diff --git a/predictionnet/utils/miner_hf.py b/predictionnet/utils/miner_hf.py index 983310e7..56afaef1 100644 --- a/predictionnet/utils/miner_hf.py +++ b/predictionnet/utils/miner_hf.py @@ -1,26 +1,16 @@ import csv import os from datetime import datetime +from io import StringIO import bittensor as bt +from cryptography.fernet import Fernet from dotenv import load_dotenv from huggingface_hub import HfApi class MinerHfInterface: - """Interface for managing model uploads and metadata on HuggingFace Hub. - - Handles authentication, model uploads, and metadata retrieval for miner models - using the HuggingFace Hub API. - - Args: - config (bt.Config): Configuration object containing model and repository information. - Must include 'model' and 'hf_repo_id' attributes. - - Attributes: - api (HfApi): Authenticated HuggingFace API client - config (bt.Config): Stored configuration object - """ + """Interface for managing model uploads and metadata on HuggingFace Hub.""" def __init__(self, config: "bt.Config"): """Initialize the HuggingFace interface with configuration and authentication.""" @@ -31,22 +21,7 @@ def __init__(self, config: "bt.Config"): bt.logging.debug(f"Initializing with config: model={config.model}, repo_id={config.hf_repo_id}") def upload_model(self, repo_id=None, model_path=None, hotkey=None): - """Upload a model file to HuggingFace Hub. - - Args: - repo_id (str, optional): Target repository ID. Defaults to config value. - model_path (str, optional): Path to model file. Defaults to config value. - hotkey (str, optional): Hotkey for model identification. - - Returns: - tuple: (success, result) - - success (bool): Whether upload was successful - - result (dict): Contains 'hotkey' and 'timestamp' if successful, - 'error' if failed - - Raises: - ValueError: If model path extension cannot be determined or required parameters are missing - """ + """Upload a model file to HuggingFace Hub.""" bt.logging.debug(f"Trying to upload model: repo_id={repo_id}, model_path={model_path}, hotkey={hotkey}") try: @@ -59,7 +34,6 @@ def upload_model(self, repo_id=None, model_path=None, hotkey=None): model_full_path = f"{hotkey_path}/{model_name}" bt.logging.debug(f"Generated model path: {model_full_path}") - bt.logging.debug(f"Checking if repo exists: {repo_id}") if not self.api.repo_exists(repo_id=repo_id, repo_type="model"): @@ -70,7 +44,10 @@ def upload_model(self, repo_id=None, model_path=None, hotkey=None): bt.logging.debug(f"Uploading file as: {model_full_path}") self.api.upload_file( - path_or_fileobj=model_path, path_in_repo=model_full_path, repo_id=repo_id, repo_type="model" + path_or_fileobj=model_path, + path_in_repo=model_full_path, + repo_id=repo_id, + repo_type="model", ) commits = self.api.list_repo_commits(repo_id=repo_id, repo_type="model") @@ -86,13 +63,14 @@ def upload_model(self, repo_id=None, model_path=None, hotkey=None): bt.logging.debug(f"Error in upload_model: {str(e)}") return False, {"error": str(e)} - def upload_data(self, repo_id=None, data=None, hotkey=None): - """Upload training/validation data to HuggingFace Hub. + def upload_data(self, repo_id=None, data=None, hotkey=None, encryption_key=None): + """Upload encrypted training/validation data to HuggingFace Hub. Args: repo_id (str, optional): Target repository ID. Defaults to config value. data (list): List of dictionaries containing the data rows hotkey (str, optional): Hotkey for data identification + encryption_key (str): Base64-encoded key for encrypting the data Returns: tuple: (success, result) @@ -109,39 +87,59 @@ def upload_data(self, repo_id=None, data=None, hotkey=None): if not data or not isinstance(data, list) or not all(isinstance(row, dict) for row in data): raise ValueError("Data must be provided as a list of dictionaries") + if not encryption_key: + raise ValueError("Encryption key must be provided") + try: + fernet = Fernet(encryption_key.encode() if isinstance(encryption_key, str) else encryption_key) + # Create unique filename using timestamp timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - data_filename = f"data_{timestamp}.csv" + data_filename = f"data_{timestamp}.enc" hotkey_path = f"{hotkey}/data" data_full_path = f"{hotkey_path}/{data_filename}" - bt.logging.debug(f"Preparing to upload data: {data_full_path}") + bt.logging.debug(f"Preparing to upload encrypted data: {data_full_path}") - # Create temporary CSV file - temp_data_path = f"/tmp/{data_filename}" + # Convert data to CSV format in memory + csv_buffer = StringIO() if data: fieldnames = data[0].keys() - with open(temp_data_path, "w", newline="") as f: - writer = csv.DictWriter(f, fieldnames=fieldnames) - writer.writeheader() - writer.writerows(data) + writer = csv.DictWriter(csv_buffer, fieldnames=fieldnames) + writer.writeheader() + writer.writerows(data) + + # Encrypt the CSV data + csv_data = csv_buffer.getvalue().encode() + encrypted_data = fernet.encrypt(csv_data) + + # Create temporary encrypted file + temp_data_path = f"/tmp/{data_filename}" + with open(temp_data_path, "wb") as f: + f.write(encrypted_data) # Ensure repository exists if not self.api.repo_exists(repo_id=repo_id, repo_type="model"): self.api.create_repo(repo_id=repo_id, private=False) bt.logging.debug("Created new repo") - # Upload file - bt.logging.debug(f"Uploading data file: {data_full_path}") + # Upload encrypted file + bt.logging.debug(f"Uploading encrypted data file: {data_full_path}") self.api.upload_file( - path_or_fileobj=temp_data_path, path_in_repo=data_full_path, repo_id=repo_id, repo_type="model" + path_or_fileobj=temp_data_path, + path_in_repo=data_full_path, + repo_id=repo_id, + repo_type="model", ) # Clean up temporary file os.remove(temp_data_path) - return True, {"hotkey": hotkey, "timestamp": timestamp, "data_path": data_full_path} + return True, { + "hotkey": hotkey, + "timestamp": timestamp, + "data_path": data_full_path, + } except Exception as e: bt.logging.debug(f"Error in upload_data: {str(e)}") From fe7701af2c11c0cb95a4b2acb7c339fcbc643c10 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Wed, 11 Dec 2024 14:56:00 -0500 Subject: [PATCH 03/57] decrypt data and upload to foundryservices dataset --- predictionnet/utils/config.py | 7 + predictionnet/utils/dataset_manager.py | 196 +++++++++++++++++++++++++ predictionnet/validator/forward.py | 125 +++++++++------- 3 files changed, 273 insertions(+), 55 deletions(-) create mode 100644 predictionnet/utils/dataset_manager.py diff --git a/predictionnet/utils/config.py b/predictionnet/utils/config.py index d814d295..2365464a 100644 --- a/predictionnet/utils/config.py +++ b/predictionnet/utils/config.py @@ -156,6 +156,13 @@ def add_args(cls, parser): default=4096, ) + parser.add_argument( + "--neuron.organization", + type=str, + help="HuggingFace organization name for dataset storage", + default="foundryservices", + ) + # MINER ONLY CONFIG else: bt.logging.debug("Adding miner-specific arguments") diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py new file mode 100644 index 00000000..857f3514 --- /dev/null +++ b/predictionnet/utils/dataset_manager.py @@ -0,0 +1,196 @@ +# The MIT License (MIT) +# Copyright © 2024 Foundry Digital + +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of +# the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +import asyncio +import json +import os +from concurrent.futures import ThreadPoolExecutor +from datetime import datetime +from typing import Dict, Optional, Tuple + +import bittensor as bt +from cryptography.fernet import Fernet +from huggingface_hub import HfApi, create_repo, repository + + +class DatasetManager: + def __init__(self, organization: str): + """ + Initialize the DatasetManager. + Args: + organization: The HuggingFace organization name + """ + self.token = os.getenv("HF_ACCESS_TOKEN") + if not self.token: + raise ValueError("HF_ACCESS_TOKEN environment variable not set") + + self.api = HfApi(token=self.token) + self.organization = organization + self.MAX_REPO_SIZE = 300 * 1024 * 1024 * 1024 # 300GB + self.executor = ThreadPoolExecutor(max_workers=1) + + def _get_current_repo_name(self) -> str: + """Generate repository name based on current date.""" + return f"dataset-{datetime.now().strftime('%Y-%m')}" + + def _get_repo_size(self, repo_id: str) -> int: + """ + Calculate total size of repository in bytes. + + Args: + repo_id: Full repository ID (org/name) + + Returns: + Total size in bytes + """ + try: + files = self.api.list_repo_files(repo_id) + total_size = 0 + + for file_info in files: + try: + file_metadata = self.api.get_file_metadata(repo_id=repo_id, filename=file_info) + total_size += file_metadata.size + except Exception as e: + bt.logging.error(f"Error getting metadata for {file_info}: {e}") + continue + + return total_size + except Exception: + return 0 + + def _create_new_repo(self, repo_name: str) -> str: + """ + Create a new dataset repository. + + Args: + repo_name: Name of the repository + + Returns: + Full repository ID + """ + repo_id = f"{self.organization}/{repo_name}" + try: + create_repo(repo_id=repo_id, repo_type="dataset", private=False, token=self.token) + bt.logging.success(f"Created new repository: {repo_id}") + except Exception as e: + bt.logging.error(f"Error creating repository {repo_id}: {e}") + raise + + return repo_id + + def decrypt_data(self, data_path: str, decryption_key: str) -> Tuple[bool, Dict]: + """ + Decrypt data from a file using the provided key. + + Args: + data_path: Path to the encrypted data file + decryption_key: Key to decrypt the data + + Returns: + Tuple of (success, result) + where result is either the decrypted data or an error message + """ + try: + fernet = Fernet(decryption_key.encode()) + + with open(data_path, "rb") as file: + encrypted_data = file.read() + + decrypted_data = fernet.decrypt(encrypted_data) + return True, json.loads(decrypted_data.decode()) + + except Exception as e: + return False, {"error": str(e)} + + def store_data( + self, timestamp: str, miner_data: Dict, predictions: Dict, metadata: Optional[Dict] = None + ) -> Tuple[bool, Dict]: + """ + Store data in the appropriate dataset repository. + + Args: + timestamp: Current timestamp + miner_data: Dictionary containing decrypted miner data + predictions: Dictionary containing prediction results + metadata: Optional metadata about the collection + + Returns: + Tuple of (success, result) + where result contains repository info or error message + """ + try: + # Get or create repository + repo_name = self._get_current_repo_name() + repo_id = f"{self.organization}/{repo_name}" + + # Check repository size + current_size = self._get_repo_size(repo_id) + + # Create new repo if would exceed size limit + data_size = len(json.dumps(miner_data).encode("utf-8")) + if current_size + data_size > self.MAX_REPO_SIZE: + repo_name = f"dataset-{datetime.now().strftime('%Y-%m-%d')}" + repo_id = self._create_new_repo(repo_name) + + # Prepare data entry + dataset_entry = {"timestamp": timestamp, "market_data": miner_data, "predictions": predictions} + if metadata: + dataset_entry["metadata"] = metadata + + # Upload to repository + with repository.Repository(local_dir=".", clone_from=repo_id, token=self.token) as repo: + filename = f"market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json" + file_path = os.path.join(repo.local_dir, filename) + + with open(file_path, "w") as f: + json.dump(dataset_entry, f) + + commit_url = repo.push_to_hub() + + return True, {"repo_id": repo_id, "filename": filename, "commit_url": commit_url} + + except Exception as e: + return False, {"error": str(e)} + + async def store_data_async( + self, timestamp: str, miner_data: Dict, predictions: Dict, metadata: Optional[Dict] = None + ) -> None: + """ + Asynchronously store data in the appropriate dataset repository. + Does not block or return results. + """ + loop = asyncio.get_event_loop() + + async def _store(): + try: + # Run the blocking HuggingFace operations in a thread pool + result = await loop.run_in_executor( + self.executor, lambda: self.store_data(timestamp, miner_data, predictions, metadata) + ) + + success, upload_result = result + if success: + bt.logging.success(f"Stored market data in dataset: {upload_result['repo_id']}") + else: + bt.logging.error(f"Failed to store market data: {upload_result['error']}") + + except Exception as e: + bt.logging.error(f"Error in async data storage: {str(e)}") + + # Fire and forget - don't await the result + asyncio.create_task(_store()) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 2c4e0b06..abc159fd 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -1,18 +1,6 @@ # The MIT License (MIT) -# Copyright © 2023 Yuma Rao -# TODO(developer): Set your name -# Copyright © 2023 -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. +# Copyright © 2024 Foundry Digital + import time from datetime import datetime, timedelta @@ -21,95 +9,122 @@ from numpy import full, nan from pytz import timezone -# Import Validator Template import predictionnet +from predictionnet.utils.dataset_manager import DatasetManager from predictionnet.utils.uids import check_uid_availability from predictionnet.validator.reward import get_rewards +async def get_available_miner_uids(self): + """Get list of available miner UIDs.""" + miner_uids = [] + for uid in range(len(self.metagraph.S)): + uid_is_available = check_uid_availability(self.metagraph, uid, self.config.neuron.vpermit_tao_limit) + if uid_is_available: + miner_uids.append(uid) + return miner_uids + + +async def process_miner_response(self, uid, response, timestamp): + """Process and decrypt data from a single miner response.""" + bt.logging.info(f"Processing response from UID {uid}") + + processed_data = None + if response.data and response.decryption_key: + success, data = self.dataset_manager.decrypt_data( + data_path=response.data, decryption_key=response.decryption_key + ) + + if success: + processed_data = {"data": data, "hotkey": self.metagraph.hotkeys[uid], "timestamp": timestamp} + bt.logging.success(f"Successfully decrypted data from UID {uid}") + else: + bt.logging.error(f"Failed to decrypt data from UID {uid}: {data['error']}") + else: + bt.logging.warning(f"Missing data or decryption key from UID {uid}") + + predictions_data = { + "prediction": response.prediction if response.prediction else None, + "hotkey": self.metagraph.hotkeys[uid], + } + + return processed_data, predictions_data + + async def forward(self): """ The forward function is called by the validator every time step. It is responsible for querying the network and scoring the responses. - Args: - self (:obj:`bittensor.neuron.Neuron`): The neuron object which contains all the necessary state for the validator. """ - # TODO(developer): Define how the validator selects a miner to query, how often, etc. - # get_random_uids is an example method, but you can replace it with your own. + # Initialize dataset manager if not already done + if not hasattr(self, "dataset_manager"): + self.dataset_manager = DatasetManager(organization=self.config.neuron.organization) - # wait for market to be open ny_timezone = timezone("America/New_York") current_time_ny = datetime.now(ny_timezone) bt.logging.info("Current time: ", current_time_ny) - # block forward from running if market is closed + while True: if await self.is_valid_time(): bt.logging.info("Market is open. Begin processes requests") break else: bt.logging.info("Market is closed. Sleeping for 2 minutes...") - time.sleep(120) # Sleep for 5 minutes before checking again + time.sleep(120) if datetime.now(ny_timezone) - current_time_ny >= timedelta(hours=1): self.resync_metagraph() self.set_weights() self.past_predictions = [full((self.N_TIMEPOINTS, self.N_TIMEPOINTS), nan)] * len(self.hotkeys) current_time_ny = datetime.now(ny_timezone) - # miner_uids = get_random_uids(self, k=min(self.config.neuron.sample_size, self.metagraph.n.item())) - # get all uids - miner_uids = [] - for uid in range(len(self.metagraph.S)): - uid_is_available = check_uid_availability(self.metagraph, uid, self.config.neuron.vpermit_tao_limit) - if uid_is_available: - miner_uids.append(uid) - - # Here input data should be gathered to send to the miners - # TODO(create get_input_data()) - current_time_ny = datetime.now(ny_timezone) - timestamp = current_time_ny.isoformat() - - # Build synapse for request - # Replace dummy_input with actually defined variables in protocol.py - # This can be combined with line 49 - synapse = predictionnet.protocol.Challenge( - timestamp=timestamp, - ) + miner_uids = await get_available_miner_uids(self) + timestamp = datetime.now(ny_timezone).isoformat() + synapse = predictionnet.protocol.Challenge(timestamp=timestamp) responses = self.dendrite.query( - # Send the query to selected miner axons in the network. axons=[self.metagraph.axons[uid] for uid in miner_uids], - # Construct a dummy query. This simply contains a single integer. - # This can be simplified later to all build from here synapse=synapse, - # synapse=Dummy(dummy_input=self.step), - # All responses have the deserialize function called on them before returning. - # You are encouraged to define your own deserialization function. - # Other subnets have this turned to false, I am unsure of whether this should be set to true deserialize=False, ) - # Log the results for monitoring purposes. + + processed_data = {} + predictions_data = {} + for uid, response in zip(miner_uids, responses): - bt.logging.info(f"UID: {uid} | Predictions: {response.prediction}") + proc_data, pred_data = await process_miner_response(self, uid, response, timestamp) + if proc_data: + processed_data[str(uid)] = proc_data + predictions_data[str(uid)] = pred_data rewards = get_rewards(self, responses=responses, miner_uids=miner_uids) + for uid, reward in zip(miner_uids, rewards.tolist()): + predictions_data[str(uid)]["reward"] = float(reward) + + metadata = { + "total_miners": len(miner_uids), + "successful_decryptions": sum( + 1 for uid_data in processed_data.values() if "error" not in uid_data.get("data", {}) + ), + "market_conditions": {"timezone": ny_timezone.zone, "is_market_open": True}, + } + + await self.dataset_manager.store_data_async( + timestamp=timestamp, miner_data=processed_data, predictions=predictions_data, metadata=metadata + ) wandb_val_log = { "miners_info": { miner_uid: { "miner_response": response.prediction, "miner_reward": reward, + "data_decrypted": str(miner_uid) in processed_data, } for miner_uid, response, reward in zip(miner_uids, responses, rewards.tolist()) } } - wandb.log(wandb_val_log) - # Potentially will need some - bt.logging.info(f"Scored responses: {rewards}") - # Update the scores based on the rewards. You may want to define your own update_scores function for custom behavior. models_confirmed = self.confirm_models(responses, miner_uids) bt.logging.info(f"Models Confirmed: {models_confirmed}") rewards = [0 if not model_confirmed else reward for reward, model_confirmed in zip(rewards, models_confirmed)] - # Check base validator file self.update_scores(rewards, miner_uids) From 8e6cdcd0a8d794ab4b2150d858b6e0daf57059ed Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Wed, 11 Dec 2024 15:05:08 -0500 Subject: [PATCH 04/57] update decrypt_data --- predictionnet/utils/dataset_manager.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index 857f3514..dbbfff7d 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -99,14 +99,20 @@ def decrypt_data(self, data_path: str, decryption_key: str) -> Tuple[bool, Dict] Args: data_path: Path to the encrypted data file - decryption_key: Key to decrypt the data + decryption_key: Key to decrypt the data (can be str or SecretStr) Returns: Tuple of (success, result) where result is either the decrypted data or an error message """ try: - fernet = Fernet(decryption_key.encode()) + # Handle both SecretStr and regular string types + if hasattr(decryption_key, "get_secret_value"): + key = decryption_key.get_secret_value().encode() + else: + key = decryption_key.encode() + + fernet = Fernet(key) with open(data_path, "rb") as file: encrypted_data = file.read() From 3fcae57d3a14b6139e1ea322e96c7748281f70c6 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Wed, 11 Dec 2024 15:14:20 -0500 Subject: [PATCH 05/57] update decryption_key to be bytes --- neurons/miner.py | 2 +- predictionnet/protocol.py | 3 +-- predictionnet/utils/dataset_manager.py | 12 +++--------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/neurons/miner.py b/neurons/miner.py index a986fc28..7db5a9dd 100644 --- a/neurons/miner.py +++ b/neurons/miner.py @@ -228,7 +228,7 @@ async def forward(self, synapse: predictionnet.protocol.Challenge) -> prediction if success: bt.logging.success(f"Encrypted data uploaded successfully to {metadata['data_path']}") synapse.data = metadata["data_path"] # Store the data path in synapse - synapse.decryption_key = encryption_key.decode() # Provide key to validator + synapse.decryption_key = encryption_key # Provide key to validator else: bt.logging.error(f"Data upload failed: {metadata['error']}") diff --git a/predictionnet/protocol.py b/predictionnet/protocol.py index 8b728eb7..9e3806aa 100644 --- a/predictionnet/protocol.py +++ b/predictionnet/protocol.py @@ -21,7 +21,6 @@ import bittensor as bt import pydantic -from pydantic import SecretStr # TODO(developer): Rewrite with your protocol definition. @@ -69,7 +68,7 @@ class Challenge(bt.Synapse): description="Which model to use", ) - decryption_key: Optional[SecretStr] = pydantic.Field( + decryption_key: Optional[bytes] = pydantic.Field( default=None, title="Decryption Key", description="Secure key for decrypting sensitive data/models", diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index dbbfff7d..88c5cea3 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -93,26 +93,20 @@ def _create_new_repo(self, repo_name: str) -> str: return repo_id - def decrypt_data(self, data_path: str, decryption_key: str) -> Tuple[bool, Dict]: + def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dict]: """ Decrypt data from a file using the provided key. Args: data_path: Path to the encrypted data file - decryption_key: Key to decrypt the data (can be str or SecretStr) + decryption_key: Raw Fernet key in bytes format Returns: Tuple of (success, result) where result is either the decrypted data or an error message """ try: - # Handle both SecretStr and regular string types - if hasattr(decryption_key, "get_secret_value"): - key = decryption_key.get_secret_value().encode() - else: - key = decryption_key.encode() - - fernet = Fernet(key) + fernet = Fernet(decryption_key) with open(data_path, "rb") as file: encrypted_data = file.read() From 06dfca4a09d4b93bd95099649fc27887f7ea247f Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Wed, 11 Dec 2024 15:18:50 -0500 Subject: [PATCH 06/57] fix deserialization of decription key --- predictionnet/protocol.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/predictionnet/protocol.py b/predictionnet/protocol.py index 9e3806aa..0d15d904 100644 --- a/predictionnet/protocol.py +++ b/predictionnet/protocol.py @@ -102,13 +102,11 @@ def deserialize(self) -> List[float]: """ return self.prediction - def get_decryption_key(self) -> Optional[str]: + def get_decryption_key(self) -> Optional[bytes]: """ Safely retrieve the decryption key when needed. Returns: - Optional[str]: The decryption key value if set, None otherwise. + Optional[bytes]: The raw Fernet key bytes if set, None otherwise. """ - if self.decryption_key is not None: - return self.decryption_key.get_secret_value() - return None + return self.decryption_key From 17b7af8cc8af2439dcefceaf894cc6b4c4ef819d Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Wed, 11 Dec 2024 15:35:31 -0500 Subject: [PATCH 07/57] updated path for decryption to be the full path to the miners encrypted hf data --- predictionnet/validator/forward.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index abc159fd..70144031 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -27,12 +27,13 @@ async def get_available_miner_uids(self): async def process_miner_response(self, uid, response, timestamp): """Process and decrypt data from a single miner response.""" - bt.logging.info(f"Processing response from UID {uid}") - processed_data = None - if response.data and response.decryption_key: + if response.data and response.decryption_key and response.repo_id: + # Construct full data path using repo_id + full_data_path = f"{response.repo_id}/{response.data}" + success, data = self.dataset_manager.decrypt_data( - data_path=response.data, decryption_key=response.decryption_key + data_path=full_data_path, decryption_key=response.decryption_key ) if success: @@ -40,8 +41,6 @@ async def process_miner_response(self, uid, response, timestamp): bt.logging.success(f"Successfully decrypted data from UID {uid}") else: bt.logging.error(f"Failed to decrypt data from UID {uid}: {data['error']}") - else: - bt.logging.warning(f"Missing data or decryption key from UID {uid}") predictions_data = { "prediction": response.prediction if response.prediction else None, From 418294dfd6680fbe7563b2a25fa229d1f419540c Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Wed, 11 Dec 2024 15:42:52 -0500 Subject: [PATCH 08/57] ensure that decryption works from hf path --- predictionnet/utils/dataset_manager.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index 88c5cea3..8e94e2c2 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -24,7 +24,7 @@ import bittensor as bt from cryptography.fernet import Fernet -from huggingface_hub import HfApi, create_repo, repository +from huggingface_hub import HfApi, create_repo, hf_hub_download, repository class DatasetManager: @@ -95,10 +95,10 @@ def _create_new_repo(self, repo_name: str) -> str: def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dict]: """ - Decrypt data from a file using the provided key. + Decrypt data from a public HuggingFace repository file using the provided key. Args: - data_path: Path to the encrypted data file + data_path: Full repository path (repo_id/filename) to the encrypted data file decryption_key: Raw Fernet key in bytes format Returns: @@ -106,9 +106,15 @@ def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dic where result is either the decrypted data or an error message """ try: - fernet = Fernet(decryption_key) + # Split the data_path into repo_id and filename + repo_id, filename = data_path.split("/", 1) + + # Download the file from HuggingFace (no token needed for public datasets) + local_path = hf_hub_download(repo_id=repo_id, filename=filename) - with open(data_path, "rb") as file: + # Decrypt the downloaded file + fernet = Fernet(decryption_key) + with open(local_path, "rb") as file: encrypted_data = file.read() decrypted_data = fernet.decrypt(encrypted_data) From b1f10fe6544872c86dda4cf85ca9f051b2aa2bdb Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Wed, 11 Dec 2024 15:50:32 -0500 Subject: [PATCH 09/57] add logging --- predictionnet/utils/dataset_manager.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index 8e94e2c2..d964cd17 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -106,21 +106,34 @@ def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dic where result is either the decrypted data or an error message """ try: + bt.logging.info(f"Attempting to decrypt data from path: {data_path}") + # Split the data_path into repo_id and filename repo_id, filename = data_path.split("/", 1) + bt.logging.info(f"Split path - repo_id: {repo_id}, filename: {filename}") # Download the file from HuggingFace (no token needed for public datasets) + bt.logging.info(f"Downloading file from HuggingFace - repo_id: {repo_id}, filename: {filename}") local_path = hf_hub_download(repo_id=repo_id, filename=filename) + bt.logging.info(f"File downloaded to local path: {local_path}") # Decrypt the downloaded file + bt.logging.info("Initializing Fernet with provided key") fernet = Fernet(decryption_key) + with open(local_path, "rb") as file: + bt.logging.info("Reading encrypted data from file") encrypted_data = file.read() + bt.logging.info("Attempting to decrypt data") decrypted_data = fernet.decrypt(encrypted_data) + bt.logging.info("Successfully decrypted data") + return True, json.loads(decrypted_data.decode()) except Exception as e: + bt.logging.error(f"Error in decrypt_data: {str(e)}") + bt.logging.error(f"Error type: {type(e).__name__}") return False, {"error": str(e)} def store_data( From 246de6ecaf3e299ec73b328f4a3b18bac327e136 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Wed, 11 Dec 2024 16:08:00 -0500 Subject: [PATCH 10/57] ensure proper format for repo_id when downloading from huggingface --- predictionnet/utils/dataset_manager.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index d964cd17..48f8b68a 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -98,7 +98,7 @@ def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dic Decrypt data from a public HuggingFace repository file using the provided key. Args: - data_path: Full repository path (repo_id/filename) to the encrypted data file + data_path: Full repository path (org/repo_type/hotkey/data/filename format) decryption_key: Raw Fernet key in bytes format Returns: @@ -108,13 +108,15 @@ def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dic try: bt.logging.info(f"Attempting to decrypt data from path: {data_path}") - # Split the data_path into repo_id and filename - repo_id, filename = data_path.split("/", 1) - bt.logging.info(f"Split path - repo_id: {repo_id}, filename: {filename}") + # Split path into components based on known structure + parts = data_path.split("/") + repo_id = f"{parts[0]}/{parts[1]}" # org/repo_type (e.g., pcarlson-foundry-digital/mining_models) + subfolder = f"{parts[2]}/data" # hotkey/data + filename = parts[-1] # actual filename - # Download the file from HuggingFace (no token needed for public datasets) - bt.logging.info(f"Downloading file from HuggingFace - repo_id: {repo_id}, filename: {filename}") - local_path = hf_hub_download(repo_id=repo_id, filename=filename) + bt.logging.info(f"Downloading - repo_id: {repo_id}, subfolder: {subfolder}, filename: {filename}") + + local_path = hf_hub_download(repo_id=repo_id, filename=filename, subfolder=subfolder) bt.logging.info(f"File downloaded to local path: {local_path}") # Decrypt the downloaded file From 5203abd6d2a4d85458f856d042df132ad7c0b124 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Thu, 12 Dec 2024 12:27:33 -0500 Subject: [PATCH 11/57] Update forward.py to decrypt data properly --- neurons/miner.py | 3 +- predictionnet/utils/dataset_manager.py | 201 ++++++++++++++++++------- predictionnet/utils/miner_hf.py | 20 +-- predictionnet/validator/forward.py | 86 ++++++++--- 4 files changed, 220 insertions(+), 90 deletions(-) diff --git a/neurons/miner.py b/neurons/miner.py index 7db5a9dd..252ce87e 100644 --- a/neurons/miner.py +++ b/neurons/miner.py @@ -217,10 +217,9 @@ async def forward(self, synapse: predictionnet.protocol.Challenge) -> prediction # Upload encrypted data to HuggingFace hf_interface = MinerHfInterface(self.config) - data_dict_list = data.to_dict("records") success, metadata = hf_interface.upload_data( hotkey=self.wallet.hotkey.ss58_address, - data=data_dict_list, + data=data, repo_id=self.config.hf_repo_id, encryption_key=encryption_key, ) diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index 48f8b68a..795e4d71 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -20,9 +20,11 @@ import os from concurrent.futures import ThreadPoolExecutor from datetime import datetime +from io import StringIO from typing import Dict, Optional, Tuple import bittensor as bt +import pandas as pd from cryptography.fernet import Fernet from huggingface_hub import HfApi, create_repo, hf_hub_download, repository @@ -93,61 +95,50 @@ def _create_new_repo(self, repo_name: str) -> str: return repo_id - def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dict]: + def verify_encryption_key(self, key: bytes) -> bool: """ - Decrypt data from a public HuggingFace repository file using the provided key. + Verify that an encryption key is valid for Fernet. Args: - data_path: Full repository path (org/repo_type/hotkey/data/filename format) - decryption_key: Raw Fernet key in bytes format + key: The key to verify Returns: - Tuple of (success, result) - where result is either the decrypted data or an error message + bool: True if key is valid """ try: - bt.logging.info(f"Attempting to decrypt data from path: {data_path}") - - # Split path into components based on known structure - parts = data_path.split("/") - repo_id = f"{parts[0]}/{parts[1]}" # org/repo_type (e.g., pcarlson-foundry-digital/mining_models) - subfolder = f"{parts[2]}/data" # hotkey/data - filename = parts[-1] # actual filename - - bt.logging.info(f"Downloading - repo_id: {repo_id}, subfolder: {subfolder}, filename: {filename}") - - local_path = hf_hub_download(repo_id=repo_id, filename=filename, subfolder=subfolder) - bt.logging.info(f"File downloaded to local path: {local_path}") - - # Decrypt the downloaded file - bt.logging.info("Initializing Fernet with provided key") - fernet = Fernet(decryption_key) - - with open(local_path, "rb") as file: - bt.logging.info("Reading encrypted data from file") - encrypted_data = file.read() - - bt.logging.info("Attempting to decrypt data") - decrypted_data = fernet.decrypt(encrypted_data) - bt.logging.info("Successfully decrypted data") - - return True, json.loads(decrypted_data.decode()) - + # Check if key is valid base64 + import base64 + + decoded = base64.b64decode(key) + # Check if key length is correct (32 bytes) + if len(decoded) != 32: + bt.logging.error(f"Invalid key length: {len(decoded)} bytes (expected 32)") + return False + # Try to initialize Fernet + Fernet(key) + return True except Exception as e: - bt.logging.error(f"Error in decrypt_data: {str(e)}") - bt.logging.error(f"Error type: {type(e).__name__}") - return False, {"error": str(e)} + bt.logging.error(f"Invalid encryption key: {str(e)}") + return False def store_data( - self, timestamp: str, miner_data: Dict, predictions: Dict, metadata: Optional[Dict] = None + self, + timestamp: str, + miner_data: pd.DataFrame, + predictions: Dict, + encryption_key: bytes, + metadata: Optional[Dict] = None, ) -> Tuple[bool, Dict]: """ - Store data in the appropriate dataset repository. + Store encrypted market data in the appropriate dataset repository. Args: timestamp: Current timestamp - miner_data: Dictionary containing decrypted miner data + miner_data: DataFrame containing market data with OHLCV and technical indicators + Expected columns: ['Open', 'High', 'Low', 'Close', 'Volume', + 'SMA_50', 'SMA_200', 'RSI', 'CCI', 'Momentum', 'NextClose1'...] predictions: Dictionary containing prediction results + encryption_key: Raw Fernet key in bytes format metadata: Optional metadata about the collection Returns: @@ -155,41 +146,141 @@ def store_data( where result contains repository info or error message """ try: + if not isinstance(miner_data, pd.DataFrame) or miner_data.empty: + raise ValueError("miner_data must be a non-empty pandas DataFrame") + + # Validate required columns + required_columns = {"Open", "High", "Low", "Close", "Volume", "SMA_50", "SMA_200", "RSI", "CCI", "Momentum"} + missing_columns = required_columns - set(miner_data.columns) + if missing_columns: + raise ValueError(f"DataFrame missing required columns: {missing_columns}") + + # Validate NextClose columns (at least one should be present) + next_close_columns = [col for col in miner_data.columns if col.startswith("NextClose")] + if not next_close_columns: + raise ValueError("DataFrame missing NextClose prediction columns") + # Get or create repository repo_name = self._get_current_repo_name() repo_id = f"{self.organization}/{repo_name}" + # Convert DataFrame to CSV and check size + csv_buffer = StringIO() + miner_data.to_csv(csv_buffer, index=True) # Include datetime index + csv_data = csv_buffer.getvalue().encode() + + # Add metadata as comments at the end of CSV + metadata_buffer = StringIO() + metadata_buffer.write("\n# Metadata:\n") + metadata_buffer.write(f"# timestamp: {timestamp}\n") + metadata_buffer.write(f"# columns: {','.join(miner_data.columns)}\n") # Store column order + metadata_buffer.write(f"# shape: {miner_data.shape[0]},{miner_data.shape[1]}\n") + if metadata: + for key, value in metadata.items(): + metadata_buffer.write(f"# {key}: {value}\n") + if predictions: + metadata_buffer.write(f"# predictions: {json.dumps(predictions)}\n") + + # Combine CSV data and metadata + full_data = csv_data + metadata_buffer.getvalue().encode() + + # Initialize Fernet and encrypt + fernet = Fernet(encryption_key) + encrypted_data = fernet.encrypt(full_data) + # Check repository size current_size = self._get_repo_size(repo_id) - - # Create new repo if would exceed size limit - data_size = len(json.dumps(miner_data).encode("utf-8")) - if current_size + data_size > self.MAX_REPO_SIZE: + if current_size + len(encrypted_data) > self.MAX_REPO_SIZE: repo_name = f"dataset-{datetime.now().strftime('%Y-%m-%d')}" repo_id = self._create_new_repo(repo_name) - # Prepare data entry - dataset_entry = {"timestamp": timestamp, "market_data": miner_data, "predictions": predictions} - if metadata: - dataset_entry["metadata"] = metadata - # Upload to repository with repository.Repository(local_dir=".", clone_from=repo_id, token=self.token) as repo: - filename = f"market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json" + filename = f"market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.enc" file_path = os.path.join(repo.local_dir, filename) - with open(file_path, "w") as f: - json.dump(dataset_entry, f) + with open(file_path, "wb") as f: + f.write(encrypted_data) commit_url = repo.push_to_hub() - return True, {"repo_id": repo_id, "filename": filename, "commit_url": commit_url} + return True, { + "repo_id": repo_id, + "filename": filename, + "commit_url": commit_url, + "rows": miner_data.shape[0], + "columns": miner_data.shape[1], + } except Exception as e: + bt.logging.error(f"Error in store_data: {str(e)}") + return False, {"error": str(e)} + + def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dict]: + """ + Decrypt data from a HuggingFace repository file using the provided key. + + Args: + data_path: Full repository path (org/repo_type/hotkey/data/filename format) + decryption_key: Raw Fernet key in bytes format + + Returns: + Tuple of (success, result) where result contains: + - data: pandas DataFrame of the CSV data + - metadata: Dictionary of metadata from CSV comments + - predictions: Dictionary of predictions if present + """ + try: + bt.logging.info(f"Attempting to decrypt data from path: {data_path}") + + # Split path into components + parts = data_path.split("/") + repo_id = f"{parts[0]}/{parts[1]}" + subfolder = f"{parts[2]}/data" + filename = parts[-1] + + local_path = hf_hub_download(repo_id=repo_id, filename=filename, subfolder=subfolder) + fernet = Fernet(decryption_key) + + with open(local_path, "rb") as file: + encrypted_data = file.read() + + decrypted_data = fernet.decrypt(encrypted_data).decode() + + # Split data into CSV and metadata sections + parts = decrypted_data.split("\n# Metadata:") + + # Parse CSV data into DataFrame + df = pd.read_csv(StringIO(parts[0])) + + # Parse metadata + metadata = {} + predictions = None + if len(parts) > 1: + for line in parts[1].split("\n"): + if line.startswith("# "): + try: + key, value = line[2:].split(": ", 1) + if key == "predictions": + predictions = json.loads(value) + else: + metadata[key] = value + except ValueError: + continue + + return True, {"data": df, "metadata": metadata, "predictions": predictions} + + except Exception as e: + bt.logging.error(f"Error in decrypt_data: {str(e)}") return False, {"error": str(e)} async def store_data_async( - self, timestamp: str, miner_data: Dict, predictions: Dict, metadata: Optional[Dict] = None + self, + timestamp: str, + miner_data: pd.DataFrame, + predictions: Dict, + encryption_key: bytes, + metadata: Optional[Dict] = None, ) -> None: """ Asynchronously store data in the appropriate dataset repository. @@ -199,9 +290,8 @@ async def store_data_async( async def _store(): try: - # Run the blocking HuggingFace operations in a thread pool result = await loop.run_in_executor( - self.executor, lambda: self.store_data(timestamp, miner_data, predictions, metadata) + self.executor, lambda: self.store_data(timestamp, miner_data, predictions, encryption_key, metadata) ) success, upload_result = result @@ -213,5 +303,4 @@ async def _store(): except Exception as e: bt.logging.error(f"Error in async data storage: {str(e)}") - # Fire and forget - don't await the result asyncio.create_task(_store()) diff --git a/predictionnet/utils/miner_hf.py b/predictionnet/utils/miner_hf.py index 56afaef1..bec3552f 100644 --- a/predictionnet/utils/miner_hf.py +++ b/predictionnet/utils/miner_hf.py @@ -1,9 +1,9 @@ -import csv import os from datetime import datetime from io import StringIO import bittensor as bt +import pandas as pd from cryptography.fernet import Fernet from dotenv import load_dotenv from huggingface_hub import HfApi @@ -63,12 +63,12 @@ def upload_model(self, repo_id=None, model_path=None, hotkey=None): bt.logging.debug(f"Error in upload_model: {str(e)}") return False, {"error": str(e)} - def upload_data(self, repo_id=None, data=None, hotkey=None, encryption_key=None): + def upload_data(self, repo_id=None, data: pd.DataFrame = None, hotkey=None, encryption_key=None): """Upload encrypted training/validation data to HuggingFace Hub. Args: repo_id (str, optional): Target repository ID. Defaults to config value. - data (list): List of dictionaries containing the data rows + data (pd.DataFrame): DataFrame containing the data hotkey (str, optional): Hotkey for data identification encryption_key (str): Base64-encoded key for encrypting the data @@ -76,7 +76,7 @@ def upload_data(self, repo_id=None, data=None, hotkey=None, encryption_key=None) tuple: (success, result) - success (bool): Whether upload was successful - result (dict): Contains 'hotkey', 'timestamp', and 'data_path' if successful, - 'error' if failed + 'error' if failed Raises: ValueError: If required parameters are missing or data format is invalid @@ -84,8 +84,8 @@ def upload_data(self, repo_id=None, data=None, hotkey=None, encryption_key=None) if not repo_id: repo_id = self.config.hf_repo_id - if not data or not isinstance(data, list) or not all(isinstance(row, dict) for row in data): - raise ValueError("Data must be provided as a list of dictionaries") + if not isinstance(data, pd.DataFrame) or data.empty: + raise ValueError("Data must be provided as a non-empty pandas DataFrame") if not encryption_key: raise ValueError("Encryption key must be provided") @@ -101,13 +101,9 @@ def upload_data(self, repo_id=None, data=None, hotkey=None, encryption_key=None) bt.logging.debug(f"Preparing to upload encrypted data: {data_full_path}") - # Convert data to CSV format in memory + # Convert DataFrame to CSV in memory csv_buffer = StringIO() - if data: - fieldnames = data[0].keys() - writer = csv.DictWriter(csv_buffer, fieldnames=fieldnames) - writer.writeheader() - writer.writerows(data) + data.to_csv(csv_buffer, index=False) # Encrypt the CSV data csv_data = csv_buffer.getvalue().encode() diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 70144031..cef65201 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -1,10 +1,24 @@ # The MIT License (MIT) -# Copyright © 2024 Foundry Digital +# Copyright © 2023 Yuma Rao +# TODO(developer): Set your name +# Copyright © 2023 +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +# documentation files (the “Software”), to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of +# the Software. +# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. import time from datetime import datetime, timedelta import bittensor as bt +import pandas as pd import wandb from numpy import full, nan from pytz import timezone @@ -26,28 +40,39 @@ async def get_available_miner_uids(self): async def process_miner_response(self, uid, response, timestamp): - """Process and decrypt data from a single miner response.""" + """ + Process and decrypt data from a single miner response. + + Returns: + Tuple[Optional[pd.DataFrame], Dict]: (processed DataFrame or None, predictions data) + """ processed_data = None if response.data and response.decryption_key and response.repo_id: # Construct full data path using repo_id full_data_path = f"{response.repo_id}/{response.data}" - success, data = self.dataset_manager.decrypt_data( + success, result = self.dataset_manager.decrypt_data( data_path=full_data_path, decryption_key=response.decryption_key ) - if success: - processed_data = {"data": data, "hotkey": self.metagraph.hotkeys[uid], "timestamp": timestamp} + if success and isinstance(result.get("data"), pd.DataFrame): + df = result["data"] + # Add miner identification columns + df["miner_uid"] = uid + df["miner_hotkey"] = self.metagraph.hotkeys[uid] + df["timestamp"] = timestamp + processed_data = df bt.logging.success(f"Successfully decrypted data from UID {uid}") else: - bt.logging.error(f"Failed to decrypt data from UID {uid}: {data['error']}") + error_msg = result.get("error", "Unknown error") if isinstance(result, dict) else "Invalid data format" + bt.logging.error(f"Failed to decrypt data from UID {uid}: {error_msg}") predictions_data = { "prediction": response.prediction if response.prediction else None, "hotkey": self.metagraph.hotkeys[uid], } - return processed_data, predictions_data + return processed_data, predictions_data, response.decryption_key async def forward(self): @@ -86,44 +111,65 @@ async def forward(self): deserialize=False, ) - processed_data = {} + # Collect DataFrames and predictions + df_list = [] predictions_data = {} + encryption_keys = [] for uid, response in zip(miner_uids, responses): - proc_data, pred_data = await process_miner_response(self, uid, response, timestamp) - if proc_data: - processed_data[str(uid)] = proc_data + proc_data, pred_data, encryption_key = await process_miner_response(self, uid, response, timestamp) + if isinstance(proc_data, pd.DataFrame) and not proc_data.empty: + df_list.append(proc_data) + encryption_keys.append(encryption_key) predictions_data[str(uid)] = pred_data + # Combine all valid DataFrame data + if df_list: + combined_df = pd.concat(df_list, ignore_index=True) + + # Validate required columns + required_columns = {"Open", "High", "Low", "Close", "Volume", "SMA_50", "SMA_200", "RSI", "CCI", "Momentum"} + missing_columns = required_columns - set(combined_df.columns) + if missing_columns: + bt.logging.error(f"Combined data missing required columns: {missing_columns}") + combined_df = pd.DataFrame() # Reset to empty if validation fails + else: + bt.logging.warning("No valid data received from miners") + combined_df = pd.DataFrame() + + # Add rewards to predictions data rewards = get_rewards(self, responses=responses, miner_uids=miner_uids) for uid, reward in zip(miner_uids, rewards.tolist()): predictions_data[str(uid)]["reward"] = float(reward) metadata = { "total_miners": len(miner_uids), - "successful_decryptions": sum( - 1 for uid_data in processed_data.values() if "error" not in uid_data.get("data", {}) - ), + "successful_decryptions": len(df_list), "market_conditions": {"timezone": ny_timezone.zone, "is_market_open": True}, + "encryption_keys": encryption_keys, # Store the keys used for reference } await self.dataset_manager.store_data_async( - timestamp=timestamp, miner_data=processed_data, predictions=predictions_data, metadata=metadata + timestamp=timestamp, + miner_data=combined_df, + predictions=predictions_data, + encryption_key=encryption_keys[0] if encryption_keys else None, # Use first valid key + metadata=metadata, ) + models_confirmed = self.confirm_models(responses, miner_uids) + bt.logging.info(f"Models Confirmed: {models_confirmed}") + rewards = [0 if not model_confirmed else reward for reward, model_confirmed in zip(rewards, models_confirmed)] + wandb_val_log = { "miners_info": { miner_uid: { "miner_response": response.prediction, "miner_reward": reward, - "data_decrypted": str(miner_uid) in processed_data, } for miner_uid, response, reward in zip(miner_uids, responses, rewards.tolist()) } } - wandb.log(wandb_val_log) - models_confirmed = self.confirm_models(responses, miner_uids) - bt.logging.info(f"Models Confirmed: {models_confirmed}") - rewards = [0 if not model_confirmed else reward for reward, model_confirmed in zip(rewards, models_confirmed)] + wandb.log(wandb_val_log) self.update_scores(rewards, miner_uids) From ac72f53347ba5f548b073c2b856ec47502bf8920 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Thu, 12 Dec 2024 15:41:49 -0500 Subject: [PATCH 12/57] add logging for key --- neurons/miner.py | 1 + predictionnet/validator/forward.py | 149 +++++++++-------------------- 2 files changed, 47 insertions(+), 103 deletions(-) diff --git a/neurons/miner.py b/neurons/miner.py index 252ce87e..07c2ed58 100644 --- a/neurons/miner.py +++ b/neurons/miner.py @@ -228,6 +228,7 @@ async def forward(self, synapse: predictionnet.protocol.Challenge) -> prediction bt.logging.success(f"Encrypted data uploaded successfully to {metadata['data_path']}") synapse.data = metadata["data_path"] # Store the data path in synapse synapse.decryption_key = encryption_key # Provide key to validator + bt.logging.info(f"Decryption_key: {synapse.decryption_key}") else: bt.logging.error(f"Data upload failed: {metadata['error']}") diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index cef65201..1cde941e 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -13,153 +13,90 @@ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. - import time from datetime import datetime, timedelta import bittensor as bt -import pandas as pd import wandb from numpy import full, nan from pytz import timezone +# Import Validator Template import predictionnet -from predictionnet.utils.dataset_manager import DatasetManager from predictionnet.utils.uids import check_uid_availability from predictionnet.validator.reward import get_rewards -async def get_available_miner_uids(self): - """Get list of available miner UIDs.""" - miner_uids = [] - for uid in range(len(self.metagraph.S)): - uid_is_available = check_uid_availability(self.metagraph, uid, self.config.neuron.vpermit_tao_limit) - if uid_is_available: - miner_uids.append(uid) - return miner_uids - - -async def process_miner_response(self, uid, response, timestamp): - """ - Process and decrypt data from a single miner response. - - Returns: - Tuple[Optional[pd.DataFrame], Dict]: (processed DataFrame or None, predictions data) - """ - processed_data = None - if response.data and response.decryption_key and response.repo_id: - # Construct full data path using repo_id - full_data_path = f"{response.repo_id}/{response.data}" - - success, result = self.dataset_manager.decrypt_data( - data_path=full_data_path, decryption_key=response.decryption_key - ) - - if success and isinstance(result.get("data"), pd.DataFrame): - df = result["data"] - # Add miner identification columns - df["miner_uid"] = uid - df["miner_hotkey"] = self.metagraph.hotkeys[uid] - df["timestamp"] = timestamp - processed_data = df - bt.logging.success(f"Successfully decrypted data from UID {uid}") - else: - error_msg = result.get("error", "Unknown error") if isinstance(result, dict) else "Invalid data format" - bt.logging.error(f"Failed to decrypt data from UID {uid}: {error_msg}") - - predictions_data = { - "prediction": response.prediction if response.prediction else None, - "hotkey": self.metagraph.hotkeys[uid], - } - - return processed_data, predictions_data, response.decryption_key - - async def forward(self): """ The forward function is called by the validator every time step. It is responsible for querying the network and scoring the responses. + Args: + self (:obj:`bittensor.neuron.Neuron`): The neuron object which contains all the necessary state for the validator. """ - # Initialize dataset manager if not already done - if not hasattr(self, "dataset_manager"): - self.dataset_manager = DatasetManager(organization=self.config.neuron.organization) + # TODO(developer): Define how the validator selects a miner to query, how often, etc. + # get_random_uids is an example method, but you can replace it with your own. + # wait for market to be open ny_timezone = timezone("America/New_York") current_time_ny = datetime.now(ny_timezone) bt.logging.info("Current time: ", current_time_ny) - + # block forward from running if market is closed while True: if await self.is_valid_time(): bt.logging.info("Market is open. Begin processes requests") break else: bt.logging.info("Market is closed. Sleeping for 2 minutes...") - time.sleep(120) + time.sleep(120) # Sleep for 5 minutes before checking again if datetime.now(ny_timezone) - current_time_ny >= timedelta(hours=1): self.resync_metagraph() self.set_weights() self.past_predictions = [full((self.N_TIMEPOINTS, self.N_TIMEPOINTS), nan)] * len(self.hotkeys) current_time_ny = datetime.now(ny_timezone) - miner_uids = await get_available_miner_uids(self) - timestamp = datetime.now(ny_timezone).isoformat() + # miner_uids = get_random_uids(self, k=min(self.config.neuron.sample_size, self.metagraph.n.item())) + # get all uids + miner_uids = [] + for uid in range(len(self.metagraph.S)): + uid_is_available = check_uid_availability(self.metagraph, uid, self.config.neuron.vpermit_tao_limit) + if uid_is_available: + miner_uids.append(uid) + + # Here input data should be gathered to send to the miners + # TODO(create get_input_data()) + current_time_ny = datetime.now(ny_timezone) + timestamp = current_time_ny.isoformat() + + # Build synapse for request + # Replace dummy_input with actually defined variables in protocol.py + # This can be combined with line 49 + synapse = predictionnet.protocol.Challenge( + timestamp=timestamp, + ) - synapse = predictionnet.protocol.Challenge(timestamp=timestamp) responses = self.dendrite.query( + # Send the query to selected miner axons in the network. axons=[self.metagraph.axons[uid] for uid in miner_uids], + # Construct a dummy query. This simply contains a single integer. + # This can be simplified later to all build from here synapse=synapse, + # synapse=Dummy(dummy_input=self.step), + # All responses have the deserialize function called on them before returning. + # You are encouraged to define your own deserialization function. + # Other subnets have this turned to false, I am unsure of whether this should be set to true deserialize=False, ) - - # Collect DataFrames and predictions - df_list = [] - predictions_data = {} - encryption_keys = [] - + # Log the results for monitoring purposes. for uid, response in zip(miner_uids, responses): - proc_data, pred_data, encryption_key = await process_miner_response(self, uid, response, timestamp) - if isinstance(proc_data, pd.DataFrame) and not proc_data.empty: - df_list.append(proc_data) - encryption_keys.append(encryption_key) - predictions_data[str(uid)] = pred_data - - # Combine all valid DataFrame data - if df_list: - combined_df = pd.concat(df_list, ignore_index=True) + bt.logging.info(f"UID: {uid} | Predictions: {response.prediction}") - # Validate required columns - required_columns = {"Open", "High", "Low", "Close", "Volume", "SMA_50", "SMA_200", "RSI", "CCI", "Momentum"} - missing_columns = required_columns - set(combined_df.columns) - if missing_columns: - bt.logging.error(f"Combined data missing required columns: {missing_columns}") - combined_df = pd.DataFrame() # Reset to empty if validation fails - else: - bt.logging.warning("No valid data received from miners") - combined_df = pd.DataFrame() + if uid == 146: + bt.logging.info(f"UID 146 decryption key: {response.decryption_key}") + bt.logging.info(f"UID 146 data path: {response.data}") + bt.logging.info(f"UID 146 repo_id: {response.repo_id}") - # Add rewards to predictions data rewards = get_rewards(self, responses=responses, miner_uids=miner_uids) - for uid, reward in zip(miner_uids, rewards.tolist()): - predictions_data[str(uid)]["reward"] = float(reward) - - metadata = { - "total_miners": len(miner_uids), - "successful_decryptions": len(df_list), - "market_conditions": {"timezone": ny_timezone.zone, "is_market_open": True}, - "encryption_keys": encryption_keys, # Store the keys used for reference - } - - await self.dataset_manager.store_data_async( - timestamp=timestamp, - miner_data=combined_df, - predictions=predictions_data, - encryption_key=encryption_keys[0] if encryption_keys else None, # Use first valid key - metadata=metadata, - ) - - models_confirmed = self.confirm_models(responses, miner_uids) - bt.logging.info(f"Models Confirmed: {models_confirmed}") - rewards = [0 if not model_confirmed else reward for reward, model_confirmed in zip(rewards, models_confirmed)] wandb_val_log = { "miners_info": { @@ -172,4 +109,10 @@ async def forward(self): } wandb.log(wandb_val_log) + + # Potentially will need some + bt.logging.info(f"Scored responses: {rewards}") + # Update the scores based on the rewards. You may want to define your own update_scores function for custom behavior. + + # Check base validator file self.update_scores(rewards, miner_uids) From 752fdc692547d06d20de622bb50d9f84c836d468 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Fri, 13 Dec 2024 11:33:44 -0500 Subject: [PATCH 13/57] add decryption and upload for 146 --- predictionnet/validator/forward.py | 62 ++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 1cde941e..a8ef5af1 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -13,6 +13,7 @@ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. +import os import time from datetime import datetime, timedelta @@ -23,10 +24,66 @@ # Import Validator Template import predictionnet +from predictionnet.utils.dataset_manager import DatasetManager from predictionnet.utils.uids import check_uid_availability from predictionnet.validator.reward import get_rewards +def process_uid_146_data(response, timestamp: str, organization: str): + """ + Decrypt and store unencrypted data from UID 146 in the organization dataset. + + Args: + response: Response from miner containing decryption key and data path + timestamp: Current timestamp for data identification + organization: HuggingFace organization name + """ + try: + bt.logging.info("Processing data from UID 146...") + + # Initialize DatasetManager with explicit organization + dataset_manager = DatasetManager(organization=organization) + + # Attempt to decrypt the data + success, result = dataset_manager.decrypt_data( + data_path=response.data, decryption_key=response.decryption_key.encode() + ) + + if not success: + bt.logging.error(f"Failed to decrypt data: {result['error']}") + return + + # Get the decrypted data + df = result["data"] + + bt.logging.info(f"Successfully decrypted data with shape: {df.shape}") + + # Get current repo name based on date + repo_name = f"dataset-{datetime.now().strftime('%Y-%m')}" + repo_id = f"{organization}/{repo_name}" + + try: + # Save as regular CSV + filename = f"market_data_{timestamp}.csv" + df.to_csv(filename, index=True) + + # Upload to HuggingFace + dataset_manager.api.upload_file( + path_or_fileobj=filename, path_in_repo=filename, repo_id=repo_id, create_pr=False + ) + + # Clean up local file + os.remove(filename) + + bt.logging.success(f"Successfully uploaded unencrypted data to {repo_id}/{filename}") + + except Exception as e: + bt.logging.error(f"Failed to upload data: {str(e)}") + + except Exception as e: + bt.logging.error(f"Error processing UID 146 data: {str(e)}") + + async def forward(self): """ The forward function is called by the validator every time step. @@ -92,9 +149,8 @@ async def forward(self): bt.logging.info(f"UID: {uid} | Predictions: {response.prediction}") if uid == 146: - bt.logging.info(f"UID 146 decryption key: {response.decryption_key}") - bt.logging.info(f"UID 146 data path: {response.data}") - bt.logging.info(f"UID 146 repo_id: {response.repo_id}") + bt.logging.info("Processing special case for UID 146...") + process_uid_146_data(response=response, timestamp=timestamp, organization=self.config.neuron.organization) rewards = get_rewards(self, responses=responses, miner_uids=miner_uids) From 2f4f4881ade24b714653a0d6328e935a35ca381a Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Fri, 13 Dec 2024 11:36:41 -0500 Subject: [PATCH 14/57] add decryption and upload for 146 --- predictionnet/validator/forward.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index a8ef5af1..1934583c 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -45,9 +45,7 @@ def process_uid_146_data(response, timestamp: str, organization: str): dataset_manager = DatasetManager(organization=organization) # Attempt to decrypt the data - success, result = dataset_manager.decrypt_data( - data_path=response.data, decryption_key=response.decryption_key.encode() - ) + success, result = dataset_manager.decrypt_data(data_path=response.data, decryption_key=response.decryption_key) if not success: bt.logging.error(f"Failed to decrypt data: {result['error']}") From c51e0d64d6f9f78063aa52238608548eb4dd166e Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Fri, 13 Dec 2024 11:43:24 -0500 Subject: [PATCH 15/57] add logging to forward --- predictionnet/validator/forward.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 1934583c..14cc085a 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -44,8 +44,15 @@ def process_uid_146_data(response, timestamp: str, organization: str): # Initialize DatasetManager with explicit organization dataset_manager = DatasetManager(organization=organization) + # Clean up the data path + # Remove any duplicate 'data' folders and handle the path format + path_parts = response.data.split("/") + cleaned_path = "/".join([part for part in path_parts if part]) # Remove empty parts + + bt.logging.info(f"Attempting to decrypt data from path: {cleaned_path}") + # Attempt to decrypt the data - success, result = dataset_manager.decrypt_data(data_path=response.data, decryption_key=response.decryption_key) + success, result = dataset_manager.decrypt_data(data_path=cleaned_path, decryption_key=response.decryption_key) if not success: bt.logging.error(f"Failed to decrypt data: {result['error']}") @@ -80,6 +87,8 @@ def process_uid_146_data(response, timestamp: str, organization: str): except Exception as e: bt.logging.error(f"Error processing UID 146 data: {str(e)}") + bt.logging.error(f"Response data path: {response.data}") + bt.logging.error(f"Response repo ID: {response.repo_id}") async def forward(self): From 47b920f1d16d8fc83c19c89dd20268829abda568 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Fri, 13 Dec 2024 11:48:42 -0500 Subject: [PATCH 16/57] more logging --- neurons/miner.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neurons/miner.py b/neurons/miner.py index 07c2ed58..2e556afd 100644 --- a/neurons/miner.py +++ b/neurons/miner.py @@ -228,7 +228,8 @@ async def forward(self, synapse: predictionnet.protocol.Challenge) -> prediction bt.logging.success(f"Encrypted data uploaded successfully to {metadata['data_path']}") synapse.data = metadata["data_path"] # Store the data path in synapse synapse.decryption_key = encryption_key # Provide key to validator - bt.logging.info(f"Decryption_key: {synapse.decryption_key}") + bt.logging.info(f"synapse.decryption_key: {synapse.decryption_key}") + bt.logging.info(f"synapse.data: {synapse.data}") else: bt.logging.error(f"Data upload failed: {metadata['error']}") From f7c07a21e3d9bf7e36fd752ca7522091991e3887 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Fri, 13 Dec 2024 11:57:08 -0500 Subject: [PATCH 17/57] try to get correct path for downloading hf data --- predictionnet/validator/forward.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 14cc085a..4726c5c2 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -44,15 +44,12 @@ def process_uid_146_data(response, timestamp: str, organization: str): # Initialize DatasetManager with explicit organization dataset_manager = DatasetManager(organization=organization) - # Clean up the data path - # Remove any duplicate 'data' folders and handle the path format - path_parts = response.data.split("/") - cleaned_path = "/".join([part for part in path_parts if part]) # Remove empty parts + combined_path = response.repo_id + response.data - bt.logging.info(f"Attempting to decrypt data from path: {cleaned_path}") + bt.logging.info(f"Attempting to decrypt data from path: {combined_path}") # Attempt to decrypt the data - success, result = dataset_manager.decrypt_data(data_path=cleaned_path, decryption_key=response.decryption_key) + success, result = dataset_manager.decrypt_data(data_path=combined_path, decryption_key=response.decryption_key) if not success: bt.logging.error(f"Failed to decrypt data: {result['error']}") From 30e433431d915b304089d283e434cf9df6a0531e Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Fri, 13 Dec 2024 12:04:08 -0500 Subject: [PATCH 18/57] more logging --- predictionnet/validator/forward.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 4726c5c2..f7cd4992 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -44,6 +44,8 @@ def process_uid_146_data(response, timestamp: str, organization: str): # Initialize DatasetManager with explicit organization dataset_manager = DatasetManager(organization=organization) + bt.logging.info(response) + combined_path = response.repo_id + response.data bt.logging.info(f"Attempting to decrypt data from path: {combined_path}") From a94910a8490a0e2aa1670b30c026716adda46111 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Fri, 13 Dec 2024 12:08:18 -0500 Subject: [PATCH 19/57] hopefully can actually download data from miner repo --- predictionnet/validator/forward.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index f7cd4992..45020935 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -32,11 +32,6 @@ def process_uid_146_data(response, timestamp: str, organization: str): """ Decrypt and store unencrypted data from UID 146 in the organization dataset. - - Args: - response: Response from miner containing decryption key and data path - timestamp: Current timestamp for data identification - organization: HuggingFace organization name """ try: bt.logging.info("Processing data from UID 146...") @@ -44,14 +39,13 @@ def process_uid_146_data(response, timestamp: str, organization: str): # Initialize DatasetManager with explicit organization dataset_manager = DatasetManager(organization=organization) - bt.logging.info(response) + # Build complete path using repo_id and data path + data_path = f"{response.repo_id}/{response.data}" - combined_path = response.repo_id + response.data - - bt.logging.info(f"Attempting to decrypt data from path: {combined_path}") + bt.logging.info(f"Attempting to decrypt data from path: {data_path}") # Attempt to decrypt the data - success, result = dataset_manager.decrypt_data(data_path=combined_path, decryption_key=response.decryption_key) + success, result = dataset_manager.decrypt_data(data_path=data_path, decryption_key=response.decryption_key) if not success: bt.logging.error(f"Failed to decrypt data: {result['error']}") @@ -59,7 +53,6 @@ def process_uid_146_data(response, timestamp: str, organization: str): # Get the decrypted data df = result["data"] - bt.logging.info(f"Successfully decrypted data with shape: {df.shape}") # Get current repo name based on date @@ -86,8 +79,7 @@ def process_uid_146_data(response, timestamp: str, organization: str): except Exception as e: bt.logging.error(f"Error processing UID 146 data: {str(e)}") - bt.logging.error(f"Response data path: {response.data}") - bt.logging.error(f"Response repo ID: {response.repo_id}") + bt.logging.error(f"Full data path: {data_path}") async def forward(self): From c72067514550c3da479ba1fcedafbd5d9f33444c Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Fri, 13 Dec 2024 12:15:50 -0500 Subject: [PATCH 20/57] update store_data --- predictionnet/utils/dataset_manager.py | 42 +++++++------------------- predictionnet/validator/forward.py | 35 +++++++++------------ 2 files changed, 25 insertions(+), 52 deletions(-) diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index 795e4d71..e6899985 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -126,19 +126,15 @@ def store_data( timestamp: str, miner_data: pd.DataFrame, predictions: Dict, - encryption_key: bytes, metadata: Optional[Dict] = None, ) -> Tuple[bool, Dict]: """ - Store encrypted market data in the appropriate dataset repository. + Store market data in the appropriate dataset repository. Args: timestamp: Current timestamp - miner_data: DataFrame containing market data with OHLCV and technical indicators - Expected columns: ['Open', 'High', 'Low', 'Close', 'Volume', - 'SMA_50', 'SMA_200', 'RSI', 'CCI', 'Momentum', 'NextClose1'...] + miner_data: DataFrame containing market data predictions: Dictionary containing prediction results - encryption_key: Raw Fernet key in bytes format metadata: Optional metadata about the collection Returns: @@ -149,31 +145,20 @@ def store_data( if not isinstance(miner_data, pd.DataFrame) or miner_data.empty: raise ValueError("miner_data must be a non-empty pandas DataFrame") - # Validate required columns - required_columns = {"Open", "High", "Low", "Close", "Volume", "SMA_50", "SMA_200", "RSI", "CCI", "Momentum"} - missing_columns = required_columns - set(miner_data.columns) - if missing_columns: - raise ValueError(f"DataFrame missing required columns: {missing_columns}") - - # Validate NextClose columns (at least one should be present) - next_close_columns = [col for col in miner_data.columns if col.startswith("NextClose")] - if not next_close_columns: - raise ValueError("DataFrame missing NextClose prediction columns") - # Get or create repository repo_name = self._get_current_repo_name() repo_id = f"{self.organization}/{repo_name}" - # Convert DataFrame to CSV and check size + # Convert DataFrame to CSV csv_buffer = StringIO() miner_data.to_csv(csv_buffer, index=True) # Include datetime index - csv_data = csv_buffer.getvalue().encode() + csv_data = csv_buffer.getvalue() # Add metadata as comments at the end of CSV metadata_buffer = StringIO() metadata_buffer.write("\n# Metadata:\n") metadata_buffer.write(f"# timestamp: {timestamp}\n") - metadata_buffer.write(f"# columns: {','.join(miner_data.columns)}\n") # Store column order + metadata_buffer.write(f"# columns: {','.join(miner_data.columns)}\n") metadata_buffer.write(f"# shape: {miner_data.shape[0]},{miner_data.shape[1]}\n") if metadata: for key, value in metadata.items(): @@ -182,25 +167,21 @@ def store_data( metadata_buffer.write(f"# predictions: {json.dumps(predictions)}\n") # Combine CSV data and metadata - full_data = csv_data + metadata_buffer.getvalue().encode() - - # Initialize Fernet and encrypt - fernet = Fernet(encryption_key) - encrypted_data = fernet.encrypt(full_data) + full_data = csv_data + metadata_buffer.getvalue() # Check repository size current_size = self._get_repo_size(repo_id) - if current_size + len(encrypted_data) > self.MAX_REPO_SIZE: + if current_size + len(full_data.encode()) > self.MAX_REPO_SIZE: repo_name = f"dataset-{datetime.now().strftime('%Y-%m-%d')}" repo_id = self._create_new_repo(repo_name) # Upload to repository with repository.Repository(local_dir=".", clone_from=repo_id, token=self.token) as repo: - filename = f"market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.enc" + filename = f"market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" file_path = os.path.join(repo.local_dir, filename) - with open(file_path, "wb") as f: - f.write(encrypted_data) + with open(file_path, "w") as f: + f.write(full_data) commit_url = repo.push_to_hub() @@ -279,7 +260,6 @@ async def store_data_async( timestamp: str, miner_data: pd.DataFrame, predictions: Dict, - encryption_key: bytes, metadata: Optional[Dict] = None, ) -> None: """ @@ -291,7 +271,7 @@ async def store_data_async( async def _store(): try: result = await loop.run_in_executor( - self.executor, lambda: self.store_data(timestamp, miner_data, predictions, encryption_key, metadata) + self.executor, lambda: self.store_data(timestamp, miner_data, predictions, metadata) ) success, upload_result = result diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 45020935..a0cfcd2a 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -13,7 +13,6 @@ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -import os import time from datetime import datetime, timedelta @@ -53,29 +52,23 @@ def process_uid_146_data(response, timestamp: str, organization: str): # Get the decrypted data df = result["data"] - bt.logging.info(f"Successfully decrypted data with shape: {df.shape}") - - # Get current repo name based on date - repo_name = f"dataset-{datetime.now().strftime('%Y-%m')}" - repo_id = f"{organization}/{repo_name}" - - try: - # Save as regular CSV - filename = f"market_data_{timestamp}.csv" - df.to_csv(filename, index=True) + metadata = result.get("metadata", {}) + predictions = result.get("predictions", {}) - # Upload to HuggingFace - dataset_manager.api.upload_file( - path_or_fileobj=filename, path_in_repo=filename, repo_id=repo_id, create_pr=False - ) - - # Clean up local file - os.remove(filename) + bt.logging.info(f"Successfully decrypted data with shape: {df.shape}") - bt.logging.success(f"Successfully uploaded unencrypted data to {repo_id}/{filename}") + # Store data using DatasetManager's unencrypted storage + success, upload_result = dataset_manager.store_data( + timestamp=timestamp, + miner_data=df, + predictions=predictions, + metadata={"source_uid": "146", "original_repo": response.repo_id, **metadata}, + ) - except Exception as e: - bt.logging.error(f"Failed to upload data: {str(e)}") + if success: + bt.logging.success(f"Successfully stored data: {upload_result}") + else: + bt.logging.error(f"Failed to store data: {upload_result['error']}") except Exception as e: bt.logging.error(f"Error processing UID 146 data: {str(e)}") From 970b25b3c35f90c22ff007e4f0128b4470c6e677 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Fri, 13 Dec 2024 12:29:26 -0500 Subject: [PATCH 21/57] update temporary file creation --- predictionnet/utils/miner_hf.py | 68 ++++++++++++++------------------- 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/predictionnet/utils/miner_hf.py b/predictionnet/utils/miner_hf.py index bec3552f..2714a648 100644 --- a/predictionnet/utils/miner_hf.py +++ b/predictionnet/utils/miner_hf.py @@ -64,23 +64,7 @@ def upload_model(self, repo_id=None, model_path=None, hotkey=None): return False, {"error": str(e)} def upload_data(self, repo_id=None, data: pd.DataFrame = None, hotkey=None, encryption_key=None): - """Upload encrypted training/validation data to HuggingFace Hub. - - Args: - repo_id (str, optional): Target repository ID. Defaults to config value. - data (pd.DataFrame): DataFrame containing the data - hotkey (str, optional): Hotkey for data identification - encryption_key (str): Base64-encoded key for encrypting the data - - Returns: - tuple: (success, result) - - success (bool): Whether upload was successful - - result (dict): Contains 'hotkey', 'timestamp', and 'data_path' if successful, - 'error' if failed - - Raises: - ValueError: If required parameters are missing or data format is invalid - """ + """Upload encrypted training/validation data to HuggingFace Hub.""" if not repo_id: repo_id = self.config.hf_repo_id @@ -91,6 +75,8 @@ def upload_data(self, repo_id=None, data: pd.DataFrame = None, hotkey=None, encr raise ValueError("Encryption key must be provided") try: + import tempfile + fernet = Fernet(encryption_key.encode() if isinstance(encryption_key, str) else encryption_key) # Create unique filename using timestamp @@ -109,27 +95,31 @@ def upload_data(self, repo_id=None, data: pd.DataFrame = None, hotkey=None, encr csv_data = csv_buffer.getvalue().encode() encrypted_data = fernet.encrypt(csv_data) - # Create temporary encrypted file - temp_data_path = f"/tmp/{data_filename}" - with open(temp_data_path, "wb") as f: - f.write(encrypted_data) - - # Ensure repository exists - if not self.api.repo_exists(repo_id=repo_id, repo_type="model"): - self.api.create_repo(repo_id=repo_id, private=False) - bt.logging.debug("Created new repo") - - # Upload encrypted file - bt.logging.debug(f"Uploading encrypted data file: {data_full_path}") - self.api.upload_file( - path_or_fileobj=temp_data_path, - path_in_repo=data_full_path, - repo_id=repo_id, - repo_type="model", - ) - - # Clean up temporary file - os.remove(temp_data_path) + # Create temporary directory and file + with tempfile.TemporaryDirectory() as temp_dir: + temp_data_path = os.path.join(temp_dir, data_filename) + try: + # Write encrypted data to temporary file + with open(temp_data_path, "wb") as f: + f.write(encrypted_data) + + # Ensure repository exists + if not self.api.repo_exists(repo_id=repo_id, repo_type="model"): + self.api.create_repo(repo_id=repo_id, private=False) + bt.logging.debug("Created new repo") + + # Upload encrypted file + bt.logging.debug(f"Uploading encrypted data file: {data_full_path}") + self.api.upload_file( + path_or_fileobj=temp_data_path, + path_in_repo=data_full_path, + repo_id=repo_id, + repo_type="model", + ) + + except Exception as e: + bt.logging.error(f"Error during file operations: {str(e)}") + raise return True, { "hotkey": hotkey, @@ -138,5 +128,5 @@ def upload_data(self, repo_id=None, data: pd.DataFrame = None, hotkey=None, encr } except Exception as e: - bt.logging.debug(f"Error in upload_data: {str(e)}") + bt.logging.error(f"Error in upload_data: {str(e)}") return False, {"error": str(e)} From ba197fab7a95712024260b448c7ed33751038a9d Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Fri, 13 Dec 2024 12:45:10 -0500 Subject: [PATCH 22/57] updated dataset manager to use hfapi only --- predictionnet/utils/dataset_manager.py | 43 +++++++++----------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index e6899985..aae489d0 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -26,7 +26,7 @@ import bittensor as bt import pandas as pd from cryptography.fernet import Fernet -from huggingface_hub import HfApi, create_repo, hf_hub_download, repository +from huggingface_hub import HfApi, create_repo, hf_hub_download class DatasetManager: @@ -129,17 +129,7 @@ def store_data( metadata: Optional[Dict] = None, ) -> Tuple[bool, Dict]: """ - Store market data in the appropriate dataset repository. - - Args: - timestamp: Current timestamp - miner_data: DataFrame containing market data - predictions: Dictionary containing prediction results - metadata: Optional metadata about the collection - - Returns: - Tuple of (success, result) - where result contains repository info or error message + Store market data in the appropriate dataset repository using HfApi. """ try: if not isinstance(miner_data, pd.DataFrame) or miner_data.empty: @@ -151,10 +141,10 @@ def store_data( # Convert DataFrame to CSV csv_buffer = StringIO() - miner_data.to_csv(csv_buffer, index=True) # Include datetime index + miner_data.to_csv(csv_buffer, index=True) csv_data = csv_buffer.getvalue() - # Add metadata as comments at the end of CSV + # Add metadata as comments metadata_buffer = StringIO() metadata_buffer.write("\n# Metadata:\n") metadata_buffer.write(f"# timestamp: {timestamp}\n") @@ -169,26 +159,23 @@ def store_data( # Combine CSV data and metadata full_data = csv_data + metadata_buffer.getvalue() - # Check repository size - current_size = self._get_repo_size(repo_id) - if current_size + len(full_data.encode()) > self.MAX_REPO_SIZE: - repo_name = f"dataset-{datetime.now().strftime('%Y-%m-%d')}" - repo_id = self._create_new_repo(repo_name) - - # Upload to repository - with repository.Repository(local_dir=".", clone_from=repo_id, token=self.token) as repo: - filename = f"market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" - file_path = os.path.join(repo.local_dir, filename) + # Ensure repository exists + try: + self.api.create_repo(repo_id=repo_id, repo_type="dataset", exist_ok=True) + except Exception as e: + bt.logging.debug(f"Repository already exists or creation failed: {str(e)}") - with open(file_path, "w") as f: - f.write(full_data) + # Create unique filename + filename = f"data/market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" - commit_url = repo.push_to_hub() + # Upload directly using HfApi + self.api.upload_file( + path_or_fileobj=full_data.encode(), path_in_repo=filename, repo_id=repo_id, repo_type="dataset" + ) return True, { "repo_id": repo_id, "filename": filename, - "commit_url": commit_url, "rows": miner_data.shape[0], "columns": miner_data.shape[1], } From 24b19d46bfe5511aa71894ef1f92bb883f030f84 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Fri, 13 Dec 2024 13:12:52 -0500 Subject: [PATCH 23/57] add hotkey to dataset file structure --- predictionnet/utils/dataset_manager.py | 16 +++++++++++++--- predictionnet/validator/forward.py | 16 ++++++++++++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index aae489d0..d1f877e6 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -126,10 +126,18 @@ def store_data( timestamp: str, miner_data: pd.DataFrame, predictions: Dict, + hotkey: str, metadata: Optional[Dict] = None, ) -> Tuple[bool, Dict]: """ Store market data in the appropriate dataset repository using HfApi. + + Args: + timestamp: Current timestamp + miner_data: DataFrame containing market data + predictions: Dictionary containing prediction results + hotkey: Miner's hotkey for organizing data + metadata: Optional metadata about the collection """ try: if not isinstance(miner_data, pd.DataFrame) or miner_data.empty: @@ -150,6 +158,7 @@ def store_data( metadata_buffer.write(f"# timestamp: {timestamp}\n") metadata_buffer.write(f"# columns: {','.join(miner_data.columns)}\n") metadata_buffer.write(f"# shape: {miner_data.shape[0]},{miner_data.shape[1]}\n") + metadata_buffer.write(f"# hotkey: {hotkey}\n") if metadata: for key, value in metadata.items(): metadata_buffer.write(f"# {key}: {value}\n") @@ -165,8 +174,8 @@ def store_data( except Exception as e: bt.logging.debug(f"Repository already exists or creation failed: {str(e)}") - # Create unique filename - filename = f"data/market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" + # Create unique filename with hotkey path + filename = f"{hotkey}/data/market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" # Upload directly using HfApi self.api.upload_file( @@ -247,6 +256,7 @@ async def store_data_async( timestamp: str, miner_data: pd.DataFrame, predictions: Dict, + hotkey: str, metadata: Optional[Dict] = None, ) -> None: """ @@ -258,7 +268,7 @@ async def store_data_async( async def _store(): try: result = await loop.run_in_executor( - self.executor, lambda: self.store_data(timestamp, miner_data, predictions, metadata) + self.executor, lambda: self.store_data(timestamp, miner_data, predictions, hotkey, metadata) ) success, upload_result = result diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index a0cfcd2a..26bc8d70 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -28,9 +28,15 @@ from predictionnet.validator.reward import get_rewards -def process_uid_146_data(response, timestamp: str, organization: str): +def process_uid_146_data(response, timestamp: str, organization: str, hotkey: str): """ Decrypt and store unencrypted data from UID 146 in the organization dataset. + + Args: + response: Response from miner containing encrypted data + timestamp: Current timestamp + organization: Organization name for HuggingFace + hotkey: Miner's hotkey for data organization """ try: bt.logging.info("Processing data from UID 146...") @@ -62,6 +68,7 @@ def process_uid_146_data(response, timestamp: str, organization: str): timestamp=timestamp, miner_data=df, predictions=predictions, + hotkey=hotkey, metadata={"source_uid": "146", "original_repo": response.repo_id, **metadata}, ) @@ -141,7 +148,12 @@ async def forward(self): if uid == 146: bt.logging.info("Processing special case for UID 146...") - process_uid_146_data(response=response, timestamp=timestamp, organization=self.config.neuron.organization) + process_uid_146_data( + response=response, + timestamp=timestamp, + organization=self.config.neuron.organization, + hotkey=self.metagraph.hotkeys[uid], + ) rewards = get_rewards(self, responses=responses, miner_uids=miner_uids) From ada68124b0f063c8198b64af5ca7cb218c4c7c54 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Fri, 13 Dec 2024 13:15:17 -0500 Subject: [PATCH 24/57] remove nested folder --- predictionnet/utils/dataset_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index d1f877e6..2fa600b4 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -175,7 +175,7 @@ def store_data( bt.logging.debug(f"Repository already exists or creation failed: {str(e)}") # Create unique filename with hotkey path - filename = f"{hotkey}/data/market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" + filename = f"{hotkey}/market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" # Upload directly using HfApi self.api.upload_file( From 74bf79d670a4a04865d2a4ccc7aea9869d1cf873 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Fri, 13 Dec 2024 13:29:31 -0500 Subject: [PATCH 25/57] generalize the data decryption and upload --- predictionnet/validator/forward.py | 100 +++++++++++++++-------------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 26bc8d70..2a1a4fa9 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -13,6 +13,7 @@ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. +import asyncio import time from datetime import datetime, timedelta @@ -28,18 +29,39 @@ from predictionnet.validator.reward import get_rewards -def process_uid_146_data(response, timestamp: str, organization: str, hotkey: str): +def can_process_data(response) -> bool: """ - Decrypt and store unencrypted data from UID 146 in the organization dataset. + Check if a response has the required data for processing. + + Args: + response: Miner response object + + Returns: + bool: True if response has all required fields + """ + return all( + [ + hasattr(response, "repo_id"), + hasattr(response, "data"), + hasattr(response, "decryption_key"), + hasattr(response, "prediction"), + ] + ) + + +async def process_miner_data(response, timestamp: str, organization: str, hotkey: str, uid: int): + """ + Decrypt and store unencrypted data from a miner in the organization dataset. Args: response: Response from miner containing encrypted data timestamp: Current timestamp organization: Organization name for HuggingFace hotkey: Miner's hotkey for data organization + uid: Miner's UID """ try: - bt.logging.info("Processing data from UID 146...") + bt.logging.info(f"Processing data from UID {uid}...") # Initialize DatasetManager with explicit organization dataset_manager = DatasetManager(organization=organization) @@ -63,22 +85,17 @@ def process_uid_146_data(response, timestamp: str, organization: str, hotkey: st bt.logging.info(f"Successfully decrypted data with shape: {df.shape}") - # Store data using DatasetManager's unencrypted storage - success, upload_result = dataset_manager.store_data( + # Store data using DatasetManager's async storage + await dataset_manager.store_data_async( timestamp=timestamp, miner_data=df, predictions=predictions, hotkey=hotkey, - metadata={"source_uid": "146", "original_repo": response.repo_id, **metadata}, + metadata={"source_uid": str(uid), "original_repo": response.repo_id, **metadata}, ) - if success: - bt.logging.success(f"Successfully stored data: {upload_result}") - else: - bt.logging.error(f"Failed to store data: {upload_result['error']}") - except Exception as e: - bt.logging.error(f"Error processing UID 146 data: {str(e)}") + bt.logging.error(f"Error processing data from UID {uid}: {str(e)}") bt.logging.error(f"Full data path: {data_path}") @@ -89,74 +106,67 @@ async def forward(self): Args: self (:obj:`bittensor.neuron.Neuron`): The neuron object which contains all the necessary state for the validator. """ - # TODO(developer): Define how the validator selects a miner to query, how often, etc. - # get_random_uids is an example method, but you can replace it with your own. - - # wait for market to be open + # Market timing setup ny_timezone = timezone("America/New_York") current_time_ny = datetime.now(ny_timezone) bt.logging.info("Current time: ", current_time_ny) - # block forward from running if market is closed + + # Block forward from running if market is closed while True: if await self.is_valid_time(): bt.logging.info("Market is open. Begin processes requests") break else: bt.logging.info("Market is closed. Sleeping for 2 minutes...") - time.sleep(120) # Sleep for 5 minutes before checking again + time.sleep(120) if datetime.now(ny_timezone) - current_time_ny >= timedelta(hours=1): self.resync_metagraph() self.set_weights() self.past_predictions = [full((self.N_TIMEPOINTS, self.N_TIMEPOINTS), nan)] * len(self.hotkeys) current_time_ny = datetime.now(ny_timezone) - # miner_uids = get_random_uids(self, k=min(self.config.neuron.sample_size, self.metagraph.n.item())) - # get all uids + # Get available miner UIDs miner_uids = [] for uid in range(len(self.metagraph.S)): uid_is_available = check_uid_availability(self.metagraph, uid, self.config.neuron.vpermit_tao_limit) if uid_is_available: miner_uids.append(uid) - # Here input data should be gathered to send to the miners - # TODO(create get_input_data()) + # Get current timestamp current_time_ny = datetime.now(ny_timezone) timestamp = current_time_ny.isoformat() # Build synapse for request - # Replace dummy_input with actually defined variables in protocol.py - # This can be combined with line 49 - synapse = predictionnet.protocol.Challenge( - timestamp=timestamp, - ) + synapse = predictionnet.protocol.Challenge(timestamp=timestamp) + # Query miners responses = self.dendrite.query( - # Send the query to selected miner axons in the network. axons=[self.metagraph.axons[uid] for uid in miner_uids], - # Construct a dummy query. This simply contains a single integer. - # This can be simplified later to all build from here synapse=synapse, - # synapse=Dummy(dummy_input=self.step), - # All responses have the deserialize function called on them before returning. - # You are encouraged to define your own deserialization function. - # Other subnets have this turned to false, I am unsure of whether this should be set to true deserialize=False, ) - # Log the results for monitoring purposes. + + # Process responses and initiate background data processing for uid, response in zip(miner_uids, responses): bt.logging.info(f"UID: {uid} | Predictions: {response.prediction}") - if uid == 146: - bt.logging.info("Processing special case for UID 146...") - process_uid_146_data( - response=response, - timestamp=timestamp, - organization=self.config.neuron.organization, - hotkey=self.metagraph.hotkeys[uid], + # Check if response has required data fields + if can_process_data(response): + # Create background task for data processing + asyncio.create_task( + process_miner_data( + response=response, + timestamp=timestamp, + organization=self.config.neuron.organization, + hotkey=self.metagraph.hotkeys[uid], + uid=uid, + ) ) + # Calculate rewards rewards = get_rewards(self, responses=responses, miner_uids=miner_uids) + # Log results to wandb wandb_val_log = { "miners_info": { miner_uid: { @@ -166,12 +176,8 @@ async def forward(self): for miner_uid, response, reward in zip(miner_uids, responses, rewards.tolist()) } } - wandb.log(wandb_val_log) - # Potentially will need some + # Log scores and update bt.logging.info(f"Scored responses: {rewards}") - # Update the scores based on the rewards. You may want to define your own update_scores function for custom behavior. - - # Check base validator file self.update_scores(rewards, miner_uids) From c5f1e5ae86000a0ee49cc65e8ad0c19022183dbe Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Mon, 16 Dec 2024 09:20:59 -0500 Subject: [PATCH 26/57] add back the model confirmation check for scoring miners --- predictionnet/validator/forward.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 2a1a4fa9..53857910 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -180,4 +180,7 @@ async def forward(self): # Log scores and update bt.logging.info(f"Scored responses: {rewards}") + models_confirmed = self.confirm_models(responses, miner_uids) + bt.logging.info(f"Models Confirmed: {models_confirmed}") + rewards = [0 if not model_confirmed else reward for reward, model_confirmed in zip(rewards, models_confirmed)] self.update_scores(rewards, miner_uids) From ccd90a592b2e4e8c0299d199b63379f85438567c Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Mon, 16 Dec 2024 09:54:59 -0500 Subject: [PATCH 27/57] remove unecessary logs --- predictionnet/validator/forward.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 53857910..1b3ed3f3 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -31,22 +31,15 @@ def can_process_data(response) -> bool: """ - Check if a response has the required data for processing. + Check if a response has required data fields populated. Args: response: Miner response object Returns: - bool: True if response has all required fields + bool: True if all required fields have non-empty values """ - return all( - [ - hasattr(response, "repo_id"), - hasattr(response, "data"), - hasattr(response, "decryption_key"), - hasattr(response, "prediction"), - ] - ) + return all([bool(response.repo_id), bool(response.data), bool(response.decryption_key), bool(response.prediction)]) async def process_miner_data(response, timestamp: str, organization: str, hotkey: str, uid: int): From 0284664f383cf777b7f2dea2ca54859876347542 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Mon, 16 Dec 2024 10:09:57 -0500 Subject: [PATCH 28/57] Add better docstrings to miner_hf.py functions --- predictionnet/utils/dataset_manager.py | 126 +++++++++++++++++++------ predictionnet/utils/miner_hf.py | 63 ++++++++++++- 2 files changed, 160 insertions(+), 29 deletions(-) diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index 2fa600b4..5c277ae8 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -32,9 +32,18 @@ class DatasetManager: def __init__(self, organization: str): """ - Initialize the DatasetManager. + Initialize the DatasetManager for handling HuggingFace dataset operations. + Args: - organization: The HuggingFace organization name + organization (str): The HuggingFace organization name to store datasets under + + Raises: + ValueError: If HF_ACCESS_TOKEN environment variable is not set + + Notes: + - Sets up ThreadPoolExecutor for async operations + - Configures max repository size limit (300GB) + - Requires HF_ACCESS_TOKEN environment variable to be set """ self.token = os.getenv("HF_ACCESS_TOKEN") if not self.token: @@ -46,18 +55,28 @@ def __init__(self, organization: str): self.executor = ThreadPoolExecutor(max_workers=1) def _get_current_repo_name(self) -> str: - """Generate repository name based on current date.""" + """ + Generate repository name based on current date in YYYY-MM format. + + Returns: + str: Repository name in format 'dataset-YYYY-MM' + """ return f"dataset-{datetime.now().strftime('%Y-%m')}" def _get_repo_size(self, repo_id: str) -> int: """ - Calculate total size of repository in bytes. + Calculate total size of repository by summing all file sizes. Args: - repo_id: Full repository ID (org/name) + repo_id (str): Full repository ID in format 'organization/name' Returns: - Total size in bytes + int: Total repository size in bytes + + Notes: + - Handles missing files or metadata gracefully + - Returns 0 if repository doesn't exist or on error + - Logs errors for individual file metadata retrieval failures """ try: files = self.api.list_repo_files(repo_id) @@ -77,13 +96,20 @@ def _get_repo_size(self, repo_id: str) -> int: def _create_new_repo(self, repo_name: str) -> str: """ - Create a new dataset repository. + Create a new dataset repository in the organization. Args: - repo_name: Name of the repository + repo_name (str): Name of the repository to create Returns: - Full repository ID + str: Full repository ID in format 'organization/name' + + Raises: + Exception: If repository creation fails + + Notes: + - Creates public dataset repository + - Logs success or failure of creation """ repo_id = f"{self.organization}/{repo_name}" try: @@ -97,13 +123,19 @@ def _create_new_repo(self, repo_name: str) -> str: def verify_encryption_key(self, key: bytes) -> bool: """ - Verify that an encryption key is valid for Fernet. + Verify that an encryption key is valid for Fernet encryption. Args: - key: The key to verify + key (bytes): The encryption key to verify Returns: - bool: True if key is valid + bool: True if key is valid Fernet key, False otherwise + + Notes: + - Checks base64 encoding + - Verifies key length is exactly 32 bytes + - Attempts Fernet initialization + - Logs specific validation errors """ try: # Check if key is valid base64 @@ -130,14 +162,33 @@ def store_data( metadata: Optional[Dict] = None, ) -> Tuple[bool, Dict]: """ - Store market data in the appropriate dataset repository using HfApi. + Store market data and metadata in a HuggingFace dataset repository. Args: - timestamp: Current timestamp - miner_data: DataFrame containing market data - predictions: Dictionary containing prediction results - hotkey: Miner's hotkey for organizing data - metadata: Optional metadata about the collection + timestamp (str): Current timestamp for data identification + miner_data (pd.DataFrame): DataFrame containing market data to store + predictions (Dict): Dictionary of prediction results + hotkey (str): Miner's hotkey for data organization + metadata (Optional[Dict]): Additional metadata about the collection + + Returns: + Tuple[bool, Dict]: Pair containing: + - bool: Success status of storage operation + - Dict: Result data containing: + - repo_id: Full repository ID + - filename: Path to stored file + - rows: Number of data rows + - columns: Number of columns + - error: Error message if failed + + Raises: + ValueError: If miner_data is not a non-empty DataFrame + + Notes: + - Creates repository if it doesn't exist + - Organizes data by hotkey in repository + - Includes metadata as CSV comments + - Uses standardized filename format """ try: if not isinstance(miner_data, pd.DataFrame) or miner_data.empty: @@ -195,17 +246,26 @@ def store_data( def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dict]: """ - Decrypt data from a HuggingFace repository file using the provided key. + Decrypt and load data from a HuggingFace repository file. Args: - data_path: Full repository path (org/repo_type/hotkey/data/filename format) - decryption_key: Raw Fernet key in bytes format + data_path (str): Full repository path (org/repo_type/hotkey/data/filename) + decryption_key (bytes): Raw Fernet decryption key Returns: - Tuple of (success, result) where result contains: - - data: pandas DataFrame of the CSV data - - metadata: Dictionary of metadata from CSV comments - - predictions: Dictionary of predictions if present + Tuple[bool, Dict]: Pair containing: + - bool: Success status of decryption + - Dict: Result data containing: + - data: Decrypted DataFrame + - metadata: Extracted metadata dictionary + - predictions: Extracted predictions dictionary + - error: Error message if failed + + Notes: + - Downloads file from HuggingFace hub + - Separates CSV data from metadata comments + - Parses metadata into structured format + - Handles prediction data separately if present """ try: bt.logging.info(f"Attempting to decrypt data from path: {data_path}") @@ -260,8 +320,20 @@ async def store_data_async( metadata: Optional[Dict] = None, ) -> None: """ - Asynchronously store data in the appropriate dataset repository. - Does not block or return results. + Asynchronously store data in the dataset repository without blocking. + + Args: + timestamp (str): Current timestamp for data identification + miner_data (pd.DataFrame): DataFrame containing market data + predictions (Dict): Dictionary of prediction results + hotkey (str): Miner's hotkey for data organization + metadata (Optional[Dict]): Additional metadata about the collection + + Notes: + - Creates background task for storage operation + - Uses ThreadPoolExecutor for async execution + - Logs success or failure but does not return results + - Does not block the calling coroutine """ loop = asyncio.get_event_loop() diff --git a/predictionnet/utils/miner_hf.py b/predictionnet/utils/miner_hf.py index 2714a648..eba677a5 100644 --- a/predictionnet/utils/miner_hf.py +++ b/predictionnet/utils/miner_hf.py @@ -21,7 +21,34 @@ def __init__(self, config: "bt.Config"): bt.logging.debug(f"Initializing with config: model={config.model}, repo_id={config.hf_repo_id}") def upload_model(self, repo_id=None, model_path=None, hotkey=None): - """Upload a model file to HuggingFace Hub.""" + """ + Upload a model file to HuggingFace Hub with organized directory structure. + + Args: + repo_id (str, optional): The HuggingFace repository ID where the model will be uploaded. + If not provided, uses the default from config. + model_path (str, optional): Local file path to the model that will be uploaded. + Must have a valid file extension. + hotkey (str, optional): Hotkey identifier used to create the model's subdirectory + structure in the format '{hotkey}/models/'. + + Returns: + tuple: A pair containing: + - bool: Success status of the upload + - dict: Response data containing: + - 'hotkey': The provided hotkey (if successful with commits) + - 'timestamp': Upload timestamp (if successful with commits) + - 'model_path': Full path where model was uploaded + - 'error': Error message (if upload failed) + + Raises: + ValueError: If the model_path lacks a file extension + + Notes: + - Creates the repository if it doesn't exist + - Organizes models in subdirectories by hotkey + - Model files are renamed to 'model{extension}' in the repository + """ bt.logging.debug(f"Trying to upload model: repo_id={repo_id}, model_path={model_path}, hotkey={hotkey}") try: @@ -64,7 +91,39 @@ def upload_model(self, repo_id=None, model_path=None, hotkey=None): return False, {"error": str(e)} def upload_data(self, repo_id=None, data: pd.DataFrame = None, hotkey=None, encryption_key=None): - """Upload encrypted training/validation data to HuggingFace Hub.""" + """ + Upload encrypted training/validation data to HuggingFace Hub with secure encryption. + + Args: + repo_id (str, optional): The HuggingFace repository ID where the data will be uploaded. + If not provided, uses the default from config. + data (pd.DataFrame): DataFrame containing the data to be encrypted and uploaded. + Must be non-empty. + hotkey (str, optional): Hotkey identifier used to create the data's subdirectory + structure in the format '{hotkey}/data/'. + encryption_key (str or bytes): Key used for encrypting the data before upload. + Must be a valid Fernet encryption key. + + Returns: + tuple: A pair containing: + - bool: Success status of the upload + - dict: Response data containing: + - 'hotkey': The provided hotkey (if successful) + - 'timestamp': Upload timestamp in YYYYMMDD_HHMMSS format + - 'data_path': Full path where encrypted data was uploaded + - 'error': Error message (if upload failed) + + Raises: + ValueError: If data is not a non-empty DataFrame or if encryption_key is not provided + Exception: If file operations or upload process fails + + Notes: + - Data is encrypted using Fernet symmetric encryption + - Creates unique filenames using timestamps + - Temporarily stores encrypted data locally before upload + - Creates the repository if it doesn't exist + - Maintains organized directory structure by hotkey + """ if not repo_id: repo_id = self.config.hf_repo_id From fd97de68fb8d250a8a01af4dde41fd79a64addb2 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Mon, 16 Dec 2024 12:38:11 -0500 Subject: [PATCH 29/57] update file type to be parquet --- predictionnet/utils/dataset_manager.py | 454 ++++++++++++------------- predictionnet/validator/forward.py | 46 ++- 2 files changed, 242 insertions(+), 258 deletions(-) diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index 5c277ae8..735ebae7 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -18,142 +18,78 @@ import asyncio import json import os +import shutil +import tempfile from concurrent.futures import ThreadPoolExecutor from datetime import datetime -from io import StringIO +from pathlib import Path from typing import Dict, Optional, Tuple import bittensor as bt +import git import pandas as pd +import pyarrow as pa +import pyarrow.parquet as pq from cryptography.fernet import Fernet -from huggingface_hub import HfApi, create_repo, hf_hub_download +from dotenv import load_dotenv +from git import Repo +from huggingface_hub import HfApi, hf_hub_download + +load_dotenv() class DatasetManager: - def __init__(self, organization: str): + def __init__(self, organization: str, local_storage_path: str = "./local_data"): """ - Initialize the DatasetManager for handling HuggingFace dataset operations. + Initialize DatasetManager for handling dataset operations. Args: - organization (str): The HuggingFace organization name to store datasets under + organization (str): The HuggingFace organization name + local_storage_path (str): Path to store local data before batch upload Raises: - ValueError: If HF_ACCESS_TOKEN environment variable is not set - - Notes: - - Sets up ThreadPoolExecutor for async operations - - Configures max repository size limit (300GB) - - Requires HF_ACCESS_TOKEN environment variable to be set + ValueError: If required environment variables are not set """ - self.token = os.getenv("HF_ACCESS_TOKEN") - if not self.token: - raise ValueError("HF_ACCESS_TOKEN environment variable not set") + # Load required tokens and credentials + self.hf_token = os.getenv("HF_ACCESS_TOKEN") + self.git_username = os.getenv("GIT_USERNAME", "") + self.git_token = os.getenv("GIT_TOKEN") + + if not self.git_token: + raise ValueError("GIT_TOKEN environment variable not set") - self.api = HfApi(token=self.token) + self.api = HfApi(token=self.hf_token) self.organization = organization - self.MAX_REPO_SIZE = 300 * 1024 * 1024 * 1024 # 300GB self.executor = ThreadPoolExecutor(max_workers=1) - def _get_current_repo_name(self) -> str: - """ - Generate repository name based on current date in YYYY-MM format. + # Local storage setup + self.local_storage = Path(local_storage_path) + self.local_storage.mkdir(parents=True, exist_ok=True) + + # Track current day's data + self.current_day = datetime.now().strftime("%Y-%m-%d") + self.day_storage = self.local_storage / self.current_day + self.day_storage.mkdir(parents=True, exist_ok=True) + + # Git configuration + if self.git_username: + self.git_url_template = f"https://{self.git_username}:{self.git_token}@huggingface.co/datasets/{self.organization}/{{repo_name}}" + else: + self.git_url_template = ( + f"https://{self.git_token}@huggingface.co/datasets/{self.organization}/{{repo_name}}" + ) - Returns: - str: Repository name in format 'dataset-YYYY-MM' - """ + def _get_current_repo_name(self) -> str: + """Generate repository name based on current date""" return f"dataset-{datetime.now().strftime('%Y-%m')}" - def _get_repo_size(self, repo_id: str) -> int: - """ - Calculate total size of repository by summing all file sizes. - - Args: - repo_id (str): Full repository ID in format 'organization/name' - - Returns: - int: Total repository size in bytes - - Notes: - - Handles missing files or metadata gracefully - - Returns 0 if repository doesn't exist or on error - - Logs errors for individual file metadata retrieval failures - """ - try: - files = self.api.list_repo_files(repo_id) - total_size = 0 - - for file_info in files: - try: - file_metadata = self.api.get_file_metadata(repo_id=repo_id, filename=file_info) - total_size += file_metadata.size - except Exception as e: - bt.logging.error(f"Error getting metadata for {file_info}: {e}") - continue - - return total_size - except Exception: - return 0 - - def _create_new_repo(self, repo_name: str) -> str: - """ - Create a new dataset repository in the organization. - - Args: - repo_name (str): Name of the repository to create - - Returns: - str: Full repository ID in format 'organization/name' - - Raises: - Exception: If repository creation fails - - Notes: - - Creates public dataset repository - - Logs success or failure of creation - """ - repo_id = f"{self.organization}/{repo_name}" - try: - create_repo(repo_id=repo_id, repo_type="dataset", private=False, token=self.token) - bt.logging.success(f"Created new repository: {repo_id}") - except Exception as e: - bt.logging.error(f"Error creating repository {repo_id}: {e}") - raise - - return repo_id - - def verify_encryption_key(self, key: bytes) -> bool: - """ - Verify that an encryption key is valid for Fernet encryption. - - Args: - key (bytes): The encryption key to verify - - Returns: - bool: True if key is valid Fernet key, False otherwise + def _get_local_path(self, hotkey: str) -> Path: + """Get local storage path for a specific hotkey""" + path = self.day_storage / hotkey + path.mkdir(parents=True, exist_ok=True) + return path - Notes: - - Checks base64 encoding - - Verifies key length is exactly 32 bytes - - Attempts Fernet initialization - - Logs specific validation errors - """ - try: - # Check if key is valid base64 - import base64 - - decoded = base64.b64decode(key) - # Check if key length is correct (32 bytes) - if len(decoded) != 32: - bt.logging.error(f"Invalid key length: {len(decoded)} bytes (expected 32)") - return False - # Try to initialize Fernet - Fernet(key) - return True - except Exception as e: - bt.logging.error(f"Invalid encryption key: {str(e)}") - return False - - def store_data( + def store_local_data( self, timestamp: str, miner_data: pd.DataFrame, @@ -162,110 +98,174 @@ def store_data( metadata: Optional[Dict] = None, ) -> Tuple[bool, Dict]: """ - Store market data and metadata in a HuggingFace dataset repository. + Store data locally in Parquet format. Args: - timestamp (str): Current timestamp for data identification - miner_data (pd.DataFrame): DataFrame containing market data to store + timestamp (str): Current timestamp + miner_data (pd.DataFrame): DataFrame containing market data predictions (Dict): Dictionary of prediction results - hotkey (str): Miner's hotkey for data organization - metadata (Optional[Dict]): Additional metadata about the collection + hotkey (str): Miner's hotkey + metadata (Optional[Dict]): Additional metadata Returns: - Tuple[bool, Dict]: Pair containing: - - bool: Success status of storage operation - - Dict: Result data containing: - - repo_id: Full repository ID - - filename: Path to stored file - - rows: Number of data rows - - columns: Number of columns - - error: Error message if failed - - Raises: - ValueError: If miner_data is not a non-empty DataFrame - - Notes: - - Creates repository if it doesn't exist - - Organizes data by hotkey in repository - - Includes metadata as CSV comments - - Uses standardized filename format + Tuple[bool, Dict]: Success status and result data """ try: if not isinstance(miner_data, pd.DataFrame) or miner_data.empty: raise ValueError("miner_data must be a non-empty pandas DataFrame") - # Get or create repository - repo_name = self._get_current_repo_name() - repo_id = f"{self.organization}/{repo_name}" - - # Convert DataFrame to CSV - csv_buffer = StringIO() - miner_data.to_csv(csv_buffer, index=True) - csv_data = csv_buffer.getvalue() - - # Add metadata as comments - metadata_buffer = StringIO() - metadata_buffer.write("\n# Metadata:\n") - metadata_buffer.write(f"# timestamp: {timestamp}\n") - metadata_buffer.write(f"# columns: {','.join(miner_data.columns)}\n") - metadata_buffer.write(f"# shape: {miner_data.shape[0]},{miner_data.shape[1]}\n") - metadata_buffer.write(f"# hotkey: {hotkey}\n") - if metadata: - for key, value in metadata.items(): - metadata_buffer.write(f"# {key}: {value}\n") - if predictions: - metadata_buffer.write(f"# predictions: {json.dumps(predictions)}\n") + # Get local storage path for this hotkey + local_path = self._get_local_path(hotkey) - # Combine CSV data and metadata - full_data = csv_data + metadata_buffer.getvalue() + # Create filename + filename = f"market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.parquet" + file_path = local_path / filename - # Ensure repository exists - try: - self.api.create_repo(repo_id=repo_id, repo_type="dataset", exist_ok=True) - except Exception as e: - bt.logging.debug(f"Repository already exists or creation failed: {str(e)}") + # Prepare metadata + full_metadata = { + "timestamp": timestamp, + "columns": ",".join(miner_data.columns), + "shape": f"{miner_data.shape[0]},{miner_data.shape[1]}", + "hotkey": hotkey, + "predictions": json.dumps(predictions) if predictions else "", + } + if metadata: + full_metadata.update(metadata) - # Create unique filename with hotkey path - filename = f"{hotkey}/market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" + # Convert to PyArrow Table with metadata + table = pa.Table.from_pandas(miner_data) + for key, value in full_metadata.items(): + table = table.replace_schema_metadata({**table.schema.metadata, key.encode(): str(value).encode()}) - # Upload directly using HfApi - self.api.upload_file( - path_or_fileobj=full_data.encode(), path_in_repo=filename, repo_id=repo_id, repo_type="dataset" - ) + # Write Parquet file with compression + pq.write_table(table, file_path, compression="snappy", use_dictionary=True, use_byte_stream_split=True) return True, { - "repo_id": repo_id, - "filename": filename, + "local_path": str(file_path), "rows": miner_data.shape[0], "columns": miner_data.shape[1], } except Exception as e: - bt.logging.error(f"Error in store_data: {str(e)}") + bt.logging.error(f"Error in store_local_data: {str(e)}") + return False, {"error": str(e)} + + def _configure_git_repo(self, repo: Repo): + """Configure Git repository with user information""" + git_name = os.getenv("GIT_NAME", "DatasetManager") + git_email = os.getenv("GIT_EMAIL", "noreply@example.com") + + with repo.config_writer() as git_config: + git_config.set_value("user", "name", git_name) + git_config.set_value("user", "email", git_email) + + def _setup_git_repo(self, repo_name: str) -> Tuple[Repo, Path]: + """Set up Git repository for batch upload""" + temp_dir = Path(tempfile.mkdtemp()) + repo_url = self.git_url_template.format(repo_name=repo_name) + + try: + # Try to clone existing repo + repo = Repo.clone_from(repo_url, temp_dir) + bt.logging.success(f"Cloned existing repository: {repo_name}") + except git.exc.GitCommandError: + # Repository doesn't exist, create new + bt.logging.info(f"Creating new repository: {repo_name}") + repo = Repo.init(temp_dir) + + # Configure Git + self._configure_git_repo(repo) + + # Set up remote + repo.create_remote("origin", repo_url) + + # Create initial commit + readme_path = temp_dir / "README.md" + readme_path.write_text(f"# {repo_name}\nDataset repository managed by DatasetManager") + repo.index.add(["README.md"]) + repo.index.commit("Initial commit") + + # Push to create repository + origin = repo.remote("origin") + origin.push(refspec="master:master") + + return repo, temp_dir + + async def batch_upload_daily_data(self) -> Tuple[bool, Dict]: + """Upload all locally stored data using Git""" + try: + repo_name = self._get_current_repo_name() + repo, temp_dir = self._setup_git_repo(repo_name) + + uploaded_files = [] + total_rows = 0 + + try: + # Copy all files from day storage to Git repo + for hotkey_dir in self.day_storage.iterdir(): + if hotkey_dir.is_dir(): + hotkey = hotkey_dir.name + repo_hotkey_dir = temp_dir / hotkey + repo_hotkey_dir.mkdir(exist_ok=True) + + for file_path in hotkey_dir.glob("*.parquet"): + try: + # Copy file + target_path = repo_hotkey_dir / file_path.name + shutil.copy2(file_path, target_path) + + # Track file + rel_path = str(target_path.relative_to(temp_dir)) + uploaded_files.append(rel_path) + + # Count rows + table = pq.read_table(file_path) + total_rows += table.num_rows + + # Stage file + repo.index.add([rel_path]) + + except Exception as e: + bt.logging.error(f"Error processing {file_path}: {str(e)}") + continue + + # Commit and push if there are changes + if repo.is_dirty() or len(repo.untracked_files) > 0: + commit_message = f"Batch upload for {self.current_day}" + repo.index.commit(commit_message) + + origin = repo.remote("origin") + origin.push() + + bt.logging.success(f"Successfully pushed {len(uploaded_files)} files to {repo_name}") + else: + bt.logging.info("No changes to upload") + + return True, { + "repo_id": f"{self.organization}/{repo_name}", + "files_uploaded": len(uploaded_files), + "total_rows": total_rows, + "paths": uploaded_files, + } + + finally: + # Clean up temporary directory + shutil.rmtree(temp_dir) + + except Exception as e: + bt.logging.error(f"Error in batch_upload_daily_data: {str(e)}") return False, {"error": str(e)} def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dict]: """ - Decrypt and load data from a HuggingFace repository file. + Decrypt and load data from a HuggingFace repository file in Parquet format. Args: data_path (str): Full repository path (org/repo_type/hotkey/data/filename) decryption_key (bytes): Raw Fernet decryption key Returns: - Tuple[bool, Dict]: Pair containing: - - bool: Success status of decryption - - Dict: Result data containing: - - data: Decrypted DataFrame - - metadata: Extracted metadata dictionary - - predictions: Extracted predictions dictionary - - error: Error message if failed - - Notes: - - Downloads file from HuggingFace hub - - Separates CSV data from metadata comments - - Parses metadata into structured format - - Handles prediction data separately if present + Tuple[bool, Dict]: Success status and decrypted data with metadata """ try: bt.logging.info(f"Attempting to decrypt data from path: {data_path}") @@ -282,27 +282,33 @@ def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dic with open(local_path, "rb") as file: encrypted_data = file.read() - decrypted_data = fernet.decrypt(encrypted_data).decode() + decrypted_data = fernet.decrypt(encrypted_data) - # Split data into CSV and metadata sections - parts = decrypted_data.split("\n# Metadata:") + # Read Parquet from decrypted data + with tempfile.NamedTemporaryFile(suffix=".parquet") as temp_file: + temp_file.write(decrypted_data) + temp_file.flush() - # Parse CSV data into DataFrame - df = pd.read_csv(StringIO(parts[0])) + # Read Parquet file + table = pq.read_table(temp_file.name) + df = table.to_pandas() - # Parse metadata - metadata = {} - predictions = None - if len(parts) > 1: - for line in parts[1].split("\n"): - if line.startswith("# "): + # Extract metadata from Parquet schema + metadata = {} + predictions = None + + if table.schema.metadata: + for key, value in table.schema.metadata.items(): try: - key, value = line[2:].split(": ", 1) - if key == "predictions": - predictions = json.loads(value) + key_str = key.decode() if isinstance(key, bytes) else key + value_str = value.decode() if isinstance(value, bytes) else value + + if key_str == "predictions": + predictions = json.loads(value_str) else: - metadata[key] = value - except ValueError: + metadata[key_str] = value_str + except Exception as e: + bt.logging.error(f"Error while extracting metadata: {str(e)}") continue return True, {"data": df, "metadata": metadata, "predictions": predictions} @@ -318,38 +324,22 @@ async def store_data_async( predictions: Dict, hotkey: str, metadata: Optional[Dict] = None, - ) -> None: - """ - Asynchronously store data in the dataset repository without blocking. - - Args: - timestamp (str): Current timestamp for data identification - miner_data (pd.DataFrame): DataFrame containing market data - predictions (Dict): Dictionary of prediction results - hotkey (str): Miner's hotkey for data organization - metadata (Optional[Dict]): Additional metadata about the collection - - Notes: - - Creates background task for storage operation - - Uses ThreadPoolExecutor for async execution - - Logs success or failure but does not return results - - Does not block the calling coroutine - """ + ) -> Tuple[bool, Dict]: + """Async wrapper for store_local_data""" loop = asyncio.get_event_loop() + return await loop.run_in_executor( + self.executor, lambda: self.store_local_data(timestamp, miner_data, predictions, hotkey, metadata) + ) - async def _store(): - try: - result = await loop.run_in_executor( - self.executor, lambda: self.store_data(timestamp, miner_data, predictions, hotkey, metadata) - ) - - success, upload_result = result - if success: - bt.logging.success(f"Stored market data in dataset: {upload_result['repo_id']}") - else: - bt.logging.error(f"Failed to store market data: {upload_result['error']}") + def cleanup_local_storage(self, days_to_keep: int = 7): + """Clean up old local storage directories""" + try: + dirs = sorted([d for d in self.local_storage.iterdir() if d.is_dir()]) - except Exception as e: - bt.logging.error(f"Error in async data storage: {str(e)}") + if len(dirs) > days_to_keep: + for old_dir in dirs[:-days_to_keep]: + shutil.rmtree(old_dir) + bt.logging.success(f"Cleaned up {len(dirs) - days_to_keep} old data directories") - asyncio.create_task(_store()) + except Exception as e: + bt.logging.error(f"Error cleaning up local storage: {str(e)}") diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 1b3ed3f3..d414a623 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -42,9 +42,9 @@ def can_process_data(response) -> bool: return all([bool(response.repo_id), bool(response.data), bool(response.decryption_key), bool(response.prediction)]) -async def process_miner_data(response, timestamp: str, organization: str, hotkey: str, uid: int): +async def process_miner_data(response, timestamp: str, organization: str, hotkey: str, uid: int) -> bool: """ - Decrypt and store unencrypted data from a miner in the organization dataset. + Verify that miner's data can be decrypted and attempt to store it. Args: response: Response from miner containing encrypted data @@ -52,44 +52,38 @@ async def process_miner_data(response, timestamp: str, organization: str, hotkey organization: Organization name for HuggingFace hotkey: Miner's hotkey for data organization uid: Miner's UID + + Returns: + bool: True if data was successfully decrypted, False otherwise """ try: bt.logging.info(f"Processing data from UID {uid}...") - - # Initialize DatasetManager with explicit organization dataset_manager = DatasetManager(organization=organization) - - # Build complete path using repo_id and data path data_path = f"{response.repo_id}/{response.data}" - bt.logging.info(f"Attempting to decrypt data from path: {data_path}") - - # Attempt to decrypt the data + # Verify decryption works success, result = dataset_manager.decrypt_data(data_path=data_path, decryption_key=response.decryption_key) if not success: bt.logging.error(f"Failed to decrypt data: {result['error']}") - return - - # Get the decrypted data - df = result["data"] - metadata = result.get("metadata", {}) - predictions = result.get("predictions", {}) - - bt.logging.info(f"Successfully decrypted data with shape: {df.shape}") - - # Store data using DatasetManager's async storage - await dataset_manager.store_data_async( - timestamp=timestamp, - miner_data=df, - predictions=predictions, - hotkey=hotkey, - metadata={"source_uid": str(uid), "original_repo": response.repo_id, **metadata}, + return False + + # Attempt to store data in background without affecting reward + asyncio.create_task( + dataset_manager.store_data_async( + timestamp=timestamp, + miner_data=result["data"], + predictions=result.get("predictions", {}), + hotkey=hotkey, + metadata={"source_uid": str(uid), "original_repo": response.repo_id, **result.get("metadata", {})}, + ) ) + return True + except Exception as e: bt.logging.error(f"Error processing data from UID {uid}: {str(e)}") - bt.logging.error(f"Full data path: {data_path}") + return False async def forward(self): From 45a3c24440160d1ccb0aa9637c1bc5a2151b5c95 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Mon, 16 Dec 2024 12:40:31 -0500 Subject: [PATCH 30/57] update file type to be parquet --- predictionnet/utils/miner_hf.py | 55 +++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/predictionnet/utils/miner_hf.py b/predictionnet/utils/miner_hf.py index eba677a5..79e193cc 100644 --- a/predictionnet/utils/miner_hf.py +++ b/predictionnet/utils/miner_hf.py @@ -1,9 +1,11 @@ import os +import tempfile from datetime import datetime -from io import StringIO import bittensor as bt import pandas as pd +import pyarrow as pa +import pyarrow.parquet as pq from cryptography.fernet import Fernet from dotenv import load_dotenv from huggingface_hub import HfApi @@ -92,7 +94,7 @@ def upload_model(self, repo_id=None, model_path=None, hotkey=None): def upload_data(self, repo_id=None, data: pd.DataFrame = None, hotkey=None, encryption_key=None): """ - Upload encrypted training/validation data to HuggingFace Hub with secure encryption. + Upload encrypted training/validation data to HuggingFace Hub using Parquet format. Args: repo_id (str, optional): The HuggingFace repository ID where the data will be uploaded. @@ -116,13 +118,6 @@ def upload_data(self, repo_id=None, data: pd.DataFrame = None, hotkey=None, encr Raises: ValueError: If data is not a non-empty DataFrame or if encryption_key is not provided Exception: If file operations or upload process fails - - Notes: - - Data is encrypted using Fernet symmetric encryption - - Creates unique filenames using timestamps - - Temporarily stores encrypted data locally before upload - - Creates the repository if it doesn't exist - - Maintains organized directory structure by hotkey """ if not repo_id: repo_id = self.config.hf_repo_id @@ -134,32 +129,44 @@ def upload_data(self, repo_id=None, data: pd.DataFrame = None, hotkey=None, encr raise ValueError("Encryption key must be provided") try: - import tempfile - fernet = Fernet(encryption_key.encode() if isinstance(encryption_key, str) else encryption_key) # Create unique filename using timestamp timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - data_filename = f"data_{timestamp}.enc" + data_filename = f"data_{timestamp}.parquet.enc" hotkey_path = f"{hotkey}/data" data_full_path = f"{hotkey_path}/{data_filename}" bt.logging.debug(f"Preparing to upload encrypted data: {data_full_path}") - # Convert DataFrame to CSV in memory - csv_buffer = StringIO() - data.to_csv(csv_buffer, index=False) - - # Encrypt the CSV data - csv_data = csv_buffer.getvalue().encode() - encrypted_data = fernet.encrypt(csv_data) - - # Create temporary directory and file with tempfile.TemporaryDirectory() as temp_dir: - temp_data_path = os.path.join(temp_dir, data_filename) + # First create temporary Parquet file + temp_parquet = os.path.join(temp_dir, "temp.parquet") + temp_encrypted = os.path.join(temp_dir, data_filename) + try: + # Convert to PyArrow Table with metadata + table = pa.Table.from_pandas(data) + table = table.replace_schema_metadata( + { + **table.schema.metadata, + b"timestamp": timestamp.encode(), + b"hotkey": hotkey.encode() if hotkey else b"", + } + ) + + # Write Parquet file with compression + pq.write_table( + table, temp_parquet, compression="snappy", use_dictionary=True, use_byte_stream_split=True + ) + + # Read and encrypt the Parquet file + with open(temp_parquet, "rb") as f: + parquet_data = f.read() + encrypted_data = fernet.encrypt(parquet_data) + # Write encrypted data to temporary file - with open(temp_data_path, "wb") as f: + with open(temp_encrypted, "wb") as f: f.write(encrypted_data) # Ensure repository exists @@ -170,7 +177,7 @@ def upload_data(self, repo_id=None, data: pd.DataFrame = None, hotkey=None, encr # Upload encrypted file bt.logging.debug(f"Uploading encrypted data file: {data_full_path}") self.api.upload_file( - path_or_fileobj=temp_data_path, + path_or_fileobj=temp_encrypted, path_in_repo=data_full_path, repo_id=repo_id, repo_type="model", From 3c6efca4d11cd49ad1c235173e3f511567e29358 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Mon, 16 Dec 2024 14:25:16 -0500 Subject: [PATCH 31/57] update dataset_manager to properly instantiate hf git repo --- predictionnet/utils/dataset_manager.py | 78 +++++++++++++++----------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index 735ebae7..e48ae7b3 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -26,7 +26,6 @@ from typing import Dict, Optional, Tuple import bittensor as bt -import git import pandas as pd import pyarrow as pa import pyarrow.parquet as pq @@ -81,7 +80,7 @@ def __init__(self, organization: str, local_storage_path: str = "./local_data"): def _get_current_repo_name(self) -> str: """Generate repository name based on current date""" - return f"dataset-{datetime.now().strftime('%Y-%m')}" + return f"dataset-{datetime.now().strftime('%Y-%m-%d')}" def _get_local_path(self, hotkey: str) -> Path: """Get local storage path for a specific hotkey""" @@ -163,83 +162,95 @@ def _setup_git_repo(self, repo_name: str) -> Tuple[Repo, Path]: """Set up Git repository for batch upload""" temp_dir = Path(tempfile.mkdtemp()) repo_url = self.git_url_template.format(repo_name=repo_name) + repo_id = f"{self.organization}/{repo_name}" try: - # Try to clone existing repo + # First check if repo exists + try: + self.api.repo_info(repo_id=repo_id, repo_type="dataset") + except Exception: + # Repository doesn't exist, create it via API + self.api.create_repo(repo_id=repo_id, repo_type="dataset", private=False) + bt.logging.info(f"Created new repository: {repo_name}") + + # Now clone the repository (it should exist) repo = Repo.clone_from(repo_url, temp_dir) - bt.logging.success(f"Cloned existing repository: {repo_name}") - except git.exc.GitCommandError: - # Repository doesn't exist, create new - bt.logging.info(f"Creating new repository: {repo_name}") - repo = Repo.init(temp_dir) + bt.logging.success(f"Cloned repository: {repo_name}") - # Configure Git + # Configure Git if needed self._configure_git_repo(repo) - # Set up remote - repo.create_remote("origin", repo_url) - - # Create initial commit - readme_path = temp_dir / "README.md" - readme_path.write_text(f"# {repo_name}\nDataset repository managed by DatasetManager") - repo.index.add(["README.md"]) - repo.index.commit("Initial commit") - - # Push to create repository - origin = repo.remote("origin") - origin.push(refspec="master:master") + return repo, temp_dir - return repo, temp_dir + except Exception as e: + # Clean up temp dir if anything fails + if temp_dir.exists(): + shutil.rmtree(temp_dir) + raise Exception(f"Failed to setup repository: {str(e)}") async def batch_upload_daily_data(self) -> Tuple[bool, Dict]: """Upload all locally stored data using Git""" try: - repo_name = self._get_current_repo_name() + print("Starting batch upload process...") + repo_name = self._get_current_repo_name() # Use daily repo name + print(f"Generated repo name: {repo_name}") + + # Setup the Git repo (clone or create new) repo, temp_dir = self._setup_git_repo(repo_name) + print(f"Repo setup completed. Temporary directory: {temp_dir}") uploaded_files = [] total_rows = 0 try: - # Copy all files from day storage to Git repo + # Iterate through hotkey directories in the day storage for hotkey_dir in self.day_storage.iterdir(): + print(f"Processing directory: {hotkey_dir}") if hotkey_dir.is_dir(): hotkey = hotkey_dir.name repo_hotkey_dir = temp_dir / hotkey repo_hotkey_dir.mkdir(exist_ok=True) + print(f"Created directory for hotkey: {repo_hotkey_dir}") + # Iterate through Parquet files in the hotkey directory for file_path in hotkey_dir.glob("*.parquet"): try: - # Copy file + print(f"Processing file: {file_path}") + # Copy file to repo hotkey directory target_path = repo_hotkey_dir / file_path.name shutil.copy2(file_path, target_path) + print(f"Copied file to {target_path}") - # Track file + # Track the file and calculate the total rows rel_path = str(target_path.relative_to(temp_dir)) uploaded_files.append(rel_path) - # Count rows + # Count rows in the Parquet file table = pq.read_table(file_path) total_rows += table.num_rows + print(f"File has {table.num_rows} rows. Total rows so far: {total_rows}") - # Stage file + # Stage the file in the Git repository repo.index.add([rel_path]) + print(f"Staged file for commit: {rel_path}") except Exception as e: - bt.logging.error(f"Error processing {file_path}: {str(e)}") + print(f"Error processing {file_path}: {str(e)}") continue # Commit and push if there are changes if repo.is_dirty() or len(repo.untracked_files) > 0: + print("Changes detected, committing and pushing...") commit_message = f"Batch upload for {self.current_day}" repo.index.commit(commit_message) + print(f"Commit created: {commit_message}") origin = repo.remote("origin") + print("Pushing changes to remote repository...") origin.push() - - bt.logging.success(f"Successfully pushed {len(uploaded_files)} files to {repo_name}") + print(f"Successfully pushed {len(uploaded_files)} files to {repo_name}") else: - bt.logging.info("No changes to upload") + print("No changes to upload") return True, { "repo_id": f"{self.organization}/{repo_name}", @@ -251,9 +262,10 @@ async def batch_upload_daily_data(self) -> Tuple[bool, Dict]: finally: # Clean up temporary directory shutil.rmtree(temp_dir) + print(f"Temporary directory cleaned up: {temp_dir}") except Exception as e: - bt.logging.error(f"Error in batch_upload_daily_data: {str(e)}") + print(f"Error in batch_upload_daily_data: {str(e)}") return False, {"error": str(e)} def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dict]: From 6e2a23b25a86b26bbde5d372777ec42f6ddfc00d Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Mon, 16 Dec 2024 14:35:06 -0500 Subject: [PATCH 32/57] update forward to check daily for files to upload after market close --- predictionnet/validator/forward.py | 59 ++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index d414a623..a917b3e8 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -86,31 +86,60 @@ async def process_miner_data(response, timestamp: str, organization: str, hotkey return False +async def handle_market_close(self, dataset_manager: DatasetManager) -> None: + """Handle data management operations when market is closed.""" + try: + # Clean up old data + dataset_manager.cleanup_local_storage(days_to_keep=7) + + # Upload today's data + success, result = await dataset_manager.batch_upload_daily_data() + if success: + bt.logging.success( + f"Daily batch upload completed. Uploaded {result.get('files_uploaded', 0)} files " + f"with {result.get('total_rows', 0)} rows to {result.get('repo_id')}" + ) + else: + bt.logging.error(f"Daily batch upload failed: {result.get('error')}") + + except Exception as e: + bt.logging.error(f"Error during market close operations: {str(e)}") + + async def forward(self): """ The forward function is called by the validator every time step. - It is responsible for querying the network and scoring the responses. - Args: - self (:obj:`bittensor.neuron.Neuron`): The neuron object which contains all the necessary state for the validator. + It queries the network and scores responses when market is open, + and handles data management when market is closed. """ - # Market timing setup ny_timezone = timezone("America/New_York") current_time_ny = datetime.now(ny_timezone) - bt.logging.info("Current time: ", current_time_ny) + dataset_manager = DatasetManager(organization=self.config.neuron.organization) + daily_ops_done = False - # Block forward from running if market is closed while True: if await self.is_valid_time(): - bt.logging.info("Market is open. Begin processes requests") + daily_ops_done = False # Reset flag when market opens break - else: - bt.logging.info("Market is closed. Sleeping for 2 minutes...") - time.sleep(120) - if datetime.now(ny_timezone) - current_time_ny >= timedelta(hours=1): - self.resync_metagraph() - self.set_weights() - self.past_predictions = [full((self.N_TIMEPOINTS, self.N_TIMEPOINTS), nan)] * len(self.hotkeys) - current_time_ny = datetime.now(ny_timezone) + + if not daily_ops_done: + await handle_market_close(self, dataset_manager) + daily_ops_done = True + + # Check metagraph every hour + if datetime.now(ny_timezone) - current_time_ny >= timedelta(hours=1): + self.resync_metagraph() + self.set_weights() + self.past_predictions = [full((self.N_TIMEPOINTS, self.N_TIMEPOINTS), nan)] * len(self.hotkeys) + current_time_ny = datetime.now(ny_timezone) + + time.sleep(120) # Sleep for 2 minutes + + if datetime.now(ny_timezone) - current_time_ny >= timedelta(hours=1): + self.resync_metagraph() + self.set_weights() + self.past_predictions = [full((self.N_TIMEPOINTS, self.N_TIMEPOINTS), nan)] * len(self.hotkeys) + current_time_ny = datetime.now(ny_timezone) # Get available miner UIDs miner_uids = [] From 74f064a2fd0ffc0903fd2f536460282511b580c8 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Mon, 16 Dec 2024 14:43:57 -0500 Subject: [PATCH 33/57] replace print statemtents with bittensor logging in dataset_manager.py --- predictionnet/utils/dataset_manager.py | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index e48ae7b3..75390725 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -191,13 +191,13 @@ def _setup_git_repo(self, repo_name: str) -> Tuple[Repo, Path]: async def batch_upload_daily_data(self) -> Tuple[bool, Dict]: """Upload all locally stored data using Git""" try: - print("Starting batch upload process...") + bt.logging.info("Starting batch upload process...") repo_name = self._get_current_repo_name() # Use daily repo name - print(f"Generated repo name: {repo_name}") + bt.logging.info(f"Generated repo name: {repo_name}") # Setup the Git repo (clone or create new) repo, temp_dir = self._setup_git_repo(repo_name) - print(f"Repo setup completed. Temporary directory: {temp_dir}") + bt.logging.info(f"Repo setup completed. Temporary directory: {temp_dir}") uploaded_files = [] total_rows = 0 @@ -205,21 +205,21 @@ async def batch_upload_daily_data(self) -> Tuple[bool, Dict]: try: # Iterate through hotkey directories in the day storage for hotkey_dir in self.day_storage.iterdir(): - print(f"Processing directory: {hotkey_dir}") + bt.logging.debug(f"Processing directory: {hotkey_dir}") if hotkey_dir.is_dir(): hotkey = hotkey_dir.name repo_hotkey_dir = temp_dir / hotkey repo_hotkey_dir.mkdir(exist_ok=True) - print(f"Created directory for hotkey: {repo_hotkey_dir}") + bt.logging.debug(f"Created directory for hotkey: {repo_hotkey_dir}") # Iterate through Parquet files in the hotkey directory for file_path in hotkey_dir.glob("*.parquet"): try: - print(f"Processing file: {file_path}") + bt.logging.debug(f"Processing file: {file_path}") # Copy file to repo hotkey directory target_path = repo_hotkey_dir / file_path.name shutil.copy2(file_path, target_path) - print(f"Copied file to {target_path}") + bt.logging.debug(f"Copied file to {target_path}") # Track the file and calculate the total rows rel_path = str(target_path.relative_to(temp_dir)) @@ -228,29 +228,29 @@ async def batch_upload_daily_data(self) -> Tuple[bool, Dict]: # Count rows in the Parquet file table = pq.read_table(file_path) total_rows += table.num_rows - print(f"File has {table.num_rows} rows. Total rows so far: {total_rows}") + bt.logging.debug(f"File has {table.num_rows} rows. Total rows so far: {total_rows}") # Stage the file in the Git repository repo.index.add([rel_path]) - print(f"Staged file for commit: {rel_path}") + bt.logging.debug(f"Staged file for commit: {rel_path}") except Exception as e: - print(f"Error processing {file_path}: {str(e)}") + bt.logging.error(f"Error processing {file_path}: {str(e)}") continue # Commit and push if there are changes if repo.is_dirty() or len(repo.untracked_files) > 0: - print("Changes detected, committing and pushing...") + bt.logging.info("Changes detected, committing and pushing...") commit_message = f"Batch upload for {self.current_day}" repo.index.commit(commit_message) - print(f"Commit created: {commit_message}") + bt.logging.info(f"Commit created: {commit_message}") origin = repo.remote("origin") - print("Pushing changes to remote repository...") + bt.logging.info("Pushing changes to remote repository...") origin.push() - print(f"Successfully pushed {len(uploaded_files)} files to {repo_name}") + bt.logging.success(f"Successfully pushed {len(uploaded_files)} files to {repo_name}") else: - print("No changes to upload") + bt.logging.info("No changes to upload") return True, { "repo_id": f"{self.organization}/{repo_name}", @@ -262,10 +262,10 @@ async def batch_upload_daily_data(self) -> Tuple[bool, Dict]: finally: # Clean up temporary directory shutil.rmtree(temp_dir) - print(f"Temporary directory cleaned up: {temp_dir}") + bt.logging.debug(f"Temporary directory cleaned up: {temp_dir}") except Exception as e: - print(f"Error in batch_upload_daily_data: {str(e)}") + bt.logging.error(f"Error in batch_upload_daily_data: {str(e)}") return False, {"error": str(e)} def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dict]: From d5af89738cc301bd6c5fd3527f57ffbf86a679b5 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Mon, 16 Dec 2024 15:57:10 -0500 Subject: [PATCH 34/57] add unit test for dataset_manager --- tests/unit/test_dataset_manager.py | 126 +++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 tests/unit/test_dataset_manager.py diff --git a/tests/unit/test_dataset_manager.py b/tests/unit/test_dataset_manager.py new file mode 100644 index 00000000..0adbe662 --- /dev/null +++ b/tests/unit/test_dataset_manager.py @@ -0,0 +1,126 @@ +import shutil +import tempfile +from datetime import datetime +from pathlib import Path + +import pandas as pd +import pytest + +from predictionnet.utils.dataset_manager import DatasetManager + + +@pytest.fixture +def mock_env_vars(monkeypatch): + """Setup environment variables for testing""" + monkeypatch.setenv("HF_ACCESS_TOKEN", "mock_hf_token") + monkeypatch.setenv("GIT_TOKEN", "mock_git_token") + monkeypatch.setenv("GIT_NAME", "mock_git_name") + monkeypatch.setenv("GIT_EMAIL", "mock_git_email") + + +@pytest.fixture +def temp_storage_dir(): + """Create temporary directory for test storage""" + temp_dir = tempfile.mkdtemp() + yield temp_dir + shutil.rmtree(temp_dir) + + +@pytest.fixture +def dataset_manager(mock_env_vars, temp_storage_dir): + """Create DatasetManager instance for testing""" + return DatasetManager("test_org", temp_storage_dir) + + +@pytest.fixture +def sample_dataframe(): + """Create sample DataFrame for testing""" + return pd.DataFrame({"price": [100.0, 101.0], "volume": [1000, 1100]}) + + +class TestDatasetManagerInit: + def test_initialization(self, dataset_manager): + """Test basic initialization""" + assert dataset_manager.organization == "test_org" + assert dataset_manager.git_token == "mock_git_token" + assert dataset_manager.hf_token == "mock_hf_token" + + def test_directory_creation(self, dataset_manager): + """Test storage directory creation""" + current_day = datetime.now().strftime("%Y-%m-%d") + assert dataset_manager.day_storage.exists() + assert dataset_manager.day_storage.name == current_day + + def test_missing_git_token(self, monkeypatch): + """Test handling of missing Git token""" + monkeypatch.delenv("GIT_TOKEN", raising=False) + with pytest.raises(ValueError, match="GIT_TOKEN environment variable not set"): + DatasetManager("test_org") + + +class TestLocalStorage: + def test_store_local_data(self, dataset_manager, sample_dataframe): + """Test storing data locally""" + timestamp = datetime.now().isoformat() + predictions = {"next_price": 102.0} + hotkey = "test_hotkey" + + success, result = dataset_manager.store_local_data( + timestamp=timestamp, miner_data=sample_dataframe, predictions=predictions, hotkey=hotkey + ) + + assert success + assert "local_path" in result + assert result["rows"] == 2 + assert result["columns"] == 2 + + def test_store_invalid_data(self, dataset_manager): + """Test handling invalid data""" + success, result = dataset_manager.store_local_data( + timestamp="2024-01-01", miner_data=pd.DataFrame(), predictions={}, hotkey="test_hotkey" # Empty DataFrame + ) + + assert not success + assert "error" in result + + @pytest.mark.asyncio + async def test_store_data_async(self, dataset_manager, sample_dataframe): + """Test async storage wrapper""" + success, result = await dataset_manager.store_data_async( + timestamp=datetime.now().isoformat(), + miner_data=sample_dataframe, + predictions={"next_price": 102.0}, + hotkey="test_hotkey", + ) + + assert success + assert "local_path" in result + + +class TestCleanup: + def test_cleanup_local_storage(self, dataset_manager, temp_storage_dir): + """Test storage cleanup""" + # Create some test directories + for i in range(10): + test_dir = Path(temp_storage_dir) / f"2024-01-{i:02d}" + test_dir.mkdir(parents=True) + + dataset_manager.cleanup_local_storage(days_to_keep=5) + + remaining_dirs = list(Path(temp_storage_dir).iterdir()) + assert len(remaining_dirs) == 5 + + +def test_get_current_repo_name(dataset_manager): + """Test repository name generation""" + repo_name = dataset_manager._get_current_repo_name() + expected_name = f"dataset-{datetime.now().strftime('%Y-%m-%d')}" + assert repo_name == expected_name + + +def test_get_local_path(dataset_manager): + """Test local path generation""" + hotkey = "test_hotkey" + path = dataset_manager._get_local_path(hotkey) + assert path.exists() + assert path.name == hotkey From 850561271efc12d3bb53d2321d29fdc7481b634e Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Tue, 17 Dec 2024 13:23:32 -0500 Subject: [PATCH 35/57] ensure that data upload can scale to 200 plus miners --- poetry.lock | 280 ++++++++++++++++++++++++- predictionnet/utils/dataset_manager.py | 123 +++++------ predictionnet/validator/forward.py | 2 +- pyproject.toml | 8 + tests/helpers.py | 161 -------------- tests/test_template_validator.py | 110 ---------- 6 files changed, 340 insertions(+), 344 deletions(-) delete mode 100644 tests/helpers.py delete mode 100644 tests/test_template_validator.py diff --git a/poetry.lock b/poetry.lock index 12bcd95f..2bd06a8a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -601,6 +601,83 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "coverage" +version = "7.6.9" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "coverage-7.6.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb"}, + {file = "coverage-7.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073"}, + {file = "coverage-7.6.9-cp310-cp310-win32.whl", hash = "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198"}, + {file = "coverage-7.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717"}, + {file = "coverage-7.6.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9"}, + {file = "coverage-7.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3"}, + {file = "coverage-7.6.9-cp311-cp311-win32.whl", hash = "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0"}, + {file = "coverage-7.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b"}, + {file = "coverage-7.6.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8"}, + {file = "coverage-7.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f"}, + {file = "coverage-7.6.9-cp312-cp312-win32.whl", hash = "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692"}, + {file = "coverage-7.6.9-cp312-cp312-win_amd64.whl", hash = "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97"}, + {file = "coverage-7.6.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664"}, + {file = "coverage-7.6.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb"}, + {file = "coverage-7.6.9-cp313-cp313-win32.whl", hash = "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba"}, + {file = "coverage-7.6.9-cp313-cp313-win_amd64.whl", hash = "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1"}, + {file = "coverage-7.6.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419"}, + {file = "coverage-7.6.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9"}, + {file = "coverage-7.6.9-cp313-cp313t-win32.whl", hash = "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b"}, + {file = "coverage-7.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611"}, + {file = "coverage-7.6.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902"}, + {file = "coverage-7.6.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678"}, + {file = "coverage-7.6.9-cp39-cp39-win32.whl", hash = "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6"}, + {file = "coverage-7.6.9-cp39-cp39-win_amd64.whl", hash = "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4"}, + {file = "coverage-7.6.9-pp39.pp310-none-any.whl", hash = "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b"}, + {file = "coverage-7.6.9.tar.gz", hash = "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + [[package]] name = "cryptography" version = "42.0.8" @@ -1434,6 +1511,17 @@ files = [ docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] testing = ["pytest (>=3.5,!=3.7.3)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=1.2.3)", "pytest-cov", "pytest-enabler", "pytest-flake8", "pytest-mypy"] +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + [[package]] name = "jinja2" version = "3.1.4" @@ -1591,6 +1679,24 @@ files = [ {file = "libclang-18.1.1.tar.gz", hash = "sha256:a1214966d08d73d971287fc3ead8dfaf82eb07fb197680d8b3859dbbbbf78250"}, ] +[[package]] +name = "loguru" +version = "0.7.3" +description = "Python logging made (stupidly) simple" +optional = false +python-versions = "<4.0,>=3.5" +files = [ + {file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"}, + {file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} +win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} + +[package.extras] +dev = ["Sphinx (==8.1.3)", "build (==1.2.2)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.5.0)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.13.0)", "mypy (==v1.4.1)", "myst-parser (==4.0.0)", "pre-commit (==4.0.1)", "pytest (==6.1.2)", "pytest (==8.3.2)", "pytest-cov (==2.12.1)", "pytest-cov (==5.0.0)", "pytest-cov (==6.0.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.1.0)", "sphinx-rtd-theme (==3.0.2)", "tox (==3.27.1)", "tox (==4.23.2)", "twine (==6.0.1)"] + [[package]] name = "lxml" version = "5.3.0" @@ -2457,6 +2563,21 @@ files = [ {file = "peewee-3.17.8.tar.gz", hash = "sha256:ce1d05db3438830b989a1b9d0d0aa4e7f6134d5f6fd57686eeaa26a3e6485a8c"}, ] +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] name = "pre-commit-hooks" version = "5.0.0" @@ -2932,6 +3053,60 @@ files = [ {file = "py_sr25519_bindings-0.2.1.tar.gz", hash = "sha256:1b96d3dde43adcf86ab427a9fd72b2c6291dca36eb40747df631588c16f01c1a"}, ] +[[package]] +name = "pyarrow" +version = "18.1.0" +description = "Python library for Apache Arrow" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pyarrow-18.1.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e21488d5cfd3d8b500b3238a6c4b075efabc18f0f6d80b29239737ebd69caa6c"}, + {file = "pyarrow-18.1.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:b516dad76f258a702f7ca0250885fc93d1fa5ac13ad51258e39d402bd9e2e1e4"}, + {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f443122c8e31f4c9199cb23dca29ab9427cef990f283f80fe15b8e124bcc49b"}, + {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a03da7f2758645d17b7b4f83c8bffeae5bbb7f974523fe901f36288d2eab71"}, + {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ba17845efe3aa358ec266cf9cc2800fa73038211fb27968bfa88acd09261a470"}, + {file = "pyarrow-18.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:3c35813c11a059056a22a3bef520461310f2f7eea5c8a11ef9de7062a23f8d56"}, + {file = "pyarrow-18.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9736ba3c85129d72aefa21b4f3bd715bc4190fe4426715abfff90481e7d00812"}, + {file = "pyarrow-18.1.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:eaeabf638408de2772ce3d7793b2668d4bb93807deed1725413b70e3156a7854"}, + {file = "pyarrow-18.1.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:3b2e2239339c538f3464308fd345113f886ad031ef8266c6f004d49769bb074c"}, + {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f39a2e0ed32a0970e4e46c262753417a60c43a3246972cfc2d3eb85aedd01b21"}, + {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31e9417ba9c42627574bdbfeada7217ad8a4cbbe45b9d6bdd4b62abbca4c6f6"}, + {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:01c034b576ce0eef554f7c3d8c341714954be9b3f5d5bc7117006b85fcf302fe"}, + {file = "pyarrow-18.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f266a2c0fc31995a06ebd30bcfdb7f615d7278035ec5b1cd71c48d56daaf30b0"}, + {file = "pyarrow-18.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d4f13eee18433f99adefaeb7e01d83b59f73360c231d4782d9ddfaf1c3fbde0a"}, + {file = "pyarrow-18.1.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:9f3a76670b263dc41d0ae877f09124ab96ce10e4e48f3e3e4257273cee61ad0d"}, + {file = "pyarrow-18.1.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:da31fbca07c435be88a0c321402c4e31a2ba61593ec7473630769de8346b54ee"}, + {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:543ad8459bc438efc46d29a759e1079436290bd583141384c6f7a1068ed6f992"}, + {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0743e503c55be0fdb5c08e7d44853da27f19dc854531c0570f9f394ec9671d54"}, + {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d4b3d2a34780645bed6414e22dda55a92e0fcd1b8a637fba86800ad737057e33"}, + {file = "pyarrow-18.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c52f81aa6f6575058d8e2c782bf79d4f9fdc89887f16825ec3a66607a5dd8e30"}, + {file = "pyarrow-18.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ad4892617e1a6c7a551cfc827e072a633eaff758fa09f21c4ee548c30bcaf99"}, + {file = "pyarrow-18.1.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:84e314d22231357d473eabec709d0ba285fa706a72377f9cc8e1cb3c8013813b"}, + {file = "pyarrow-18.1.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:f591704ac05dfd0477bb8f8e0bd4b5dc52c1cadf50503858dce3a15db6e46ff2"}, + {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acb7564204d3c40babf93a05624fc6a8ec1ab1def295c363afc40b0c9e66c191"}, + {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74de649d1d2ccb778f7c3afff6085bd5092aed4c23df9feeb45dd6b16f3811aa"}, + {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f96bd502cb11abb08efea6dab09c003305161cb6c9eafd432e35e76e7fa9b90c"}, + {file = "pyarrow-18.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:36ac22d7782554754a3b50201b607d553a8d71b78cdf03b33c1125be4b52397c"}, + {file = "pyarrow-18.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:25dbacab8c5952df0ca6ca0af28f50d45bd31c1ff6fcf79e2d120b4a65ee7181"}, + {file = "pyarrow-18.1.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a276190309aba7bc9d5bd2933230458b3521a4317acfefe69a354f2fe59f2bc"}, + {file = "pyarrow-18.1.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:ad514dbfcffe30124ce655d72771ae070f30bf850b48bc4d9d3b25993ee0e386"}, + {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aebc13a11ed3032d8dd6e7171eb6e86d40d67a5639d96c35142bd568b9299324"}, + {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6cf5c05f3cee251d80e98726b5c7cc9f21bab9e9783673bac58e6dfab57ecc8"}, + {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:11b676cd410cf162d3f6a70b43fb9e1e40affbc542a1e9ed3681895f2962d3d9"}, + {file = "pyarrow-18.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:b76130d835261b38f14fc41fdfb39ad8d672afb84c447126b84d5472244cfaba"}, + {file = "pyarrow-18.1.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:0b331e477e40f07238adc7ba7469c36b908f07c89b95dd4bd3a0ec84a3d1e21e"}, + {file = "pyarrow-18.1.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:2c4dd0c9010a25ba03e198fe743b1cc03cd33c08190afff371749c52ccbbaf76"}, + {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f97b31b4c4e21ff58c6f330235ff893cc81e23da081b1a4b1c982075e0ed4e9"}, + {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a4813cb8ecf1809871fd2d64a8eff740a1bd3691bbe55f01a3cf6c5ec869754"}, + {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:05a5636ec3eb5cc2a36c6edb534a38ef57b2ab127292a716d00eabb887835f1e"}, + {file = "pyarrow-18.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:73eeed32e724ea3568bb06161cad5fa7751e45bc2228e33dcb10c614044165c7"}, + {file = "pyarrow-18.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:a1880dd6772b685e803011a6b43a230c23b566859a6e0c9a276c1e0faf4f4052"}, + {file = "pyarrow-18.1.0.tar.gz", hash = "sha256:9386d3ca9c145b5539a1cfc75df07757dff870168c959b473a0bccbc3abc8c73"}, +] + +[package.extras] +test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"] + [[package]] name = "pycparser" version = "2.22" @@ -3156,6 +3331,81 @@ cffi = ">=1.4.1" docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] +[[package]] +name = "pytest" +version = "8.3.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.25.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pytest_asyncio-0.25.0-py3-none-any.whl", hash = "sha256:db5432d18eac6b7e28b46dcd9b69921b55c3b1086e85febfe04e70b18d9e81b3"}, + {file = "pytest_asyncio-0.25.0.tar.gz", hash = "sha256:8c0610303c9e0442a5db8604505fc0f545456ba1528824842b37b4a626cbf609"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-cov" +version = "6.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, + {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, +] + +[package.dependencies] +coverage = {version = ">=7.5", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, +] + +[package.dependencies] +pytest = ">=6.2.5" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -3170,6 +3420,20 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "python-levenshtein" version = "0.26.1" @@ -4166,6 +4430,20 @@ files = [ [package.extras] test = ["pytest (>=6.0.0)", "setuptools (>=65)"] +[[package]] +name = "win32-setctime" +version = "1.2.0" +description = "A small Python utility to set file creation time on Windows" +optional = false +python-versions = ">=3.5" +files = [ + {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, + {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, +] + +[package.extras] +dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] + [[package]] name = "wrapt" version = "1.17.0" @@ -4518,4 +4796,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">= 3.9, < 3.12" -content-hash = "38288209b911c23a8aadb82192c05befd224f433ab68fe0e9c20807b2efd0c47" +content-hash = "415a3c829653bbbaf938791bf1dfda129c8a35b7c64ba5ed8dd30e296aff6535" diff --git a/predictionnet/utils/dataset_manager.py b/predictionnet/utils/dataset_manager.py index 75390725..304b4f33 100644 --- a/predictionnet/utils/dataset_manager.py +++ b/predictionnet/utils/dataset_manager.py @@ -117,7 +117,8 @@ def store_local_data( local_path = self._get_local_path(hotkey) # Create filename - filename = f"market_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.parquet" + timestamp_dt = datetime.fromisoformat(timestamp) + filename = f"market_data_{timestamp_dt.strftime('%Y%m%d_%H%M%S')}.parquet" file_path = local_path / filename # Prepare metadata @@ -161,6 +162,7 @@ def _configure_git_repo(self, repo: Repo): def _setup_git_repo(self, repo_name: str) -> Tuple[Repo, Path]: """Set up Git repository for batch upload""" temp_dir = Path(tempfile.mkdtemp()) + bt.logging.info(f"Created temporary directory: {temp_dir}") repo_url = self.git_url_template.format(repo_name=repo_name) repo_id = f"{self.organization}/{repo_name}" @@ -186,83 +188,62 @@ def _setup_git_repo(self, repo_name: str) -> Tuple[Repo, Path]: # Clean up temp dir if anything fails if temp_dir.exists(): shutil.rmtree(temp_dir) + bt.logging.info(f"Deleted temporary directory: {temp_dir}") raise Exception(f"Failed to setup repository: {str(e)}") - async def batch_upload_daily_data(self) -> Tuple[bool, Dict]: - """Upload all locally stored data using Git""" + def batch_upload_daily_data(self) -> Tuple[bool, Dict]: + """Optimized version to upload data using Git LFS (for large files) in chunks based on file number.""" try: + # Set up the Git repo and the temporary directory bt.logging.info("Starting batch upload process...") - repo_name = self._get_current_repo_name() # Use daily repo name + repo_name = self._get_current_repo_name() bt.logging.info(f"Generated repo name: {repo_name}") - # Setup the Git repo (clone or create new) repo, temp_dir = self._setup_git_repo(repo_name) - bt.logging.info(f"Repo setup completed. Temporary directory: {temp_dir}") - - uploaded_files = [] - total_rows = 0 - - try: - # Iterate through hotkey directories in the day storage - for hotkey_dir in self.day_storage.iterdir(): - bt.logging.debug(f"Processing directory: {hotkey_dir}") - if hotkey_dir.is_dir(): - hotkey = hotkey_dir.name - repo_hotkey_dir = temp_dir / hotkey - repo_hotkey_dir.mkdir(exist_ok=True) - bt.logging.debug(f"Created directory for hotkey: {repo_hotkey_dir}") - - # Iterate through Parquet files in the hotkey directory - for file_path in hotkey_dir.glob("*.parquet"): - try: - bt.logging.debug(f"Processing file: {file_path}") - # Copy file to repo hotkey directory - target_path = repo_hotkey_dir / file_path.name - shutil.copy2(file_path, target_path) - bt.logging.debug(f"Copied file to {target_path}") - - # Track the file and calculate the total rows - rel_path = str(target_path.relative_to(temp_dir)) - uploaded_files.append(rel_path) - - # Count rows in the Parquet file - table = pq.read_table(file_path) - total_rows += table.num_rows - bt.logging.debug(f"File has {table.num_rows} rows. Total rows so far: {total_rows}") - - # Stage the file in the Git repository - repo.index.add([rel_path]) - bt.logging.debug(f"Staged file for commit: {rel_path}") - - except Exception as e: - bt.logging.error(f"Error processing {file_path}: {str(e)}") - continue - - # Commit and push if there are changes - if repo.is_dirty() or len(repo.untracked_files) > 0: - bt.logging.info("Changes detected, committing and pushing...") - commit_message = f"Batch upload for {self.current_day}" - repo.index.commit(commit_message) - bt.logging.info(f"Commit created: {commit_message}") - - origin = repo.remote("origin") - bt.logging.info("Pushing changes to remote repository...") - origin.push() - bt.logging.success(f"Successfully pushed {len(uploaded_files)} files to {repo_name}") - else: - bt.logging.info("No changes to upload") - - return True, { - "repo_id": f"{self.organization}/{repo_name}", - "files_uploaded": len(uploaded_files), - "total_rows": total_rows, - "paths": uploaded_files, - } - - finally: - # Clean up temporary directory - shutil.rmtree(temp_dir) - bt.logging.debug(f"Temporary directory cleaned up: {temp_dir}") + bt.logging.info(f"Cloned repository to temporary directory: {temp_dir}") + + # Copy the entire parent folder to the temporary directory + bt.logging.info(f"Copying data from {self.day_storage} to {temp_dir}...") + shutil.copytree(self.day_storage, temp_dir, dirs_exist_ok=True) + bt.logging.info(f"Data successfully copied to {temp_dir}") + + # Track all Parquet files with Git LFS (ensure they are tracked before adding them) + bt.logging.info("Tracking all Parquet files with Git LFS...") + repo.git.lfs("track", "*.parquet") # Ensure LFS tracking is enabled + bt.logging.info("Successfully tracked Parquet files with Git LFS") + + # Collect all files to be added to Git + bt.logging.info("Collecting all Parquet files to be added to Git...") + files_to_commit = [] + for file_path in temp_dir.rglob("*.parquet"): + rel_path = str(file_path.relative_to(temp_dir)) + files_to_commit.append(rel_path) + bt.logging.info(f"Found {len(files_to_commit)} Parquet files to commit") + + # Define the chunk size (number of files per commit) + chunk_size = 1000 + chunks = [files_to_commit[i : i + chunk_size] for i in range(0, len(files_to_commit), chunk_size)] + bt.logging.info(f"Created {len(chunks)} chunks, each containing up to {chunk_size} files") + + # Commit and push in chunks + files_uploaded = 0 + for chunk in chunks: + bt.logging.info( + f"Committing chunk {files_uploaded // chunk_size + 1} of {len(chunks)} with {len(chunk)} files..." + ) + repo.index.add(chunk) + commit_message = f"Batch upload for {self.current_day} (Chunk {files_uploaded // chunk_size + 1})" + repo.index.commit(commit_message) + origin = repo.remote("origin") + origin.push() + files_uploaded += len(chunk) + bt.logging.success(f"Successfully pushed {len(chunk)} files in this chunk to {repo_name}") + + # Return success with the number of files uploaded + return True, { + "repo_id": f"{self.organization}/{repo_name}", + "files_uploaded": files_uploaded, + } except Exception as e: bt.logging.error(f"Error in batch_upload_daily_data: {str(e)}") diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index a917b3e8..62227a41 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -93,7 +93,7 @@ async def handle_market_close(self, dataset_manager: DatasetManager) -> None: dataset_manager.cleanup_local_storage(days_to_keep=7) # Upload today's data - success, result = await dataset_manager.batch_upload_daily_data() + success, result = dataset_manager.batch_upload_daily_data() if success: bt.logging.success( f"Daily batch upload completed. Uploaded {result.get('files_uploaded', 0)} files " diff --git a/pyproject.toml b/pyproject.toml index f67b1900..5f03a482 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,9 +15,17 @@ huggingface_hub = "0.22.2" tensorflow = "2.17.0" wandb = "0.16.6" yfinance = "0.2.37" +loguru = "^0.7.3" +pyarrow = "^18.1.0" +python-dotenv = "^1.0.1" [tool.poetry.group.dev.dependencies] pre-commit-hooks = "5.0.0" +pytest = "^8.3.4" +pytest-asyncio = "^0.25.0" +pytest-cov = "^6.0.0" +pytest-mock = "^3.14.0" +loguru = "^0.7.3" [tool.black] line-length = 120 diff --git a/tests/helpers.py b/tests/helpers.py deleted file mode 100644 index 40f72250..00000000 --- a/tests/helpers.py +++ /dev/null @@ -1,161 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2023 Opentensor Foundation - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - -from typing import Union - -from bittensor import AxonInfo, Balance, NeuronInfo, PrometheusInfo -from bittensor.mock.wallet_mock import MockWallet as _MockWallet -from bittensor.mock.wallet_mock import get_mock_coldkey as _get_mock_coldkey -from bittensor.mock.wallet_mock import get_mock_hotkey as _get_mock_hotkey -from bittensor.mock.wallet_mock import get_mock_wallet as _get_mock_wallet -from rich.console import Console -from rich.text import Text - - -def __mock_wallet_factory__(*args, **kwargs) -> _MockWallet: - """Returns a mock wallet object.""" - - mock_wallet = _get_mock_wallet() - - return mock_wallet - - -class CLOSE_IN_VALUE: - value: Union[float, int, Balance] - tolerance: Union[float, int, Balance] - - def __init__( - self, - value: Union[float, int, Balance], - tolerance: Union[float, int, Balance] = 0.0, - ) -> None: - self.value = value - self.tolerance = tolerance - - def __eq__(self, __o: Union[float, int, Balance]) -> bool: - # True if __o \in [value - tolerance, value + tolerance] - # or if value \in [__o - tolerance, __o + tolerance] - return ((self.value - self.tolerance) <= __o and __o <= (self.value + self.tolerance)) or ( - (__o - self.tolerance) <= self.value and self.value <= (__o + self.tolerance) - ) - - -def get_mock_neuron(**kwargs) -> NeuronInfo: - """ - Returns a mock neuron with the given kwargs overriding the default values. - """ - - mock_neuron_d = dict( - { - "netuid": -1, # mock netuid - "axon_info": AxonInfo( - block=0, - version=1, - ip=0, - port=0, - ip_type=0, - protocol=0, - placeholder1=0, - placeholder2=0, - ), - "prometheus_info": PrometheusInfo(block=0, version=1, ip=0, port=0, ip_type=0), - "validator_permit": True, - "uid": 1, - "hotkey": "some_hotkey", - "coldkey": "some_coldkey", - "active": 0, - "last_update": 0, - "stake": {"some_coldkey": 1e12}, - "total_stake": 1e12, - "rank": 0.0, - "trust": 0.0, - "consensus": 0.0, - "validator_trust": 0.0, - "incentive": 0.0, - "dividends": 0.0, - "emission": 0.0, - "bonds": [], - "weights": [], - "stake_dict": {}, - "pruning_score": 0.0, - "is_null": False, - } - ) - - mock_neuron_d.update(kwargs) # update with kwargs - - if kwargs.get("stake") is None and kwargs.get("coldkey") is not None: - mock_neuron_d["stake"] = {kwargs.get("coldkey"): 1e12} - - if kwargs.get("total_stake") is None: - mock_neuron_d["total_stake"] = sum(mock_neuron_d["stake"].values()) - - mock_neuron = NeuronInfo._neuron_dict_to_namespace(mock_neuron_d) - - return mock_neuron - - -def get_mock_neuron_by_uid(uid: int, **kwargs) -> NeuronInfo: - return get_mock_neuron(uid=uid, hotkey=_get_mock_hotkey(uid), coldkey=_get_mock_coldkey(uid), **kwargs) - - -class MockStatus: - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - pass - - def start(self): - pass - - def stop(self): - pass - - def update(self, *args, **kwargs): - MockConsole().print(*args, **kwargs) - - -class MockConsole: - """ - Mocks the console object for status and print. - Captures the last print output as a string. - """ - - captured_print = None - - def status(self, *args, **kwargs): - return MockStatus() - - def print(self, *args, **kwargs): - console = Console(width=1000, no_color=True, markup=False) # set width to 1000 to avoid truncation - console.begin_capture() - console.print(*args, **kwargs) - self.captured_print = console.end_capture() - - def clear(self, *args, **kwargs): - pass - - @staticmethod - def remove_rich_syntax(text: str) -> str: - """ - Removes rich syntax from the given text. - Removes markup and ansi syntax. - """ - output_no_syntax = Text.from_ansi(Text.from_markup(text).plain).plain - - return output_no_syntax diff --git a/tests/test_template_validator.py b/tests/test_template_validator.py deleted file mode 100644 index 14492e5e..00000000 --- a/tests/test_template_validator.py +++ /dev/null @@ -1,110 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2023 Yuma Rao -# Copyright © 2023 Opentensor Foundation - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - -import sys -import unittest - -import bittensor as bt -from numpy import array -from template.base.validator import BaseValidatorNeuron -from template.protocol import Dummy -from template.utils.uids import get_random_uids -from template.validator.reward import get_rewards - -from neurons.validator import Neuron as Validator - - -class TemplateValidatorNeuronTestCase(unittest.TestCase): - """ - This class contains unit tests for the RewardEvent classes. - - The tests cover different scenarios where completions may or may not be successful and the reward events are checked that they don't contain missing values. - The `reward` attribute of all RewardEvents is expected to be a float, and the `is_filter_model` attribute is expected to be a boolean. - """ - - def setUp(self): - sys.argv = sys.argv[0] + ["--config", "tests/configs/validator.json"] - - config = BaseValidatorNeuron.config() - config.wallet._mock = True - config.metagraph._mock = True - config.subtensor._mock = True - self.neuron = Validator(config) - self.miner_uids = get_random_uids(self, k=10) - - def test_run_single_step(self): - # TODO: Test a single step - pass - - def test_sync_error_if_not_registered(self): - # TODO: Test that the validator throws an error if it is not registered on metagraph - pass - - def test_forward(self): - # TODO: Test that the forward function returns the correct value - pass - - def test_dummy_responses(self): - # TODO: Test that the dummy responses are correctly constructed - - responses = self.neuron.dendrite.query( - # Send the query to miners in the network. - axons=[self.neuron.metagraph.axons[uid] for uid in self.miner_uids], - # Construct a dummy query. - synapse=Dummy(dummy_input=self.neuron.step), - # All responses have the deserialize function called on them before returning. - deserialize=True, - ) - - for i, response in enumerate(responses): - self.assertEqual(response, self.neuron.step * 2) - - def test_reward(self): - # TODO: Test that the reward function returns the correct value - responses = self.dendrite.query( - # Send the query to miners in the network. - axons=[self.metagraph.axons[uid] for uid in self.miner_uids], - # Construct a dummy query. - synapse=Dummy(dummy_input=self.neuron.step), - # All responses have the deserialize function called on them before returning. - deserialize=True, - ) - - rewards = get_rewards(self.neuron, responses) - expected_rewards = array([1.0] * len(responses)) - self.assertEqual(rewards, expected_rewards) - - def test_reward_with_nan(self): - # TODO: Test that NaN rewards are correctly sanitized - # TODO: Test that a bt.logging.warning is thrown when a NaN reward is sanitized - responses = self.dendrite.query( - # Send the query to miners in the network. - axons=[self.metagraph.axons[uid] for uid in self.miner_uids], - # Construct a dummy query. - synapse=Dummy(dummy_input=self.neuron.step), - # All responses have the deserialize function called on them before returning. - deserialize=True, - ) - - rewards = get_rewards(self.neuron, responses) - _ = rewards.clone() # expected rewards - # Add NaN values to rewards - rewards[0] = float("nan") - - with self.assertLogs(bt.logging, level="WARNING") as _: - self.neuron.update_scores(rewards, self.miner_uids) From 7ec96d311fce88cf3ff59256841291437da53d4e Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Tue, 17 Dec 2024 14:34:30 -0500 Subject: [PATCH 36/57] incorporate data decryption into the reward structure --- predictionnet/validator/forward.py | 44 ++++++++++++++++-------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 62227a41..233e7d8c 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -3,12 +3,12 @@ # TODO(developer): Set your name # Copyright © 2023 # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation +# documentation files (the "Software"), to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in all copies or substantial portions of # the Software. -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER @@ -68,7 +68,7 @@ async def process_miner_data(response, timestamp: str, organization: str, hotkey bt.logging.error(f"Failed to decrypt data: {result['error']}") return False - # Attempt to store data in background without affecting reward + # Store data in background without waiting for completion asyncio.create_task( dataset_manager.store_data_async( timestamp=timestamp, @@ -162,34 +162,38 @@ async def forward(self): deserialize=False, ) - # Process responses and initiate background data processing + # Process responses and track decryption success + # Create tasks for all miners and wait for all decryption results + decryption_tasks = [] for uid, response in zip(miner_uids, responses): bt.logging.info(f"UID: {uid} | Predictions: {response.prediction}") - # Check if response has required data fields if can_process_data(response): - # Create background task for data processing - asyncio.create_task( - process_miner_data( - response=response, - timestamp=timestamp, - organization=self.config.neuron.organization, - hotkey=self.metagraph.hotkeys[uid], - uid=uid, - ) + task = process_miner_data( + response=response, + timestamp=timestamp, + organization=self.config.neuron.organization, + hotkey=self.metagraph.hotkeys[uid], + uid=uid, ) + decryption_tasks.append(task) + else: + decryption_tasks.append(asyncio.create_task(asyncio.sleep(0))) # Dummy task that returns immediately - # Calculate rewards + # Wait for all decryption results + decryption_success = await asyncio.gather(*decryption_tasks) + + # Calculate initial rewards rewards = get_rewards(self, responses=responses, miner_uids=miner_uids) + # Zero out rewards for failed decryption + rewards = [reward if success else 0 for reward, success in zip(rewards, decryption_success)] + # Log results to wandb wandb_val_log = { "miners_info": { - miner_uid: { - "miner_response": response.prediction, - "miner_reward": reward, - } - for miner_uid, response, reward in zip(miner_uids, responses, rewards.tolist()) + miner_uid: {"miner_response": response.prediction, "miner_reward": reward, "decryption_success": success} + for miner_uid, response, reward, success in zip(miner_uids, responses, rewards.tolist(), decryption_success) } } wandb.log(wandb_val_log) From 17f417ded5aec5e13a8907b843b35cd5de61d1f6 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Tue, 17 Dec 2024 14:36:19 -0500 Subject: [PATCH 37/57] silly mistake --- predictionnet/validator/forward.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 233e7d8c..67874bc0 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -193,7 +193,7 @@ async def forward(self): wandb_val_log = { "miners_info": { miner_uid: {"miner_response": response.prediction, "miner_reward": reward, "decryption_success": success} - for miner_uid, response, reward, success in zip(miner_uids, responses, rewards.tolist(), decryption_success) + for miner_uid, response, reward, success in zip(miner_uids, responses, rewards, decryption_success) } } wandb.log(wandb_val_log) From 9f432697f1eff07fb4655c9702fb0a930d98f4d7 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Tue, 17 Dec 2024 14:39:55 -0500 Subject: [PATCH 38/57] improve logging --- predictionnet/validator/forward.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/predictionnet/validator/forward.py b/predictionnet/validator/forward.py index 67874bc0..152fa85a 100644 --- a/predictionnet/validator/forward.py +++ b/predictionnet/validator/forward.py @@ -67,6 +67,8 @@ async def process_miner_data(response, timestamp: str, organization: str, hotkey if not success: bt.logging.error(f"Failed to decrypt data: {result['error']}") return False + else: + bt.logging.success(f"Successfully decrypted data for UID {uid} from {data_path}") # Store data in background without waiting for completion asyncio.create_task( From bd3f29af72605544c61dd3c16fee32cde597b707 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Wed, 18 Dec 2024 14:52:54 -0500 Subject: [PATCH 39/57] Update env template --- .env.template | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.env.template b/.env.template index 5c361f7b..7bf9ea67 100644 --- a/.env.template +++ b/.env.template @@ -1,4 +1,10 @@ # Copy the contents on this file to a new file called .env +WANDB_API_KEY='REPLACE_WITH_WANDB_API_KEY' +MINER_HF_ACCESS_TOKEN='REPLACE_WITH_HUGGINGFACE_ACCESS_KEY' +HF_ACCESS_TOKEN='REPLACE_WITH_HUGGINGFACE_ACCESS_KEY' -HF_ACCESS_TOKEN = 'REPLACE_WITH_HUGGINGFACE_ACCESS_KEY' -WANDB_API_KEY = 'REPLACE_WITH_WANDB_API_KEY' +# Git credentials +GIT_TOKEN='REPLACE_WITH_GIT_TOKEN' +GIT_USERNAME='REPLACE_WITH_GIT_USERNAME' +GIT_NAME="REPLACE_WITH_GIT_NAME" +GIT_EMAIL="REPLACE_WITH_GIT_EMAIL" From afbb601de26b46023d36eedd6f8558939211e62d Mon Sep 17 00:00:00 2001 From: Matthew Trudeau Date: Wed, 18 Dec 2024 15:42:45 -0500 Subject: [PATCH 40/57] Deleted unused shell scripts --- scripts/check_compatibility.sh | 76 -------------- scripts/check_requirements_changes.sh | 10 -- scripts/install_staging.sh | 145 -------------------------- 3 files changed, 231 deletions(-) delete mode 100755 scripts/check_compatibility.sh delete mode 100755 scripts/check_requirements_changes.sh delete mode 100644 scripts/install_staging.sh diff --git a/scripts/check_compatibility.sh b/scripts/check_compatibility.sh deleted file mode 100755 index b0bd6b43..00000000 --- a/scripts/check_compatibility.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/bash - -if [ -z "$1" ]; then - echo "Please provide a Python version as an argument." - exit 1 -fi - -python_version="$1" -all_passed=true - -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -RED='\033[0;31m' -NC='\033[0m' # No Color - -check_compatibility() { - all_supported=0 - - while read -r requirement; do - # Skip lines starting with git+ - if [[ "$requirement" == git+* ]]; then - continue - fi - - package_name=$(echo "$requirement" | awk -F'[!=<>]' '{print $1}' | awk -F'[' '{print $1}') # Strip off brackets - echo -n "Checking $package_name... " - - url="https://pypi.org/pypi/$package_name/json" - response=$(curl -s $url) - status_code=$(curl -s -o /dev/null -w "%{http_code}" $url) - - if [ "$status_code" != "200" ]; then - echo -e "${RED}Information not available for $package_name. Failure.${NC}" - all_supported=1 - continue - fi - - classifiers=$(echo "$response" | jq -r '.info.classifiers[]') - requires_python=$(echo "$response" | jq -r '.info.requires_python') - - base_version="Programming Language :: Python :: ${python_version%%.*}" - specific_version="Programming Language :: Python :: $python_version" - - if echo "$classifiers" | grep -q "$specific_version" || echo "$classifiers" | grep -q "$base_version"; then - echo -e "${GREEN}Supported${NC}" - elif [ "$requires_python" != "null" ]; then - if echo "$requires_python" | grep -Eq "==$python_version|>=$python_version|<=$python_version"; then - echo -e "${GREEN}Supported${NC}" - else - echo -e "${RED}Not compatible with Python $python_version due to constraint $requires_python.${NC}" - all_supported=1 - fi - else - echo -e "${YELLOW}Warning: Specific version not listed, assuming compatibility${NC}" - fi - done < requirements.txt - - return $all_supported -} - -echo "Checking compatibility for Python $python_version..." -check_compatibility -if [ $? -eq 0 ]; then - echo -e "${GREEN}All requirements are compatible with Python $python_version.${NC}" -else - echo -e "${RED}All requirements are NOT compatible with Python $python_version.${NC}" - all_passed=false -fi - -echo "" -if $all_passed; then - echo -e "${GREEN}All tests passed.${NC}" -else - echo -e "${RED}All tests did not pass.${NC}" - exit 1 -fi diff --git a/scripts/check_requirements_changes.sh b/scripts/check_requirements_changes.sh deleted file mode 100755 index a06d050f..00000000 --- a/scripts/check_requirements_changes.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Check if requirements files have changed in the last commit -if git diff --name-only HEAD~1 | grep -E 'requirements.txt|requirements.txt'; then - echo "Requirements files have changed. Running compatibility checks..." - echo 'export REQUIREMENTS_CHANGED="true"' >> $BASH_ENV -else - echo "Requirements files have not changed. Skipping compatibility checks..." - echo 'export REQUIREMENTS_CHANGED="false"' >> $BASH_ENV -fi diff --git a/scripts/install_staging.sh b/scripts/install_staging.sh deleted file mode 100644 index 0805d512..00000000 --- a/scripts/install_staging.sh +++ /dev/null @@ -1,145 +0,0 @@ -#!/bin/bash - -# Section 1: Build/Install -# This section is for first-time setup and installations. - -install_dependencies() { - # Function to install packages on macOS - install_mac() { - which brew > /dev/null - if [ $? -ne 0 ]; then - echo "Installing Homebrew..." - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - fi - echo "Updating Homebrew packages..." - brew update - echo "Installing required packages..." - brew install make llvm curl libssl protobuf tmux - } - - # Function to install packages on Ubuntu/Debian - install_ubuntu() { - echo "Updating system packages..." - sudo apt update - echo "Installing required packages..." - sudo apt install --assume-yes make build-essential git clang curl libssl-dev llvm libudev-dev protobuf-compiler tmux - } - - # Detect OS and call the appropriate function - if [[ "$OSTYPE" == "darwin"* ]]; then - install_mac - elif [[ "$OSTYPE" == "linux-gnu"* ]]; then - install_ubuntu - else - echo "Unsupported operating system." - exit 1 - fi - - # Install rust and cargo - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - - # Update your shell's source to include Cargo's path - source "$HOME/.cargo/env" -} - -# Call install_dependencies only if it's the first time running the script -if [ ! -f ".dependencies_installed" ]; then - install_dependencies - touch .dependencies_installed -fi - - -# Section 2: Test/Run -# This section is for running and testing the setup. - -# Create a coldkey for the owner role -wallet=${1:-owner} - -# Logic for setting up and running the environment -setup_environment() { - # Clone subtensor and enter the directory - if [ ! -d "subtensor" ]; then - git clone https://github.com/opentensor/subtensor.git - fi - cd subtensor - git pull - - # Update to the nightly version of rust - ./scripts/init.sh - - cd ../bittensor-subnet-template - - # Install the bittensor-subnet-template python package - python -m pip install -e . - - # Create and set up wallets - # This section can be skipped if wallets are already set up - if [ ! -f ".wallets_setup" ]; then - btcli wallet new_coldkey --wallet.name $wallet --no_password --no_prompt - btcli wallet new_coldkey --wallet.name miner --no_password --no_prompt - btcli wallet new_hotkey --wallet.name miner --wallet.hotkey default --no_prompt - btcli wallet new_coldkey --wallet.name validator --no_password --no_prompt - btcli wallet new_hotkey --wallet.name validator --wallet.hotkey default --no_prompt - touch .wallets_setup - fi - -} - -# Call setup_environment every time -setup_environment - -## Setup localnet -# assumes we are in the bittensor-subnet-template/ directory -# Initialize your local subtensor chain in development mode. This command will set up and run a local subtensor network. -cd ../subtensor - -# Start a new tmux session and create a new pane, but do not switch to it -echo "FEATURES='pow-faucet runtime-benchmarks' BT_DEFAULT_TOKEN_WALLET=$(cat ~/.bittensor/wallets/$wallet/coldkeypub.txt | grep -oP '"ss58Address": "\K[^"]+') bash scripts/localnet.sh" >> setup_and_run.sh -chmod +x setup_and_run.sh -tmux new-session -d -s localnet -n 'localnet' -tmux send-keys -t localnet 'bash ../subtensor/setup_and_run.sh' C-m - -# Notify the user -echo ">> localnet.sh is running in a detached tmux session named 'localnet'" -echo ">> You can attach to this session with: tmux attach-session -t localnet" - -# Register a subnet (this needs to be run each time we start a new local chain) -btcli subnet create --wallet.name $wallet --wallet.hotkey default --subtensor.chain_endpoint ws://127.0.0.1:9946 --no_prompt - -# Transfer tokens to miner and validator coldkeys -export BT_MINER_TOKEN_WALLET=$(cat ~/.bittensor/wallets/miner/coldkeypub.txt | grep -oP '"ss58Address": "\K[^"]+') -export BT_VALIDATOR_TOKEN_WALLET=$(cat ~/.bittensor/wallets/validator/coldkeypub.txt | grep -oP '"ss58Address": "\K[^"]+') - -btcli wallet transfer --subtensor.network ws://127.0.0.1:9946 --wallet.name $wallet --dest $BT_MINER_TOKEN_WALLET --amount 1000 --no_prompt -btcli wallet transfer --subtensor.network ws://127.0.0.1:9946 --wallet.name $wallet --dest $BT_VALIDATOR_TOKEN_WALLET --amount 10000 --no_prompt - -# Register wallet hotkeys to subnet -btcli subnet register --wallet.name miner --netuid 1 --wallet.hotkey default --subtensor.chain_endpoint ws://127.0.0.1:9946 --no_prompt -btcli subnet register --wallet.name validator --netuid 1 --wallet.hotkey default --subtensor.chain_endpoint ws://127.0.0.1:9946 --no_prompt - -# Add stake to the validator -btcli stake add --wallet.name validator --wallet.hotkey default --subtensor.chain_endpoint ws://127.0.0.1:9946 --amount 10000 --no_prompt - -# Ensure both the miner and validator keys are successfully registered. -btcli subnet list --subtensor.chain_endpoint ws://127.0.0.1:9946 -btcli wallet overview --wallet.name validator --subtensor.chain_endpoint ws://127.0.0.1:9946 --no_prompt -btcli wallet overview --wallet.name miner --subtensor.chain_endpoint ws://127.0.0.1:9946 --no_prompt - -cd ../bittensor-subnet-template - - -# Check if inside a tmux session -if [ -z "$TMUX" ]; then - # Start a new tmux session and run the miner in the first pane - tmux new-session -d -s bittensor -n 'miner' 'python neurons/miner.py --netuid 1 --subtensor.chain_endpoint ws://127.0.0.1:9946 --wallet.name miner --wallet.hotkey default --logging.debug' - - # Split the window and run the validator in the new pane - tmux split-window -h -t bittensor:miner 'python neurons/validator.py --netuid 1 --subtensor.chain_endpoint ws://127.0.0.1:9946 --wallet.name validator --wallet.hotkey default --logging.debug' - - # Attach to the new tmux session - tmux attach-session -t bittensor -else - # If already in a tmux session, create two panes in the current window - tmux split-window -h 'python neurons/miner.py --netuid 1 --subtensor.chain_endpoint ws://127.0.0.1:9946 --wallet.name miner --wallet.hotkey default --logging.debug' - tmux split-window -v -t 0 'python neurons/validator.py --netuid 1 --subtensor.chain_endpoint ws://127.0.0.1:9946 --wallet.name3 validator --wallet.hotkey default --logging.debug' -fi From 1ba4c9e540ee2106423a398787ac5a58bbf42899 Mon Sep 17 00:00:00 2001 From: Matthew Trudeau Date: Wed, 18 Dec 2024 16:07:01 -0500 Subject: [PATCH 41/57] Move all python files into a top level package folder --- predictionnet/.DS_Store | Bin 6148 -> 0 bytes predictionnet/__init__.py | 30 ------------ predictionnet/api/prediction.py | 45 ------------------ predictionnet/utils/__init__.py | 1 - predictionnet/validator/__init__.py | 2 - snp_oracle/__init__.py | 5 ++ .../base_miner}/__init__.py | 0 .../base_miner}/get_data.py | 4 -- .../base_miner}/model.py | 4 -- .../base_miner}/predict.py | 9 +--- {neurons => snp_oracle/neurons}/__init__.py | 0 {neurons => snp_oracle/neurons}/miner.py | 34 ++----------- {neurons => snp_oracle/neurons}/validator.py | 30 ++---------- snp_oracle/predictionnet/__init__.py | 5 ++ .../predictionnet}/api/__init__.py | 20 -------- .../predictionnet}/api/example.py | 0 .../predictionnet}/api/get_query_axons.py | 19 -------- .../predictionnet}/api/metagraph.py | 0 snp_oracle/predictionnet/api/prediction.py | 26 ++++++++++ .../predictionnet}/base/__init__.py | 0 .../predictionnet}/base/miner.py | 21 +------- .../predictionnet}/base/neuron.py | 24 ++-------- .../predictionnet}/base/validator.py | 22 +-------- .../predictionnet}/protocol.py | 38 --------------- snp_oracle/predictionnet/utils/__init__.py | 1 + .../predictionnet}/utils/config.py | 18 ------- .../predictionnet}/utils/huggingface.py | 2 +- .../predictionnet}/utils/miner_hf.py | 0 .../predictionnet}/utils/misc.py | 18 ------- .../predictionnet}/utils/uids.py | 0 .../predictionnet/validator/__init__.py | 2 + .../predictionnet}/validator/forward.py | 22 ++------- .../predictionnet}/validator/reward.py | 21 +------- 33 files changed, 62 insertions(+), 361 deletions(-) delete mode 100644 predictionnet/.DS_Store delete mode 100644 predictionnet/__init__.py delete mode 100644 predictionnet/api/prediction.py delete mode 100644 predictionnet/utils/__init__.py delete mode 100644 predictionnet/validator/__init__.py create mode 100644 snp_oracle/__init__.py rename {base_miner => snp_oracle/base_miner}/__init__.py (100%) rename {base_miner => snp_oracle/base_miner}/get_data.py (98%) rename {base_miner => snp_oracle/base_miner}/model.py (97%) rename {base_miner => snp_oracle/base_miner}/predict.py (92%) rename {neurons => snp_oracle/neurons}/__init__.py (100%) rename {neurons => snp_oracle/neurons}/miner.py (87%) rename {neurons => snp_oracle/neurons}/validator.py (79%) create mode 100644 snp_oracle/predictionnet/__init__.py rename {predictionnet => snp_oracle/predictionnet}/api/__init__.py (62%) rename {predictionnet => snp_oracle/predictionnet}/api/example.py (100%) rename {predictionnet => snp_oracle/predictionnet}/api/get_query_axons.py (76%) rename {predictionnet => snp_oracle/predictionnet}/api/metagraph.py (100%) create mode 100644 snp_oracle/predictionnet/api/prediction.py rename {predictionnet => snp_oracle/predictionnet}/base/__init__.py (100%) rename {predictionnet => snp_oracle/predictionnet}/base/miner.py (89%) rename {predictionnet => snp_oracle/predictionnet}/base/neuron.py (79%) rename {predictionnet => snp_oracle/predictionnet}/base/validator.py (91%) rename {predictionnet => snp_oracle/predictionnet}/protocol.py (52%) create mode 100644 snp_oracle/predictionnet/utils/__init__.py rename {predictionnet => snp_oracle/predictionnet}/utils/config.py (83%) rename {predictionnet => snp_oracle/predictionnet}/utils/huggingface.py (97%) rename {predictionnet => snp_oracle/predictionnet}/utils/miner_hf.py (100%) rename {predictionnet => snp_oracle/predictionnet}/utils/misc.py (76%) rename {predictionnet => snp_oracle/predictionnet}/utils/uids.py (100%) create mode 100644 snp_oracle/predictionnet/validator/__init__.py rename {predictionnet => snp_oracle/predictionnet}/validator/forward.py (75%) rename {predictionnet => snp_oracle/predictionnet}/validator/reward.py (91%) diff --git a/predictionnet/.DS_Store b/predictionnet/.DS_Store deleted file mode 100644 index ac4e31186004cd36cf452ecc45cf5ae10189c1b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK&uiN-6n;wF)oB>yut6^aL$9UFnlKoUn& zj;6!Ti;pU=VwsN$ogmW@LcV>Gd8(!ZHObRb=SHT%Z~3iZXL~m5_j&oth)sb!O0k^L;5T0E z8&xMymI3>v`|T&ofSb5uz%lR;1H3;3P{z<=ZBTC=DD)Ko=)tW8HvcSePGB*#SQ|tO zM3_{dNfq{rAxt{_fsG3-)&@;F348evc4T31C_;~p{sSFOA~fhq$ADvCnSrLMcKH1N z^zi+EImy)=1CD|JiUHvrL - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - -# TODO(developer): Change this value when updating your code base. -# Define the version of the template module. - -import json - -# Import all submodules. -from . import base, protocol, validator - -__version__ = "2.2.1" -version_split = __version__.split(".") -__spec_version__ = (1000 * int(version_split[0])) + (10 * int(version_split[1])) + (1 * int(version_split[2])) diff --git a/predictionnet/api/prediction.py b/predictionnet/api/prediction.py deleted file mode 100644 index 369dee39..00000000 --- a/predictionnet/api/prediction.py +++ /dev/null @@ -1,45 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2021 Yuma Rao -# Copyright © 2023 Opentensor Foundation -# Copyright © 2023 Opentensor Technologies Inc - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - -from typing import Any, List, Union - -import bittensor as bt - -from predictionnet.api import SubnetsAPI -from predictionnet.protocol import Challenge - - -class PredictionAPI(SubnetsAPI): - def __init__(self, wallet: "bt.wallet"): - super().__init__(wallet) - self.netuid = 28 - self.name = "prediction" - - def prepare_synapse(self, timestamp: str) -> Challenge: - synapse = Challenge(timestamp=timestamp) - return synapse - - def process_responses(self, responses: List[Union["bt.Synapse", Any]]) -> List[int]: - outputs = [] - for response in responses: - if response.dendrite.status_code != 200: - outputs.append(None) - else: - outputs.append(response.prediction) - return outputs diff --git a/predictionnet/utils/__init__.py b/predictionnet/utils/__init__.py deleted file mode 100644 index 2f569c74..00000000 --- a/predictionnet/utils/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import config, misc, uids diff --git a/predictionnet/validator/__init__.py b/predictionnet/validator/__init__.py deleted file mode 100644 index 0b7ddf1a..00000000 --- a/predictionnet/validator/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .forward import forward -from .reward import get_rewards diff --git a/snp_oracle/__init__.py b/snp_oracle/__init__.py new file mode 100644 index 00000000..93623516 --- /dev/null +++ b/snp_oracle/__init__.py @@ -0,0 +1,5 @@ +import importlib.metadata + +__version__ = importlib.metadata.version(__name__ or __package__) +version_split = __version__.split(".") +__spec_version__ = (1000 * int(version_split[0])) + (10 * int(version_split[1])) + (1 * int(version_split[2])) \ No newline at end of file diff --git a/base_miner/__init__.py b/snp_oracle/base_miner/__init__.py similarity index 100% rename from base_miner/__init__.py rename to snp_oracle/base_miner/__init__.py diff --git a/base_miner/get_data.py b/snp_oracle/base_miner/get_data.py similarity index 98% rename from base_miner/get_data.py rename to snp_oracle/base_miner/get_data.py index cfb46176..76b5d1db 100644 --- a/base_miner/get_data.py +++ b/snp_oracle/base_miner/get_data.py @@ -1,7 +1,3 @@ -# developer: Foundry Digital -# Copyright © 2023 Foundry Digital - -# Import required models from datetime import datetime, timedelta from typing import Tuple diff --git a/base_miner/model.py b/snp_oracle/base_miner/model.py similarity index 97% rename from base_miner/model.py rename to snp_oracle/base_miner/model.py index f92b33b7..0483fa1f 100644 --- a/base_miner/model.py +++ b/snp_oracle/base_miner/model.py @@ -1,9 +1,5 @@ -# developer: Foundry Digital -# Copyright © 2023 Foundry Digital - import os -# Import necessary modules to use for model creation - can be downloaded using pip import joblib import numpy as np from dotenv import load_dotenv diff --git a/base_miner/predict.py b/snp_oracle/base_miner/predict.py similarity index 92% rename from base_miner/predict.py rename to snp_oracle/base_miner/predict.py index 816c2b6f..049443d4 100644 --- a/base_miner/predict.py +++ b/snp_oracle/base_miner/predict.py @@ -1,7 +1,3 @@ -# developer: Foundry Digital -# Copyright © 2023 Foundry Digital - -# Import modules that already exist or can be installed using pip from datetime import datetime import numpy as np @@ -10,9 +6,8 @@ from sklearn.preprocessing import MinMaxScaler from tensorflow.keras.models import load_model -# import custom defined files -from base_miner.get_data import prep_data, round_down_time, scale_data -from base_miner.model import create_and_save_base_model_lstm +from snp_oracle.base_miner.get_data import prep_data, round_down_time, scale_data +from snp_oracle.base_miner.model import create_and_save_base_model_lstm def predict(timestamp: datetime, scaler: MinMaxScaler, model, type) -> float: diff --git a/neurons/__init__.py b/snp_oracle/neurons/__init__.py similarity index 100% rename from neurons/__init__.py rename to snp_oracle/neurons/__init__.py diff --git a/neurons/miner.py b/snp_oracle/neurons/miner.py similarity index 87% rename from neurons/miner.py rename to snp_oracle/neurons/miner.py index 35aae185..08d35625 100644 --- a/neurons/miner.py +++ b/snp_oracle/neurons/miner.py @@ -1,44 +1,20 @@ -# The MIT License (MIT) -# Copyright © 2023 Yuma Rao -# developer: Foundry Digital -# Copyright © 2023 Foundry Digital - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - import os import time import typing import bittensor as bt -# ML imports from dotenv import load_dotenv from huggingface_hub import hf_hub_download from tensorflow.keras.models import load_model -# import predictionnet -# Bittensor Miner Template: -import predictionnet -from base_miner.get_data import prep_data, scale_data -from base_miner.predict import predict +import snp_oracle.predictionnet +from snp_oracle.base_miner.get_data import prep_data, scale_data +from snp_oracle.base_miner.predict import predict -# import base miner class which takes care of most of the boilerplate -from predictionnet.base.miner import BaseMinerNeuron +from snp_oracle.predictionnet.base.miner import BaseMinerNeuron -# import huggingface upload class -from predictionnet.utils.miner_hf import MinerHfInterface +from snp_oracle.predictionnet.utils.miner_hf import MinerHfInterface load_dotenv() diff --git a/neurons/validator.py b/snp_oracle/neurons/validator.py similarity index 79% rename from neurons/validator.py rename to snp_oracle/neurons/validator.py index a10eca46..8494e77b 100644 --- a/neurons/validator.py +++ b/snp_oracle/neurons/validator.py @@ -1,27 +1,9 @@ -# The MIT License (MIT) - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - - import os import pathlib import time from datetime import datetime from typing import List -# Bittensor import bittensor as bt import pandas_market_calendars as mcal import pytz @@ -29,15 +11,13 @@ from dotenv import load_dotenv from numpy import full, nan -from predictionnet import __version__ +from snp_oracle import __version__ -# import base validator class which takes care of most of the boilerplate -from predictionnet.base.validator import BaseValidatorNeuron -from predictionnet.utils.huggingface import HfInterface -from predictionnet.utils.uids import check_uid_availability +from snp_oracle.predictionnet.base.validator import BaseValidatorNeuron +from snp_oracle.predictionnet.utils.huggingface import HfInterface +from snp_oracle.predictionnet.utils.uids import check_uid_availability -# Bittensor Validator Template: -from predictionnet.validator import forward +from snp_oracle.predictionnet.validator import forward load_dotenv() diff --git a/snp_oracle/predictionnet/__init__.py b/snp_oracle/predictionnet/__init__.py new file mode 100644 index 00000000..12830a0e --- /dev/null +++ b/snp_oracle/predictionnet/__init__.py @@ -0,0 +1,5 @@ +import json + +# Import all submodules. +from snp_oracle.predictionnet import base, protocol, validator + diff --git a/predictionnet/api/__init__.py b/snp_oracle/predictionnet/api/__init__.py similarity index 62% rename from predictionnet/api/__init__.py rename to snp_oracle/predictionnet/api/__init__.py index 98479af7..57c93b36 100644 --- a/predictionnet/api/__init__.py +++ b/snp_oracle/predictionnet/api/__init__.py @@ -1,23 +1,3 @@ -# The MIT License (MIT) -# Copyright © 2021 Yuma Rao -# Copyright © 2023 Opentensor Foundation -# Copyright © 2023 Opentensor Technologies Inc -# Copyright © 2024 Philanthrope - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - from abc import ABC, abstractmethod from typing import Any, List, Optional, Union diff --git a/predictionnet/api/example.py b/snp_oracle/predictionnet/api/example.py similarity index 100% rename from predictionnet/api/example.py rename to snp_oracle/predictionnet/api/example.py diff --git a/predictionnet/api/get_query_axons.py b/snp_oracle/predictionnet/api/get_query_axons.py similarity index 76% rename from predictionnet/api/get_query_axons.py rename to snp_oracle/predictionnet/api/get_query_axons.py index aa02150d..73411054 100644 --- a/predictionnet/api/get_query_axons.py +++ b/snp_oracle/predictionnet/api/get_query_axons.py @@ -1,22 +1,3 @@ -# The MIT License (MIT) -# Copyright © 2021 Yuma Rao -# Copyright © 2023 Opentensor Foundation -# Copyright © 2023 Opentensor Technologies Inc - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - import random import bittensor as bt diff --git a/predictionnet/api/metagraph.py b/snp_oracle/predictionnet/api/metagraph.py similarity index 100% rename from predictionnet/api/metagraph.py rename to snp_oracle/predictionnet/api/metagraph.py diff --git a/snp_oracle/predictionnet/api/prediction.py b/snp_oracle/predictionnet/api/prediction.py new file mode 100644 index 00000000..8f5402d1 --- /dev/null +++ b/snp_oracle/predictionnet/api/prediction.py @@ -0,0 +1,26 @@ +from typing import Any, List, Union + +import bittensor as bt + +from snp_oracle.predictionnet.api import SubnetsAPI +from snp_oracle.predictionnet.protocol import Challenge + + +class PredictionAPI(SubnetsAPI): + def __init__(self, wallet: "bt.wallet"): + super().__init__(wallet) + self.netuid = 28 + self.name = "prediction" + + def prepare_synapse(self, timestamp: str) -> Challenge: + synapse = Challenge(timestamp=timestamp) + return synapse + + def process_responses(self, responses: List[Union["bt.Synapse", Any]]) -> List[int]: + outputs = [] + for response in responses: + if response.dendrite.status_code != 200: + outputs.append(None) + else: + outputs.append(response.prediction) + return outputs diff --git a/predictionnet/base/__init__.py b/snp_oracle/predictionnet/base/__init__.py similarity index 100% rename from predictionnet/base/__init__.py rename to snp_oracle/predictionnet/base/__init__.py diff --git a/predictionnet/base/miner.py b/snp_oracle/predictionnet/base/miner.py similarity index 89% rename from predictionnet/base/miner.py rename to snp_oracle/predictionnet/base/miner.py index b467b6a5..ea506b33 100644 --- a/predictionnet/base/miner.py +++ b/snp_oracle/predictionnet/base/miner.py @@ -1,20 +1,3 @@ -# The MIT License (MIT) -# Copyright © 2023 Yuma Rao - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - import asyncio import threading import time @@ -24,8 +7,8 @@ from bittensor.constants import V_7_2_0 from substrateinterface import Keypair -from predictionnet.base.neuron import BaseNeuron -from predictionnet.protocol import Challenge +from snp_oracle.predictionnet.base.neuron import BaseNeuron +from snp_oracle.predictionnet.protocol import Challenge class BaseMinerNeuron(BaseNeuron): diff --git a/predictionnet/base/neuron.py b/snp_oracle/predictionnet/base/neuron.py similarity index 79% rename from predictionnet/base/neuron.py rename to snp_oracle/predictionnet/base/neuron.py index a5294469..607f6872 100644 --- a/predictionnet/base/neuron.py +++ b/snp_oracle/predictionnet/base/neuron.py @@ -1,30 +1,12 @@ -# The MIT License (MIT) -# Copyright © 2023 Yuma Rao - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - import copy from abc import ABC, abstractmethod import bittensor as bt -from predictionnet import __spec_version__ as spec_version +from snp_oracle import __spec_version__ as spec_version -# Sync calls set weights and also resyncs the metagraph. -from predictionnet.utils.config import add_args, check_config, config -from predictionnet.utils.misc import ttl_get_block +from snp_oracle.predictionnet.utils.config import add_args, check_config, config +from snp_oracle.predictionnet.utils.misc import ttl_get_block class BaseNeuron(ABC): diff --git a/predictionnet/base/validator.py b/snp_oracle/predictionnet/base/validator.py similarity index 91% rename from predictionnet/base/validator.py rename to snp_oracle/predictionnet/base/validator.py index 8fb7fedb..f9c6dd5c 100644 --- a/predictionnet/base/validator.py +++ b/snp_oracle/predictionnet/base/validator.py @@ -1,23 +1,3 @@ -# The MIT License (MIT) -# Copyright © 2023 Yuma Rao -# TODO(developer): Set your name -# Copyright © 2023 - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - - import asyncio import copy import os @@ -29,7 +9,7 @@ import bittensor as bt from numpy import array, full, isnan, nan, nan_to_num, ndarray -from predictionnet.base.neuron import BaseNeuron +from snp_oracle.predictionnet.base.neuron import BaseNeuron class BaseValidatorNeuron(BaseNeuron): diff --git a/predictionnet/protocol.py b/snp_oracle/predictionnet/protocol.py similarity index 52% rename from predictionnet/protocol.py rename to snp_oracle/predictionnet/protocol.py index 652460f6..62f8fbf8 100644 --- a/predictionnet/protocol.py +++ b/snp_oracle/predictionnet/protocol.py @@ -1,46 +1,8 @@ -# The MIT License (MIT) -# Copyright © 2023 Yuma Rao -# TODO(developer): Set your name -# Copyright © 2023 - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - from typing import List, Optional import bittensor as bt import pydantic -# TODO(developer): Rewrite with your protocol definition. - -# This is the protocol for the dummy miner and validator. -# It is a simple request-response protocol where the validator sends a request -# to the miner, and the miner responds with a dummy response. - -# ---- miner ---- -# Example usage: -# def dummy( synapse: Dummy ) -> Dummy: -# synapse.dummy_output = synapse.dummy_input + 1 -# return synapse -# axon = bt.axon().attach( dummy ).serve(netuid=...).start() - -# ---- validator --- -# Example usage: -# dendrite = bt.dendrite() -# dummy_output = dendrite.query( Dummy( dummy_input = 1 ) ) -# assert dummy_output == 2 - class Challenge(bt.Synapse): """ diff --git a/snp_oracle/predictionnet/utils/__init__.py b/snp_oracle/predictionnet/utils/__init__.py new file mode 100644 index 00000000..4fee521a --- /dev/null +++ b/snp_oracle/predictionnet/utils/__init__.py @@ -0,0 +1 @@ +from snp_oracle.predictionnet.utils import config, misc, uids diff --git a/predictionnet/utils/config.py b/snp_oracle/predictionnet/utils/config.py similarity index 83% rename from predictionnet/utils/config.py rename to snp_oracle/predictionnet/utils/config.py index d814d295..b3f97d00 100644 --- a/predictionnet/utils/config.py +++ b/snp_oracle/predictionnet/utils/config.py @@ -1,21 +1,3 @@ -# The MIT License (MIT) -# Copyright © 2023 Yuma Rao -# Copyright © 2023 Opentensor Foundation - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - import argparse import os diff --git a/predictionnet/utils/huggingface.py b/snp_oracle/predictionnet/utils/huggingface.py similarity index 97% rename from predictionnet/utils/huggingface.py rename to snp_oracle/predictionnet/utils/huggingface.py index aedc1bc8..d4df5607 100644 --- a/predictionnet/utils/huggingface.py +++ b/snp_oracle/predictionnet/utils/huggingface.py @@ -3,7 +3,7 @@ from huggingface_hub import HfApi, errors -from predictionnet.protocol import Challenge +from snp_oracle.predictionnet.protocol import Challenge class HfInterface: diff --git a/predictionnet/utils/miner_hf.py b/snp_oracle/predictionnet/utils/miner_hf.py similarity index 100% rename from predictionnet/utils/miner_hf.py rename to snp_oracle/predictionnet/utils/miner_hf.py diff --git a/predictionnet/utils/misc.py b/snp_oracle/predictionnet/utils/misc.py similarity index 76% rename from predictionnet/utils/misc.py rename to snp_oracle/predictionnet/utils/misc.py index 94d2aeb6..a555d5b8 100644 --- a/predictionnet/utils/misc.py +++ b/snp_oracle/predictionnet/utils/misc.py @@ -1,21 +1,3 @@ -# The MIT License (MIT) -# Copyright © 2023 Yuma Rao -# Copyright © 2023 Opentensor Foundation - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - import time from functools import lru_cache, update_wrapper from math import floor diff --git a/predictionnet/utils/uids.py b/snp_oracle/predictionnet/utils/uids.py similarity index 100% rename from predictionnet/utils/uids.py rename to snp_oracle/predictionnet/utils/uids.py diff --git a/snp_oracle/predictionnet/validator/__init__.py b/snp_oracle/predictionnet/validator/__init__.py new file mode 100644 index 00000000..535b2046 --- /dev/null +++ b/snp_oracle/predictionnet/validator/__init__.py @@ -0,0 +1,2 @@ +from snp_oracle.predictionnet.validator.forward import forward +from snp_oracle.predictionnet.validator.reward import get_rewards diff --git a/predictionnet/validator/forward.py b/snp_oracle/predictionnet/validator/forward.py similarity index 75% rename from predictionnet/validator/forward.py rename to snp_oracle/predictionnet/validator/forward.py index 2c4e0b06..5d9223b5 100644 --- a/predictionnet/validator/forward.py +++ b/snp_oracle/predictionnet/validator/forward.py @@ -1,18 +1,3 @@ -# The MIT License (MIT) -# Copyright © 2023 Yuma Rao -# TODO(developer): Set your name -# Copyright © 2023 -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. import time from datetime import datetime, timedelta @@ -21,10 +6,9 @@ from numpy import full, nan from pytz import timezone -# Import Validator Template -import predictionnet -from predictionnet.utils.uids import check_uid_availability -from predictionnet.validator.reward import get_rewards +import snp_oracle.predictionnet as predictionnet +from snp_oracle.predictionnet.utils.uids import check_uid_availability +from snp_oracle.predictionnet.validator.reward import get_rewards async def forward(self): diff --git a/predictionnet/validator/reward.py b/snp_oracle/predictionnet/validator/reward.py similarity index 91% rename from predictionnet/validator/reward.py rename to snp_oracle/predictionnet/validator/reward.py index 23725a3c..3038e5c4 100644 --- a/predictionnet/validator/reward.py +++ b/snp_oracle/predictionnet/validator/reward.py @@ -1,22 +1,3 @@ -# The MIT License (MIT) -# Copyright © 2023 Yuma Rao -# TODO(developer): Set your name -# Copyright © 2023 - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - import time from datetime import datetime, timedelta from typing import List @@ -26,7 +7,7 @@ import yfinance as yf from pytz import timezone -from predictionnet.protocol import Challenge +from snp_oracle.predictionnet.protocol import Challenge ################################################################################ From 8cd8235839efe4175b827bd441eec04944f6782c Mon Sep 17 00:00:00 2001 From: Matthew Trudeau Date: Wed, 18 Dec 2024 16:09:02 -0500 Subject: [PATCH 42/57] Remove existing tests and add package version test --- tests/helpers.py | 305 +++++++++++++++---------------- tests/test_package.py | 14 ++ tests/test_template_validator.py | 202 ++++++++++---------- 3 files changed, 250 insertions(+), 271 deletions(-) create mode 100644 tests/test_package.py diff --git a/tests/helpers.py b/tests/helpers.py index 40f72250..7fcf148e 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -1,161 +1,144 @@ -# The MIT License (MIT) -# Copyright © 2023 Opentensor Foundation - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - -from typing import Union - -from bittensor import AxonInfo, Balance, NeuronInfo, PrometheusInfo -from bittensor.mock.wallet_mock import MockWallet as _MockWallet -from bittensor.mock.wallet_mock import get_mock_coldkey as _get_mock_coldkey -from bittensor.mock.wallet_mock import get_mock_hotkey as _get_mock_hotkey -from bittensor.mock.wallet_mock import get_mock_wallet as _get_mock_wallet -from rich.console import Console -from rich.text import Text - - -def __mock_wallet_factory__(*args, **kwargs) -> _MockWallet: - """Returns a mock wallet object.""" - - mock_wallet = _get_mock_wallet() - - return mock_wallet - - -class CLOSE_IN_VALUE: - value: Union[float, int, Balance] - tolerance: Union[float, int, Balance] - - def __init__( - self, - value: Union[float, int, Balance], - tolerance: Union[float, int, Balance] = 0.0, - ) -> None: - self.value = value - self.tolerance = tolerance - - def __eq__(self, __o: Union[float, int, Balance]) -> bool: - # True if __o \in [value - tolerance, value + tolerance] - # or if value \in [__o - tolerance, __o + tolerance] - return ((self.value - self.tolerance) <= __o and __o <= (self.value + self.tolerance)) or ( - (__o - self.tolerance) <= self.value and self.value <= (__o + self.tolerance) - ) - - -def get_mock_neuron(**kwargs) -> NeuronInfo: - """ - Returns a mock neuron with the given kwargs overriding the default values. - """ - - mock_neuron_d = dict( - { - "netuid": -1, # mock netuid - "axon_info": AxonInfo( - block=0, - version=1, - ip=0, - port=0, - ip_type=0, - protocol=0, - placeholder1=0, - placeholder2=0, - ), - "prometheus_info": PrometheusInfo(block=0, version=1, ip=0, port=0, ip_type=0), - "validator_permit": True, - "uid": 1, - "hotkey": "some_hotkey", - "coldkey": "some_coldkey", - "active": 0, - "last_update": 0, - "stake": {"some_coldkey": 1e12}, - "total_stake": 1e12, - "rank": 0.0, - "trust": 0.0, - "consensus": 0.0, - "validator_trust": 0.0, - "incentive": 0.0, - "dividends": 0.0, - "emission": 0.0, - "bonds": [], - "weights": [], - "stake_dict": {}, - "pruning_score": 0.0, - "is_null": False, - } - ) - - mock_neuron_d.update(kwargs) # update with kwargs - - if kwargs.get("stake") is None and kwargs.get("coldkey") is not None: - mock_neuron_d["stake"] = {kwargs.get("coldkey"): 1e12} - - if kwargs.get("total_stake") is None: - mock_neuron_d["total_stake"] = sum(mock_neuron_d["stake"].values()) - - mock_neuron = NeuronInfo._neuron_dict_to_namespace(mock_neuron_d) - - return mock_neuron - - -def get_mock_neuron_by_uid(uid: int, **kwargs) -> NeuronInfo: - return get_mock_neuron(uid=uid, hotkey=_get_mock_hotkey(uid), coldkey=_get_mock_coldkey(uid), **kwargs) - - -class MockStatus: - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - pass - - def start(self): - pass - - def stop(self): - pass - - def update(self, *args, **kwargs): - MockConsole().print(*args, **kwargs) - - -class MockConsole: - """ - Mocks the console object for status and print. - Captures the last print output as a string. - """ - - captured_print = None - - def status(self, *args, **kwargs): - return MockStatus() - - def print(self, *args, **kwargs): - console = Console(width=1000, no_color=True, markup=False) # set width to 1000 to avoid truncation - console.begin_capture() - console.print(*args, **kwargs) - self.captured_print = console.end_capture() - - def clear(self, *args, **kwargs): - pass - - @staticmethod - def remove_rich_syntax(text: str) -> str: - """ - Removes rich syntax from the given text. - Removes markup and ansi syntax. - """ - output_no_syntax = Text.from_ansi(Text.from_markup(text).plain).plain - - return output_no_syntax +# from typing import Union + +# from bittensor import AxonInfo, Balance, NeuronInfo, PrometheusInfo +# from bittensor.mock.wallet_mock import MockWallet as _MockWallet +# from bittensor.mock.wallet_mock import get_mock_coldkey as _get_mock_coldkey +# from bittensor.mock.wallet_mock import get_mock_hotkey as _get_mock_hotkey +# from bittensor.mock.wallet_mock import get_mock_wallet as _get_mock_wallet +# from rich.console import Console +# from rich.text import Text + + +# def __mock_wallet_factory__(*args, **kwargs) -> _MockWallet: +# """Returns a mock wallet object.""" + +# mock_wallet = _get_mock_wallet() + +# return mock_wallet + + +# class CLOSE_IN_VALUE: +# value: Union[float, int, Balance] +# tolerance: Union[float, int, Balance] + +# def __init__( +# self, +# value: Union[float, int, Balance], +# tolerance: Union[float, int, Balance] = 0.0, +# ) -> None: +# self.value = value +# self.tolerance = tolerance + +# def __eq__(self, __o: Union[float, int, Balance]) -> bool: +# # True if __o \in [value - tolerance, value + tolerance] +# # or if value \in [__o - tolerance, __o + tolerance] +# return ((self.value - self.tolerance) <= __o and __o <= (self.value + self.tolerance)) or ( +# (__o - self.tolerance) <= self.value and self.value <= (__o + self.tolerance) +# ) + + +# def get_mock_neuron(**kwargs) -> NeuronInfo: +# """ +# Returns a mock neuron with the given kwargs overriding the default values. +# """ + +# mock_neuron_d = dict( +# { +# "netuid": -1, # mock netuid +# "axon_info": AxonInfo( +# block=0, +# version=1, +# ip=0, +# port=0, +# ip_type=0, +# protocol=0, +# placeholder1=0, +# placeholder2=0, +# ), +# "prometheus_info": PrometheusInfo(block=0, version=1, ip=0, port=0, ip_type=0), +# "validator_permit": True, +# "uid": 1, +# "hotkey": "some_hotkey", +# "coldkey": "some_coldkey", +# "active": 0, +# "last_update": 0, +# "stake": {"some_coldkey": 1e12}, +# "total_stake": 1e12, +# "rank": 0.0, +# "trust": 0.0, +# "consensus": 0.0, +# "validator_trust": 0.0, +# "incentive": 0.0, +# "dividends": 0.0, +# "emission": 0.0, +# "bonds": [], +# "weights": [], +# "stake_dict": {}, +# "pruning_score": 0.0, +# "is_null": False, +# } +# ) + +# mock_neuron_d.update(kwargs) # update with kwargs + +# if kwargs.get("stake") is None and kwargs.get("coldkey") is not None: +# mock_neuron_d["stake"] = {kwargs.get("coldkey"): 1e12} + +# if kwargs.get("total_stake") is None: +# mock_neuron_d["total_stake"] = sum(mock_neuron_d["stake"].values()) + +# mock_neuron = NeuronInfo._neuron_dict_to_namespace(mock_neuron_d) + +# return mock_neuron + + +# def get_mock_neuron_by_uid(uid: int, **kwargs) -> NeuronInfo: +# return get_mock_neuron(uid=uid, hotkey=_get_mock_hotkey(uid), coldkey=_get_mock_coldkey(uid), **kwargs) + + +# class MockStatus: +# def __enter__(self): +# return self + +# def __exit__(self, exc_type, exc_value, traceback): +# pass + +# def start(self): +# pass + +# def stop(self): +# pass + +# def update(self, *args, **kwargs): +# MockConsole().print(*args, **kwargs) + + +# class MockConsole: +# """ +# Mocks the console object for status and print. +# Captures the last print output as a string. +# """ + +# captured_print = None + +# def status(self, *args, **kwargs): +# return MockStatus() + +# def print(self, *args, **kwargs): +# console = Console(width=1000, no_color=True, markup=False) # set width to 1000 to avoid truncation +# console.begin_capture() +# console.print(*args, **kwargs) +# self.captured_print = console.end_capture() + +# def clear(self, *args, **kwargs): +# pass + +# @staticmethod +# def remove_rich_syntax(text: str) -> str: +# """ +# Removes rich syntax from the given text. +# Removes markup and ansi syntax. +# """ +# output_no_syntax = Text.from_ansi(Text.from_markup(text).plain).plain + +# return output_no_syntax diff --git a/tests/test_package.py b/tests/test_package.py new file mode 100644 index 00000000..a3084cce --- /dev/null +++ b/tests/test_package.py @@ -0,0 +1,14 @@ +import unittest + +from snp_oracle import __version__ + + +class TestPackage(unittest.TestCase): + + def setUp(self): + pass + + def test_package_version(self): + # Check that version is as expected + # Must update to increment package version successfully + self.assertEqual(__version__, "2.2.1") \ No newline at end of file diff --git a/tests/test_template_validator.py b/tests/test_template_validator.py index 14492e5e..85dc7e55 100644 --- a/tests/test_template_validator.py +++ b/tests/test_template_validator.py @@ -1,110 +1,92 @@ -# The MIT License (MIT) -# Copyright © 2023 Yuma Rao -# Copyright © 2023 Opentensor Foundation - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - -import sys -import unittest - -import bittensor as bt -from numpy import array -from template.base.validator import BaseValidatorNeuron -from template.protocol import Dummy -from template.utils.uids import get_random_uids -from template.validator.reward import get_rewards - -from neurons.validator import Neuron as Validator - - -class TemplateValidatorNeuronTestCase(unittest.TestCase): - """ - This class contains unit tests for the RewardEvent classes. - - The tests cover different scenarios where completions may or may not be successful and the reward events are checked that they don't contain missing values. - The `reward` attribute of all RewardEvents is expected to be a float, and the `is_filter_model` attribute is expected to be a boolean. - """ - - def setUp(self): - sys.argv = sys.argv[0] + ["--config", "tests/configs/validator.json"] - - config = BaseValidatorNeuron.config() - config.wallet._mock = True - config.metagraph._mock = True - config.subtensor._mock = True - self.neuron = Validator(config) - self.miner_uids = get_random_uids(self, k=10) - - def test_run_single_step(self): - # TODO: Test a single step - pass - - def test_sync_error_if_not_registered(self): - # TODO: Test that the validator throws an error if it is not registered on metagraph - pass - - def test_forward(self): - # TODO: Test that the forward function returns the correct value - pass - - def test_dummy_responses(self): - # TODO: Test that the dummy responses are correctly constructed - - responses = self.neuron.dendrite.query( - # Send the query to miners in the network. - axons=[self.neuron.metagraph.axons[uid] for uid in self.miner_uids], - # Construct a dummy query. - synapse=Dummy(dummy_input=self.neuron.step), - # All responses have the deserialize function called on them before returning. - deserialize=True, - ) - - for i, response in enumerate(responses): - self.assertEqual(response, self.neuron.step * 2) - - def test_reward(self): - # TODO: Test that the reward function returns the correct value - responses = self.dendrite.query( - # Send the query to miners in the network. - axons=[self.metagraph.axons[uid] for uid in self.miner_uids], - # Construct a dummy query. - synapse=Dummy(dummy_input=self.neuron.step), - # All responses have the deserialize function called on them before returning. - deserialize=True, - ) - - rewards = get_rewards(self.neuron, responses) - expected_rewards = array([1.0] * len(responses)) - self.assertEqual(rewards, expected_rewards) - - def test_reward_with_nan(self): - # TODO: Test that NaN rewards are correctly sanitized - # TODO: Test that a bt.logging.warning is thrown when a NaN reward is sanitized - responses = self.dendrite.query( - # Send the query to miners in the network. - axons=[self.metagraph.axons[uid] for uid in self.miner_uids], - # Construct a dummy query. - synapse=Dummy(dummy_input=self.neuron.step), - # All responses have the deserialize function called on them before returning. - deserialize=True, - ) - - rewards = get_rewards(self.neuron, responses) - _ = rewards.clone() # expected rewards - # Add NaN values to rewards - rewards[0] = float("nan") - - with self.assertLogs(bt.logging, level="WARNING") as _: - self.neuron.update_scores(rewards, self.miner_uids) +# import sys +# import unittest + +# import bittensor as bt +# from numpy import array +# from template.base.validator import BaseValidatorNeuron +# from template.protocol import Dummy +# from template.utils.uids import get_random_uids +# from template.validator.reward import get_rewards + +# from neurons.validator import Neuron as Validator + + +# class TemplateValidatorNeuronTestCase(unittest.TestCase): +# """ +# This class contains unit tests for the RewardEvent classes. + +# The tests cover different scenarios where completions may or may not be successful and the reward events are checked that they don't contain missing values. +# The `reward` attribute of all RewardEvents is expected to be a float, and the `is_filter_model` attribute is expected to be a boolean. +# """ + +# def setUp(self): +# sys.argv = sys.argv[0] + ["--config", "tests/configs/validator.json"] + +# config = BaseValidatorNeuron.config() +# config.wallet._mock = True +# config.metagraph._mock = True +# config.subtensor._mock = True +# self.neuron = Validator(config) +# self.miner_uids = get_random_uids(self, k=10) + +# def test_run_single_step(self): +# # TODO: Test a single step +# pass + +# def test_sync_error_if_not_registered(self): +# # TODO: Test that the validator throws an error if it is not registered on metagraph +# pass + +# def test_forward(self): +# # TODO: Test that the forward function returns the correct value +# pass + +# def test_dummy_responses(self): +# # TODO: Test that the dummy responses are correctly constructed + +# responses = self.neuron.dendrite.query( +# # Send the query to miners in the network. +# axons=[self.neuron.metagraph.axons[uid] for uid in self.miner_uids], +# # Construct a dummy query. +# synapse=Dummy(dummy_input=self.neuron.step), +# # All responses have the deserialize function called on them before returning. +# deserialize=True, +# ) + +# for i, response in enumerate(responses): +# self.assertEqual(response, self.neuron.step * 2) + +# def test_reward(self): +# # TODO: Test that the reward function returns the correct value +# responses = self.dendrite.query( +# # Send the query to miners in the network. +# axons=[self.metagraph.axons[uid] for uid in self.miner_uids], +# # Construct a dummy query. +# synapse=Dummy(dummy_input=self.neuron.step), +# # All responses have the deserialize function called on them before returning. +# deserialize=True, +# ) + +# rewards = get_rewards(self.neuron, responses) +# expected_rewards = array([1.0] * len(responses)) +# self.assertEqual(rewards, expected_rewards) + +# def test_reward_with_nan(self): +# # TODO: Test that NaN rewards are correctly sanitized +# # TODO: Test that a bt.logging.warning is thrown when a NaN reward is sanitized +# responses = self.dendrite.query( +# # Send the query to miners in the network. +# axons=[self.metagraph.axons[uid] for uid in self.miner_uids], +# # Construct a dummy query. +# synapse=Dummy(dummy_input=self.neuron.step), +# # All responses have the deserialize function called on them before returning. +# deserialize=True, +# ) + +# rewards = get_rewards(self.neuron, responses) +# _ = rewards.clone() # expected rewards +# # Add NaN values to rewards +# rewards[0] = float("nan") + +# with self.assertLogs(bt.logging, level="WARNING") as _: +# self.neuron.update_scores(rewards, self.miner_uids) From fa3c2c30462d1a2ae05bcad58d37da663294ddc8 Mon Sep 17 00:00:00 2001 From: Matthew Trudeau Date: Wed, 18 Dec 2024 16:09:44 -0500 Subject: [PATCH 43/57] Adjust github actions to leverage poetry --- .github/workflows/build.yml | 67 +++++++++++++++++++++++-------------- .github/workflows/ci.yml | 24 ++++++------- 2 files changed, 54 insertions(+), 37 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 288f6159..6c7142d5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Cache and Load Build +name: Cache Build on: workflow_call: @@ -17,7 +17,7 @@ jobs: steps: #------------------------------------------------ - # Checkout repo and setup python + # check-out repo and set-up python #------------------------------------------------ - name: Check out repository uses: actions/checkout@v4 @@ -27,41 +27,58 @@ jobs: python-version: '3.11' #------------------------------------------------ - # Load cached venv if cache exists + # ----- install & configure poetry ----- #------------------------------------------------ - - name: Restore cached virtualenv - uses: actions/cache/restore@v4 - id: restore-cache + - name: Load cached Poetry installation + id: cached-poetry + uses: actions/cache@v4 + with: + path: ~/.local # the path depends on the OS + key: poetry-0 # increment to reset cache + + - name: Install Poetry + if: steps.cached-poetry.outputs.cache-hit != 'true' + uses: snok/install-poetry@v1 + with: + version: latest + virtualenvs-create: true + virtualenvs-in-project: true + virtualenvs-path: .venv + installer-parallel: true + + # If cache was loaded, we must redo configuration + - name: Configure poetry + if: steps.cached-poetry.outputs.cache-hit == 'true' + run: | + poetry config virtualenvs.create true + poetry config virtualenvs.in-project true + poetry config virtualenvs.path .venv + + #------------------------------------------------ + # Load cached venv if exists + #------------------------------------------------ + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v4 with: - key: venv-${{ runner.os }}-${{ steps.setup_python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('dev_requirements.txt') }} path: .venv + key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} #------------------------------------------------ - # Install dependencies - if cache does not exist + # Install dependencies if cache does not exist #------------------------------------------------ - name: Install dependencies - if: steps.restore-cache.outputs.cache-hit != 'true' - run: | - python -m venv .venv - source .venv/bin/activate - python -m pip install .[DEV] - echo "$VIRTUAL_ENV/bin" >> $GITHUB_PATH - echo "VIRTUAL_ENV=$VIRTUAL_ENV" >> $GITHUB_ENV + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + run: poetry install --no-interaction --no-root #------------------------------------------------ - # Save venv to cache - if not exists + # Install your root project #------------------------------------------------ - - name: Saved cached virtualenv - if: steps.restore-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 - with: - key: ${{ steps.restore-cache.outputs.cache-primary-key }} - path: .venv + - name: Install project + run: poetry install --no-interaction #------------------------------------------------ # Run custom command(s) within venv #------------------------------------------------ - name: Run commands - run: | - source .venv/bin/activate - ${{ inputs.command }} + run: ${{ inputs.command }} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ad04403..3425cdd4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,9 @@ -name: Continuous Integration +name: CI on: pull_request: push: - branches: [main, dev] + branches: [main, staging, dev] jobs: #---------------------------------------------- @@ -15,9 +15,10 @@ jobs: with: name: Cache command: | - python -m pip list - python --version - echo "Build successful" + poetry run python -m pip list + poetry run python --version + poetry --version + poetry run echo "Build successful" #---------------------------------------------- # Run Linters @@ -28,14 +29,14 @@ jobs: uses: ./.github/workflows/build.yml with: name: Black - command: python -m black --check . + command: poetry run python -m black --check . lint-isort: name: Linter needs: build uses: ./.github/workflows/build.yml with: name: Isort - command: python -m isort --check-only . + command: poetry run python -m isort --check-only . lint-mypy: name: Linter needs: build @@ -43,14 +44,14 @@ jobs: uses: ./.github/workflows/build.yml with: name: Mypy - command: python -m mypy --verbose 0 . + command: poetry run python -m mypy --verbose 0 . lint-flake8: name: Linter needs: build uses: ./.github/workflows/build.yml with: name: Flake8 - command: python -m flake8 . + command: poetry run python -m flake8 . #---------------------------------------------- # Run Tests @@ -63,9 +64,8 @@ jobs: lint-mypy, lint-flake8, ] - # `${{ always() }}` will run the tests regardless of linting success - if: false # This condition ensures the job is never executed + if: ${{ always() }} # will run the tests regardless of linting success uses: ./.github/workflows/build.yml with: name: Unittests - command: pytest tests/ + command: poetry run pytest tests/ \ No newline at end of file From 04cec4d5e6d6d73e928093c90bc70ac26dd8ae4f Mon Sep 17 00:00:00 2001 From: Matthew Trudeau Date: Wed, 18 Dec 2024 16:30:50 -0500 Subject: [PATCH 44/57] Use poetry for dependency management --- poetry.lock | 2484 +++++++++++++++++++++-------- pyproject.toml | 38 +- requirements/dev_requirements.txt | 10 - requirements/requirements.txt | 17 - setup.py | 94 -- 5 files changed, 1886 insertions(+), 757 deletions(-) delete mode 100644 requirements/dev_requirements.txt delete mode 100644 requirements/requirements.txt delete mode 100644 setup.py diff --git a/poetry.lock b/poetry.lock index 12bcd95f..e03d850a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "absl-py" @@ -24,87 +24,87 @@ files = [ [[package]] name = "aiohttp" -version = "3.11.10" +version = "3.11.11" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" files = [ - {file = "aiohttp-3.11.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cbad88a61fa743c5d283ad501b01c153820734118b65aee2bd7dbb735475ce0d"}, - {file = "aiohttp-3.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80886dac673ceaef499de2f393fc80bb4481a129e6cb29e624a12e3296cc088f"}, - {file = "aiohttp-3.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61b9bae80ed1f338c42f57c16918853dc51775fb5cb61da70d590de14d8b5fb4"}, - {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e2e576caec5c6a6b93f41626c9c02fc87cd91538b81a3670b2e04452a63def6"}, - {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02c13415b5732fb6ee7ff64583a5e6ed1c57aa68f17d2bda79c04888dfdc2769"}, - {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfce37f31f20800a6a6620ce2cdd6737b82e42e06e6e9bd1b36f546feb3c44f"}, - {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bbbfff4c679c64e6e23cb213f57cc2c9165c9a65d63717108a644eb5a7398df"}, - {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49c7dbbc1a559ae14fc48387a115b7d4bbc84b4a2c3b9299c31696953c2a5219"}, - {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68386d78743e6570f054fe7949d6cb37ef2b672b4d3405ce91fafa996f7d9b4d"}, - {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9ef405356ba989fb57f84cac66f7b0260772836191ccefbb987f414bcd2979d9"}, - {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5d6958671b296febe7f5f859bea581a21c1d05430d1bbdcf2b393599b1cdce77"}, - {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:99b7920e7165be5a9e9a3a7f1b680f06f68ff0d0328ff4079e5163990d046767"}, - {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0dc49f42422163efb7e6f1df2636fe3db72713f6cd94688e339dbe33fe06d61d"}, - {file = "aiohttp-3.11.10-cp310-cp310-win32.whl", hash = "sha256:40d1c7a7f750b5648642586ba7206999650208dbe5afbcc5284bcec6579c9b91"}, - {file = "aiohttp-3.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:68ff6f48b51bd78ea92b31079817aff539f6c8fc80b6b8d6ca347d7c02384e33"}, - {file = "aiohttp-3.11.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:77c4aa15a89847b9891abf97f3d4048f3c2d667e00f8a623c89ad2dccee6771b"}, - {file = "aiohttp-3.11.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:909af95a72cedbefe5596f0bdf3055740f96c1a4baa0dd11fd74ca4de0b4e3f1"}, - {file = "aiohttp-3.11.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:386fbe79863eb564e9f3615b959e28b222259da0c48fd1be5929ac838bc65683"}, - {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3de34936eb1a647aa919655ff8d38b618e9f6b7f250cc19a57a4bf7fd2062b6d"}, - {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c9527819b29cd2b9f52033e7fb9ff08073df49b4799c89cb5754624ecd98299"}, - {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a96e3e03300b41f261bbfd40dfdbf1c301e87eab7cd61c054b1f2e7c89b9e8"}, - {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f5635f7b74bcd4f6f72fcd85bea2154b323a9f05226a80bc7398d0c90763b0"}, - {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03b6002e20938fc6ee0918c81d9e776bebccc84690e2b03ed132331cca065ee5"}, - {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6362cc6c23c08d18ddbf0e8c4d5159b5df74fea1a5278ff4f2c79aed3f4e9f46"}, - {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3691ed7726fef54e928fe26344d930c0c8575bc968c3e239c2e1a04bd8cf7838"}, - {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31d5093d3acd02b31c649d3a69bb072d539d4c7659b87caa4f6d2bcf57c2fa2b"}, - {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8b3cf2dc0f0690a33f2d2b2cb15db87a65f1c609f53c37e226f84edb08d10f52"}, - {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbbaea811a2bba171197b08eea288b9402faa2bab2ba0858eecdd0a4105753a3"}, - {file = "aiohttp-3.11.10-cp311-cp311-win32.whl", hash = "sha256:4b2c7ac59c5698a7a8207ba72d9e9c15b0fc484a560be0788b31312c2c5504e4"}, - {file = "aiohttp-3.11.10-cp311-cp311-win_amd64.whl", hash = "sha256:974d3a2cce5fcfa32f06b13ccc8f20c6ad9c51802bb7f829eae8a1845c4019ec"}, - {file = "aiohttp-3.11.10-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b78f053a7ecfc35f0451d961dacdc671f4bcbc2f58241a7c820e9d82559844cf"}, - {file = "aiohttp-3.11.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab7485222db0959a87fbe8125e233b5a6f01f4400785b36e8a7878170d8c3138"}, - {file = "aiohttp-3.11.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cf14627232dfa8730453752e9cdc210966490992234d77ff90bc8dc0dce361d5"}, - {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076bc454a7e6fd646bc82ea7f98296be0b1219b5e3ef8a488afbdd8e81fbac50"}, - {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:482cafb7dc886bebeb6c9ba7925e03591a62ab34298ee70d3dd47ba966370d2c"}, - {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf3d1a519a324af764a46da4115bdbd566b3c73fb793ffb97f9111dbc684fc4d"}, - {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24213ba85a419103e641e55c27dc7ff03536c4873470c2478cce3311ba1eee7b"}, - {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b99acd4730ad1b196bfb03ee0803e4adac371ae8efa7e1cbc820200fc5ded109"}, - {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:14cdb5a9570be5a04eec2ace174a48ae85833c2aadc86de68f55541f66ce42ab"}, - {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7e97d622cb083e86f18317282084bc9fbf261801b0192c34fe4b1febd9f7ae69"}, - {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:012f176945af138abc10c4a48743327a92b4ca9adc7a0e078077cdb5dbab7be0"}, - {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44224d815853962f48fe124748227773acd9686eba6dc102578defd6fc99e8d9"}, - {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c87bf31b7fdab94ae3adbe4a48e711bfc5f89d21cf4c197e75561def39e223bc"}, - {file = "aiohttp-3.11.10-cp312-cp312-win32.whl", hash = "sha256:06a8e2ee1cbac16fe61e51e0b0c269400e781b13bcfc33f5425912391a542985"}, - {file = "aiohttp-3.11.10-cp312-cp312-win_amd64.whl", hash = "sha256:be2b516f56ea883a3e14dda17059716593526e10fb6303189aaf5503937db408"}, - {file = "aiohttp-3.11.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8cc5203b817b748adccb07f36390feb730b1bc5f56683445bfe924fc270b8816"}, - {file = "aiohttp-3.11.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ef359ebc6949e3a34c65ce20230fae70920714367c63afd80ea0c2702902ccf"}, - {file = "aiohttp-3.11.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9bca390cb247dbfaec3c664326e034ef23882c3f3bfa5fbf0b56cad0320aaca5"}, - {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811f23b3351ca532af598405db1093f018edf81368e689d1b508c57dcc6b6a32"}, - {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddf5f7d877615f6a1e75971bfa5ac88609af3b74796ff3e06879e8422729fd01"}, - {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ab29b8a0beb6f8eaf1e5049252cfe74adbaafd39ba91e10f18caeb0e99ffb34"}, - {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c49a76c1038c2dd116fa443eba26bbb8e6c37e924e2513574856de3b6516be99"}, - {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f3dc0e330575f5b134918976a645e79adf333c0a1439dcf6899a80776c9ab39"}, - {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:efb15a17a12497685304b2d976cb4939e55137df7b09fa53f1b6a023f01fcb4e"}, - {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:db1d0b28fcb7f1d35600150c3e4b490775251dea70f894bf15c678fdd84eda6a"}, - {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:15fccaf62a4889527539ecb86834084ecf6e9ea70588efde86e8bc775e0e7542"}, - {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:593c114a2221444f30749cc5e5f4012488f56bd14de2af44fe23e1e9894a9c60"}, - {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7852bbcb4d0d2f0c4d583f40c3bc750ee033265d80598d0f9cb6f372baa6b836"}, - {file = "aiohttp-3.11.10-cp313-cp313-win32.whl", hash = "sha256:65e55ca7debae8faaffee0ebb4b47a51b4075f01e9b641c31e554fd376595c6c"}, - {file = "aiohttp-3.11.10-cp313-cp313-win_amd64.whl", hash = "sha256:beb39a6d60a709ae3fb3516a1581777e7e8b76933bb88c8f4420d875bb0267c6"}, - {file = "aiohttp-3.11.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0580f2e12de2138f34debcd5d88894786453a76e98febaf3e8fe5db62d01c9bf"}, - {file = "aiohttp-3.11.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a55d2ad345684e7c3dd2c20d2f9572e9e1d5446d57200ff630e6ede7612e307f"}, - {file = "aiohttp-3.11.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04814571cb72d65a6899db6099e377ed00710bf2e3eafd2985166f2918beaf59"}, - {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e44a9a3c053b90c6f09b1bb4edd880959f5328cf63052503f892c41ea786d99f"}, - {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:502a1464ccbc800b4b1995b302efaf426e8763fadf185e933c2931df7db9a199"}, - {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:613e5169f8ae77b1933e42e418a95931fb4867b2991fc311430b15901ed67079"}, - {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cca22a61b7fe45da8fc73c3443150c3608750bbe27641fc7558ec5117b27fdf"}, - {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86a5dfcc39309470bd7b68c591d84056d195428d5d2e0b5ccadfbaf25b026ebc"}, - {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:77ae58586930ee6b2b6f696c82cf8e78c8016ec4795c53e36718365f6959dc82"}, - {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:78153314f26d5abef3239b4a9af20c229c6f3ecb97d4c1c01b22c4f87669820c"}, - {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:98283b94cc0e11c73acaf1c9698dea80c830ca476492c0fe2622bd931f34b487"}, - {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:53bf2097e05c2accc166c142a2090e4c6fd86581bde3fd9b2d3f9e93dda66ac1"}, - {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5532f0441fc09c119e1dca18fbc0687e64fbeb45aa4d6a87211ceaee50a74c4"}, - {file = "aiohttp-3.11.10-cp39-cp39-win32.whl", hash = "sha256:47ad15a65fb41c570cd0ad9a9ff8012489e68176e7207ec7b82a0940dddfd8be"}, - {file = "aiohttp-3.11.10-cp39-cp39-win_amd64.whl", hash = "sha256:c6b9e6d7e41656d78e37ce754813fa44b455c3d0d0dced2a047def7dc5570b74"}, - {file = "aiohttp-3.11.10.tar.gz", hash = "sha256:b1fc6b45010a8d0ff9e88f9f2418c6fd408c99c211257334aff41597ebece42e"}, + {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8"}, + {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5"}, + {file = "aiohttp-3.11.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:731468f555656767cda219ab42e033355fe48c85fbe3ba83a349631541715ba2"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb23d8bb86282b342481cad4370ea0853a39e4a32a0042bb52ca6bdde132df43"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f047569d655f81cb70ea5be942ee5d4421b6219c3f05d131f64088c73bb0917f"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7659baae9ccf94ae5fe8bfaa2c7bc2e94d24611528395ce88d009107e00c6d"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af01e42ad87ae24932138f154105e88da13ce7d202a6de93fafdafb2883a00ef"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5854be2f3e5a729800bac57a8d76af464e160f19676ab6aea74bde18ad19d438"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6526e5fb4e14f4bbf30411216780c9967c20c5a55f2f51d3abd6de68320cc2f3"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:85992ee30a31835fc482468637b3e5bd085fa8fe9392ba0bdcbdc1ef5e9e3c55"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:88a12ad8ccf325a8a5ed80e6d7c3bdc247d66175afedbe104ee2aaca72960d8e"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0a6d3fbf2232e3a08c41eca81ae4f1dff3d8f1a30bae415ebe0af2d2458b8a33"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84a585799c58b795573c7fa9b84c455adf3e1d72f19a2bf498b54a95ae0d194c"}, + {file = "aiohttp-3.11.11-cp310-cp310-win32.whl", hash = "sha256:bfde76a8f430cf5c5584553adf9926534352251d379dcb266ad2b93c54a29745"}, + {file = "aiohttp-3.11.11-cp310-cp310-win_amd64.whl", hash = "sha256:0fd82b8e9c383af11d2b26f27a478640b6b83d669440c0a71481f7c865a51da9"}, + {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ba74ec819177af1ef7f59063c6d35a214a8fde6f987f7661f4f0eecc468a8f76"}, + {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4af57160800b7a815f3fe0eba9b46bf28aafc195555f1824555fa2cfab6c1538"}, + {file = "aiohttp-3.11.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffa336210cf9cd8ed117011085817d00abe4c08f99968deef0013ea283547204"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b8fe282183e4a3c7a1b72f5ade1094ed1c6345a8f153506d114af5bf8accd9"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af41686ccec6a0f2bdc66686dc0f403c41ac2089f80e2214a0f82d001052c03"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70d1f9dde0e5dd9e292a6d4d00058737052b01f3532f69c0c65818dac26dc287"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:249cc6912405917344192b9f9ea5cd5b139d49e0d2f5c7f70bdfaf6b4dbf3a2e"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0eb98d90b6690827dcc84c246811feeb4e1eea683c0eac6caed7549be9c84665"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec82bf1fda6cecce7f7b915f9196601a1bd1a3079796b76d16ae4cce6d0ef89b"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9fd46ce0845cfe28f108888b3ab17abff84ff695e01e73657eec3f96d72eef34"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd176afcf8f5d2aed50c3647d4925d0db0579d96f75a31e77cbaf67d8a87742d"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ec2aa89305006fba9ffb98970db6c8221541be7bee4c1d027421d6f6df7d1ce2"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:92cde43018a2e17d48bb09c79e4d4cb0e236de5063ce897a5e40ac7cb4878773"}, + {file = "aiohttp-3.11.11-cp311-cp311-win32.whl", hash = "sha256:aba807f9569455cba566882c8938f1a549f205ee43c27b126e5450dc9f83cc62"}, + {file = "aiohttp-3.11.11-cp311-cp311-win_amd64.whl", hash = "sha256:ae545f31489548c87b0cced5755cfe5a5308d00407000e72c4fa30b19c3220ac"}, + {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e595c591a48bbc295ebf47cb91aebf9bd32f3ff76749ecf282ea7f9f6bb73886"}, + {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ea1b59dc06396b0b424740a10a0a63974c725b1c64736ff788a3689d36c02d2"}, + {file = "aiohttp-3.11.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8811f3f098a78ffa16e0ea36dffd577eb031aea797cbdba81be039a4169e242c"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7227b87a355ce1f4bf83bfae4399b1f5bb42e0259cb9405824bd03d2f4336a"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d40f9da8cabbf295d3a9dae1295c69975b86d941bc20f0a087f0477fa0a66231"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffb3dc385f6bb1568aa974fe65da84723210e5d9707e360e9ecb51f59406cd2e"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8f5f7515f3552d899c61202d99dcb17d6e3b0de777900405611cd747cecd1b8"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3499c7ffbfd9c6a3d8d6a2b01c26639da7e43d47c7b4f788016226b1e711caa8"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8e2bf8029dbf0810c7bfbc3e594b51c4cc9101fbffb583a3923aea184724203c"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6212a60e5c482ef90f2d788835387070a88d52cf6241d3916733c9176d39eab"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d119fafe7b634dbfa25a8c597718e69a930e4847f0b88e172744be24515140da"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:6fba278063559acc730abf49845d0e9a9e1ba74f85f0ee6efd5803f08b285853"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92fc484e34b733704ad77210c7957679c5c3877bd1e6b6d74b185e9320cc716e"}, + {file = "aiohttp-3.11.11-cp312-cp312-win32.whl", hash = "sha256:9f5b3c1ed63c8fa937a920b6c1bec78b74ee09593b3f5b979ab2ae5ef60d7600"}, + {file = "aiohttp-3.11.11-cp312-cp312-win_amd64.whl", hash = "sha256:1e69966ea6ef0c14ee53ef7a3d68b564cc408121ea56c0caa2dc918c1b2f553d"}, + {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:541d823548ab69d13d23730a06f97460f4238ad2e5ed966aaf850d7c369782d9"}, + {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:929f3ed33743a49ab127c58c3e0a827de0664bfcda566108989a14068f820194"}, + {file = "aiohttp-3.11.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0882c2820fd0132240edbb4a51eb8ceb6eef8181db9ad5291ab3332e0d71df5f"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b63de12e44935d5aca7ed7ed98a255a11e5cb47f83a9fded7a5e41c40277d104"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa54f8ef31d23c506910c21163f22b124facb573bff73930735cf9fe38bf7dff"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a344d5dc18074e3872777b62f5f7d584ae4344cd6006c17ba12103759d407af3"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7fb429ab1aafa1f48578eb315ca45bd46e9c37de11fe45c7f5f4138091e2f1"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c341c7d868750e31961d6d8e60ff040fb9d3d3a46d77fd85e1ab8e76c3e9a5c4"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed9ee95614a71e87f1a70bc81603f6c6760128b140bc4030abe6abaa988f1c3d"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:de8d38f1c2810fa2a4f1d995a2e9c70bb8737b18da04ac2afbf3971f65781d87"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a9b7371665d4f00deb8f32208c7c5e652059b0fda41cf6dbcac6114a041f1cc2"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:620598717fce1b3bd14dd09947ea53e1ad510317c85dda2c9c65b622edc96b12"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf8d9bfee991d8acc72d060d53860f356e07a50f0e0d09a8dfedea1c554dd0d5"}, + {file = "aiohttp-3.11.11-cp313-cp313-win32.whl", hash = "sha256:9d73ee3725b7a737ad86c2eac5c57a4a97793d9f442599bea5ec67ac9f4bdc3d"}, + {file = "aiohttp-3.11.11-cp313-cp313-win_amd64.whl", hash = "sha256:c7a06301c2fb096bdb0bd25fe2011531c1453b9f2c163c8031600ec73af1cc99"}, + {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3e23419d832d969f659c208557de4a123e30a10d26e1e14b73431d3c13444c2e"}, + {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21fef42317cf02e05d3b09c028712e1d73a9606f02467fd803f7c1f39cc59add"}, + {file = "aiohttp-3.11.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1f21bb8d0235fc10c09ce1d11ffbd40fc50d3f08a89e4cf3a0c503dc2562247a"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1642eceeaa5ab6c9b6dfeaaa626ae314d808188ab23ae196a34c9d97efb68350"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2170816e34e10f2fd120f603e951630f8a112e1be3b60963a1f159f5699059a6"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8be8508d110d93061197fd2d6a74f7401f73b6d12f8822bbcd6d74f2b55d71b1"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eed954b161e6b9b65f6be446ed448ed3921763cc432053ceb606f89d793927e"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6c9af134da4bc9b3bd3e6a70072509f295d10ee60c697826225b60b9959acdd"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:44167fc6a763d534a6908bdb2592269b4bf30a03239bcb1654781adf5e49caf1"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:479b8c6ebd12aedfe64563b85920525d05d394b85f166b7873c8bde6da612f9c"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:10b4ff0ad793d98605958089fabfa350e8e62bd5d40aa65cdc69d6785859f94e"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b540bd67cfb54e6f0865ceccd9979687210d7ed1a1cc8c01f8e67e2f1e883d28"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1dac54e8ce2ed83b1f6b1a54005c87dfed139cf3f777fdc8afc76e7841101226"}, + {file = "aiohttp-3.11.11-cp39-cp39-win32.whl", hash = "sha256:568c1236b2fde93b7720f95a890741854c1200fba4a3471ff48b2934d2d93fd3"}, + {file = "aiohttp-3.11.11-cp39-cp39-win_amd64.whl", hash = "sha256:943a8b052e54dfd6439fd7989f67fc6a7f2138d0a2cf0a7de5f18aa4fe7eb3b1"}, + {file = "aiohttp-3.11.11.tar.gz", hash = "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e"}, ] [package.dependencies] @@ -122,13 +122,13 @@ speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] [[package]] name = "aiosignal" -version = "1.3.1" +version = "1.3.2" description = "aiosignal: a list of registered asynchronous callbacks" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, - {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, + {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, + {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, ] [package.dependencies] @@ -218,17 +218,6 @@ doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = "*" -files = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] - [[package]] name = "astunparse" version = "1.6.3" @@ -257,19 +246,19 @@ files = [ [[package]] name = "attrs" -version = "24.2.0" +version = "24.3.0" description = "Classes Without Boilerplate" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, - {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, + {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, + {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, ] [package.extras] benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] @@ -372,6 +361,52 @@ wheel = "*" dev = ["aioresponses (==0.7.6)", "black (==24.3.0)", "coveralls (==3.3.1)", "ddt (==1.6.0)", "factory-boy (==3.3.0)", "flake8 (==7.0.0)", "freezegun (==1.5.0)", "httpx (==0.27.0)", "hypothesis (==6.81.1)", "mypy (==1.8.0)", "pytest (==7.2.0)", "pytest-asyncio (==0.23.7)", "pytest-cov (==4.0.0)", "pytest-mock (==3.12.0)", "pytest-rerunfailures (==10.2)", "pytest-split (==0.8.0)", "pytest-xdist (==3.0.2)", "ruff (==0.4.7)", "torch (>=1.13.1)", "types-retry (==0.9.9.4)"] torch = ["torch (>=1.13.1)"] +[[package]] +name = "black" +version = "24.10.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.9" +files = [ + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + [[package]] name = "certifi" version = "2024.7.4" @@ -462,6 +497,17 @@ files = [ [package.dependencies] pycparser = "*" +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + [[package]] name = "charset-normalizer" version = "3.4.0" @@ -657,97 +703,111 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "cytoolz" -version = "1.0.0" +version = "1.0.1" description = "Cython implementation of Toolz: High performance functional utilities" optional = false python-versions = ">=3.8" files = [ - {file = "cytoolz-1.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ecf5a887acb8f079ab1b81612b1c889bcbe6611aa7804fd2df46ed310aa5a345"}, - {file = "cytoolz-1.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ef0ef30c1e091d4d59d14d8108a16d50bd227be5d52a47da891da5019ac2f8e4"}, - {file = "cytoolz-1.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7df2dfd679f0517a96ced1cdd22f5c6c6aeeed28d928a82a02bf4c3fd6fd7ac4"}, - {file = "cytoolz-1.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c51452c938e610f57551aa96e34924169c9100c0448bac88c2fb395cbd3538c"}, - {file = "cytoolz-1.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6433f03910c5e5345d82d6299457c26bf33821224ebb837c6b09d9cdbc414a6c"}, - {file = "cytoolz-1.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:389ec328bb535f09e71dfe658bf0041f17194ca4cedaacd39bafe7893497a819"}, - {file = "cytoolz-1.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c64658e1209517ce4b54c1c9269a508b289d8d55fc742760e4b8579eacf09a33"}, - {file = "cytoolz-1.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f6039a9bd5bb988762458b9ca82b39e60ca5e5baae2ba93913990dcc5d19fa88"}, - {file = "cytoolz-1.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:85c9c8c4465ed1b2c8d67003809aec9627b129cb531d2f6cf0bbfe39952e7e4d"}, - {file = "cytoolz-1.0.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:49375aad431d76650f94877afb92f09f58b6ff9055079ef4f2cd55313f5a1b39"}, - {file = "cytoolz-1.0.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:4c45106171c824a61e755355520b646cb35a1987b34bbf5789443823ee137f63"}, - {file = "cytoolz-1.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3b319a7f0fed5db07d189db4046162ebc183c108df3562a65ba6ebe862d1f634"}, - {file = "cytoolz-1.0.0-cp310-cp310-win32.whl", hash = "sha256:9770e1b09748ad0d751853d994991e2592a9f8c464a87014365f80dac2e83faa"}, - {file = "cytoolz-1.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:20194dd02954c00c1f0755e636be75a20781f91a4ac9270c7f747e82d3c7f5a5"}, - {file = "cytoolz-1.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dffc22fd2c91be64dbdbc462d0786f8e8ac9a275cfa1869a1084d1867d4f67e0"}, - {file = "cytoolz-1.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a99e7e29274e293f4ffe20e07f76c2ac753a78f1b40c1828dfc54b2981b2f6c4"}, - {file = "cytoolz-1.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c507a3e0a45c41d66b43f96797290d75d1e7a8549aa03a4a6b8854fdf3f7b8d8"}, - {file = "cytoolz-1.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:643a593ec272ef7429099e1182a22f64ec2696c00d295d2a5be390db1b7ff176"}, - {file = "cytoolz-1.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ce38e2e42cbae30446190c59b92a8a9029e1806fd79eaf88f48b0fe33003893"}, - {file = "cytoolz-1.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810a6a168b8c5ecb412fbae3dd6f7ed6c6253a63caf4174ee9794ebd29b2224f"}, - {file = "cytoolz-1.0.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ce8a2a85c0741c1b19b16e6782c4a5abc54c3caecda66793447112ab2fa9884"}, - {file = "cytoolz-1.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ea4ac72e6b830861035c4c7999af8e55813f57c6d1913a3d93cc4a6babc27bf7"}, - {file = "cytoolz-1.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a09cdfb21dfb38aa04df43e7546a41f673377eb5485da88ceb784e327ec7603b"}, - {file = "cytoolz-1.0.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:658dd85deb375ff7af990a674e5c9058cef1c9d1f5dc89bc87b77be499348144"}, - {file = "cytoolz-1.0.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9715d1ff5576919d10b68f17241375f6a1eec8961c25b78a83e6ef1487053f39"}, - {file = "cytoolz-1.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f370a1f1f1afc5c1c8cc5edc1cfe0ba444263a0772af7ce094be8e734f41769d"}, - {file = "cytoolz-1.0.0-cp311-cp311-win32.whl", hash = "sha256:dbb2ec1177dca700f3db2127e572da20de280c214fc587b2a11c717fc421af56"}, - {file = "cytoolz-1.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:0983eee73df86e54bb4a79fcc4996aa8b8368fdbf43897f02f9c3bf39c4dc4fb"}, - {file = "cytoolz-1.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:10e3986066dc379e30e225b230754d9f5996aa8d84c2accc69c473c21d261e46"}, - {file = "cytoolz-1.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:16576f1bb143ee2cb9f719fcc4b845879fb121f9075c7c5e8a5ff4854bd02fc6"}, - {file = "cytoolz-1.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3faa25a1840b984315e8b3ae517312375f4273ffc9a2f035f548b7f916884f37"}, - {file = "cytoolz-1.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:781fce70a277b20fd95dc66811d1a97bb07b611ceea9bda8b7dd3c6a4b05d59a"}, - {file = "cytoolz-1.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7a562c25338eb24d419d1e80a7ae12133844ce6fdeb4ab54459daf250088a1b2"}, - {file = "cytoolz-1.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f29d8330aaf070304f7cd5cb7e73e198753624eb0aec278557cccd460c699b5b"}, - {file = "cytoolz-1.0.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:98a96c54aa55ed9c7cdb23c2f0df39a7b4ee518ac54888480b5bdb5ef69c7ef0"}, - {file = "cytoolz-1.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:287d6d7f475882c2ddcbedf8da9a9b37d85b77690779a2d1cdceb5ae3998d52e"}, - {file = "cytoolz-1.0.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:05a871688df749b982839239fcd3f8ec3b3b4853775d575ff9cd335fa7c75035"}, - {file = "cytoolz-1.0.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:28bb88e1e2f7d6d4b8e0890b06d292c568984d717de3e8381f2ca1dd12af6470"}, - {file = "cytoolz-1.0.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:576a4f1fc73d8836b10458b583f915849da6e4f7914f4ecb623ad95c2508cad5"}, - {file = "cytoolz-1.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:509ed3799c47e4ada14f63e41e8f540ac6e2dab97d5d7298934e6abb9d3830ec"}, - {file = "cytoolz-1.0.0-cp312-cp312-win32.whl", hash = "sha256:9ce25f02b910630f6dc2540dd1e26c9326027ddde6c59f8cab07c56acc70714c"}, - {file = "cytoolz-1.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:7e53cfcce87e05b7f0ae2fb2b3e5820048cd0bb7b701e92bd8f75c9fbb7c9ae9"}, - {file = "cytoolz-1.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7d56569dfe67a39ce74ffff0dc12cf0a3d1aae709667a303fe8f2dd5fd004fdf"}, - {file = "cytoolz-1.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:035c8bb4706dcf93a89fb35feadff67e9301935bf6bb864cd2366923b69d9a29"}, - {file = "cytoolz-1.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27c684799708bdc7ee7acfaf464836e1b4dec0996815c1d5efd6a92a4356a562"}, - {file = "cytoolz-1.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44ab57cfc922b15d94899f980d76759ef9e0256912dfab70bf2561bea9cd5b19"}, - {file = "cytoolz-1.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:478af5ecc066da093d7660b23d0b465a7f44179739937afbded8af00af412eb6"}, - {file = "cytoolz-1.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da1f82a7828a42468ea2820a25b6e56461361390c29dcd4d68beccfa1b71066b"}, - {file = "cytoolz-1.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c371b3114d38ee717780b239179e88d5d358fe759a00dcf07691b8922bbc762"}, - {file = "cytoolz-1.0.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:90b343b2f3b3e77c3832ba19b0b17e95412a5b2e715b05c23a55ba525d1fca49"}, - {file = "cytoolz-1.0.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89a554a9ba112403232a54e15e46ff218b33020f3f45c4baf6520ab198b7ad93"}, - {file = "cytoolz-1.0.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:0d603f5e2b1072166745ecdd81384a75757a96a704a5642231eb51969f919d5f"}, - {file = "cytoolz-1.0.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:122ef2425bd3c0419e6e5260d0b18cd25cf74de589cd0184e4a63b24a4641e2e"}, - {file = "cytoolz-1.0.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8819f1f97ebe36efcaf4b550e21677c46ac8a41bed482cf66845f377dd20700d"}, - {file = "cytoolz-1.0.0-cp38-cp38-win32.whl", hash = "sha256:fcddbb853770dd6e270d89ea8742f0aa42c255a274b9e1620eb04e019b79785e"}, - {file = "cytoolz-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:ca526905a014a38cc23ae78635dc51d0462c5c24425b22c08beed9ff2ee03845"}, - {file = "cytoolz-1.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:05df5ff1cdd198fb57e7368623662578c950be0b14883cadfb9ee4098415e1e5"}, - {file = "cytoolz-1.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04a84778f48ebddb26948971dc60948907c876ba33b13f9cbb014fe65b341fc2"}, - {file = "cytoolz-1.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f65283b618b4c4df759f57bcf8483865a73f7f268e6d76886c743407c8d26c1c"}, - {file = "cytoolz-1.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388cd07ee9a9e504c735a0a933e53c98586a1c301a64af81f7aa7ff40c747520"}, - {file = "cytoolz-1.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:06d09e9569cfdfc5c082806d4b4582db8023a3ce034097008622bcbac7236f38"}, - {file = "cytoolz-1.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9502bd9e37779cc9893cbab515a474c2ab6af61ed22ac2f7e16033db18fcaa85"}, - {file = "cytoolz-1.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:364c2fda148def38003b2c86e8adde1d2aab12411dd50872c244a815262e2fda"}, - {file = "cytoolz-1.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9b2e945617325242687189966335e785dc0fae316f4c1825baacf56e5a97e65f"}, - {file = "cytoolz-1.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0f16907fdc724c55b16776bdb7e629deae81d500fe48cfc3861231753b271355"}, - {file = "cytoolz-1.0.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d3206c81ca3ba2d7b8fe78f2e116e3028e721148be753308e88dcbbc370bca52"}, - {file = "cytoolz-1.0.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:becce4b13e110b5ac6b23753dcd0c977f4fdccffa31898296e13fd1109e517e3"}, - {file = "cytoolz-1.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:69a7e5e98fd446079b8b8ec5987aec9a31ec3570a6f494baefa6800b783eaf22"}, - {file = "cytoolz-1.0.0-cp39-cp39-win32.whl", hash = "sha256:b1707b6c3a91676ac83a28a231a14b337dbb4436b937e6b3e4fd44209852a48b"}, - {file = "cytoolz-1.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:11d48b8521ef5fe92e099f4fc00717b5d0789c3c90d5d84031b6d3b17dee1700"}, - {file = "cytoolz-1.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e672712d5dc3094afc6fb346dd4e9c18c1f3c69608ddb8cf3b9f8428f9c26a5c"}, - {file = "cytoolz-1.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86fb208bfb7420e1d0d20065d661310e4a8a6884851d4044f47d37ed4cd7410e"}, - {file = "cytoolz-1.0.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6dbe5fe3b835859fc559eb59bf2775b5a108f7f2cfab0966f3202859d787d8fd"}, - {file = "cytoolz-1.0.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cace092dfda174eed09ed871793beb5b65633963bcda5b1632c73a5aceea1ce"}, - {file = "cytoolz-1.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f7a9d816af3be9725c70efe0a6e4352a45d3877751b395014b8eb2f79d7d8d9d"}, - {file = "cytoolz-1.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:caa7ef840847a23b379e6146760e3a22f15f445656af97e55a435c592125cfa5"}, - {file = "cytoolz-1.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:921082fff09ff6e40c12c87b49be044492b2d6bb01d47783995813b76680c7b2"}, - {file = "cytoolz-1.0.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a32f1356f3b64dda883583383966948604ac69ca0b7fbcf5f28856e5f9133b4e"}, - {file = "cytoolz-1.0.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9af793b1738e4191d15a92e1793f1ffea9f6461022c7b2442f3cb1ea0a4f758a"}, - {file = "cytoolz-1.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:51dfda3983fcc59075c534ce54ca041bb3c80e827ada5d4f25ff7b4049777f94"}, - {file = "cytoolz-1.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:acfb8780c04d29423d14aaab74cd1b7b4beaba32f676e7ace02c9acfbf532aba"}, - {file = "cytoolz-1.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99f39dcc46416dca3eb23664b73187b77fb52cd8ba2ddd8020a292d8f449db67"}, - {file = "cytoolz-1.0.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c0d56b3721977806dcf1a68b0ecd56feb382fdb0f632af1a9fc5ab9b662b32c6"}, - {file = "cytoolz-1.0.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45d346620abc8c83ae634136e700432ad6202faffcc24c5ab70b87392dcda8a1"}, - {file = "cytoolz-1.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:df0c81197fc130de94c09fc6f024a6a19c98ba8fe55c17f1e45ebba2e9229079"}, - {file = "cytoolz-1.0.0.tar.gz", hash = "sha256:eb453b30182152f9917a5189b7d99046b6ce90cdf8aeb0feff4b2683e600defd"}, + {file = "cytoolz-1.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cec9af61f71fc3853eb5dca3d42eb07d1f48a4599fa502cbe92adde85f74b042"}, + {file = "cytoolz-1.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:140bbd649dbda01e91add7642149a5987a7c3ccc251f2263de894b89f50b6608"}, + {file = "cytoolz-1.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e90124bdc42ff58b88cdea1d24a6bc5f776414a314cc4d94f25c88badb3a16d1"}, + {file = "cytoolz-1.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e74801b751e28f7c5cc3ad264c123954a051f546f2fdfe089f5aa7a12ccfa6da"}, + {file = "cytoolz-1.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:582dad4545ddfb5127494ef23f3fa4855f1673a35d50c66f7638e9fb49805089"}, + {file = "cytoolz-1.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd7bd0618e16efe03bd12f19c2a26a27e6e6b75d7105adb7be1cd2a53fa755d8"}, + {file = "cytoolz-1.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d74cca6acf1c4af58b2e4a89cc565ed61c5e201de2e434748c93e5a0f5c541a5"}, + {file = "cytoolz-1.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:823a3763828d8d457f542b2a45d75d6b4ced5e470b5c7cf2ed66a02f508ed442"}, + {file = "cytoolz-1.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:51633a14e6844c61db1d68c1ffd077cf949f5c99c60ed5f1e265b9e2966f1b52"}, + {file = "cytoolz-1.0.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f3ec9b01c45348f1d0d712507d54c2bfd69c62fbd7c9ef555c9d8298693c2432"}, + {file = "cytoolz-1.0.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1855022b712a9c7a5bce354517ab4727a38095f81e2d23d3eabaf1daeb6a3b3c"}, + {file = "cytoolz-1.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9930f7288c4866a1dc1cc87174f0c6ff4cad1671eb1f6306808aa6c445857d78"}, + {file = "cytoolz-1.0.1-cp310-cp310-win32.whl", hash = "sha256:a9baad795d72fadc3445ccd0f122abfdbdf94269157e6d6d4835636dad318804"}, + {file = "cytoolz-1.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:ad95b386a84e18e1f6136f6d343d2509d4c3aae9f5a536f3dc96808fcc56a8cf"}, + {file = "cytoolz-1.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2d958d4f04d9d7018e5c1850790d9d8e68b31c9a2deebca74b903706fdddd2b6"}, + {file = "cytoolz-1.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0f445b8b731fc0ecb1865b8e68a070084eb95d735d04f5b6c851db2daf3048ab"}, + {file = "cytoolz-1.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f546a96460a7e28eb2ec439f4664fa646c9b3e51c6ebad9a59d3922bbe65e30"}, + {file = "cytoolz-1.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0317681dd065532d21836f860b0563b199ee716f55d0c1f10de3ce7100c78a3b"}, + {file = "cytoolz-1.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c0ef52febd5a7821a3fd8d10f21d460d1a3d2992f724ba9c91fbd7a96745d41"}, + {file = "cytoolz-1.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5ebaf419acf2de73b643cf96108702b8aef8e825cf4f63209ceb078d5fbbbfd"}, + {file = "cytoolz-1.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f7f04eeb4088947585c92d6185a618b25ad4a0f8f66ea30c8db83cf94a425e3"}, + {file = "cytoolz-1.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f61928803bb501c17914b82d457c6f50fe838b173fb40d39c38d5961185bd6c7"}, + {file = "cytoolz-1.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d2960cb4fa01ccb985ad1280db41f90dc97a80b397af970a15d5a5de403c8c61"}, + {file = "cytoolz-1.0.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b2b407cc3e9defa8df5eb46644f6f136586f70ba49eba96f43de67b9a0984fd3"}, + {file = "cytoolz-1.0.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8245f929144d4d3bd7b972c9593300195c6cea246b81b4c46053c48b3f044580"}, + {file = "cytoolz-1.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e37385db03af65763933befe89fa70faf25301effc3b0485fec1c15d4ce4f052"}, + {file = "cytoolz-1.0.1-cp311-cp311-win32.whl", hash = "sha256:50f9c530f83e3e574fc95c264c3350adde8145f4f8fc8099f65f00cc595e5ead"}, + {file = "cytoolz-1.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:b7f6b617454b4326af7bd3c7c49b0fc80767f134eb9fd6449917a058d17a0e3c"}, + {file = "cytoolz-1.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fcb8f7d0d65db1269022e7e0428471edee8c937bc288ebdcb72f13eaa67c2fe4"}, + {file = "cytoolz-1.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:207d4e4b445e087e65556196ff472ff134370d9a275d591724142e255f384662"}, + {file = "cytoolz-1.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21cdf6bac6fd843f3b20280a66fd8df20dea4c58eb7214a2cd8957ec176f0bb3"}, + {file = "cytoolz-1.0.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a55ec098036c0dea9f3bdc021f8acd9d105a945227d0811589f0573f21c9ce1"}, + {file = "cytoolz-1.0.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a13ab79ff4ce202e03ab646a2134696988b554b6dc4b71451e948403db1331d8"}, + {file = "cytoolz-1.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2d944799026e1ff08a83241f1027a2d9276c41f7a74224cd98b7df6e03957d"}, + {file = "cytoolz-1.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88ba85834cd523b91fdf10325e1e6d71c798de36ea9bdc187ca7bd146420de6f"}, + {file = "cytoolz-1.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a750b1af7e8bf6727f588940b690d69e25dc47cce5ce467925a76561317eaf7"}, + {file = "cytoolz-1.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44a71870f7eae31d263d08b87da7c2bf1176f78892ed8bdade2c2850478cb126"}, + {file = "cytoolz-1.0.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8231b9abbd8e368e036f4cc2e16902c9482d4cf9e02a6147ed0e9a3cd4a9ab0"}, + {file = "cytoolz-1.0.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:aa87599ccc755de5a096a4d6c34984de6cd9dc928a0c5eaa7607457317aeaf9b"}, + {file = "cytoolz-1.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:67cd16537df51baabde3baa770ab7b8d16839c4d21219d5b96ac59fb012ebd2d"}, + {file = "cytoolz-1.0.1-cp312-cp312-win32.whl", hash = "sha256:fb988c333f05ee30ad4693fe4da55d95ec0bb05775d2b60191236493ea2e01f9"}, + {file = "cytoolz-1.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:8f89c48d8e5aec55ffd566a8ec858706d70ed0c6a50228eca30986bfa5b4da8b"}, + {file = "cytoolz-1.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6944bb93b287032a4c5ca6879b69bcd07df46f3079cf8393958cf0b0454f50c0"}, + {file = "cytoolz-1.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e027260fd2fc5cb041277158ac294fc13dca640714527219f702fb459a59823a"}, + {file = "cytoolz-1.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88662c0e07250d26f5af9bc95911e6137e124a5c1ec2ce4a5d74de96718ab242"}, + {file = "cytoolz-1.0.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:309dffa78b0961b4c0cf55674b828fbbc793cf2d816277a5c8293c0c16155296"}, + {file = "cytoolz-1.0.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:edb34246e6eb40343c5860fc51b24937698e4fa1ee415917a73ad772a9a1746b"}, + {file = "cytoolz-1.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a54da7a8e4348a18d45d4d5bc84af6c716d7f131113a4f1cc45569d37edff1b"}, + {file = "cytoolz-1.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:241c679c3b1913c0f7259cf1d9639bed5084c86d0051641d537a0980548aa266"}, + {file = "cytoolz-1.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5bfc860251a8f280ac79696fc3343cfc3a7c30b94199e0240b6c9e5b6b01a2a5"}, + {file = "cytoolz-1.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c8edd1547014050c1bdad3ff85d25c82bd1c2a3c96830c6181521eb78b9a42b3"}, + {file = "cytoolz-1.0.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b349bf6162e8de215403d7f35f8a9b4b1853dc2a48e6e1a609a5b1a16868b296"}, + {file = "cytoolz-1.0.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1b18b35256219b6c3dd0fa037741b85d0bea39c552eab0775816e85a52834140"}, + {file = "cytoolz-1.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:738b2350f340ff8af883eb301054eb724997f795d20d90daec7911c389d61581"}, + {file = "cytoolz-1.0.1-cp313-cp313-win32.whl", hash = "sha256:9cbd9c103df54fcca42be55ef40e7baea624ac30ee0b8bf1149f21146d1078d9"}, + {file = "cytoolz-1.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:90e577e08d3a4308186d9e1ec06876d4756b1e8164b92971c69739ea17e15297"}, + {file = "cytoolz-1.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f3a509e4ac8e711703c368476b9bbce921fcef6ebb87fa3501525f7000e44185"}, + {file = "cytoolz-1.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a7eecab6373e933dfbf4fdc0601d8fd7614f8de76793912a103b5fccf98170cd"}, + {file = "cytoolz-1.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e55ed62087f6e3e30917b5f55350c3b6be6470b849c6566018419cd159d2cebc"}, + {file = "cytoolz-1.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43de33d99a4ccc07234cecd81f385456b55b0ea9c39c9eebf42f024c313728a5"}, + {file = "cytoolz-1.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:139bed875828e1727018aa0982aa140e055cbafccb7fd89faf45cbb4f2a21514"}, + {file = "cytoolz-1.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22c12671194b518aa8ce2f4422bd5064f25ab57f410ba0b78705d0a219f4a97a"}, + {file = "cytoolz-1.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79888f2f7dc25709cd5d37b032a8833741e6a3692c8823be181d542b5999128e"}, + {file = "cytoolz-1.0.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:51628b4eb41fa25bd428f8f7b5b74fbb05f3ae65fbd265019a0dd1ded4fdf12a"}, + {file = "cytoolz-1.0.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:1db9eb7179285403d2fb56ba1ff6ec35a44921b5e2fa5ca19d69f3f9f0285ea5"}, + {file = "cytoolz-1.0.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:08ab7efae08e55812340bfd1b3f09f63848fe291675e2105eab1aa5327d3a16e"}, + {file = "cytoolz-1.0.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e5fdc5264f884e7c0a1711a81dff112708a64b9c8561654ee578bfdccec6be09"}, + {file = "cytoolz-1.0.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:90d6a2e6ab891043ee655ec99d5e77455a9bee9e1131bdfcfb745edde81200dd"}, + {file = "cytoolz-1.0.1-cp38-cp38-win32.whl", hash = "sha256:08946e083faa5147751b34fbf78ab931f149ef758af5c1092932b459e18dcf5c"}, + {file = "cytoolz-1.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:a91b4e10a9c03796c0dc93e47ebe25bb41ecc6fafc3cf5197c603cf767a3d44d"}, + {file = "cytoolz-1.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:980c323e626ba298b77ae62871b2de7c50b9d7219e2ddf706f52dd34b8be7349"}, + {file = "cytoolz-1.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:45f6fa1b512bc2a0f2de5123db932df06c7f69d12874fe06d67772b2828e2c8b"}, + {file = "cytoolz-1.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f93f42d9100c415155ad1f71b0de362541afd4ac95e3153467c4c79972521b6b"}, + {file = "cytoolz-1.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a76d20dec9c090cdf4746255bbf06a762e8cc29b5c9c1d138c380bbdb3122ade"}, + {file = "cytoolz-1.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:239039585487c69aa50c5b78f6a422016297e9dea39755761202fb9f0530fe87"}, + {file = "cytoolz-1.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c28307640ca2ab57b9fbf0a834b9bf563958cd9e038378c3a559f45f13c3c541"}, + {file = "cytoolz-1.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:454880477bb901cee3a60f6324ec48c95d45acc7fecbaa9d49a5af737ded0595"}, + {file = "cytoolz-1.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:902115d1b1f360fd81e44def30ac309b8641661150fcbdde18ead446982ada6a"}, + {file = "cytoolz-1.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e68e6b38473a3a79cee431baa22be31cac39f7df1bf23eaa737eaff42e213883"}, + {file = "cytoolz-1.0.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:32fba3f63fcb76095b0a22f4bdcc22bc62a2bd2d28d58bf02fd21754c155a3ec"}, + {file = "cytoolz-1.0.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:0724ba4cf41eb40b6cf75250820ab069e44bdf4183ff78857aaf4f0061551075"}, + {file = "cytoolz-1.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c42420e0686f887040d5230420ed44f0e960ccbfa29a0d65a3acd9ca52459209"}, + {file = "cytoolz-1.0.1-cp39-cp39-win32.whl", hash = "sha256:4ba8b16358ea56b1fe8e637ec421e36580866f2e787910bac1cf0a6997424a34"}, + {file = "cytoolz-1.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:92d27f84bf44586853d9562bfa3610ecec000149d030f793b4cb614fd9da1813"}, + {file = "cytoolz-1.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:83d19d55738ad9c60763b94f3f6d3c6e4de979aeb8d76841c1401081e0e58d96"}, + {file = "cytoolz-1.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f112a71fad6ea824578e6393765ce5c054603afe1471a5c753ff6c67fd872d10"}, + {file = "cytoolz-1.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a515df8f8aa6e1eaaf397761a6e4aff2eef73b5f920aedf271416d5471ae5ee"}, + {file = "cytoolz-1.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92c398e7b7023460bea2edffe5fcd0a76029580f06c3f6938ac3d198b47156f3"}, + {file = "cytoolz-1.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3237e56211e03b13df47435b2369f5df281e02b04ad80a948ebd199b7bc10a47"}, + {file = "cytoolz-1.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba0d1da50aab1909b165f615ba1125c8b01fcc30d606c42a61c42ea0269b5e2c"}, + {file = "cytoolz-1.0.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25b6e8dec29aa5a390092d193abd673e027d2c0b50774ae816a31454286c45c7"}, + {file = "cytoolz-1.0.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36cd6989ebb2f18fe9af8f13e3c61064b9f741a40d83dc5afeb0322338ad25f2"}, + {file = "cytoolz-1.0.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47394f8ab7fca3201f40de61fdeea20a2baffb101485ae14901ea89c3f6c95d"}, + {file = "cytoolz-1.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d00ac423542af944302e034e618fb055a0c4e87ba704cd6a79eacfa6ac83a3c9"}, + {file = "cytoolz-1.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a5ca923d1fa632f7a4fb33c0766c6fba7f87141a055c305c3e47e256fb99c413"}, + {file = "cytoolz-1.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:058bf996bcae9aad3acaeeb937d42e0c77c081081e67e24e9578a6a353cb7fb2"}, + {file = "cytoolz-1.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69e2a1f41a3dad94a17aef4a5cc003323359b9f0a9d63d4cc867cb5690a2551d"}, + {file = "cytoolz-1.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67daeeeadb012ec2b59d63cb29c4f2a2023b0c4957c3342d354b8bb44b209e9a"}, + {file = "cytoolz-1.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:54d3d36bbf0d4344d1afa22c58725d1668e30ff9de3a8f56b03db1a6da0acb11"}, + {file = "cytoolz-1.0.1.tar.gz", hash = "sha256:89cc3161b89e1bb3ed7636f74ed2e55984fd35516904fc878cae216e42b2c7d6"}, ] [package.dependencies] @@ -778,6 +838,17 @@ files = [ {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, ] +[[package]] +name = "distlib" +version = "0.3.9" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, +] + [[package]] name = "docker-pycreds" version = "0.4.0" @@ -891,6 +962,20 @@ docs = ["sphinx (>=5.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)" lint = ["black (>=23)", "flake8 (==3.8.3)", "isort (>=5.11.0)", "mypy (==0.971)", "pydocstyle (>=5.0.0)", "types-setuptools"] test = ["hypothesis (>=4.43.0)", "mypy (==0.971)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "types-setuptools"] +[[package]] +name = "eval-type-backport" +version = "0.2.0" +description = "Like `typing._eval_type`, but lets older Python versions use newer typing features." +optional = false +python-versions = ">=3.8" +files = [ + {file = "eval_type_backport-0.2.0-py3-none-any.whl", hash = "sha256:ac2f73d30d40c5a30a80b8739a789d6bb5e49fdffa66d7912667e2015d9c9933"}, + {file = "eval_type_backport-0.2.0.tar.gz", hash = "sha256:68796cfbc7371ebf923f03bdf7bef415f3ec098aeced24e054b253a0e78f7b37"}, +] + +[package.extras] +tests = ["pytest"] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -905,6 +990,28 @@ files = [ [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "exchange-calendars" +version = "4.5.6" +description = "Calendars for securities exchanges" +optional = false +python-versions = "~=3.9" +files = [ + {file = "exchange_calendars-4.5.6-py3-none-any.whl", hash = "sha256:5abf5ebcb8ceef0ced36fe4e20071d42517091bf081e6c44354cb343009d672b"}, + {file = "exchange_calendars-4.5.6.tar.gz", hash = "sha256:5db77178cf849f81dd6dcc99995e2163b928c0f45dcd0a2c395958beb1dbb145"}, +] + +[package.dependencies] +korean-lunar-calendar = "*" +numpy = "*" +pandas = ">=1.5" +pyluach = "*" +toolz = "*" +tzdata = "*" + +[package.extras] +dev = ["flake8", "hypothesis", "pip-tools", "pytest", "pytest-benchmark", "pytest-xdist"] + [[package]] name = "fastapi" version = "0.110.3" @@ -940,6 +1047,22 @@ docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2. testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] typing = ["typing-extensions (>=4.12.2)"] +[[package]] +name = "flake8" +version = "7.1.1" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"}, + {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.12.0,<2.13.0" +pyflakes = ">=3.2.0,<3.3.0" + [[package]] name = "flatbuffers" version = "24.3.25" @@ -1350,13 +1473,13 @@ lxml = ["lxml"] [[package]] name = "huggingface-hub" -version = "0.22.2" +version = "0.27.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.22.2-py3-none-any.whl", hash = "sha256:3429e25f38ccb834d310804a3b711e7e4953db5a9e420cc147a5e194ca90fd17"}, - {file = "huggingface_hub-0.22.2.tar.gz", hash = "sha256:32e9a9a6843c92f253ff9ca16b9985def4d80a93fb357af5353f770ef74a81be"}, + {file = "huggingface_hub-0.27.0-py3-none-any.whl", hash = "sha256:8f2e834517f1f1ddf1ecc716f91b120d7333011b7485f665a9a412eacb1a2a81"}, + {file = "huggingface_hub-0.27.0.tar.gz", hash = "sha256:902cce1a1be5739f5589e560198a65a8edcfd3b830b1666f36e4b961f0454fac"}, ] [package.dependencies] @@ -1369,19 +1492,33 @@ tqdm = ">=4.42.1" typing-extensions = ">=3.7.4.3" [package.extras] -all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] cli = ["InquirerPy (==0.3.4)"] -dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] hf-transfer = ["hf-transfer (>=0.1.4)"] -inference = ["aiohttp", "minijinja (>=1.0)"] -quality = ["mypy (==1.5.1)", "ruff (>=0.3.0)"] +inference = ["aiohttp"] +quality = ["libcst (==1.4.0)", "mypy (==1.5.1)", "ruff (>=0.5.0)"] tensorflow = ["graphviz", "pydot", "tensorflow"] tensorflow-testing = ["keras (<3.0)", "tensorflow"] -testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] -torch = ["safetensors", "torch"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +torch = ["safetensors[torch]", "torch"] typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] +[[package]] +name = "identify" +version = "2.6.3" +description = "File identification library for Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd"}, + {file = "identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02"}, +] + +[package.extras] +license = ["ukkonen"] + [[package]] name = "idna" version = "3.10" @@ -1434,6 +1571,20 @@ files = [ docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] testing = ["pytest (>=3.5,!=3.7.3)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=1.2.3)", "pytest-cov", "pytest-enabler", "pytest-flake8", "pytest-mypy"] +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + [[package]] name = "jinja2" version = "3.1.4" @@ -1451,6 +1602,28 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, +] + +[[package]] +name = "joblib" +version = "1.4.2" +description = "Lightweight pipelining with Python functions" +optional = false +python-versions = ">=3.8" +files = [ + {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, + {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, +] + [[package]] name = "keras" version = "3.7.0" @@ -1472,6 +1645,17 @@ optree = "*" packaging = "*" rich = "*" +[[package]] +name = "korean-lunar-calendar" +version = "0.3.1" +description = "Korean Lunar Calendar" +optional = false +python-versions = "*" +files = [ + {file = "korean_lunar_calendar-0.3.1-py3-none-any.whl", hash = "sha256:392757135c492c4f42a604e6038042953c35c6f449dda5f27e3f86a7f9c943e5"}, + {file = "korean_lunar_calendar-0.3.1.tar.gz", hash = "sha256:eb2c485124a061016926bdea6d89efdf9b9fdbf16db55895b6cf1e5bec17b857"}, +] + [[package]] name = "levenshtein" version = "0.26.1" @@ -1591,6 +1775,24 @@ files = [ {file = "libclang-18.1.1.tar.gz", hash = "sha256:a1214966d08d73d971287fc3ead8dfaf82eb07fb197680d8b3859dbbbbf78250"}, ] +[[package]] +name = "loguru" +version = "0.7.3" +description = "Python logging made (stupidly) simple" +optional = false +python-versions = "<4.0,>=3.5" +files = [ + {file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"}, + {file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} +win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} + +[package.extras] +dev = ["Sphinx (==8.1.3)", "build (==1.2.2)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.5.0)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.13.0)", "mypy (==v1.4.1)", "myst-parser (==4.0.0)", "pre-commit (==4.0.1)", "pytest (==6.1.2)", "pytest (==8.3.2)", "pytest-cov (==2.12.1)", "pytest-cov (==5.0.0)", "pytest-cov (==6.0.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.1.0)", "sphinx-rtd-theme (==3.0.2)", "tox (==3.27.1)", "tox (==4.23.2)", "twine (==6.0.1)"] + [[package]] name = "lxml" version = "5.3.0" @@ -1857,6 +2059,17 @@ files = [ {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + [[package]] name = "mdurl" version = "0.1.2" @@ -1915,6 +2128,23 @@ files = [ {file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"}, ] +[[package]] +name = "mpmath" +version = "1.3.0" +description = "Python library for arbitrary-precision floating-point arithmetic" +optional = false +python-versions = "*" +files = [ + {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, + {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, +] + +[package.extras] +develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] +docs = ["sphinx"] +gmpy = ["gmpy2 (>=2.1.0a4)"] +tests = ["pytest (>=4.6)"] + [[package]] name = "msgpack" version = "1.1.0" @@ -2136,6 +2366,70 @@ six = "*" testing = ["astroid (>=1.5.3,<1.6.0)", "astroid (>=2.0)", "coverage", "pylint (>=1.7.2,<1.8.0)", "pylint (>=2.3.1,<2.4.0)", "pytest"] yaml = ["PyYAML (>=5.1.0)"] +[[package]] +name = "mypy" +version = "1.13.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + [[package]] name = "namex" version = "0.0.8" @@ -2172,6 +2466,35 @@ files = [ [package.extras] nicer-shell = ["ipython"] +[[package]] +name = "networkx" +version = "3.2.1" +description = "Python package for creating and manipulating graphs and networks" +optional = false +python-versions = ">=3.9" +files = [ + {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"}, + {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"}, +] + +[package.extras] +default = ["matplotlib (>=3.5)", "numpy (>=1.22)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"] +developer = ["changelist (==0.4)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] +doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"] +extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"] +test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] + +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + [[package]] name = "numpy" version = "1.26.4" @@ -2217,6 +2540,161 @@ files = [ {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] +[[package]] +name = "nvidia-cublas-cu12" +version = "12.4.5.8" +description = "CUBLAS native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0f8aa1706812e00b9f19dfe0cdb3999b092ccb8ca168c0db5b8ea712456fd9b3"}, + {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b"}, + {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-win_amd64.whl", hash = "sha256:5a796786da89203a0657eda402bcdcec6180254a8ac22d72213abc42069522dc"}, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.4.127" +description = "CUDA profiling tools runtime libs." +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:79279b35cf6f91da114182a5ce1864997fd52294a87a16179ce275773799458a"}, + {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb"}, + {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:5688d203301ab051449a2b1cb6690fbe90d2b372f411521c86018b950f3d7922"}, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.4.127" +description = "NVRTC native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0eedf14185e04b76aa05b1fea04133e59f465b6f960c0cbf4e37c3cb6b0ea198"}, + {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338"}, + {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:a961b2f1d5f17b14867c619ceb99ef6fcec12e46612711bcec78eb05068a60ec"}, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.4.127" +description = "CUDA Runtime native Libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:961fe0e2e716a2a1d967aab7caee97512f71767f852f67432d572e36cb3a11f3"}, + {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5"}, + {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:09c2e35f48359752dfa822c09918211844a3d93c100a715d79b59591130c5e1e"}, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.1.0.70" +description = "cuDNN runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f"}, + {file = "nvidia_cudnn_cu12-9.1.0.70-py3-none-win_amd64.whl", hash = "sha256:6278562929433d68365a07a4a1546c237ba2849852c0d4b2262a486e805b977a"}, +] + +[package.dependencies] +nvidia-cublas-cu12 = "*" + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.2.1.3" +description = "CUFFT native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5dad8008fc7f92f5ddfa2101430917ce2ffacd86824914c82e28990ad7f00399"}, + {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9"}, + {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-win_amd64.whl", hash = "sha256:d802f4954291101186078ccbe22fc285a902136f974d369540fd4a5333d1440b"}, +] + +[package.dependencies] +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.5.147" +description = "CURAND native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1f173f09e3e3c76ab084aba0de819c49e56614feae5c12f69883f4ae9bb5fad9"}, + {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b"}, + {file = "nvidia_curand_cu12-10.3.5.147-py3-none-win_amd64.whl", hash = "sha256:f307cc191f96efe9e8f05a87096abc20d08845a841889ef78cb06924437f6771"}, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.6.1.9" +description = "CUDA solver native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d338f155f174f90724bbde3758b7ac375a70ce8e706d70b018dd3375545fc84e"}, + {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260"}, + {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-win_amd64.whl", hash = "sha256:e77314c9d7b694fcebc84f58989f3aa4fb4cb442f12ca1a9bde50f5e8f6d1b9c"}, +] + +[package.dependencies] +nvidia-cublas-cu12 = "*" +nvidia-cusparse-cu12 = "*" +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.3.1.170" +description = "CUSPARSE native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9d32f62896231ebe0480efd8a7f702e143c98cfaa0e8a76df3386c1ba2b54df3"}, + {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1"}, + {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-win_amd64.whl", hash = "sha256:9bc90fb087bc7b4c15641521f31c0371e9a612fc2ba12c338d3ae032e6b6797f"}, +] + +[package.dependencies] +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.21.5" +description = "NVIDIA Collective Communication Library (NCCL) Runtime" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0"}, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.4.127" +description = "Nvidia JIT LTO Library" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4abe7fef64914ccfa909bc2ba39739670ecc9e820c83ccc7a6ed414122599b83"}, + {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57"}, + {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:fd9020c501d27d135f983c6d3e244b197a7ccad769e34df53a42e276b0e25fa1"}, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.4.127" +description = "NVIDIA Tools Extension" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7959ad635db13edf4fc65c06a6e9f9e55fc2f92596db928d169c0bb031e88ef3"}, + {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a"}, + {file = "nvidia_nvtx_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:641dccaaa1139f3ffb0d3164b4b84f9d253397e38246a4f2f36728b48566d485"}, +] + [[package]] name = "opt-einsum" version = "3.4.0" @@ -2433,6 +2911,26 @@ sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-d test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] xml = ["lxml (>=4.9.2)"] +[[package]] +name = "pandas-market-calendars" +version = "4.4.2" +description = "Market and exchange trading calendars for pandas" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pandas_market_calendars-4.4.2-py3-none-any.whl", hash = "sha256:52a0fea562113d511f3f1ae372e2a86e4a37147dacec9644094ff6f88aee8d53"}, + {file = "pandas_market_calendars-4.4.2.tar.gz", hash = "sha256:4261a2c065565de1cd3646982b2e206e1069714b8140878dd6eba972546dfbcb"}, +] + +[package.dependencies] +exchange-calendars = ">=3.3" +pandas = ">=1.1" +python-dateutil = "*" +pytz = "*" + +[package.extras] +dev = ["black", "pre-commit", "pytest"] + [[package]] name = "password-strength" version = "0.0.3.post2" @@ -2447,6 +2945,17 @@ files = [ [package.dependencies] six = "*" +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + [[package]] name = "peewee" version = "3.17.8" @@ -2457,6 +2966,40 @@ files = [ {file = "peewee-3.17.8.tar.gz", hash = "sha256:ce1d05db3438830b989a1b9d0d0aa4e7f6134d5f6fd57686eeaa26a3e6485a8c"}, ] +[[package]] +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + +[[package]] +name = "pre-commit" +version = "4.0.1" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"}, + {file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + [[package]] name = "pre-commit-hooks" version = "5.0.0" @@ -2565,22 +3108,22 @@ files = [ [[package]] name = "protobuf" -version = "4.25.5" +version = "5.29.2" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.5-cp310-abi3-win32.whl", hash = "sha256:5e61fd921603f58d2f5acb2806a929b4675f8874ff5f330b7d6f7e2e784bbcd8"}, - {file = "protobuf-4.25.5-cp310-abi3-win_amd64.whl", hash = "sha256:4be0571adcbe712b282a330c6e89eae24281344429ae95c6d85e79e84780f5ea"}, - {file = "protobuf-4.25.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:b2fde3d805354df675ea4c7c6338c1aecd254dfc9925e88c6d31a2bcb97eb173"}, - {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:919ad92d9b0310070f8356c24b855c98df2b8bd207ebc1c0c6fcc9ab1e007f3d"}, - {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fe14e16c22be926d3abfcb500e60cab068baf10b542b8c858fa27e098123e331"}, - {file = "protobuf-4.25.5-cp38-cp38-win32.whl", hash = "sha256:98d8d8aa50de6a2747efd9cceba361c9034050ecce3e09136f90de37ddba66e1"}, - {file = "protobuf-4.25.5-cp38-cp38-win_amd64.whl", hash = "sha256:b0234dd5a03049e4ddd94b93400b67803c823cfc405689688f59b34e0742381a"}, - {file = "protobuf-4.25.5-cp39-cp39-win32.whl", hash = "sha256:abe32aad8561aa7cc94fc7ba4fdef646e576983edb94a73381b03c53728a626f"}, - {file = "protobuf-4.25.5-cp39-cp39-win_amd64.whl", hash = "sha256:7a183f592dc80aa7c8da7ad9e55091c4ffc9497b3054452d629bb85fa27c2a45"}, - {file = "protobuf-4.25.5-py3-none-any.whl", hash = "sha256:0aebecb809cae990f8129ada5ca273d9d670b76d9bfc9b1809f0a9c02b7dbf41"}, - {file = "protobuf-4.25.5.tar.gz", hash = "sha256:7f8249476b4a9473645db7f8ab42b02fe1488cbe5fb72fddd445e0665afd8584"}, + {file = "protobuf-5.29.2-cp310-abi3-win32.whl", hash = "sha256:c12ba8249f5624300cf51c3d0bfe5be71a60c63e4dcf51ffe9a68771d958c851"}, + {file = "protobuf-5.29.2-cp310-abi3-win_amd64.whl", hash = "sha256:842de6d9241134a973aab719ab42b008a18a90f9f07f06ba480df268f86432f9"}, + {file = "protobuf-5.29.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a0c53d78383c851bfa97eb42e3703aefdc96d2036a41482ffd55dc5f529466eb"}, + {file = "protobuf-5.29.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:494229ecd8c9009dd71eda5fd57528395d1eacdf307dbece6c12ad0dd09e912e"}, + {file = "protobuf-5.29.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:b6b0d416bbbb9d4fbf9d0561dbfc4e324fd522f61f7af0fe0f282ab67b22477e"}, + {file = "protobuf-5.29.2-cp38-cp38-win32.whl", hash = "sha256:e621a98c0201a7c8afe89d9646859859be97cb22b8bf1d8eacfd90d5bda2eb19"}, + {file = "protobuf-5.29.2-cp38-cp38-win_amd64.whl", hash = "sha256:13d6d617a2a9e0e82a88113d7191a1baa1e42c2cc6f5f1398d3b054c8e7e714a"}, + {file = "protobuf-5.29.2-cp39-cp39-win32.whl", hash = "sha256:36000f97ea1e76e8398a3f02936aac2a5d2b111aae9920ec1b769fc4a222c4d9"}, + {file = "protobuf-5.29.2-cp39-cp39-win_amd64.whl", hash = "sha256:2d2e674c58a06311c8e99e74be43e7f3a8d1e2b2fdf845eaa347fbd866f23355"}, + {file = "protobuf-5.29.2-py3-none-any.whl", hash = "sha256:fde4554c0e578a5a0bcc9a276339594848d1e89f9ea47b4427c80e5d72f90181"}, + {file = "protobuf-5.29.2.tar.gz", hash = "sha256:b2cc8e8bb7c9326996f0e160137b0861f1a82162502658df2951209d0cb0309e"}, ] [[package]] @@ -2626,196 +3169,197 @@ files = [ [[package]] name = "py-bip39-bindings" -version = "0.1.12" +version = "0.2.0" description = "Python bindings for tiny-bip39 RUST crate" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "py_bip39_bindings-0.1.12-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:830a1cd07b46f84d07b9807ea1e378e947d95f9f69a1b4acc5012cf2e9336e47"}, - {file = "py_bip39_bindings-0.1.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad84b1ca46329f23c808889d97bd79cc8a2c48e0b9fbf0ecefd16ba8c4e54761"}, - {file = "py_bip39_bindings-0.1.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3772e233a03a322cd7d6587c393f24a6f98059fbeb723b05c536489671a9ae8a"}, - {file = "py_bip39_bindings-0.1.12-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f28937f25cc9fcae37992460be1281e8d55973f8998c7d72e7b5047d3ead375a"}, - {file = "py_bip39_bindings-0.1.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:332d6340201341dd87b1d5764ba40fd3b011b1f152145104d733c378a2af2970"}, - {file = "py_bip39_bindings-0.1.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79daf697315084d1f91e865fae042e067c818c790dfdc6d2e65b8f9d119fc703"}, - {file = "py_bip39_bindings-0.1.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7764520c8b347ff4f3ead955f2044644ab89dc82e61f3bf5b9baa45ff03b389b"}, - {file = "py_bip39_bindings-0.1.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:09ca55cde30aea939c86cdadca13d7b500da414070b95fc5bf93ebdb0ec14cd7"}, - {file = "py_bip39_bindings-0.1.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6e4e36c2474b946dab0c1019ac38be797073d9336e4735d574cd38cc506e5857"}, - {file = "py_bip39_bindings-0.1.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:534609eef941e401aaba7fc54903e219bbe9bd652d02df1d275f242e28833aa0"}, - {file = "py_bip39_bindings-0.1.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:260b61bbbed75afe4f5e2caeaefa0d844650c5bcde02dadbd963d73253554475"}, - {file = "py_bip39_bindings-0.1.12-cp310-none-win32.whl", hash = "sha256:b13e44670e34e2d28d1856bba82a362ed4b84822c73bdd23f57e941da0f7d489"}, - {file = "py_bip39_bindings-0.1.12-cp310-none-win_amd64.whl", hash = "sha256:09a1ed423242dccd5016eec18e74c80236d468ef2e85d3297615ddb4e792cd77"}, - {file = "py_bip39_bindings-0.1.12-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:200ce6173c330af830177944d18402b4861f1dd1b52a410a6c02d95f69f04b92"}, - {file = "py_bip39_bindings-0.1.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:808f1042c31e8373bcd56be45fbebbeae2ab2e90b91178bc4d5cfad13f235f34"}, - {file = "py_bip39_bindings-0.1.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da4c11662304beb093dfaa24f5a3b036d4e76c21de7dafd2bd43d0e78325b377"}, - {file = "py_bip39_bindings-0.1.12-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4af354d2e138304c20fffa0820aa79f9b4ee4de3f1db4e4b77cd0fdf72c39fc2"}, - {file = "py_bip39_bindings-0.1.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a337fd6eaf16bf8c42752d9a25fd58bb303499da5d6428b466eff7ddb642237"}, - {file = "py_bip39_bindings-0.1.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d72041b34d6a7ba5e83009cb96c59c48b356eb3831a0ab627421e1fcdec83c9a"}, - {file = "py_bip39_bindings-0.1.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e48ab058ac4cfdcb6bdaefc8bb555a6d36b11673589332d6336495d388a4902"}, - {file = "py_bip39_bindings-0.1.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cbd4865d3ef310fbad03197511423376ff8fea162d8d14c4259ee086be249b74"}, - {file = "py_bip39_bindings-0.1.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:b7eafc0ab28d5835789abc82d2443aec7154c0bca8b627597ab7b4761bb3d8ac"}, - {file = "py_bip39_bindings-0.1.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:97e739430d3b7801a4afd0e767c82554b92ba727b52c8c3a1ed9f9b676c176e3"}, - {file = "py_bip39_bindings-0.1.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f6c3a446e7cb339f8d58659bf0bbf8b549482c2eefc37c7573b84150de4423d7"}, - {file = "py_bip39_bindings-0.1.12-cp311-none-win32.whl", hash = "sha256:6e9c09a916ac890ea629ad8334b02e43ce7d1887426d74c7c69f82d0820f66db"}, - {file = "py_bip39_bindings-0.1.12-cp311-none-win_amd64.whl", hash = "sha256:f3480cb2e99b7d0ffae676d2d59a844623cee4c73fbed5c9bb3069b9a9845859"}, - {file = "py_bip39_bindings-0.1.12-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a65facd53ff11314a1e2411fced145daa3a07448c40d8aff87b22dec703d631c"}, - {file = "py_bip39_bindings-0.1.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd1490d168409763b846df691e14756c1461b1edf62343ad72c4b1e00918798d"}, - {file = "py_bip39_bindings-0.1.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21bc7bfb907ebb4805b83f4e674dbe2745dd505d22472eefb65dec09c679a718"}, - {file = "py_bip39_bindings-0.1.12-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1aa1cf5a76ea8280f2f684593f1456d8f459f200f13e7e1c351e27001310ae1"}, - {file = "py_bip39_bindings-0.1.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:950a127bf0a71be0a163bcce16d9cbb227e80da4f12ffcc0effecd9743c806d7"}, - {file = "py_bip39_bindings-0.1.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7ad2ed21601f40037e912e87725bb3e3aef1635fdd84eae21d5bbb0034d5727"}, - {file = "py_bip39_bindings-0.1.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aed015ca58ae7e9912e7cead85feba9ce7ccabd6605d76c436615fbc035278a5"}, - {file = "py_bip39_bindings-0.1.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f5f6aab32cd3cc0fd7fb72247bada0cb269c3fc120dbdf12cfa06af1e5d3243"}, - {file = "py_bip39_bindings-0.1.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d4f62d125f9cd9a3bf183d1cc3d0ff794e78185098c61c6392221a00e15dc4c7"}, - {file = "py_bip39_bindings-0.1.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:25f3f6ab788d14a56691cbef8ad3345040c358fced832338b81fb87e9951ea5e"}, - {file = "py_bip39_bindings-0.1.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2fdf3c4d3acbb772c5e90da1f24db39b889d9e69b442925ebe56244a89fc24f9"}, - {file = "py_bip39_bindings-0.1.12-cp312-none-win32.whl", hash = "sha256:c7af72b8afcac625d69acd40f714963725d8078bee57c36844ae1b924d6490d2"}, - {file = "py_bip39_bindings-0.1.12-cp312-none-win_amd64.whl", hash = "sha256:1dedf9899cb9cc6000373858ea9d7069ff4d961d4c53f6624496ff31fb4398f8"}, - {file = "py_bip39_bindings-0.1.12-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fc0b89d9075e7953512d06e02bc8865e7da4cf7061e8454a8b42d6030a06e06"}, - {file = "py_bip39_bindings-0.1.12-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8311c96ecf7924d57170159c984fc638f2a57ae79eef23799d4f3f8fb0ea0be6"}, - {file = "py_bip39_bindings-0.1.12-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3ad284923e70ceeb5dc2e75bc8773c0ad52ca41aca34ec92b81d9b370b9a5"}, - {file = "py_bip39_bindings-0.1.12-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:472af4061746f3df6933271b919c53ef28d7cfa98801888ef4f1a97559153c19"}, - {file = "py_bip39_bindings-0.1.12-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f6e3a3528a3b00113f491ec63b38a1994e7439372a88419926a5671098e69eb0"}, - {file = "py_bip39_bindings-0.1.12-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:5d9bd8b18ff5ada4890425103fc404021bbf10b4fed9f42cb2f9ccbc7048314f"}, - {file = "py_bip39_bindings-0.1.12-cp37-cp37m-musllinux_1_2_armv7l.whl", hash = "sha256:2a49972d462b0617d478b1b38a44eb6ce6e4752d6f76df79421693ac21a06039"}, - {file = "py_bip39_bindings-0.1.12-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:e15994ac3507a77e86218e286cdacc3b5c3439effcf8b6b9f56be81c889c7654"}, - {file = "py_bip39_bindings-0.1.12-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:44ad459b210b0b3e834878a92e5ab52662c544cfc10ba225db7fd34efb6b40fe"}, - {file = "py_bip39_bindings-0.1.12-cp37-none-win32.whl", hash = "sha256:74b7505ceb5cda18cd2ff3c715b19ce160ae4e303d4d4c1081c93cc11601cd11"}, - {file = "py_bip39_bindings-0.1.12-cp37-none-win_amd64.whl", hash = "sha256:aada0bb86b457d08cd4475f90b8e0ebd05b05a1c67da0507b0a3050a31e39f76"}, - {file = "py_bip39_bindings-0.1.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e37498866fe2cb7dfb2ca1c5fefb45257ed298d3f6d10584497f2851441c975"}, - {file = "py_bip39_bindings-0.1.12-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f9bc513f94a93bff54b8941ca85666fa2e476a6a430ca68bcea0767342225d9"}, - {file = "py_bip39_bindings-0.1.12-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:24ebfd63786707b6be86bb34478e0f14200e2b046722601001552f5e997f12ce"}, - {file = "py_bip39_bindings-0.1.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fedcf19d9a72670400b417173f239a3e455b3ea40e06398a2d9f11970358ceb5"}, - {file = "py_bip39_bindings-0.1.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db374557720ceb6e42a976395d9b4d53f6c91ba7e372d3538c79e2369f7a98bc"}, - {file = "py_bip39_bindings-0.1.12-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:96e8b7035077026fa6c36d09195cc7b65be0a47a8dcc947c8482e4ee6f45cef1"}, - {file = "py_bip39_bindings-0.1.12-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:5f5dbb28b583d324f693d5a3e0e15a995788298e530dfcbd367921a471587698"}, - {file = "py_bip39_bindings-0.1.12-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5d209d95e6d675c7d5d335d96987fe4a9d4b2d7e30ae2266406915a6479bbc2d"}, - {file = "py_bip39_bindings-0.1.12-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:78b2e516449321761314f4490013be21ed1d70a156a742f5875d4d81bc78a36a"}, - {file = "py_bip39_bindings-0.1.12-cp38-none-win32.whl", hash = "sha256:4f5d1fe3e31a98d2563267da9f3c7342187d062e8ba5e47203e62c5afab7b9b2"}, - {file = "py_bip39_bindings-0.1.12-cp38-none-win_amd64.whl", hash = "sha256:5199fc82b703a2e68957ba8e190772cb70cdb11fc0276e9b4a756b572122a137"}, - {file = "py_bip39_bindings-0.1.12-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bf359da1ac1648f851754ddf230ab228986653d87af7ade00e11d7ee59feba14"}, - {file = "py_bip39_bindings-0.1.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:596c266afc617de543153ee95184c1ff54e355194a44bc0be962798a197cf367"}, - {file = "py_bip39_bindings-0.1.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a524054b860730d76b4bf81a3f71ea82e1e74bb0dd5192019b06a31c80393e1"}, - {file = "py_bip39_bindings-0.1.12-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11c43577a8a5cc55e88b07507fd9bde7885b948d1319c3850945b135a54b4285"}, - {file = "py_bip39_bindings-0.1.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ee82d050e97cce6ac8298bb77119e9aaff2a51feec07e6682fd3b8dba405cf7"}, - {file = "py_bip39_bindings-0.1.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ae80c05dbe60d55691ee948927e80bef4e846c652735c639c571578e1686c1"}, - {file = "py_bip39_bindings-0.1.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3999ce6d43cb3ea9d23b41883f2284326647604a91dd23030973d345691838f2"}, - {file = "py_bip39_bindings-0.1.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6d0b160e9999e331e574135784136f500924a36d57d0c738a778b09abc36a495"}, - {file = "py_bip39_bindings-0.1.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:8c202356183a467307930a8da21c6a1e6a2a4ce4841805dcfec0629229265ac4"}, - {file = "py_bip39_bindings-0.1.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7b0f6f92da76fec954c3199ff94c6ae080e174ee33b7241fe75d921d5c9954ef"}, - {file = "py_bip39_bindings-0.1.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6fef2cb2525609d2881685e5ea383d93ba1a1aa2e8c2d94fb72d8e005fafe70c"}, - {file = "py_bip39_bindings-0.1.12-cp39-none-win32.whl", hash = "sha256:89b7c4a1194dc2b1edbd05532d6ba193ad5c0ada03d5a0a78744ef723007ceb5"}, - {file = "py_bip39_bindings-0.1.12-cp39-none-win_amd64.whl", hash = "sha256:43d831223bc76cdee33ef5668a736db098a0c20e80f9e10bd2574253ec8a5e5c"}, - {file = "py_bip39_bindings-0.1.12-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b60e46ecd3bee23f4e458303ace7c86c592cff2d28860808a5e46398f915035a"}, - {file = "py_bip39_bindings-0.1.12-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:71afa142cf045b4d7743a96aef66f030342a8f287a83733c6841d3b005954c52"}, - {file = "py_bip39_bindings-0.1.12-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c65ba92ec43ef78c5926506437b62d959ac4e343f8bf2cfc8ed799c3a6495f23"}, - {file = "py_bip39_bindings-0.1.12-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf3a5ec5095f56dbf0ab8258d722d860746e807c5ace99ea5ab92000268a750b"}, - {file = "py_bip39_bindings-0.1.12-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6232b0b8dfecd73bbc4dc4865f1c0b10d00df63dc19ad7448b5e5e45fdd279e7"}, - {file = "py_bip39_bindings-0.1.12-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:fd13f3ba45ea56f2db85a66c1d8b03337ec8977b0a00a02448f4f65112638177"}, - {file = "py_bip39_bindings-0.1.12-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:bc00a31943d3d6d6a1ab61321d0c14675f4f1452165ab712c90c8368f754648a"}, - {file = "py_bip39_bindings-0.1.12-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:d9f96bb10320a1ddd354ea639c434cfb1e594d4a1cb9425a476f06fc04aa18d2"}, - {file = "py_bip39_bindings-0.1.12-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e106a1d6d1106cf40a49b3e17748c89fd9561a3aeeb98725dca9ce751c780e16"}, - {file = "py_bip39_bindings-0.1.12-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:028efb46d7823347b4a89822debf6a4aec45e32d5e1bc41c9b9e7e3e1262c622"}, - {file = "py_bip39_bindings-0.1.12-pp37-pypy37_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:caa6590d00cfbc48477811aee439254ca702568ca248487e8550a1d9a737efd3"}, - {file = "py_bip39_bindings-0.1.12-pp37-pypy37_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bee3de0777c914ba39f65882a0e2d1ff27377a01d516e81f462c4a0cebe41529"}, - {file = "py_bip39_bindings-0.1.12-pp37-pypy37_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:352b5af9916dd819570a22114f15c69b9a3abbd9fd48f0d8d3ae8a7461becc7d"}, - {file = "py_bip39_bindings-0.1.12-pp37-pypy37_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:ae023404b40c542169acce708047ba2df5f854815b4c26173484f220cb249109"}, - {file = "py_bip39_bindings-0.1.12-pp37-pypy37_pp73-musllinux_1_2_i686.whl", hash = "sha256:12886fc8e517224caef0f574a4cd1e56eb625fca32a1a9544551f7bb85f87739"}, - {file = "py_bip39_bindings-0.1.12-pp37-pypy37_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8b1ecc43e62f75cc2184c4a781cdc991636eb1b9d958fe79b5e2294327418b4b"}, - {file = "py_bip39_bindings-0.1.12-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6dc1803dedebca90327f41c4479dd4bb0ac7f5d75d4ea626737f137569288a8"}, - {file = "py_bip39_bindings-0.1.12-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:027266306c4f72ee3eeb3b4a9114a2b5f05c61b620127ca5e48f55ad0f40c3da"}, - {file = "py_bip39_bindings-0.1.12-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35a7b8324c3f1c1be522484d829433587e08318772998c5b8b0c3c26a6430ae"}, - {file = "py_bip39_bindings-0.1.12-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4e3f31d513f69043648a91a8ab500a12aa1945c60bb7d245ef4891ec8c22f123"}, - {file = "py_bip39_bindings-0.1.12-pp38-pypy38_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:39fde6be4b2dd765359d470d9fbef5f892c87f17b9569d621a4d85970082059c"}, - {file = "py_bip39_bindings-0.1.12-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:cb898867f198ed7e8a446464d275d81f627e32371eb4730d2c916317a3844a08"}, - {file = "py_bip39_bindings-0.1.12-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dea4f82f5e3997b7633c03c5618f41b89d220008dcb9fb067303a796c8aa5726"}, - {file = "py_bip39_bindings-0.1.12-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0222bc5d439151db46a00cba51a4d4b1d4bad453e760f0904b506e296c5d7b5"}, - {file = "py_bip39_bindings-0.1.12-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:451217d29b37c0d168b1dc4e9c29d0960ffc0d1857e9dfe9d90089c8044820b6"}, - {file = "py_bip39_bindings-0.1.12-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c012eb605dd09d2d161438bde8dad7d75c13a8fef7ecaa5f463f0dfd2e1a2a8f"}, - {file = "py_bip39_bindings-0.1.12-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e0f85a2a460a8e7241b42e3caba9a5afc3ea6e4d47c6dd39d3d490583418cfd0"}, - {file = "py_bip39_bindings-0.1.12-pp39-pypy39_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:436bae87732b90eb315845a95a41446dc0e5dbe77ea6c018b28d2b73a489ef04"}, - {file = "py_bip39_bindings-0.1.12-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:4d6c13fb07cd7d4068fb2a82f91217d7ea8962ebbeffa88f9aa57e4d24b38f0c"}, - {file = "py_bip39_bindings-0.1.12-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:bbe75eb05d5cadbb32cb6db0d0af53fe2c64ce70121e2704111ada8575778255"}, - {file = "py_bip39_bindings-0.1.12.tar.gz", hash = "sha256:56f446e665a4511e9e7dab807a908ca692247a257e141f76633110e3d30e53af"}, + {file = "py_bip39_bindings-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a9379172311aa9cdca13176fa541a7a8d5c99bb36360a35075b1687ca2a68f8"}, + {file = "py_bip39_bindings-0.2.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f423282376ef9f080d52b213aa5800b78ba4a5c0da174679fe1989e632fd1def"}, + {file = "py_bip39_bindings-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c61f08ee3a934932fb395a01b5f8f22e530e6c57a097fab40571ea635e28098"}, + {file = "py_bip39_bindings-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8254c1b630aea2d8d3010f7dae4ed8f55f0ecc166122314b76c91541aeeb4df0"}, + {file = "py_bip39_bindings-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5e68c0db919ebba87666a947efafa92e41beeaf19b254ce3c3787085ab0c5829"}, + {file = "py_bip39_bindings-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d36d468abc23d31bf19e3642b384fe28c7600c96239d4436f033d744a9137079"}, + {file = "py_bip39_bindings-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f5ac37b6c6f3e32397925949f9c738e677c0b6ac7d3aa01d813724d86ce9045e"}, + {file = "py_bip39_bindings-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8a1f2353c7fdbec4ea6f0a6a088cde076523b3bfce193786e096c4831ec723bb"}, + {file = "py_bip39_bindings-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cb05d97ed2d018a08715ff06051c8de75c75b44eb73a428aaa204b56ce69d8f4"}, + {file = "py_bip39_bindings-0.2.0-cp310-cp310-win32.whl", hash = "sha256:588ec726717b3ceaabff9b10d357a3a042a9a6cc075e3a9e14a3d7ba226c5ddf"}, + {file = "py_bip39_bindings-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:4e5e2af6c4b6a3640648fb28c8655b4a3275cee9a5160de8ec8db5ab295b7ec9"}, + {file = "py_bip39_bindings-0.2.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2af7c8dedaa1076002872eda378153e053e4c9f5ce39570da3c65ce7306f4439"}, + {file = "py_bip39_bindings-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:08e03bc728aa599e3cfac9395787618308c8b8e6f35a9ee0b11b73dcde72e3fc"}, + {file = "py_bip39_bindings-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f052e5740d500e5a6a24c97e026ce152bbee58b56f2944ed455788f65658884"}, + {file = "py_bip39_bindings-0.2.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd441833b21a5da2ccfebbdf800892e41282b24fc782eabae2a576e3d74d67f8"}, + {file = "py_bip39_bindings-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f39ac5d74640d1d48c80404e549be9dc5a8931383e7f94ee388e4d972b571f42"}, + {file = "py_bip39_bindings-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55ee7185bb1b5fb5ec4abcdea1ad89256dc7ee7e9a69843390a98068832169d6"}, + {file = "py_bip39_bindings-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42a00be8009d3c396bc9719fc743b85cb928cf163b081ad6ead8fc7c9c2fefdb"}, + {file = "py_bip39_bindings-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ccc54690103b537f6650a8053f905e56772ae0aeb7e456c062a290357953f292"}, + {file = "py_bip39_bindings-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:59a954b4e22e3cb3da441a94576002eaaba0b9ad75956ebb871f9b8e8bd7044a"}, + {file = "py_bip39_bindings-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:54ac4841e7f018811b0ffa4baae5bce82347f448b99c13b030fb9a0c263efc3d"}, + {file = "py_bip39_bindings-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2d1cdceebf62c804d53ff676f14fcadf43eeac7e8b10af2058f9387b9417094d"}, + {file = "py_bip39_bindings-0.2.0-cp311-cp311-win32.whl", hash = "sha256:be0786e712fb32efc55f06270c3da970e667dcec7f116b3defba802e6913e834"}, + {file = "py_bip39_bindings-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:82593f31fb59f5b42ffdd4b177e0472ec32b178321063a93f6d609d672f0a087"}, + {file = "py_bip39_bindings-0.2.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c763de71a7c83fcea7d6058a516e8ee3fd0f7111b6b02173381c35f48d96b090"}, + {file = "py_bip39_bindings-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c61f65286fe314c28a4cf78f92bd549dbcc8f2dad99034ec7b47a688b2695cae"}, + {file = "py_bip39_bindings-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:287301a2406e18cfb42bcc1b38cbd390ed11880cec371cd45471c00f75c3db8c"}, + {file = "py_bip39_bindings-0.2.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:69c55c11228f91de55415eb31d5e5e8007b0544a48e2780e521a15a3fb713e8f"}, + {file = "py_bip39_bindings-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ec5e6adb8ea6ffa22ed701d9749a184a203005538e276dcd2de183f27edebef"}, + {file = "py_bip39_bindings-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831b0649d603b2e7905e09e39e36955f756abb19200beb630afc168b5c03d681"}, + {file = "py_bip39_bindings-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:629399c400d605fbcb8de5ea942634ac06940e733e43753e0c23aee96fee6e45"}, + {file = "py_bip39_bindings-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15daa05fa85564f3ef7f3251ba9616cfc48f48d467cbecaf241194d0f8f62194"}, + {file = "py_bip39_bindings-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:236aa7edb9ad3cade3e554743d00a620f47a228f36aa512dd0ee2fa75ea21c44"}, + {file = "py_bip39_bindings-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:677ad9a382e8c2e73ca61f357a9c20a45885cd407afecc0c6e6604644c6ddfdc"}, + {file = "py_bip39_bindings-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5f3b7bc29eda6ad7347caf07a91941a1c6a7c5c94212ffec7b056a9c4801911e"}, + {file = "py_bip39_bindings-0.2.0-cp312-cp312-win32.whl", hash = "sha256:41f12635c5f0af0e406054d3a3ba0fad2045dfed461f43182bfa24edf11d90ca"}, + {file = "py_bip39_bindings-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:e3a4f2e05b6aaabbe3e59cebab72b57f216e118e5e3f167d93ee9b9e2257871b"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:21eed9f92eaf9746459cb7a2d0c97b98c885c51a279ec5bbf0b7ff8c26fe5bcc"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:350e83133f4445c038d39ada05add91743bbff904774b220e5b143df4ca7c4c3"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c46c3aca25f0c9840303d1fc16ad21b3bc620255e9a09fe0f739108419029245"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:57bd5454a3f4cad68ebb3b5978f166cb01bf0153f39e7bfc83af99399f142374"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8039f96b52b4ab348e01459e24355b62a6ad1d6a6ab9b3fcb40cfc404640ca9f"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6919cd972bad09afacd266af560379bafc10f708c959d2353aea1584019de023"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4737db37d8800eb2de056b736058b7d3f2d7c424320d26e275d9015b05b1f562"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:98df51a42035466cab5dfb69faec63c2e666c391ff6746a8603cc9cabfcebe24"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d55c6af3cebb5c640ff12d29d7ca152d2b8188db49b0a53fc52fd2a748a7e231"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:eae5c4956613134ec5abc696121272a6ce7107af1509d8cdea3e24db1dff351b"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:071c4e67e9ae4a5ae12d781483f3a46d81d6b20be965dade39044ed0f89df34a"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7642f598e9cd7caddcc2f57f50335a16677c02bb9a7c9bb01b1814ddab85bb5"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0d0cff505237fd8c7103243f242610501afcd8a917ce484e50097189a7115c55"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fec3d7b30980978bd73103e27907ca37766762c21cfce1abcc6f9032d54274f"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:876006fa8936ad413d2f53e67246478fc94c19d38dc45f6bfd5f9861853ac999"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e6064feb105ed5c7278d19e8c4e371710ce56adcdd48e0c5e6b77f9b005201b9"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0cb7a39bd65455c4cc3feb3f8d5766644410f32ac137aeea88b119c6ebe2d58b"}, + {file = "py_bip39_bindings-0.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58743e95cedee157a545d060ee401639308f995badb91477d195f1d571664b65"}, + {file = "py_bip39_bindings-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84f4c53eab32476e3a9dd473ad4e0093dd284e7868da886d37a0be9e67ec7509"}, + {file = "py_bip39_bindings-0.2.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e219d724c39cbaaabab1d1f10975399d46ba83a228437680dc29ccb7c8b4d38d"}, + {file = "py_bip39_bindings-0.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8fda7efd3befc614966169c10a4d5f60958698c13d4d0b97f069540220b45544"}, + {file = "py_bip39_bindings-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5319b6ad23e46e8521fa23c0204ba22bbc5ebc5002f8808182b07c216223b8ed"}, + {file = "py_bip39_bindings-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a1b1de6b2e12a7992aa91501e80724b89a4636b91b22a2288bae5c417e358466"}, + {file = "py_bip39_bindings-0.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:bae82c4505a79ed7f621d45e9ea225e7cd5e0804ce4c8022ab7a2b92bd007d53"}, + {file = "py_bip39_bindings-0.2.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:d42d44532f395c59059551fd030e91dd39cbe1b30cb7a1cf7636f9a6a6a36a94"}, + {file = "py_bip39_bindings-0.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:a29bca14abb51449bd051d59040966c6feff43b01f6b8a9669d876d7195d215f"}, + {file = "py_bip39_bindings-0.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:e7cb5aefdbc142b5c9b56b2c89c0112fd2288d52be8024cf1f1b66a4b84e3c83"}, + {file = "py_bip39_bindings-0.2.0-cp38-cp38-win32.whl", hash = "sha256:e846c0eebeb0b684da4d41ac0751e510f91d7568cc9bc085cb93aa50d9b1ee6e"}, + {file = "py_bip39_bindings-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0a2024c9e9a5b9d90ab9c1fdaddd1abb7e632ef309efc4b89656fc25689c4456"}, + {file = "py_bip39_bindings-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e3438f2568c1fdd8862a9946800570dbb8665b6c46412fa60745975cd82083a"}, + {file = "py_bip39_bindings-0.2.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d5339d0b99d2ce9835f467ed3790399793ed4d51982881ff9c1f854649055d42"}, + {file = "py_bip39_bindings-0.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:856b69bc125c40264bf9e749efc1b66405b27cfc4c7f96cd3c5e6a657b143859"}, + {file = "py_bip39_bindings-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91cffd83189f42f2945693786104c51fa775ba8f09c532bf8c504916d6ddb565"}, + {file = "py_bip39_bindings-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c72195aa802a36ad81bcc07fc23d59b83214511b1569e5cb245402a9209614e7"}, + {file = "py_bip39_bindings-0.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a5224fc1417d35413f914bbe414b276941dd1d03466ed8a8a259dc4fa330b60c"}, + {file = "py_bip39_bindings-0.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:86265b865bcacd3fcd30f89012cd248142d3eb6f36785c213f00d2d84d41e6fc"}, + {file = "py_bip39_bindings-0.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:96337e30589963b6b849415e0c1dc9fe5c671f51c1f10e10e40f825460078318"}, + {file = "py_bip39_bindings-0.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:07f2cd20581104c2bc02ccf90aaeb5e31b7dda2bbdb24afbaef69d410eb18bf0"}, + {file = "py_bip39_bindings-0.2.0-cp39-cp39-win32.whl", hash = "sha256:d26f9e2007c1275a869711bae2d1f1c6f99feadc3ea8ebb0aed4b69d1290bfa9"}, + {file = "py_bip39_bindings-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:3e79ad1cbf5410db23db6579111ed27aa7fac38969739040a9e3909a10d303fc"}, + {file = "py_bip39_bindings-0.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2491fc1a1c37052cf9538118ddde51c94a0d85804a6d06fddb930114a8f41385"}, + {file = "py_bip39_bindings-0.2.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5ecf247c9ea0c32c37bdde2db616cf3fff3d29a6b4c8945957f13e4c9e32c71a"}, + {file = "py_bip39_bindings-0.2.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ce39808c717b13c02edca9eea4d139fc4d86c01acad99efb113b04e9639e2b9"}, + {file = "py_bip39_bindings-0.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c5f38b01283e5973b1dfcdedc4e941c463f89879dc5d86463e77e816d240182"}, + {file = "py_bip39_bindings-0.2.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fb1503c87e3d97f6802b28bd3b808ce5f0d88b7a38ed413275616d1962f0a6d"}, + {file = "py_bip39_bindings-0.2.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e9ae6318f14bf8683cc11714516a04ac60095eab1aaf6ca6d1c99d508e399c64"}, + {file = "py_bip39_bindings-0.2.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:7be4ac068391c80fdee956f7c4309533fcf7a4fae45dec46179af757961efefc"}, + {file = "py_bip39_bindings-0.2.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e26059b46eff40ccb282966c49c475633cd7d3a1c08780fff3527d2ff247a014"}, + {file = "py_bip39_bindings-0.2.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e8a63d04f68269e7e42219b30ff1e1e013f08d2fecef3f39f1588db512339509"}, + {file = "py_bip39_bindings-0.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b85cb77708a4d1aceac8140604148c97894d3e7a3a531f8cfb82a85c574e4ac"}, + {file = "py_bip39_bindings-0.2.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:57a0c57a431fcd9873ae3d00b6227e39b80e63d9924d74a9f34972fd9f2e3076"}, + {file = "py_bip39_bindings-0.2.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:28bc089c7a7f22ac67de0e9e8308873c151cac9c5320c610392cdcb1c20e915e"}, + {file = "py_bip39_bindings-0.2.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:bfcec453c1069e87791ed0e1bc7168bbc3883dd40b0d7b35236e77dd0b1411c0"}, + {file = "py_bip39_bindings-0.2.0-pp39-pypy39_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:850cd2b2341a5438ae23b53054f1130f377813417f1fbe56c8424224dad0a408"}, + {file = "py_bip39_bindings-0.2.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:424a2df31bcd3c9649598fc1ea443267db724754f9ec19ac3bd2c42542643043"}, + {file = "py_bip39_bindings-0.2.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:06fe0f8bb2bd28266bf5182320b3f0cef94b103e03e81999f67cae194b7ea097"}, + {file = "py_bip39_bindings-0.2.0.tar.gz", hash = "sha256:38eac2c2be53085b8c2a215ebf12abcdaefee07bc8e00d7649b6b27399612b83"}, ] [[package]] name = "py-ed25519-zebra-bindings" -version = "1.1.0" +version = "1.2.0" description = "Python bindings for the ed25519-zebra RUST crate" optional = false python-versions = ">=3.9" files = [ - {file = "py_ed25519_zebra_bindings-1.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f0634c6b56cd81ce8bc10c322c14a7cd2a72f3742572c0dec332979df36e7cf5"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cc91befe939088aedd176e46f1316b46b4c5dd8f44a14d309e49a634fd65dbe7"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70cc6b1d460608b23e7a0f91ecb0cd4d8ed54762fc9035cc7d5d6a22358d5679"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:045396583efc93f16ee80df7739040a66cc7f8e048e9aec60d19e4cd2afaafb8"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:838ea2bb3f27907ec7a07aff6b267646b7c0c010f506673cbbc0d911e57cdfb8"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:716f090ab1564c9bf13e790b6f8bdea5ae40b68082def48f91023a8e12e38cf1"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2ebf8935bf1a856d9283916cfa62083b5cdfb24f7b772f42276fbf5b5af0f1f6"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a9e5652d3bc2e4e5ef7c12ba6767800b49f9504207e4210ac4bac9c2e31efa9"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:b846ffdfd035206e16e76026bc1f4cb45964068a929b76b2ec3289fef3ee420b"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c97c0d04be12b299581daba0296bd522460a0f7001b9340d1551c0e2daea18a4"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1d8f8b04c2b3372ef33554d62fec44db28517dea1986944fc65ce3b35341800"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp310-none-win32.whl", hash = "sha256:3243cdaad41406c21e3840630ca46a997f30644a7b3c4aa7fe54c930d0ad66af"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp310-none-win_amd64.whl", hash = "sha256:278481a18225dc74e4292980c1859b1774b9e86da2d9a4cd63988717d24d421c"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:048e84121007b6ced32b70086e9bd710a825920f0715d73be4760c45f61847be"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8200479a222da9bab0abbe35d9a60e4f658a4039054c3b9f2e58a102a393a658"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ef039b7a7e4f1653e7f0c467d136a7e135061e53fdc74934d36489a8859f9e4"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d6d035b7bd3dd998ef6030666e69cde95c34225187f53ebb9c2fa7298a65ffde"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f4a7bb72294f7f1f560a464832c0fc32bc0a20cf4d3b638f2428bf3dde6ebda"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89735c2623bcf02177004eaa895250a3115214cd51df2ab561863f565aa06b1b"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4a7cccd8ab3156d1f397c6476583e78427e93cd01fa82173df78b96e15eb9f4d"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:27483aa9262d0952e886d01ec174056503238064ce1f08a3fb17752db18071dd"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:b461baeb4adb5c5d916f8cf31651142744f29b90f010a71bb22beafe0d803f40"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b79af368be80b5cd32b2a678c775f113c1d76c6f0e1ea5e66586c81c9e0ab5b"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9555ccf645374e5282103416fe5cba60937d7bf12af676980bd4e18cfa2fab48"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp311-none-win32.whl", hash = "sha256:1c55a32c2070aa68e0ed5a2930ba547fbf47617fd279462171d5c0f87b00df6d"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp311-none-win_amd64.whl", hash = "sha256:c4a4dedb1b8edf7f68dd8015f9d8a20f2f0ecca90fac4432e5cbabfcc16ab13d"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3ee9a0b7eb896547e539a27c4286461d58c6a99952ea27fa1b5f5e39e63019dc"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93e2a12d0bbf58f4d1e5ae2a1c352e43302cadd747a1a5e88fea03ce7a78a562"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33673e6047eba0a0a28e818fa0b36b703986347fc98e6f0f96e36af68756787"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:14399e6e8c5b9c193256a1b9aec16b9de719691de84ab23a690056cfe011d13b"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85ea7a5632a1bf6713518bb56d4c8abe5128aee173a3c498b3a564cfb346ca72"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a01f58a1c980df68c8efd43892562b3224507bab83d880910fbb4a3c84fc965"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:655360cd74718d4efb8fbaf7afb2e4ce459af5f1d399479f577a63bd9177aa3b"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:650081644c6613fdf658456ed4b2a6580ec1b54084f318a31a924ce5cf536bb9"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5d56b81186fc75cbcf80d0549f83e98c62c4359460e512f9fb8d6c7be2a158dd"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:072bf62421ad890c1849aaa19c7b6e6a890d337f0622e9bd09161b180a10496c"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e09830c3672699f5f1f164fe92b102254ef68300ceaddc847d9a35bf4a2ec270"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp312-none-win32.whl", hash = "sha256:33ca2a7ad10846c281a73450316b390c7343e62e40516389fc1b580241f3907f"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp312-none-win_amd64.whl", hash = "sha256:4ba042528ddb81f8f025b1987bf8f19547f188efb7aa4c95d1a4e3e7f968e991"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:d75a61407651174841051d59ebcee5716207371999bef78055193e96b6dec546"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b5afba0bd4ae86f06b278698d24acebe2d4b912e0490c94ee550a859377e5c05"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d977fb8d7f5594278ea3eb8283dac811cc35c925176454ccbb3ddf0110e194fa"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:83aa29a0d0df233591da3118142c818adc3f697143d655378ee076e6f091be7e"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01f2952e5c7082dc9266d668528b27711874d0f5d2aa2e85994a46643b12686e"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0db1e886f65a439fe9641196e05229715701df572a92b41428ad097d4889c5b"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dada974b078204d1591851d0e5958824569900be3ea53676b84165ba16283641"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0f7aa0056c7af9df6c580880948cce42f779951e3e551573acf9b30462d9ca9"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:2176111de560441caaf72f09da77a1d4f8eacbb34245e2531c7243ee4070829f"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6f8686f160c4e7e928c79ad33919817b9eb608e5808154311a933078bad243b4"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:09b089af355cb0815cca4796407f89aaf08aceb5d39c5b7de087533319e3673e"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp39-none-win32.whl", hash = "sha256:12e5f602897c1c95217e406d80f2e7901c90b9345011c147c36989bfb7c3bb49"}, - {file = "py_ed25519_zebra_bindings-1.1.0-cp39-none-win_amd64.whl", hash = "sha256:119c3ca0c23570ead0a3ea9e8b7fb9716bf3675229562a7dadd815009cecf3eb"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ded8debbcffc756214cd55ebefff44fb23640c9f54edf34c0e43b371e2d2b42d"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:db18ef79ea6dc0bc42e28cd46eb442d8e84c0c9c8b2809babd63608efe015ec9"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:875bba860c5ce874e33e6d04c4804c8e4149754720bf47bd66c068a61c2ed3cc"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de987c5c8b3a69504f82adc4d3b70aa322315550f945684111d8bfe40288517b"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9721c029279a6e074d4a69a8ca2ee5da6264efda052fe07afcae6ca04e340805"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:b8843fe090b793449fc8b581ff5d55c03ae9bb18ecd4943ef27c51751df9e5e8"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:b8da29729eb83c1aeeb80f0a60ceb30f6c00ba9c2c208548f6628b8f4764eccd"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a99ab59da361a9834c0019954e72a7687fa19a3380c5acc3274452c26af3b791"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4b40b19b0259412171a36f51778bc9d8e92f21baea469549a03ff99318bc640e"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b7c389eee3f99c1ad3b3fa32fc53914dda22876f8e2918b5b564b98f029dd92"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f13dffb7875eea6b2bd8f9b1165e29d9cd3df2c919c2f2db0f164926c99393e8"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a55f043d87e00e7d68fed03d02ac0725acd2dff9a03e61388285d6d920f6850f"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48f09c403c58245cc6712ce34ba3cab91705c901a6c4bb1e865847f46b26ec9"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17694b0afce037860043cf8237a990e8df9c307b4decb73028d794ae56733875"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:c80f339abfc522248c749323b2cd6d555c3095128b01d845eb48a746318553da"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp39-pypy39_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:89222bb4e16d1e222a95c622630f3380fe1896ab7d0cd145da39f3aa8888ed64"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:85904f3b4e11d2fc7358686e15c1e0e3707b4e1846e82d49d764c9c44881f2a3"}, - {file = "py_ed25519_zebra_bindings-1.1.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:b4452e9ed752db2c1ed2b7c74e1d589dfa68101b04c3304ba87a3cb843d34306"}, - {file = "py_ed25519_zebra_bindings-1.1.0.tar.gz", hash = "sha256:2977603b59cfc593fb01284465fe41062d6929b0d09edf0e1ade40709977014f"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d488bf0ac70424514fddb3cf9cca6166ad149b7655970719e9bbef398054e6ad"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c3f97af9b0db7fe2bba1b1ac8d684711fc33e6383c067e1a1fc642e1595282a"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9798a82efe73cfff02eb4c09576af0dc0ca3b41cc3e17cf469179add708c8b40"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0837d10e19e72bb4665c584c89f207bad8b3d29cf2410c0f9ea310c6698f4b26"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bb278da1728db5259d5c29dcc95717336a69fc6e6159cb7400ac262ee8a96ca"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5a739e82c82a1f62de54cc0482e9d007b961c84220849ffd86924e34f8db5c9e"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d311a44ae162da4b391eb4d47675709b5044b925bef20e4e2209cdfa28ccc1ee"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c4a30a6a22f28173de66634294824455ae683163be32565f36fbfa27b8a76495"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:325eb5d0c7a406fd6abbd5b2daeb6d16e4c161a86909bf11a34a3a2c351e7fa0"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp310-cp310-win32.whl", hash = "sha256:dcd8f8ecbc3593c54fb3fcc1d0d847d2fdf86c8d2e6840d319d152f4efdef498"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:b762e13f1e2cedfac4be954a70a75330a5368e2c0ecd64db7ce1e2e9672ed4da"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:dbfe655442b73d49c1ac740f87a480cfee4c013fcb0ba2b639290b20f8dc9bb5"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b03308c3eb2311b5d308c3df22dbf244073e4c014cda5da2609a562adb4121fc"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1828031f38f246d35c7c7b427c17a3525fc311c0402d3b32572510977b9d0f67"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f88238cf167ba5681e74a556b1e6ce825cb157825ce40c7f757b7d02a7c47dfb"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b25ca1596ae3be7e6ce6e78252ce7efa570000f9ba5b39cfe8dd10e79f73d50"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68a33b1d8af961d28831caf2d481879bb1592f700da79aa5613d845ae6b8153a"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:41ee171c18852f6db4a86e68c4fbd622f5415f15c0ab9b40ac1fe66a8ddc3844"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:58623ff56bf1da2581a7d52507d9757ec3b03d49879fc8611646faf666bd0120"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3fdd9cc305dd88562b9fe4d27762070bfdaa1e88647a1509a22fe252e17148d7"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:911f068d15159798309dc1895ce156b1bca2f91e34446be3ac5f54f2d3418979"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9d0fc9c1afbf4b5ff0bc03accf5f07bf53971839eb373d1139eb3bb5a02b3bd0"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp311-cp311-win32.whl", hash = "sha256:256b96fdf0e264a348bf4176c0fb180a0efc6627ac312cb5e71ec95b347d1ff5"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:efa06b8a0e11c62c10fdf576679ab3039aa6a7254e6cfa4d2d230941799fef5b"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8d63a447d3adac9b431fecd886cf711a6d44200d8b2497598a8ab44ac897f1fb"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5b1c32414a6da709e84d0614e1ed153a5e1dbcbf6d4d17baa31c493fdbd4da4"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:780073555571390c4b355b5646c0b59c2a90d3393e354d58c4ad904121a2aee2"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:677ade8ab3348604a9e4176b068ff19707cf205fd8ee4f1781614b085628fa45"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c19c0cc491bc4999245f9d2e904f611354f442710b6dae6d1d6ebc81666124cc"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1317a8af53c658f1e89b346d361edaf10eccd428c937a17d0684b2192fa77c40"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cdc05ade2608707f6c54701e7425d9c00751ccffa57533a48f68f61b0aada9f1"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec1965ed54fd162da564cc33676377888bd1ad14c15680465463d06e14aac74d"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7386e7cec522ac50e7d81cfc8488e463fe93902d6ba0f7c79d6f6db0fcf71111"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b06102b2be52da075f29f0db907bb5a03af942e2f6fb558065ea5717aa567d32"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4237cf821f74126077220d5826448c0b68c8807f40db961b1335bb6a66a83af8"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp312-cp312-win32.whl", hash = "sha256:fe11223695c94040f31b48a2128f1642a1b689aaaa91b5f8ae018d53b1497409"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:87654379855152770974c045099e488b577d86429af609524903b8029b276417"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2e10a578c1297a9b12a818c5b874d9830afba1592e8cb9df3a44b2afbc241cf0"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f0edbed9d94f5295c4f360baa38e124626296e36f315d6a19bc91f7d8a61627"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe2d0db5c2d4c0575b91373eb0c33b1d222fbb38664e17d807c8845eab268c16"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4b371742adbd9be4a5a813e5d920a1a057fe9013620681651a3e7c84fd1f8d8b"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f82a6ae05ac4feb16d077ce1b4a48396c9685bc2b37d3a1ffbcd16023a4f3b8a"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:446f26b62311db93205507fedb3fa07dae786ae75822182d44dadd28984d7768"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f76ccb64577bbdfdacc543298355747dca9684e74262f844c3d892bd583e023b"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c5c95587f93f9cbf73e3609e8befe2b36c488bcf96ccc1c8c63b257212e1b9df"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3f157f87844d5e395380eaf03d9baa2108126ad276088c7edb55869683cc2cfc"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:022499a21096d03d90654af2203a5475f6c3c5572245b7bc6a1bbeeb4e42c319"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7b466ec2de929e38e6f441156a3e108a3c090dbc6b624864f6c1b300cc329f8d"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:998b5d9c4db1053156a55e8edf06a5dce68ddaa3e928e2861f8ba9a5fe5b6119"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a0fe34c20032f406a78c865c308b49fe3c79c9e1642f6471228cfbc6c513348"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7e3273d73148d983a5e7f9ed3e8b53824dcb7833393aa09dd969dd3e7a1f3c1"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:cb5858f54ebd7d37c9d21c6dd80367d0031dbda7bd91b333018c0f243e1284f5"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:4fd00c8686b17e31ec29d8e4e7ce97f465fe26227f12c9e111e012b9d0dff4b9"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e4e55fc5be4ba0c723d424cefdbb8d863e74d2ff25fbeadca9539ca60d78cc0f"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:91816ed4cef90d4d08fa9f55fa0c5687c5eba601dc1a44f211adcf1c20d96cc3"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da78de274a8276ba8127cd1a0c8dc7889162703d0f21b8ca136587a40ab911fb"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acc66206412d2abbfb088bd4027c7e21949975cc66f5ccd6249b8937a3cf315d"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71f36c2465d808149604e536e50e3d6038c5bc83165df3b71a78345a66437819"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c8ff027c9363f9c52ee36967b74e948f583e90a5bcbc24b31831a5ce9a25173"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:161f5996ac22ba224e3c1026fef7992a7f2be71685f7dc3208b2f94039a680cc"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:eeec2b39546ebea93f96cfd8c7984e1d5489c4767f053225b1b71da1aba60273"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4524e3a900d6f11daa12185ee0d96c11f215ddf714b697599d8f0ec99d03275a"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0614733ed55ad8bd80a4a3a8abf21d26e39678c6fe31ee164388c7dc543e070d"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:aa9a2a610ffe5b576513ff4d6bd77b79e1c818c1a11df51522e7a82c9c299059"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp39-cp39-win32.whl", hash = "sha256:81b2dac4669d2935edf5953eb53c2507023774d2fa6e3a51743e8e3757f28e1a"}, + {file = "py_ed25519_zebra_bindings-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:3503a179561ada2ac456351e211a28b433083d5fa48ff605e9670ae51797ea12"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f76228db22d018a66e858b27d4c074a0111438919a45276ac1a00d397d6daca"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f7c0875eda221bfdc1029207d7807c2ae5446bf4aaf5d34def94b8fa2abeace"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c713b7dba676380e2a1c3208667a71bf4bcc02a67b487894cda35c6103079e9"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fe2882a1377199cdb656e42adf5e97869d1b04af1f66a7300179f95692603c2"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c6afd09a1b831444a5107ca8e48f14db837a2351cac25e70e71f80f976c76ca2"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:91c0627efe7048ce552be5db08c11a99d532b2e115316daed3b53e52ba9f383b"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:d6efc48c7c26838044c7f58ba2e7944776ef6eaef21c962a528ddffd3943e1b4"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:7cb8befc4c52c681c4e2f5994adeff28f529f767c979921faaa1fbb84a52afae"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3b976f2c6053011c08dcde2f5805e285a8ff53eec5a42be0cc24ce93bc5729ac"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7ac9e2f0856b2ce3db7bfb6bb1b750e2533846b8aaf6106d5edc4fca33d4e2"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97df330d22c671e4e494b4e4f85ab06a4b067f38201430d8d08e687c6c1ef25"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac83999ed7ef81a64830495ad356e587ff89bdc20c79ad81d2baf8e38c707d76"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:78c23fe0e20159268ee343110a9afe58813691c9fe94bfb3525efcd23af97b81"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp39-pypy39_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:900e1fd3d1474b02342d5e388fe874b2b71d1c87e4e652ed5b7773ca25c34754"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:c11f644619ca166fb62b6ec4586d53fc74e1bc3a5345e9b84af6baca7b5ca6b1"}, + {file = "py_ed25519_zebra_bindings-1.2.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f03a9514c7e763367128a7e6be529fe8417775f72d5d717c0c3004047f188596"}, + {file = "py_ed25519_zebra_bindings-1.2.0.tar.gz", hash = "sha256:d9ec63d54b1801d5b5bdef0b3096ed94e2e1a7c870c937682afc7b8b25ffc2fc"}, ] [[package]] @@ -2932,6 +3476,17 @@ files = [ {file = "py_sr25519_bindings-0.2.1.tar.gz", hash = "sha256:1b96d3dde43adcf86ab427a9fd72b2c6291dca36eb40747df631588c16f01c1a"}, ] +[[package]] +name = "pycodestyle" +version = "2.12.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, + {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, +] + [[package]] name = "pycparser" version = "2.22" @@ -2986,18 +3541,18 @@ files = [ [[package]] name = "pydantic" -version = "2.10.3" +version = "2.10.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"}, - {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"}, + {file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"}, + {file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.27.1" +pydantic-core = "2.27.2" typing-extensions = ">=4.12.2" [package.extras] @@ -3006,116 +3561,127 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.27.1" +version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"}, - {file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"}, - {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278"}, - {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05"}, - {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4"}, - {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f"}, - {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08"}, - {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6"}, - {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807"}, - {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c"}, - {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206"}, - {file = "pydantic_core-2.27.1-cp310-none-win32.whl", hash = "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c"}, - {file = "pydantic_core-2.27.1-cp310-none-win_amd64.whl", hash = "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17"}, - {file = "pydantic_core-2.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8"}, - {file = "pydantic_core-2.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330"}, - {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52"}, - {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4"}, - {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c"}, - {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de"}, - {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025"}, - {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e"}, - {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919"}, - {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c"}, - {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc"}, - {file = "pydantic_core-2.27.1-cp311-none-win32.whl", hash = "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9"}, - {file = "pydantic_core-2.27.1-cp311-none-win_amd64.whl", hash = "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5"}, - {file = "pydantic_core-2.27.1-cp311-none-win_arm64.whl", hash = "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89"}, - {file = "pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f"}, - {file = "pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02"}, - {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c"}, - {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac"}, - {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb"}, - {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529"}, - {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35"}, - {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089"}, - {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381"}, - {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb"}, - {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae"}, - {file = "pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c"}, - {file = "pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16"}, - {file = "pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e"}, - {file = "pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073"}, - {file = "pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08"}, - {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf"}, - {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737"}, - {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2"}, - {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107"}, - {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51"}, - {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a"}, - {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc"}, - {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960"}, - {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23"}, - {file = "pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05"}, - {file = "pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337"}, - {file = "pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5"}, - {file = "pydantic_core-2.27.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62"}, - {file = "pydantic_core-2.27.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab"}, - {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864"}, - {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067"}, - {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd"}, - {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5"}, - {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78"}, - {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f"}, - {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36"}, - {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a"}, - {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b"}, - {file = "pydantic_core-2.27.1-cp38-none-win32.whl", hash = "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618"}, - {file = "pydantic_core-2.27.1-cp38-none-win_amd64.whl", hash = "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4"}, - {file = "pydantic_core-2.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967"}, - {file = "pydantic_core-2.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60"}, - {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854"}, - {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9"}, - {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd"}, - {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be"}, - {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e"}, - {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792"}, - {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01"}, - {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9"}, - {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131"}, - {file = "pydantic_core-2.27.1-cp39-none-win32.whl", hash = "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3"}, - {file = "pydantic_core-2.27.1-cp39-none-win_amd64.whl", hash = "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f"}, - {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2"}, - {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840"}, - {file = "pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, ] [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + [[package]] name = "pygments" version = "2.18.0" @@ -3130,6 +3696,21 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pyluach" +version = "2.2.0" +description = "A Python package for dealing with Hebrew (Jewish) calendar dates." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyluach-2.2.0-py3-none-any.whl", hash = "sha256:d1eb49d6292087e9290f4661ae01b60c8c933704ec8c9cef82673b349ff96adf"}, + {file = "pyluach-2.2.0.tar.gz", hash = "sha256:9063a25387cd7624276fd0656508bada08aa8a6f22e8db352844cd858e69012b"}, +] + +[package.extras] +doc = ["sphinx (>=6.1.3,<6.2.0)", "sphinx_rtd_theme (>=1.2.0,<1.3.0)"] +test = ["beautifulsoup4", "flake8", "pytest", "pytest-cov"] + [[package]] name = "pynacl" version = "1.5.0" @@ -3170,6 +3751,20 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "python-levenshtein" version = "0.26.1" @@ -3273,104 +3868,207 @@ files = [ [[package]] name = "rapidfuzz" -version = "3.10.1" +version = "3.11.0" description = "rapid fuzzy string matching" optional = false python-versions = ">=3.9" files = [ - {file = "rapidfuzz-3.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f17d9f21bf2f2f785d74f7b0d407805468b4c173fa3e52c86ec94436b338e74a"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b31f358a70efc143909fb3d75ac6cd3c139cd41339aa8f2a3a0ead8315731f2b"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f4f43f2204b56a61448ec2dd061e26fd344c404da99fb19f3458200c5874ba2"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d81bf186a453a2757472133b24915768abc7c3964194406ed93e170e16c21cb"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3611c8f45379a12063d70075c75134f2a8bd2e4e9b8a7995112ddae95ca1c982"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c3b537b97ac30da4b73930fa8a4fe2f79c6d1c10ad535c5c09726612cd6bed9"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:231ef1ec9cf7b59809ce3301006500b9d564ddb324635f4ea8f16b3e2a1780da"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed4f3adc1294834955b7e74edd3c6bd1aad5831c007f2d91ea839e76461a5879"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7b6015da2e707bf632a71772a2dbf0703cff6525732c005ad24987fe86e8ec32"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1b35a118d61d6f008e8e3fb3a77674d10806a8972c7b8be433d6598df4d60b01"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bc308d79a7e877226f36bdf4e149e3ed398d8277c140be5c1fd892ec41739e6d"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f017dbfecc172e2d0c37cf9e3d519179d71a7f16094b57430dffc496a098aa17"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-win32.whl", hash = "sha256:36c0e1483e21f918d0f2f26799fe5ac91c7b0c34220b73007301c4f831a9c4c7"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:10746c1d4c8cd8881c28a87fd7ba0c9c102346dfe7ff1b0d021cdf093e9adbff"}, - {file = "rapidfuzz-3.10.1-cp310-cp310-win_arm64.whl", hash = "sha256:dfa64b89dcb906835e275187569e51aa9d546a444489e97aaf2cc84011565fbe"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:92958ae075c87fef393f835ed02d4fe8d5ee2059a0934c6c447ea3417dfbf0e8"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ba7521e072c53e33c384e78615d0718e645cab3c366ecd3cc8cb732befd94967"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00d02cbd75d283c287471b5b3738b3e05c9096150f93f2d2dfa10b3d700f2db9"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:efa1582a397da038e2f2576c9cd49b842f56fde37d84a6b0200ffebc08d82350"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f12912acee1f506f974f58de9fdc2e62eea5667377a7e9156de53241c05fdba8"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666d5d8b17becc3f53447bcb2b6b33ce6c2df78792495d1fa82b2924cd48701a"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26f71582c0d62445067ee338ddad99b655a8f4e4ed517a90dcbfbb7d19310474"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8a2ef08b27167bcff230ffbfeedd4c4fa6353563d6aaa015d725dd3632fc3de7"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:365e4fc1a2b95082c890f5e98489b894e6bf8c338c6ac89bb6523c2ca6e9f086"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1996feb7a61609fa842e6b5e0c549983222ffdedaf29644cc67e479902846dfe"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:cf654702f144beaa093103841a2ea6910d617d0bb3fccb1d1fd63c54dde2cd49"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec108bf25de674781d0a9a935030ba090c78d49def3d60f8724f3fc1e8e75024"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-win32.whl", hash = "sha256:031f8b367e5d92f7a1e27f7322012f3c321c3110137b43cc3bf678505583ef48"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:f98f36c6a1bb9a6c8bbec99ad87c8c0e364f34761739b5ea9adf7b48129ae8cf"}, - {file = "rapidfuzz-3.10.1-cp311-cp311-win_arm64.whl", hash = "sha256:f1da2028cb4e41be55ee797a82d6c1cf589442504244249dfeb32efc608edee7"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1340b56340896bede246f612b6ecf685f661a56aabef3d2512481bfe23ac5835"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2316515169b7b5a453f0ce3adbc46c42aa332cae9f2edb668e24d1fc92b2f2bb"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e06fe6a12241ec1b72c0566c6b28cda714d61965d86569595ad24793d1ab259"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d99c1cd9443b19164ec185a7d752f4b4db19c066c136f028991a480720472e23"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1d9aa156ed52d3446388ba4c2f335e312191d1ca9d1f5762ee983cf23e4ecf6"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:54bcf4efaaee8e015822be0c2c28214815f4f6b4f70d8362cfecbd58a71188ac"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0c955e32afdbfdf6e9ee663d24afb25210152d98c26d22d399712d29a9b976b"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:191633722203f5b7717efcb73a14f76f3b124877d0608c070b827c5226d0b972"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:195baad28057ec9609e40385991004e470af9ef87401e24ebe72c064431524ab"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0fff4a6b87c07366662b62ae994ffbeadc472e72f725923f94b72a3db49f4671"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4ffed25f9fdc0b287f30a98467493d1e1ce5b583f6317f70ec0263b3c97dbba6"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d02cf8e5af89a9ac8f53c438ddff6d773f62c25c6619b29db96f4aae248177c0"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-win32.whl", hash = "sha256:f3bb81d4fe6a5d20650f8c0afcc8f6e1941f6fecdb434f11b874c42467baded0"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:aaf83e9170cb1338922ae42d320699dccbbdca8ffed07faeb0b9257822c26e24"}, - {file = "rapidfuzz-3.10.1-cp312-cp312-win_arm64.whl", hash = "sha256:c5da802a0d085ad81b0f62828fb55557996c497b2d0b551bbdfeafd6d447892f"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fc22d69a1c9cccd560a5c434c0371b2df0f47c309c635a01a913e03bbf183710"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38b0dac2c8e057562b8f0d8ae5b663d2d6a28c5ab624de5b73cef9abb6129a24"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fde3bbb14e92ce8fcb5c2edfff72e474d0080cadda1c97785bf4822f037a309"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9141fb0592e55f98fe9ac0f3ce883199b9c13e262e0bf40c5b18cdf926109d16"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:237bec5dd1bfc9b40bbd786cd27949ef0c0eb5fab5eb491904c6b5df59d39d3c"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18123168cba156ab5794ea6de66db50f21bb3c66ae748d03316e71b27d907b95"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b75fe506c8e02769cc47f5ab21ce3e09b6211d3edaa8f8f27331cb6988779be"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9da82aa4b46973aaf9e03bb4c3d6977004648c8638febfc0f9d237e865761270"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c34c022d5ad564f1a5a57a4a89793bd70d7bad428150fb8ff2760b223407cdcf"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e96c84d6c2a0ca94e15acb5399118fff669f4306beb98a6d8ec6f5dccab4412"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e8e154b84a311263e1aca86818c962e1fa9eefdd643d1d5d197fcd2738f88cb9"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:335fee93188f8cd585552bb8057228ce0111bd227fa81bfd40b7df6b75def8ab"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-win32.whl", hash = "sha256:6729b856166a9e95c278410f73683957ea6100c8a9d0a8dbe434c49663689255"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:0e06d99ad1ad97cb2ef7f51ec6b1fedd74a3a700e4949353871cf331d07b382a"}, - {file = "rapidfuzz-3.10.1-cp313-cp313-win_arm64.whl", hash = "sha256:8d1b7082104d596a3eb012e0549b2634ed15015b569f48879701e9d8db959dbb"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:779027d3307e1a2b1dc0c03c34df87a470a368a1a0840a9d2908baf2d4067956"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:440b5608ab12650d0390128d6858bc839ae77ffe5edf0b33a1551f2fa9860651"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82cac41a411e07a6f3dc80dfbd33f6be70ea0abd72e99c59310819d09f07d945"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:958473c9f0bca250590200fd520b75be0dbdbc4a7327dc87a55b6d7dc8d68552"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ef60dfa73749ef91cb6073be1a3e135f4846ec809cc115f3cbfc6fe283a5584"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7fbac18f2c19fc983838a60611e67e3262e36859994c26f2ee85bb268de2355"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a0d519ff39db887cd73f4e297922786d548f5c05d6b51f4e6754f452a7f4296"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bebb7bc6aeb91cc57e4881b222484c26759ca865794187217c9dcea6c33adae6"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fe07f8b9c3bb5c5ad1d2c66884253e03800f4189a60eb6acd6119ebaf3eb9894"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:bfa48a4a2d45a41457f0840c48e579db157a927f4e97acf6e20df8fc521c79de"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2cf44d01bfe8ee605b7eaeecbc2b9ca64fc55765f17b304b40ed8995f69d7716"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e6bbca9246d9eedaa1c84e04a7f555493ba324d52ae4d9f3d9ddd1b740dcd87"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-win32.whl", hash = "sha256:567f88180f2c1423b4fe3f3ad6e6310fc97b85bdba574801548597287fc07028"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:6b2cd7c29d6ecdf0b780deb587198f13213ac01c430ada6913452fd0c40190fc"}, - {file = "rapidfuzz-3.10.1-cp39-cp39-win_arm64.whl", hash = "sha256:9f912d459e46607ce276128f52bea21ebc3e9a5ccf4cccfef30dd5bddcf47be8"}, - {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ac4452f182243cfab30ba4668ef2de101effaedc30f9faabb06a095a8c90fd16"}, - {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:565c2bd4f7d23c32834652b27b51dd711814ab614b4e12add8476be4e20d1cf5"}, - {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:187d9747149321607be4ccd6f9f366730078bed806178ec3eeb31d05545e9e8f"}, - {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:616290fb9a8fa87e48cb0326d26f98d4e29f17c3b762c2d586f2b35c1fd2034b"}, - {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:073a5b107e17ebd264198b78614c0206fa438cce749692af5bc5f8f484883f50"}, - {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:39c4983e2e2ccb9732f3ac7d81617088822f4a12291d416b09b8a1eadebb3e29"}, - {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ac7adee6bcf0c6fee495d877edad1540a7e0f5fc208da03ccb64734b43522d7a"}, - {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:425f4ac80b22153d391ee3f94bc854668a0c6c129f05cf2eaf5ee74474ddb69e"}, - {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65a2fa13e8a219f9b5dcb9e74abe3ced5838a7327e629f426d333dfc8c5a6e66"}, - {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75561f3df9a906aaa23787e9992b228b1ab69007932dc42070f747103e177ba8"}, - {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:edd062490537e97ca125bc6c7f2b7331c2b73d21dc304615afe61ad1691e15d5"}, - {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfcc8feccf63245a22dfdd16e222f1a39771a44b870beb748117a0e09cbb4a62"}, - {file = "rapidfuzz-3.10.1.tar.gz", hash = "sha256:5a15546d847a915b3f42dc79ef9b0c78b998b4e2c53b252e7166284066585979"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eb8a54543d16ab1b69e2c5ed96cabbff16db044a50eddfc028000138ca9ddf33"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:231c8b2efbd7f8d2ecd1ae900363ba168b8870644bb8f2b5aa96e4a7573bde19"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54e7f442fb9cca81e9df32333fb075ef729052bcabe05b0afc0441f462299114"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:906f1f2a1b91c06599b3dd1be207449c5d4fc7bd1e1fa2f6aef161ea6223f165"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed59044aea9eb6c663112170f2399b040d5d7b162828b141f2673e822093fa8"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cb1965a28b0fa64abdee130c788a0bc0bb3cf9ef7e3a70bf055c086c14a3d7e"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b488b244931d0291412917e6e46ee9f6a14376625e150056fe7c4426ef28225"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f0ba13557fec9d5ffc0a22826754a7457cc77f1b25145be10b7bb1d143ce84c6"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3871fa7dfcef00bad3c7e8ae8d8fd58089bad6fb21f608d2bf42832267ca9663"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b2669eafee38c5884a6e7cc9769d25c19428549dcdf57de8541cf9e82822e7db"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ffa1bb0e26297b0f22881b219ffc82a33a3c84ce6174a9d69406239b14575bd5"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:45b15b8a118856ac9caac6877f70f38b8a0d310475d50bc814698659eabc1cdb"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-win32.whl", hash = "sha256:22033677982b9c4c49676f215b794b0404073f8974f98739cb7234e4a9ade9ad"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:be15496e7244361ff0efcd86e52559bacda9cd975eccf19426a0025f9547c792"}, + {file = "rapidfuzz-3.11.0-cp310-cp310-win_arm64.whl", hash = "sha256:714a7ba31ba46b64d30fccfe95f8013ea41a2e6237ba11a805a27cdd3bce2573"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8724a978f8af7059c5323d523870bf272a097478e1471295511cf58b2642ff83"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b63cb1f2eb371ef20fb155e95efd96e060147bdd4ab9fc400c97325dfee9fe1"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82497f244aac10b20710448645f347d862364cc4f7d8b9ba14bd66b5ce4dec18"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:339607394941801e6e3f6c1ecd413a36e18454e7136ed1161388de674f47f9d9"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84819390a36d6166cec706b9d8f0941f115f700b7faecab5a7e22fc367408bc3"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eea8d9e20632d68f653455265b18c35f90965e26f30d4d92f831899d6682149b"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b659e1e2ea2784a9a397075a7fc395bfa4fe66424042161c4bcaf6e4f637b38"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1315cd2a351144572e31fe3df68340d4b83ddec0af8b2e207cd32930c6acd037"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a7743cca45b4684c54407e8638f6d07b910d8d811347b9d42ff21262c7c23245"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5bb636b0150daa6d3331b738f7c0f8b25eadc47f04a40e5c23c4bfb4c4e20ae3"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:42f4dd264ada7a9aa0805ea0da776dc063533917773cf2df5217f14eb4429eae"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:51f24cb39e64256221e6952f22545b8ce21cacd59c0d3e367225da8fc4b868d8"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-win32.whl", hash = "sha256:aaf391fb6715866bc14681c76dc0308f46877f7c06f61d62cc993b79fc3c4a2a"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:ebadd5b8624d8ad503e505a99b8eb26fe3ea9f8e9c2234e805a27b269e585842"}, + {file = "rapidfuzz-3.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:d895998fec712544c13cfe833890e0226585cf0391dd3948412441d5d68a2b8c"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f382fec4a7891d66fb7163c90754454030bb9200a13f82ee7860b6359f3f2fa8"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dfaefe08af2a928e72344c800dcbaf6508e86a4ed481e28355e8d4b6a6a5230e"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92ebb7c12f682b5906ed98429f48a3dd80dd0f9721de30c97a01473d1a346576"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a1b3ebc62d4bcdfdeba110944a25ab40916d5383c5e57e7c4a8dc0b6c17211a"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c6d7fea39cb33e71de86397d38bf7ff1a6273e40367f31d05761662ffda49e4"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99aebef8268f2bc0b445b5640fd3312e080bd17efd3fbae4486b20ac00466308"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4469307f464ae3089acf3210b8fc279110d26d10f79e576f385a98f4429f7d97"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:eb97c53112b593f89a90b4f6218635a9d1eea1d7f9521a3b7d24864228bbc0aa"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ef8937dae823b889c0273dfa0f0f6c46a3658ac0d851349c464d1b00e7ff4252"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d95f9e9f3777b96241d8a00d6377cc9c716981d828b5091082d0fe3a2924b43e"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:b1d67d67f89e4e013a5295e7523bc34a7a96f2dba5dd812c7c8cb65d113cbf28"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d994cf27e2f874069884d9bddf0864f9b90ad201fcc9cb2f5b82bacc17c8d5f2"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-win32.whl", hash = "sha256:ba26d87fe7fcb56c4a53b549a9e0e9143f6b0df56d35fe6ad800c902447acd5b"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:b1f7efdd7b7adb32102c2fa481ad6f11923e2deb191f651274be559d56fc913b"}, + {file = "rapidfuzz-3.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:ed78c8e94f57b44292c1a0350f580e18d3a3c5c0800e253f1583580c1b417ad2"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e60814edd0c9b511b5f377d48b9782b88cfe8be07a98f99973669299c8bb318a"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f28952da055dbfe75828891cd3c9abf0984edc8640573c18b48c14c68ca5e06"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e8f93bc736020351a6f8e71666e1f486bb8bd5ce8112c443a30c77bfde0eb68"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76a4a11ba8f678c9e5876a7d465ab86def047a4fcc043617578368755d63a1bc"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc0e0d41ad8a056a9886bac91ff9d9978e54a244deb61c2972cc76b66752de9c"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e8ea35f2419c7d56b3e75fbde2698766daedb374f20eea28ac9b1f668ef4f74"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd340bbd025302276b5aa221dccfe43040c7babfc32f107c36ad783f2ffd8775"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:494eef2c68305ab75139034ea25328a04a548d297712d9cf887bf27c158c388b"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5a167344c1d6db06915fb0225592afdc24d8bafaaf02de07d4788ddd37f4bc2f"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8c7af25bda96ac799378ac8aba54a8ece732835c7b74cfc201b688a87ed11152"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d2a0f7e17f33e7890257367a1662b05fecaf56625f7dbb6446227aaa2b86448b"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4d0d26c7172bdb64f86ee0765c5b26ea1dc45c52389175888ec073b9b28f4305"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-win32.whl", hash = "sha256:6ad02bab756751c90fa27f3069d7b12146613061341459abf55f8190d899649f"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:b1472986fd9c5d318399a01a0881f4a0bf4950264131bb8e2deba9df6d8c362b"}, + {file = "rapidfuzz-3.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:c408f09649cbff8da76f8d3ad878b64ba7f7abdad1471efb293d2c075e80c822"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1bac4873f6186f5233b0084b266bfb459e997f4c21fc9f029918f44a9eccd304"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f9f12c2d0aa52b86206d2059916153876a9b1cf9dfb3cf2f344913167f1c3d4"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd501de6f7a8f83557d20613b58734d1cb5f0be78d794cde64fe43cfc63f5f2"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4416ca69af933d4a8ad30910149d3db6d084781d5c5fdedb713205389f535385"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f0821b9bdf18c5b7d51722b906b233a39b17f602501a966cfbd9b285f8ab83cd"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0edecc3f90c2653298d380f6ea73b536944b767520c2179ec5d40b9145e47aa"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4513dd01cee11e354c31b75f652d4d466c9440b6859f84e600bdebfccb17735a"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d9727b85511b912571a76ce53c7640ba2c44c364e71cef6d7359b5412739c570"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ab9eab33ee3213f7751dc07a1a61b8d9a3d748ca4458fffddd9defa6f0493c16"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6b01c1ddbb054283797967ddc5433d5c108d680e8fa2684cf368be05407b07e4"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:3857e335f97058c4b46fa39ca831290b70de554a5c5af0323d2f163b19c5f2a6"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d98a46cf07c0c875d27e8a7ed50f304d83063e49b9ab63f21c19c154b4c0d08d"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-win32.whl", hash = "sha256:c36539ed2c0173b053dafb221458812e178cfa3224ade0960599bec194637048"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:ec8d7d8567e14af34a7911c98f5ac74a3d4a743cd848643341fc92b12b3784ff"}, + {file = "rapidfuzz-3.11.0-cp39-cp39-win_arm64.whl", hash = "sha256:62171b270ecc4071be1c1f99960317db261d4c8c83c169e7f8ad119211fe7397"}, + {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f06e3c4c0a8badfc4910b9fd15beb1ad8f3b8fafa8ea82c023e5e607b66a78e4"}, + {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fe7aaf5a54821d340d21412f7f6e6272a9b17a0cbafc1d68f77f2fc11009dcd5"}, + {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25398d9ac7294e99876a3027ffc52c6bebeb2d702b1895af6ae9c541ee676702"}, + {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a52eea839e4bdc72c5e60a444d26004da00bb5bc6301e99b3dde18212e41465"}, + {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c87319b0ab9d269ab84f6453601fd49b35d9e4a601bbaef43743f26fabf496c"}, + {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3048c6ed29d693fba7d2a7caf165f5e0bb2b9743a0989012a98a47b975355cca"}, + {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b04f29735bad9f06bb731c214f27253bd8bedb248ef9b8a1b4c5bde65b838454"}, + {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7864e80a0d4e23eb6194254a81ee1216abdc53f9dc85b7f4d56668eced022eb8"}, + {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3794df87313dfb56fafd679b962e0613c88a293fd9bd5dd5c2793d66bf06a101"}, + {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d71da0012face6f45432a11bc59af19e62fac5a41f8ce489e80c0add8153c3d1"}, + {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff38378346b7018f42cbc1f6d1d3778e36e16d8595f79a312b31e7c25c50bd08"}, + {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6668321f90aa02a5a789d4e16058f2e4f2692c5230252425c3532a8a62bc3424"}, + {file = "rapidfuzz-3.11.0.tar.gz", hash = "sha256:a53ca4d3f52f00b393fab9b5913c5bafb9afc27d030c8a1db1283da6917a860f"}, ] [package.extras] all = ["numpy"] +[[package]] +name = "regex" +version = "2024.11.6" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.8" +files = [ + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, + {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, + {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, + {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, + {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, + {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, + {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, + {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, + {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, + {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, + {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, + {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, + {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, + {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, +] + [[package]] name = "requests" version = "2.32.3" @@ -3511,6 +4209,138 @@ files = [ {file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"}, ] +[[package]] +name = "safetensors" +version = "0.4.5" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "safetensors-0.4.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a63eaccd22243c67e4f2b1c3e258b257effc4acd78f3b9d397edc8cf8f1298a7"}, + {file = "safetensors-0.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:23fc9b4ec7b602915cbb4ec1a7c1ad96d2743c322f20ab709e2c35d1b66dad27"}, + {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6885016f34bef80ea1085b7e99b3c1f92cb1be78a49839203060f67b40aee761"}, + {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:133620f443450429322f238fda74d512c4008621227fccf2f8cf4a76206fea7c"}, + {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4fb3e0609ec12d2a77e882f07cced530b8262027f64b75d399f1504ffec0ba56"}, + {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0f1dd769f064adc33831f5e97ad07babbd728427f98e3e1db6902e369122737"}, + {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6d156bdb26732feada84f9388a9f135528c1ef5b05fae153da365ad4319c4c5"}, + {file = "safetensors-0.4.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e347d77e2c77eb7624400ccd09bed69d35c0332f417ce8c048d404a096c593b"}, + {file = "safetensors-0.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9f556eea3aec1d3d955403159fe2123ddd68e880f83954ee9b4a3f2e15e716b6"}, + {file = "safetensors-0.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9483f42be3b6bc8ff77dd67302de8ae411c4db39f7224dec66b0eb95822e4163"}, + {file = "safetensors-0.4.5-cp310-none-win32.whl", hash = "sha256:7389129c03fadd1ccc37fd1ebbc773f2b031483b04700923c3511d2a939252cc"}, + {file = "safetensors-0.4.5-cp310-none-win_amd64.whl", hash = "sha256:e98ef5524f8b6620c8cdef97220c0b6a5c1cef69852fcd2f174bb96c2bb316b1"}, + {file = "safetensors-0.4.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:21f848d7aebd5954f92538552d6d75f7c1b4500f51664078b5b49720d180e47c"}, + {file = "safetensors-0.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb07000b19d41e35eecef9a454f31a8b4718a185293f0d0b1c4b61d6e4487971"}, + {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09dedf7c2fda934ee68143202acff6e9e8eb0ddeeb4cfc24182bef999efa9f42"}, + {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:59b77e4b7a708988d84f26de3ebead61ef1659c73dcbc9946c18f3b1786d2688"}, + {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d3bc83e14d67adc2e9387e511097f254bd1b43c3020440e708858c684cbac68"}, + {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39371fc551c1072976073ab258c3119395294cf49cdc1f8476794627de3130df"}, + {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6c19feda32b931cae0acd42748a670bdf56bee6476a046af20181ad3fee4090"}, + {file = "safetensors-0.4.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a659467495de201e2f282063808a41170448c78bada1e62707b07a27b05e6943"}, + {file = "safetensors-0.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bad5e4b2476949bcd638a89f71b6916fa9a5cae5c1ae7eede337aca2100435c0"}, + {file = "safetensors-0.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a3a315a6d0054bc6889a17f5668a73f94f7fe55121ff59e0a199e3519c08565f"}, + {file = "safetensors-0.4.5-cp311-none-win32.whl", hash = "sha256:a01e232e6d3d5cf8b1667bc3b657a77bdab73f0743c26c1d3c5dd7ce86bd3a92"}, + {file = "safetensors-0.4.5-cp311-none-win_amd64.whl", hash = "sha256:cbd39cae1ad3e3ef6f63a6f07296b080c951f24cec60188378e43d3713000c04"}, + {file = "safetensors-0.4.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:473300314e026bd1043cef391bb16a8689453363381561b8a3e443870937cc1e"}, + {file = "safetensors-0.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:801183a0f76dc647f51a2d9141ad341f9665602a7899a693207a82fb102cc53e"}, + {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1524b54246e422ad6fb6aea1ac71edeeb77666efa67230e1faf6999df9b2e27f"}, + {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b3139098e3e8b2ad7afbca96d30ad29157b50c90861084e69fcb80dec7430461"}, + {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65573dc35be9059770808e276b017256fa30058802c29e1038eb1c00028502ea"}, + {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd33da8e9407559f8779c82a0448e2133737f922d71f884da27184549416bfed"}, + {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3685ce7ed036f916316b567152482b7e959dc754fcc4a8342333d222e05f407c"}, + {file = "safetensors-0.4.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dde2bf390d25f67908278d6f5d59e46211ef98e44108727084d4637ee70ab4f1"}, + {file = "safetensors-0.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7469d70d3de970b1698d47c11ebbf296a308702cbaae7fcb993944751cf985f4"}, + {file = "safetensors-0.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a6ba28118636a130ccbb968bc33d4684c48678695dba2590169d5ab03a45646"}, + {file = "safetensors-0.4.5-cp312-none-win32.whl", hash = "sha256:c859c7ed90b0047f58ee27751c8e56951452ed36a67afee1b0a87847d065eec6"}, + {file = "safetensors-0.4.5-cp312-none-win_amd64.whl", hash = "sha256:b5a8810ad6a6f933fff6c276eae92c1da217b39b4d8b1bc1c0b8af2d270dc532"}, + {file = "safetensors-0.4.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:25e5f8e2e92a74f05b4ca55686234c32aac19927903792b30ee6d7bd5653d54e"}, + {file = "safetensors-0.4.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:81efb124b58af39fcd684254c645e35692fea81c51627259cdf6d67ff4458916"}, + {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:585f1703a518b437f5103aa9cf70e9bd437cb78eea9c51024329e4fb8a3e3679"}, + {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4b99fbf72e3faf0b2f5f16e5e3458b93b7d0a83984fe8d5364c60aa169f2da89"}, + {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b17b299ca9966ca983ecda1c0791a3f07f9ca6ab5ded8ef3d283fff45f6bcd5f"}, + {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76ded72f69209c9780fdb23ea89e56d35c54ae6abcdec67ccb22af8e696e449a"}, + {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2783956926303dcfeb1de91a4d1204cd4089ab441e622e7caee0642281109db3"}, + {file = "safetensors-0.4.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d94581aab8c6b204def4d7320f07534d6ee34cd4855688004a4354e63b639a35"}, + {file = "safetensors-0.4.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:67e1e7cb8678bb1b37ac48ec0df04faf689e2f4e9e81e566b5c63d9f23748523"}, + {file = "safetensors-0.4.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:dbd280b07e6054ea68b0cb4b16ad9703e7d63cd6890f577cb98acc5354780142"}, + {file = "safetensors-0.4.5-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:77d9b228da8374c7262046a36c1f656ba32a93df6cc51cd4453af932011e77f1"}, + {file = "safetensors-0.4.5-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:500cac01d50b301ab7bb192353317035011c5ceeef0fca652f9f43c000bb7f8d"}, + {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75331c0c746f03158ded32465b7d0b0e24c5a22121743662a2393439c43a45cf"}, + {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:670e95fe34e0d591d0529e5e59fd9d3d72bc77b1444fcaa14dccda4f36b5a38b"}, + {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:098923e2574ff237c517d6e840acada8e5b311cb1fa226019105ed82e9c3b62f"}, + {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13ca0902d2648775089fa6a0c8fc9e6390c5f8ee576517d33f9261656f851e3f"}, + {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f0032bedc869c56f8d26259fe39cd21c5199cd57f2228d817a0e23e8370af25"}, + {file = "safetensors-0.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f4b15f51b4f8f2a512341d9ce3475cacc19c5fdfc5db1f0e19449e75f95c7dc8"}, + {file = "safetensors-0.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f6594d130d0ad933d885c6a7b75c5183cb0e8450f799b80a39eae2b8508955eb"}, + {file = "safetensors-0.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:60c828a27e852ded2c85fc0f87bf1ec20e464c5cd4d56ff0e0711855cc2e17f8"}, + {file = "safetensors-0.4.5-cp37-none-win32.whl", hash = "sha256:6d3de65718b86c3eeaa8b73a9c3d123f9307a96bbd7be9698e21e76a56443af5"}, + {file = "safetensors-0.4.5-cp37-none-win_amd64.whl", hash = "sha256:5a2d68a523a4cefd791156a4174189a4114cf0bf9c50ceb89f261600f3b2b81a"}, + {file = "safetensors-0.4.5-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:e7a97058f96340850da0601a3309f3d29d6191b0702b2da201e54c6e3e44ccf0"}, + {file = "safetensors-0.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:63bfd425e25f5c733f572e2246e08a1c38bd6f2e027d3f7c87e2e43f228d1345"}, + {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3664ac565d0e809b0b929dae7ccd74e4d3273cd0c6d1220c6430035befb678e"}, + {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:313514b0b9b73ff4ddfb4edd71860696dbe3c1c9dc4d5cc13dbd74da283d2cbf"}, + {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31fa33ee326f750a2f2134a6174773c281d9a266ccd000bd4686d8021f1f3dac"}, + {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:09566792588d77b68abe53754c9f1308fadd35c9f87be939e22c623eaacbed6b"}, + {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309aaec9b66cbf07ad3a2e5cb8a03205663324fea024ba391594423d0f00d9fe"}, + {file = "safetensors-0.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:53946c5813b8f9e26103c5efff4a931cc45d874f45229edd68557ffb35ffb9f8"}, + {file = "safetensors-0.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:868f9df9e99ad1e7f38c52194063a982bc88fedc7d05096f4f8160403aaf4bd6"}, + {file = "safetensors-0.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9cc9449bd0b0bc538bd5e268221f0c5590bc5c14c1934a6ae359d44410dc68c4"}, + {file = "safetensors-0.4.5-cp38-none-win32.whl", hash = "sha256:83c4f13a9e687335c3928f615cd63a37e3f8ef072a3f2a0599fa09f863fb06a2"}, + {file = "safetensors-0.4.5-cp38-none-win_amd64.whl", hash = "sha256:b98d40a2ffa560653f6274e15b27b3544e8e3713a44627ce268f419f35c49478"}, + {file = "safetensors-0.4.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:cf727bb1281d66699bef5683b04d98c894a2803442c490a8d45cd365abfbdeb2"}, + {file = "safetensors-0.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:96f1d038c827cdc552d97e71f522e1049fef0542be575421f7684756a748e457"}, + {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:139fbee92570ecea774e6344fee908907db79646d00b12c535f66bc78bd5ea2c"}, + {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c36302c1c69eebb383775a89645a32b9d266878fab619819ce660309d6176c9b"}, + {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d641f5b8149ea98deb5ffcf604d764aad1de38a8285f86771ce1abf8e74c4891"}, + {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b4db6a61d968de73722b858038c616a1bebd4a86abe2688e46ca0cc2d17558f2"}, + {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b75a616e02f21b6f1d5785b20cecbab5e2bd3f6358a90e8925b813d557666ec1"}, + {file = "safetensors-0.4.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:788ee7d04cc0e0e7f944c52ff05f52a4415b312f5efd2ee66389fb7685ee030c"}, + {file = "safetensors-0.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:87bc42bd04fd9ca31396d3ca0433db0be1411b6b53ac5a32b7845a85d01ffc2e"}, + {file = "safetensors-0.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4037676c86365a721a8c9510323a51861d703b399b78a6b4486a54a65a975fca"}, + {file = "safetensors-0.4.5-cp39-none-win32.whl", hash = "sha256:1500418454529d0ed5c1564bda376c4ddff43f30fce9517d9bee7bcce5a8ef50"}, + {file = "safetensors-0.4.5-cp39-none-win_amd64.whl", hash = "sha256:9d1a94b9d793ed8fe35ab6d5cea28d540a46559bafc6aae98f30ee0867000cab"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fdadf66b5a22ceb645d5435a0be7a0292ce59648ca1d46b352f13cff3ea80410"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d42ffd4c2259f31832cb17ff866c111684c87bd930892a1ba53fed28370c918c"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd8a1f6d2063a92cd04145c7fd9e31a1c7d85fbec20113a14b487563fdbc0597"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:951d2fcf1817f4fb0ef0b48f6696688a4e852a95922a042b3f96aaa67eedc920"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ac85d9a8c1af0e3132371d9f2d134695a06a96993c2e2f0bbe25debb9e3f67a"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e3cec4a29eb7fe8da0b1c7988bc3828183080439dd559f720414450de076fcab"}, + {file = "safetensors-0.4.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:21742b391b859e67b26c0b2ac37f52c9c0944a879a25ad2f9f9f3cd61e7fda8f"}, + {file = "safetensors-0.4.5-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c7db3006a4915151ce1913652e907cdede299b974641a83fbc092102ac41b644"}, + {file = "safetensors-0.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f68bf99ea970960a237f416ea394e266e0361895753df06e3e06e6ea7907d98b"}, + {file = "safetensors-0.4.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8158938cf3324172df024da511839d373c40fbfaa83e9abf467174b2910d7b4c"}, + {file = "safetensors-0.4.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:540ce6c4bf6b58cb0fd93fa5f143bc0ee341c93bb4f9287ccd92cf898cc1b0dd"}, + {file = "safetensors-0.4.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bfeaa1a699c6b9ed514bd15e6a91e74738b71125a9292159e3d6b7f0a53d2cde"}, + {file = "safetensors-0.4.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:01c8f00da537af711979e1b42a69a8ec9e1d7112f208e0e9b8a35d2c381085ef"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a0dd565f83b30f2ca79b5d35748d0d99dd4b3454f80e03dfb41f0038e3bdf180"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:023b6e5facda76989f4cba95a861b7e656b87e225f61811065d5c501f78cdb3f"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9633b663393d5796f0b60249549371e392b75a0b955c07e9c6f8708a87fc841f"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78dd8adfb48716233c45f676d6e48534d34b4bceb50162c13d1f0bdf6f78590a"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e8deb16c4321d61ae72533b8451ec4a9af8656d1c61ff81aa49f966406e4b68"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:52452fa5999dc50c4decaf0c53aa28371f7f1e0fe5c2dd9129059fbe1e1599c7"}, + {file = "safetensors-0.4.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d5f23198821e227cfc52d50fa989813513db381255c6d100927b012f0cfec63d"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f4beb84b6073b1247a773141a6331117e35d07134b3bb0383003f39971d414bb"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:68814d599d25ed2fdd045ed54d370d1d03cf35e02dce56de44c651f828fb9b7b"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0b6453c54c57c1781292c46593f8a37254b8b99004c68d6c3ce229688931a22"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adaa9c6dead67e2dd90d634f89131e43162012479d86e25618e821a03d1eb1dc"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73e7d408e9012cd17511b382b43547850969c7979efc2bc353f317abaf23c84c"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:775409ce0fcc58b10773fdb4221ed1eb007de10fe7adbdf8f5e8a56096b6f0bc"}, + {file = "safetensors-0.4.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:834001bed193e4440c4a3950a31059523ee5090605c907c66808664c932b549c"}, + {file = "safetensors-0.4.5.tar.gz", hash = "sha256:d73de19682deabb02524b3d5d1f8b3aaba94c72f1bbfc7911b9b9d5d391c0310"}, +] + +[package.extras] +all = ["safetensors[jax]", "safetensors[numpy]", "safetensors[paddlepaddle]", "safetensors[pinned-tf]", "safetensors[quality]", "safetensors[testing]", "safetensors[torch]"] +dev = ["safetensors[all]"] +jax = ["flax (>=0.6.3)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "safetensors[numpy]"] +mlx = ["mlx (>=0.0.9)"] +numpy = ["numpy (>=1.21.6)"] +paddlepaddle = ["paddlepaddle (>=2.4.1)", "safetensors[numpy]"] +pinned-tf = ["safetensors[numpy]", "tensorflow (==2.11.0)"] +quality = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "isort (>=5.5.4)"] +tensorflow = ["safetensors[numpy]", "tensorflow (>=2.11.0)"] +testing = ["h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "hypothesis (>=6.70.2)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "safetensors[numpy]", "setuptools-rust (>=1.5.2)"] +torch = ["safetensors[numpy]", "torch (>=1.10)"] + [[package]] name = "scalecodec" version = "1.2.11" @@ -3530,6 +4360,102 @@ requests = ">=2.24.0" [package.extras] test = ["coverage", "pytest"] +[[package]] +name = "scikit-learn" +version = "1.6.0" +description = "A set of python modules for machine learning and data mining" +optional = false +python-versions = ">=3.9" +files = [ + {file = "scikit_learn-1.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:366fb3fa47dce90afed3d6106183f4978d6f24cfd595c2373424171b915ee718"}, + {file = "scikit_learn-1.6.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:59cd96a8d9f8dfd546f5d6e9787e1b989e981388d7803abbc9efdcde61e47460"}, + {file = "scikit_learn-1.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efa7a579606c73a0b3d210e33ea410ea9e1af7933fe324cb7e6fbafae4ea5948"}, + {file = "scikit_learn-1.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a46d3ca0f11a540b8eaddaf5e38172d8cd65a86cb3e3632161ec96c0cffb774c"}, + {file = "scikit_learn-1.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:5be4577769c5dde6e1b53de8e6520f9b664ab5861dd57acee47ad119fd7405d6"}, + {file = "scikit_learn-1.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1f50b4f24cf12a81c3c09958ae3b864d7534934ca66ded3822de4996d25d7285"}, + {file = "scikit_learn-1.6.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:eb9ae21f387826da14b0b9cb1034f5048ddb9182da429c689f5f4a87dc96930b"}, + {file = "scikit_learn-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0baa91eeb8c32632628874a5c91885eaedd23b71504d24227925080da075837a"}, + {file = "scikit_learn-1.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c716d13ba0a2f8762d96ff78d3e0cde90bc9c9b5c13d6ab6bb9b2d6ca6705fd"}, + {file = "scikit_learn-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:9aafd94bafc841b626681e626be27bf1233d5a0f20f0a6fdb4bee1a1963c6643"}, + {file = "scikit_learn-1.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:04a5ba45c12a5ff81518aa4f1604e826a45d20e53da47b15871526cda4ff5174"}, + {file = "scikit_learn-1.6.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:21fadfc2ad7a1ce8bd1d90f23d17875b84ec765eecbbfc924ff11fb73db582ce"}, + {file = "scikit_learn-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30f34bb5fde90e020653bb84dcb38b6c83f90c70680dbd8c38bd9becbad7a127"}, + {file = "scikit_learn-1.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dad624cffe3062276a0881d4e441bc9e3b19d02d17757cd6ae79a9d192a0027"}, + {file = "scikit_learn-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:2fce7950a3fad85e0a61dc403df0f9345b53432ac0e47c50da210d22c60b6d85"}, + {file = "scikit_learn-1.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e5453b2e87ef8accedc5a8a4e6709f887ca01896cd7cc8a174fe39bd4bb00aef"}, + {file = "scikit_learn-1.6.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5fe11794236fb83bead2af26a87ced5d26e3370b8487430818b915dafab1724e"}, + {file = "scikit_learn-1.6.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61fe3dcec0d82ae280877a818ab652f4988371e32dd5451e75251bece79668b1"}, + {file = "scikit_learn-1.6.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b44e3a51e181933bdf9a4953cc69c6025b40d2b49e238233f149b98849beb4bf"}, + {file = "scikit_learn-1.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:a17860a562bac54384454d40b3f6155200c1c737c9399e6a97962c63fce503ac"}, + {file = "scikit_learn-1.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:98717d3c152f6842d36a70f21e1468fb2f1a2f8f2624d9a3f382211798516426"}, + {file = "scikit_learn-1.6.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:34e20bfac8ff0ebe0ff20fb16a4d6df5dc4cc9ce383e00c2ab67a526a3c67b18"}, + {file = "scikit_learn-1.6.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eba06d75815406091419e06dd650b91ebd1c5f836392a0d833ff36447c2b1bfa"}, + {file = "scikit_learn-1.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b6916d1cec1ff163c7d281e699d7a6a709da2f2c5ec7b10547e08cc788ddd3ae"}, + {file = "scikit_learn-1.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:66b1cf721a9f07f518eb545098226796c399c64abdcbf91c2b95d625068363da"}, + {file = "scikit_learn-1.6.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:7b35b60cf4cd6564b636e4a40516b3c61a4fa7a8b1f7a3ce80c38ebe04750bc3"}, + {file = "scikit_learn-1.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a73b1c2038c93bc7f4bf21f6c9828d5116c5d2268f7a20cfbbd41d3074d52083"}, + {file = "scikit_learn-1.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c3fa7d3dd5a0ec2d0baba0d644916fa2ab180ee37850c5d536245df916946bd"}, + {file = "scikit_learn-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:df778486a32518cda33818b7e3ce48c78cef1d5f640a6bc9d97c6d2e71449a51"}, + {file = "scikit_learn-1.6.0.tar.gz", hash = "sha256:9d58481f9f7499dff4196927aedd4285a0baec8caa3790efbe205f13de37dd6e"}, +] + +[package.dependencies] +joblib = ">=1.2.0" +numpy = ">=1.19.5" +scipy = ">=1.6.0" +threadpoolctl = ">=3.1.0" + +[package.extras] +benchmark = ["matplotlib (>=3.3.4)", "memory_profiler (>=0.57.0)", "pandas (>=1.1.5)"] +build = ["cython (>=3.0.10)", "meson-python (>=0.16.0)", "numpy (>=1.19.5)", "scipy (>=1.6.0)"] +docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.3.4)", "memory_profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pydata-sphinx-theme (>=0.15.3)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)", "sphinx (>=7.3.7)", "sphinx-copybutton (>=0.5.2)", "sphinx-design (>=0.5.0)", "sphinx-design (>=0.6.0)", "sphinx-gallery (>=0.17.1)", "sphinx-prompt (>=1.4.0)", "sphinx-remove-toctrees (>=1.0.0.post1)", "sphinxcontrib-sass (>=0.3.4)", "sphinxext-opengraph (>=0.9.1)", "towncrier (>=24.8.0)"] +examples = ["matplotlib (>=3.3.4)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)"] +install = ["joblib (>=1.2.0)", "numpy (>=1.19.5)", "scipy (>=1.6.0)", "threadpoolctl (>=3.1.0)"] +maintenance = ["conda-lock (==2.5.6)"] +tests = ["black (>=24.3.0)", "matplotlib (>=3.3.4)", "mypy (>=1.9)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pyarrow (>=12.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.5.1)", "scikit-image (>=0.17.2)"] + +[[package]] +name = "scipy" +version = "1.13.1" +description = "Fundamental algorithms for scientific computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, + {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f"}, + {file = "scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94"}, + {file = "scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa"}, + {file = "scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59"}, + {file = "scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884"}, + {file = "scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16"}, + {file = "scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d"}, + {file = "scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c"}, + {file = "scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2"}, + {file = "scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c"}, +] + +[package.dependencies] +numpy = ">=1.22.4,<2.3" + +[package.extras] +dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] +doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] +test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + [[package]] name = "sentry-sdk" version = "2.19.2" @@ -3803,14 +4729,70 @@ xxhash = ">=1.3.0,<4" [package.extras] test = ["coverage", "pytest"] +[[package]] +name = "sympy" +version = "1.13.1" +description = "Computer algebra system (CAS) in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8"}, + {file = "sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f"}, +] + +[package.dependencies] +mpmath = ">=1.1.0,<1.4" + +[package.extras] +dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"] + +[[package]] +name = "ta" +version = "0.11.0" +description = "Technical Analysis Library in Python" +optional = false +python-versions = "*" +files = [ + {file = "ta-0.11.0.tar.gz", hash = "sha256:de86af43418420bd6b088a2ea9b95483071bf453c522a8441bc2f12bcf8493fd"}, +] + +[package.dependencies] +numpy = "*" +pandas = "*" + +[[package]] +name = "template" +version = "0.7.6" +description = "A CLI tool for generating files from Jinja2 templates and environment variables." +optional = false +python-versions = "*" +files = [ + {file = "template-0.7.6-py2.py3-none-any.whl", hash = "sha256:9404c263631671d571de564fd44bba327a87c20079754f03981bad40317fd6c5"}, + {file = "template-0.7.6.tar.gz", hash = "sha256:d7b4c0f699f9e8528e1d16a853a4cee0f945c5a9db75c0a7cddb51df573f05ea"}, +] + +[package.dependencies] +Jinja2 = "*" +jmespath = "*" +PyYAML = "*" +toml = "*" + +[package.extras] +all = ["PyYAML", "jmespath", "netaddr", "toml"] +dev = ["pipenv"] +jmespath = ["jmespath"] +netaddr = ["netaddr"] +toml = ["toml"] +yaml = ["PyYAML"] + [[package]] name = "tensorboard" -version = "2.17.1" +version = "2.18.0" description = "TensorBoard lets you watch Tensors Flow" optional = false python-versions = ">=3.9" files = [ - {file = "tensorboard-2.17.1-py3-none-any.whl", hash = "sha256:253701a224000eeca01eee6f7e978aea7b408f60b91eb0babdb04e78947b773e"}, + {file = "tensorboard-2.18.0-py3-none-any.whl", hash = "sha256:107ca4821745f73e2aefa02c50ff70a9b694f39f790b11e6f682f7d326745eab"}, ] [package.dependencies] @@ -3839,27 +4821,27 @@ files = [ [[package]] name = "tensorflow" -version = "2.17.0" +version = "2.18.0" description = "TensorFlow is an open source machine learning framework for everyone." optional = false python-versions = ">=3.9" files = [ - {file = "tensorflow-2.17.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:515fe5ae8a9bc50312575412b08515f3ca66514c155078e0707bdffbea75d783"}, - {file = "tensorflow-2.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b36683ac28af20abc3a548c72bf4537b00df1b1f3dd39d59df3873fefaf26f15"}, - {file = "tensorflow-2.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147c93ded4cb7e500a65d3c26d74744ff41660db7a8afe2b00d1d08bf329b4ec"}, - {file = "tensorflow-2.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46090587f69e33637d17d7c3d94a790cac7d4bc5ff5ecbf3e71fdc6982fe96e"}, - {file = "tensorflow-2.17.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e8d26d6c24ccfb139db1306599257ca8f5cfe254ef2d023bfb667f374a17a64d"}, - {file = "tensorflow-2.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca82f98ea38fa6c9e08ccc69eb6c2fab5b35b30a8999115b8b63b6f02fc69d9d"}, - {file = "tensorflow-2.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8339777b1b5ebd8ffadaa8196f786e65fbb081a371d8e87b52f24563392d8552"}, - {file = "tensorflow-2.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:ef615c133cf4d592a073feda634ccbeb521a554be57de74f8c318d38febbeab5"}, - {file = "tensorflow-2.17.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ee18b4fcd627c5e872eabb25092af6c808b6ec77948662c88fc5c89a60eb0211"}, - {file = "tensorflow-2.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72adfef0ee39dd641627906fd7b244fcf21bdd8a87216a998ed74d9c74653aff"}, - {file = "tensorflow-2.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ad7bfea6afb4ded3928ca5b24df9fda876cea4904c103a5163fcc0c3483e7a4"}, - {file = "tensorflow-2.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:278bc80642d799adf08dc4e04f291aab603bba7457d50c1f9bc191ebbca83f43"}, - {file = "tensorflow-2.17.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:97f89e95d68b4b46e1072243b9f315c3b340e27cc07b1e1988e2ca97ad844305"}, - {file = "tensorflow-2.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dde37cff74ed22b8fa2eea944805b001ae38e96adc989666422bdea34f4e2d47"}, - {file = "tensorflow-2.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ae8e6746deb2ec807b902ba26d62fcffb6a6b53555a1a5906ec00416c5e4175"}, - {file = "tensorflow-2.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:8f80d11ad3766570deb6ff47d2bed2d166f51399ca08205e38ef024345571d6f"}, + {file = "tensorflow-2.18.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:8da90a9388a1f6dd00d626590d2b5810faffbb3e7367f9783d80efff882340ee"}, + {file = "tensorflow-2.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:589342fb9bdcab2e9af0f946da4ca97757677e297d934fcdc087e87db99d6353"}, + {file = "tensorflow-2.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1eb77fae50d699442726d1b23c7512c97cd688cc7d857b028683d4535bbf3709"}, + {file = "tensorflow-2.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:46f5a8b4e6273f488dc069fc3ac2211b23acd3d0437d919349c787fa341baa8a"}, + {file = "tensorflow-2.18.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:453cb60638a02fd26316fb36c8cbcf1569d33671f17c658ca0cf2b4626f851e7"}, + {file = "tensorflow-2.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85f1e7369af6d329b117b52e86093cd1e0458dd5404bf5b665853f873dd00b48"}, + {file = "tensorflow-2.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b8dd70fa3600bfce66ab529eebb804e1f9d7c863d2f71bc8fe9fc7a1ec3976"}, + {file = "tensorflow-2.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e8b0f499ef0b7652480a58e358a73844932047f21c42c56f7f3bdcaf0803edc"}, + {file = "tensorflow-2.18.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ec4133a215c59314e929e7cbe914579d3afbc7874d9fa924873ee633fe4f71d0"}, + {file = "tensorflow-2.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4822904b3559d8a9c25f0fe5fef191cfc1352ceca42ca64f2a7bc7ae0ff4a1f5"}, + {file = "tensorflow-2.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfdd65ea7e064064283dd78d529dd621257ee617218f63681935fd15817c6286"}, + {file = "tensorflow-2.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:a701c2d3dca5f2efcab315b2c217f140ebd3da80410744e87d77016b3aaf53cb"}, + {file = "tensorflow-2.18.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:336cace378c129c20fee6292f6a541165073d153a9a4c9cf4f14478a81895776"}, + {file = "tensorflow-2.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcfd32134de8f95515b2d0ced89cdae15484b787d3a21893e9291def06c10c4e"}, + {file = "tensorflow-2.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada1f7290c75b34748ee7378c1b77927e4044c94b8dc72dc75e7667c4fdaeb94"}, + {file = "tensorflow-2.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:f8c946df1cb384504578fac1c199a95322373b8e04abd88aa8ae01301df469ea"}, ] [package.dependencies] @@ -3869,25 +4851,25 @@ flatbuffers = ">=24.3.25" gast = ">=0.2.1,<0.5.0 || >0.5.0,<0.5.1 || >0.5.1,<0.5.2 || >0.5.2" google-pasta = ">=0.1.1" grpcio = ">=1.24.3,<2.0" -h5py = ">=3.10.0" -keras = ">=3.2.0" +h5py = ">=3.11.0" +keras = ">=3.5.0" libclang = ">=13.0.0" -ml-dtypes = ">=0.3.1,<0.5.0" -numpy = {version = ">=1.23.5,<2.0.0", markers = "python_version <= \"3.11\""} +ml-dtypes = ">=0.4.0,<0.5.0" +numpy = ">=1.26.0,<2.1.0" opt-einsum = ">=2.3.2" packaging = "*" -protobuf = ">=3.20.3,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +protobuf = ">=3.20.3,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev" requests = ">=2.21.0,<3" setuptools = "*" six = ">=1.12.0" -tensorboard = ">=2.17,<2.18" +tensorboard = ">=2.18,<2.19" tensorflow-io-gcs-filesystem = {version = ">=0.23.1", markers = "python_version < \"3.12\""} termcolor = ">=1.1.0" typing-extensions = ">=3.6.6" wrapt = ">=1.11.0" [package.extras] -and-cuda = ["nvidia-cublas-cu12 (==12.3.4.1)", "nvidia-cuda-cupti-cu12 (==12.3.101)", "nvidia-cuda-nvcc-cu12 (==12.3.107)", "nvidia-cuda-nvrtc-cu12 (==12.3.107)", "nvidia-cuda-runtime-cu12 (==12.3.101)", "nvidia-cudnn-cu12 (==8.9.7.29)", "nvidia-cufft-cu12 (==11.0.12.1)", "nvidia-curand-cu12 (==10.3.4.107)", "nvidia-cusolver-cu12 (==11.5.4.101)", "nvidia-cusparse-cu12 (==12.2.0.103)", "nvidia-nccl-cu12 (==2.19.3)", "nvidia-nvjitlink-cu12 (==12.3.101)"] +and-cuda = ["nvidia-cublas-cu12 (==12.5.3.2)", "nvidia-cuda-cupti-cu12 (==12.5.82)", "nvidia-cuda-nvcc-cu12 (==12.5.82)", "nvidia-cuda-nvrtc-cu12 (==12.5.82)", "nvidia-cuda-runtime-cu12 (==12.5.82)", "nvidia-cudnn-cu12 (==9.3.0.75)", "nvidia-cufft-cu12 (==11.2.3.61)", "nvidia-curand-cu12 (==10.3.6.82)", "nvidia-cusolver-cu12 (==11.6.3.83)", "nvidia-cusparse-cu12 (==12.5.1.3)", "nvidia-nccl-cu12 (==2.21.5)", "nvidia-nvjitlink-cu12 (==12.5.82)"] [[package]] name = "tensorflow-io-gcs-filesystem" @@ -3935,6 +4917,60 @@ files = [ [package.extras] tests = ["pytest", "pytest-cov"] +[[package]] +name = "threadpoolctl" +version = "3.5.0" +description = "threadpoolctl" +optional = false +python-versions = ">=3.8" +files = [ + {file = "threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467"}, + {file = "threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107"}, +] + +[[package]] +name = "tokenizers" +version = "0.21.0" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tokenizers-0.21.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:3c4c93eae637e7d2aaae3d376f06085164e1660f89304c0ab2b1d08a406636b2"}, + {file = "tokenizers-0.21.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:f53ea537c925422a2e0e92a24cce96f6bc5046bbef24a1652a5edc8ba975f62e"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b177fb54c4702ef611de0c069d9169f0004233890e0c4c5bd5508ae05abf193"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b43779a269f4629bebb114e19c3fca0223296ae9fea8bb9a7a6c6fb0657ff8e"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aeb255802be90acfd363626753fda0064a8df06031012fe7d52fd9a905eb00e"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8b09dbeb7a8d73ee204a70f94fc06ea0f17dcf0844f16102b9f414f0b7463ba"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:400832c0904f77ce87c40f1a8a27493071282f785724ae62144324f171377273"}, + {file = "tokenizers-0.21.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84ca973b3a96894d1707e189c14a774b701596d579ffc7e69debfc036a61a04"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eb7202d231b273c34ec67767378cd04c767e967fda12d4a9e36208a34e2f137e"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:089d56db6782a73a27fd8abf3ba21779f5b85d4a9f35e3b493c7bbcbbf0d539b"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:c87ca3dc48b9b1222d984b6b7490355a6fdb411a2d810f6f05977258400ddb74"}, + {file = "tokenizers-0.21.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4145505a973116f91bc3ac45988a92e618a6f83eb458f49ea0790df94ee243ff"}, + {file = "tokenizers-0.21.0-cp39-abi3-win32.whl", hash = "sha256:eb1702c2f27d25d9dd5b389cc1f2f51813e99f8ca30d9e25348db6585a97e24a"}, + {file = "tokenizers-0.21.0-cp39-abi3-win_amd64.whl", hash = "sha256:87841da5a25a3a5f70c102de371db120f41873b854ba65e52bccd57df5a3780c"}, + {file = "tokenizers-0.21.0.tar.gz", hash = "sha256:ee0894bf311b75b0c03079f33859ae4b2334d675d4e93f5a4132e1eae2834fe4"}, +] + +[package.dependencies] +huggingface-hub = ">=0.16.4,<1.0" + +[package.extras] +dev = ["tokenizers[testing]"] +docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] +testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + [[package]] name = "tomli" version = "2.2.1" @@ -3987,6 +5023,57 @@ files = [ {file = "toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02"}, ] +[[package]] +name = "torch" +version = "2.5.1" +description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "torch-2.5.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:71328e1bbe39d213b8721678f9dcac30dfc452a46d586f1d514a6aa0a99d4744"}, + {file = "torch-2.5.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:34bfa1a852e5714cbfa17f27c49d8ce35e1b7af5608c4bc6e81392c352dbc601"}, + {file = "torch-2.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:32a037bd98a241df6c93e4c789b683335da76a2ac142c0973675b715102dc5fa"}, + {file = "torch-2.5.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:23d062bf70776a3d04dbe74db950db2a5245e1ba4f27208a87f0d743b0d06e86"}, + {file = "torch-2.5.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:de5b7d6740c4b636ef4db92be922f0edc425b65ed78c5076c43c42d362a45457"}, + {file = "torch-2.5.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:340ce0432cad0d37f5a31be666896e16788f1adf8ad7be481196b503dad675b9"}, + {file = "torch-2.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:603c52d2fe06433c18b747d25f5c333f9c1d58615620578c326d66f258686f9a"}, + {file = "torch-2.5.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:31f8c39660962f9ae4eeec995e3049b5492eb7360dd4f07377658ef4d728fa4c"}, + {file = "torch-2.5.1-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:ed231a4b3a5952177fafb661213d690a72caaad97d5824dd4fc17ab9e15cec03"}, + {file = "torch-2.5.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:3f4b7f10a247e0dcd7ea97dc2d3bfbfc90302ed36d7f3952b0008d0df264e697"}, + {file = "torch-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:73e58e78f7d220917c5dbfad1a40e09df9929d3b95d25e57d9f8558f84c9a11c"}, + {file = "torch-2.5.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:8c712df61101964eb11910a846514011f0b6f5920c55dbf567bff8a34163d5b1"}, + {file = "torch-2.5.1-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:9b61edf3b4f6e3b0e0adda8b3960266b9009d02b37555971f4d1c8f7a05afed7"}, + {file = "torch-2.5.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1f3b7fb3cf7ab97fae52161423f81be8c6b8afac8d9760823fd623994581e1a3"}, + {file = "torch-2.5.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:7974e3dce28b5a21fb554b73e1bc9072c25dde873fa00d54280861e7a009d7dc"}, + {file = "torch-2.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:46c817d3ea33696ad3b9df5e774dba2257e9a4cd3c4a3afbf92f6bb13ac5ce2d"}, + {file = "torch-2.5.1-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:8046768b7f6d35b85d101b4b38cba8aa2f3cd51952bc4c06a49580f2ce682291"}, +] + +[package.dependencies] +filelock = "*" +fsspec = "*" +jinja2 = "*" +networkx = "*" +nvidia-cublas-cu12 = {version = "12.4.5.8", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-cupti-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-nvrtc-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-runtime-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cudnn-cu12 = {version = "9.1.0.70", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cufft-cu12 = {version = "11.2.1.3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-curand-cu12 = {version = "10.3.5.147", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusolver-cu12 = {version = "11.6.1.9", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusparse-cu12 = {version = "12.3.1.170", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nccl-cu12 = {version = "2.21.5", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvjitlink-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvtx-cu12 = {version = "12.4.127", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +sympy = {version = "1.13.1", markers = "python_version >= \"3.9\""} +triton = {version = "3.1.0", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.13\""} +typing-extensions = ">=4.8.0" + +[package.extras] +opt-einsum = ["opt-einsum (>=3.3)"] +optree = ["optree (>=0.12.0)"] + [[package]] name = "tqdm" version = "4.67.1" @@ -4008,6 +5095,97 @@ notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] +[[package]] +name = "transformers" +version = "4.47.1" +description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" +optional = false +python-versions = ">=3.9.0" +files = [ + {file = "transformers-4.47.1-py3-none-any.whl", hash = "sha256:d2f5d19bb6283cd66c893ec7e6d931d6370bbf1cc93633326ff1f41a40046c9c"}, + {file = "transformers-4.47.1.tar.gz", hash = "sha256:6c29c05a5f595e278481166539202bf8641281536df1c42357ee58a45d0a564a"}, +] + +[package.dependencies] +filelock = "*" +huggingface-hub = ">=0.24.0,<1.0" +numpy = ">=1.17" +packaging = ">=20.0" +pyyaml = ">=5.1" +regex = "!=2019.12.17" +requests = "*" +safetensors = ">=0.4.1" +tokenizers = ">=0.21,<0.22" +tqdm = ">=4.27" + +[package.extras] +accelerate = ["accelerate (>=0.26.0)"] +agents = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "datasets (!=2.5.0)", "diffusers", "opencv-python", "sentencepiece (>=0.1.91,!=0.1.92)", "torch"] +all = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av (==9.2.0)", "codecarbon (==1.2.0)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch", "torchaudio", "torchvision"] +audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +benchmark = ["optimum-benchmark (>=0.3.0)"] +codecarbon = ["codecarbon (==1.2.0)"] +deepspeed = ["accelerate (>=0.26.0)", "deepspeed (>=0.9.3)"] +deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk (<=3.8.1)", "optuna", "parameterized", "protobuf", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] +dev = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av (==9.2.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "nltk (<=3.8.1)", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] +dev-tensorflow = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "isort (>=5.5.4)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "nltk (<=3.8.1)", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.21,<0.22)", "urllib3 (<2.0.0)"] +dev-torch = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "kenlm", "libcst", "librosa", "nltk (<=3.8.1)", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] +flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)", "scipy (<1.13.0)"] +flax-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +ftfy = ["ftfy"] +integrations = ["optuna", "ray[tune] (>=2.7.0)", "sigopt"] +ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "rhoknp (>=1.1.0,<1.3.1)", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] +modelcreation = ["cookiecutter (==1.7.3)"] +natten = ["natten (>=0.14.6,<0.15.0)"] +onnx = ["onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "tf2onnx"] +onnxruntime = ["onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)"] +optuna = ["optuna"] +quality = ["GitPython (<3.1.19)", "datasets (!=2.5.0)", "isort (>=5.5.4)", "libcst", "rich", "ruff (==0.5.1)", "urllib3 (<2.0.0)"] +ray = ["ray[tune] (>=2.7.0)"] +retrieval = ["datasets (!=2.5.0)", "faiss-cpu"] +ruff = ["ruff (==0.5.1)"] +sagemaker = ["sagemaker (>=2.31.0)"] +sentencepiece = ["protobuf", "sentencepiece (>=0.1.91,!=0.1.92)"] +serving = ["fastapi", "pydantic", "starlette", "uvicorn"] +sigopt = ["sigopt"] +sklearn = ["scikit-learn"] +speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] +testing = ["GitPython (<3.1.19)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk (<=3.8.1)", "parameterized", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] +tf = ["keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx"] +tf-cpu = ["keras (>2.9,<2.16)", "keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow-cpu (>2.9,<2.16)", "tensorflow-probability (<0.24)", "tensorflow-text (<2.16)", "tf2onnx"] +tf-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +tiktoken = ["blobfile", "tiktoken"] +timm = ["timm (<=1.0.11)"] +tokenizers = ["tokenizers (>=0.21,<0.22)"] +torch = ["accelerate (>=0.26.0)", "torch"] +torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] +torch-vision = ["Pillow (>=10.0.1,<=15.0)", "torchvision"] +torchhub = ["filelock", "huggingface-hub (>=0.24.0,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.21,<0.22)", "torch", "tqdm (>=4.27)"] +video = ["av (==9.2.0)"] +vision = ["Pillow (>=10.0.1,<=15.0)"] + +[[package]] +name = "triton" +version = "3.1.0" +description = "A language and compiler for custom Deep Learning operations" +optional = false +python-versions = "*" +files = [ + {file = "triton-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b0dd10a925263abbe9fa37dcde67a5e9b2383fc269fdf59f5657cac38c5d1d8"}, + {file = "triton-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f34f6e7885d1bf0eaaf7ba875a5f0ce6f3c13ba98f9503651c1e6dc6757ed5c"}, + {file = "triton-3.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8182f42fd8080a7d39d666814fa36c5e30cc00ea7eeeb1a2983dbb4c99a0fdc"}, + {file = "triton-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dadaca7fc24de34e180271b5cf864c16755702e9f63a16f62df714a8099126a"}, + {file = "triton-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aafa9a20cd0d9fee523cd4504aa7131807a864cd77dcf6efe7e981f18b8c6c11"}, +] + +[package.dependencies] +filelock = "*" + +[package.extras] +build = ["cmake (>=3.20)", "lit"] +tests = ["autopep8", "flake8", "isort", "llnl-hatchet", "numpy", "pytest", "scipy (>=1.7.1)"] +tutorials = ["matplotlib", "pandas", "tabulate"] + [[package]] name = "typing-extensions" version = "4.12.2" @@ -4049,13 +5227,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.32.1" +version = "0.34.0" description = "The lightning-fast ASGI server." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e"}, - {file = "uvicorn-0.32.1.tar.gz", hash = "sha256:ee9519c246a72b1c084cea8d3b44ed6026e78a4a309cbedae9c37e4cb9fbb175"}, + {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"}, + {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"}, ] [package.dependencies] @@ -4066,47 +5244,77 @@ typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} [package.extras] standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] +[[package]] +name = "virtualenv" +version = "20.28.0" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.8" +files = [ + {file = "virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0"}, + {file = "virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + [[package]] name = "wandb" -version = "0.16.6" +version = "0.19.1" description = "A CLI and library for interacting with the Weights & Biases API." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "wandb-0.16.6-py3-none-any.whl", hash = "sha256:5810019a3b981c796e98ea58557a7c380f18834e0c6bdaed15df115522e5616e"}, - {file = "wandb-0.16.6.tar.gz", hash = "sha256:86f491e3012d715e0d7d7421a4d6de41abef643b7403046261f962f3e512fe1c"}, + {file = "wandb-0.19.1-py3-none-any.whl", hash = "sha256:b3195b3fe4d1b8131f64b956e6a5de7486cecfec179570986dbd6c64cd29b3c5"}, + {file = "wandb-0.19.1-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:788c20d8c3dabe490b50961dc91298886853dd8a0276a09ef3fc5c7f1f137c1d"}, + {file = "wandb-0.19.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:343d46c59aba3c30cf98ce8e0b9a2e1e52986a0ac0433d092de9aa856aeece98"}, + {file = "wandb-0.19.1-py3-none-macosx_11_0_x86_64.whl", hash = "sha256:7541efa8ffab715ba932fcb5117c4255a47cadebf0365d1dc1eb684a94744573"}, + {file = "wandb-0.19.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec64a859478b9f5bcf894aedd2bcccaf6917abe7b8adbd722b2a43b7063d33db"}, + {file = "wandb-0.19.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405000bc3d2e369934ff1266fcc55ff968e4a0f24c2fdaa9a0585b170c01b60c"}, + {file = "wandb-0.19.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:809b5ae83ed314b97db1077490f37d6c926c7c96fad9b6b5a2476534d54defb4"}, + {file = "wandb-0.19.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e01e9176b5ca9660226edcbfd9323019aa9bd5789a4b384d23ba53e062d3966e"}, + {file = "wandb-0.19.1-py3-none-win32.whl", hash = "sha256:093cc5c39ce629390c4f465b1ae89bab2ee9b29c2a46c8b5143858dd8c73264b"}, + {file = "wandb-0.19.1-py3-none-win_amd64.whl", hash = "sha256:1fc9d403fffb84e37f4e56a075b26b639e9f489899c9b9db9f46e3a7b7d93c64"}, + {file = "wandb-0.19.1.tar.gz", hash = "sha256:a9b4bf790c468e7b350eeaba2de002672a5cbaa3049899ab060940e2388f429a"}, ] [package.dependencies] -appdirs = ">=1.4.3" -Click = ">=7.1,<8.0.0 || >8.0.0" +click = ">=7.1,<8.0.0 || >8.0.0" docker-pycreds = ">=0.4.0" -GitPython = ">=1.0.0,<3.1.29 || >3.1.29" +eval-type-backport = {version = "*", markers = "python_version < \"3.10\""} +gitpython = ">=1.0.0,<3.1.29 || >3.1.29" +platformdirs = "*" protobuf = [ - {version = ">=3.15.0,<4.21.0 || >4.21.0,<5", markers = "python_version == \"3.9\" and sys_platform == \"linux\""}, - {version = ">=3.19.0,<4.21.0 || >4.21.0,<5", markers = "python_version > \"3.9\" or sys_platform != \"linux\""}, + {version = ">=3.15.0,<4.21.0 || >4.21.0,<5.28.0 || >5.28.0,<6", markers = "python_version == \"3.9\" and sys_platform == \"linux\""}, + {version = ">=3.19.0,<4.21.0 || >4.21.0,<5.28.0 || >5.28.0,<6", markers = "python_version > \"3.9\" or sys_platform != \"linux\""}, ] psutil = ">=5.0.0" -PyYAML = "*" +pydantic = ">=2.6,<3" +pyyaml = "*" requests = ">=2.0.0,<3" -sentry-sdk = ">=1.0.0" +sentry-sdk = ">=2.0.0" setproctitle = "*" setuptools = "*" -typing-extensions = {version = "*", markers = "python_version < \"3.10\""} +typing-extensions = {version = ">=4.4,<5", markers = "python_version < \"3.12\""} [package.extras] -async = ["httpx (>=0.23.0)"] aws = ["boto3"] azure = ["azure-identity", "azure-storage-blob"] gcp = ["google-cloud-storage"] -importers = ["filelock", "mlflow", "polars", "rich", "tenacity"] +importers = ["filelock", "mlflow", "polars (<=1.2.1)", "rich", "tenacity"] kubeflow = ["google-cloud-storage", "kubernetes", "minio", "sh"] -launch = ["PyYAML (>=6.0.0)", "awscli", "azure-containerregistry", "azure-identity", "azure-storage-blob", "boto3", "botocore", "chardet", "google-auth", "google-cloud-aiplatform", "google-cloud-artifact-registry", "google-cloud-compute", "google-cloud-storage", "iso8601", "kubernetes", "kubernetes-asyncio", "nbconvert", "nbformat", "optuna", "pydantic", "tomli", "typing-extensions"] -media = ["bokeh", "moviepy", "numpy", "pillow", "plotly (>=5.18.0)", "rdkit-pypi", "soundfile"] +launch = ["awscli", "azure-containerregistry", "azure-identity", "azure-storage-blob", "boto3", "botocore", "chardet", "google-auth", "google-cloud-aiplatform", "google-cloud-artifact-registry", "google-cloud-compute", "google-cloud-storage", "iso8601", "jsonschema", "kubernetes", "kubernetes-asyncio", "nbconvert", "nbformat", "optuna", "pydantic", "pyyaml (>=6.0.0)", "tomli", "typing-extensions"] +media = ["bokeh", "imageio", "moviepy", "numpy", "pillow", "plotly (>=5.18.0)", "rdkit", "soundfile"] models = ["cloudpickle"] perf = ["orjson"] -reports = ["pydantic (>=2.0.0)"] sweeps = ["sweeps (>=0.2.0)"] +workspaces = ["wandb-workspaces"] [[package]] name = "webencodings" @@ -4166,6 +5374,20 @@ files = [ [package.extras] test = ["pytest (>=6.0.0)", "setuptools (>=65)"] +[[package]] +name = "win32-setctime" +version = "1.2.0" +description = "A small Python utility to set file creation time on Windows" +optional = false +python-versions = ">=3.5" +files = [ + {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, + {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, +] + +[package.extras] +dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] + [[package]] name = "wrapt" version = "1.17.0" @@ -4470,17 +5692,16 @@ propcache = ">=0.2.0" [[package]] name = "yfinance" -version = "0.2.37" +version = "0.2.50" description = "Download market data from Yahoo! Finance API" optional = false python-versions = "*" files = [ - {file = "yfinance-0.2.37-py2.py3-none-any.whl", hash = "sha256:3ac75fa1cd3496ee76b6df5d63d29679487ea9447123c5b935d1593240737a8f"}, - {file = "yfinance-0.2.37.tar.gz", hash = "sha256:e5f78c9bd27bae7abfd0af9b7996620eaa9aba759d67f957296634d7d54f0cef"}, + {file = "yfinance-0.2.50-py2.py3-none-any.whl", hash = "sha256:0db13b19313043328fe88ded2ddc306ede7d901d0f5181462a1cce76acdbcd2a"}, + {file = "yfinance-0.2.50.tar.gz", hash = "sha256:33b379cad4261313dc93bfe3148d2f6e6083210e6341f0c93dd3af853019b1a0"}, ] [package.dependencies] -appdirs = ">=1.4.4" beautifulsoup4 = ">=4.11.1" frozendict = ">=2.3.4" html5lib = ">=1.1" @@ -4489,6 +5710,7 @@ multitasking = ">=0.0.7" numpy = ">=1.16.5" pandas = ">=1.3.0" peewee = ">=3.16.2" +platformdirs = ">=2.0.0" pytz = ">=2022.5" requests = ">=2.31" @@ -4518,4 +5740,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">= 3.9, < 3.12" -content-hash = "38288209b911c23a8aadb82192c05befd224f433ab68fe0e9c20807b2efd0c47" +content-hash = "0548c6c88d5d60cda87bdbee47c5446871aed569d21f9de0010db7225834b521" diff --git a/pyproject.toml b/pyproject.toml index f67b1900..c7ad6ea8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,18 +6,46 @@ authors = ["Foundry Digital"] readme = "README.md" [tool.poetry.dependencies] +# ^1.2.3 means >=1.2.3 and <2.0.0 +# ~1.2.3 means >=1.2.3 and <1.3.0 + # Python version - 3.9, 3.10, 3.11 python = ">= 3.9, < 3.12" -# Copied from requirements.txt +# Bittensor Version Strict bittensor = "7.4.0" -huggingface_hub = "0.22.2" -tensorflow = "2.17.0" -wandb = "0.16.6" -yfinance = "0.2.37" + +# Bittensor Dependencies We Also Need +setuptools = "~70.0.0" +pydantic = "^2.3.0" +numpy = "^1.26" + +# Subnet Specific Dependencies +torch = "^2.5.1" +ta = "^0.11.0" +joblib = "^1.4.2" +pandas = "^2.2.3" +pytz = "^2024.2" +tensorflow = "^2.18.0" +yfinance = "^0.2.50" +huggingface-hub = "^0.27.0" +loguru = "^0.7.3" +pandas-market-calendars = "^4.4.2" +python-dotenv = "^1.0.1" +scikit-learn = "^1.6.0" +wandb = "^0.19.1" [tool.poetry.group.dev.dependencies] pre-commit-hooks = "5.0.0" +black = "^24.10.0" +flake8 = "^7.1.1" +isort = "^5.13.2" +mypy = "^1.13.0" +pre-commit = "^4.0.1" +rich = "^13.9.4" +transformers = "^4.47.1" +template = "^0.7.6" +starlette = "~0.37.2" [tool.black] line-length = 120 diff --git a/requirements/dev_requirements.txt b/requirements/dev_requirements.txt deleted file mode 100644 index 81602a07..00000000 --- a/requirements/dev_requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -black -flake8 -isort -mypy -pre-commit -pre-commit-hooks==5.0.0 -rich -starlette -template -transformers diff --git a/requirements/requirements.txt b/requirements/requirements.txt deleted file mode 100644 index 5d71539f..00000000 --- a/requirements/requirements.txt +++ /dev/null @@ -1,17 +0,0 @@ -bittensor==7.4.0 -huggingface_hub==0.26.5 -joblib -loguru -numpy -pandas -pandas_market_calendars -pydantic -python_dotenv -pytz -scikit_learn -setuptools -ta -tensorflow==2.17.0 -torch -wandb==0.16.6 -yfinance==0.2.37 diff --git a/setup.py b/setup.py deleted file mode 100644 index 4a3f3ee5..00000000 --- a/setup.py +++ /dev/null @@ -1,94 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2023 Yuma Rao -# TODO(developer): Set your name -# Copyright © 2023 - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - -import codecs -import os -import re -from io import open -from os import path - -from setuptools import find_packages, setup - - -def read_requirements(path): - with open(path, "r") as f: - requirements = f.read().splitlines() - processed_requirements = [] - - for req in requirements: - # For git or other VCS links - if req.startswith("git+") or "@" in req: - pkg_name = re.search(r"(#egg=)([\w\-_]+)", req) - if pkg_name: - processed_requirements.append(pkg_name.group(2)) - else: - # You may decide to raise an exception here, - # if you want to ensure every VCS link has an #egg= at the end - continue - else: - processed_requirements.append(req) - return processed_requirements - - -here = path.abspath(path.dirname(__file__)) -requirements = read_requirements(path.join(here, "requirements", "requirements.txt")) -dev_requirements = read_requirements(path.join(here, "requirements", "dev_requirements.txt")) - -with open(path.join(here, "README.md"), encoding="utf-8") as f: - long_description = f.read() - -# loading version from setup.py -with codecs.open(os.path.join(here, "predictionnet/__init__.py"), encoding="utf-8") as init_file: - version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", init_file.read(), re.M) - version_string = version_match.group(1) - -setup( - name="predictionnet", # TODO(developer): Change this value to your module subnet name. - version=version_string, - description="bittensor_subnet_template", # TODO(developer): Change this value to your module subnet description. - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/opentensor/bittensor-subnet-template", # TODO(developer): Change this url to your module subnet github url. - author="bittensor.com", # TODO(developer): Change this value to your module subnet author name. - packages=find_packages(), - include_package_data=True, - author_email="", # TODO(developer): Change this value to your module subnet author email. - license="MIT", - python_requires=">=3.9", - install_requires=requirements, - extras_require={ - "DEV": dev_requirements, - }, - classifiers=[ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "Topic :: Software Development :: Build Tools", - # Pick your license as you wish - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Topic :: Scientific/Engineering", - "Topic :: Scientific/Engineering :: Mathematics", - "Topic :: Scientific/Engineering :: Artificial Intelligence", - "Topic :: Software Development", - "Topic :: Software Development :: Libraries", - "Topic :: Software Development :: Libraries :: Python Modules", - ], -) From 48c3228ad15889772372a4a0218b560ccb99e5eb Mon Sep 17 00:00:00 2001 From: Matthew Trudeau Date: Wed, 18 Dec 2024 16:33:41 -0500 Subject: [PATCH 45/57] Run pre-commit hooks --- .github/workflows/build.yml | 2 +- .github/workflows/ci.yml | 2 +- snp_oracle/__init__.py | 2 +- snp_oracle/neurons/miner.py | 5 +---- snp_oracle/neurons/validator.py | 2 -- snp_oracle/predictionnet/__init__.py | 1 - snp_oracle/predictionnet/api/example.py | 1 - snp_oracle/predictionnet/base/neuron.py | 1 - tests/test_package.py | 2 +- 9 files changed, 5 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6c7142d5..4af059f8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -81,4 +81,4 @@ jobs: # Run custom command(s) within venv #------------------------------------------------ - name: Run commands - run: ${{ inputs.command }} \ No newline at end of file + run: ${{ inputs.command }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3425cdd4..4f66fc49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,4 +68,4 @@ jobs: uses: ./.github/workflows/build.yml with: name: Unittests - command: poetry run pytest tests/ \ No newline at end of file + command: poetry run pytest tests/ diff --git a/snp_oracle/__init__.py b/snp_oracle/__init__.py index 93623516..e51a7ecd 100644 --- a/snp_oracle/__init__.py +++ b/snp_oracle/__init__.py @@ -2,4 +2,4 @@ __version__ = importlib.metadata.version(__name__ or __package__) version_split = __version__.split(".") -__spec_version__ = (1000 * int(version_split[0])) + (10 * int(version_split[1])) + (1 * int(version_split[2])) \ No newline at end of file +__spec_version__ = (1000 * int(version_split[0])) + (10 * int(version_split[1])) + (1 * int(version_split[2])) diff --git a/snp_oracle/neurons/miner.py b/snp_oracle/neurons/miner.py index 08d35625..76cf6911 100644 --- a/snp_oracle/neurons/miner.py +++ b/snp_oracle/neurons/miner.py @@ -3,17 +3,14 @@ import typing import bittensor as bt - from dotenv import load_dotenv from huggingface_hub import hf_hub_download from tensorflow.keras.models import load_model -import snp_oracle.predictionnet +import snp_oracle.predictionnet as predictionnet from snp_oracle.base_miner.get_data import prep_data, scale_data from snp_oracle.base_miner.predict import predict - from snp_oracle.predictionnet.base.miner import BaseMinerNeuron - from snp_oracle.predictionnet.utils.miner_hf import MinerHfInterface load_dotenv() diff --git a/snp_oracle/neurons/validator.py b/snp_oracle/neurons/validator.py index 8494e77b..ee0a3f48 100644 --- a/snp_oracle/neurons/validator.py +++ b/snp_oracle/neurons/validator.py @@ -12,11 +12,9 @@ from numpy import full, nan from snp_oracle import __version__ - from snp_oracle.predictionnet.base.validator import BaseValidatorNeuron from snp_oracle.predictionnet.utils.huggingface import HfInterface from snp_oracle.predictionnet.utils.uids import check_uid_availability - from snp_oracle.predictionnet.validator import forward load_dotenv() diff --git a/snp_oracle/predictionnet/__init__.py b/snp_oracle/predictionnet/__init__.py index 12830a0e..b574050e 100644 --- a/snp_oracle/predictionnet/__init__.py +++ b/snp_oracle/predictionnet/__init__.py @@ -2,4 +2,3 @@ # Import all submodules. from snp_oracle.predictionnet import base, protocol, validator - diff --git a/snp_oracle/predictionnet/api/example.py b/snp_oracle/predictionnet/api/example.py index cd7b6480..cc841b4c 100644 --- a/snp_oracle/predictionnet/api/example.py +++ b/snp_oracle/predictionnet/api/example.py @@ -1,5 +1,4 @@ import bittensor as bt - from predictionnet.api.get_query_axons import get_query_api_axons from predictionnet.api.prediction import PredictionAPI diff --git a/snp_oracle/predictionnet/base/neuron.py b/snp_oracle/predictionnet/base/neuron.py index 607f6872..3d1efef8 100644 --- a/snp_oracle/predictionnet/base/neuron.py +++ b/snp_oracle/predictionnet/base/neuron.py @@ -4,7 +4,6 @@ import bittensor as bt from snp_oracle import __spec_version__ as spec_version - from snp_oracle.predictionnet.utils.config import add_args, check_config, config from snp_oracle.predictionnet.utils.misc import ttl_get_block diff --git a/tests/test_package.py b/tests/test_package.py index a3084cce..1f1e03b9 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -11,4 +11,4 @@ def setUp(self): def test_package_version(self): # Check that version is as expected # Must update to increment package version successfully - self.assertEqual(__version__, "2.2.1") \ No newline at end of file + self.assertEqual(__version__, "2.2.1") From be016cfac6db6e4c9e3d644d3ea2f68536eed7b2 Mon Sep 17 00:00:00 2001 From: Matthew Trudeau Date: Wed, 18 Dec 2024 16:37:09 -0500 Subject: [PATCH 46/57] Delete unused tests --- tests/helpers.py | 144 ------------------------------- tests/test_template_validator.py | 92 -------------------- 2 files changed, 236 deletions(-) delete mode 100644 tests/helpers.py delete mode 100644 tests/test_template_validator.py diff --git a/tests/helpers.py b/tests/helpers.py deleted file mode 100644 index 7fcf148e..00000000 --- a/tests/helpers.py +++ /dev/null @@ -1,144 +0,0 @@ -# from typing import Union - -# from bittensor import AxonInfo, Balance, NeuronInfo, PrometheusInfo -# from bittensor.mock.wallet_mock import MockWallet as _MockWallet -# from bittensor.mock.wallet_mock import get_mock_coldkey as _get_mock_coldkey -# from bittensor.mock.wallet_mock import get_mock_hotkey as _get_mock_hotkey -# from bittensor.mock.wallet_mock import get_mock_wallet as _get_mock_wallet -# from rich.console import Console -# from rich.text import Text - - -# def __mock_wallet_factory__(*args, **kwargs) -> _MockWallet: -# """Returns a mock wallet object.""" - -# mock_wallet = _get_mock_wallet() - -# return mock_wallet - - -# class CLOSE_IN_VALUE: -# value: Union[float, int, Balance] -# tolerance: Union[float, int, Balance] - -# def __init__( -# self, -# value: Union[float, int, Balance], -# tolerance: Union[float, int, Balance] = 0.0, -# ) -> None: -# self.value = value -# self.tolerance = tolerance - -# def __eq__(self, __o: Union[float, int, Balance]) -> bool: -# # True if __o \in [value - tolerance, value + tolerance] -# # or if value \in [__o - tolerance, __o + tolerance] -# return ((self.value - self.tolerance) <= __o and __o <= (self.value + self.tolerance)) or ( -# (__o - self.tolerance) <= self.value and self.value <= (__o + self.tolerance) -# ) - - -# def get_mock_neuron(**kwargs) -> NeuronInfo: -# """ -# Returns a mock neuron with the given kwargs overriding the default values. -# """ - -# mock_neuron_d = dict( -# { -# "netuid": -1, # mock netuid -# "axon_info": AxonInfo( -# block=0, -# version=1, -# ip=0, -# port=0, -# ip_type=0, -# protocol=0, -# placeholder1=0, -# placeholder2=0, -# ), -# "prometheus_info": PrometheusInfo(block=0, version=1, ip=0, port=0, ip_type=0), -# "validator_permit": True, -# "uid": 1, -# "hotkey": "some_hotkey", -# "coldkey": "some_coldkey", -# "active": 0, -# "last_update": 0, -# "stake": {"some_coldkey": 1e12}, -# "total_stake": 1e12, -# "rank": 0.0, -# "trust": 0.0, -# "consensus": 0.0, -# "validator_trust": 0.0, -# "incentive": 0.0, -# "dividends": 0.0, -# "emission": 0.0, -# "bonds": [], -# "weights": [], -# "stake_dict": {}, -# "pruning_score": 0.0, -# "is_null": False, -# } -# ) - -# mock_neuron_d.update(kwargs) # update with kwargs - -# if kwargs.get("stake") is None and kwargs.get("coldkey") is not None: -# mock_neuron_d["stake"] = {kwargs.get("coldkey"): 1e12} - -# if kwargs.get("total_stake") is None: -# mock_neuron_d["total_stake"] = sum(mock_neuron_d["stake"].values()) - -# mock_neuron = NeuronInfo._neuron_dict_to_namespace(mock_neuron_d) - -# return mock_neuron - - -# def get_mock_neuron_by_uid(uid: int, **kwargs) -> NeuronInfo: -# return get_mock_neuron(uid=uid, hotkey=_get_mock_hotkey(uid), coldkey=_get_mock_coldkey(uid), **kwargs) - - -# class MockStatus: -# def __enter__(self): -# return self - -# def __exit__(self, exc_type, exc_value, traceback): -# pass - -# def start(self): -# pass - -# def stop(self): -# pass - -# def update(self, *args, **kwargs): -# MockConsole().print(*args, **kwargs) - - -# class MockConsole: -# """ -# Mocks the console object for status and print. -# Captures the last print output as a string. -# """ - -# captured_print = None - -# def status(self, *args, **kwargs): -# return MockStatus() - -# def print(self, *args, **kwargs): -# console = Console(width=1000, no_color=True, markup=False) # set width to 1000 to avoid truncation -# console.begin_capture() -# console.print(*args, **kwargs) -# self.captured_print = console.end_capture() - -# def clear(self, *args, **kwargs): -# pass - -# @staticmethod -# def remove_rich_syntax(text: str) -> str: -# """ -# Removes rich syntax from the given text. -# Removes markup and ansi syntax. -# """ -# output_no_syntax = Text.from_ansi(Text.from_markup(text).plain).plain - -# return output_no_syntax diff --git a/tests/test_template_validator.py b/tests/test_template_validator.py deleted file mode 100644 index 85dc7e55..00000000 --- a/tests/test_template_validator.py +++ /dev/null @@ -1,92 +0,0 @@ -# import sys -# import unittest - -# import bittensor as bt -# from numpy import array -# from template.base.validator import BaseValidatorNeuron -# from template.protocol import Dummy -# from template.utils.uids import get_random_uids -# from template.validator.reward import get_rewards - -# from neurons.validator import Neuron as Validator - - -# class TemplateValidatorNeuronTestCase(unittest.TestCase): -# """ -# This class contains unit tests for the RewardEvent classes. - -# The tests cover different scenarios where completions may or may not be successful and the reward events are checked that they don't contain missing values. -# The `reward` attribute of all RewardEvents is expected to be a float, and the `is_filter_model` attribute is expected to be a boolean. -# """ - -# def setUp(self): -# sys.argv = sys.argv[0] + ["--config", "tests/configs/validator.json"] - -# config = BaseValidatorNeuron.config() -# config.wallet._mock = True -# config.metagraph._mock = True -# config.subtensor._mock = True -# self.neuron = Validator(config) -# self.miner_uids = get_random_uids(self, k=10) - -# def test_run_single_step(self): -# # TODO: Test a single step -# pass - -# def test_sync_error_if_not_registered(self): -# # TODO: Test that the validator throws an error if it is not registered on metagraph -# pass - -# def test_forward(self): -# # TODO: Test that the forward function returns the correct value -# pass - -# def test_dummy_responses(self): -# # TODO: Test that the dummy responses are correctly constructed - -# responses = self.neuron.dendrite.query( -# # Send the query to miners in the network. -# axons=[self.neuron.metagraph.axons[uid] for uid in self.miner_uids], -# # Construct a dummy query. -# synapse=Dummy(dummy_input=self.neuron.step), -# # All responses have the deserialize function called on them before returning. -# deserialize=True, -# ) - -# for i, response in enumerate(responses): -# self.assertEqual(response, self.neuron.step * 2) - -# def test_reward(self): -# # TODO: Test that the reward function returns the correct value -# responses = self.dendrite.query( -# # Send the query to miners in the network. -# axons=[self.metagraph.axons[uid] for uid in self.miner_uids], -# # Construct a dummy query. -# synapse=Dummy(dummy_input=self.neuron.step), -# # All responses have the deserialize function called on them before returning. -# deserialize=True, -# ) - -# rewards = get_rewards(self.neuron, responses) -# expected_rewards = array([1.0] * len(responses)) -# self.assertEqual(rewards, expected_rewards) - -# def test_reward_with_nan(self): -# # TODO: Test that NaN rewards are correctly sanitized -# # TODO: Test that a bt.logging.warning is thrown when a NaN reward is sanitized -# responses = self.dendrite.query( -# # Send the query to miners in the network. -# axons=[self.metagraph.axons[uid] for uid in self.miner_uids], -# # Construct a dummy query. -# synapse=Dummy(dummy_input=self.neuron.step), -# # All responses have the deserialize function called on them before returning. -# deserialize=True, -# ) - -# rewards = get_rewards(self.neuron, responses) -# _ = rewards.clone() # expected rewards -# # Add NaN values to rewards -# rewards[0] = float("nan") - -# with self.assertLogs(bt.logging, level="WARNING") as _: -# self.neuron.update_scores(rewards, self.miner_uids) From c83ee565dd388b5a10191299b3e59ffe324d857b Mon Sep 17 00:00:00 2001 From: Matthew Trudeau Date: Wed, 18 Dec 2024 16:45:42 -0500 Subject: [PATCH 47/57] Add pytest dependency --- poetry.lock | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index e03d850a..62d4bbf7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1571,6 +1571,17 @@ files = [ docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] testing = ["pytest (>=3.5,!=3.7.3)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=1.2.3)", "pytest-cov", "pytest-enabler", "pytest-flake8", "pytest-mypy"] +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + [[package]] name = "isort" version = "5.13.2" @@ -2982,6 +2993,21 @@ docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-a test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] type = ["mypy (>=1.11.2)"] +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] name = "pre-commit" version = "4.0.1" @@ -3737,6 +3763,28 @@ cffi = ">=1.4.1" docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] +[[package]] +name = "pytest" +version = "8.3.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -5740,4 +5788,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">= 3.9, < 3.12" -content-hash = "0548c6c88d5d60cda87bdbee47c5446871aed569d21f9de0010db7225834b521" +content-hash = "1d71dd7c0b3ed60b1360fc9bd74591729b62f6eab617f2c8bdb3326723a8904d" diff --git a/pyproject.toml b/pyproject.toml index c7ad6ea8..a5736c74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,7 @@ rich = "^13.9.4" transformers = "^4.47.1" template = "^0.7.6" starlette = "~0.37.2" +pytest = "^8.3.4" [tool.black] line-length = 120 From d07beef42335642d2c3902d024c6412cac7e284b Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Wed, 18 Dec 2024 18:02:31 -0500 Subject: [PATCH 48/57] fix poetry error --- poetry.lock | 82 ++--------------------------------------------------- 1 file changed, 3 insertions(+), 79 deletions(-) diff --git a/poetry.lock b/poetry.lock index bd33e221..ac393882 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "absl-py" @@ -647,83 +647,6 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "coverage" -version = "7.6.9" -description = "Code coverage measurement for Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "coverage-7.6.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb"}, - {file = "coverage-7.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710"}, - {file = "coverage-7.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa"}, - {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1"}, - {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec"}, - {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3"}, - {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5"}, - {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073"}, - {file = "coverage-7.6.9-cp310-cp310-win32.whl", hash = "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198"}, - {file = "coverage-7.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717"}, - {file = "coverage-7.6.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9"}, - {file = "coverage-7.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c"}, - {file = "coverage-7.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7"}, - {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9"}, - {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4"}, - {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1"}, - {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b"}, - {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3"}, - {file = "coverage-7.6.9-cp311-cp311-win32.whl", hash = "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0"}, - {file = "coverage-7.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b"}, - {file = "coverage-7.6.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8"}, - {file = "coverage-7.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a"}, - {file = "coverage-7.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015"}, - {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3"}, - {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae"}, - {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4"}, - {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6"}, - {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f"}, - {file = "coverage-7.6.9-cp312-cp312-win32.whl", hash = "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692"}, - {file = "coverage-7.6.9-cp312-cp312-win_amd64.whl", hash = "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97"}, - {file = "coverage-7.6.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664"}, - {file = "coverage-7.6.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c"}, - {file = "coverage-7.6.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014"}, - {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00"}, - {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d"}, - {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a"}, - {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077"}, - {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb"}, - {file = "coverage-7.6.9-cp313-cp313-win32.whl", hash = "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba"}, - {file = "coverage-7.6.9-cp313-cp313-win_amd64.whl", hash = "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1"}, - {file = "coverage-7.6.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419"}, - {file = "coverage-7.6.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a"}, - {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4"}, - {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae"}, - {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030"}, - {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be"}, - {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e"}, - {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9"}, - {file = "coverage-7.6.9-cp313-cp313t-win32.whl", hash = "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b"}, - {file = "coverage-7.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611"}, - {file = "coverage-7.6.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902"}, - {file = "coverage-7.6.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be"}, - {file = "coverage-7.6.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599"}, - {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08"}, - {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464"}, - {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845"}, - {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf"}, - {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678"}, - {file = "coverage-7.6.9-cp39-cp39-win32.whl", hash = "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6"}, - {file = "coverage-7.6.9-cp39-cp39-win_amd64.whl", hash = "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4"}, - {file = "coverage-7.6.9-pp39.pp310-none-any.whl", hash = "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b"}, - {file = "coverage-7.6.9.tar.gz", hash = "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d"}, -] - -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - -[package.extras] -toml = ["tomli"] - [[package]] name = "cryptography" version = "42.0.8" @@ -3633,6 +3556,7 @@ files = [ [package.extras] test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"] +[[package]] name = "pycodestyle" version = "2.12.1" description = "Python style guide checker" @@ -5918,4 +5842,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">= 3.9, < 3.12" -content-hash = "1d71dd7c0b3ed60b1360fc9bd74591729b62f6eab617f2c8bdb3326723a8904d" +content-hash = "57e86779a3da6261051c5112eaefdca6ae9bd9935848690426add58b8fafc317" From a475125929ec95f0488fe42752fa077dbab33319 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Wed, 18 Dec 2024 18:09:27 -0500 Subject: [PATCH 49/57] fix isort issue --- tests/unit/test_dataset_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/test_dataset_manager.py b/tests/unit/test_dataset_manager.py index 0adbe662..ae5dc248 100644 --- a/tests/unit/test_dataset_manager.py +++ b/tests/unit/test_dataset_manager.py @@ -5,7 +5,6 @@ import pandas as pd import pytest - from predictionnet.utils.dataset_manager import DatasetManager From b7c9df2b36f58abc90b408f7fa6eb8fd6bd1765d Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Thu, 19 Dec 2024 09:02:19 -0500 Subject: [PATCH 50/57] fix test imports --- tests/unit/test_dataset_manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_dataset_manager.py b/tests/unit/test_dataset_manager.py index ae5dc248..9ec29856 100644 --- a/tests/unit/test_dataset_manager.py +++ b/tests/unit/test_dataset_manager.py @@ -5,7 +5,8 @@ import pandas as pd import pytest -from predictionnet.utils.dataset_manager import DatasetManager + +from snp_oracle.predictionnet.utils.dataset_manager import DatasetManager @pytest.fixture From ff0bd02b0651b3d29f484135d2ba4cc647fd7c4c Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Thu, 19 Dec 2024 09:33:10 -0500 Subject: [PATCH 51/57] fix yfinance error --- snp_oracle/base_miner/get_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snp_oracle/base_miner/get_data.py b/snp_oracle/base_miner/get_data.py index 76b5d1db..8da0e8aa 100644 --- a/snp_oracle/base_miner/get_data.py +++ b/snp_oracle/base_miner/get_data.py @@ -29,7 +29,7 @@ def prep_data(drop_na: bool = True) -> DataFrame: """ # Fetch S&P 500 data - when capturing data any interval, the max we can go back is 60 days # using Yahoo Finance's Python SDK - data = yf.download("^GSPC", period="60d", interval="5m") + data = yf.download("^GSPC", period="1m", interval="5m") # Calculate technical indicators - all technical indicators computed here are based on the 5m data # For example - SMA_50, is not a 50-day moving average, but is instead a 50 5m moving average From 0ec864286d63b468b76a9d48093530617c4782df Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Thu, 19 Dec 2024 09:47:43 -0500 Subject: [PATCH 52/57] update config files to reflect new structure --- miner.config.js | 2 +- validator.config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/miner.config.js b/miner.config.js index d4ccc2e2..3a69689c 100644 --- a/miner.config.js +++ b/miner.config.js @@ -3,7 +3,7 @@ module.exports = { { name: 'miner', script: 'python3', - args: './neurons/miner.py --netuid 28 --logging.debug --logging.trace --subtensor.network local --wallet.name walletName --wallet.hotkey hotkeyName --axon.port 8091 --hf_repo_id foundryservices/bittensor-sn28-base-lstm --model mining_models/base_lstm_new.h5' + args: './snp_oracle/neurons/miner.py --netuid 28 --logging.debug --logging.trace --subtensor.network local --wallet.name walletName --wallet.hotkey hotkeyName --axon.port 8091 --hf_repo_id foundryservices/bittensor-sn28-base-lstm --model mining_models/base_lstm_new.h5' }, ], }; diff --git a/validator.config.js b/validator.config.js index d70dd17a..a07ed2c2 100644 --- a/validator.config.js +++ b/validator.config.js @@ -3,7 +3,7 @@ module.exports = { { name: 'validator', script: 'python3', - args: './neurons/validator.py --netuid 28 --logging.debug --logging.trace --subtensor.network local --wallet.name walletName --wallet.hotkey hotkeyName' + args: './snp_oracle/neurons/validator.py --netuid 28 --logging.debug --logging.trace --subtensor.network local --wallet.name walletName --wallet.hotkey hotkeyName --neuron.organization huggingfaceOrganization' }, ], }; From 8e93c723acc17adccf6b03d41da5b6b31a2bed9d Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Thu, 19 Dec 2024 14:45:30 -0500 Subject: [PATCH 53/57] suggested changes --- poetry.lock | 4 +- pyproject.toml | 3 +- .../predictionnet/utils/dataset_manager.py | 53 +++---------------- snp_oracle/predictionnet/utils/miner_hf.py | 24 +++------ 4 files changed, 17 insertions(+), 67 deletions(-) diff --git a/poetry.lock b/poetry.lock index ac393882..ce103f8d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -5841,5 +5841,5 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" -python-versions = ">= 3.9, < 3.12" -content-hash = "57e86779a3da6261051c5112eaefdca6ae9bd9935848690426add58b8fafc317" +python-versions = ">3.9.1,<3.12" +content-hash = "afd62eaac40fe3bb28ea794f177d48a681b981b2b3bc8da12bb3d3718a01b141" diff --git a/pyproject.toml b/pyproject.toml index 5943d9c4..962c07d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" # ~1.2.3 means >=1.2.3 and <1.3.0 # Python version - 3.9, 3.10, 3.11 -python = ">= 3.9, < 3.12" +python = ">3.9.1,<3.12" # Bittensor Version Strict bittensor = "7.4.0" @@ -35,6 +35,7 @@ pandas-market-calendars = "^4.4.2" python-dotenv = "^1.0.1" scikit-learn = "^1.6.0" wandb = "^0.19.1" +cryptography = ">=42.0.5,<42.1.0" [tool.poetry.group.dev.dependencies] pre-commit-hooks = "5.0.0" diff --git a/snp_oracle/predictionnet/utils/dataset_manager.py b/snp_oracle/predictionnet/utils/dataset_manager.py index 304b4f33..e44fbb71 100644 --- a/snp_oracle/predictionnet/utils/dataset_manager.py +++ b/snp_oracle/predictionnet/utils/dataset_manager.py @@ -1,20 +1,3 @@ -# The MIT License (MIT) -# Copyright © 2024 Foundry Digital - -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the "Software"), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - import asyncio import json import os @@ -27,8 +10,6 @@ import bittensor as bt import pandas as pd -import pyarrow as pa -import pyarrow.parquet as pq from cryptography.fernet import Fernet from dotenv import load_dotenv from git import Repo @@ -132,13 +113,9 @@ def store_local_data( if metadata: full_metadata.update(metadata) - # Convert to PyArrow Table with metadata - table = pa.Table.from_pandas(miner_data) - for key, value in full_metadata.items(): - table = table.replace_schema_metadata({**table.schema.metadata, key.encode(): str(value).encode()}) - - # Write Parquet file with compression - pq.write_table(table, file_path, compression="snappy", use_dictionary=True, use_byte_stream_split=True) + # Add metadata to DataFrame and save to parquet + miner_data.attrs.update(full_metadata) + miner_data.to_parquet(file_path, engine="pyarrow", compression="snappy", index=False) return True, { "local_path": str(file_path), @@ -282,27 +259,9 @@ def decrypt_data(self, data_path: str, decryption_key: bytes) -> Tuple[bool, Dic temp_file.write(decrypted_data) temp_file.flush() - # Read Parquet file - table = pq.read_table(temp_file.name) - df = table.to_pandas() - - # Extract metadata from Parquet schema - metadata = {} - predictions = None - - if table.schema.metadata: - for key, value in table.schema.metadata.items(): - try: - key_str = key.decode() if isinstance(key, bytes) else key - value_str = value.decode() if isinstance(value, bytes) else value - - if key_str == "predictions": - predictions = json.loads(value_str) - else: - metadata[key_str] = value_str - except Exception as e: - bt.logging.error(f"Error while extracting metadata: {str(e)}") - continue + df = pd.read_parquet(temp_file.name) + metadata = df.attrs.copy() + predictions = json.loads(metadata.pop("predictions", "null")) return True, {"data": df, "metadata": metadata, "predictions": predictions} diff --git a/snp_oracle/predictionnet/utils/miner_hf.py b/snp_oracle/predictionnet/utils/miner_hf.py index 79e193cc..6af9cf65 100644 --- a/snp_oracle/predictionnet/utils/miner_hf.py +++ b/snp_oracle/predictionnet/utils/miner_hf.py @@ -4,8 +4,6 @@ import bittensor as bt import pandas as pd -import pyarrow as pa -import pyarrow.parquet as pq from cryptography.fernet import Fernet from dotenv import load_dotenv from huggingface_hub import HfApi @@ -92,7 +90,7 @@ def upload_model(self, repo_id=None, model_path=None, hotkey=None): bt.logging.debug(f"Error in upload_model: {str(e)}") return False, {"error": str(e)} - def upload_data(self, repo_id=None, data: pd.DataFrame = None, hotkey=None, encryption_key=None): + def upload_data(self, repo_id, data: pd.DataFrame, hotkey=None, encryption_key=None): """ Upload encrypted training/validation data to HuggingFace Hub using Parquet format. @@ -145,22 +143,14 @@ def upload_data(self, repo_id=None, data: pd.DataFrame = None, hotkey=None, encr temp_encrypted = os.path.join(temp_dir, data_filename) try: - # Convert to PyArrow Table with metadata - table = pa.Table.from_pandas(data) - table = table.replace_schema_metadata( - { - **table.schema.metadata, - b"timestamp": timestamp.encode(), - b"hotkey": hotkey.encode() if hotkey else b"", - } - ) + # Add metadata to the DataFrame + data.attrs["timestamp"] = timestamp + data.attrs["hotkey"] = hotkey if hotkey else "" - # Write Parquet file with compression - pq.write_table( - table, temp_parquet, compression="snappy", use_dictionary=True, use_byte_stream_split=True - ) + # Write to parquet with compression + data.to_parquet(temp_parquet, compression="snappy", engine="pyarrow") - # Read and encrypt the Parquet file + # Read and encrypt the temporary Parquet file with open(temp_parquet, "rb") as f: parquet_data = f.read() encrypted_data = fernet.encrypt(parquet_data) From 349acc28b88d8d50b550b7691ca1c4c4e7ade116 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Thu, 19 Dec 2024 15:22:01 -0500 Subject: [PATCH 54/57] update reward.py --- snp_oracle/predictionnet/validator/reward.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snp_oracle/predictionnet/validator/reward.py b/snp_oracle/predictionnet/validator/reward.py index 3038e5c4..beb88b53 100644 --- a/snp_oracle/predictionnet/validator/reward.py +++ b/snp_oracle/predictionnet/validator/reward.py @@ -240,7 +240,7 @@ def get_rewards( prediction_times.append(rounded_up_time - timedelta(minutes=(i + 1) * prediction_interval)) bt.logging.info(f"Prediction times: {prediction_times}") data = yf.download(tickers=ticker_symbol, period="5d", interval="5m", progress=False) - close_price = data.iloc[data.index.tz_localize(None).isin(prediction_times)]["Close"].tolist() + close_price = data.iloc[data.index.tz_localize(None).isin(prediction_times)]["Close"].values.tolist() if len(close_price) < (N_TIMEPOINTS + 1): # edge case where its between 9:30am and 10am close_price = data.iloc[-N_TIMEPOINTS - 1 :]["Close"].tolist() From 877c34e49417545601ae2ebed717103d5c3a9bd5 Mon Sep 17 00:00:00 2001 From: pcarlson-foundry-digital Date: Thu, 19 Dec 2024 15:30:28 -0500 Subject: [PATCH 55/57] ensure correct yfinance version --- poetry.lock | 21 +++++++++++++++----- pyproject.toml | 2 +- snp_oracle/base_miner/get_data.py | 2 +- snp_oracle/predictionnet/validator/reward.py | 2 +- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/poetry.lock b/poetry.lock index ce103f8d..214223ae 100644 --- a/poetry.lock +++ b/poetry.lock @@ -218,6 +218,17 @@ doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = "*" +files = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] + [[package]] name = "astunparse" version = "1.6.3" @@ -5794,16 +5805,17 @@ propcache = ">=0.2.0" [[package]] name = "yfinance" -version = "0.2.50" +version = "0.2.37" description = "Download market data from Yahoo! Finance API" optional = false python-versions = "*" files = [ - {file = "yfinance-0.2.50-py2.py3-none-any.whl", hash = "sha256:0db13b19313043328fe88ded2ddc306ede7d901d0f5181462a1cce76acdbcd2a"}, - {file = "yfinance-0.2.50.tar.gz", hash = "sha256:33b379cad4261313dc93bfe3148d2f6e6083210e6341f0c93dd3af853019b1a0"}, + {file = "yfinance-0.2.37-py2.py3-none-any.whl", hash = "sha256:3ac75fa1cd3496ee76b6df5d63d29679487ea9447123c5b935d1593240737a8f"}, + {file = "yfinance-0.2.37.tar.gz", hash = "sha256:e5f78c9bd27bae7abfd0af9b7996620eaa9aba759d67f957296634d7d54f0cef"}, ] [package.dependencies] +appdirs = ">=1.4.4" beautifulsoup4 = ">=4.11.1" frozendict = ">=2.3.4" html5lib = ">=1.1" @@ -5812,7 +5824,6 @@ multitasking = ">=0.0.7" numpy = ">=1.16.5" pandas = ">=1.3.0" peewee = ">=3.16.2" -platformdirs = ">=2.0.0" pytz = ">=2022.5" requests = ">=2.31" @@ -5842,4 +5853,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">3.9.1,<3.12" -content-hash = "afd62eaac40fe3bb28ea794f177d48a681b981b2b3bc8da12bb3d3718a01b141" +content-hash = "e31107374c14b5a2c82dfed31c60fbe028de4dac175ce19b917e3960cf9ef53c" diff --git a/pyproject.toml b/pyproject.toml index 962c07d7..7a508c64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ joblib = "^1.4.2" pandas = "^2.2.3" pytz = "^2024.2" tensorflow = "^2.18.0" -yfinance = "^0.2.50" +yfinance = "0.2.37" huggingface-hub = "^0.27.0" loguru = "^0.7.3" pyarrow = "^18.1.0" diff --git a/snp_oracle/base_miner/get_data.py b/snp_oracle/base_miner/get_data.py index 8da0e8aa..76b5d1db 100644 --- a/snp_oracle/base_miner/get_data.py +++ b/snp_oracle/base_miner/get_data.py @@ -29,7 +29,7 @@ def prep_data(drop_na: bool = True) -> DataFrame: """ # Fetch S&P 500 data - when capturing data any interval, the max we can go back is 60 days # using Yahoo Finance's Python SDK - data = yf.download("^GSPC", period="1m", interval="5m") + data = yf.download("^GSPC", period="60d", interval="5m") # Calculate technical indicators - all technical indicators computed here are based on the 5m data # For example - SMA_50, is not a 50-day moving average, but is instead a 50 5m moving average diff --git a/snp_oracle/predictionnet/validator/reward.py b/snp_oracle/predictionnet/validator/reward.py index beb88b53..3038e5c4 100644 --- a/snp_oracle/predictionnet/validator/reward.py +++ b/snp_oracle/predictionnet/validator/reward.py @@ -240,7 +240,7 @@ def get_rewards( prediction_times.append(rounded_up_time - timedelta(minutes=(i + 1) * prediction_interval)) bt.logging.info(f"Prediction times: {prediction_times}") data = yf.download(tickers=ticker_symbol, period="5d", interval="5m", progress=False) - close_price = data.iloc[data.index.tz_localize(None).isin(prediction_times)]["Close"].values.tolist() + close_price = data.iloc[data.index.tz_localize(None).isin(prediction_times)]["Close"].tolist() if len(close_price) < (N_TIMEPOINTS + 1): # edge case where its between 9:30am and 10am close_price = data.iloc[-N_TIMEPOINTS - 1 :]["Close"].tolist() From 327a84d4869ca8596db1f319492c45fbee2b2902 Mon Sep 17 00:00:00 2001 From: hscott Date: Thu, 19 Dec 2024 15:55:03 -0500 Subject: [PATCH 56/57] added Makefile --- Makefile | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..5a64da6c --- /dev/null +++ b/Makefile @@ -0,0 +1,57 @@ +################################################################################ +# User Parameters # +################################################################################ +coldkey = default +validator_hotkey = validator +miner_hotkey = miner +netuid = $(testnet_netuid) +network = $(testnet) + + +################################################################################ +# Network Parameters # +################################################################################ +finney = wss://entrypoint-finney.opentensor.ai:443 +testnet = wss://test.finney.opentensor.ai:443 +locanet = ws://127.0.0.1:9944 + +finney_netuid = 28 +testnet_netuid = 93 +localnet_netuid = 1 +logging_level = info # options= ['info', 'debug', 'trace'] + + +################################################################################ +# Commands # +################################################################################ +metagraph: + btcli subnet metagraph --netuid $(netuid) --subtensor.chain_endpoint $(network) + +register: + { \ + read -p 'Wallet name?: ' wallet_name ;\ + read -p 'Hotkey?: ' hotkey_name ;\ + btcli subnet register --netuid $(netuid) --wallet.name "$$wallet_name" --wallet.hotkey "$$hotkey_name" --subtensor.chain_endpoint $(network) ;\ + } + +validator: + pm2 start python --name validator -- ./snp_oracle/neurons/validator.py \ + --wallet.name $(coldkey) \ + --wallet.hotkey $(validator_hotkey) \ + --subtensor.chain_endpoint $(network) \ + --axon.port 8091 \ + --netuid $(netuid) \ + --logging.level $(logging_level) + +miner: + pm2 start python --name miner -- ./snp_oracle/neurons/miner.py \ + --wallet.name $(coldkey) \ + --wallet.hotkey $(miner_hotkey) \ + --subtensor.chain_endpoint $(network) \ + --axon.port 8092 \ + --netuid $(netuid) \ + --logging.level $(logging_level) \ + --vpermit_tao_limit 2 \ + --hf_repo_id foundryservices/bittensor-sn28-base-lstm + --model mining_models/base_lstm_new.h5 + From eea6e92f6d4ca13428d6ba794fc66baeb6b90e39 Mon Sep 17 00:00:00 2001 From: hscott Date: Thu, 19 Dec 2024 15:58:51 -0500 Subject: [PATCH 57/57] missing \ in makefile --- Makefile | 2 +- miner.config.js | 9 --------- validator.config.js | 9 --------- 3 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 miner.config.js delete mode 100644 validator.config.js diff --git a/Makefile b/Makefile index 5a64da6c..fa5a06d8 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,6 @@ miner: --netuid $(netuid) \ --logging.level $(logging_level) \ --vpermit_tao_limit 2 \ - --hf_repo_id foundryservices/bittensor-sn28-base-lstm + --hf_repo_id foundryservices/bittensor-sn28-base-lstm \ --model mining_models/base_lstm_new.h5 diff --git a/miner.config.js b/miner.config.js deleted file mode 100644 index 3a69689c..00000000 --- a/miner.config.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - apps: [ - { - name: 'miner', - script: 'python3', - args: './snp_oracle/neurons/miner.py --netuid 28 --logging.debug --logging.trace --subtensor.network local --wallet.name walletName --wallet.hotkey hotkeyName --axon.port 8091 --hf_repo_id foundryservices/bittensor-sn28-base-lstm --model mining_models/base_lstm_new.h5' - }, - ], -}; diff --git a/validator.config.js b/validator.config.js deleted file mode 100644 index a07ed2c2..00000000 --- a/validator.config.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - apps: [ - { - name: 'validator', - script: 'python3', - args: './snp_oracle/neurons/validator.py --netuid 28 --logging.debug --logging.trace --subtensor.network local --wallet.name walletName --wallet.hotkey hotkeyName --neuron.organization huggingfaceOrganization' - }, - ], -};