diff --git a/.gitignore b/.gitignore index 87accd9..9a3a91b 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,8 @@ share/python-wheels/ MANIFEST _version.py +mnemonic.txt + # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. diff --git a/dclimate_zarr_client/__init__.py b/dclimate_zarr_client/__init__.py index 1cc3d65..4b2d83b 100644 --- a/dclimate_zarr_client/__init__.py +++ b/dclimate_zarr_client/__init__.py @@ -1,6 +1,6 @@ # public API from .client import ( - load_ipfs_via_stac, + # load_ipfs_via_stac, load_s3, geo_temporal_query, ) # Use renamed function @@ -10,7 +10,7 @@ ) __all__ = [ - "load_ipfs_via_stac", + # "load_ipfs_via_stac", "load_s3", "geo_temporal_query", "GeotemporalData", diff --git a/dclimate_zarr_client/client.py b/dclimate_zarr_client/client.py index 8256d24..e28cb4d 100644 --- a/dclimate_zarr_client/client.py +++ b/dclimate_zarr_client/client.py @@ -12,10 +12,9 @@ ) from .geotemporal_data import GeotemporalData, DEFAULT_POINT_LIMIT from .s3_retrieval import get_dataset_from_s3 -from .ipfs_retrieval import ( - get_dataset_hamt_cid_from_stac, - _get_dataset_by_ipfs_cid, -) +# from .ipfs_retrieval import ( +# _get_dataset_by_ipfs_cid, +# ) # Define the top-level dClimate STAC catalog IPNS name DCLIMATE_STAC_CATALOG_IPNS = ( @@ -23,65 +22,65 @@ ) -def load_ipfs_via_stac( - dataset_name: str, - # as_of: typing.Optional[datetime.datetime] = None, # Removed as_of - gateway_uri_stem: str | None = None, - rpc_uri_stem: str | None = None, -) -> GeotemporalData: - """ - Load a Geotemporal dataset from IPFS/IPNS via the dClimate STAC catalog. +# def load_ipfs_via_stac( +# dataset_name: str, +# # as_of: typing.Optional[datetime.datetime] = None, # Removed as_of +# gateway_uri_stem: str | None = None, +# rpc_uri_stem: str | None = None, +# ) -> GeotemporalData: +# """ +# Load a Geotemporal dataset from IPFS/IPNS via the dClimate STAC catalog. - This function finds the dataset's IPNS name by navigating the STAC catalog - starting from a root IPNS name, resolves the dataset's IPNS name to its - current IPFS CID, and then loads the Zarr dataset. +# This function finds the dataset's IPNS name by navigating the STAC catalog +# starting from a root IPNS name, resolves the dataset's IPNS name to its +# current IPFS CID, and then loads the Zarr dataset. - Parameters - ---------- - dataset_name : str - The identifier (e.g., 'cpc-precip-conus') of the dataset as found in the STAC catalog. - as_of: datetime.datetime, optional - Pull in most recent data created before this time. If ``None``, just get most - recent. Defaults to ``None``. - gateway_uri_stem : str, optional - Custom IPFS HTTP Gateway URI stem (e.g., "http://localhost:8080"). - If None, uses the default from py-hamt's IPFSStore. - rpc_uri_stem : str, optional - Custom IPFS RPC API URI stem (e.g., "http://localhost:5001"). - If None, uses the default from py-hamt's IPFSStore. +# Parameters +# ---------- +# dataset_name : str +# The identifier (e.g., 'cpc-precip-conus') of the dataset as found in the STAC catalog. +# as_of: datetime.datetime, optional +# Pull in most recent data created before this time. If ``None``, just get most +# recent. Defaults to ``None``. +# gateway_uri_stem : str, optional +# Custom IPFS HTTP Gateway URI stem (e.g., "http://localhost:8080"). +# If None, uses the default from py-hamt's IPFSStore. +# rpc_uri_stem : str, optional +# Custom IPFS RPC API URI stem (e.g., "http://localhost:5001"). +# If None, uses the default from py-hamt's IPFSStore. - Returns - ------- - GeotemporalData - A wrapper around the loaded Xarray dataset. +# Returns +# ------- +# GeotemporalData +# A wrapper around the loaded Xarray dataset. - Raises - ------ - DatasetNotFoundError - If the dataset cannot be found in the STAC catalog or lacks the HAMT asset. - IpfsConnectionError - If connection to IPFS fails. - StacCatalogError - For issues during STAC parsing or traversal. - """ - # 1. Find the dataset's HAMT root IPFS CID from the STAC catalog - # Calls the renamed function from ipfs_retrieval - dataset_hamt_cid = get_dataset_hamt_cid_from_stac( - root_catalog_ipns=DCLIMATE_STAC_CATALOG_IPNS, - target_dataset_id=dataset_name, - gateway_uri_stem=gateway_uri_stem, # Pass through config - rpc_uri_stem=rpc_uri_stem, # Pass through config - ) +# Raises +# ------ +# DatasetNotFoundError +# If the dataset cannot be found in the STAC catalog or lacks the HAMT asset. +# IpfsConnectionError +# If connection to IPFS fails. +# StacCatalogError +# For issues during STAC parsing or traversal. +# """ +# # 1. Find the dataset's HAMT root IPFS CID from the STAC catalog +# # Calls the renamed function from ipfs_retrieval +# dataset_hamt_cid = await get_dataset_hamt_cid_from_stac( +# root_catalog_ipns=DCLIMATE_STAC_CATALOG_IPNS, +# target_dataset_id=dataset_name, +# gateway_uri_stem=gateway_uri_stem, # Pass through config +# rpc_uri_stem=rpc_uri_stem, # Pass through config +# ) - # 2. Load the dataset using the directly obtained HAMT IPFS CID - # NO LONGER NEED to resolve dataset IPNS name - ds = _get_dataset_by_ipfs_cid( - ipfs_cid=dataset_hamt_cid, - gateway_uri_stem=gateway_uri_stem, - rpc_uri_stem=rpc_uri_stem, - ) +# # 2. Load the dataset using the directly obtained HAMT IPFS CID +# # NO LONGER NEED to resolve dataset IPNS name +# ds = _get_dataset_by_ipfs_cid( +# ipfs_cid=dataset_hamt_cid, +# gateway_uri_stem=gateway_uri_stem, +# rpc_uri_stem=rpc_uri_stem, +# ) - return GeotemporalData(ds, dataset_name=dataset_name) +# return GeotemporalData(ds, dataset_name=dataset_name) def load_s3( @@ -220,14 +219,14 @@ def geo_temporal_query( point_limit = DEFAULT_POINT_LIMIT # Load the dataset based on the source - if source == "ipfs": - # *** CALL THE RENAMED FUNCTION *** - data = load_ipfs_via_stac( - dataset_name, - gateway_uri_stem=gateway_uri_stem, - rpc_uri_stem=rpc_uri_stem, - ) - elif source == "s3": + # if source == "ipfs": + # # *** CALL THE RENAMED FUNCTION *** + # data = load_ipfs_via_stac( + # dataset_name, + # gateway_uri_stem=gateway_uri_stem, + # rpc_uri_stem=rpc_uri_stem, + # ) + if source == "s3": if not bucket_name: raise ValueError("bucket_name is required when source is 's3'") data = load_s3(dataset_name, bucket_name) diff --git a/dclimate_zarr_client/contracts/StacRegistry.sol b/dclimate_zarr_client/contracts/StacRegistry.sol new file mode 100644 index 0000000..dc4946e --- /dev/null +++ b/dclimate_zarr_client/contracts/StacRegistry.sol @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/** + * @title Ownable + * @dev The Ownable contract has an owner address, and provides basic authorization control + * functions, this simplifies the implementation of "user permissions". + * For production, consider using OpenZeppelin's Ownable contract for more features. + */ +contract Ownable { + address public owner; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + constructor() { + owner = msg.sender; + } + + modifier onlyOwner() { + require(msg.sender == owner, "Ownable: caller is not the owner"); + _; + } + + function transferOwnership(address newOwner) public virtual onlyOwner { + require(newOwner != address(0), "Ownable: new owner is the zero address"); + emit OwnershipTransferred(owner, newOwner); + owner = newOwner; + } +} + + +/** + * @title StacRegistry (Developer-Friendly Version) + * @dev A central registry for STAC data. This version is optimized for off-chain + * developer convenience, returning human-readable string arrays. + * WARNING: The getter functions are gas-intensive and should NOT be called from other contracts. + */ +contract StacRegistry is Ownable { + + enum UpdateCategory { APPEND, REPLACE } + + string public stacRootCid; + + // --- Core Data Storage --- + // collection -> dataset -> type -> cid + mapping(bytes32 => mapping(bytes32 => mapping(bytes32 => string))) private _cids; + + // --- Arrays for Human-Readable Getters --- + string[] private _collectionNames; + mapping(bytes32 => string[]) private _datasetNames; // Key: keccak256(collectionName) + mapping(bytes32 => string[]) private _typeNames; // Key: keccak256(collectionName + datasetName) + + // --- Events --- + event StacRootUpdated(string newCid, address indexed updater); + event CidUpdated(string collection, string dataset, string dataType, string newCid, UpdateCategory category, address indexed updater); + event CollectionInitialized(string collectionName, address indexed initializer); + event DatasetInitialized(string collectionName, string datasetName, address indexed initializer); + event TypeInitialized(string collectionName, string datasetName, string typeName, address indexed initializer); + + + // --- Initialization Functions (Owner Only) --- + + function initCollection(string calldata collectionName) external onlyOwner { + bytes32 collectionKey = keccak256(abi.encodePacked(collectionName)); + require(_datasetNames[collectionKey].length == 0, "Collection already exists"); + _collectionNames.push(collectionName); + emit CollectionInitialized(collectionName, msg.sender); + } + + function initDataset(string calldata collectionName, string calldata datasetName) external onlyOwner { + bytes32 collectionKey = keccak256(abi.encodePacked(collectionName)); + bytes32 typeMapKey = keccak256(abi.encodePacked(collectionName, datasetName)); + require(_typeNames[typeMapKey].length == 0, "Dataset already exists"); + + _datasetNames[collectionKey].push(datasetName); + emit DatasetInitialized(collectionName, datasetName, msg.sender); + } + + function initType( + string calldata collectionName, + string calldata datasetName, + string calldata typeName, + string calldata initialCid + ) external onlyOwner { + bytes32 collectionKey = keccak256(abi.encodePacked(collectionName)); + bytes32 datasetKey = keccak256(abi.encodePacked(datasetName)); + bytes32 typeKey = keccak256(abi.encodePacked(typeName)); + bytes32 typeMapKey = keccak256(abi.encodePacked(collectionName, datasetName)); + + require(bytes(_cids[collectionKey][datasetKey][typeKey]).length == 0, "Type already exists"); + + _cids[collectionKey][datasetKey][typeKey] = initialCid; + _typeNames[typeMapKey].push(typeName); + emit TypeInitialized(collectionName, datasetName, typeName, msg.sender); + } + + // --- Update Functions (Owner Only) --- + + function updateStacRoot(string calldata newCid) external onlyOwner { + stacRootCid = newCid; + emit StacRootUpdated(newCid, msg.sender); + } + + function updateCid(string calldata path, string calldata newCid, UpdateCategory category) external onlyOwner { + (string memory collection, string memory dataset, string memory dataType) = _parsePath(path); + + bytes32 collectionKey = keccak256(abi.encodePacked(collection)); + bytes32 datasetKey = keccak256(abi.encodePacked(dataset)); + bytes32 typeKey = keccak256(abi.encodePacked(dataType)); + + require(bytes(_cids[collectionKey][datasetKey][typeKey]).length > 0, "Path does not resolve to an existing type"); + + _cids[collectionKey][datasetKey][typeKey] = newCid; + emit CidUpdated(collection, dataset, dataType, newCid, category, msg.sender); + } + + function changeOwner(address newOwner) external onlyOwner { + transferOwnership(newOwner); + } + + // --- Read-Only Functions (Human-Readable) --- + + /** + * @notice Resolves a path string to its stored CID. + */ + function resolve(string calldata path) external view returns (string memory) { + (string memory collection, string memory dataset, string memory dataType) = _parsePath(path); + bytes32 collectionKey = keccak256(abi.encodePacked(collection)); + bytes32 datasetKey = keccak256(abi.encodePacked(dataset)); + bytes32 typeKey = keccak256(abi.encodePacked(dataType)); + + return _cids[collectionKey][datasetKey][typeKey]; + } + + /** + * @notice Gets all initialized collection names as readable strings. + * @dev WARNING: Do not call this from another smart contract. + */ + function getCollections() external view returns (string[] memory) { + return _collectionNames; + } + + /** + * @notice Gets all dataset names for a given collection as readable strings. + * @dev WARNING: Do not call this from another smart contract. + */ + function getDatasets(string calldata collectionName) external view returns (string[] memory) { + bytes32 collectionKey = keccak256(abi.encodePacked(collectionName)); + return _datasetNames[collectionKey]; + } + + /** + * @notice Gets all type names for a given dataset as readable strings. + * @dev WARNING: Do not call this from another smart contract. + */ + function getTypes(string calldata collectionName, string calldata datasetName) external view returns (string[] memory) { + bytes32 typeMapKey = keccak256(abi.encodePacked(collectionName, datasetName)); + return _typeNames[typeMapKey]; + } + + + // --- Internal Helpers --- + + function _parsePath(string calldata path) internal pure returns (string memory collection, string memory dataset, string memory dataType) { + bytes memory pathBytes = bytes(path); + uint256 firstDelimiterIndex = 0; + for (uint256 i = 0; i < pathBytes.length; i++) { + if (pathBytes[i] == '-') { + firstDelimiterIndex = i; + break; + } + } + require(firstDelimiterIndex > 0, "Invalid path format: no first '-' found."); + uint256 lastDelimiterIndex = 0; + for (uint256 i = pathBytes.length - 1; i > 0; i--) { + if (pathBytes[i] == '-') { + lastDelimiterIndex = i; + break; + } + } + require(lastDelimiterIndex > firstDelimiterIndex, "Invalid path format: no second '-' found or delimiters misplaced."); + + // Use the _slice helper function to extract the parts + collection = string(_slice(pathBytes, 0, firstDelimiterIndex)); + dataset = string(_slice(pathBytes, firstDelimiterIndex + 1, lastDelimiterIndex)); + dataType = string(_slice(pathBytes, lastDelimiterIndex + 1, pathBytes.length)); + + require(bytes(collection).length > 0, "Collection part cannot be empty"); + require(bytes(dataset).length > 0, "Dataset part cannot be empty"); + require(bytes(dataType).length > 0, "Type part cannot be empty"); + } + + /** + * @dev Slices a bytes array. + * @param data The bytes array to slice. + * @param start The starting index. + * @param end The ending index. + * @return A new bytes array containing the slice. + */ + function _slice(bytes memory data, uint256 start, uint256 end) internal pure returns (bytes memory) { + require(end >= start, "Slice: end cannot be less than start"); + uint256 len = end - start; + bytes memory result = new bytes(len); + for (uint256 i = 0; i < len; i++) { + result[i] = data[start + i]; + } + return result; + } +} \ No newline at end of file diff --git a/dclimate_zarr_client/contracts/compiled/contracts/StacRegistry.json b/dclimate_zarr_client/contracts/compiled/contracts/StacRegistry.json new file mode 100644 index 0000000..f944a5f --- /dev/null +++ b/dclimate_zarr_client/contracts/compiled/contracts/StacRegistry.json @@ -0,0 +1 @@ +{"compilers":[{"contractTypes":["Ownable","StacRegistry"],"name":"solidity","settings":{"optimizer":{"enabled":true,"runs":200},"outputSelection":{"StacRegistry.sol":{"":["ast"],"*":["abi","bin-runtime","devdoc","userdoc","evm.bytecode.object","evm.bytecode.sourceMap","evm.deployedBytecode.object"]}}},"version":"0.8.30+commit.73712a01"}],"contractTypes":{"Ownable":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}],"contractName":"Ownable","deploymentBytecode":{"bytecode":"0x6080604052348015600e575f5ffd5b505f80546001600160a01b031916331790556101fa8061002d5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c80638da5cb5b14610038578063f2fde38b14610066575b5f5ffd5b5f5461004a906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f35b610079610074366004610197565b61007b565b005b5f546001600160a01b031633146100d95760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b6001600160a01b03811661013e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016100d0565b5f80546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35f80546001600160a01b0319166001600160a01b0392909216919091179055565b5f602082840312156101a7575f5ffd5b81356001600160a01b03811681146101bd575f5ffd5b939250505056fea2646970667358221220a09236cd587946c1eca0e2e4927163068ff8b84fd4f0dbfc1cf8ca49a592d08e64736f6c634300081e0033"},"devdoc":{"details":"The Ownable contract has an owner address, and provides basic authorization control functions, this simplifies the implementation of \"user permissions\". For production, consider using OpenZeppelin's Ownable contract for more features.","kind":"dev","methods":{},"title":"Ownable","version":1},"methodIdentifiers":{"owner()":"0x8da5cb5b","transferOwnership(address)":"0xf2fde38b"},"runtimeBytecode":{"bytecode":"0x608060405234801561000f575f5ffd5b5060043610610034575f3560e01c80638da5cb5b14610038578063f2fde38b14610066575b5f5ffd5b5f5461004a906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f35b610079610074366004610197565b61007b565b005b5f546001600160a01b031633146100d95760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b6001600160a01b03811661013e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016100d0565b5f80546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35f80546001600160a01b0319166001600160a01b0392909216919091179055565b5f602082840312156101a7575f5ffd5b81356001600160a01b03811681146101bd575f5ffd5b939250505056fea2646970667358221220a09236cd587946c1eca0e2e4927163068ff8b84fd4f0dbfc1cf8ca49a592d08e64736f6c634300081e0033"},"sourceId":"StacRegistry.sol","sourcemap":"333:554:0:-:0;;;473:49;;;;;;;;;-1:-1:-1;497:5:0;:18;;-1:-1:-1;;;;;;497:18:0;505:10;497:18;;;333:554;;;;;;","userdoc":{"kind":"user","methods":{},"version":1}},"StacRegistry":{"abi":[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"collection","type":"string"},{"indexed":false,"internalType":"string","name":"dataset","type":"string"},{"indexed":false,"internalType":"string","name":"dataType","type":"string"},{"indexed":false,"internalType":"string","name":"newCid","type":"string"},{"indexed":false,"internalType":"enum StacRegistry.UpdateCategory","name":"category","type":"uint8"},{"indexed":true,"internalType":"address","name":"updater","type":"address"}],"name":"CidUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"collectionName","type":"string"},{"indexed":true,"internalType":"address","name":"initializer","type":"address"}],"name":"CollectionInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"collectionName","type":"string"},{"indexed":false,"internalType":"string","name":"datasetName","type":"string"},{"indexed":true,"internalType":"address","name":"initializer","type":"address"}],"name":"DatasetInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"newCid","type":"string"},{"indexed":true,"internalType":"address","name":"updater","type":"address"}],"name":"StacRootUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"collectionName","type":"string"},{"indexed":false,"internalType":"string","name":"datasetName","type":"string"},{"indexed":false,"internalType":"string","name":"typeName","type":"string"},{"indexed":true,"internalType":"address","name":"initializer","type":"address"}],"name":"TypeInitialized","type":"event"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"changeOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getCollections","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"collectionName","type":"string"}],"name":"getDatasets","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"collectionName","type":"string"},{"internalType":"string","name":"datasetName","type":"string"}],"name":"getTypes","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"collectionName","type":"string"}],"name":"initCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"collectionName","type":"string"},{"internalType":"string","name":"datasetName","type":"string"}],"name":"initDataset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"collectionName","type":"string"},{"internalType":"string","name":"datasetName","type":"string"},{"internalType":"string","name":"typeName","type":"string"},{"internalType":"string","name":"initialCid","type":"string"}],"name":"initType","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"path","type":"string"}],"name":"resolve","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stacRootCid","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"path","type":"string"},{"internalType":"string","name":"newCid","type":"string"},{"internalType":"enum StacRegistry.UpdateCategory","name":"category","type":"uint8"}],"name":"updateCid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newCid","type":"string"}],"name":"updateStacRoot","outputs":[],"stateMutability":"nonpayable","type":"function"}],"contractName":"StacRegistry","deploymentBytecode":{"bytecode":"0x6080604052348015600e575f5ffd5b505f80546001600160a01b0319163317905561195d8061002d5f395ff3fe608060405234801561000f575f5ffd5b50600436106100cb575f3560e01c80636f0cf8cb11610088578063abbc971c11610063578063abbc971c146101a0578063e1064bba146101b3578063eff71c79146101c6578063f2fde38b146101d9575f5ffd5b80636f0cf8cb1461015b5780638da5cb5b14610163578063a6f9dae11461018d575f5ffd5b806304094107146100cf5780631da81c16146100e4578063461a4478146100f757806346e6358614610120578063607ef1631461013557806367433b1814610148575b5f5ffd5b6100e26100dd3660046112f1565b6101ec565b005b6100e26100f236600461135d565b610347565b61010a61010536600461135d565b6103c5565b60405161011791906113ca565b60405180910390f35b61012861050d565b60405161011791906113e3565b6101286101433660046112f1565b6105e1565b6100e261015636600461135d565b6106ed565b61010a610823565b5f54610175906001600160a01b031681565b6040516001600160a01b039091168152602001610117565b6100e261019b366004611446565b6108af565b6100e26101ae36600461146c565b6108e4565b6100e26101c13660046114f2565b610aae565b6101286101d436600461135d565b610ca8565b6100e26101e7366004611446565b610dae565b5f546001600160a01b0316331461021e5760405162461bcd60e51b8152600401610215906115c0565b60405180910390fd5b5f84846040516020016102329291906115f5565b6040516020818303038152906040528051906020012090505f858585856040516020016102629493929190611604565b60408051601f1981840301815291815281516020928301205f8181526005909352912054909150156102cf5760405162461bcd60e51b81526020600482015260166024820152754461746173657420616c72656164792065786973747360501b6044820152606401610215565b5f828152600460209081526040822080546001810182559083529120016102f78486836116bb565b50336001600160a01b03167f687a8a6a3df759865850822dd5a24a77d64ad29f7f0bff7908793cf5eb7e048287878787604051610337949392919061179d565b60405180910390a2505050505050565b5f546001600160a01b031633146103705760405162461bcd60e51b8152600401610215906115c0565b600161037d8284836116bb565b50336001600160a01b03167fe50e50e78a6e2a0383a03e5273dc58c6faf064c45be7342814de1d32c577c93083836040516103b99291906117ce565b60405180910390a25050565b60605f5f5f6103d48686610e95565b9250925092505f836040516020016103ec91906117e9565b6040516020818303038152906040528051906020012090505f8360405160200161041691906117e9565b6040516020818303038152906040528051906020012090505f8360405160200161044091906117e9565b60408051601f1981840301815291815281516020928301205f868152600284528281208682528452828120828252909352912080549192509061048290611637565b80601f01602080910402602001604051908101604052809291908181526020018280546104ae90611637565b80156104f95780601f106104d0576101008083540402835291602001916104f9565b820191905f5260205f20905b8154815290600101906020018083116104dc57829003601f168201915b505050505096505050505050505b92915050565b60606003805480602002602001604051908101604052809291908181526020015f905b828210156105d8578382905f5260205f2001805461054d90611637565b80601f016020809104026020016040519081016040528092919081815260200182805461057990611637565b80156105c45780601f1061059b576101008083540402835291602001916105c4565b820191905f5260205f20905b8154815290600101906020018083116105a757829003601f168201915b505050505081526020019060010190610530565b50505050905090565b60605f858585856040516020016105fb9493929190611604565b60408051601f1981840301815282825280516020918201205f8181526005835283812080548085028701850190955284865291955090929184015b828210156106de578382905f5260205f2001805461065390611637565b80601f016020809104026020016040519081016040528092919081815260200182805461067f90611637565b80156106ca5780601f106106a1576101008083540402835291602001916106ca565b820191905f5260205f20905b8154815290600101906020018083116106ad57829003601f168201915b505050505081526020019060010190610636565b50505050915050949350505050565b5f546001600160a01b031633146107165760405162461bcd60e51b8152600401610215906115c0565b5f828260405160200161072a9291906115f5565b60408051601f1981840301815291815281516020928301205f81815260049093529120549091501561079e5760405162461bcd60e51b815260206004820152601960248201527f436f6c6c656374696f6e20616c726561647920657869737473000000000000006044820152606401610215565b600380546001810182555f919091527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b016107da8385836116bb565b50336001600160a01b03167fb9c7ca2c1d7d6ac251b4b3d037f933e5f54305b9ca8ddb8047d04e4cc6172a2784846040516108169291906117ce565b60405180910390a2505050565b6001805461083090611637565b80601f016020809104026020016040519081016040528092919081815260200182805461085c90611637565b80156108a75780601f1061087e576101008083540402835291602001916108a7565b820191905f5260205f20905b81548152906001019060200180831161088a57829003601f168201915b505050505081565b5f546001600160a01b031633146108d85760405162461bcd60e51b8152600401610215906115c0565b6108e181610dae565b50565b5f546001600160a01b0316331461090d5760405162461bcd60e51b8152600401610215906115c0565b5f5f5f61091a8888610e95565b9250925092505f8360405160200161093291906117e9565b6040516020818303038152906040528051906020012090505f8360405160200161095c91906117e9565b6040516020818303038152906040528051906020012090505f8360405160200161098691906117e9565b60408051601f1981840301815291815281516020928301205f8681526002845282812086825284528281208282529093529082208054919350906109c990611637565b905011610a2a5760405162461bcd60e51b815260206004820152602960248201527f5061746820646f6573206e6f74207265736f6c766520746f20616e206578697360448201526874696e67207479706560b81b6064820152608401610215565b5f83815260026020908152604080832085845282528083208484529091529020610a55898b836116bb565b50336001600160a01b03167fb17ac485b95246393a5eaff3393ebca1a5e17646287d2e8d4cde350be3c965d08787878d8d8d604051610a99969594939291906117ff565b60405180910390a25050505050505050505050565b5f546001600160a01b03163314610ad75760405162461bcd60e51b8152600401610215906115c0565b5f8888604051602001610aeb9291906115f5565b6040516020818303038152906040528051906020012090505f8787604051602001610b179291906115f5565b6040516020818303038152906040528051906020012090505f8686604051602001610b439291906115f5565b6040516020818303038152906040528051906020012090505f8b8b8b8b604051602001610b739493929190611604565b60408051601f1981840301815291815281516020928301205f8781526002845282812087825284528281208682529093529120805491925090610bb590611637565b159050610bfa5760405162461bcd60e51b81526020600482015260136024820152725479706520616c72656164792065786973747360681b6044820152606401610215565b5f84815260026020908152604080832086845282528083208584529091529020610c258688836116bb565b505f81815260056020908152604082208054600181018255908352912001610c4e888a836116bb565b50336001600160a01b03167fb49dd854b8a9a06ab72f50457084391db365a4e2091d03b6e37198de6e6571658d8d8d8d8d8d604051610c929695949392919061187c565b60405180910390a2505050505050505050505050565b60605f8383604051602001610cbe9291906115f5565b60408051601f1981840301815282825280516020918201205f8181526004835283812080548085028701850190955284865291955090929184015b82821015610da1578382905f5260205f20018054610d1690611637565b80601f0160208091040260200160405190810160405280929190818152602001828054610d4290611637565b8015610d8d5780601f10610d6457610100808354040283529160200191610d8d565b820191905f5260205f20905b815481529060010190602001808311610d7057829003601f168201915b505050505081526020019060010190610cf9565b5050505091505092915050565b5f546001600160a01b03163314610dd75760405162461bcd60e51b8152600401610215906115c0565b6001600160a01b038116610e3c5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610215565b5f80546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35f80546001600160a01b0319166001600160a01b0392909216919091179055565b60608060605f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92018290525093945083925050505b8251811015610f1957828181518110610ef057610ef06118c4565b01602001516001600160f81b031916602d60f81b03610f1157809150610f19565b600101610ed5565b505f8111610f7a5760405162461bcd60e51b815260206004820152602860248201527f496e76616c6964207061746820666f726d61743a206e6f20666972737420272d60448201526713903337bab7321760c11b6064820152608401610215565b81515f908190610f8c906001906118ec565b90505b8015610fda57838181518110610fa757610fa76118c4565b01602001516001600160f81b031916602d60f81b03610fc857809150610fda565b80610fd2816118ff565b915050610f8f565b5081811161105a5760405162461bcd60e51b815260206004820152604160248201527f496e76616c6964207061746820666f726d61743a206e6f207365636f6e64202760448201527f2d2720666f756e64206f722064656c696d6974657273206d6973706c616365646064820152601760f91b608482015260a401610215565b611065835f84611190565b955061107c83611076846001611914565b83611190565b94506110948361108d836001611914565b8551611190565b93505f8651116110e65760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c656374696f6e20706172742063616e6e6f7420626520656d707479006044820152606401610215565b5f8551116111365760405162461bcd60e51b815260206004820152601c60248201527f4461746173657420706172742063616e6e6f7420626520656d707479000000006044820152606401610215565b5f8451116111865760405162461bcd60e51b815260206004820152601960248201527f5479706520706172742063616e6e6f7420626520656d707479000000000000006044820152606401610215565b5050509250925092565b6060828210156111ee5760405162461bcd60e51b8152602060048201526024808201527f536c6963653a20656e642063616e6e6f74206265206c657373207468616e20736044820152631d185c9d60e21b6064820152608401610215565b5f6111f984846118ec565b90505f8167ffffffffffffffff81111561121557611215611623565b6040519080825280601f01601f19166020018201604052801561123f576020820181803683370190505b5090505f5b828110156112a257866112578288611914565b81518110611267576112676118c4565b602001015160f81c60f81b828281518110611284576112846118c4565b60200101906001600160f81b03191690815f1a905350600101611244565b5095945050505050565b5f5f83601f8401126112bc575f5ffd5b50813567ffffffffffffffff8111156112d3575f5ffd5b6020830191508360208285010111156112ea575f5ffd5b9250929050565b5f5f5f5f60408587031215611304575f5ffd5b843567ffffffffffffffff81111561131a575f5ffd5b611326878288016112ac565b909550935050602085013567ffffffffffffffff811115611345575f5ffd5b611351878288016112ac565b95989497509550505050565b5f5f6020838503121561136e575f5ffd5b823567ffffffffffffffff811115611384575f5ffd5b611390858286016112ac565b90969095509350505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6113dc602083018461139c565b9392505050565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b8281101561143a57603f1987860301845261142585835161139c565b94506020938401939190910190600101611409565b50929695505050505050565b5f60208284031215611456575f5ffd5b81356001600160a01b03811681146113dc575f5ffd5b5f5f5f5f5f60608688031215611480575f5ffd5b853567ffffffffffffffff811115611496575f5ffd5b6114a2888289016112ac565b909650945050602086013567ffffffffffffffff8111156114c1575f5ffd5b6114cd888289016112ac565b9094509250506040860135600281106114e4575f5ffd5b809150509295509295909350565b5f5f5f5f5f5f5f5f6080898b031215611509575f5ffd5b883567ffffffffffffffff81111561151f575f5ffd5b61152b8b828c016112ac565b909950975050602089013567ffffffffffffffff81111561154a575f5ffd5b6115568b828c016112ac565b909750955050604089013567ffffffffffffffff811115611575575f5ffd5b6115818b828c016112ac565b909550935050606089013567ffffffffffffffff8111156115a0575f5ffd5b6115ac8b828c016112ac565b999c989b5096995094979396929594505050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b818382375f9101908152919050565b838582375f8482015f8152838582375f93019283525090949350505050565b634e487b7160e01b5f52604160045260245ffd5b600181811c9082168061164b57607f821691505b60208210810361166957634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156116b657805f5260205f20601f840160051c810160208510156116945750805b601f840160051c820191505b818110156116b3575f81556001016116a0565b50505b505050565b67ffffffffffffffff8311156116d3576116d3611623565b6116e7836116e18354611637565b8361166f565b5f601f841160018114611718575f85156117015750838201355b5f19600387901b1c1916600186901b1783556116b3565b5f83815260208120601f198716915b828110156117475786850135825560209485019460019092019101611727565b5086821015611763575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f6117b0604083018688611775565b82810360208401526117c3818587611775565b979650505050505050565b602081525f6117e1602083018486611775565b949350505050565b5f82518060208501845e5f920191825250919050565b60a081525f61181160a083018961139c565b8281036020840152611823818961139c565b90508281036040840152611837818861139c565b9050828103606084015261184c818688611775565b9150506002831061186b57634e487b7160e01b5f52602160045260245ffd5b826080830152979650505050505050565b606081525f61188f60608301888a611775565b82810360208401526118a2818789611775565b905082810360408401526118b7818587611775565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b81810381811115610507576105076118d8565b5f8161190d5761190d6118d8565b505f190190565b80820180821115610507576105076118d856fea264697066735822122081c3321f2311eb7d3804351732662070456e8f14460096f93b4e3a8c62b06a7664736f6c634300081e0033"},"devdoc":{"details":"A central registry for STAC data. This version is optimized for off-chain developer convenience, returning human-readable string arrays. WARNING: The getter functions are gas-intensive and should NOT be called from other contracts.","kind":"dev","methods":{"getCollections()":{"details":"WARNING: Do not call this from another smart contract."},"getDatasets(string)":{"details":"WARNING: Do not call this from another smart contract."},"getTypes(string,string)":{"details":"WARNING: Do not call this from another smart contract."}},"title":"StacRegistry (Developer-Friendly Version)","version":1},"methodIdentifiers":{"changeOwner(address)":"0xa6f9dae1","getCollections()":"0x46e63586","getDatasets(string)":"0xeff71c79","getTypes(string,string)":"0x607ef163","initCollection(string)":"0x67433b18","initDataset(string,string)":"0x04094107","initType(string,string,string,string)":"0xe1064bba","owner()":"0x8da5cb5b","resolve(string)":"0x461a4478","stacRootCid()":"0x6f0cf8cb","transferOwnership(address)":"0xf2fde38b","updateCid(string,string,uint8)":"0xabbc971c","updateStacRoot(string)":"0x1da81c16"},"runtimeBytecode":{"bytecode":"0x608060405234801561000f575f5ffd5b50600436106100cb575f3560e01c80636f0cf8cb11610088578063abbc971c11610063578063abbc971c146101a0578063e1064bba146101b3578063eff71c79146101c6578063f2fde38b146101d9575f5ffd5b80636f0cf8cb1461015b5780638da5cb5b14610163578063a6f9dae11461018d575f5ffd5b806304094107146100cf5780631da81c16146100e4578063461a4478146100f757806346e6358614610120578063607ef1631461013557806367433b1814610148575b5f5ffd5b6100e26100dd3660046112f1565b6101ec565b005b6100e26100f236600461135d565b610347565b61010a61010536600461135d565b6103c5565b60405161011791906113ca565b60405180910390f35b61012861050d565b60405161011791906113e3565b6101286101433660046112f1565b6105e1565b6100e261015636600461135d565b6106ed565b61010a610823565b5f54610175906001600160a01b031681565b6040516001600160a01b039091168152602001610117565b6100e261019b366004611446565b6108af565b6100e26101ae36600461146c565b6108e4565b6100e26101c13660046114f2565b610aae565b6101286101d436600461135d565b610ca8565b6100e26101e7366004611446565b610dae565b5f546001600160a01b0316331461021e5760405162461bcd60e51b8152600401610215906115c0565b60405180910390fd5b5f84846040516020016102329291906115f5565b6040516020818303038152906040528051906020012090505f858585856040516020016102629493929190611604565b60408051601f1981840301815291815281516020928301205f8181526005909352912054909150156102cf5760405162461bcd60e51b81526020600482015260166024820152754461746173657420616c72656164792065786973747360501b6044820152606401610215565b5f828152600460209081526040822080546001810182559083529120016102f78486836116bb565b50336001600160a01b03167f687a8a6a3df759865850822dd5a24a77d64ad29f7f0bff7908793cf5eb7e048287878787604051610337949392919061179d565b60405180910390a2505050505050565b5f546001600160a01b031633146103705760405162461bcd60e51b8152600401610215906115c0565b600161037d8284836116bb565b50336001600160a01b03167fe50e50e78a6e2a0383a03e5273dc58c6faf064c45be7342814de1d32c577c93083836040516103b99291906117ce565b60405180910390a25050565b60605f5f5f6103d48686610e95565b9250925092505f836040516020016103ec91906117e9565b6040516020818303038152906040528051906020012090505f8360405160200161041691906117e9565b6040516020818303038152906040528051906020012090505f8360405160200161044091906117e9565b60408051601f1981840301815291815281516020928301205f868152600284528281208682528452828120828252909352912080549192509061048290611637565b80601f01602080910402602001604051908101604052809291908181526020018280546104ae90611637565b80156104f95780601f106104d0576101008083540402835291602001916104f9565b820191905f5260205f20905b8154815290600101906020018083116104dc57829003601f168201915b505050505096505050505050505b92915050565b60606003805480602002602001604051908101604052809291908181526020015f905b828210156105d8578382905f5260205f2001805461054d90611637565b80601f016020809104026020016040519081016040528092919081815260200182805461057990611637565b80156105c45780601f1061059b576101008083540402835291602001916105c4565b820191905f5260205f20905b8154815290600101906020018083116105a757829003601f168201915b505050505081526020019060010190610530565b50505050905090565b60605f858585856040516020016105fb9493929190611604565b60408051601f1981840301815282825280516020918201205f8181526005835283812080548085028701850190955284865291955090929184015b828210156106de578382905f5260205f2001805461065390611637565b80601f016020809104026020016040519081016040528092919081815260200182805461067f90611637565b80156106ca5780601f106106a1576101008083540402835291602001916106ca565b820191905f5260205f20905b8154815290600101906020018083116106ad57829003601f168201915b505050505081526020019060010190610636565b50505050915050949350505050565b5f546001600160a01b031633146107165760405162461bcd60e51b8152600401610215906115c0565b5f828260405160200161072a9291906115f5565b60408051601f1981840301815291815281516020928301205f81815260049093529120549091501561079e5760405162461bcd60e51b815260206004820152601960248201527f436f6c6c656374696f6e20616c726561647920657869737473000000000000006044820152606401610215565b600380546001810182555f919091527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b016107da8385836116bb565b50336001600160a01b03167fb9c7ca2c1d7d6ac251b4b3d037f933e5f54305b9ca8ddb8047d04e4cc6172a2784846040516108169291906117ce565b60405180910390a2505050565b6001805461083090611637565b80601f016020809104026020016040519081016040528092919081815260200182805461085c90611637565b80156108a75780601f1061087e576101008083540402835291602001916108a7565b820191905f5260205f20905b81548152906001019060200180831161088a57829003601f168201915b505050505081565b5f546001600160a01b031633146108d85760405162461bcd60e51b8152600401610215906115c0565b6108e181610dae565b50565b5f546001600160a01b0316331461090d5760405162461bcd60e51b8152600401610215906115c0565b5f5f5f61091a8888610e95565b9250925092505f8360405160200161093291906117e9565b6040516020818303038152906040528051906020012090505f8360405160200161095c91906117e9565b6040516020818303038152906040528051906020012090505f8360405160200161098691906117e9565b60408051601f1981840301815291815281516020928301205f8681526002845282812086825284528281208282529093529082208054919350906109c990611637565b905011610a2a5760405162461bcd60e51b815260206004820152602960248201527f5061746820646f6573206e6f74207265736f6c766520746f20616e206578697360448201526874696e67207479706560b81b6064820152608401610215565b5f83815260026020908152604080832085845282528083208484529091529020610a55898b836116bb565b50336001600160a01b03167fb17ac485b95246393a5eaff3393ebca1a5e17646287d2e8d4cde350be3c965d08787878d8d8d604051610a99969594939291906117ff565b60405180910390a25050505050505050505050565b5f546001600160a01b03163314610ad75760405162461bcd60e51b8152600401610215906115c0565b5f8888604051602001610aeb9291906115f5565b6040516020818303038152906040528051906020012090505f8787604051602001610b179291906115f5565b6040516020818303038152906040528051906020012090505f8686604051602001610b439291906115f5565b6040516020818303038152906040528051906020012090505f8b8b8b8b604051602001610b739493929190611604565b60408051601f1981840301815291815281516020928301205f8781526002845282812087825284528281208682529093529120805491925090610bb590611637565b159050610bfa5760405162461bcd60e51b81526020600482015260136024820152725479706520616c72656164792065786973747360681b6044820152606401610215565b5f84815260026020908152604080832086845282528083208584529091529020610c258688836116bb565b505f81815260056020908152604082208054600181018255908352912001610c4e888a836116bb565b50336001600160a01b03167fb49dd854b8a9a06ab72f50457084391db365a4e2091d03b6e37198de6e6571658d8d8d8d8d8d604051610c929695949392919061187c565b60405180910390a2505050505050505050505050565b60605f8383604051602001610cbe9291906115f5565b60408051601f1981840301815282825280516020918201205f8181526004835283812080548085028701850190955284865291955090929184015b82821015610da1578382905f5260205f20018054610d1690611637565b80601f0160208091040260200160405190810160405280929190818152602001828054610d4290611637565b8015610d8d5780601f10610d6457610100808354040283529160200191610d8d565b820191905f5260205f20905b815481529060010190602001808311610d7057829003601f168201915b505050505081526020019060010190610cf9565b5050505091505092915050565b5f546001600160a01b03163314610dd75760405162461bcd60e51b8152600401610215906115c0565b6001600160a01b038116610e3c5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610215565b5f80546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35f80546001600160a01b0319166001600160a01b0392909216919091179055565b60608060605f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92018290525093945083925050505b8251811015610f1957828181518110610ef057610ef06118c4565b01602001516001600160f81b031916602d60f81b03610f1157809150610f19565b600101610ed5565b505f8111610f7a5760405162461bcd60e51b815260206004820152602860248201527f496e76616c6964207061746820666f726d61743a206e6f20666972737420272d60448201526713903337bab7321760c11b6064820152608401610215565b81515f908190610f8c906001906118ec565b90505b8015610fda57838181518110610fa757610fa76118c4565b01602001516001600160f81b031916602d60f81b03610fc857809150610fda565b80610fd2816118ff565b915050610f8f565b5081811161105a5760405162461bcd60e51b815260206004820152604160248201527f496e76616c6964207061746820666f726d61743a206e6f207365636f6e64202760448201527f2d2720666f756e64206f722064656c696d6974657273206d6973706c616365646064820152601760f91b608482015260a401610215565b611065835f84611190565b955061107c83611076846001611914565b83611190565b94506110948361108d836001611914565b8551611190565b93505f8651116110e65760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c656374696f6e20706172742063616e6e6f7420626520656d707479006044820152606401610215565b5f8551116111365760405162461bcd60e51b815260206004820152601c60248201527f4461746173657420706172742063616e6e6f7420626520656d707479000000006044820152606401610215565b5f8451116111865760405162461bcd60e51b815260206004820152601960248201527f5479706520706172742063616e6e6f7420626520656d707479000000000000006044820152606401610215565b5050509250925092565b6060828210156111ee5760405162461bcd60e51b8152602060048201526024808201527f536c6963653a20656e642063616e6e6f74206265206c657373207468616e20736044820152631d185c9d60e21b6064820152608401610215565b5f6111f984846118ec565b90505f8167ffffffffffffffff81111561121557611215611623565b6040519080825280601f01601f19166020018201604052801561123f576020820181803683370190505b5090505f5b828110156112a257866112578288611914565b81518110611267576112676118c4565b602001015160f81c60f81b828281518110611284576112846118c4565b60200101906001600160f81b03191690815f1a905350600101611244565b5095945050505050565b5f5f83601f8401126112bc575f5ffd5b50813567ffffffffffffffff8111156112d3575f5ffd5b6020830191508360208285010111156112ea575f5ffd5b9250929050565b5f5f5f5f60408587031215611304575f5ffd5b843567ffffffffffffffff81111561131a575f5ffd5b611326878288016112ac565b909550935050602085013567ffffffffffffffff811115611345575f5ffd5b611351878288016112ac565b95989497509550505050565b5f5f6020838503121561136e575f5ffd5b823567ffffffffffffffff811115611384575f5ffd5b611390858286016112ac565b90969095509350505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6113dc602083018461139c565b9392505050565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b8281101561143a57603f1987860301845261142585835161139c565b94506020938401939190910190600101611409565b50929695505050505050565b5f60208284031215611456575f5ffd5b81356001600160a01b03811681146113dc575f5ffd5b5f5f5f5f5f60608688031215611480575f5ffd5b853567ffffffffffffffff811115611496575f5ffd5b6114a2888289016112ac565b909650945050602086013567ffffffffffffffff8111156114c1575f5ffd5b6114cd888289016112ac565b9094509250506040860135600281106114e4575f5ffd5b809150509295509295909350565b5f5f5f5f5f5f5f5f6080898b031215611509575f5ffd5b883567ffffffffffffffff81111561151f575f5ffd5b61152b8b828c016112ac565b909950975050602089013567ffffffffffffffff81111561154a575f5ffd5b6115568b828c016112ac565b909750955050604089013567ffffffffffffffff811115611575575f5ffd5b6115818b828c016112ac565b909550935050606089013567ffffffffffffffff8111156115a0575f5ffd5b6115ac8b828c016112ac565b999c989b5096995094979396929594505050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b818382375f9101908152919050565b838582375f8482015f8152838582375f93019283525090949350505050565b634e487b7160e01b5f52604160045260245ffd5b600181811c9082168061164b57607f821691505b60208210810361166957634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156116b657805f5260205f20601f840160051c810160208510156116945750805b601f840160051c820191505b818110156116b3575f81556001016116a0565b50505b505050565b67ffffffffffffffff8311156116d3576116d3611623565b6116e7836116e18354611637565b8361166f565b5f601f841160018114611718575f85156117015750838201355b5f19600387901b1c1916600186901b1783556116b3565b5f83815260208120601f198716915b828110156117475786850135825560209485019460019092019101611727565b5086821015611763575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f6117b0604083018688611775565b82810360208401526117c3818587611775565b979650505050505050565b602081525f6117e1602083018486611775565b949350505050565b5f82518060208501845e5f920191825250919050565b60a081525f61181160a083018961139c565b8281036020840152611823818961139c565b90508281036040840152611837818861139c565b9050828103606084015261184c818688611775565b9150506002831061186b57634e487b7160e01b5f52602160045260245ffd5b826080830152979650505050505050565b606081525f61188f60608301888a611775565b82810360208401526118a2818789611775565b905082810360408401526118b7818587611775565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b81810381811115610507576105076118d8565b5f8161190d5761190d6118d8565b505f190190565b80820180821115610507576105076118d856fea264697066735822122081c3321f2311eb7d3804351732662070456e8f14460096f93b4e3a8c62b06a7664736f6c634300081e0033"},"sourceId":"StacRegistry.sol","sourcemap":"1196:7559:0:-:0;;;;;;;;;;;;-1:-1:-1;497:5:0;:18;;-1:-1:-1;;;;;;497:18:0;505:10;497:18;;;1196:7559;;;;;;","userdoc":{"kind":"user","methods":{"getCollections()":{"notice":"Gets all initialized collection names as readable strings."},"getDatasets(string)":{"notice":"Gets all dataset names for a given collection as readable strings."},"getTypes(string,string)":{"notice":"Gets all type names for a given dataset as readable strings."},"resolve(string)":{"notice":"Resolves a path string to its stored CID."}},"version":1}}},"manifest":"ethpm/3","sources":{"StacRegistry.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\n/**\n * @title Ownable\n * @dev The Ownable contract has an owner address, and provides basic authorization control\n * functions, this simplifies the implementation of \"user permissions\".\n * For production, consider using OpenZeppelin's Ownable contract for more features.\n */\ncontract Ownable {\n address public owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n constructor() {\n owner = msg.sender;\n }\n\n modifier onlyOwner() {\n require(msg.sender == owner, \"Ownable: caller is not the owner\");\n _;\n }\n\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n emit OwnershipTransferred(owner, newOwner);\n owner = newOwner;\n }\n}\n\n\n/**\n * @title StacRegistry (Developer-Friendly Version)\n * @dev A central registry for STAC data. This version is optimized for off-chain\n * developer convenience, returning human-readable string arrays.\n * WARNING: The getter functions are gas-intensive and should NOT be called from other contracts.\n */\ncontract StacRegistry is Ownable {\n\n enum UpdateCategory { APPEND, REPLACE }\n\n string public stacRootCid;\n\n // --- Core Data Storage ---\n // collection -> dataset -> type -> cid\n mapping(bytes32 => mapping(bytes32 => mapping(bytes32 => string))) private _cids;\n\n // --- Arrays for Human-Readable Getters ---\n string[] private _collectionNames;\n mapping(bytes32 => string[]) private _datasetNames; // Key: keccak256(collectionName)\n mapping(bytes32 => string[]) private _typeNames; // Key: keccak256(collectionName + datasetName)\n\n // --- Events ---\n event StacRootUpdated(string newCid, address indexed updater);\n event CidUpdated(string collection, string dataset, string dataType, string newCid, UpdateCategory category, address indexed updater);\n event CollectionInitialized(string collectionName, address indexed initializer);\n event DatasetInitialized(string collectionName, string datasetName, address indexed initializer);\n event TypeInitialized(string collectionName, string datasetName, string typeName, address indexed initializer);\n\n\n // --- Initialization Functions (Owner Only) ---\n\n function initCollection(string calldata collectionName) external onlyOwner {\n bytes32 collectionKey = keccak256(abi.encodePacked(collectionName));\n require(_datasetNames[collectionKey].length == 0, \"Collection already exists\");\n _collectionNames.push(collectionName);\n emit CollectionInitialized(collectionName, msg.sender);\n }\n\n function initDataset(string calldata collectionName, string calldata datasetName) external onlyOwner {\n bytes32 collectionKey = keccak256(abi.encodePacked(collectionName));\n bytes32 typeMapKey = keccak256(abi.encodePacked(collectionName, datasetName));\n require(_typeNames[typeMapKey].length == 0, \"Dataset already exists\");\n \n _datasetNames[collectionKey].push(datasetName);\n emit DatasetInitialized(collectionName, datasetName, msg.sender);\n }\n\n function initType(\n string calldata collectionName,\n string calldata datasetName,\n string calldata typeName,\n string calldata initialCid\n ) external onlyOwner {\n bytes32 collectionKey = keccak256(abi.encodePacked(collectionName));\n bytes32 datasetKey = keccak256(abi.encodePacked(datasetName));\n bytes32 typeKey = keccak256(abi.encodePacked(typeName));\n bytes32 typeMapKey = keccak256(abi.encodePacked(collectionName, datasetName));\n\n require(bytes(_cids[collectionKey][datasetKey][typeKey]).length == 0, \"Type already exists\");\n\n _cids[collectionKey][datasetKey][typeKey] = initialCid;\n _typeNames[typeMapKey].push(typeName);\n emit TypeInitialized(collectionName, datasetName, typeName, msg.sender);\n }\n\n // --- Update Functions (Owner Only) ---\n\n function updateStacRoot(string calldata newCid) external onlyOwner {\n stacRootCid = newCid;\n emit StacRootUpdated(newCid, msg.sender);\n }\n\n function updateCid(string calldata path, string calldata newCid, UpdateCategory category) external onlyOwner {\n (string memory collection, string memory dataset, string memory dataType) = _parsePath(path);\n \n bytes32 collectionKey = keccak256(abi.encodePacked(collection));\n bytes32 datasetKey = keccak256(abi.encodePacked(dataset));\n bytes32 typeKey = keccak256(abi.encodePacked(dataType));\n\n require(bytes(_cids[collectionKey][datasetKey][typeKey]).length > 0, \"Path does not resolve to an existing type\");\n\n _cids[collectionKey][datasetKey][typeKey] = newCid;\n emit CidUpdated(collection, dataset, dataType, newCid, category, msg.sender);\n }\n \n function changeOwner(address newOwner) external onlyOwner {\n transferOwnership(newOwner);\n }\n\n // --- Read-Only Functions (Human-Readable) ---\n\n /**\n * @notice Resolves a path string to its stored CID.\n */\n function resolve(string calldata path) external view returns (string memory) {\n (string memory collection, string memory dataset, string memory dataType) = _parsePath(path);\n bytes32 collectionKey = keccak256(abi.encodePacked(collection));\n bytes32 datasetKey = keccak256(abi.encodePacked(dataset));\n bytes32 typeKey = keccak256(abi.encodePacked(dataType));\n\n return _cids[collectionKey][datasetKey][typeKey];\n }\n \n /**\n * @notice Gets all initialized collection names as readable strings.\n * @dev WARNING: Do not call this from another smart contract.\n */\n function getCollections() external view returns (string[] memory) {\n return _collectionNames;\n }\n\n /**\n * @notice Gets all dataset names for a given collection as readable strings.\n * @dev WARNING: Do not call this from another smart contract.\n */\n function getDatasets(string calldata collectionName) external view returns (string[] memory) {\n bytes32 collectionKey = keccak256(abi.encodePacked(collectionName));\n return _datasetNames[collectionKey];\n }\n\n /**\n * @notice Gets all type names for a given dataset as readable strings.\n * @dev WARNING: Do not call this from another smart contract.\n */\n function getTypes(string calldata collectionName, string calldata datasetName) external view returns (string[] memory) {\n bytes32 typeMapKey = keccak256(abi.encodePacked(collectionName, datasetName));\n return _typeNames[typeMapKey];\n }\n\n\n // --- Internal Helpers ---\n\n function _parsePath(string calldata path) internal pure returns (string memory collection, string memory dataset, string memory dataType) {\n bytes memory pathBytes = bytes(path);\n uint256 firstDelimiterIndex = 0;\n for (uint256 i = 0; i < pathBytes.length; i++) {\n if (pathBytes[i] == '-') {\n firstDelimiterIndex = i;\n break;\n }\n }\n require(firstDelimiterIndex > 0, \"Invalid path format: no first '-' found.\");\n uint256 lastDelimiterIndex = 0;\n for (uint256 i = pathBytes.length - 1; i > 0; i--) {\n if (pathBytes[i] == '-') {\n lastDelimiterIndex = i;\n break;\n }\n }\n require(lastDelimiterIndex > firstDelimiterIndex, \"Invalid path format: no second '-' found or delimiters misplaced.\");\n \n // Use the _slice helper function to extract the parts\n collection = string(_slice(pathBytes, 0, firstDelimiterIndex));\n dataset = string(_slice(pathBytes, firstDelimiterIndex + 1, lastDelimiterIndex));\n dataType = string(_slice(pathBytes, lastDelimiterIndex + 1, pathBytes.length));\n\n require(bytes(collection).length > 0, \"Collection part cannot be empty\");\n require(bytes(dataset).length > 0, \"Dataset part cannot be empty\");\n require(bytes(dataType).length > 0, \"Type part cannot be empty\");\n }\n\n /**\n * @dev Slices a bytes array.\n * @param data The bytes array to slice.\n * @param start The starting index.\n * @param end The ending index.\n * @return A new bytes array containing the slice.\n */\n function _slice(bytes memory data, uint256 start, uint256 end) internal pure returns (bytes memory) {\n require(end >= start, \"Slice: end cannot be less than start\");\n uint256 len = end - start;\n bytes memory result = new bytes(len);\n for (uint256 i = 0; i < len; i++) {\n result[i] = data[start + i];\n }\n return result;\n }\n}\n","urls":[]}}} \ No newline at end of file diff --git a/dclimate_zarr_client/ipfs_retrieval.py b/dclimate_zarr_client/ipfs_retrieval.py index 7f186e5..656998a 100644 --- a/dclimate_zarr_client/ipfs_retrieval.py +++ b/dclimate_zarr_client/ipfs_retrieval.py @@ -7,7 +7,8 @@ import json import xarray as xr from multiformats import CID -from py_hamt import HAMT, IPFSStore, IPFSZarr3 +from py_hamt import HAMT, KuboCAS +from py_hamt.zarr_hamt_store import ZarrHAMTStore from .dclimate_zarr_errors import ( DatasetNotFoundError, @@ -30,557 +31,505 @@ _stac_hamt_cid_cache: typing.Dict[str, str] = {} +# async def get_dataset_stac( +# stac_root_cid: str, +# collection: str, +# dataset: str, +# gateway_uri_stem: str, +# ) -> dict: +# """ +# Traverses a STAC catalog on IPFS starting from a root CID to find and +# return a specific dataset's STAC Item JSON. + +# This function uses KuboCAS for all IPFS interactions. + +# Args: +# stac_root_cid (str): The root CID of the STAC catalog. +# collection (str): The ID or title of the target collection. +# dataset (str): The ID of the target dataset (STAC Item). +# gateway_uri_stem (str): The base URL for the IPFS gateway. + +# Returns: +# dict: The STAC Item JSON for the requested dataset. + +# Raises: +# StacCatalogError: If the catalog structure is invalid or a network error occurs. +# DatasetNotFoundError: If the collection or dataset cannot be found. +# """ +# logger.info( +# f"Searching STAC catalog for dataset '{dataset}' in collection '{collection}'" +# ) +# async with KuboCAS(gateway_base_url=gateway_uri_stem) as kubo_cas: +# try: +# # 1. Fetch the root catalog +# logger.debug(f"Fetching root catalog from CID: {stac_root_cid}") +# root_catalog_bytes = await kubo_cas.load(CID.decode(stac_root_cid)) +# root_catalog = json.loads(root_catalog_bytes) + +# if not isinstance(root_catalog, dict) or root_catalog.get("type") != "Catalog": +# raise StacCatalogError("Invalid root catalog format.") + +# # 2. Find the target collection's CID +# collection_cid_str = None +# for link in root_catalog.get("links", []): +# # Match by collection ID or title for flexibility +# if link.get("rel") == "child" and ( +# link.get("title") == collection or link.get("id") == collection +# ): +# href_obj = link.get("href") +# if isinstance(href_obj, dict) and "/" in href_obj: +# collection_cid_str = href_obj["/"] +# break + +# if not collection_cid_str: +# raise DatasetNotFoundError(f"Collection '{collection}' not found in root catalog.") + +# # 3. Fetch the collection +# logger.debug(f"Fetching collection '{collection}' from CID: {collection_cid_str}") +# collection_bytes = await kubo_cas.load(CID.decode(collection_cid_str)) +# collection_json = json.loads(collection_bytes) + +# if not isinstance(collection_json, dict) or collection_json.get("type") != "Collection": +# raise StacCatalogError(f"Invalid format for collection '{collection}'.") + +# # 4. Find and return the target dataset (item) or collection +# for item_link in collection_json.get("links", []): +# if item_link.get("rel") == "item" or item_link.get("rel") == "child": +# item_href_obj = item_link.get("href") +# item_cid_str = None +# if isinstance(item_href_obj, dict) and "/" in item_href_obj: +# item_cid_str = item_href_obj["/"] + +# if not item_cid_str: +# continue + +# # Fetch the item to check its ID +# item_bytes = await kubo_cas.load(CID.decode(item_cid_str)) +# item_json = json.loads(item_bytes) + +# if item_json.get("id") == dataset: +# logger.info(f"Found matching STAC Item for dataset '{dataset}'.") +# return item_json # Return the full STAC item + +# # If the loop completes, the dataset was not found in the collection +# raise DatasetNotFoundError(f"Dataset '{dataset}' not found in collection '{collection}'.") + +# except Exception as e: +# # Catch potential errors from KuboCAS (e.g., network issues) or JSON parsing +# # and wrap them in a more specific error. +# logger.error(f"Failed during STAC traversal: {e}", exc_info=True) +# raise StacCatalogError(f"An error occurred during STAC traversal: {e}") from e + # --- IPFSStore Configuration --- -def _get_ipfs_store( - gateway_uri_stem: str | None = None, rpc_uri_stem: str | None = None -) -> IPFSStore: - """Creates or retrieves a configured IPFSStore instance.""" - # Use environment variables as defaults if not provided - gateway = gateway_uri_stem or os.environ.get("IPFS_GATEWAY_URI_STEM") - rpc = rpc_uri_stem or os.environ.get("IPFS_RPC_URI_STEM") - - # Simple way to manage store instances per configuration. - # More sophisticated caching/management might be needed in complex apps. - store_kwargs = {} - # Only pass if explicitly set or found in env, otherwise IPFSStore uses its defaults - if gateway: - store_kwargs["gateway_uri_stem"] = gateway - if rpc: - store_kwargs["rpc_uri_stem"] = rpc - - logger.debug(f"Creating IPFSStore with kwargs: {store_kwargs}") - return IPFSStore(**store_kwargs) +# async def _get_ipfs_store( +# gateway_uri_stem: str | None = None, rpc_uri_stem: str | None = None +# ) -> KuboCAS: +# """Creates or retrieves a configured IPFSStore instance.""" +# # Use environment variables as defaults if not provided +# gateway = gateway_uri_stem or os.environ.get("IPFS_GATEWAY_URI_STEM") +# rpc = rpc_uri_stem or os.environ.get("IPFS_RPC_URI_STEM") + +# async with KuboCAS(gateway_base_url=gateway, rpc_base_url=rpc) as kubo_cas: +# return kubo_cas + # --- IPFS Interaction Helpers --- -def fetch_json_from_cid(cid_str: str, ipfs_store: IPFSStore) -> dict: - """Fetches and parses JSON data from an IPFS CID using IPFSStore (gateway or RPC).""" - try: - logger.debug(f"Fetching JSON from CID: {cid_str}") - if cid_str.startswith("/ipfs/"): - cid_str = cid_str[6:] - - # Validate CID format before attempting fetch - try: - cid = CID.decode(cid_str) - except Exception as decode_err: - raise StacCatalogError( - f"Failed to decode CID string '{cid_str}': {decode_err}" - ) from decode_err - - # Use the store's timeout - json_bytes = ipfs_store.load(cid) - - if not json_bytes: - raise StacCatalogError(f"No data returned for CID: {cid_str}") - return json.loads(json_bytes) - except json.JSONDecodeError as e: - raise StacCatalogError(f"Failed to decode JSON from CID {cid_str}: {e}") from e - except requests.exceptions.Timeout as e: - raise IpfsConnectionError( - f"Timeout fetching CID {cid_str} via IPFSStore (using {ipfs_store.gateway_uri_stem or ipfs_store.rpc_uri_stem}). Details: {e}" - ) from e - except Exception as e: - if ( - "Connection refused" in str(e) - or "Max retries exceeded" in str(e) - or "Failed to establish a new connection" in str(e) - ): - raise IpfsConnectionError( - f"Failed to connect via IPFSStore (using {ipfs_store.gateway_uri_stem or ipfs_store.rpc_uri_stem}) to fetch CID {cid_str}. " - f"Is IPFS daemon/gateway running/accessible? Details: {e}" - ) from e - raise StacCatalogError( - f"Error fetching data for CID {cid_str} via IPFSStore: {e}" - ) from e - - -# NEW function to fetch JSON directly from IPNS via Gateway GET request -def fetch_json_from_ipns( - ipns_name: str, gateway_uri_stem: str | None = None, timeout: int = 120 -) -> dict: - """ - Fetches and parses JSON data directly from an IPNS name using an HTTP Gateway GET request. - Includes retry logic (without nocache) on certain failures. - - Args: - ipns_name (str): The IPNS name (with or without /ipns/ prefix). - gateway_uri_stem (str, optional): Custom IPFS HTTP Gateway URI stem. Defaults to IPFSStore default. - timeout (int): Timeout in seconds for the request. Defaults to 120. - - Returns: - dict: The parsed JSON content. - - Raises: - IpfsConnectionError: If the IPFS gateway is unreachable or times out. - StacCatalogError: If the response is not valid JSON or other request errors occur. - ValueError: If IPNS name is empty or gateway not configured. - """ - if not ipns_name: - raise ValueError("IPNS name cannot be empty.") - - ipfs_store = _get_ipfs_store(gateway_uri_stem=gateway_uri_stem) - gateway_base = ipfs_store.gateway_uri_stem - if not gateway_base: - # IPFSStore provides a default, so this should ideally not happen unless explicitly cleared/misconfigured - raise ValueError("IPFS Gateway URI stem is not configured.") - - if not ipns_name.startswith("/ipns/"): - ipns_name_for_url = f"/ipns/{ipns_name}" - else: - ipns_name_for_url = ipns_name - - target_url = f"{gateway_base.rstrip('/')}{ipns_name_for_url}" - response = None - last_error = None - - # --- Attempt 1: GET with nocache=true --- - try: - params = {"nocache": "true"} - headers = {"Accept": "application/json"} # Be explicit about wanting JSON - logger.debug( - f"Fetching JSON via Gateway GET (nocache=true): {ipns_name_for_url} at {target_url}" - ) - response = requests.get( - target_url, - params=params, - headers=headers, - timeout=timeout, - allow_redirects=True, - ) - response.raise_for_status() # Check for HTTP errors - # Directly parse JSON from response body - json_content = response.json() - logger.info(f"Successfully fetched JSON from IPNS '{ipns_name}' (nocache=true)") - return json_content # Return the parsed dictionary - - except requests.exceptions.ConnectionError as e: - raise IpfsConnectionError( - f"Connection error fetching IPNS '{ipns_name}' via Gateway {gateway_base}. Is Gateway running/accessible? Details: {e}" - ) from e - except requests.exceptions.Timeout as e: - logger.warning( - f"Timeout fetching IPNS {ipns_name_for_url} via Gateway GET (nocache=true): {e}. Will retry without nocache." - ) - last_error = IpfsConnectionError(f"Timeout (nocache=true): {e}") - except requests.exceptions.RequestException as e: # Includes HTTP errors - logger.warning( - f"RequestException fetching IPNS {ipns_name_for_url} via Gateway GET (nocache=true): {e}. Will retry without nocache." - ) - last_error = e - except json.JSONDecodeError as e: - # This can happen if gateway returns HTML error page or non-JSON content - response_text = response.text[:500] if response else "[No Response]" - status_code = response.status_code if response else "[No Status]" - logger.warning( - f"Invalid JSON fetching IPNS {ipns_name_for_url} via Gateway GET (nocache=true, Status: {status_code}, URL: {target_url}): {e}. Response text: {response_text}. Will retry." - ) - last_error = StacCatalogError( - f"JSONDecodeError (nocache=true, Status: {status_code}): {e}. Response: {response_text[:100]}" - ) - except Exception as e: # Catch other unexpected errors - logger.warning( - f"Unexpected error fetching IPNS {ipns_name_for_url} via Gateway GET (nocache=true): {e}. Will retry." - ) - last_error = StacCatalogError( - f"Unexpected error (nocache=true): {type(e).__name__}: {e}" - ) - - # --- Attempt 2: GET without nocache (if Attempt 1 failed) --- - logger.info( - f"Retrying fetch JSON via Gateway GET without nocache for: {ipns_name_for_url}" - ) - try: - params = {} # No nocache - headers = {"Accept": "application/json"} - logger.debug( - f"Fetching JSON via Gateway GET (nocache=false): {ipns_name_for_url} at {target_url}" - ) - response = requests.get( - target_url, - params=params, - headers=headers, - timeout=timeout, - allow_redirects=True, - ) # Retry - response.raise_for_status() - json_content = response.json() - logger.info( - f"Successfully fetched JSON from IPNS '{ipns_name}' (nocache=false)" - ) - return json_content - - except requests.exceptions.ConnectionError as e: - raise IpfsConnectionError( - f"Connection error during IPNS fetch retry for '{ipns_name}' via Gateway {gateway_base}. Details: {e}" - ) from e - except requests.exceptions.Timeout as e: - raise IpfsConnectionError( - f"Timeout ({timeout}s) during IPNS fetch retry for '{ipns_name}' via Gateway {gateway_base}." - ) from e - except requests.exceptions.RequestException as e: # Includes HTTP errors on retry - err_msg = ( - f"Error fetching IPNS '{ipns_name}' (retry) via Gateway {gateway_base}: {e}" - ) - response_text = "[No response object]" - status_code = "[No status code]" - if response is not None: - status_code = response.status_code - try: - response_text = response.text[:500] - except Exception: - response_text = "[Could not read response text]" - err_msg += f" Status Code: {status_code}, Response: {response_text}" - if last_error: - err_msg += f" | Initial error (nocache=true): {type(last_error).__name__}: {last_error}" - raise StacCatalogError( - err_msg - ) from e # Raise as StacCatalogError as it prevents catalog reading - except json.JSONDecodeError as e: - err_msg = f"Invalid JSON fetching IPNS '{ipns_name}' (retry) via Gateway {gateway_base}: {e}. Response text: {response.text[:500] if response else '[No Response]'}" - if last_error: - err_msg += f" | Initial error (nocache=true): {type(last_error).__name__}: {last_error}" - raise StacCatalogError(err_msg) from e - except Exception as e: # Catch any other unexpected error during retry - err_msg = f"Unexpected error during IPNS fetch retry for '{ipns_name}' via Gateway: {e}" - if last_error: - err_msg += f" | Initial error (nocache=true): {type(last_error).__name__}: {last_error}" - raise StacCatalogError(err_msg) from e +# async def fetch_json_from_cid(cid_str: str, ipfs_store: KuboCAS) -> dict: +# """Fetches and parses JSON data from an IPFS CID using KuboCAS (gateway or RPC).""" +# try: +# logger.debug(f"Fetching JSON from CID: {cid_str}") +# if cid_str.startswith("/ipfs/"): +# cid_str = cid_str[6:] + +# # Validate CID format before attempting fetch +# try: +# cid = CID.decode(cid_str) +# except Exception as decode_err: +# raise StacCatalogError( +# f"Failed to decode CID string '{cid_str}': {decode_err}" +# ) from decode_err + +# # Use the store's timeout +# json_bytes = await ipfs_store.load(cid) + +# if not json_bytes: +# raise StacCatalogError(f"No data returned for CID: {cid_str}") +# return json.loads(json_bytes) +# except json.JSONDecodeError as e: +# raise StacCatalogError(f"Failed to decode JSON from CID {cid_str}: {e}") from e +# except requests.exceptions.Timeout as e: +# raise IpfsConnectionError( +# f"Timeout fetching CID {cid_str} via KuboCAS (using {ipfs_store.gateway_uri_stem or ipfs_store.rpc_uri_stem}). Details: {e}" +# ) from e +# except Exception as e: +# if ( +# "Connection refused" in str(e) +# or "Max retries exceeded" in str(e) +# or "Failed to establish a new connection" in str(e) +# ): +# raise IpfsConnectionError( +# f"Failed to connect via KuboCAS (using {ipfs_store.gateway_uri_stem or ipfs_store.rpc_uri_stem}) to fetch CID {cid_str}. " +# f"Is IPFS daemon/gateway running/accessible? Details: {e}" +# ) from e +# raise StacCatalogError( +# f"Error fetching data for CID {cid_str} via KuboCAS: {e}" +# ) from e # --- STAC Traversal and HAMT CID Retrieval --- -def get_dataset_hamt_cid_from_stac( - root_catalog_ipns: str, - target_dataset_id: str, - gateway_uri_stem: str | None = None, - rpc_uri_stem: str - | None = None, # Keep rpc_uri_stem for IPFSStore config if needed by fetch_json_from_cid -) -> str: - """ - Traverses the dClimate STAC catalog starting from a root IPNS name - to find the HAMT root IPFS CID associated with the target dataset ID. - Handles IPLD link format `{"/": "cid_string"}`. - """ - if target_dataset_id in _stac_hamt_cid_cache: - logger.info(f"Found dataset '{target_dataset_id}' HAMT CID in cache.") - return _stac_hamt_cid_cache[target_dataset_id] - - logger.info(f"Searching STAC catalog for dataset: {target_dataset_id}") - # Still need ipfs_store for subsequent CID fetches if rpc differs from gateway - ipfs_store = _get_ipfs_store(gateway_uri_stem, rpc_uri_stem) - - # *** Use fetch_json_from_ipns for the root catalog *** - try: - logger.debug(f"Fetching root catalog content from IPNS: {root_catalog_ipns}") - catalog = fetch_json_from_ipns( - root_catalog_ipns, gateway_uri_stem=gateway_uri_stem - ) - except (IpfsConnectionError, StacCatalogError, ValueError) as e: - raise StacCatalogError( - f"Failed to fetch root catalog from IPNS '{root_catalog_ipns}': {e}" - ) from e - - # Rest of the logic remains similar, using fetch_json_from_cid for /ipfs/ links - if not isinstance(catalog, dict) or catalog.get("type") != "Catalog": - raise StacCatalogError( - f"Invalid root catalog format fetched from IPNS {root_catalog_ipns}" - ) - - collections_to_visit = [] - for link in catalog.get("links", []): - if link.get("rel") == "child" and link.get("type") == "application/json": - href_obj = link.get("href") - if isinstance(href_obj, dict): - collection_cid_str = href_obj.get("/") - if isinstance(collection_cid_str, str): - collections_to_visit.append(collection_cid_str) - logger.debug( - f"Found child collection link (CID): {collection_cid_str}" - ) - else: - logger.warning( - f"Skipping child link with invalid href dict content in root catalog: {link}" - ) - elif isinstance(href_obj, str) and href_obj.startswith("/ipfs/"): - # Allow legacy /ipfs/ string format for backward compatibility? Risky. - # For now, strictly expect dict for IPLD links as per example. - logger.warning( - f"Skipping child link with unexpected string href format (expected dict): {link}" - ) - # If needed: collections_to_visit.append(href_obj[6:]) - else: - logger.warning( - f"Skipping invalid child link format in root catalog: {link}" - ) - - if not collections_to_visit: - raise StacCatalogError( - f"No valid child collection links found in root catalog fetched from IPNS {root_catalog_ipns}" - ) - - logger.info(f"Found {len(collections_to_visit)} collections to search.") - - for collection_cid in collections_to_visit: - logger.debug(f"Fetching collection content for CID: {collection_cid}") - try: - # *** Use fetch_json_from_cid *** - collection = fetch_json_from_cid(collection_cid, ipfs_store) - if ( - not isinstance(collection, dict) - or collection.get("type") != "Collection" - ): - logger.warning( - f"Skipping invalid collection format for CID {collection_cid}. Type: {collection.get('type')}" - ) - continue - - items_found_in_collection = 0 - for link in collection.get("links", []): - if link.get("rel") == "item" and link.get("type") == "application/json": - item_href_obj = link.get("href") - item_cid = None # Reset item_cid for each link - - # *** MODIFIED: Handle dict href for item links *** - if isinstance(item_href_obj, dict): - item_cid = item_href_obj.get("/") # Extract item CID string - elif isinstance(item_href_obj, str) and item_href_obj.startswith( - "/ipfs/" - ): - logger.warning( - f"Found item link with legacy string href format in {collection_cid}: {link}" - ) - item_cid = item_href_obj[6:] - else: - logger.warning( - f"Skipping invalid item link format in collection {collection_cid}: {link}" - ) - continue # Skip this link if format is wrong - - if isinstance(item_cid, str): - items_found_in_collection += 1 - # logger.debug(f"Fetching item content for CID: {item_cid}") # Can be verbose - try: - # *** Use fetch_json_from_cid with the extracted item CID string *** - item = fetch_json_from_cid(item_cid, ipfs_store) - - if ( - not isinstance(item, dict) - or item.get("type") != "Feature" - ): - logger.warning( - f"Skipping invalid item format for CID {item_cid}. Type: {item.get('type')}" - ) - continue - - item_id = item.get("id") - if item_id == target_dataset_id: - logger.info( - f"Found matching item for '{target_dataset_id}' with CID {item_cid} in collection {collection_cid}" - ) - hamt_asset = item.get("assets", {}).get("hamt-zarr", {}) - hamt_cid_href = hamt_asset.get( - "href" - ) # This should be the /ipfs/ string - - if not isinstance( - hamt_cid_href, str - ) or not hamt_cid_href.startswith("/ipfs/"): - raise StacCatalogError( - f"STAC Item '{item_id}' (CID: {item_cid}) is missing a valid string 'assets.hamt-zarr.href' starting with /ipfs/. Found: '{hamt_cid_href}' (type: {type(hamt_cid_href).__name__})" - ) - - hamt_cid_str = hamt_cid_href[ - 6: - ] # Slice the /ipfs/ prefix - logger.info( - f"Successfully extracted HAMT CID for '{target_dataset_id}': {hamt_cid_str}" - ) - _stac_hamt_cid_cache[target_dataset_id] = hamt_cid_str - return hamt_cid_str - - except (StacCatalogError, IpfsConnectionError) as item_err: - # Log error but continue searching other items/collections - logger.error( - f"Error processing item {item_cid} in collection {collection_cid}, continuing search: {item_err}" - ) - except ( - Exception - ) as item_err: # Catch unexpected errors during item processing - logger.error( - f"Unexpected error processing item {item_cid} in collection {collection_cid}, continuing search: {type(item_err).__name__}: {item_err}" - ) - # else: Invalid item CID extracted, already logged warning - - logger.debug( - f"Finished searching {items_found_in_collection} items in collection {collection.get('id', collection_cid)}." - ) - - except (StacCatalogError, IpfsConnectionError) as col_err: - # Log error but continue searching other collections - logger.error( - f"Error processing collection {collection_cid}, continuing search: {col_err}" - ) - except ( - Exception - ) as col_err: # Catch unexpected errors during collection processing - logger.error( - f"Unexpected error processing collection {collection_cid}, continuing search: {type(col_err).__name__}: {col_err}" - ) - - # If loop completes without finding the dataset - raise DatasetNotFoundError( - f"Dataset ID '{target_dataset_id}' not found after searching all collections in the STAC catalog rooted at IPNS '{root_catalog_ipns}'." - ) - - -def _get_host(uri: str = "/api/v0"): - """Parse the ipfs api host address from `IPFS_HOST` environment variable. - If not found, use localhost:5001/api/v0. - - Args: - uri (str): the uri where ipfs gateway api listens - - Returns: - str: ipfs gateway url - - """ - - host_from_env = os.getenv("IPFS_HOST") - return host_from_env + uri if host_from_env else DEFAULT_HOST - - -def _get_single_metadata(ipfs_hash: str) -> dict: - """Get metadata for given ipfs hash over ipld - - Args: - ipfs_hash (str): ipfs hash for which to get metadata - - Returns: - dict: dict of metadata for hash - """ - - r = requests.get(f"{_get_host()}/ipfs/{ipfs_hash}") - r.raise_for_status() - return r.json() - - -def _get_previous_hash_from_metadata(metadata: dict) -> typing.Optional[str]: - """Pull in last updated hash from STAC metadata - - Args: - metadata (dict): STAC metadata - - Returns: - str: Previous hash, or None if given root metadata - """ - links = metadata["links"] - try: - link_to_previous = [ - link for link in links if link["rel"] in {"prev", "previous"} - ][0] - except IndexError: - return None - return link_to_previous["metadata href"]["/"] - - -def _resolve_ipns_name_hash(ipns_name_hash: str) -> str: - """Find the latest IPFS hash corresponding to a stable ipns name hash - - Args: - ipfs_name_hash (str): stable IPNS name hash - - Returns: - str: ipfs hash corresponding to this ipns name hash - """ - r = requests.get(f"{_get_host()}/ipns/{ipns_name_hash}", params={"offline": True}) - r.raise_for_status() - return r.json()["Path"].split("/")[-1] - - -def update_cache_if_changed(new_data: dict) -> None: - """Update the local cache file only if the new data differs from what is cached.""" - cache_file = os.path.join(os.path.dirname(__file__), "cids.json") - try: - with open(cache_file, "r") as f: - cached_data = json.load(f) - except (FileNotFoundError, json.JSONDecodeError): - cached_data = None - - if cached_data != new_data: - with open(cache_file, "w") as f: - json.dump(new_data, f) - - -def get_ipns_name_hash(ipns_key_str: str) -> str: - """Find the latest IPNS name hash corresponding to a string (key) - - Args: - ipfs_key_str (str): a string (key) identifying a dataset - - Raises: - KeyError: raised if no IPNS key string is found in the IPNS keys list - - Returns: - str: ipfsname hash corresponding to the provided string - """ +# async def get_dataset_hamt_cid_from_stac( +# root_catalog_ipns: str, +# target_dataset_id: str, +# gateway_uri_stem: str | None = None, +# rpc_uri_stem: ( +# str | None +# ) = None, # Keep rpc_uri_stem for IPFSStore config if needed by fetch_json_from_cid +# ) -> str: +# """ +# Traverses the dClimate STAC catalog starting from a root IPNS name +# to find the HAMT root IPFS CID associated with the target dataset ID. +# Handles IPLD link format `{"/": "cid_string"}`. +# """ +# if target_dataset_id in _stac_hamt_cid_cache: +# logger.info(f"Found dataset '{target_dataset_id}' HAMT CID in cache.") +# return _stac_hamt_cid_cache[target_dataset_id] + +# logger.info(f"Searching STAC catalog for dataset: {target_dataset_id}") +# # Still need ipfs_store for subsequent CID fetches if rpc differs from gateway +# ipfs_store = _get_ipfs_store(gateway_uri_stem, rpc_uri_stem) + +# # *** Use fetch_json_from_ipns for the root catalog *** +# try: +# logger.debug(f"Fetching root catalog content from IPNS: {root_catalog_ipns}") +# catalog = fetch_json_from_ipns( +# root_catalog_ipns, gateway_uri_stem=gateway_uri_stem +# ) +# except (IpfsConnectionError, StacCatalogError, ValueError) as e: +# raise StacCatalogError( +# f"Failed to fetch root catalog from IPNS '{root_catalog_ipns}': {e}" +# ) from e + +# # Rest of the logic remains similar, using fetch_json_from_cid for /ipfs/ links +# if not isinstance(catalog, dict) or catalog.get("type") != "Catalog": +# raise StacCatalogError( +# f"Invalid root catalog format fetched from IPNS {root_catalog_ipns}" +# ) + +# collections_to_visit = [] +# for link in catalog.get("links", []): +# if link.get("rel") == "child" and link.get("type") == "application/json": +# href_obj = link.get("href") +# if isinstance(href_obj, dict): +# collection_cid_str = href_obj.get("/") +# if isinstance(collection_cid_str, str): +# collections_to_visit.append(collection_cid_str) +# logger.debug( +# f"Found child collection link (CID): {collection_cid_str}" +# ) +# else: +# logger.warning( +# f"Skipping child link with invalid href dict content in root catalog: {link}" +# ) +# elif isinstance(href_obj, str) and href_obj.startswith("/ipfs/"): +# # Allow legacy /ipfs/ string format for backward compatibility? Risky. +# # For now, strictly expect dict for IPLD links as per example. +# logger.warning( +# f"Skipping child link with unexpected string href format (expected dict): {link}" +# ) +# collections_to_visit.append(href_obj[6:]) +# else: +# logger.warning( +# f"Skipping invalid child link format in root catalog: {link}" +# ) + +# if not collections_to_visit: +# raise StacCatalogError( +# f"No valid child collection links found in root catalog fetched from IPNS {root_catalog_ipns}" +# ) + +# logger.info(f"Found {len(collections_to_visit)} collections to search.") + +# for collection_cid in collections_to_visit: +# logger.debug(f"Fetching collection content for CID: {collection_cid}") +# try: +# # --- collection JSON --- +# collection = await fetch_json_from_cid(collection_cid, ipfs_store) +# if ( +# not isinstance(collection, dict) +# or collection.get("type") != "Collection" +# ): +# logger.warning( +# f"Skipping invalid collection format for CID {collection_cid}. " +# f"Type: {collection.get('type')}" +# ) +# continue + +# items_found_in_collection = 0 +# for link in collection.get("links", []): +# if link.get("rel") == "item" and link.get("type") == "application/json": +# item_href_obj = link.get("href") +# item_cid = None # reset each link + +# # --- handle IPLD or legacy /ipfs/ links --- +# if isinstance(item_href_obj, dict): +# item_cid = item_href_obj.get("/") # IPLD dict +# elif isinstance(item_href_obj, str) and item_href_obj.startswith( +# "/ipfs/" +# ): +# logger.warning( +# f"Found legacy string href in {collection_cid}: {link}" +# ) +# item_cid = item_href_obj[6:] # strip "/ipfs/" +# else: +# logger.warning( +# f"Skipping invalid item link in {collection_cid}: {link}" +# ) +# continue + +# if not isinstance(item_cid, str): +# # already logged warning +# continue + +# items_found_in_collection += 1 +# item_id: str | None = None # keep in scope for except +# try: +# # --- item JSON --- +# item = await fetch_json_from_cid(item_cid, ipfs_store) + +# if not isinstance(item, dict) or item.get("type") != "Feature": +# logger.warning( +# f"Skipping invalid item format for CID {item_cid}. " +# f"Type: {item.get('type')}" +# ) +# continue + +# item_id = item.get("id") +# if item_id != target_dataset_id: +# # not the dataset we're looking for +# continue + +# logger.info( +# f"Found matching item for '{target_dataset_id}' " +# f"(CID {item_cid}) in collection {collection_cid}" +# ) +# hamt_asset = item.get("assets", {}).get("hamt-zarr", {}) +# hamt_cid_href = hamt_asset.get("href") # expected "/ipfs/" + +# if not isinstance( +# hamt_cid_href, str +# ) or not hamt_cid_href.startswith("/ipfs/"): +# raise StacCatalogError( +# f"STAC Item '{item_id}' (CID: {item_cid}) is missing a " +# f"valid string 'assets.hamt-zarr.href' starting with " +# f"/ipfs/. Found: '{hamt_cid_href}' " +# f"(type: {type(hamt_cid_href).__name__})" +# ) + +# hamt_cid_str = hamt_cid_href[6:] # drop "/ipfs/" +# logger.info( +# f"Successfully extracted HAMT CID for '{target_dataset_id}': " +# f"{hamt_cid_str}" +# ) +# _stac_hamt_cid_cache[target_dataset_id] = hamt_cid_str +# return hamt_cid_str + +# # ── error handling ────────────────────────────────────────── +# except StacCatalogError as item_err: +# # If the failing item *is* the target dataset, bubble it up. +# if item_id == target_dataset_id: +# raise item_err +# logger.error( +# f"Error processing non-target item {item_cid} in collection " +# f"{collection_cid}: {item_err}" +# ) +# except IpfsConnectionError as item_err: +# logger.error( +# f"IPFS error processing item {item_cid} in collection " +# f"{collection_cid}: {item_err}" +# ) +# except Exception as item_err: +# logger.error( +# f"Unexpected error processing item {item_cid} in collection " +# f"{collection_cid}: {type(item_err).__name__}: {item_err}" +# ) + +# logger.debug( +# f"Finished searching {items_found_in_collection} items in collection " +# f"{collection.get('id', collection_cid)}." +# ) + +# except StacCatalogError as col_err: +# # ← this is the error that means “the target dataset is malformed” +# # → let it propagate to the caller so tests (and callers) can see it. +# raise col_err +# except IpfsConnectionError as col_err: +# # ← still swallow network errors so other collections can be tried +# logger.error( +# f"IPFS error processing collection {collection_cid}, continuing search: " +# f"{col_err}" +# ) +# except Exception as col_err: +# logger.error( +# f"Unexpected error processing collection {collection_cid}, continuing " +# f"search: {type(col_err).__name__}: {col_err}" +# ) + +# # If the loop completes without returning +# raise DatasetNotFoundError( +# f"Dataset ID '{target_dataset_id}' not found after searching all collections " +# f"in the STAC catalog rooted at IPNS '{root_catalog_ipns}'." +# ) + + +# def _get_host(uri: str = "/api/v0"): +# """Parse the ipfs api host address from `IPFS_HOST` environment variable. +# If not found, use localhost:5001/api/v0. + +# Args: +# uri (str): the uri where ipfs gateway api listens - try: - # 1) Try to fetch from endpoint - r = requests.get(CID_ENDPOINT, params={"decoder": "json"}) - r.raise_for_status() - json_cid = r.json() # raises JSONDecodeError if endpoint returns malformed JSON +# Returns: +# str: ipfs gateway url + +# """ - # Update cache only if there is a change - update_cache_if_changed(json_cid) +# host_from_env = os.getenv("IPFS_HOST") +# return host_from_env + uri if host_from_env else DEFAULT_HOST - for entry in json_cid: - if entry == ipns_key_str: - return json_cid[entry] - except (requests.RequestException, KeyError, json.JSONDecodeError): - # 2) If remote fails or is malformed, try local fallback - cache_file = os.path.join(os.path.dirname(__file__), "cids.json") - if os.path.exists(cache_file): - try: - with open(cache_file, "r") as f: - json_cid = json.load( - f - ) # <-- can raise JSONDecodeError if file is empty/corrupt - for entry in json_cid: - if entry == ipns_key_str: - return json_cid[entry] - except (KeyError, json.JSONDecodeError) as err: - # We tried local, but it’s also invalid (bad JSON or missing key) - raise DatasetNotFoundError("Invalid dataset name") from err - - # 3) If we get here, local file either doesn't exist or didn't have the key - raise DatasetNotFoundError("Invalid dataset name") from None - - -def _get_relevant_metadata(ipfs_head_hash: str, as_of: datetime.datetime) -> dict: - """Iterates through STAC metadata until metadata generated before as_of is found - - Args: - ipfs_head_hash (str): first hash in chain - as_of (datetime.datetime): cutoff date for finding metadata - - Raises: - NoMetadataFoundError: raised if no metadata older than cutoff date is found - - Returns: - dict: relevant metadata - """ - cur_metadata = _get_single_metadata(ipfs_head_hash) - while True: - time_generated = datetime.datetime.strptime( - cur_metadata["properties"]["updated"], "%Y-%m-%dT%H:%M:%SZ" - ) - if time_generated <= as_of: - return cur_metadata - prev_hash = _get_previous_hash_from_metadata(cur_metadata) - if prev_hash is None: - raise NoMetadataFoundError(f"No metadata found after as_of: {as_of}") - cur_metadata = _get_single_metadata(prev_hash) +# def _get_single_metadata(ipfs_hash: str) -> dict: +# """Get metadata for given ipfs hash over ipld + +# Args: +# ipfs_hash (str): ipfs hash for which to get metadata + +# Returns: +# dict: dict of metadata for hash +# """ + +# r = requests.get(f"{_get_host()}/ipfs/{ipfs_hash}") +# r.raise_for_status() +# return r.json() + + +# def _get_previous_hash_from_metadata(metadata: dict) -> typing.Optional[str]: +# """Pull in last updated hash from STAC metadata + +# Args: +# metadata (dict): STAC metadata + +# Returns: +# str: Previous hash, or None if given root metadata +# """ +# links = metadata["links"] +# try: +# link_to_previous = [ +# link for link in links if link["rel"] in {"prev", "previous"} +# ][0] +# except IndexError: +# return None +# return link_to_previous["metadata href"]["/"] + + +# def _resolve_ipns_name_hash(ipns_name_hash: str) -> str: +# """Find the latest IPFS hash corresponding to a stable ipns name hash + +# Args: +# ipfs_name_hash (str): stable IPNS name hash + +# Returns: +# str: ipfs hash corresponding to this ipns name hash +# """ +# r = requests.get(f"{_get_host()}/ipns/{ipns_name_hash}", params={"offline": True}) +# r.raise_for_status() +# return r.json()["Path"].split("/")[-1] + + +# def update_cache_if_changed(new_data: dict) -> None: +# """Update the local cache file only if the new data differs from what is cached.""" +# cache_file = os.path.join(os.path.dirname(__file__), "cids.json") +# try: +# with open(cache_file, "r") as f: +# cached_data = json.load(f) +# except (FileNotFoundError, json.JSONDecodeError): +# cached_data = None + +# if cached_data != new_data: +# with open(cache_file, "w") as f: +# json.dump(new_data, f) + + +# def get_ipns_name_hash(ipns_key_str: str) -> str: +# """Find the latest IPNS name hash corresponding to a string (key) + +# Args: +# ipfs_key_str (str): a string (key) identifying a dataset + +# Raises: +# KeyError: raised if no IPNS key string is found in the IPNS keys list + +# Returns: +# str: ipfsname hash corresponding to the provided string +# """ + +# try: +# # 1) Try to fetch from endpoint +# r = requests.get(CID_ENDPOINT, params={"decoder": "json"}) +# r.raise_for_status() +# json_cid = r.json() # raises JSONDecodeError if endpoint returns malformed JSON + +# # Update cache only if there is a change +# update_cache_if_changed(json_cid) + +# for entry in json_cid: +# if entry == ipns_key_str: +# return json_cid[entry] + +# except (requests.RequestException, KeyError, json.JSONDecodeError): +# # 2) If remote fails or is malformed, try local fallback +# cache_file = os.path.join(os.path.dirname(__file__), "cids.json") +# if os.path.exists(cache_file): +# try: +# with open(cache_file, "r") as f: +# json_cid = json.load( +# f +# ) # <-- can raise JSONDecodeError if file is empty/corrupt +# for entry in json_cid: +# if entry == ipns_key_str: +# return json_cid[entry] +# except (KeyError, json.JSONDecodeError) as err: +# # We tried local, but it’s also invalid (bad JSON or missing key) +# raise DatasetNotFoundError("Invalid dataset name") from err + +# # 3) If we get here, local file either doesn't exist or didn't have the key +# raise DatasetNotFoundError("Invalid dataset name") from None + + +# def _get_relevant_metadata(ipfs_head_hash: str, as_of: datetime.datetime) -> dict: +# """Iterates through STAC metadata until metadata generated before as_of is found + +# Args: +# ipfs_head_hash (str): first hash in chain +# as_of (datetime.datetime): cutoff date for finding metadata + +# Raises: +# NoMetadataFoundError: raised if no metadata older than cutoff date is found + +# Returns: +# dict: relevant metadata +# """ +# cur_metadata = _get_single_metadata(ipfs_head_hash) +# while True: +# time_generated = datetime.datetime.strptime( +# cur_metadata["properties"]["updated"], "%Y-%m-%dT%H:%M:%SZ" +# ) +# if time_generated <= as_of: +# return cur_metadata +# prev_hash = _get_previous_hash_from_metadata(cur_metadata) +# if prev_hash is None: +# raise NoMetadataFoundError(f"No metadata found after as_of: {as_of}") +# cur_metadata = _get_single_metadata(prev_hash) # def get_dataset_by_ipfs_hash(ipfs_hash: str) -> xr.Dataset: @@ -598,99 +547,102 @@ def _get_relevant_metadata(ipfs_head_hash: str, as_of: datetime.datetime) -> dic # --- Zarr Dataset Loading --- # Renamed from get_dataset_by_ipns_hash -def _get_dataset_by_ipfs_cid( - ipfs_cid: str, - gateway_uri_stem: str | None = None, - rpc_uri_stem: str | None = None, -) -> xr.Dataset: - """ - Gets an xarray dataset directly from its Zarr root IPFS CID using py-hamt. - - Args: - ipfs_cid (str): The IPFS CID of the Zarr dataset's root node (e.g., HAMT root). - gateway_uri_stem (str, optional): Custom IPFS HTTP Gateway URI stem. - rpc_uri_stem (str, optional): Custom IPFS RPC API URI stem. - - Returns: - xr.Dataset: The loaded dataset. - - Raises: - IpfsConnectionError: If connection to IPFS fails during loading. - Exception: Other errors during Zarr parsing or IPFS interaction. - """ - if not ipfs_cid: - raise ValueError("IPFS CID cannot be empty.") - - logger.info(f"Loading Zarr dataset from IPFS CID: {ipfs_cid}") - ipfs_store = _get_ipfs_store(gateway_uri_stem, rpc_uri_stem) - - try: - # root_node_id expects a CID object or string representation - cid_obj = None - try: - cid_obj = CID.decode(ipfs_cid) - except Exception as decode_err: - raise ValueError( - f"Invalid IPFS CID format: {ipfs_cid}. Error: {decode_err}" - ) from decode_err - - # Initialize HAMT store - hamt_store = HAMT(store=ipfs_store, root_node_id=cid_obj, read_only=True) - - # Wrap with IPFSZarr3 store adapter - ipfszarr3_store = IPFSZarr3(hamt_store, read_only=True) - - # Ensure registered codecs (like encryption) are available if needed - # zarr.registry.get_codec(...) or ensure they are registered beforehand - - # consolidated=False is typical for HAMT stores, but could be True if explicitly created that way - ds = xr.open_zarr(store=ipfszarr3_store, chunks=None) - logger.info(f"Successfully loaded dataset from CID: {ipfs_cid}") - return ds - except (requests.exceptions.RequestException, IpfsConnectionError) as e: - # Catch connection/network errors during loading - if ( - "Connection refused" in str(e) - or "Max retries exceeded" in str(e) - or "Timeout" in str(e) - ): - raise IpfsConnectionError( - f"IPFS connection failed while loading dataset from CID {ipfs_cid}. " - f"Gateway: {ipfs_store.gateway_uri_stem}, RPC: {ipfs_store.rpc_uri_stem}. Details: {e}" - ) from e - else: - # Other network errors - raise RuntimeError( - f"Network error loading Zarr dataset from IPFS CID {ipfs_cid}: {e}" - ) from e - except FileNotFoundError as e: - # xarray/zarr raises FileNotFoundError if root metadata (.zgroup, .zarray) is missing - raise StacCatalogError( - f"Zarr metadata not found at CID {ipfs_cid}. Is it a valid Zarr root? Error: {e}" - ) from e - except Exception as e: - # Catch other potential errors (e.g., Zarr format errors, py-hamt errors) - logger.error( - f"Failed to load Zarr dataset from IPFS CID {ipfs_cid}: {type(e).__name__}: {e}", - exc_info=True, - ) - raise RuntimeError( - f"Failed to load Zarr dataset from IPFS CID {ipfs_cid}" - ) from e - - -def get_metadata_by_key(key: str) -> dict: - """Get STAC metadata for specific dataset - - Args: - key (str): dataset key - - Returns: - dict: STAC metadata corresponding to key - """ - ipns_name = get_ipns_name_hash(key) - ipfs_hash = _resolve_ipns_name_hash(ipns_name) - return _get_single_metadata(ipfs_hash) +# def _get_dataset_by_ipfs_cid( +# ipfs_cid: str, +# gateway_uri_stem: str | None = None, +# rpc_uri_stem: str | None = None, +# ) -> xr.Dataset: +# """ +# Gets an xarray dataset directly from its Zarr root IPFS CID using py-hamt. + +# Args: +# ipfs_cid (str): The IPFS CID of the Zarr dataset's root node (e.g., HAMT root). +# gateway_uri_stem (str, optional): Custom IPFS HTTP Gateway URI stem. +# rpc_uri_stem (str, optional): Custom IPFS RPC API URI stem. + +# Returns: +# xr.Dataset: The loaded dataset. + +# Raises: +# IpfsConnectionError: If connection to IPFS fails during loading. +# Exception: Other errors during Zarr parsing or IPFS interaction. +# """ +# if not ipfs_cid: +# raise ValueError("IPFS CID cannot be empty.") + +# logger.info(f"Loading Zarr dataset from IPFS CID: {ipfs_cid}") +# ipfs_store = _get_ipfs_store(gateway_uri_stem, rpc_uri_stem) + +# try: +# # root_node_id expects a CID object or string representation +# cid_obj = None +# try: +# cid_obj = CID.decode(ipfs_cid) +# except Exception as decode_err: +# raise ValueError( +# f"Invalid IPFS CID format: {ipfs_cid}. Error: {decode_err}" +# ) from decode_err + +# # Initialize HAMT store +# hamt_store = HAMT(store=ipfs_store, root_node_id=cid_obj, read_only=True) + +# # Wrap with ZarrHAMTStore adapter +# zarr_hamt__store = ZarrHAMTStore(hamt_store, read_only=True) + +# # Ensure registered codecs (like encryption) are available if needed +# # zarr.registry.get_codec(...) or ensure they are registered beforehand + +# # consolidated=False is typical for HAMT stores, but could be True if explicitly created that way +# ds = xr.open_zarr(store=zarr_hamt__store, chunks=None) +# logger.info(f"Successfully loaded dataset from CID: {ipfs_cid}") +# return ds +# except (requests.exceptions.RequestException, IpfsConnectionError) as e: +# # Catch connection/network errors during loading +# if ( +# "Connection refused" in str(e) +# or "Max retries exceeded" in str(e) +# or "Timeout" in str(e) +# ): +# raise IpfsConnectionError( +# f"IPFS connection failed while loading dataset from CID {ipfs_cid}. " +# f"Gateway: {ipfs_store.gateway_uri_stem}, RPC: {ipfs_store.rpc_uri_stem}. Details: {e}" +# ) from e +# else: +# # Other network errors +# raise RuntimeError( +# f"Network error loading Zarr dataset from IPFS CID {ipfs_cid}: {e}" +# ) from e +# except FileNotFoundError as e: +# # xarray/zarr raises FileNotFoundError if root metadata (.zgroup, .zarray) is missing +# raise StacCatalogError( +# f"Zarr metadata not found at CID {ipfs_cid}. Is it a valid Zarr root? Error: {e}" +# ) from e +# except ValueError: +# # Let ValueErrors propagate, e.g. from invalid CID format +# raise +# except Exception as e: +# # Catch other potential errors (e.g., Zarr format errors, py-hamt errors) +# logger.error( +# f"Failed to load Zarr dataset from IPFS CID {ipfs_cid}: {type(e).__name__}: {e}", +# exc_info=True, +# ) +# raise RuntimeError( +# f"Failed to load Zarr dataset from IPFS CID {ipfs_cid}" +# ) from e + + +# def get_metadata_by_key(key: str) -> dict: +# """Get STAC metadata for specific dataset + +# Args: +# key (str): dataset key + +# Returns: +# dict: STAC metadata corresponding to key +# """ +# ipns_name = get_ipns_name_hash(key) +# ipfs_hash = _resolve_ipns_name_hash(ipns_name) +# return _get_single_metadata(ipfs_hash) # def list_datasets() -> typing.List[str]: @@ -730,164 +682,169 @@ def get_metadata_by_key(key: str) -> dict: # --- Metadata and Listing --- -def list_datasets( - root_catalog_ipns: str | None = None, - gateway_uri_stem: str | None = None, - rpc_uri_stem: str | None = None, -) -> typing.List[str]: - """ - Lists available dataset IDs by traversing the STAC catalog starting from IPNS. - Handles IPLD link format `{"/": "cid_string"}`. - Also populates the internal HAMT CID cache as a side effect. - """ - if root_catalog_ipns is None: - # Avoid circular import, get constant dynamically if needed - from .client import DCLIMATE_STAC_CATALOG_IPNS - - root_catalog_ipns = DCLIMATE_STAC_CATALOG_IPNS - if not root_catalog_ipns: - raise ValueError("Root catalog IPNS name is not defined.") - - logger.info( - f"Listing datasets by traversing STAC catalog from IPNS: {root_catalog_ipns}" - ) - ipfs_store = _get_ipfs_store(gateway_uri_stem, rpc_uri_stem) - dataset_ids = set() - - # *** Use fetch_json_from_ipns for the root catalog *** - try: - catalog = fetch_json_from_ipns( - root_catalog_ipns, gateway_uri_stem=gateway_uri_stem - ) - except (IpfsConnectionError, StacCatalogError, ValueError) as e: - # Make error message slightly more specific for listing context - raise StacCatalogError( - f"Failed to fetch or parse root catalog from IPNS '{root_catalog_ipns}' for listing datasets: {e}" - ) from e - - if not isinstance(catalog, dict) or catalog.get("type") != "Catalog": - raise StacCatalogError( - f"Invalid root catalog format fetched from IPNS {root_catalog_ipns} for listing. Type: {catalog.get('type')}" - ) - - collections_to_visit = [] - for link in catalog.get("links", []): - if link.get("rel") == "child" and link.get("type") == "application/json": - href_obj = link.get("href") - # *** MODIFIED: Handle dict href for IPLD links *** - if isinstance(href_obj, dict): - collection_cid_str = href_obj.get("/") - if isinstance(collection_cid_str, str): - collections_to_visit.append(collection_cid_str) - else: - logger.warning( - f"Skipping child link with invalid href dict content in root catalog during list: {link}" - ) - # Add handling for legacy string format if necessary/desired - # elif isinstance(href_obj, str) and href_obj.startswith("/ipfs/"): ... - else: - logger.warning( - f"Skipping invalid child link format in root catalog during list: {link}" - ) - - if not collections_to_visit: - logger.warning( - f"No child collection links found in root catalog {root_catalog_ipns} during list." - ) - return [] # Return empty list if no collections to check - - logger.info(f"Found {len(collections_to_visit)} collections to scan for items.") - - for collection_cid in collections_to_visit: - try: - # *** Use fetch_json_from_cid with the extracted CID string *** - collection = fetch_json_from_cid(collection_cid, ipfs_store) - - if ( - not isinstance(collection, dict) - or collection.get("type") != "Collection" - ): - logger.warning( - f"Skipping invalid collection format during list (CID: {collection_cid}). Type: {collection.get('type')}" - ) - continue - - for link in collection.get("links", []): - if link.get("rel") == "item" and link.get("type") == "application/json": - item_href_obj = link.get("href") - item_cid = None - - # *** MODIFIED: Handle dict href for item links *** - if isinstance(item_href_obj, dict): - item_cid = item_href_obj.get("/") - # Add handling for legacy string format if necessary/desired - # elif isinstance(item_href_obj, str) and item_href_obj.startswith("/ipfs/"): ... - else: - logger.warning( - f"Skipping invalid item link format in collection {collection_cid} during list: {link}" - ) - continue - - if isinstance(item_cid, str): - try: - # Fetch item JSON *only* to get the ID and cache the HAMT CID - # Avoids deeper processing if only listing - item = fetch_json_from_cid(item_cid, ipfs_store) - - if ( - not isinstance(item, dict) - or item.get("type") != "Feature" - ): - logger.warning( - f"Skipping invalid item format during list (CID: {item_cid}). Type: {item.get('type')}" - ) - continue - - item_id = item.get("id") - if isinstance(item_id, str) and item_id: - dataset_ids.add(item_id) # Add valid ID to our set - - # Populate cache as a side effect - hamt_asset = item.get("assets", {}).get("hamt-zarr", {}) - hamt_cid_href = hamt_asset.get("href") - if ( - item_id not in _stac_hamt_cid_cache - and isinstance(hamt_cid_href, str) - and hamt_cid_href.startswith("/ipfs/") - ): - hamt_cid_str = hamt_cid_href[6:] - _stac_hamt_cid_cache[item_id] = hamt_cid_str - logger.debug( - f"Cached HAMT CID {hamt_cid_str} for {item_id} during list." - ) - else: - logger.warning( - f"Item {item_cid} has missing or invalid 'id'. Skipping." - ) - - except (StacCatalogError, IpfsConnectionError) as item_err: - # Log and skip this specific item if fetching/parsing fails - logger.warning( - f"Skipping item {item_cid} during list due to error: {item_err}" - ) - except Exception as item_err: # Catch unexpected errors - logger.warning( - f"Skipping item {item_cid} during list due to unexpected error: {type(item_err).__name__}: {item_err}" - ) - # else: Invalid item CID extracted, already logged warning - - except (StacCatalogError, IpfsConnectionError) as col_err: - # Log and skip this specific collection if fetching/parsing fails - logger.warning( - f"Skipping collection {collection_cid} during list due to error: {col_err}" - ) - except Exception as col_err: # Catch unexpected errors - logger.warning( - f"Skipping collection {collection_cid} during list due to unexpected error: {type(col_err).__name__}: {col_err}" - ) - - found_datasets = sorted(list(dataset_ids)) - logger.info( - f"Finished listing datasets. Found {len(found_datasets)} unique dataset IDs." - ) - return found_datasets +# async def list_datasets( +# root_catalog_ipns: str | None = None, +# gateway_uri_stem: str | None = None, +# rpc_uri_stem: str | None = None, +# ) -> typing.List[str]: +# """ +# Lists available dataset IDs by traversing the STAC catalog starting from IPNS. +# Handles IPLD link format `{"/": "cid_string"}`. +# Also populates the internal HAMT CID cache as a side effect. +# """ +# if root_catalog_ipns is None: +# # Avoid circular import, get constant dynamically if needed +# from .client import DCLIMATE_STAC_CATALOG_IPNS + +# root_catalog_ipns = DCLIMATE_STAC_CATALOG_IPNS +# if not root_catalog_ipns: +# raise ValueError("Root catalog IPNS name is not defined.") + +# logger.info( +# f"Listing datasets by traversing STAC catalog from IPNS: {root_catalog_ipns}" +# ) +# ipfs_store = _get_ipfs_store(gateway_uri_stem, rpc_uri_stem) +# dataset_ids = set() + +# # *** Use fetch_json_from_ipns for the root catalog *** +# try: +# catalog = fetch_json_from_ipns( +# root_catalog_ipns, gateway_uri_stem=gateway_uri_stem +# ) +# except (IpfsConnectionError, StacCatalogError, ValueError) as e: +# # Make error message slightly more specific for listing context +# raise StacCatalogError( +# f"Failed to fetch or parse root catalog from IPNS '{root_catalog_ipns}' for listing datasets: {e}" +# ) from e + +# if not isinstance(catalog, dict) or catalog.get("type") != "Catalog": +# raise StacCatalogError( +# f"Invalid root catalog format fetched from IPNS {root_catalog_ipns} for listing. Type: {catalog.get('type')}" +# ) + +# collections_to_visit = [] +# for link in catalog.get("links", []): +# if link.get("rel") == "child" and link.get("type") == "application/json": +# href_obj = link.get("href") +# # *** MODIFIED: Handle dict href for IPLD links *** +# if isinstance(href_obj, dict): +# collection_cid_str = href_obj.get("/") +# if isinstance(collection_cid_str, str): +# collections_to_visit.append(collection_cid_str) +# else: +# logger.warning( +# f"Skipping child link with invalid href dict content in root catalog during list: {link}" +# ) +# # Add handling for legacy string format if necessary/desired +# # elif isinstance(href_obj, str) and href_obj.startswith("/ipfs/"): ... +# else: +# logger.warning( +# f"Skipping invalid child link format in root catalog during list: {link}" +# ) + +# if not collections_to_visit: +# logger.warning( +# f"No child collection links found in root catalog {root_catalog_ipns} during list." +# ) +# return [] # Return empty list if no collections to check + +# logger.info(f"Found {len(collections_to_visit)} collections to scan for items.") + +# for collection_cid in collections_to_visit: +# try: +# # *** Use fetch_json_from_cid with the extracted CID string *** +# collection = await fetch_json_from_cid(collection_cid, ipfs_store) + +# if ( +# not isinstance(collection, dict) +# or collection.get("type") != "Collection" +# ): +# logger.warning( +# f"Skipping invalid collection format during list (CID: {collection_cid}). Type: {collection.get('type')}" +# ) +# continue + +# for link in collection.get("links", []): +# if link.get("rel") == "item" and link.get("type") == "application/json": +# item_href_obj = link.get("href") +# item_cid = None + +# # *** MODIFIED: Handle dict href for item links *** +# if isinstance(item_href_obj, dict): +# item_cid = item_href_obj.get("/") +# # Add handling for legacy string format if necessary/desired +# # elif isinstance(item_href_obj, str) and item_href_obj.startswith("/ipfs/"): ... +# else: +# logger.warning( +# f"Skipping invalid item link format in collection {collection_cid} during list: {link}" +# ) +# continue + +# if isinstance(item_cid, str): +# try: +# # Fetch item JSON *only* to get the ID and cache the HAMT CID +# # Avoids deeper processing if only listing +# item = await fetch_json_from_cid(item_cid, ipfs_store) + +# if ( +# not isinstance(item, dict) +# or item.get("type") != "Feature" +# ): +# logger.warning( +# f"Skipping invalid item format during list (CID: {item_cid}). Type: {item.get('type')}" +# ) +# continue + +# item_id = item.get("id") +# if isinstance(item_id, str) and item_id: +# dataset_ids.add(item_id) # Add valid ID to our set + +# # Populate cache as a side effect +# hamt_asset = item.get("assets", {}).get("hamt-zarr", {}) +# hamt_cid_href = hamt_asset.get("href") +# if ( +# item_id not in _stac_hamt_cid_cache +# and isinstance(hamt_cid_href, str) +# and hamt_cid_href.startswith("/ipfs/") +# ): +# hamt_cid_str = hamt_cid_href[6:] +# _stac_hamt_cid_cache[item_id] = hamt_cid_str +# logger.debug( +# f"Cached HAMT CID {hamt_cid_str} for {item_id} during list." +# ) +# else: +# logger.warning( +# f"Item {item_cid} has missing or invalid 'id'. Skipping." +# ) + +# except (StacCatalogError, IpfsConnectionError) as item_err: +# # Log and skip this specific item if fetching/parsing fails +# logger.warning( +# f"Skipping item {item_cid} during list due to error: {item_err}" +# ) +# except Exception as item_err: # Catch unexpected errors +# logger.warning( +# f"Skipping item {item_cid} during list due to unexpected error: {type(item_err).__name__}: {item_err}" +# ) +# # else: Invalid item CID extracted, already logged warning + +# # 1️⃣ propagate a StacCatalogError that bubbled up from the **target item** +# except StacCatalogError as col_err: +# raise col_err + +# # 2️⃣ still swallow IPFS/network problems so that other collections can be tried +# except IpfsConnectionError as col_err: +# logger.error( +# f"IPFS error processing collection {collection_cid}, continuing search: " +# f"{col_err}" +# ) +# except Exception as col_err: # Catch unexpected errors +# logger.warning( +# f"Skipping collection {collection_cid} during list due to unexpected error: {type(col_err).__name__}: {col_err}" +# ) + +# found_datasets = sorted(list(dataset_ids)) +# logger.info( +# f"Finished listing datasets. Found {len(found_datasets)} unique dataset IDs." +# ) +# return found_datasets diff --git a/dclimate_zarr_client/loader.py b/dclimate_zarr_client/loader.py new file mode 100644 index 0000000..d316fd4 --- /dev/null +++ b/dclimate_zarr_client/loader.py @@ -0,0 +1,139 @@ +""" +Functions that will map to endpoints in the flask app +""" +from .loaders.era5 import ERA5Loader +from .geotemporal_data import GeotemporalData +import xarray as xr +# from .registry import get_cid_from_registry +from py_hamt import KuboCAS +from multiformats import CID +import json + + +# Mapping of collections to their specific loader classes +LOADER_STRATEGIES = { + "era5": ERA5Loader, + # Add other collections here, e.g., "cpc": CPCLoader +} + +DCLIMATE_GATEWAY_URL = "https://ipfs-gateway.dclimate.net/" + + +async def dclimate_dataset_loader(collection: str, dataset: str, options: dict = None, gateway_url: str = DCLIMATE_GATEWAY_URL) -> xr.Dataset: + """ + Loads a dClimate dataset by dynamically fetching the STAC catalog CID + from an on-chain registry. + + Args: + collection (str): The name of the data collection (e.g., "era5"). + dataset (str): The name of the dataset (e.g., "2m_temperature"). + options (dict, optional): Options for loading, e.g., {'finalized_only': True}. + + Returns: + xr.Dataset: The loaded Xarray dataset. + """ + options = options or {} + + # Select the appropriate loading strategy + loader_class = LOADER_STRATEGIES.get(collection) + loader = loader_class( + gateway_url=gateway_url, + ) + + return await loader.load(dataset=dataset, options=options) + +async def get_geo_temporal_dataset( + collection: str, + dataset: str, + options: dict = None, + gateway_url: str = DCLIMATE_GATEWAY_URL +) -> GeotemporalData: + """ + Loads a geo-temporal dataset from dClimate by dynamically fetching the STAC catalog CID + from an on-chain registry. + """ + options = options or {} + + # Select the appropriate loading strategy + loader_class = LOADER_STRATEGIES.get(collection) + loader = loader_class( + gateway_url=gateway_url, + ) + + ds = await loader.load(dataset=dataset, options=options) + return GeotemporalData(ds, dataset_name=dataset) + + + +async def _load_stac_json_from_cid(cid: str) -> dict: + """Loads a STAC JSON from a given CID.""" + async with KuboCAS(gateway_base_url=DCLIMATE_GATEWAY_URL) as kubo_cas: + try: + item_bytes = await kubo_cas.load(CID.decode(cid)) + item = json.loads(item_bytes) + return item + except Exception as e: + print(f"Error loading STAC JSON from CID {cid}: {e}") + raise + +# Get all datasets in the catalog +# async def get_all_datasets(collection: str, gateway_url: str = DCLIMATE_GATEWAY_URL) -> list: +# """ +# Fetches all datasets available in a specific collection from dClimate. + +# Args: +# collection (str): The name of the data collection (e.g., "era5"). +# gateway_url (str): The IPFS gateway URL to use for fetching data. + +# Returns: +# list: A list of dataset names available in the specified collection. +# """ +# stac_root_cid = get_cid_from_registry() + +# stac_json = await _load_stac_json_from_cid(stac_root_cid) +# if not stac_json: +# raise ValueError(f"No STAC JSON found for collection: {collection}") + +# collection_cid = None + +# # Capitalize all letters in collection +# collection = collection.upper() + +# # The first links that are of rel child are all the collections +# for link in stac_json.get("links", []): +# if link.get("rel") == "child" and link.get("title") == collection: +# collection_cid = link.get("href")["/"] +# break + +# if not collection_cid: +# raise ValueError(f"No datasets found for collection: {collection}") + +# # Get the stac for the collection +# collection_stac = await _load_stac_json_from_cid(collection_cid) +# if not collection_stac: +# raise ValueError(f"No STAC JSON found for collection CID: {collection_cid}") +# # Extract dataset names from the collection STAC +# datasets = [] +# for item in collection_stac.get("links", []): +# if item.get("rel") == "child": +# datasets.append(item.get("title")) +# if item.get("rel") == "item": +# datasets.append(item.get("title")) +# return datasets + +# async def get_all_collections(gateway_url: str = DCLIMATE_GATEWAY_URL) -> list: +# """ +# Fetches all collections available in the dClimate catalog. +# """ +# stac_root_cid = get_cid_from_registry() + +# stac_json = await _load_stac_json_from_cid(stac_root_cid) +# if not stac_json: +# raise ValueError(f"No STAC JSON found for collection: {collection}") + +# collections = [] +# for link in stac_json.get("links", []): +# if link.get("rel") == "child": +# collections.append(link.get("title")) + +# return collections \ No newline at end of file diff --git a/dclimate_zarr_client/loaders/__init__.py b/dclimate_zarr_client/loaders/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dclimate_zarr_client/loaders/base.py b/dclimate_zarr_client/loaders/base.py new file mode 100644 index 0000000..c2f99c7 --- /dev/null +++ b/dclimate_zarr_client/loaders/base.py @@ -0,0 +1,26 @@ +from abc import ABC, abstractmethod +import xarray as xr +from web3 import Web3 +from py_hamt import KuboCAS, ShardedZarrStore +from multiformats import CID +import json + +class BaseLoader(ABC): + + def __init__(self, gateway_url: str): + self.gateway_url = gateway_url + + async def _load_zarr_from_cid(self, cid: str) -> xr.Dataset: + """Loads a sharded Zarr store from a given CID.""" + async with KuboCAS(gateway_base_url=self.gateway_url) as kubo_cas: + store = await ShardedZarrStore.open( + cas=kubo_cas, + read_only=True, + root_cid=cid + ) + return xr.open_zarr(store=store, chunks="auto") + + @abstractmethod + async def load(self, dataset: str, options: dict) -> xr.Dataset: + """Abstract method to be implemented by each strategy.""" + pass \ No newline at end of file diff --git a/dclimate_zarr_client/loaders/era5.py b/dclimate_zarr_client/loaders/era5.py new file mode 100644 index 0000000..6ff98bf --- /dev/null +++ b/dclimate_zarr_client/loaders/era5.py @@ -0,0 +1,48 @@ +import xarray as xr +from .base import BaseLoader +# from dclimate_zarr_client.ipfs_retrieval import get_dataset_stac +import numpy as np +import pandas as pd +from dclimate_zarr_client.registry import resolve_path + +class ERA5Loader(BaseLoader): + collection = "era5" + + # Helps get the finalized or unfinalized dataset CID + # async def _get_dataset_type_cid(self, stac: dict, type: str) -> str: + # links = stac.get("links", []) + # dataset_cid = None + # for link in links: + # if link.get("rel") == "item" and link.get("title") == type: + # dataset_cid = link.get("href")["/"] + # break + # finalized_stac_item = await self._load_stac_json_from_cid(dataset_cid) + # finalized_stac_cid = finalized_stac_item.get("assets", {}).get("sharded-zarr", {}).get("href", None) + # return finalized_stac_cid + + async def load(self, dataset: str, options: dict) -> xr.Dataset: + + finalized_only = options.get('finalized_only', False) + finalized_stac_cid = resolve_path(f"{self.collection}-{dataset}-finalized") + + # # # 2. --- Lazily Open Finalized Dataset --- + ds_finalized = await self._load_zarr_from_cid(finalized_stac_cid) + + if finalized_only: + return ds_finalized + + # # 2. --- Lazily Open Unfinalized Dataset --- + # unfinalized_stac_cid = await self._get_dataset_type_cid(dataset_stac, "non-finalized") + + unfinalized_stac_cid = resolve_path(f"{self.collection}-{dataset}-non_finalized") + ds_unfinalized = await self._load_zarr_from_cid(unfinalized_stac_cid) + + # 3. --- Perform Lazy Slicing and Concatenation --- + # This operation is lazy. It reads only the 'time' coordinate, not the full data. + finalization_date = np.datetime64(ds_finalized.time.max().values) + start_time = finalization_date + np.timedelta64(1, 'h') + ds_unfinalized_sliced = ds_unfinalized.sel(time=slice(start_time, None)) + combined_ds = xr.concat([ds_finalized, ds_unfinalized_sliced], dim="time") + + return combined_ds + \ No newline at end of file diff --git a/dclimate_zarr_client/registry.py b/dclimate_zarr_client/registry.py new file mode 100644 index 0000000..1118e6f --- /dev/null +++ b/dclimate_zarr_client/registry.py @@ -0,0 +1,186 @@ +import os +import json +from pathlib import Path +from eth_account import Account +from ape import accounts, Project, networks +from ape.exceptions import AccountsError +from web3 import Web3 +from typing import List + +# --- Configuration --- + +# Path to the directory containing the compiled contract artifacts +CONTRACTS_PROJECT_PATH = Path(__file__).parent / "contracts" + +# Load environment variables +STAC_REGISTRY_CONTRACT_ADDRESS = os.environ.get("STAC_REGISTRY_CONTRACT_ADDRESS", "0xb54A652489b864638d02C508A2d5Bc14FbFA8df8") +RPC_URL = os.environ.get("RPC_URL", "https://sepolia.base.org") +MNEMONIC_PATH = os.environ.get("MNEMONIC_PATH", "mnemonic.txt") + + +# --- Helper Functions --- + +def _get_web3_instance() -> Web3: + """Initializes and returns a Web3 instance.""" + return Web3(Web3.HTTPProvider(RPC_URL)) + +def _get_contract_abi() -> list: + """Loads the contract ABI from the project's build artifacts.""" + abi_path = CONTRACTS_PROJECT_PATH / "compiled" / "contracts" / "StacRegistry.json" + if not abi_path.is_file(): + raise FileNotFoundError(f"ABI file not found at: {abi_path}. Please compile your contract.") + with open(abi_path) as f: + contract_json = json.load(f) + return contract_json['contractTypes']['StacRegistry']['abi'] + +def _load_owner_account(): + """Loads the owner account from a mnemonic file for web3.py usage.""" + mnemonic_file = Path(MNEMONIC_PATH) + if not mnemonic_file.is_file(): + raise FileNotFoundError(f"Mnemonic file not found at: {MNEMONIC_PATH}") + + Account.enable_unaudited_hdwallet_features() + mnemonic = mnemonic_file.read_text().strip() + return Account.from_mnemonic(mnemonic) + +def _load_deployer_account_ape(): + """Loads a deployer account from a mnemonic for Ape usage.""" + mnemonic_file = Path(MNEMONIC_PATH) + alias = f"etl_deployer_{mnemonic_file.stem}" + try: + return accounts.load(alias) + except AccountsError: + print(f"Ape account '{alias}' not found. Importing from mnemonic...") + return accounts.import_account_from_mnemonic( + account_alias=alias, + mnemonic=mnemonic_file.read_text().strip(), + passphrase=os.environ.get("APE_ACCOUNT_PASSPHRASE", "") + ) + +def _execute_transaction(function_call): + """Builds, signs, and sends a transaction for the given function call.""" + w3 = _get_web3_instance() + owner_account = _load_owner_account() + + tx = function_call.build_transaction({ + 'chainId': w3.eth.chain_id, + 'from': owner_account.address, + 'nonce': w3.eth.get_transaction_count(owner_account.address), + 'gas': 5000000, # Increased default gas limit for more complex transactions + 'gasPrice': w3.eth.gas_price, + }) + + signed_tx = w3.eth.account.sign_transaction(tx, private_key=owner_account.key) + tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) + receipt = w3.eth.wait_for_transaction_receipt(tx_hash) + + print(f"✓ Transaction successful! Hash: {receipt.transactionHash.hex()}") + return receipt.transactionHash.hex() + + +# --- Deployment Function --- + +def deploy_registry(network: str = "base:sepolia") -> str: + """Deploys the StacRegistry contract using Ape.""" + project = Project(path=CONTRACTS_PROJECT_PATH) + deployer = _load_deployer_account_ape() + + with networks.parse_network_choice(network) as provider: + print(f"Using network: {provider.name}") + print(f"Deployer address: {deployer.address}") + + StacRegistry = project.StacRegistry + + print("Deploying StacRegistry...") + contract_instance = deployer.deploy(StacRegistry) + + print(f"✓ Deployment successful! Contract Address: {contract_instance.address}") + return contract_instance.address + +# --- Read-Only (Getter) Functions --- + +def get_stac_root_cid(contract_address: str = STAC_REGISTRY_CONTRACT_ADDRESS) -> str: + """Retrieves the top-level STAC Root CID.""" + w3 = _get_web3_instance() + contract = w3.eth.contract(address=contract_address, abi=_get_contract_abi()) + cid = contract.functions.stacRootCid().call() + print(f"STAC Root CID: {cid}") + return cid + +def resolve_path(path: str, contract_address: str = STAC_REGISTRY_CONTRACT_ADDRESS) -> str: + """Resolves a 'collection-dataset-type' path to its CID.""" + w3 = _get_web3_instance() + contract = w3.eth.contract(address=contract_address, abi=_get_contract_abi()) + cid = contract.functions.resolve(path).call() + print(f"CID for path '{path}': {cid}") + return cid + +def get_collections(contract_address: str = STAC_REGISTRY_CONTRACT_ADDRESS) -> List[str]: + """Retrieves the list of all collection names.""" + w3 = _get_web3_instance() + contract = w3.eth.contract(address=contract_address, abi=_get_contract_abi()) + collections = contract.functions.getCollections().call() + print(f"Available Collections: {collections}") + return collections + +def get_datasets(collection_name: str, contract_address: str = STAC_REGISTRY_CONTRACT_ADDRESS) -> List[str]: + """Retrieves the list of all dataset names for a given collection.""" + w3 = _get_web3_instance() + contract = w3.eth.contract(address=contract_address, abi=_get_contract_abi()) + datasets = contract.functions.getDatasets(collection_name).call() + print(f"Available Datasets in '{collection_name}': {datasets}") + return datasets + +def get_types(collection_name: str, dataset_name: str, contract_address: str = STAC_REGISTRY_CONTRACT_ADDRESS) -> List[str]: + """Retrieves the list of all type names for a given dataset.""" + w3 = _get_web3_instance() + contract = w3.eth.contract(address=contract_address, abi=_get_contract_abi()) + types = contract.functions.getTypes(collection_name, dataset_name).call() + print(f"Available Types in '{collection_name}/{dataset_name}': {types}") + return types + + +# --- State-Changing (Update/Init) Functions --- + +def update_stac_root_cid(new_cid: str, contract_address: str = STAC_REGISTRY_CONTRACT_ADDRESS) -> str: + """Updates the top-level STAC Root CID.""" + print(f"Updating STAC Root CID to: {new_cid}") + w3 = _get_web3_instance() + contract = w3.eth.contract(address=contract_address, abi=_get_contract_abi()) + function_call = contract.functions.updateStacRoot(new_cid) + return _execute_transaction(function_call) + +def update_path_cid(path: str, new_cid: str, category: int = 1, contract_address: str = STAC_REGISTRY_CONTRACT_ADDRESS) -> str: + """ + Updates the CID for a specific 'collection-dataset-type' path. + Category: 0 for APPEND, 1 for REPLACE. + """ + print(f"Updating CID for path '{path}' to: {new_cid}") + w3 = _get_web3_instance() + contract = w3.eth.contract(address=contract_address, abi=_get_contract_abi()) + function_call = contract.functions.updateCid(path, new_cid, category) + return _execute_transaction(function_call) + +def init_collection(collection_name: str, contract_address: str = STAC_REGISTRY_CONTRACT_ADDRESS) -> str: + """Initializes a new collection.""" + print(f"Initializing collection: {collection_name}") + w3 = _get_web3_instance() + contract = w3.eth.contract(address=contract_address, abi=_get_contract_abi()) + function_call = contract.functions.initCollection(collection_name) + return _execute_transaction(function_call) + +def init_dataset(collection_name: str, dataset_name: str, contract_address: str = STAC_REGISTRY_CONTRACT_ADDRESS) -> str: + """Initializes a new dataset within a collection.""" + print(f"Initializing dataset '{dataset_name}' in collection '{collection_name}'") + w3 = _get_web3_instance() + contract = w3.eth.contract(address=contract_address, abi=_get_contract_abi()) + function_call = contract.functions.initDataset(collection_name, dataset_name) + return _execute_transaction(function_call) + +def init_type(collection_name: str, dataset_name: str, type_name: str, initial_cid: str, contract_address: str = STAC_REGISTRY_CONTRACT_ADDRESS) -> str: + """Initializes a new type for a dataset with an initial CID.""" + print(f"Initializing type '{type_name}' for '{collection_name}/{dataset_name}' with CID: {initial_cid}") + w3 = _get_web3_instance() + contract = w3.eth.contract(address=contract_address, abi=_get_contract_abi()) + function_call = contract.functions.initType(collection_name, dataset_name, type_name, initial_cid) + return _execute_transaction(function_call) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index b774661..b265afe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,15 +11,16 @@ authors = [ { name = "Evan Schechter", email = "evan@arbol.io" }, { name = "Chris Rossi", email = "chris@christophermrossi.com" }, { name = "Robert Banick", email = "robert.banick@arbol.io" }, + { name = "Phil Van Meppelen scheppink", email = "phil@dclimate.net" }, ] requires-python = ">=3.12" dependencies = [ "aiobotocore", - "xarray", + "xarray[complete]>=2025.3.0", "rioxarray", - "zarr", + "zarr>=3.0.8", "numpy>=2.1.3", - "py_hamt @ git+https://github.com/dClimate/py-hamt", + "py-hamt>=3.2.0", "requests", "pyarrow", "geopandas", @@ -28,8 +29,19 @@ dependencies = [ "shapely", "scipy", "pycryptodome>=3.21.0", + "eth-ape>=0.8.4", + "web3>=6.20.1", + "pytest-asyncio>=1.0.0", + "ape-base>=0.8.1", + "ape-solidity>=0.8.5", + "eth-account>=0.11.3", ] +[tool.uv.sources] +py-hamt = { git = "https://github.com/dClimate/py-hamt", rev = "a60039b4e2305847c8b9241557a35f96038d7ca0" } +# local path for py-hamt +# py-hamt = { path = "../py-hamt", editable = true } + [project.urls] Homepage = "https://dclimate.net/" diff --git a/tests/test_geotemporal_utils.py b/tests/test_geotemporal_utils.py new file mode 100644 index 0000000..f402d6e --- /dev/null +++ b/tests/test_geotemporal_utils.py @@ -0,0 +1,32 @@ +import numpy as np +import pytest + +from dclimate_zarr_client.geotemporal_data import _haversine, _check_input_parameters +from dclimate_zarr_client import dclimate_zarr_errors as errors + + +def test_haversine_single_points(): + dist = _haversine(0, 0, 0, 1) + assert dist == pytest.approx(111.195, rel=1e-3) + + dist = _haversine(36.12, -86.67, 33.94, -118.40) + assert dist == pytest.approx(2886.44, rel=1e-2) + + +def test_haversine_arrays(): + lats1 = np.array([0, 10]) + lons1 = np.array([0, 0]) + lats2 = np.array([0, 20]) + lons2 = np.array([1, 0]) + dists = _haversine(lats1, lons1, lats2, lons2) + assert np.allclose(dists, [111.195, 1111.95], rtol=1e-3) + + +def test_check_input_parameters_invalid_period(): + with pytest.raises(errors.InvalidTimePeriodError): + _check_input_parameters(time_period="decade") + + +def test_check_input_parameters_invalid_method(): + with pytest.raises(errors.InvalidAggregationMethodError): + _check_input_parameters(agg_method="average") diff --git a/tests/test_integration.py b/tests/test_integration.py new file mode 100644 index 0000000..b937468 --- /dev/null +++ b/tests/test_integration.py @@ -0,0 +1,172 @@ +import pytest +from pathlib import Path +import time +from web3 import Web3 +import os +# Assuming your updated registry functions are in this module +from dclimate_zarr_client import registry, loader + + +# Mark all tests in this file as asyncio +pytestmark = pytest.mark.asyncio + +RPC_URL = os.environ.get("RPC_URL", "https://sepolia.base.org") + +@pytest.fixture(scope="module") +def w3(): + """Provides a Web3 instance connected to a local test network.""" + return Web3(Web3.HTTPProvider(RPC_URL)) + +@pytest.fixture(scope="module") +def contract_address(w3): + """Deploys the contract and returns its address.""" + contract_address = "0xb54A652489b864638d02C508A2d5Bc14FbFA8df8" + return contract_address + +@pytest.fixture(scope="module") +def mnemonic(tmp_path_factory): + """ + Fixture to provide the path to the mnemonic file for testing. + + IMPORTANT: You must manually create a `mnemonic.txt` file in your project's + root directory containing a seed phrase for a wallet funded with testnet ETH + (e.g., Base Sepolia ETH). These tests will fail if the account has no funds. + """ + mnemonic_path = Path("mnemonic.txt") + if not mnemonic_path.exists(): + pytest.fail( + "A `mnemonic.txt` file is required for live testnet tests. " + "Please create it and fund the associated account with testnet ETH." + ) + return str(mnemonic_path) + + +# This test is now parameterized to run ONLY on the Base Sepolia testnet. +# NOTE: This will be slower and will consume real testnet ETH. +# @pytest.mark.parametrize("network", ["base:sepolia"]) +# async def test_full_lifecycle_on_testnet(mnemonic, network): +# """ +# Tests the full contract deployment and hierarchical initialization on the testnet. +# """ +# print(f"\n--- Running Full Integration Test on {network} ---") + +# w3 = registry._get_web3_instance() +# assert w3.is_connected(), "Web3 is not connected. Check your RPC URL and network configuration." + +# # 1. Deploy the contract +# # The new contract doesn't require an initial CID at deployment. +# print("Step 1: Deploying StacSuperRegistry...") +# # contract_address = registry.deploy_registry(network=network) +# contract_address = "0xb54A652489b864638d02C508A2d5Bc14FbFA8df8" +# assert isinstance(contract_address, str) +# assert contract_address.startswith("0x") +# print(f"✅ Deployment successful: {contract_address}") + +# time.sleep(10) + + +# # --- 2. Initialize the full data path --- + +# # Define the data structure +# collection = "era5" +# dataset = "2m_temperature" +# dtype = "non_finalized" +# cid = "bafyr4ibj3bfl5oo7bf6gagzr2g33jlnf23mq2xo632mbl6ytfry7jbuepy" +# full_path = f"{collection}-{dataset}-{dtype}" + +# # Initialize the Collection +# # print(f"\nStep 2: Initializing Collection '{collection}'...") +# # tx_hash_collection = registry.init_collection(contract_address, collection) +# # assert tx_hash_collection.startswith("0x") +# # print(f"✅ Collection initialized. Tx: {tx_hash_collection}") + +# # w3.eth.wait_for_transaction_receipt(tx_hash_collection, timeout=120) # 120-second timeout + +# # time.sleep(10) + +# # Initialize the Dataset +# # print(f"\nStep 3: Initializing Dataset '{dataset}'...") +# # tx_hash_dataset = registry.init_dataset(contract_address, collection, dataset) +# # assert tx_hash_dataset.startswith("0x") +# # print(f"✅ Dataset initialized. Tx: {tx_hash_dataset}") + +# # w3.eth.wait_for_transaction_receipt(tx_hash_dataset, timeout=120) # 120-second timeout +# # time.sleep(10) + + +# # Initialize the Type and set its CID +# print(f"\nStep 4: Initializing Type '{dtype}' with its CID...") +# tx_hash_type = registry.init_type(contract_address, collection, dataset, dtype, cid) +# assert tx_hash_type.startswith("0x") +# print(f"✅ Type initialized. Tx: {tx_hash_type}") + +# w3.eth.wait_for_transaction_receipt(tx_hash_type, timeout=120) # 120-second timeout +# time.sleep(10) + +# # --- 3. Verify the data --- + +# print(f"\nStep 5: Verifying the stored CID for path '{full_path}'...") +# # Use the resolve function to get the CID for the full path +# retrieved_cid = registry.resolve_path(contract_address, full_path) + +# assert retrieved_cid == cid +# print(f"✅ Verification successful! Retrieved CID matches expected CID.") + +# # Optional: Verify getter functions +# print("\nStep 6: Verifying getter functions...") +# collections = registry.get_collections(contract_address) +# datasets = registry.get_datasets(contract_address, collection) +# types = registry.get_types(contract_address, collection, dataset) + +# assert collection in collections +# assert dataset in datasets +# assert dtype in types +# print("✅ Getter functions returned expected values.") + +# def test_update_path_cid(w3, contract_address): +# """ +# Tests that the CID for a given path can be successfully updated. +# """ +# # --- 1. Setup: Define test data and initialize the path --- +# collection = "era5" +# dataset = "2m_temperature" +# dtype = "finalized" +# initial_cid = "baguqehradygn3nlscqe4fnuszz6yhsnsgdtmxv5trose44iw44dfa73axbfq" +# updated_cid = "bafyr4icrox4pxashkfmbyztn7jhp6zjlpj3bufg5ggsjux74zr7ocnqdpu" +# path = f"{collection}-{dataset}-{dtype}" + +# # --- 2. Action: Update the CID --- +# print("\n--- Test Action: Updating CID ---") +# # Using category 1 for REPLACE +# tx_hash_update = registry.update_path_cid(contract_address, path, updated_cid, category=1) +# w3.eth.wait_for_transaction_receipt(tx_hash_update) +# print(f"✅ CID update transaction sent. Tx: {tx_hash_update}") + +# time.sleep(5) + +# # --- 3. Assertion: Verify the CID was updated --- +# print("\n--- Test Assertion: Verifying Update ---") +# resolved_updated_cid = registry.resolve_path(contract_address, path) + +# assert resolved_updated_cid == updated_cid, "The resolved CID should match the updated CID" +# assert resolved_updated_cid != initial_cid, "The resolved CID should no longer be the initial CID" + +# print(f"✅ CID for '{path}' successfully updated to: {resolved_updated_cid}") + + +async def test_era5_loader(): + """ + This test remains unchanged. It tests the Python logic of the data loader + by mocking blockchain and IPFS calls. It is fast and does not depend on + any network connection, which is a testing best practice. + """ + era5_ds = await loader.dclimate_dataset_loader( + collection="era5", + dataset="2m_temperature", + options={'finalized_only': False} + ) + data = era5_ds.sel( + latitude=0, longitude=0, time="2023-01-01T00:00:00" + ).load() # Load the data into memory + assert data['2m_temperature'].values == 300.3167419433594 + \ No newline at end of file diff --git a/tests/test_ipfs_retrieval.py b/tests/test_ipfs_retrieval.py index 357ee39..82dbd6c 100644 --- a/tests/test_ipfs_retrieval.py +++ b/tests/test_ipfs_retrieval.py @@ -1,29 +1,1072 @@ import os +import re import json +from typing import Dict, Any # Import Dict and Any +from unittest.mock import patch, mock_open, MagicMock, ANY import pytest import requests -from unittest.mock import patch, mock_open +from multiformats import CID # Import CID import dclimate_zarr_client.ipfs_retrieval as ipfs_retrieval from dclimate_zarr_client.ipfs_retrieval import ( - # Keep imports for functions still tested here + _stac_hamt_cid_cache, + fetch_json_from_ipns, + get_dataset_hamt_cid_from_stac, get_ipns_name_hash, + _get_dataset_by_ipfs_cid, update_cache_if_changed, + _get_single_metadata, + list_datasets, DatasetNotFoundError, IpfsConnectionError, StacCatalogError, + # Import internal functions for testing + _get_ipfs_store, + fetch_json_from_cid, # noqa: E402 Testing private member + _get_host, # noqa: E402 Testing private member ) - - -# import xarray as xr +from py_hamt import IPFSStore # Import IPFSStore # Import constants/configs from dclimate_zarr_client.client import DCLIMATE_STAC_CATALOG_IPNS from .conftest import KNOWN_STAC_DATASET_ID, KNOWN_STAC_DATASET_ID_2 -# Apply IPFS check fixture to relevant tests/module -pytestmark = pytest.mark.usefixtures("check_ipfs_connection") +# Apply IPFS check fixture ONLY to tests that actually need functional IPFS +# Most tests in this file should be mocked unit tests. +# pytestmark = pytest.mark.usefixtures("check_ipfs_connection") # Remove module-level mark + +# --- Type Hinting --- +MonkeyPatch = pytest.MonkeyPatch +MockIPFSStore = MagicMock +MockResponse = MagicMock # Alias for requests.Response mock + + +# --- Fixtures --- +@pytest.fixture +def mock_ipfs_store() -> MockIPFSStore: + """Fixture to create a mock IPFSStore instance.""" + store = MagicMock(spec=IPFSStore) + store.gateway_uri_stem = "http://mock-gateway:8080" + store.rpc_uri_stem = "http://mock-rpc:5001" + return store + + +@pytest.fixture +def mock_requests_get(mocker) -> MagicMock: + """Fixture to mock requests.get.""" + return mocker.patch("requests.get") + + +# --- Helper to get cache path ---test_get_ipfs_store_defaults +def get_cache_path(): + # Helper to get the expected cache file path within the package + # Need to ensure this reflects the actual location used by the code + package_dir = os.path.dirname(ipfs_retrieval.__file__) + return os.path.join(package_dir, "cids.json") + + +# --- Tests for _get_ipfs_store --- + + +def test_get_ipfs_store_defaults(monkeypatch: MonkeyPatch): + """Test store creation uses defaults when no args/env vars are set.""" + monkeypatch.delenv("IPFS_GATEWAY_URI_STEM", raising=False) + monkeypatch.delenv("IPFS_RPC_URI_STEM", raising=False) + with patch("dclimate_zarr_client.ipfs_retrieval.IPFSStore") as mock_store_class: + store = _get_ipfs_store() + mock_store_class.assert_called_once_with() # Called with no args, uses internal defaults + assert isinstance( + store, MagicMock + ) # We mocked the class, so instance is MagicMock + + +def test_get_ipfs_store_args_override_env(monkeypatch: MonkeyPatch): + """Test that function arguments override environment variables.""" + monkeypatch.setenv("IPFS_GATEWAY_URI_STEM", "http://env-gateway:8080") + monkeypatch.setenv("IPFS_RPC_URI_STEM", "http://env-rpc:5001") + with patch("dclimate_zarr_client.ipfs_retrieval.IPFSStore") as mock_store_class: + store = _get_ipfs_store( + gateway_uri_stem="http://arg-gateway:8080", + rpc_uri_stem="http://arg-rpc:5001", + ) + mock_store_class.assert_called_once_with( + gateway_uri_stem="http://arg-gateway:8080", + rpc_uri_stem="http://arg-rpc:5001", + ) + assert isinstance(store, MagicMock) + + +def test_get_ipfs_store_env_vars(monkeypatch: MonkeyPatch): + """Test that environment variables are used when no args are provided.""" + monkeypatch.setenv("IPFS_GATEWAY_URI_STEM", "http://env-gateway:8080") + monkeypatch.setenv("IPFS_RPC_URI_STEM", "http://env-rpc:5001") + with patch("dclimate_zarr_client.ipfs_retrieval.IPFSStore") as mock_store_class: + store = _get_ipfs_store() + mock_store_class.assert_called_once_with( + gateway_uri_stem="http://env-gateway:8080", + rpc_uri_stem="http://env-rpc:5001", + ) + assert isinstance(store, MagicMock) + + +def test_get_ipfs_store_mixed_args_env(monkeypatch: MonkeyPatch): + """Test using a mix of args and env vars (args should take precedence).""" + monkeypatch.setenv("IPFS_GATEWAY_URI_STEM", "http://env-gateway:8080") + monkeypatch.delenv("IPFS_RPC_URI_STEM", raising=False) # RPC not set in env + with patch("dclimate_zarr_client.ipfs_retrieval.IPFSStore") as mock_store_class: + store = _get_ipfs_store( + rpc_uri_stem="http://arg-rpc:5001" + ) # Provide RPC as arg + mock_store_class.assert_called_once_with( + gateway_uri_stem="http://env-gateway:8080", # Gateway comes from env + rpc_uri_stem="http://arg-rpc:5001", # RPC comes from arg + ) + assert isinstance(store, MagicMock) + + +# --- Tests for fetch_json_from_cid (Unit/Mocked) --- + + +def test_fetch_json_from_cid_success(mock_ipfs_store: MockIPFSStore): + """Test successful fetching and decoding of JSON from CID.""" + valid_cid_str = "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi" + json_data: Dict[str, Any] = {"key": "value"} + mock_ipfs_store.load.return_value = json.dumps(json_data).encode("utf-8") + result: Dict[str, Any] = fetch_json_from_cid(valid_cid_str, mock_ipfs_store) + assert result == json_data + mock_ipfs_store.load.assert_called_once() + # Check that CID.decode was implicitly called by store.load mock (or explicitly if store expects CID obj) + # For MagicMock, we check the arg type if needed, assuming it passes the string + call_args = mock_ipfs_store.load.call_args[0] + assert isinstance(call_args[0], CID) + assert str(call_args[0]) == valid_cid_str + + +def test_fetch_json_from_cid_success_with_prefix(mock_ipfs_store: MockIPFSStore): + """Test successful fetching when CID string has /ipfs/ prefix.""" + cid_str_no_prefix = "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi" + cid_str_with_prefix = f"/ipfs/{cid_str_no_prefix}" + json_data: Dict[str, Any] = {"key": "value"} + mock_ipfs_store.load.return_value = json.dumps(json_data).encode("utf-8") + result: Dict[str, Any] = fetch_json_from_cid(cid_str_with_prefix, mock_ipfs_store) + assert result == json_data + mock_ipfs_store.load.assert_called_once() + call_args = mock_ipfs_store.load.call_args[0] + assert isinstance(call_args[0], CID) + assert str(call_args[0]) == cid_str_no_prefix # Prefix should be stripped + + +def test_fetch_json_from_cid_invalid_cid_string(mock_ipfs_store: MockIPFSStore): + """Test StacCatalogError when CID string is invalid.""" + invalid_cid_str = "this-is-not-a-cid" + with pytest.raises( + StacCatalogError, match=f"Failed to decode CID string '{invalid_cid_str}'" + ): + fetch_json_from_cid(invalid_cid_str, mock_ipfs_store) + mock_ipfs_store.load.assert_not_called() # Should fail before calling load + + +def test_fetch_json_from_cid_load_returns_none(mock_ipfs_store: MockIPFSStore): + """Test StacCatalogError when ipfs_store.load returns None or empty bytes.""" + valid_cid_str = "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi" + mock_ipfs_store.load.return_value = b"" # Empty bytes + with pytest.raises( + StacCatalogError, match=f"No data returned for CID: {valid_cid_str}" + ): + fetch_json_from_cid(valid_cid_str, mock_ipfs_store) + mock_ipfs_store.load.assert_called_once() + + +def test_fetch_json_from_cid_json_decode_error(mock_ipfs_store: MockIPFSStore): + """Test StacCatalogError when fetched data is not valid JSON.""" + valid_cid_str = "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi" + mock_ipfs_store.load.return_value = b"this is not json" + with pytest.raises( + StacCatalogError, match=f"Failed to decode JSON from CID {valid_cid_str}" + ): + fetch_json_from_cid(valid_cid_str, mock_ipfs_store) + mock_ipfs_store.load.assert_called_once() + + +def test_fetch_json_from_cid_timeout_error(mock_ipfs_store: MockIPFSStore): + """Test IpfsConnectionError when ipfs_store.load raises Timeout.""" + valid_cid_str = "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi" + mock_ipfs_store.load.side_effect = requests.exceptions.Timeout("Request timed out") + with pytest.raises( + IpfsConnectionError, match=f"Timeout fetching CID {valid_cid_str}" + ): + fetch_json_from_cid(valid_cid_str, mock_ipfs_store) + mock_ipfs_store.load.assert_called_once() + + +def test_fetch_json_from_cid_connection_error(mock_ipfs_store: MockIPFSStore): + """Test IpfsConnectionError when ipfs_store.load raises connection-related error.""" + valid_cid_str = "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi" + # Simulate different connection error messages + errors_to_test = [ + requests.exceptions.ConnectionError("Connection refused"), + requests.exceptions.RequestException("Max retries exceeded"), + requests.exceptions.RequestException("Failed to establish a new connection"), + ] + for error in errors_to_test: + mock_ipfs_store.load.side_effect = error + with pytest.raises( + IpfsConnectionError, + match=f"Failed to connect via IPFSStore.*to fetch CID {valid_cid_str}", + ): + fetch_json_from_cid(valid_cid_str, mock_ipfs_store) + mock_ipfs_store.load.assert_called_once() + mock_ipfs_store.load.reset_mock() # Reset mock for next error in loop + + +def test_fetch_json_from_cid_generic_load_error(mock_ipfs_store: MockIPFSStore): + """Test StacCatalogError for other exceptions during ipfs_store.load.""" + valid_cid_str = "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi" + mock_ipfs_store.load.side_effect = RuntimeError("Some other IPFSStore error") + + with pytest.raises( + StacCatalogError, + match=f"Error fetching data for CID {valid_cid_str} via IPFSStore", + ): + fetch_json_from_cid(valid_cid_str, mock_ipfs_store) + mock_ipfs_store.load.assert_called_once() + + +# --- Tests for _get_host --- +def test_get_host_default(): + """Test _get_host uses the default localhost when env var is not set.""" + # Import DEFAULT_HOST to use in assertion + from dclimate_zarr_client.ipfs_retrieval import DEFAULT_HOST + + with patch.dict(os.environ, {}, clear=True): # Ensure env var is not set + assert _get_host() == DEFAULT_HOST # Check default URI + # If IPFS_HOST is not set, _get_host IGNORES the uri argument and returns DEFAULT_HOST + assert _get_host("/custom/uri") == DEFAULT_HOST + + +def test_get_host_from_env(monkeypatch: MonkeyPatch): + """Test _get_host uses IPFS_HOST environment variable.""" + monkeypatch.setenv("IPFS_HOST", "http://my-ipfs-node:5002") + assert _get_host() == "http://my-ipfs-node:5002/api/v0" + assert _get_host("/other/uri") == "http://my-ipfs-node:5002/other/uri" + + +# --- NEW: Tests for fetch_json_from_ipns (Mocked Error Paths) --- + + +class TestFetchJsonFromIpnsErrors: + MOCK_IPNS = "k51qzi5uqu5dk89atnl883sr0g1cb2py631ckz9ng45qhk6dg0pj141jtxtx6l" + MOCK_GATEWAY = "http://mock-gateway:8080" + EXPECTED_URL = f"{MOCK_GATEWAY}/ipns/{MOCK_IPNS}" + + @pytest.fixture(autouse=True) + def setup_mocks(self, monkeypatch, mock_requests_get): + # Mock _get_ipfs_store to return a predictable gateway + mock_store = MagicMock(spec=IPFSStore) + mock_store.gateway_uri_stem = self.MOCK_GATEWAY + monkeypatch.setattr( + ipfs_retrieval, "_get_ipfs_store", lambda *args, **kwargs: mock_store + ) + self.mock_requests_get = mock_requests_get + + def mock_response( + self, status_code=200, json_data=None, text=None, raise_for_status_error=None + ) -> MockResponse: + mock_resp = MagicMock(spec=requests.Response) + mock_resp.status_code = status_code + mock_resp.raise_for_status.side_effect = raise_for_status_error + if json_data is not None: + mock_resp.json.return_value = json_data + # If json_data is provided, requests usually sets text as well + mock_resp.text = json.dumps(json_data) if text is None else text + else: + mock_resp.json.side_effect = requests.exceptions.JSONDecodeError( + "Expecting value", "doc", 0 + ) + mock_resp.text = text if text is not None else "Invalid JSON" + return mock_resp + + def test_fetch_json_from_ipns_empty_name(self): + with pytest.raises(ValueError, match="IPNS name cannot be empty"): + fetch_json_from_ipns("") + + def test_fetch_json_from_ipns_initial_timeout_then_success(self): + """Simulate timeout on first try, success on retry.""" + mock_resp_success = self.mock_response( + status_code=200, json_data={"type": "Catalog"} + ) + self.mock_requests_get.side_effect = [ + requests.exceptions.Timeout("Initial timeout"), + mock_resp_success, + ] + + result = fetch_json_from_ipns(self.MOCK_IPNS) + + assert result == {"type": "Catalog"} + assert self.mock_requests_get.call_count == 2 + # Check params: first call with nocache=true, second without + assert self.mock_requests_get.call_args_list[0].kwargs["params"] == { + "nocache": "true" + } + assert self.mock_requests_get.call_args_list[1].kwargs["params"] == {} + + def test_fetch_json_from_ipns_initial_connection_error(self): + """Simulate ConnectionError on first try (should raise immediately).""" + self.mock_requests_get.side_effect = requests.exceptions.ConnectionError( + "Gateway down" + ) + + with pytest.raises(IpfsConnectionError, match="Connection error fetching IPNS"): + fetch_json_from_ipns(self.MOCK_IPNS) + assert self.mock_requests_get.call_count == 1 + + def test_fetch_json_from_ipns_initial_json_decode_then_success(self): + """Simulate JSON decode error on first try, success on retry.""" + mock_resp_bad_json = self.mock_response( + status_code=200, text="error page" + ) + mock_resp_success = self.mock_response( + status_code=200, json_data={"type": "Catalog"} + ) + self.mock_requests_get.side_effect = [mock_resp_bad_json, mock_resp_success] + + result = fetch_json_from_ipns(self.MOCK_IPNS) + + assert result == {"type": "Catalog"} + assert self.mock_requests_get.call_count == 2 + assert self.mock_requests_get.call_args_list[0].kwargs["params"] == { + "nocache": "true" + } + assert self.mock_requests_get.call_args_list[1].kwargs["params"] == {} + + def test_fetch_json_from_ipns_initial_500_error_then_success(self): + """Simulate 500 HTTP error on first try, success on retry.""" + mock_resp_500 = self.mock_response( + status_code=500, + text="Server Error", + raise_for_status_error=requests.exceptions.HTTPError("500 Error"), + ) + mock_resp_success = self.mock_response( + status_code=200, json_data={"type": "Catalog"} + ) + self.mock_requests_get.side_effect = [mock_resp_500, mock_resp_success] + + result = fetch_json_from_ipns(self.MOCK_IPNS) + + assert result == {"type": "Catalog"} + assert self.mock_requests_get.call_count == 2 + + def test_fetch_json_from_ipns_retry_timeout(self): + """Simulate failure on first try (e.g., timeout) AND timeout on retry.""" + self.mock_requests_get.side_effect = [ + requests.exceptions.Timeout("Initial timeout"), + requests.exceptions.Timeout("Retry timeout"), + ] + + with pytest.raises( + IpfsConnectionError, match="Timeout .* during IPNS fetch retry" + ): + fetch_json_from_ipns(self.MOCK_IPNS) + assert self.mock_requests_get.call_count == 2 + + def test_fetch_json_from_ipns_retry_connection_error(self): + """Simulate failure on first try AND ConnectionError on retry.""" + self.mock_requests_get.side_effect = [ + requests.exceptions.Timeout("Initial timeout"), + requests.exceptions.ConnectionError("Retry connection failed"), + ] + + with pytest.raises( + IpfsConnectionError, match="Connection error during IPNS fetch retry" + ): + fetch_json_from_ipns(self.MOCK_IPNS) + assert self.mock_requests_get.call_count == 2 + + def test_fetch_json_from_ipns_retry_json_decode_error(self): + """Simulate failure on first try AND JSON decode error on retry.""" + mock_resp_bad_json_retry = self.mock_response( + status_code=200, text="Retry also bad json" + ) + self.mock_requests_get.side_effect = [ + requests.exceptions.Timeout("Initial timeout"), + mock_resp_bad_json_retry, + ] + + with pytest.raises( + StacCatalogError, match="Invalid JSON fetching IPNS .* \\(retry\\)" + ): + fetch_json_from_ipns(self.MOCK_IPNS) + assert self.mock_requests_get.call_count == 2 + + def test_fetch_json_from_ipns_retry_http_error(self): + """Simulate failure on first try AND HTTP error on retry.""" + mock_resp_503_retry = self.mock_response( + status_code=503, + text="Service Unavailable", + raise_for_status_error=requests.exceptions.HTTPError("503 Error"), + ) + self.mock_requests_get.side_effect = [ + requests.exceptions.Timeout("Initial timeout"), + mock_resp_503_retry, + ] + + with pytest.raises( + StacCatalogError, match="Error fetching IPNS .* \\(retry\\) via Gateway" + ): + fetch_json_from_ipns(self.MOCK_IPNS) + assert self.mock_requests_get.call_count == 2 + + def test_fetch_json_from_ipns_initial_other_exception_then_success(self): + """Simulate generic exception on first try, success on retry.""" + mock_resp_success = self.mock_response( + status_code=200, json_data={"type": "Catalog"} + ) + self.mock_requests_get.side_effect = [ + RuntimeError("Unexpected issue"), + mock_resp_success, + ] + result = fetch_json_from_ipns(self.MOCK_IPNS) + assert result == {"type": "Catalog"} + assert self.mock_requests_get.call_count == 2 + + def test_fetch_json_from_ipns_retry_other_exception(self): + """Simulate failure on first try AND generic exception on retry.""" + self.mock_requests_get.side_effect = [ + requests.exceptions.Timeout("Initial timeout"), + RuntimeError("Unexpected issue on retry"), + ] + with pytest.raises( + StacCatalogError, match="Unexpected error during IPNS fetch retry" + ): + fetch_json_from_ipns(self.MOCK_IPNS) + assert self.mock_requests_get.call_count == 2 + + +# --- NEW: Tests for get_dataset_hamt_cid_from_stac (Mocked Error Paths) --- + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_get_hamt_cid_root_catalog_fetch_error(mock_fetch_cid, mock_fetch_ipns): + """Test error when fetching the root catalog fails.""" + mock_fetch_ipns.side_effect = IpfsConnectionError("Cannot connect to gateway") + with pytest.raises(StacCatalogError, match="Failed to fetch root catalog"): + get_dataset_hamt_cid_from_stac( + DCLIMATE_STAC_CATALOG_IPNS, KNOWN_STAC_DATASET_ID + ) + mock_fetch_ipns.assert_called_once() + mock_fetch_cid.assert_not_called() + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_get_hamt_cid_invalid_root_catalog_format(mock_fetch_cid, mock_fetch_ipns): + """Test error when root catalog JSON is not a valid STAC Catalog.""" + mock_fetch_ipns.return_value = {"not": "a catalog"} + with pytest.raises(StacCatalogError, match="Invalid root catalog format"): + get_dataset_hamt_cid_from_stac( + DCLIMATE_STAC_CATALOG_IPNS, KNOWN_STAC_DATASET_ID + ) + mock_fetch_ipns.assert_called_once() + mock_fetch_cid.assert_not_called() + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_get_hamt_cid_no_child_links(mock_fetch_cid, mock_fetch_ipns): + """Test error when root catalog has no valid child links.""" + mock_fetch_ipns.return_value = { + "type": "Catalog", + "id": "root", + "links": [{"rel": "self", "href": "."}], # No child links + } + with pytest.raises(StacCatalogError, match="No valid child collection links found"): + get_dataset_hamt_cid_from_stac( + DCLIMATE_STAC_CATALOG_IPNS, KNOWN_STAC_DATASET_ID + ) + mock_fetch_ipns.assert_called_once() + mock_fetch_cid.assert_not_called() + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_get_hamt_cid_invalid_child_link_formats(mock_fetch_cid, mock_fetch_ipns): + """Test skipping various invalid child link formats.""" + mock_fetch_ipns.return_value = { + "type": "Catalog", + "id": "root", + "links": [ + { + "rel": "child", + "type": "application/json", + "href": {"/": 123}, + }, # Invalid CID value + { + "rel": "child", + "type": "application/json", + "href": "/ipfs/legacy_cid_string", + }, # Legacy string (warning expected) + { + "rel": "child", + "type": "application/json", + "href": "not_a_dict_or_ipfs_string", + }, # Invalid format + {"rel": "child", "type": "application/json"}, # Missing href + { + "rel": "child", + "type": "application/xml", + "href": {"/": "cid1"}, + }, # Wrong type + { + "rel": "item", + "type": "application/json", + "href": {"/": "cid2"}, + }, # Wrong rel + ], + } + # Expect DatasetNotFoundError because no *valid* child links lead anywhere + with pytest.raises( + DatasetNotFoundError, match=f"Dataset ID '{KNOWN_STAC_DATASET_ID}' not found" + ): + get_dataset_hamt_cid_from_stac( + DCLIMATE_STAC_CATALOG_IPNS, KNOWN_STAC_DATASET_ID + ) + mock_fetch_ipns.assert_called_once() + mock_fetch_cid.assert_called_once() # One legacy string was allowed + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_get_hamt_cid_collection_fetch_error(mock_fetch_cid, mock_fetch_ipns): + """Test scenario where fetching a collection fails but others might succeed.""" + target_dataset = "dataset-in-good-collection" + good_collection_cid = "bafyGoodCollection" + bad_collection_cid = "bafyBadCollection" + + mock_fetch_ipns.return_value = { + "type": "Catalog", + "id": "root", + "links": [ + { + "rel": "child", + "type": "application/json", + "href": {"/": bad_collection_cid}, + }, + { + "rel": "child", + "type": "application/json", + "href": {"/": good_collection_cid}, + }, + ], + } + # Mock fetch_json_from_cid behavior + good_item_cid = "bafyGoodItem" + good_hamt_cid = "bafyGoodHAMT" + + def fetch_cid_side_effect(cid_str, store): + if cid_str == bad_collection_cid: + raise IpfsConnectionError("Failed to fetch bad collection") + elif cid_str == good_collection_cid: + return { + "type": "Collection", + "id": "good-collection", + "links": [ + { + "rel": "item", + "type": "application/json", + "href": {"/": good_item_cid}, + } + ], + } + elif cid_str == good_item_cid: + return { + "type": "Feature", + "id": target_dataset, + "assets": {"hamt-zarr": {"href": f"/ipfs/{good_hamt_cid}"}}, + } + else: + raise ValueError(f"Unexpected CID requested: {cid_str}") + + mock_fetch_cid.side_effect = fetch_cid_side_effect + + # Should succeed by finding the dataset in the good collection + result_cid = get_dataset_hamt_cid_from_stac( + DCLIMATE_STAC_CATALOG_IPNS, target_dataset + ) + assert result_cid == good_hamt_cid + assert ( + mock_fetch_cid.call_count == 3 + ) # Bad collection fetch fails, good collection + good item fetches succeed + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_get_hamt_cid_invalid_collection_format(mock_fetch_cid, mock_fetch_ipns): + """Test skipping invalid collection format.""" + collection_cid = "bafyValidCollectionLink" + mock_fetch_ipns.return_value = { + "type": "Catalog", + "id": "root", + "links": [ + {"rel": "child", "type": "application/json", "href": {"/": collection_cid}} + ], + } + mock_fetch_cid.return_value = {"not": "a collection"} # Invalid format + + with pytest.raises( + DatasetNotFoundError, match=f"Dataset ID '{KNOWN_STAC_DATASET_ID}' not found" + ): + get_dataset_hamt_cid_from_stac( + DCLIMATE_STAC_CATALOG_IPNS, KNOWN_STAC_DATASET_ID + ) + mock_fetch_cid.assert_called_once_with( + collection_cid, ANY + ) # Assuming default store used internally + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_get_hamt_cid_item_fetch_error(mock_fetch_cid, mock_fetch_ipns): + """Test scenario where fetching an item fails but the target is in another item.""" + target_dataset = KNOWN_STAC_DATASET_ID + collection_cid = "bafyCollectionWithItems" + bad_item_cid = "bafyBadItem" + good_item_cid = "bafyGoodItem" + good_hamt_cid = "bafyTargetHAMT" + + mock_fetch_ipns.return_value = { + "type": "Catalog", + "id": "root", + "links": [ + {"rel": "child", "type": "application/json", "href": {"/": collection_cid}} + ], + } + + def fetch_cid_side_effect(cid_str, store): + if cid_str == collection_cid: + return { + "type": "Collection", + "id": "collection", + "links": [ + { + "rel": "item", + "type": "application/json", + "href": {"/": bad_item_cid}, + }, + { + "rel": "item", + "type": "application/json", + "href": {"/": good_item_cid}, + }, + ], + } + elif cid_str == bad_item_cid: + raise StacCatalogError("Failed to fetch bad item") + elif cid_str == good_item_cid: + return { + "type": "Feature", + "id": target_dataset, + "assets": {"hamt-zarr": {"href": f"/ipfs/{good_hamt_cid}"}}, + } + else: + raise ValueError(f"Unexpected CID requested: {cid_str}") + + mock_fetch_cid.side_effect = fetch_cid_side_effect + + result_cid = get_dataset_hamt_cid_from_stac( + DCLIMATE_STAC_CATALOG_IPNS, target_dataset + ) + assert result_cid == good_hamt_cid + # Called for collection, bad item (failed), good item (success) + assert mock_fetch_cid.call_count == 3 + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_get_hamt_cid_invalid_item_format(mock_fetch_cid, mock_fetch_ipns): + """Test skipping invalid item format.""" + # --- FIX: Clear cache at the start of the test --- + _stac_hamt_cid_cache.clear() + + collection_cid = "bafyCollection" + item_cid = "bafyItem" + mock_fetch_ipns.return_value = { + "type": "Catalog", + "id": "dclimate-stac-catalog", + "links": [ + {"rel": "child", "type": "application/json", "href": {"/": collection_cid}} + ], + } + mock_fetch_cid.side_effect = [ + # First call returns collection + { + "type": "Collection", + "id": "collection", + "links": [ + {"rel": "item", "type": "application/json", "href": {"/": item_cid}} + ], + }, + # Second call returns invalid item + {"not": "a feature"}, + ] + with pytest.raises( + DatasetNotFoundError, match=f"Dataset ID '{KNOWN_STAC_DATASET_ID}' not found" + ): + get_dataset_hamt_cid_from_stac( + DCLIMATE_STAC_CATALOG_IPNS, KNOWN_STAC_DATASET_ID + ) + assert mock_fetch_cid.call_count == 2 + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_get_hamt_cid_item_missing_hamt_asset(mock_fetch_cid, mock_fetch_ipns): + """Test error if the found item doesn't have the 'hamt-zarr' asset href.""" + target_dataset = KNOWN_STAC_DATASET_ID + collection_cid = "bafyCollection" + item_cid = "bafyItem" + mock_fetch_ipns.return_value = { + "type": "Catalog", + "id": "root", + "links": [ + {"rel": "child", "type": "application/json", "href": {"/": collection_cid}} + ], + } + mock_fetch_cid.side_effect = [ + { + "type": "Collection", + "id": "collection", + "links": [ + {"rel": "item", "type": "application/json", "href": {"/": item_cid}} + ], + }, + { + "type": "Feature", + "id": target_dataset, + "assets": {}, + }, # Missing assets.hamt-zarr.href + ] + with pytest.raises( + StacCatalogError, match="missing a valid string 'assets.hamt-zarr.href'" + ): + get_dataset_hamt_cid_from_stac(DCLIMATE_STAC_CATALOG_IPNS, target_dataset) + assert mock_fetch_cid.call_count == 2 + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_get_hamt_cid_item_invalid_hamt_asset_href(mock_fetch_cid, mock_fetch_ipns): + """Test error if the hamt-zarr href is not a valid /ipfs/ string.""" + target_dataset = KNOWN_STAC_DATASET_ID + collection_cid = "bafyCollection" + item_cid = "bafyItem" + mock_fetch_ipns.return_value = { + "type": "Catalog", + "id": "root", + "links": [ + {"rel": "child", "type": "application/json", "href": {"/": collection_cid}} + ], + } + mock_fetch_cid.side_effect = [ + { + "type": "Collection", + "id": "collection", + "links": [ + {"rel": "item", "type": "application/json", "href": {"/": item_cid}} + ], + }, + { + "type": "Feature", + "id": target_dataset, + "assets": {"hamt-zarr": {"href": "not-an-ipfs-link"}}, + }, + ] + with pytest.raises( + StacCatalogError, match="missing a valid string 'assets.hamt-zarr.href'" + ): + get_dataset_hamt_cid_from_stac(DCLIMATE_STAC_CATALOG_IPNS, target_dataset) + assert mock_fetch_cid.call_count == 2 + + +# --- NEW: Tests for _get_dataset_by_ipfs_cid (Unit/Mocked Error Paths) --- + + +def test_get_dataset_by_ipfs_cid_empty(): + """Test ValueError if ipfs_cid is empty.""" + with pytest.raises(ValueError, match="IPFS CID cannot be empty"): + _get_dataset_by_ipfs_cid("") + + +def test_get_dataset_by_ipfs_cid_invalid_format(): + """Test ValueError if ipfs_cid is not a valid CID format.""" + with pytest.raises(ValueError, match="Invalid IPFS CID format"): + _get_dataset_by_ipfs_cid("this-is-definitely-not-a-cid") + + +@patch("dclimate_zarr_client.ipfs_retrieval.xr.open_zarr") +@patch("dclimate_zarr_client.ipfs_retrieval.IPFSZarr3") +@patch("dclimate_zarr_client.ipfs_retrieval.HAMT") +@patch("dclimate_zarr_client.ipfs_retrieval._get_ipfs_store") +def test_get_dataset_by_ipfs_cid_zarr_not_found( + mock_get_store, mock_hamt, mock_ipfs_zarr3, mock_open_zarr +): + """Test StacCatalogError if Zarr metadata (e.g., .zgroup) is missing.""" + valid_cid = ( + "bafybeicg2rebjoofv4kbyovkw7af3rpiitvnl6i7ckcywaq6za2eflbka4" # Example CID + ) + mock_open_zarr.side_effect = FileNotFoundError( + "[Errno 2] No such file or directory: '.zgroup'" + ) # Simulate xr.open_zarr error + + with pytest.raises( + StacCatalogError, match=f"Zarr metadata not found at CID {valid_cid}" + ): + _get_dataset_by_ipfs_cid(valid_cid) + + mock_get_store.assert_called_once() + mock_hamt.assert_called_once() + mock_ipfs_zarr3.assert_called_once() + mock_open_zarr.assert_called_once() + + +@patch("dclimate_zarr_client.ipfs_retrieval.xr.open_zarr") +@patch("dclimate_zarr_client.ipfs_retrieval.IPFSZarr3") +@patch("dclimate_zarr_client.ipfs_retrieval.HAMT") +@patch("dclimate_zarr_client.ipfs_retrieval._get_ipfs_store") +def test_get_dataset_by_ipfs_cid_connection_error( + mock_get_store, mock_hamt, mock_ipfs_zarr3, mock_open_zarr +): + """Test IpfsConnectionError if loading data fails due to connection issues.""" + valid_cid = "bafybeicg2rebjoofv4kbyovkw7af3rpiitvnl6i7ckcywaq6za2eflbka4" + # Simulate error during xr.open_zarr which tries to read from the store + mock_open_zarr.side_effect = requests.exceptions.ConnectionError( + "Connection refused during zarr open" + ) + + with pytest.raises( + IpfsConnectionError, + match=f"IPFS connection failed while loading dataset from CID {valid_cid}", + ): + _get_dataset_by_ipfs_cid(valid_cid) + + +@patch("dclimate_zarr_client.ipfs_retrieval.xr.open_zarr") +@patch("dclimate_zarr_client.ipfs_retrieval.IPFSZarr3") +@patch("dclimate_zarr_client.ipfs_retrieval.HAMT") +@patch("dclimate_zarr_client.ipfs_retrieval._get_ipfs_store") +def test_get_dataset_by_ipfs_cid_other_runtime_error( + mock_get_store, mock_hamt, mock_ipfs_zarr3, mock_open_zarr +): + """Test generic RuntimeError for other failures during loading.""" + valid_cid = "bafybeicg2rebjoofv4kbyovkw7af3rpiitvnl6i7ckcywaq6za2eflbka4" + mock_open_zarr.side_effect = Exception("Some Zarr parsing error") + + with pytest.raises( + RuntimeError, match=f"Failed to load Zarr dataset from IPFS CID {valid_cid}" + ): + _get_dataset_by_ipfs_cid(valid_cid) + + +# --- NEW: Tests for list_datasets (Mocked Error Paths) --- + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_list_datasets_root_catalog_fetch_error(mock_fetch_cid, mock_fetch_ipns): + """Test list_datasets fails if root catalog fetch fails.""" + mock_fetch_ipns.side_effect = StacCatalogError("Cannot fetch root") + with pytest.raises( + StacCatalogError, + match="Failed to fetch or parse root catalog.*for listing datasets", + ): + list_datasets(root_catalog_ipns=DCLIMATE_STAC_CATALOG_IPNS) + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_list_datasets_no_collections(mock_fetch_cid, mock_fetch_ipns): + """Test list_datasets returns empty list if no child collections found.""" + mock_fetch_ipns.return_value = {"type": "Catalog", "id": "root", "links": []} + result = list_datasets(root_catalog_ipns=DCLIMATE_STAC_CATALOG_IPNS) + assert result == [] + mock_fetch_cid.assert_not_called() + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_list_datasets_fails_on_bad_collection(mock_fetch_cid, mock_fetch_ipns): + """Test list_datasets fails if any collection fails to load.""" + good_collection_cid = "bafyGoodCollection" + bad_collection_cid = "bafyBadCollection" + good_item_cid = "bafyGoodItem" + good_item_id = "good-dataset-id" + + mock_fetch_ipns.return_value = { + "type": "Catalog", + "id": "root", + "links": [ + { + "rel": "child", + "type": "application/json", + "href": {"/": bad_collection_cid}, + }, + { + "rel": "child", + "type": "application/json", + "href": {"/": good_collection_cid}, + }, + ], + } + + def fetch_cid_side_effect(cid_str, store): + if cid_str == bad_collection_cid: + raise StacCatalogError("Cannot load bad collection") + elif cid_str == good_collection_cid: + return { + "type": "Collection", + "id": "good", + "links": [ + { + "rel": "item", + "type": "application/json", + "href": {"/": good_item_cid}, + } + ], + } + elif cid_str == good_item_cid: + return { + "type": "Feature", + "id": good_item_id, + "assets": {"hamt-zarr": {"href": "/ipfs/bafyHAMT"}}, + } + else: + raise ValueError("Unknown CID") + + mock_fetch_cid.side_effect = fetch_cid_side_effect + + with pytest.raises(StacCatalogError, match="Cannot load bad collection"): + list_datasets(root_catalog_ipns=DCLIMATE_STAC_CATALOG_IPNS) + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_cid") +def test_list_datasets_skips_bad_item(mock_fetch_cid, mock_fetch_ipns): + """Test list_datasets skips items that fail to load or parse.""" + collection_cid = "bafyCollection" + bad_item_cid = "bafyBadItem" + good_item_cid = "bafyGoodItem" + invalid_format_item_cid = "bafyInvalidFormatItem" + missing_id_item_cid = "bafyMissingIdItem" + + good_item_id = "good-dataset-id" + + mock_fetch_ipns.return_value = { + "type": "Catalog", + "id": "root", + "links": [ + {"rel": "child", "type": "application/json", "href": {"/": collection_cid}} + ], + } + + def fetch_cid_side_effect(cid_str, store): + if cid_str == collection_cid: + return { + "type": "Collection", + "id": "coll", + "links": [ + { + "rel": "item", + "type": "application/json", + "href": {"/": bad_item_cid}, + }, + { + "rel": "item", + "type": "application/json", + "href": {"/": good_item_cid}, + }, + { + "rel": "item", + "type": "application/json", + "href": {"/": invalid_format_item_cid}, + }, + { + "rel": "item", + "type": "application/json", + "href": {"/": missing_id_item_cid}, + }, + ], + } + elif cid_str == bad_item_cid: + raise IpfsConnectionError("Cannot load bad item") + elif cid_str == good_item_cid: + return { + "type": "Feature", + "id": good_item_id, + "assets": {"hamt-zarr": {"href": "/ipfs/bafyHAMT"}}, + } + elif cid_str == invalid_format_item_cid: + return {"not": "a feature"} + elif cid_str == missing_id_item_cid: + return {"type": "Feature", "assets": {}} # Missing ID + else: + raise ValueError("Unknown CID") + + mock_fetch_cid.side_effect = fetch_cid_side_effect + + result = list_datasets(root_catalog_ipns=DCLIMATE_STAC_CATALOG_IPNS) + assert result == [good_item_id] # Only the good dataset is listed + # Called for collection, bad item, good item, invalid item, missing ID item + assert mock_fetch_cid.call_count == 5 + + +@patch("dclimate_zarr_client.ipfs_retrieval.fetch_json_from_ipns") +def test_list_datasets_uses_default_root_catalog(mock_fetch_ipns): + """Test that list_datasets uses DCLIMATE_STAC_CATALOG_IPNS if none provided.""" + # Mock fetch_ipns to return a minimal valid catalog + mock_fetch_ipns.return_value = {"type": "Catalog", "id": "root", "links": []} + list_datasets() # Call without root_catalog_ipns argument + # Assert fetch_json_from_ipns was called with the default IPNS name + mock_fetch_ipns.assert_called_once_with( + DCLIMATE_STAC_CATALOG_IPNS, gateway_uri_stem=None + ) + + +# --- Tests for Legacy/Utility Functions (get_ipns_name_hash, update_cache_if_changed covered) --- +# --- NEW: Tests for other Legacy/Utility Functions (Mocked) --- + + +def test_get_single_metadata_success(mock_requests_get: MagicMock): + """Test _get_single_metadata successfully fetches and parses JSON.""" + ipfs_hash = "QmSomeHash" + expected_metadata = {"prop": "value", "links": []} + mock_response = MagicMock(spec=requests.Response) + mock_response.raise_for_status.return_value = None + mock_response.json.return_value = expected_metadata + mock_requests_get.return_value = mock_response + + metadata = _get_single_metadata(ipfs_hash) + + assert metadata == expected_metadata + expected_url_pattern = rf".*/ipfs/{ipfs_hash}" + call_args, call_kwargs = mock_requests_get.call_args + assert re.match(expected_url_pattern, call_args[0]), ( + f"URL {call_args[0]} does not match pattern {expected_url_pattern}" + ) + mock_response.raise_for_status.assert_called_once() + mock_response.json.assert_called_once() + + +def test_get_single_metadata_http_error(mock_requests_get: MagicMock): + """Test _get_single_metadata raises HTTPError.""" + ipfs_hash = "QmSomeHash" + mock_response = MagicMock(spec=requests.Response) + http_error = requests.exceptions.HTTPError("404 Not Found") + mock_response.raise_for_status.side_effect = http_error + mock_requests_get.return_value = mock_response + + with pytest.raises(requests.exceptions.HTTPError): + _get_single_metadata(ipfs_hash) # --- Tests for get_ipns_name_hash (Legacy/Utility Function) --- @@ -245,21 +1288,6 @@ def test_list_datasets_functional(): # assert "Failed to retrieve dataset list" in str(exc.value) -# def test_list_datasets_local_cache_empty(): -# """ -# Test that if the endpoint fails AND local cache file is empty, -# list_datasets() raises RuntimeError (no data to parse). -# """ -# with patch("requests.get") as mock_requests_get: -# mock_requests_get.side_effect = requests.RequestException("Simulated error") - -# with patch("os.path.exists", return_value=True): -# with patch("builtins.open", mock_open(read_data="")): -# with pytest.raises(RuntimeError) as exc: -# list_datasets() -# assert "Failed to retrieve dataset list" in str(exc.value) - - # def test_geo_temporal_query(): # ds_bytes = geo_temporal_query( # "cpc-precip-conus", @@ -305,21 +1333,28 @@ def test_list_datasets_functional(): # These remain unit tests using mocks for file I/O. -def get_cache_path(): - # Helper to get the expected cache file path within the package - # Need to ensure this reflects the actual location used by the code - package_dir = os.path.dirname(ipfs_retrieval.__file__) - return os.path.join(package_dir, "cids.json") +# --- Helper Functions for Mocking --- # + + +def mock_exists_true(*args, **kwargs) -> bool: + return True + + +def mock_exists_false(*args, **kwargs) -> bool: + return False # Use monkeypatch fixture for modifying builtins like open -def test_update_cache_no_update(monkeypatch): +def test_update_cache_no_update(monkeypatch: MonkeyPatch): cached_data = {"dataset": "hash1"} new_data = {"dataset": "hash1"} file_path = get_cache_path() + # Mock os.path.exists needed if called before open + monkeypatch.setattr("os.path.exists", mock_exists_true) + m = mock_open(read_data=json.dumps(cached_data)) - monkeypatch.setattr("builtins.open", m) + monkeypatch.setattr("builtins.open", m) # Correct: Patch built-in open update_cache_if_changed(new_data) @@ -327,13 +1362,17 @@ def test_update_cache_no_update(monkeypatch): m.assert_called_once_with(file_path, "r") -def test_update_cache_update(monkeypatch): +def test_update_cache_update(monkeypatch: MonkeyPatch): cached_data = {"dataset": "hash1"} new_data = {"dataset": "hash2"} file_path = get_cache_path() + # Mock os.path.exists needed if called before open + monkeypatch.setattr("os.path.exists", mock_exists_true) + m = mock_open(read_data=json.dumps(cached_data)) - monkeypatch.setattr("builtins.open", m) + # Patch open in the correct namespace where it's called + monkeypatch.setattr("builtins.open", m) # Correct: Patch built-in open update_cache_if_changed(new_data) @@ -342,21 +1381,28 @@ def test_update_cache_update(monkeypatch): calls = m.call_args_list assert calls[0].args == (file_path, "r") assert calls[1].args == (file_path, "w") - # Remove the assertion checking the specific write content - # handle = m() # Remove this - # handle.write.assert_called_once_with(json.dumps(new_data)) # Remove this + # Check the content written (optional, but good practice) + # Ensure the mock handle captures the write + # Note: mock_open's write checking can be tricky. + # A simpler check is often sufficient unless exact content is critical. + # handle = m() + # handle.write.assert_called_once_with(json.dumps(new_data)) -def test_update_cache_file_not_found(monkeypatch): +def test_update_cache_file_not_found(monkeypatch: MonkeyPatch): new_data = {"dataset": "hash2"} file_path = get_cache_path() + # No need to mock os.path.exists as the function doesn't use it + # Mock 'open' to raise FileNotFoundError on first call (read), succeed on second (write) - # Create a mock handle instance for the successful write call return value mock_write_handle = mock_open().return_value m = mock_open() - m.side_effect = [FileNotFoundError, mock_write_handle] # Read fails, Write succeeds - monkeypatch.setattr("builtins.open", m) + # First call (open "r") raises FileNotFoundError + # Second call (open "w") returns the mock handle + m.side_effect = [FileNotFoundError, mock_write_handle] + # Patch open in the correct namespace + monkeypatch.setattr("builtins.open", m) # Correct: Patch built-in open update_cache_if_changed(new_data) @@ -365,16 +1411,19 @@ def test_update_cache_file_not_found(monkeypatch): calls = m.call_args_list assert calls[0].args == (file_path, "r") assert calls[1].args == (file_path, "w") - # Remove the lines trying to get handle and assert write - # handle = m() # Remove this - # handle.write.assert_called_once_with(json.dumps(new_data)) # Remove this + # Optionally check write content if needed: + # handle = calls[1]._extract_mock_return_value() # Get the handle returned by the 2nd call + # handle.write.assert_called_once_with(json.dumps(new_data)) -def test_update_cache_decode_error(monkeypatch): +def test_update_cache_decode_error(monkeypatch: MonkeyPatch): """Test when the existing cache file has invalid JSON.""" new_data = {"dataset": "hash2"} file_path = get_cache_path() + # Mock os.path.exists needed if called before open + monkeypatch.setattr("os.path.exists", mock_exists_true) + # Create separate mock handles for read and write attempts # The read handle will simulate having invalid data read_handle = mock_open(read_data="invalid json").return_value @@ -383,16 +1432,18 @@ def test_update_cache_decode_error(monkeypatch): m = mock_open() # Define side effect: return read_handle on first call, write_handle on second m.side_effect = [read_handle, write_handle] + # Patch open in the correct namespace + monkeypatch.setattr("builtins.open", m) # Correct: Patch built-in open # Mock json.load to raise error when the read_handle is passed to it # Patch it in the correct namespace where it's used mock_json_load = patch( - "dclimate_zarr_client.ipfs_retrieval.json.load", + "json.load", # Patching built-in json directly side_effect=json.JSONDecodeError("err", "doc", 0), - ).start() - - monkeypatch.setattr("builtins.open", m) + ).start() # No need for .start()/.stop() if using 'with patch(...)' + # Use 'with patch' for cleaner setup/teardown + # with patch("json.load", side_effect=json.JSONDecodeError("err", "doc", 0)): update_cache_if_changed(new_data) # Assert open was called twice: read attempt (failed decode), write attempt @@ -401,5 +1452,59 @@ def test_update_cache_decode_error(monkeypatch): assert calls[0].args == (file_path, "r") # Read attempt assert calls[1].args == (file_path, "w") # Write attempt - # Clean up the patch for json.load + # Clean up the patch for json.load if using start/stop mock_json_load.stop() + + +# --- Test Legacy get_ipns_name_hash Errors --- +# (Ensure these cover JSONDecodeError during fallback read) + + +def test_get_ipns_name_hash_local_cache_malformed_json_during_fallback(): + """Test DatasetNotFoundError when local cache read fails during fallback""" + with patch( + "requests.get", side_effect=requests.RequestException("Simulated error") + ): + with patch("os.path.exists", return_value=True): + # Mock update_cache_if_changed to avoid file writes during test setup if called early + with patch("dclimate_zarr_client.ipfs_retrieval.update_cache_if_changed"): + # Mock open specifically within the fallback block + with patch( + "dclimate_zarr_client.ipfs_retrieval.open", + mock_open(read_data="INVALID JSON!!"), + create=True, # Allow create=True if needed by mock_open internals + ) as mock_file_open: + # Mock json.load raising the error when called by get_ipns_name_hash + with patch( + "json.load", side_effect=json.JSONDecodeError("err", "doc", 0) + ): + with pytest.raises( + DatasetNotFoundError, match="Invalid dataset name" + ): + get_ipns_name_hash("cpc-precip-conus") + # Assert the mocked open was called for reading the cache + mock_file_open.assert_called_once_with(get_cache_path(), "r") + + +def test_get_ipns_name_hash_local_cache_empty_during_fallback(): + """Test DatasetNotFoundError when local cache is empty during fallback""" + with patch( + "requests.get", side_effect=requests.RequestException("Simulated error") + ): + with patch("os.path.exists", return_value=True): + with patch("dclimate_zarr_client.ipfs_retrieval.update_cache_if_changed"): + with patch( + "dclimate_zarr_client.ipfs_retrieval.open", + mock_open(read_data=""), # Empty file + create=True, + ) as mock_file_open: + # json.load will raise JSONDecodeError on empty string + with patch( + "json.load", + side_effect=json.JSONDecodeError("Expecting value", "", 0), + ): + with pytest.raises( + DatasetNotFoundError, match="Invalid dataset name" + ): + get_ipns_name_hash("cpc-precip-conus") + mock_file_open.assert_called_once_with(get_cache_path(), "r") diff --git a/uv.lock b/uv.lock index 143f13a..0940a53 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,11 @@ version = 1 revision = 1 requires-python = ">=3.12" +resolution-markers = [ + "implementation_name == 'cpython'", + "implementation_name == 'pypy'", + "implementation_name != 'cpython' and implementation_name != 'pypy'", +] [[package]] name = "affine" @@ -108,6 +113,82 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, ] +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "anyio" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, +] + +[[package]] +name = "ape-base" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ape-optimism" }, + { name = "eth-ape" }, + { name = "ethpm-types" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/64/4b3e35c494e7e7c3c1516e4cf98d2f8d5e2791881bbe2a8ce3d3ee0b2ace/ape-base-0.8.1.tar.gz", hash = "sha256:cbcc505743c62b097348c1e0a24e12e55d3a829a4a691e3c925a19f980a72465", size = 17711 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/5a/2132104828bd2761951322de2de56ae4ba7ad54fd92f76d82c0d0f837e1a/ape_base-0.8.1-py3-none-any.whl", hash = "sha256:9c3cc4a9644949f860581cf62dcf0100e30c5943089e857daf46f40d64023a23", size = 9395 }, +] + +[[package]] +name = "ape-optimism" +version = "0.8.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "eth-ape" }, + { name = "ethpm-types" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/ff/6c0e186cde8c565343bb6a376ccec25ff111780c461640d98d9328aa6d15/ape_optimism-0.8.3.tar.gz", hash = "sha256:eb48e34923759761a9c27b9d314c61530dbea1c4627be48fc4ca8fed623d7f72", size = 17641 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/1a/8c9444e555756dd13879c5d8eba8b085569dd49bd17aec6539cd4b3e2987/ape_optimism-0.8.3-py3-none-any.whl", hash = "sha256:4d263764922096debbd061eae8e5a3bb520573923958c1512fe518b894d8ac5d", size = 9068 }, +] + +[[package]] +name = "ape-solidity" +version = "0.8.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "eth-ape" }, + { name = "eth-pydantic-types" }, + { name = "ethpm-types" }, + { name = "packaging" }, + { name = "py-solc-x" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4a/9b/41d50b7ec9a06f58f299bbbc003b6d59e16d19050534c216151b7e65c2c8/ape-solidity-0.8.5.tar.gz", hash = "sha256:8200ca4d0d1df0608f14418a16cd98a730d65c3073450ad76e458bfda64e1535", size = 418307 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/2e/d07b6dba5a86bfd4f2cced0f5ada112a55dbb886552187ddc86b94aef7d4/ape_solidity-0.8.5-py3-none-any.whl", hash = "sha256:75094fd996ad0914cb2425598e0ec9329adc881050d55b4224d74f75dd994794", size = 29047 }, +] + +[[package]] +name = "asttokens" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, +] + [[package]] name = "attrs" version = "25.3.0" @@ -117,6 +198,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 }, ] +[[package]] +name = "base58" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/59/610af520e9f4d1b28c975bece4768d700884e38929e7ba0dcb25c9f6f87c/base58-1.0.3.tar.gz", hash = "sha256:9a793c599979c497800eb414c852b80866f28daaed5494703fc129592cc83e60", size = 3865 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/09/b2/21ac9591f055acc145afead895edeb73bbd69d95cf366fc5c2233f2434cb/base58-1.0.3-py3-none-any.whl", hash = "sha256:6aa0553e477478993588303c54659d15e3c17ae062508c854a8b752d07c716bd", size = 3072 }, +] + [[package]] name = "bases" version = "0.3.0" @@ -130,6 +220,42 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b4/15/7bcf28a3f971e1b0523fab46ae3ca935a589249544187558e5a8e70af393/bases-0.3.0-py3-none-any.whl", hash = "sha256:a2fef3366f3e522ff473d2e95c21523fe8e44251038d5c6150c01481585ebf5b", size = 36053 }, ] +[[package]] +name = "bitarray" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/0f/6ecf00ec04622b8309aca3cfbdba20d5399d9e4e5a4b156d9ffd2e5610d3/bitarray-3.5.0.tar.gz", hash = "sha256:e10ae216416c36500c86c08ffceaf7589f6ad54056a7007845cdd907813e7d25", size = 148024 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/47/3b2fd638a96da2e42a53647d694b3ec6faced228cae608889e685f03351b/bitarray-3.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cc918ff7beaa389055f38d7bb096b00b95434d80a6f4784624dbca4fb919464c", size = 144424 }, + { url = "https://files.pythonhosted.org/packages/98/13/e4f2211629cad18655a677c1b0135d8e82c0ab6c851d496227f6ea8cd35d/bitarray-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5964f319ef78cf69f24eebb9061e06462eed1a9ca62bdbbb6dcad611691f271d", size = 141304 }, + { url = "https://files.pythonhosted.org/packages/d4/79/e5625aabf5ca1f79b44d6b5c5f4c7fd611f3d574afe241edacd2b31a3531/bitarray-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0099251d50ddcab672d6ddf415eccf1b1117f0ab54e9321f905685d9ff910a6", size = 321007 }, + { url = "https://files.pythonhosted.org/packages/5c/63/dde5698a9f3a3bb794f48f16f8d23ff42f26ea99a2fa7a7632dc3ea275e7/bitarray-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:923fa335dcc3ecbb516f12b2b70f5991773ffe05484f8c8d9d31bd419c311e90", size = 336578 }, + { url = "https://files.pythonhosted.org/packages/ca/8c/7e0dd6dee3e4311224ddf95601cd60c0378db3d591cdc135b70abc7f3999/bitarray-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:352a6f7a5501ab4f74fda2275258fb7607b740ca74f5768d10774efca4215d4e", size = 330179 }, + { url = "https://files.pythonhosted.org/packages/bb/da/8b43649052792e9786b4cbf895191d7ee9d907a5bc802059ff3eeb57e2af/bitarray-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:403b2045f43d70b184f559bfc39cb98fef02ee03aafd1cbe6d77db345a8c8275", size = 323080 }, + { url = "https://files.pythonhosted.org/packages/fa/a9/c647eb2dd958550173c01f4859a8406b41ac8caa37a00d2be48711a31828/bitarray-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25946a0faf22f16619a1eae82ae28c0e5e09815e7d5629c507df7b4e3bc7807a", size = 310444 }, + { url = "https://files.pythonhosted.org/packages/c0/46/115dff1e6c21461a0b1681fb4ec666d50bdda5dce8c6c0e1b384e753b145/bitarray-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:18573b66d9c08b692e21abff8b77f32716ec77ab8aff60916590107d725bf648", size = 315128 }, + { url = "https://files.pythonhosted.org/packages/70/78/33e485dbe8e499677271b9b7d244b3cb57d57b0396672682a071a2b22cde/bitarray-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:39d8b00e5031d0ab8101c2e6349b04cd753d2426b963279858ed1d5b0d454cd8", size = 306860 }, + { url = "https://files.pythonhosted.org/packages/ce/11/50234d6d1c057aee92a0aa7849a7276352db3772267818ac066aee24fefa/bitarray-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e75f9150b59a4f5d205bd55e5ddb741e295045f17ebb1d4599e7e68563e98955", size = 331374 }, + { url = "https://files.pythonhosted.org/packages/12/38/2bc7a39182fbfd7fbe16b7f400034984af29e9dc640f56a349529fe13d27/bitarray-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:67343d181a4181ee608afe3a284b21d8be0ed11bdfbf9dd0779d57403556fedf", size = 335026 }, + { url = "https://files.pythonhosted.org/packages/f5/d3/b4c16d161d7b8bbf019532faf933ad7306ecbf1ae24c10d1a0ec75bb0906/bitarray-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:333c707bbc58028750e56f2da253838a71ec47c6dd6f8302c461381258e266f7", size = 314989 }, + { url = "https://files.pythonhosted.org/packages/5e/01/94be9b1fbe81143a8d842cbfa8b14fdca12b96c848239b71b944059aa310/bitarray-3.5.0-cp312-cp312-win32.whl", hash = "sha256:891817b3006f56e542f889d844c4c5c4715ea7b6e0e5825fc51dd1f7de76c14c", size = 138373 }, + { url = "https://files.pythonhosted.org/packages/9c/20/58a93d971e9d89a94c65d83a740a161370492ed51139ab42260d93de85ab/bitarray-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:86f043ed3de669eaa1ace14bc0c3b2c1b21f53728e48f00699117f94c8c0e02b", size = 145176 }, + { url = "https://files.pythonhosted.org/packages/26/02/a3946dcf6176363df87f2e0a7e4f001ac25e54fef0218ae5660d763e1ded/bitarray-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9bf8439bc80fd7a075c180a27d7b951e2ee133be6c1c102443de6e0237390284", size = 144406 }, + { url = "https://files.pythonhosted.org/packages/ec/21/b14f7ca5726ab569c3c61f3661de7bea3719d12a2988c2c653ca0fa19f81/bitarray-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ef64b8a8715b8d2393ab94b5e9c0463e43eba0dbe1e01dde93bbadb73c39cc3", size = 141298 }, + { url = "https://files.pythonhosted.org/packages/58/98/79eced6eddfc71508b021d20d385413938347d5ff805eddb15ae4c4f47e7/bitarray-3.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca3558001e7228ffd917a4db91992e0955d02c6ae878e9788460ee7499f1665", size = 320951 }, + { url = "https://files.pythonhosted.org/packages/a2/07/4b696f7c2b01706ced6d1f06f8c68b12cb1822b78e8dc85909397b3d8a2e/bitarray-3.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a1cd11d51721bc2402d8bd5b0d17b855cecb63366f711cf016546da84bb4548", size = 336462 }, + { url = "https://files.pythonhosted.org/packages/79/7d/7898927a7f4eaa9408c65dbecd4dab6e5d9273e86b02cf74f86113f97363/bitarray-3.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a23cc993ff727e7e29058ca8c663613ffc3725e02e0eba6c0650bb538a1b2e7", size = 330024 }, + { url = "https://files.pythonhosted.org/packages/f0/db/b49e7aa669bfcb176099b296b4dafdc7783633e482fed4b6dc0f4f19a1aa/bitarray-3.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f47e2f676a3cdc8c21771d3a7e736af5c8385122a8de4f0c11a965ac0905888", size = 322969 }, + { url = "https://files.pythonhosted.org/packages/25/b8/5c1110467b142809c80db5eb9234c1592ae24a0feb4ed47e8590da841866/bitarray-3.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:287ee0717df4d70a7260495006c7dfbbeb42ba9e067224359f923e2ce76f1f0e", size = 310293 }, + { url = "https://files.pythonhosted.org/packages/8c/74/9750132b2ca7d6d3a213ce144281f1bee7b1bfba2e8693f1b219d6c0ca16/bitarray-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7f45aee9532f0f4f3b0dc7058eb1730661c0a83d29a2cc00c6fbb80c3080e85b", size = 315140 }, + { url = "https://files.pythonhosted.org/packages/84/05/69616222f43b11f4ae4d88408f30b551de96c6d4d4fe98b70774004aee2c/bitarray-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a4a8dc574ad6928dc327bffbd32d3bab8cdc21e90b9c773a7e40f8c2627f572e", size = 306878 }, + { url = "https://files.pythonhosted.org/packages/b2/6e/39bf609510ae3b30ba42f3f26b906da155ed4abda4d48bf9418e0791df8b/bitarray-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:02243c8db4a43c2f1ac66396073ac68d1e41863eef248a1f5fdf859c0b3edc33", size = 331380 }, + { url = "https://files.pythonhosted.org/packages/5f/b6/f636072cc9d63598088a2869dd2011078b89b4c4293865c4482cb075b93d/bitarray-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:8fdd640b86637d9f20e620cce4efad36e1b147feb978153222195a0703a5c013", size = 334983 }, + { url = "https://files.pythonhosted.org/packages/6a/c1/a800f2d0149ca944fb18827c328803bbc9ed8bb84a80489d2ca7dbe0062a/bitarray-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:57312af2775edf4842f2561e996341394bc9650330bd352f38b6144238f95ec3", size = 315012 }, + { url = "https://files.pythonhosted.org/packages/38/9a/814c9ea5b3c9ab300a06f19a7e3f1a74424f1d46659d56b7409e989adb1a/bitarray-3.5.0-cp313-cp313-win32.whl", hash = "sha256:190eb9ce58f6ca01ba959800019d1a9ba30b2996409df938667c718a73d5c794", size = 138380 }, + { url = "https://files.pythonhosted.org/packages/3e/92/53e801988a392f4c1bf6c0dcb6d6ccb247b7988e195e30566e775fe4d44a/bitarray-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:15cd52d6dfe4374e31fca6b2d0b50f7ed6a98c316c2befdd2f8be80bb703061c", size = 145240 }, +] + [[package]] name = "blake3" version = "1.0.4" @@ -174,6 +300,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/86/f27f21a589e7d45a578ec8c64dd6e7a0c6bc5472f72c8897b41166952438/blake3-1.0.4-cp313-cp313t-win_amd64.whl", hash = "sha256:95b2223177be6e269ab5f39bf1f2c186dc4852d546f15500bb7dcc114cf681f0", size = 211884 }, ] +[[package]] +name = "bokeh" +version = "3.7.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "jinja2" }, + { name = "narwhals" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "pyyaml" }, + { name = "tornado", marker = "sys_platform != 'emscripten'" }, + { name = "xyzservices" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/75/18/12d0d6024177ad18ba65deffc363046d0cbafe116f8b964a9efa85d2800f/bokeh-3.7.3.tar.gz", hash = "sha256:70a89a9f797b103d5ee6ad15fb7944adda115cf0da996ed0b75cfba61cb12f2b", size = 6366610 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/48/08b2382e739236aa3360b7976360ba3e0c043b6234e25951c18c1eb6fa06/bokeh-3.7.3-py3-none-any.whl", hash = "sha256:b0e79dd737f088865212e4fdcb0f3b95d087f0f088bf8ca186a300ab1641e2c7", size = 7031447 }, +] + [[package]] name = "botocore" version = "1.37.1" @@ -188,6 +335,71 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3d/20/352b2bf99f93ba18986615841786cbd0d38f7856bd49d4e154a540f04afe/botocore-1.37.1-py3-none-any.whl", hash = "sha256:c1db1bfc5d8c6b3b6d1ca6794f605294b4264e82a7e727b88e0fef9c2b9fbb9c", size = 13359164 }, ] +[[package]] +name = "bottleneck" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/80/82/dd20e69b97b9072ed2d26cc95c0a573461986bf62f7fde7ac59143490918/bottleneck-1.5.0.tar.gz", hash = "sha256:c860242cf20e69d5aab2ec3c5d6c8c2a15f19e4b25b28b8fca2c2a12cefae9d8", size = 104177 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/64/127e174cec548ab98bc0fa868b4f5d3ae5276e25c856d31d235d83d885a8/bottleneck-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dbb0f0d38feda63050aa253cf9435e81a0ecfac954b0df84896636be9eabd9b6", size = 99640 }, + { url = "https://files.pythonhosted.org/packages/59/89/6e0b6463a36fd4771a9227d22ea904f892b80d95154399dd3e89fb6001f8/bottleneck-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:613165ce39bf6bd80f5307da0f05842ba534b213a89526f1eba82ea0099592fc", size = 358009 }, + { url = "https://files.pythonhosted.org/packages/f7/d6/7d1795a4a9e6383d3710a94c44010c7f2a8ba58cb5f2d9e2834a1c179afe/bottleneck-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f218e4dae6511180dcc4f06d8300e0c81e7f3df382091f464c5a919d289fab8e", size = 362875 }, + { url = "https://files.pythonhosted.org/packages/2b/1b/bab35ef291b9379a97e2fb986ce75f32eda38a47fc4954177b43590ee85e/bottleneck-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3886799cceb271eb67d057f6ecb13fb4582bda17a3b13b4fa0334638c59637c6", size = 361194 }, + { url = "https://files.pythonhosted.org/packages/d5/f3/a416fed726b81d2093578bc2112077f011c9f57b31e7ff3a1a9b00cce3d3/bottleneck-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dc8d553d4bf033d3e025cd32d4c034d2daf10709e31ced3909811d1c843e451c", size = 373253 }, + { url = "https://files.pythonhosted.org/packages/0a/40/c372f9e59b3ce340d170fbdc24c12df3d2b3c22c4809b149b7129044180b/bottleneck-1.5.0-cp312-cp312-win32.whl", hash = "sha256:0dca825048a3076f34c4a35409e3277b31ceeb3cbb117bbe2a13ff5c214bcabc", size = 107915 }, + { url = "https://files.pythonhosted.org/packages/28/5a/57571a3cd4e356bbd636bb2225fbe916f29adc2235ba3dc77cd4085c91c8/bottleneck-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f26005740e6ef6013eba8a48241606a963e862a601671eab064b7835cd12ef3d", size = 112148 }, + { url = "https://files.pythonhosted.org/packages/e9/8c/1e1fb2d30a906bc009f3fe070fa188a9593d73e5aa5e815e55edbb96eeec/bottleneck-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97285cfedf3545d9a010b2db2123f9750bf920081e29364cc465052973bd0b5a", size = 99653 }, + { url = "https://files.pythonhosted.org/packages/56/8c/876c41988a9806020dc9b150f7ad5764a3d14857c11b637285eb7431c25f/bottleneck-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1043d95674566063f638582cc8700c24c4427f532f86b9e7cfc9f9ec84abc1ff", size = 357965 }, + { url = "https://files.pythonhosted.org/packages/07/ed/2d807648157a3e94405dc7759e434cbb10784003cd8fa34d992dee02e519/bottleneck-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abc6a24a41f55765215005cec97dd69f41ac747ed0f4d446caa508531957eeda", size = 362845 }, + { url = "https://files.pythonhosted.org/packages/f9/7a/ef9c3d9578c4a627d71d107f5e6f7074b98047536edd8adf0e17be5a0c5b/bottleneck-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7967e0189defe9f49025bd6469ff0fe22af5463926af55c7ba1e4592051d8ef8", size = 361425 }, + { url = "https://files.pythonhosted.org/packages/88/e0/131f6a6adf8b2e2521e3880a29c5ec0e8ebf57b9e1f37fc2337df5c041b8/bottleneck-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:27e38e829497ca0a5eebdb79d3293aaa424f3c31c13806e5c607fd414536b7c3", size = 373411 }, + { url = "https://files.pythonhosted.org/packages/13/e4/4c24ec1347c4fa492f757b126be877201ae90bddbcdc9da17cce09de0d55/bottleneck-1.5.0-cp313-cp313-win32.whl", hash = "sha256:1214a2bf3b36c66e3898aab821ad8366a3062db6f83a8f083e2f799d202e86ea", size = 107925 }, + { url = "https://files.pythonhosted.org/packages/8b/44/5e4a73bbe79e16ddbbbdfa1bc6af49b03cbb07aec5e323a14fa3acfc58bd/bottleneck-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:436a402f0d60a9d6541d7adb0929501225a151ad03b96b756e0b607db6a106f1", size = 112158 }, + { url = "https://files.pythonhosted.org/packages/25/b2/f3ceb3c177dc9bddfb5e71e0e5949af13c5bfee283740ec81b429a4ce19a/bottleneck-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c0b661005b059fcb09988f8b5e2cd5e9c702e1bed24819ed38f85145140b5", size = 100852 }, + { url = "https://files.pythonhosted.org/packages/3e/e7/4f5e212b482529847877558ae770be8eedd985b07d3eb0209dcbdd082595/bottleneck-1.5.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48c2657102f3288e178cc341f000475a32f49a3cd8b7067e091d5446fa899383", size = 372068 }, + { url = "https://files.pythonhosted.org/packages/a8/fa/7760ecdb2b37b7097e28a2028a1d5cad6ca0b7eae3806ed795c3a12fd87d/bottleneck-1.5.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c4c94cfcba46adfe71894c63c4b186c847965e73727dbaf5fd9ade41ef38e6e", size = 376524 }, + { url = "https://files.pythonhosted.org/packages/24/38/ee90a6e39ed04a8256ecd36baaf1abcea72787214e822675bfdd9b9e896e/bottleneck-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f3e308416886e29441a0b71bce8f3eb4c7a4943be541fd918244aaf25534d36", size = 375452 }, + { url = "https://files.pythonhosted.org/packages/3f/96/82837f96be9abd2651565b9becc2f393eefe6d5a515606d662c6962df4b0/bottleneck-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bda7c475d4a7e271dbd0b1d4bbce29065edc8891361857105b7212fe383c9a36", size = 386361 }, + { url = "https://files.pythonhosted.org/packages/73/40/ddf00a9b0065d037792d6d2095a4998f15787f2c60fbdcf638767a75c37f/bottleneck-1.5.0-cp313-cp313t-win32.whl", hash = "sha256:a107ed8b5f998918c24a1e476dbd2dfc3514ab0082df7132c460b01e6ffd8cf4", size = 109355 }, + { url = "https://files.pythonhosted.org/packages/33/56/c05fd1459f2b65941fcaf2697b81fe7d3428855c8e66ab1951eed04a13e2/bottleneck-1.5.0-cp313-cp313t-win_amd64.whl", hash = "sha256:816c910c5d1fb53adb32581c52a513b206f503ae253ace70cb32d1fe4e45af1d", size = 113739 }, +] + +[[package]] +name = "cached-property" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/4b/3d870836119dbe9a5e3c9a61af8cc1a8b69d75aea564572e385882d5aefb/cached_property-2.0.1.tar.gz", hash = "sha256:484d617105e3ee0e4f1f58725e72a8ef9e93deee462222dbd51cd91230897641", size = 10574 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/0e/7d8225aab3bc1a0f5811f8e1b557aa034ac04bdf641925b30d3caf586b28/cached_property-2.0.1-py3-none-any.whl", hash = "sha256:f617d70ab1100b7bcf6e42228f9ddcb78c676ffa167278d9f730d1c2fba69ccb", size = 7428 }, +] + +[[package]] +name = "cartopy" +version = "0.24.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyproj" }, + { name = "pyshp" }, + { name = "shapely" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/75/94aff4fef338887641aa780d13795609861e6e9f9593bd66d4917ab7954b/cartopy-0.24.1.tar.gz", hash = "sha256:01c910d5634c69a7efdec46e0a17d473d2328767f001d4dc0b5c4b48e585c8bd", size = 10741277 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/76/774a4f808c6a4fc19b87c2cc38dd8731d413aad606689451c017ff93ad12/Cartopy-0.24.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a984e33977daed8f760c09c331c8368a6af060db1190af89d74a027c272e39c3", size = 10983939 }, + { url = "https://files.pythonhosted.org/packages/2f/48/8517d5d1cc56ce5c4abda1de6454593474a23412115a543f7981aa7e4377/Cartopy-0.24.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71d8a6d061d0764aba3baf357a68f3d73796a8a46d34b8c9fb241171b273c69e", size = 10972374 }, + { url = "https://files.pythonhosted.org/packages/c8/84/cb1577d5ac2f0deb002001c6e25b291735151c8c3033c97f212dc482ef72/Cartopy-0.24.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f354a1d902a8d6ee33b099acc86ac2e1af528bbc0ea718b834111c97e604981", size = 11715215 }, + { url = "https://files.pythonhosted.org/packages/11/95/40c7abae8789aae22ad2a5da3974d3270dc3526b46cee253f680f72ee6cc/Cartopy-0.24.1-cp312-cp312-win_amd64.whl", hash = "sha256:b1bb2d02b31884ee1d4f14e5b436bbf95745eac39c6fc0d6c67c83bb907b55b3", size = 10959875 }, + { url = "https://files.pythonhosted.org/packages/e6/e8/38e00eb35743f22d4ee9bcb131ab273fb47ec39cc03ce5144686a3142756/Cartopy-0.24.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d279968b845f72e3423e454b2b0b985fb2389e6ccd18fb73324abeca4e43f516", size = 10982533 }, + { url = "https://files.pythonhosted.org/packages/66/60/cc852a0835a053db18085dec1d1dd6f9cedc764d1524bfe30fd8be5ee3a7/Cartopy-0.24.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f0963b80a048252815c56fbd21bc4e5d163618a9eaa36c8898ce2c60b6c03979", size = 10971183 }, + { url = "https://files.pythonhosted.org/packages/a3/5b/476c8f3a277d7c78e5a5318bd32f234b994bfdc5d7731ae84218f2fa8a8f/Cartopy-0.24.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfdde0a6e0e56c5fc46f4e7d332237eb31bbd9908417f0f190fda5d322754184", size = 11709060 }, + { url = "https://files.pythonhosted.org/packages/93/31/50bf07ba820c5aa5d4e674d72cdb5da90bbd012ba1b9c6c95c3f96afe233/Cartopy-0.24.1-cp313-cp313-win_amd64.whl", hash = "sha256:b17cf23dd74d0a922c2a5682dacef3c0bf89fa8c0bd0eae96b87fb684f966b15", size = 10959830 }, +] + [[package]] name = "certifi" version = "2025.1.31" @@ -206,6 +418,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, ] +[[package]] +name = "cftime" +version = "1.6.4.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/c8/1155d1d58003105307c7e5985f422ae5bcb2ca0cbc553cc828f3c5a934a7/cftime-1.6.4.post1.tar.gz", hash = "sha256:50ac76cc9f10ab7bd46e44a71c51a6927051b499b4407df4f29ab13d741b942f", size = 54631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/81/0bb28d54088a61592f61a11e7fcabcea6d261c47af79e18d0f9cbcd940ae/cftime-1.6.4.post1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a590f73506f4704ba5e154ef55bfbaed5e1b4ac170f3caeb8c58e4f2c619ee4e", size = 226615 }, + { url = "https://files.pythonhosted.org/packages/f3/1e/38dbbf8a828dfb5e0e6e5c912818b77aacf2e7bcb97b262ac6126beeb29f/cftime-1.6.4.post1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:933cb10e1af4e362e77f513e3eb92b34a688729ddbf938bbdfa5ac20a7f44ba0", size = 209193 }, + { url = "https://files.pythonhosted.org/packages/9b/60/0db884c76311ecaaf31f628aa9358beae5fcb0fbbdc2eb0b790a93aa258f/cftime-1.6.4.post1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf17a1b36f62e9e73c4c9363dd811e1bbf1170f5ac26d343fb26012ccf482908", size = 1320215 }, + { url = "https://files.pythonhosted.org/packages/8d/7d/2d5fc7af06da4f3bdea59a204f741bf7a30bc5019355991b2f083e557e4e/cftime-1.6.4.post1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e18021f421aa26527bad8688c1acf0c85fa72730beb6efce969c316743294f2", size = 1367426 }, + { url = "https://files.pythonhosted.org/packages/5d/ab/e8b26d05323fc5629356c82a7f64026248f121ea1361b49df441bbc8f2d7/cftime-1.6.4.post1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5835b9d622f9304d1c23a35603a0f068739f428d902860f25e6e7e5a1b7cd8ea", size = 1385593 }, + { url = "https://files.pythonhosted.org/packages/af/7b/ca72a075a3f660315b031d62d39a3e9cfef71f7929da2621d5120077a75f/cftime-1.6.4.post1-cp312-cp312-win_amd64.whl", hash = "sha256:7f50bf0d1b664924aaee636eb2933746b942417d1f8b82ab6c1f6e8ba0da6885", size = 178918 }, + { url = "https://files.pythonhosted.org/packages/da/d8/81f086dbdc6f5a4e0bb068263471f1d12861b72562fe8c18df38268e4e29/cftime-1.6.4.post1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5c89766ebf088c097832ea618c24ed5075331f0b7bf8e9c2d4144aefbf2f1850", size = 223418 }, + { url = "https://files.pythonhosted.org/packages/4a/cc/60a825d92a4023655e330470758280a31e7b82665ef77d0e2a0fe71ea958/cftime-1.6.4.post1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f27113f7ccd1ca32881fdcb9a4bec806a5f54ae621fc1c374f1171f3ed98ef2", size = 207395 }, + { url = "https://files.pythonhosted.org/packages/ca/90/f5b26949899decce262fc76a1e64915b92050473114e0160cd6f7297f854/cftime-1.6.4.post1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da367b23eea7cf4df071c88e014a1600d6c5bbf22e3393a4af409903fa397e28", size = 1318113 }, + { url = "https://files.pythonhosted.org/packages/c3/f8/6f13d37abb7ade46e65a08acc31af776a96dde0eb569e05d4c4b01422ba6/cftime-1.6.4.post1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6579c5c83cdf09d73aa94c7bc34925edd93c5f2c7dd28e074f568f7e376271a0", size = 1366034 }, + { url = "https://files.pythonhosted.org/packages/fa/08/335cb17f3b708f9a24f96ca4abb00889c7aa20b0ae273313e7c11faf1f97/cftime-1.6.4.post1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6b731c7133d17b479ca0c3c46a7a04f96197f0a4d753f4c2284c3ff0447279b4", size = 1390156 }, + { url = "https://files.pythonhosted.org/packages/f3/2d/980323fb5ec1ef369604b61ba259a41d0336cc1a85b639ed7bd210bd1290/cftime-1.6.4.post1-cp313-cp313-win_amd64.whl", hash = "sha256:d2a8c223faea7f1248ab469cc0d7795dd46f2a423789038f439fee7190bae259", size = 178496 }, +] + [[package]] name = "charset-normalizer" version = "3.4.1" @@ -241,6 +476,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, ] +[[package]] +name = "ckzg" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/bf/ddd817e8b455b577b206fbfee951df1f4964826e9d4f2fc3148550d592c4/ckzg-1.0.2.tar.gz", hash = "sha256:4295acc380f8d42ebea4a4a0a68c424a322bb335a33bad05c72ead8cbb28d118", size = 840347 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/06/959cfafae47190d4f1930d8993653538c3de7bb1a3a32e917aa47ac9c8f0/ckzg-1.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e3cb2f8c767aee57e88944f90848e8689ce43993b9ff21589cfb97a562208fe7", size = 100499 }, + { url = "https://files.pythonhosted.org/packages/a6/27/f9b73f240bc2c4a7995a43f9b7850cd8e6931f396206f7e38d6df3e8d8d7/ckzg-1.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b29889f5bc5db530f766871c0ff4133e7270ecf63aaa3ca756d3b2731980802", size = 85778 }, + { url = "https://files.pythonhosted.org/packages/c6/79/cf1bb8d02703222b1177596a9de3e25c829db5a852a5824b5ea898396ed4/ckzg-1.0.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfcc70fb76b3d36125d646110d5001f2aa89c1c09ff5537a4550cdb7951f44d4", size = 146327 }, + { url = "https://files.pythonhosted.org/packages/ff/eb/a43b49ac53c581f7b8be88596c98db558c3059f8c8bcc339f4dac560dac2/ckzg-1.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ca8a256cdd56d06bc5ef24caac64845240dbabca402c5a1966d519b2514b4ec", size = 132109 }, + { url = "https://files.pythonhosted.org/packages/d5/6f/7051894626806a98c1c9d9608fa1ffaafc811f460e2490bcd90cc60b07a3/ckzg-1.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ea91b0236384f93ad1df01d530672f09e254bd8c3cf097ebf486aebb97f6c8c", size = 140481 }, + { url = "https://files.pythonhosted.org/packages/19/ec/bcf995869a47ef2ca645d16b2bf0052af4581079d7c622d30ba721d088c7/ckzg-1.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:65311e72780105f239d1d66512629a9f468b7c9f2609b8567fc68963ac638ef9", size = 139204 }, + { url = "https://files.pythonhosted.org/packages/61/2a/0a86fb062a6415b5ad73665051f3e5c891fe4250edd17037c9837b999c79/ckzg-1.0.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0d7600ce7a73ac41d348712d0c1fe5e4cb6caa329377064cfa3a6fd8fbffb410", size = 149095 }, + { url = "https://files.pythonhosted.org/packages/ef/88/bcb1f42b6fb6f3392025d12c434acdee7b1667b4b455851aaef72bb99dce/ckzg-1.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:19893ee7bd7da8688382cb134cb9ee7bce5c38e3a9386e3ed99bb010487d2d17", size = 144915 }, + { url = "https://files.pythonhosted.org/packages/61/44/0a53aec8ba1a8c0987b73f64262c4e500fbed97c2533630c77b63d32afb3/ckzg-1.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:c3e1a9a72695e777497e95bb2213316a1138f82d1bb5d67b9c029a522d24908e", size = 69856 }, +] + [[package]] name = "click" version = "8.1.8" @@ -277,6 +529,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/73/86/43fa9f15c5b9fb6e82620428827cd3c284aa933431405d1bcf5231ae3d3e/cligj-0.7.2-py3-none-any.whl", hash = "sha256:c1ca117dbce1fe20a5809dc96f01e1c2840f6dcc939b3ddbb1111bf330ba82df", size = 7069 }, ] +[[package]] +name = "cloudpickle" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/39/069100b84d7418bc358d81669d5748efb14b9cceacd2f9c75f550424132f/cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64", size = 22113 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/e8/64c37fadfc2816a7701fa8a6ed8d87327c7d54eacfbfb6edab14a2f2be75/cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e", size = 20992 }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -286,6 +547,47 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] +[[package]] +name = "contourpy" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580 }, + { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530 }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688 }, + { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331 }, + { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963 }, + { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681 }, + { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674 }, + { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480 }, + { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489 }, + { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042 }, + { url = "https://files.pythonhosted.org/packages/2e/61/5673f7e364b31e4e7ef6f61a4b5121c5f170f941895912f773d95270f3a2/contourpy-1.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de39db2604ae755316cb5967728f4bea92685884b1e767b7c24e983ef5f771cb", size = 271630 }, + { url = "https://files.pythonhosted.org/packages/ff/66/a40badddd1223822c95798c55292844b7e871e50f6bfd9f158cb25e0bd39/contourpy-1.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f9e896f447c5c8618f1edb2bafa9a4030f22a575ec418ad70611450720b5b08", size = 255670 }, + { url = "https://files.pythonhosted.org/packages/1e/c7/cf9fdee8200805c9bc3b148f49cb9482a4e3ea2719e772602a425c9b09f8/contourpy-1.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71e2bd4a1c4188f5c2b8d274da78faab884b59df20df63c34f74aa1813c4427c", size = 306694 }, + { url = "https://files.pythonhosted.org/packages/dd/e7/ccb9bec80e1ba121efbffad7f38021021cda5be87532ec16fd96533bb2e0/contourpy-1.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de425af81b6cea33101ae95ece1f696af39446db9682a0b56daaa48cfc29f38f", size = 345986 }, + { url = "https://files.pythonhosted.org/packages/dc/49/ca13bb2da90391fa4219fdb23b078d6065ada886658ac7818e5441448b78/contourpy-1.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:977e98a0e0480d3fe292246417239d2d45435904afd6d7332d8455981c408b85", size = 318060 }, + { url = "https://files.pythonhosted.org/packages/c8/65/5245ce8c548a8422236c13ffcdcdada6a2a812c361e9e0c70548bb40b661/contourpy-1.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:434f0adf84911c924519d2b08fc10491dd282b20bdd3fa8f60fd816ea0b48841", size = 322747 }, + { url = "https://files.pythonhosted.org/packages/72/30/669b8eb48e0a01c660ead3752a25b44fdb2e5ebc13a55782f639170772f9/contourpy-1.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c66c4906cdbc50e9cba65978823e6e00b45682eb09adbb78c9775b74eb222422", size = 1308895 }, + { url = "https://files.pythonhosted.org/packages/05/5a/b569f4250decee6e8d54498be7bdf29021a4c256e77fe8138c8319ef8eb3/contourpy-1.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8b7fc0cd78ba2f4695fd0a6ad81a19e7e3ab825c31b577f384aa9d7817dc3bef", size = 1379098 }, + { url = "https://files.pythonhosted.org/packages/19/ba/b227c3886d120e60e41b28740ac3617b2f2b971b9f601c835661194579f1/contourpy-1.3.2-cp313-cp313-win32.whl", hash = "sha256:15ce6ab60957ca74cff444fe66d9045c1fd3e92c8936894ebd1f3eef2fff075f", size = 178535 }, + { url = "https://files.pythonhosted.org/packages/12/6e/2fed56cd47ca739b43e892707ae9a13790a486a3173be063681ca67d2262/contourpy-1.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e1578f7eafce927b168752ed7e22646dad6cd9bca673c60bff55889fa236ebf9", size = 223096 }, + { url = "https://files.pythonhosted.org/packages/54/4c/e76fe2a03014a7c767d79ea35c86a747e9325537a8b7627e0e5b3ba266b4/contourpy-1.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0475b1f6604896bc7c53bb070e355e9321e1bc0d381735421a2d2068ec56531f", size = 285090 }, + { url = "https://files.pythonhosted.org/packages/7b/e2/5aba47debd55d668e00baf9651b721e7733975dc9fc27264a62b0dd26eb8/contourpy-1.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c85bb486e9be652314bb5b9e2e3b0d1b2e643d5eec4992c0fbe8ac71775da739", size = 268643 }, + { url = "https://files.pythonhosted.org/packages/a1/37/cd45f1f051fe6230f751cc5cdd2728bb3a203f5619510ef11e732109593c/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:745b57db7758f3ffc05a10254edd3182a2a83402a89c00957a8e8a22f5582823", size = 310443 }, + { url = "https://files.pythonhosted.org/packages/8b/a2/36ea6140c306c9ff6dd38e3bcec80b3b018474ef4d17eb68ceecd26675f4/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:970e9173dbd7eba9b4e01aab19215a48ee5dd3f43cef736eebde064a171f89a5", size = 349865 }, + { url = "https://files.pythonhosted.org/packages/95/b7/2fc76bc539693180488f7b6cc518da7acbbb9e3b931fd9280504128bf956/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c4639a9c22230276b7bffb6a850dfc8258a2521305e1faefe804d006b2e532", size = 321162 }, + { url = "https://files.pythonhosted.org/packages/f4/10/76d4f778458b0aa83f96e59d65ece72a060bacb20cfbee46cf6cd5ceba41/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc829960f34ba36aad4302e78eabf3ef16a3a100863f0d4eeddf30e8a485a03b", size = 327355 }, + { url = "https://files.pythonhosted.org/packages/43/a3/10cf483ea683f9f8ab096c24bad3cce20e0d1dd9a4baa0e2093c1c962d9d/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d32530b534e986374fc19eaa77fcb87e8a99e5431499949b828312bdcd20ac52", size = 1307935 }, + { url = "https://files.pythonhosted.org/packages/78/73/69dd9a024444489e22d86108e7b913f3528f56cfc312b5c5727a44188471/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e298e7e70cf4eb179cc1077be1c725b5fd131ebc81181bf0c03525c8abc297fd", size = 1372168 }, + { url = "https://files.pythonhosted.org/packages/0f/1b/96d586ccf1b1a9d2004dd519b25fbf104a11589abfd05484ff12199cca21/contourpy-1.3.2-cp313-cp313t-win32.whl", hash = "sha256:d0e589ae0d55204991450bb5c23f571c64fe43adaa53f93fc902a84c96f52fe1", size = 189550 }, + { url = "https://files.pythonhosted.org/packages/b0/e6/6000d0094e8a5e32ad62591c8609e269febb6e4db83a1c75ff8868b42731/contourpy-1.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:78e9253c3de756b3f6a5174d024c4835acd59eb3f8e2ca13e775dbffe1558f69", size = 238214 }, +] + [[package]] name = "coverage" version = "7.7.1" @@ -366,6 +668,54 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/c4/0b3eee04dac195f4730d102d7a9fbea894ae7a32ce075f84336df96a385d/crc32c-2.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:eee2a43b663feb6c79a6c1c6e5eae339c2b72cfac31ee54ec0209fa736cf7ee5", size = 39781 }, ] +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, +] + +[[package]] +name = "cytoolz" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "toolz", marker = "implementation_name == 'cpython'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/f9/3243eed3a6545c2a33a21f74f655e3fcb5d2192613cd3db81a93369eb339/cytoolz-1.0.1.tar.gz", hash = "sha256:89cc3161b89e1bb3ed7636f74ed2e55984fd35516904fc878cae216e42b2c7d6", size = 626652 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/e8/218098344ed2cb5f8441fade9b2428e435e7073962374a9c71e59ac141a7/cytoolz-1.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fcb8f7d0d65db1269022e7e0428471edee8c937bc288ebdcb72f13eaa67c2fe4", size = 414121 }, + { url = "https://files.pythonhosted.org/packages/de/27/4d729a5653718109262b758fec1a959aa9facb74c15460d9074dc76d6635/cytoolz-1.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:207d4e4b445e087e65556196ff472ff134370d9a275d591724142e255f384662", size = 390904 }, + { url = "https://files.pythonhosted.org/packages/72/c0/cbabfa788bab9c6038953bf9478adaec06e88903a726946ea7c88092f5c4/cytoolz-1.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21cdf6bac6fd843f3b20280a66fd8df20dea4c58eb7214a2cd8957ec176f0bb3", size = 2090734 }, + { url = "https://files.pythonhosted.org/packages/c3/66/369262c60f9423c2da82a60864a259c852f1aa122aced4acd2c679af58c0/cytoolz-1.0.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a55ec098036c0dea9f3bdc021f8acd9d105a945227d0811589f0573f21c9ce1", size = 2155933 }, + { url = "https://files.pythonhosted.org/packages/aa/4e/ee55186802f8d24b5fbf9a11405ccd1203b30eded07cc17750618219b94e/cytoolz-1.0.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a13ab79ff4ce202e03ab646a2134696988b554b6dc4b71451e948403db1331d8", size = 2171903 }, + { url = "https://files.pythonhosted.org/packages/a1/96/bd1a9f3396e9b7f618db8cd08d15630769ce3c8b7d0534f92cd639c977ae/cytoolz-1.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2d944799026e1ff08a83241f1027a2d9276c41f7a74224cd98b7df6e03957d", size = 2125270 }, + { url = "https://files.pythonhosted.org/packages/28/48/2a3762873091c88a69e161111cfbc6c222ff145d57ff011a642b169f04f1/cytoolz-1.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88ba85834cd523b91fdf10325e1e6d71c798de36ea9bdc187ca7bd146420de6f", size = 1973967 }, + { url = "https://files.pythonhosted.org/packages/e4/50/500bd69774bdc49a4d78ec8779eb6ac7c1a9d706bfd91cf2a1dba604373a/cytoolz-1.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a750b1af7e8bf6727f588940b690d69e25dc47cce5ce467925a76561317eaf7", size = 2021695 }, + { url = "https://files.pythonhosted.org/packages/e4/4e/ba5a0ce34869495eb50653de8d676847490cf13a2cac1760fc4d313e78de/cytoolz-1.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44a71870f7eae31d263d08b87da7c2bf1176f78892ed8bdade2c2850478cb126", size = 2010177 }, + { url = "https://files.pythonhosted.org/packages/87/57/615c630b3089a13adb15351d958d227430cf624f03b1dd39eb52c34c1f59/cytoolz-1.0.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8231b9abbd8e368e036f4cc2e16902c9482d4cf9e02a6147ed0e9a3cd4a9ab0", size = 2154321 }, + { url = "https://files.pythonhosted.org/packages/7f/0f/fe1aa2d931e3b35ecc05215bd75da945ea7346095b3b6f6027164e602d5a/cytoolz-1.0.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:aa87599ccc755de5a096a4d6c34984de6cd9dc928a0c5eaa7607457317aeaf9b", size = 2188374 }, + { url = "https://files.pythonhosted.org/packages/de/fa/fd363d97a641b6d0e2fd1d5c35b8fd41d9ccaeb4df56302f53bf23a58e3a/cytoolz-1.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:67cd16537df51baabde3baa770ab7b8d16839c4d21219d5b96ac59fb012ebd2d", size = 2077911 }, + { url = "https://files.pythonhosted.org/packages/d9/68/0a22946b98ae5201b54ccb4e651295285c0fb79406022b6ee8b2f791940c/cytoolz-1.0.1-cp312-cp312-win32.whl", hash = "sha256:fb988c333f05ee30ad4693fe4da55d95ec0bb05775d2b60191236493ea2e01f9", size = 321903 }, + { url = "https://files.pythonhosted.org/packages/62/1a/f3903197956055032f8cb297342e2dff07e50f83991aebfe5b4c4fcb55e4/cytoolz-1.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:8f89c48d8e5aec55ffd566a8ec858706d70ed0c6a50228eca30986bfa5b4da8b", size = 364490 }, + { url = "https://files.pythonhosted.org/packages/aa/2e/a9f069db0107749e9e72baf6c21abe3f006841a3bcfdc9b8420e22ef31eb/cytoolz-1.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6944bb93b287032a4c5ca6879b69bcd07df46f3079cf8393958cf0b0454f50c0", size = 407365 }, + { url = "https://files.pythonhosted.org/packages/a9/9b/5e87dd0e31f54c778b4f9f34cc14c1162d3096c8d746b0f8be97d70dd73c/cytoolz-1.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e027260fd2fc5cb041277158ac294fc13dca640714527219f702fb459a59823a", size = 385233 }, + { url = "https://files.pythonhosted.org/packages/63/00/2fd32b16284cdb97cfe092822179bc0c3bcdd5e927dd39f986169a517642/cytoolz-1.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88662c0e07250d26f5af9bc95911e6137e124a5c1ec2ce4a5d74de96718ab242", size = 2062903 }, + { url = "https://files.pythonhosted.org/packages/85/39/b3cbb5a9847ba59584a263772ad4f8ca2dbfd2a0e11efd09211d1219804c/cytoolz-1.0.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:309dffa78b0961b4c0cf55674b828fbbc793cf2d816277a5c8293c0c16155296", size = 2139517 }, + { url = "https://files.pythonhosted.org/packages/ea/39/bfcab4a46d50c467e36fe704f19d8904efead417787806ee210327f68390/cytoolz-1.0.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:edb34246e6eb40343c5860fc51b24937698e4fa1ee415917a73ad772a9a1746b", size = 2154849 }, + { url = "https://files.pythonhosted.org/packages/fd/42/3bc6ee61b0aa47e1cb40819adc1a456d7efa809f0dea9faddacb43fdde8f/cytoolz-1.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a54da7a8e4348a18d45d4d5bc84af6c716d7f131113a4f1cc45569d37edff1b", size = 2102302 }, + { url = "https://files.pythonhosted.org/packages/00/66/3f636c6ddea7b18026b90a8c238af472e423b86e427b11df02213689b012/cytoolz-1.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:241c679c3b1913c0f7259cf1d9639bed5084c86d0051641d537a0980548aa266", size = 1960872 }, + { url = "https://files.pythonhosted.org/packages/40/36/cb3b7cdd651007b69f9c48e9d104cec7cb8dc53afa1d6a720e5ad08022fa/cytoolz-1.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5bfc860251a8f280ac79696fc3343cfc3a7c30b94199e0240b6c9e5b6b01a2a5", size = 2014430 }, + { url = "https://files.pythonhosted.org/packages/88/3f/2e9bd2a16cfd269808922147551dcb2d8b68ba54a2c4deca2fa6a6cd0d5f/cytoolz-1.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c8edd1547014050c1bdad3ff85d25c82bd1c2a3c96830c6181521eb78b9a42b3", size = 2003127 }, + { url = "https://files.pythonhosted.org/packages/c4/7d/08604ff940aa784df8343c387fdf2489b948b714a6afb587775ae94da912/cytoolz-1.0.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b349bf6162e8de215403d7f35f8a9b4b1853dc2a48e6e1a609a5b1a16868b296", size = 2142369 }, + { url = "https://files.pythonhosted.org/packages/d2/c6/39919a0645bdbdf720e97cae107f959ea9d1267fbc3b0d94fc6e1d12ac8f/cytoolz-1.0.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1b18b35256219b6c3dd0fa037741b85d0bea39c552eab0775816e85a52834140", size = 2180427 }, + { url = "https://files.pythonhosted.org/packages/d8/03/dbb9d47556ee54337e7e0ac209d17ceff2d2a197c34de08005abc7a7449b/cytoolz-1.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:738b2350f340ff8af883eb301054eb724997f795d20d90daec7911c389d61581", size = 2069785 }, + { url = "https://files.pythonhosted.org/packages/ea/f8/11bb7b8947002231faae3ec2342df5896afbc19eb783a332cce6d219ff79/cytoolz-1.0.1-cp313-cp313-win32.whl", hash = "sha256:9cbd9c103df54fcca42be55ef40e7baea624ac30ee0b8bf1149f21146d1078d9", size = 320685 }, + { url = "https://files.pythonhosted.org/packages/40/eb/dde173cf2357084ca9423950be1f2f11ab11d65d8bd30165bfb8fd4213e9/cytoolz-1.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:90e577e08d3a4308186d9e1ec06876d4756b1e8164b92971c69739ea17e15297", size = 362898 }, +] + [[package]] name = "dag-cbor" version = "0.3.3" @@ -380,24 +730,68 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/14/8e/3fa50f0e4d709610be14b8b8049f5bdd9dbeddf5147286a4c59ba55f8af0/dag_cbor-0.3.3-py3-none-any.whl", hash = "sha256:e0d1c45427ed1fa21bbd103163d4b250f4ce9066337d6ffb976da1fac3a67454", size = 25457 }, ] +[[package]] +name = "dask" +version = "2025.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "cloudpickle" }, + { name = "fsspec" }, + { name = "packaging" }, + { name = "partd" }, + { name = "pyyaml" }, + { name = "toolz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/29/05feb8e2531c46d763547c66b7f5deb39b53d99b3be1b4ddddbd1cec6567/dask-2025.5.1.tar.gz", hash = "sha256:979d9536549de0e463f4cab8a8c66c3a2ef55791cd740d07d9bf58fab1d1076a", size = 10969324 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/30/53b0844a7a4c6b041b111b24ca15cc9b8661a86fe1f6aaeb2d0d7f0fb1f2/dask-2025.5.1-py3-none-any.whl", hash = "sha256:3b85fdaa5f6f989dde49da6008415b1ae996985ebdfb1e40de2c997d9010371d", size = 1474226 }, +] + +[package.optional-dependencies] +complete = [ + { name = "bokeh" }, + { name = "distributed" }, + { name = "jinja2" }, + { name = "lz4" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "pyarrow" }, +] + +[[package]] +name = "dataclassy" +version = "0.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/71/849a7708be159ff815ba9f2a671202c7ed7951f6810bb86555e379882935/dataclassy-0.11.1.tar.gz", hash = "sha256:ad6622cb91e644d13f68768558983fbc22c90a8ff7e355638485d18b9baf1198", size = 28166 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/0c/0ef792a555612974aedcd164a93c11fbbb83fc8ed5bfefa9799ea09ddc6b/dataclassy-0.11.1-py3-none-any.whl", hash = "sha256:bcb030d3d700cf9b1597042bbc8375b92773e6f68f65675a7071862c0ddb87f5", size = 23942 }, +] + [[package]] name = "dclimate-zarr-client" version = "0.1.0" source = { editable = "." } dependencies = [ { name = "aiobotocore" }, + { name = "ape-base" }, + { name = "ape-solidity" }, + { name = "eth-account" }, + { name = "eth-ape" }, { name = "geopandas" }, { name = "numpy" }, { name = "pandas" }, { name = "py-hamt" }, { name = "pyarrow" }, { name = "pycryptodome" }, + { name = "pytest-asyncio" }, { name = "requests" }, { name = "rioxarray" }, { name = "s3fs" }, { name = "scipy" }, { name = "shapely" }, - { name = "xarray" }, + { name = "web3" }, + { name = "xarray", extra = ["complete"] }, { name = "zarr" }, ] @@ -415,14 +809,19 @@ testing = [ [package.metadata] requires-dist = [ { name = "aiobotocore" }, + { name = "ape-base", specifier = ">=0.8.1" }, + { name = "ape-solidity", specifier = ">=0.8.5" }, + { name = "eth-account", specifier = ">=0.11.3" }, + { name = "eth-ape", specifier = ">=0.8.4" }, { name = "geopandas" }, { name = "numpy", specifier = ">=2.1.3" }, { name = "pandas" }, { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.1.0" }, - { name = "py-hamt", git = "https://github.com/dClimate/py-hamt" }, + { name = "py-hamt", git = "https://github.com/dClimate/py-hamt?rev=a60039b4e2305847c8b9241557a35f96038d7ca0" }, { name = "pyarrow" }, { name = "pycryptodome", specifier = ">=3.21.0" }, { name = "pytest", marker = "extra == 'testing'" }, + { name = "pytest-asyncio", specifier = ">=1.0.0" }, { name = "pytest-cov", marker = "extra == 'testing'" }, { name = "pytest-mock", marker = "extra == 'testing'" }, { name = "requests" }, @@ -431,11 +830,21 @@ requires-dist = [ { name = "s3fs" }, { name = "scipy" }, { name = "shapely" }, - { name = "xarray" }, - { name = "zarr" }, + { name = "web3", specifier = ">=6.20.1" }, + { name = "xarray", extras = ["complete"], specifier = ">=2025.3.0" }, + { name = "zarr", specifier = ">=3.0.8" }, ] provides-extras = ["testing", "dev"] +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190 }, +] + [[package]] name = "deprecated" version = "1.2.18" @@ -457,6 +866,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, ] +[[package]] +name = "distributed" +version = "2025.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "cloudpickle" }, + { name = "dask" }, + { name = "jinja2" }, + { name = "locket" }, + { name = "msgpack" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "sortedcontainers" }, + { name = "tblib" }, + { name = "toolz" }, + { name = "tornado" }, + { name = "urllib3" }, + { name = "zict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/29/ba/45950f405d023a520a4d10753ef40209a465b86c8fdc131236ec29bcb15c/distributed-2025.5.1.tar.gz", hash = "sha256:cf1d62a2c17a0a9fc1544bd10bb7afd39f22f24aaa9e3df3209c44d2cfb16703", size = 1107874 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/65/89601dcc7383f0e5109e59eab90677daa9abb260d821570cd6089c8894bf/distributed-2025.5.1-py3-none-any.whl", hash = "sha256:74782b965ddb24ce59c6441fa777e944b5962d82325cc41f228537b59bb7fbbe", size = 1014789 }, +] + [[package]] name = "donfig" version = "0.8.1.post1" @@ -470,113 +905,817 @@ wheels = [ ] [[package]] -name = "filelock" -version = "3.18.0" +name = "eip712" +version = "0.2.13" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075 } +dependencies = [ + { name = "dataclassy" }, + { name = "eth-abi" }, + { name = "eth-account" }, + { name = "eth-typing" }, + { name = "eth-utils" }, + { name = "hexbytes" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/95/aebdac7e295388f6fb953eeb85741a0b87cc5508cc86fc4e32a26d3596ad/eip712-0.2.13.tar.gz", hash = "sha256:291ab5082199469dc088e5fdce7fa07b2e64dcccde434e10a9f1c53be2d18175", size = 63372 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 }, + { url = "https://files.pythonhosted.org/packages/b1/0f/5449346b45658cb1979731f75d1fae46c468cb06cc25250ac542422af515/eip712-0.2.13-py3-none-any.whl", hash = "sha256:92bcaa1f47a48cf6f0369b88b7bac8c88962b212cfb3b9d6a54639de0616e17c", size = 10681 }, ] [[package]] -name = "frozenlist" -version = "1.5.0" +name = "eth-abi" +version = "5.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8f/ed/0f4cec13a93c02c47ec32d81d11c0c1efbadf4a471e3f3ce7cad366cbbd3/frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", size = 39930 } +dependencies = [ + { name = "eth-typing" }, + { name = "eth-utils" }, + { name = "parsimonious" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/71/d9e1380bd77fd22f98b534699af564f189b56d539cc2b9dab908d4e4c242/eth_abi-5.2.0.tar.gz", hash = "sha256:178703fa98c07d8eecd5ae569e7e8d159e493ebb6eeb534a8fe973fbc4e40ef0", size = 49797 } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/73/fa6d1a96ab7fd6e6d1c3500700963eab46813847f01ef0ccbaa726181dd5/frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21", size = 94026 }, - { url = "https://files.pythonhosted.org/packages/ab/04/ea8bf62c8868b8eada363f20ff1b647cf2e93377a7b284d36062d21d81d1/frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d", size = 54150 }, - { url = "https://files.pythonhosted.org/packages/d0/9a/8e479b482a6f2070b26bda572c5e6889bb3ba48977e81beea35b5ae13ece/frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e", size = 51927 }, - { url = "https://files.pythonhosted.org/packages/e3/12/2aad87deb08a4e7ccfb33600871bbe8f0e08cb6d8224371387f3303654d7/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a", size = 282647 }, - { url = "https://files.pythonhosted.org/packages/77/f2/07f06b05d8a427ea0060a9cef6e63405ea9e0d761846b95ef3fb3be57111/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a", size = 289052 }, - { url = "https://files.pythonhosted.org/packages/bd/9f/8bf45a2f1cd4aa401acd271b077989c9267ae8463e7c8b1eb0d3f561b65e/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee", size = 291719 }, - { url = "https://files.pythonhosted.org/packages/41/d1/1f20fd05a6c42d3868709b7604c9f15538a29e4f734c694c6bcfc3d3b935/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6", size = 267433 }, - { url = "https://files.pythonhosted.org/packages/af/f2/64b73a9bb86f5a89fb55450e97cd5c1f84a862d4ff90d9fd1a73ab0f64a5/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e", size = 283591 }, - { url = "https://files.pythonhosted.org/packages/29/e2/ffbb1fae55a791fd6c2938dd9ea779509c977435ba3940b9f2e8dc9d5316/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9", size = 273249 }, - { url = "https://files.pythonhosted.org/packages/2e/6e/008136a30798bb63618a114b9321b5971172a5abddff44a100c7edc5ad4f/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039", size = 271075 }, - { url = "https://files.pythonhosted.org/packages/ae/f0/4e71e54a026b06724cec9b6c54f0b13a4e9e298cc8db0f82ec70e151f5ce/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784", size = 285398 }, - { url = "https://files.pythonhosted.org/packages/4d/36/70ec246851478b1c0b59f11ef8ade9c482ff447c1363c2bd5fad45098b12/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631", size = 294445 }, - { url = "https://files.pythonhosted.org/packages/37/e0/47f87544055b3349b633a03c4d94b405956cf2437f4ab46d0928b74b7526/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f", size = 280569 }, - { url = "https://files.pythonhosted.org/packages/f9/7c/490133c160fb6b84ed374c266f42800e33b50c3bbab1652764e6e1fc498a/frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8", size = 44721 }, - { url = "https://files.pythonhosted.org/packages/b1/56/4e45136ffc6bdbfa68c29ca56ef53783ef4c2fd395f7cbf99a2624aa9aaa/frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f", size = 51329 }, - { url = "https://files.pythonhosted.org/packages/da/3b/915f0bca8a7ea04483622e84a9bd90033bab54bdf485479556c74fd5eaf5/frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953", size = 91538 }, - { url = "https://files.pythonhosted.org/packages/c7/d1/a7c98aad7e44afe5306a2b068434a5830f1470675f0e715abb86eb15f15b/frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0", size = 52849 }, - { url = "https://files.pythonhosted.org/packages/3a/c8/76f23bf9ab15d5f760eb48701909645f686f9c64fbb8982674c241fbef14/frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2", size = 50583 }, - { url = "https://files.pythonhosted.org/packages/1f/22/462a3dd093d11df623179d7754a3b3269de3b42de2808cddef50ee0f4f48/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f", size = 265636 }, - { url = "https://files.pythonhosted.org/packages/80/cf/e075e407fc2ae7328155a1cd7e22f932773c8073c1fc78016607d19cc3e5/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608", size = 270214 }, - { url = "https://files.pythonhosted.org/packages/a1/58/0642d061d5de779f39c50cbb00df49682832923f3d2ebfb0fedf02d05f7f/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b", size = 273905 }, - { url = "https://files.pythonhosted.org/packages/ab/66/3fe0f5f8f2add5b4ab7aa4e199f767fd3b55da26e3ca4ce2cc36698e50c4/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840", size = 250542 }, - { url = "https://files.pythonhosted.org/packages/f6/b8/260791bde9198c87a465224e0e2bb62c4e716f5d198fc3a1dacc4895dbd1/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439", size = 267026 }, - { url = "https://files.pythonhosted.org/packages/2e/a4/3d24f88c527f08f8d44ade24eaee83b2627793fa62fa07cbb7ff7a2f7d42/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de", size = 257690 }, - { url = "https://files.pythonhosted.org/packages/de/9a/d311d660420b2beeff3459b6626f2ab4fb236d07afbdac034a4371fe696e/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641", size = 253893 }, - { url = "https://files.pythonhosted.org/packages/c6/23/e491aadc25b56eabd0f18c53bb19f3cdc6de30b2129ee0bc39cd387cd560/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e", size = 267006 }, - { url = "https://files.pythonhosted.org/packages/08/c4/ab918ce636a35fb974d13d666dcbe03969592aeca6c3ab3835acff01f79c/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9", size = 276157 }, - { url = "https://files.pythonhosted.org/packages/c0/29/3b7a0bbbbe5a34833ba26f686aabfe982924adbdcafdc294a7a129c31688/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03", size = 264642 }, - { url = "https://files.pythonhosted.org/packages/ab/42/0595b3dbffc2e82d7fe658c12d5a5bafcd7516c6bf2d1d1feb5387caa9c1/frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c", size = 44914 }, - { url = "https://files.pythonhosted.org/packages/17/c4/b7db1206a3fea44bf3b838ca61deb6f74424a8a5db1dd53ecb21da669be6/frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28", size = 51167 }, - { url = "https://files.pythonhosted.org/packages/c6/c8/a5be5b7550c10858fcf9b0ea054baccab474da77d37f1e828ce043a3a5d4/frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", size = 11901 }, + { url = "https://files.pythonhosted.org/packages/7a/b4/2f3982c4cbcbf5eeb6aec62df1533c0e63c653b3021ff338d44944405676/eth_abi-5.2.0-py3-none-any.whl", hash = "sha256:17abe47560ad753f18054f5b3089fcb588f3e3a092136a416b6c1502cb7e8877", size = 28511 }, ] [[package]] -name = "fsspec" -version = "2025.3.0" +name = "eth-account" +version = "0.11.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/34/f4/5721faf47b8c499e776bc34c6a8fc17efdf7fdef0b00f398128bc5dcb4ac/fsspec-2025.3.0.tar.gz", hash = "sha256:a935fd1ea872591f2b5148907d103488fc523295e6c64b835cfad8c3eca44972", size = 298491 } +dependencies = [ + { name = "bitarray" }, + { name = "ckzg" }, + { name = "eth-abi" }, + { name = "eth-keyfile" }, + { name = "eth-keys" }, + { name = "eth-rlp" }, + { name = "eth-utils" }, + { name = "hexbytes" }, + { name = "rlp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/42/2d1e2f1cb8b3f40f8c85f7df33e78ac0fc5f947c955607238e2e4a0d418b/eth_account-0.11.3.tar.gz", hash = "sha256:a712a9534638a7cfaa4cc069f1b9d5cefeee70362cfc3a7b0a2534ee61ce76c9", size = 712791 } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/53/eb690efa8513166adef3e0669afd31e95ffde69fb3c52ec2ac7223ed6018/fsspec-2025.3.0-py3-none-any.whl", hash = "sha256:efb87af3efa9103f94ca91a7f8cb7a4df91af9f74fc106c9c7ea0efd7277c1b3", size = 193615 }, + { url = "https://files.pythonhosted.org/packages/bc/de/a850f7d3d47f7c14c50cda73c8646a9ab140608c553008bffa949d74afab/eth_account-0.11.3-py3-none-any.whl", hash = "sha256:16cf58aabc65171fc206489899b7e5546e3215e1a4debc12dbd55345c979081e", size = 355394 }, ] [[package]] -name = "geopandas" -version = "1.0.1" +name = "eth-ape" +version = "0.8.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy" }, + { name = "click" }, + { name = "eip712" }, + { name = "eth-abi" }, + { name = "eth-account" }, + { name = "eth-pydantic-types" }, + { name = "eth-typing" }, + { name = "eth-utils" }, + { name = "ethpm-types" }, + { name = "evm-trace" }, + { name = "evmchains" }, + { name = "hexbytes" }, + { name = "ijson" }, + { name = "ipython" }, + { name = "lazyasd" }, { name = "packaging" }, { name = "pandas" }, - { name = "pyogrio" }, - { name = "pyproj" }, - { name = "shapely" }, + { name = "pluggy" }, + { name = "py-geth" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pytest" }, + { name = "python-dateutil" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "rich" }, + { name = "sqlalchemy" }, + { name = "tqdm" }, + { name = "traitlets" }, + { name = "trie" }, + { name = "urllib3" }, + { name = "watchdog" }, + { name = "web3", extra = ["tester"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/39/08/2cf5d85356e45b10b8d066cf4c3ba1e9e3185423c48104eed87e8afd0455/geopandas-1.0.1.tar.gz", hash = "sha256:b8bf70a5534588205b7a56646e2082fb1de9a03599651b3d80c99ea4c2ca08ab", size = 317736 } +sdist = { url = "https://files.pythonhosted.org/packages/6a/0d/055b265b4c0eda246f57dc3e1c275ec2cb222f699e55a6d2cab93b74f63c/eth-ape-0.8.4.tar.gz", hash = "sha256:777a4e889e06fc419021256dda7905b1d1927f6a8390a5e45049a2e5fed63538", size = 1108518 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/64/7d344cfcef5efddf9cf32f59af7f855828e9d74b5f862eddf5bfd9f25323/geopandas-1.0.1-py3-none-any.whl", hash = "sha256:01e147d9420cc374d26f51fc23716ac307f32b49406e4bd8462c07e82ed1d3d6", size = 323587 }, + { url = "https://files.pythonhosted.org/packages/4b/5f/5d33851b4be473274ffdf1c764c4693ad048afd97e19323cc78f495da488/eth_ape-0.8.4-py3-none-any.whl", hash = "sha256:294f2dc04ef7d7b024095ffd86bb268d57ae08f7df85790dd1e837caa0e7d53e", size = 344322 }, ] [[package]] -name = "identify" -version = "2.6.9" +name = "eth-bloom" +version = "3.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9b/98/a71ab060daec766acc30fb47dfca219d03de34a70d616a79a38c6066c5bf/identify-2.6.9.tar.gz", hash = "sha256:d40dfe3142a1421d8518e3d3985ef5ac42890683e32306ad614a29490abeb6bf", size = 99249 } +dependencies = [ + { name = "eth-hash", extra = ["pycryptodome"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/d6/9c345601de27b536dc8b0f4c1d6cb84fe76a47de9ac02753f58deae59c86/eth_bloom-3.1.0.tar.gz", hash = "sha256:4bc918f6fde44334e92b23cfb345db961e2e3af620535cbc872444f7a143cb88", size = 9807 } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/ce/0845144ed1f0e25db5e7a79c2354c1da4b5ce392b8966449d5db8dca18f1/identify-2.6.9-py2.py3-none-any.whl", hash = "sha256:c98b4322da415a8e5a70ff6e51fbc2d2932c015532d77e9f8537b4ba7813b150", size = 99101 }, + { url = "https://files.pythonhosted.org/packages/81/e5/251bda1d3f3cff230fa39e95d41141c1b44e8f55c101c5f593ccf5a31b63/eth_bloom-3.1.0-py3-none-any.whl", hash = "sha256:c96b2dd6cafa407373bca1a9d74b650378ba672d5b17f2771bf7d3c3aaa7651c", size = 5785 }, ] [[package]] -name = "idna" -version = "3.10" +name = "eth-hash" +version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +sdist = { url = "https://files.pythonhosted.org/packages/ee/38/577b7bc9380ef9dff0f1dffefe0c9a1ded2385e7a06c306fd95afb6f9451/eth_hash-0.7.1.tar.gz", hash = "sha256:d2411a403a0b0a62e8247b4117932d900ffb4c8c64b15f92620547ca5ce46be5", size = 12227 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, + { url = "https://files.pythonhosted.org/packages/eb/db/f8775490669d28aca24871c67dd56b3e72105cb3bcae9a4ec65dd70859b3/eth_hash-0.7.1-py3-none-any.whl", hash = "sha256:0fb1add2adf99ef28883fd6228eb447ef519ea72933535ad1a0b28c6f65f868a", size = 8028 }, ] -[[package]] -name = "iniconfig" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, +[package.optional-dependencies] +pycryptodome = [ + { name = "pycryptodome" }, +] +pysha3 = [ + { name = "safe-pysha3", marker = "implementation_name == 'cpython'" }, ] [[package]] -name = "jmespath" -version = "1.0.1" +name = "eth-keyfile" +version = "0.9.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843 } +dependencies = [ + { name = "eth-keys" }, + { name = "eth-utils" }, + { name = "py-ecc" }, + { name = "pycryptodome" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/e4/3f0c20b020786e1fa6e1ecd81806c54167fa2b0839e0020086b95a6e8faf/eth_keyfile-0.9.1.tar.gz", hash = "sha256:c7a8bc6af4527d1ab2eb1d1b949d59925252e17663eaf90087da121327b51df6", size = 19787 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256 }, + { url = "https://files.pythonhosted.org/packages/5f/08/9c8bf617b39e1dd56303593292e8b4eb66497a5f0f5b997a4b291e5343c0/eth_keyfile-0.9.1-py3-none-any.whl", hash = "sha256:9789c3b4fa0bb6e2616cdc2bdd71b8755b42947d78ef1e900a0149480fabb5c2", size = 9866 }, +] + +[[package]] +name = "eth-keys" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "eth-typing" }, + { name = "eth-utils" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/11/1ed831c50bd74f57829aa06e58bd82a809c37e070ee501c953b9ac1f1552/eth_keys-0.7.0.tar.gz", hash = "sha256:79d24fd876201df67741de3e3fefb3f4dbcbb6ace66e47e6fe662851a4547814", size = 30166 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/25/0ae00f2b0095e559d61ad3dc32171bd5a29dfd95ab04b4edd641f7c75f72/eth_keys-0.7.0-py3-none-any.whl", hash = "sha256:b0cdda8ffe8e5ba69c7c5ca33f153828edcace844f67aabd4542d7de38b159cf", size = 20656 }, +] + +[[package]] +name = "eth-pydantic-types" +version = "0.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "eth-typing" }, + { name = "eth-utils" }, + { name = "hexbytes" }, + { name = "pydantic" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/19/3209c4c20832a95c57611f4fb5be51db18836929f2e6c80affc07b533616/eth-pydantic-types-0.1.3.tar.gz", hash = "sha256:4764ba82434ab6d0d98d3b7aee4ace0cc7b12cf47c7e53424525c33fb9b872af", size = 65715 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/b2/e44f9e3be45768c33f83080d7c2bfcdf698fdeaec810c98866724eb44846/eth_pydantic_types-0.1.3-py3-none-any.whl", hash = "sha256:7a54f253f32bb3c01b21dbbfc7022d476b53ad5b7da96dcc4dae24997ec69469", size = 14624 }, +] + +[[package]] +name = "eth-rlp" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "eth-utils" }, + { name = "hexbytes" }, + { name = "rlp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/2e/fb9c2e0a2d0e249b61abf462828f3f8039305dfbe5844e138ab1a3b3a413/eth-rlp-1.0.1.tar.gz", hash = "sha256:d61dbda892ee1220f28fb3663c08f6383c305db9f1f5624dc585c9cd05115027", size = 7261 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/7f/583c8286530a52d9d5c07f3895c2184e36399379d1284dc1c2c8309a8e9d/eth_rlp-1.0.1-py3-none-any.whl", hash = "sha256:dd76515d71654277377d48876b88e839d61553aaf56952e580bb7cebef2b1517", size = 4922 }, +] + +[[package]] +name = "eth-tester" +version = "0.11.0b2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "eth-abi" }, + { name = "eth-account" }, + { name = "eth-keys" }, + { name = "eth-utils" }, + { name = "rlp" }, + { name = "semantic-version" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/7b/249a37b69da25d73b5040adcdd40c5f661f6dc74e5c8f788e58cd7f2f416/eth_tester-0.11.0b2.tar.gz", hash = "sha256:b46e9acfb5cb5f3f62fd729d796ca0af231f117e35ce6f2f2f5bb55d4108edcd", size = 98972 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/e8/d910312d33c0e582371644eae2bdde949d2e75aabc06966180e1bbca23dc/eth_tester-0.11.0b2-py3-none-any.whl", hash = "sha256:d352cd1f99511dac6f38a0c449f77315a1ddbe234c8a3f3fe6ff90ac172622f3", size = 75193 }, +] + +[package.optional-dependencies] +py-evm = [ + { name = "eth-hash", marker = "implementation_name == 'cpython' or implementation_name == 'pypy'" }, + { name = "eth-hash", extra = ["pycryptodome"], marker = "implementation_name == 'pypy'" }, + { name = "eth-hash", extra = ["pysha3"], marker = "implementation_name == 'cpython'" }, + { name = "py-evm" }, +] + +[[package]] +name = "eth-typing" +version = "3.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c8/52/f5d10803345dd06f9301b4871e8ebb39971a4238b60c61f84ffc83379611/eth-typing-3.5.2.tar.gz", hash = "sha256:22bf051ddfaa35ff827c30090de167e5c5b8cc6d343f7f35c9b1c7553f6ab64d", size = 16850 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/36/50c5d5f070b7e4318dc906f8bf642719418ffef741db94f85ab6447563e1/eth_typing-3.5.2-py3-none-any.whl", hash = "sha256:1842e628fb1ffa929b94f89a9d33caafbeb9978dc96abb6036a12bc91f1c624b", size = 14481 }, +] + +[[package]] +name = "eth-utils" +version = "2.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cytoolz", marker = "implementation_name == 'cpython'" }, + { name = "eth-hash" }, + { name = "eth-typing" }, + { name = "toolz", marker = "implementation_name == 'pypy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b8/b0/ec73fd8b9a836a1b0f09db8e9ca22e3c1f7be1e4a36ce4a9e254819ee37a/eth_utils-2.3.2.tar.gz", hash = "sha256:1986d704b29202386c9bc4b27b948a134320c11c8104c45ca367e4663ae7d10e", size = 74656 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/e8/1c15f2f97ac144b7d7cb851d72b52aa717b284251981ec74773e99573e26/eth_utils-2.3.2-py3-none-any.whl", hash = "sha256:4470be372674a25b8440b69cb35bda634a079876930853814ea307248c3d198b", size = 77767 }, +] + +[[package]] +name = "ethpm-types" +version = "0.6.25" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "eth-abi" }, + { name = "eth-pydantic-types" }, + { name = "eth-utils" }, + { name = "py-cid" }, + { name = "pydantic" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/2d/27b1d081db0e8cf46b4b34c7060d277ebe4c39f05263f629b77d9a5053dc/ethpm_types-0.6.25.tar.gz", hash = "sha256:8fdef2ea36b6086682072d23a4172329c8fb5de9e0c5ee761477aab4c8131874", size = 1227238 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/75/39b6a14b15b17ecc4523aa45edfb50d6aa8cc00b7589813324f27b6694d8/ethpm_types-0.6.25-py3-none-any.whl", hash = "sha256:fe61b4f0fec3102f5a99b9972a3baeb6ff6e72fe96a29a692a122184c21ae297", size = 35154 }, +] + +[[package]] +name = "evm-trace" +version = "0.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "eth-pydantic-types" }, + { name = "eth-utils" }, + { name = "msgspec" }, + { name = "py-evm" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/78/7241a26ecf4b6a0334da407f4e0a57a67f09a05b2e8b87f5a4b5f7dbd75b/evm-trace-0.1.5.tar.gz", hash = "sha256:a6f8158a96b8440d42b02bafebc0bf4e7e504b9e7ca661de96968177d225cbc5", size = 130696 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/fa/824368e499ee2759918602bfd1b960e92dc1f3ac11703be473a56da57cb0/evm_trace-0.1.5-py3-none-any.whl", hash = "sha256:aed1c82c68e586c54286436944e2021bf0ff7b95cefb540b9b27ba38322b7e0a", size = 19789 }, +] + +[[package]] +name = "evmchains" +version = "0.0.13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/8c/552415f1132054f6fce262d8b9eaac56a7ada787f55cda9c1ab479e98a9a/evmchains-0.0.13.tar.gz", hash = "sha256:f017bff69bbdf3d822148c5500969e600e28218df55a7c19a9d9c34e7e36c179", size = 23777 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/44/dd79fbcd5dac6a297c5c1a7eaab0ac9a991530b18818684959ed65979668/evmchains-0.0.13-py3-none-any.whl", hash = "sha256:01f49c7e1e9f8095c4ec64b33db5c45b7ba4ebecc7607e8a1104b2721c57ec85", size = 16610 }, +] + +[[package]] +name = "executing" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702 }, +] + +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 }, +] + +[[package]] +name = "flox" +version = "0.10.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "numpy-groupies" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "scipy" }, + { name = "toolz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/34/6eea00e3f1de745c8adad5a3dafd46c3481294cff8699c20a9b8d80502ed/flox-0.10.4.tar.gz", hash = "sha256:2ccb6b497607857cfa68917584c5850005b27bf4748abdc24c106b10d5ce9056", size = 718663 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/dc/3489211b2f4e9d81693a2fbecbf6ead23a2fc5fa10fc4b6cd458d0d888dc/flox-0.10.4-py3-none-any.whl", hash = "sha256:bc07f74706c86d3bf4eae99002cf23e2223fa415224c5dd90e4f7b7f05a7e21a", size = 78446 }, +] + +[[package]] +name = "fonttools" +version = "4.58.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/97/5735503e58d3816b0989955ef9b2df07e4c99b246469bd8b3823a14095da/fonttools-4.58.5.tar.gz", hash = "sha256:b2a35b0a19f1837284b3a23dd64fd7761b8911d50911ecd2bdbaf5b2d1b5df9c", size = 3526243 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/68/66b498ee66f3e7e92fd68476c2509508082b7f57d68c0cdb4b8573f44331/fonttools-4.58.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c3af3fefaafb570a03051a0d6899b8374dcf8e6a4560e42575843aef33bdbad6", size = 2754751 }, + { url = "https://files.pythonhosted.org/packages/f1/1e/edbc14b79290980c3944a1f43098624bc8965f534964aa03d52041f24cb4/fonttools-4.58.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:688137789dbd44e8757ad77b49a771539d8069195ffa9a8bcf18176e90bbd86d", size = 2322342 }, + { url = "https://files.pythonhosted.org/packages/c1/d7/3c87cf147185d91c2e946460a5cf68c236427b4a23ab96793ccb7d8017c9/fonttools-4.58.5-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af65836cf84cd7cb882d0b353bdc73643a497ce23b7414c26499bb8128ca1af", size = 4897011 }, + { url = "https://files.pythonhosted.org/packages/a0/d6/fbb44cc85d4195fe54356658bd9f934328b4f74ae14addd90b4b5558b5c9/fonttools-4.58.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d2d79cfeb456bf438cb9fb87437634d4d6f228f27572ca5c5355e58472d5519d", size = 4942291 }, + { url = "https://files.pythonhosted.org/packages/4d/c8/453f82e21aedf25cdc2ae619c03a73512398cec9bd8b6c3b1c571e0b6632/fonttools-4.58.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0feac9dda9a48a7a342a593f35d50a5cee2dbd27a03a4c4a5192834a4853b204", size = 4886824 }, + { url = "https://files.pythonhosted.org/packages/40/54/e9190001b8e22d123f78925b2f508c866d9d18531694b979277ad45d59b0/fonttools-4.58.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36555230e168511e83ad8637232268649634b8dfff6ef58f46e1ebc057a041ad", size = 5038510 }, + { url = "https://files.pythonhosted.org/packages/cf/9c/07cdad4774841a6304aabae939f8cbb9538cb1d8e97f5016b334da98e73a/fonttools-4.58.5-cp312-cp312-win32.whl", hash = "sha256:26ec05319353842d127bd02516eacb25b97ca83966e40e9ad6fab85cab0576f4", size = 2188459 }, + { url = "https://files.pythonhosted.org/packages/0e/4d/1eaaad22781d55f49d1b184563842172aeb6a4fe53c029e503be81114314/fonttools-4.58.5-cp312-cp312-win_amd64.whl", hash = "sha256:778a632e538f82c1920579c0c01566a8f83dc24470c96efbf2fbac698907f569", size = 2236565 }, + { url = "https://files.pythonhosted.org/packages/3a/ee/764dd8b99891f815241f449345863cfed9e546923d9cef463f37fd1d7168/fonttools-4.58.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f4b6f1360da13cecc88c0d60716145b31e1015fbe6a59e32f73a4404e2ea92cf", size = 2745867 }, + { url = "https://files.pythonhosted.org/packages/e2/23/8fef484c02fef55e226dfeac4339a015c5480b6a496064058491759ac71e/fonttools-4.58.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a036822e915692aa2c03e2decc60f49a8190f8111b639c947a4f4e5774d0d7a", size = 2317933 }, + { url = "https://files.pythonhosted.org/packages/ab/47/f92b135864fa777e11ad68420bf89446c91a572fe2782745586f8e6aac0c/fonttools-4.58.5-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6d7709fcf4577b0f294ee6327088884ca95046e1eccde87c53bbba4d5008541", size = 4877844 }, + { url = "https://files.pythonhosted.org/packages/3e/65/6c1a83511d8ac32411930495645edb3f8dfabebcb78f08cf6009ba2585ec/fonttools-4.58.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9b5099ca99b79d6d67162778b1b1616fc0e1de02c1a178248a0da8d78a33852", size = 4940106 }, + { url = "https://files.pythonhosted.org/packages/fa/90/df8eb77d6cf266cbbba01866a1349a3e9121e0a63002cf8d6754e994f755/fonttools-4.58.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3f2c05a8d82a4d15aebfdb3506e90793aea16e0302cec385134dd960647a36c0", size = 4879458 }, + { url = "https://files.pythonhosted.org/packages/26/b1/e32f8de51b7afcfea6ad62780da2fa73212c43a32cd8cafcc852189d7949/fonttools-4.58.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79f0c4b1cc63839b61deeac646d8dba46f8ed40332c2ac1b9997281462c2e4ba", size = 5021917 }, + { url = "https://files.pythonhosted.org/packages/89/72/578aa7fe32918dd763c62f447aaed672d665ee10e3eeb1725f4d6493fe96/fonttools-4.58.5-cp313-cp313-win32.whl", hash = "sha256:a1a9a2c462760976882131cbab7d63407813413a2d32cd699e86a1ff22bf7aa5", size = 2186827 }, + { url = "https://files.pythonhosted.org/packages/71/a3/21e921b16cb9c029d3308e0cb79c9a937e9ff1fc1ee28c2419f0957b9e7c/fonttools-4.58.5-cp313-cp313-win_amd64.whl", hash = "sha256:bca61b14031a4b7dc87e14bf6ca34c275f8e4b9f7a37bc2fe746b532a924cf30", size = 2235706 }, + { url = "https://files.pythonhosted.org/packages/d7/d4/1d85a1996b6188cd2713230e002d79a6f3a289bb17cef600cba385848b72/fonttools-4.58.5-py3-none-any.whl", hash = "sha256:e48a487ed24d9b611c5c4b25db1e50e69e9854ca2670e39a3486ffcd98863ec4", size = 1115318 }, +] + +[[package]] +name = "frozenlist" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8f/ed/0f4cec13a93c02c47ec32d81d11c0c1efbadf4a471e3f3ce7cad366cbbd3/frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", size = 39930 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/73/fa6d1a96ab7fd6e6d1c3500700963eab46813847f01ef0ccbaa726181dd5/frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21", size = 94026 }, + { url = "https://files.pythonhosted.org/packages/ab/04/ea8bf62c8868b8eada363f20ff1b647cf2e93377a7b284d36062d21d81d1/frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d", size = 54150 }, + { url = "https://files.pythonhosted.org/packages/d0/9a/8e479b482a6f2070b26bda572c5e6889bb3ba48977e81beea35b5ae13ece/frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e", size = 51927 }, + { url = "https://files.pythonhosted.org/packages/e3/12/2aad87deb08a4e7ccfb33600871bbe8f0e08cb6d8224371387f3303654d7/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a", size = 282647 }, + { url = "https://files.pythonhosted.org/packages/77/f2/07f06b05d8a427ea0060a9cef6e63405ea9e0d761846b95ef3fb3be57111/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a", size = 289052 }, + { url = "https://files.pythonhosted.org/packages/bd/9f/8bf45a2f1cd4aa401acd271b077989c9267ae8463e7c8b1eb0d3f561b65e/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee", size = 291719 }, + { url = "https://files.pythonhosted.org/packages/41/d1/1f20fd05a6c42d3868709b7604c9f15538a29e4f734c694c6bcfc3d3b935/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6", size = 267433 }, + { url = "https://files.pythonhosted.org/packages/af/f2/64b73a9bb86f5a89fb55450e97cd5c1f84a862d4ff90d9fd1a73ab0f64a5/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e", size = 283591 }, + { url = "https://files.pythonhosted.org/packages/29/e2/ffbb1fae55a791fd6c2938dd9ea779509c977435ba3940b9f2e8dc9d5316/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9", size = 273249 }, + { url = "https://files.pythonhosted.org/packages/2e/6e/008136a30798bb63618a114b9321b5971172a5abddff44a100c7edc5ad4f/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039", size = 271075 }, + { url = "https://files.pythonhosted.org/packages/ae/f0/4e71e54a026b06724cec9b6c54f0b13a4e9e298cc8db0f82ec70e151f5ce/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784", size = 285398 }, + { url = "https://files.pythonhosted.org/packages/4d/36/70ec246851478b1c0b59f11ef8ade9c482ff447c1363c2bd5fad45098b12/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631", size = 294445 }, + { url = "https://files.pythonhosted.org/packages/37/e0/47f87544055b3349b633a03c4d94b405956cf2437f4ab46d0928b74b7526/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f", size = 280569 }, + { url = "https://files.pythonhosted.org/packages/f9/7c/490133c160fb6b84ed374c266f42800e33b50c3bbab1652764e6e1fc498a/frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8", size = 44721 }, + { url = "https://files.pythonhosted.org/packages/b1/56/4e45136ffc6bdbfa68c29ca56ef53783ef4c2fd395f7cbf99a2624aa9aaa/frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f", size = 51329 }, + { url = "https://files.pythonhosted.org/packages/da/3b/915f0bca8a7ea04483622e84a9bd90033bab54bdf485479556c74fd5eaf5/frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953", size = 91538 }, + { url = "https://files.pythonhosted.org/packages/c7/d1/a7c98aad7e44afe5306a2b068434a5830f1470675f0e715abb86eb15f15b/frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0", size = 52849 }, + { url = "https://files.pythonhosted.org/packages/3a/c8/76f23bf9ab15d5f760eb48701909645f686f9c64fbb8982674c241fbef14/frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2", size = 50583 }, + { url = "https://files.pythonhosted.org/packages/1f/22/462a3dd093d11df623179d7754a3b3269de3b42de2808cddef50ee0f4f48/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f", size = 265636 }, + { url = "https://files.pythonhosted.org/packages/80/cf/e075e407fc2ae7328155a1cd7e22f932773c8073c1fc78016607d19cc3e5/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608", size = 270214 }, + { url = "https://files.pythonhosted.org/packages/a1/58/0642d061d5de779f39c50cbb00df49682832923f3d2ebfb0fedf02d05f7f/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b", size = 273905 }, + { url = "https://files.pythonhosted.org/packages/ab/66/3fe0f5f8f2add5b4ab7aa4e199f767fd3b55da26e3ca4ce2cc36698e50c4/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840", size = 250542 }, + { url = "https://files.pythonhosted.org/packages/f6/b8/260791bde9198c87a465224e0e2bb62c4e716f5d198fc3a1dacc4895dbd1/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439", size = 267026 }, + { url = "https://files.pythonhosted.org/packages/2e/a4/3d24f88c527f08f8d44ade24eaee83b2627793fa62fa07cbb7ff7a2f7d42/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de", size = 257690 }, + { url = "https://files.pythonhosted.org/packages/de/9a/d311d660420b2beeff3459b6626f2ab4fb236d07afbdac034a4371fe696e/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641", size = 253893 }, + { url = "https://files.pythonhosted.org/packages/c6/23/e491aadc25b56eabd0f18c53bb19f3cdc6de30b2129ee0bc39cd387cd560/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e", size = 267006 }, + { url = "https://files.pythonhosted.org/packages/08/c4/ab918ce636a35fb974d13d666dcbe03969592aeca6c3ab3835acff01f79c/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9", size = 276157 }, + { url = "https://files.pythonhosted.org/packages/c0/29/3b7a0bbbbe5a34833ba26f686aabfe982924adbdcafdc294a7a129c31688/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03", size = 264642 }, + { url = "https://files.pythonhosted.org/packages/ab/42/0595b3dbffc2e82d7fe658c12d5a5bafcd7516c6bf2d1d1feb5387caa9c1/frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c", size = 44914 }, + { url = "https://files.pythonhosted.org/packages/17/c4/b7db1206a3fea44bf3b838ca61deb6f74424a8a5db1dd53ecb21da669be6/frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28", size = 51167 }, + { url = "https://files.pythonhosted.org/packages/c6/c8/a5be5b7550c10858fcf9b0ea054baccab474da77d37f1e828ce043a3a5d4/frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", size = 11901 }, +] + +[[package]] +name = "fsspec" +version = "2025.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/34/f4/5721faf47b8c499e776bc34c6a8fc17efdf7fdef0b00f398128bc5dcb4ac/fsspec-2025.3.0.tar.gz", hash = "sha256:a935fd1ea872591f2b5148907d103488fc523295e6c64b835cfad8c3eca44972", size = 298491 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/53/eb690efa8513166adef3e0669afd31e95ffde69fb3c52ec2ac7223ed6018/fsspec-2025.3.0-py3-none-any.whl", hash = "sha256:efb87af3efa9103f94ca91a7f8cb7a4df91af9f74fc106c9c7ea0efd7277c1b3", size = 193615 }, +] + +[[package]] +name = "geopandas" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyogrio" }, + { name = "pyproj" }, + { name = "shapely" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/08/2cf5d85356e45b10b8d066cf4c3ba1e9e3185423c48104eed87e8afd0455/geopandas-1.0.1.tar.gz", hash = "sha256:b8bf70a5534588205b7a56646e2082fb1de9a03599651b3d80c99ea4c2ca08ab", size = 317736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/64/7d344cfcef5efddf9cf32f59af7f855828e9d74b5f862eddf5bfd9f25323/geopandas-1.0.1-py3-none-any.whl", hash = "sha256:01e147d9420cc374d26f51fc23716ac307f32b49406e4bd8462c07e82ed1d3d6", size = 323587 }, +] + +[[package]] +name = "greenlet" +version = "3.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/92/bb85bd6e80148a4d2e0c59f7c0c2891029f8fd510183afc7d8d2feeed9b6/greenlet-3.2.3.tar.gz", hash = "sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365", size = 185752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/94/ad0d435f7c48debe960c53b8f60fb41c2026b1d0fa4a99a1cb17c3461e09/greenlet-3.2.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:25ad29caed5783d4bd7a85c9251c651696164622494c00802a139c00d639242d", size = 271992 }, + { url = "https://files.pythonhosted.org/packages/93/5d/7c27cf4d003d6e77749d299c7c8f5fd50b4f251647b5c2e97e1f20da0ab5/greenlet-3.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88cd97bf37fe24a6710ec6a3a7799f3f81d9cd33317dcf565ff9950c83f55e0b", size = 638820 }, + { url = "https://files.pythonhosted.org/packages/c6/7e/807e1e9be07a125bb4c169144937910bf59b9d2f6d931578e57f0bce0ae2/greenlet-3.2.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:baeedccca94880d2f5666b4fa16fc20ef50ba1ee353ee2d7092b383a243b0b0d", size = 653046 }, + { url = "https://files.pythonhosted.org/packages/9d/ab/158c1a4ea1068bdbc78dba5a3de57e4c7aeb4e7fa034320ea94c688bfb61/greenlet-3.2.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:be52af4b6292baecfa0f397f3edb3c6092ce071b499dd6fe292c9ac9f2c8f264", size = 647701 }, + { url = "https://files.pythonhosted.org/packages/cc/0d/93729068259b550d6a0288da4ff72b86ed05626eaf1eb7c0d3466a2571de/greenlet-3.2.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0cc73378150b8b78b0c9fe2ce56e166695e67478550769536a6742dca3651688", size = 649747 }, + { url = "https://files.pythonhosted.org/packages/f6/f6/c82ac1851c60851302d8581680573245c8fc300253fc1ff741ae74a6c24d/greenlet-3.2.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:706d016a03e78df129f68c4c9b4c4f963f7d73534e48a24f5f5a7101ed13dbbb", size = 605461 }, + { url = "https://files.pythonhosted.org/packages/98/82/d022cf25ca39cf1200650fc58c52af32c90f80479c25d1cbf57980ec3065/greenlet-3.2.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:419e60f80709510c343c57b4bb5a339d8767bf9aef9b8ce43f4f143240f88b7c", size = 1121190 }, + { url = "https://files.pythonhosted.org/packages/f5/e1/25297f70717abe8104c20ecf7af0a5b82d2f5a980eb1ac79f65654799f9f/greenlet-3.2.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:93d48533fade144203816783373f27a97e4193177ebaaf0fc396db19e5d61163", size = 1149055 }, + { url = "https://files.pythonhosted.org/packages/1f/8f/8f9e56c5e82eb2c26e8cde787962e66494312dc8cb261c460e1f3a9c88bc/greenlet-3.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:7454d37c740bb27bdeddfc3f358f26956a07d5220818ceb467a483197d84f849", size = 297817 }, + { url = "https://files.pythonhosted.org/packages/b1/cf/f5c0b23309070ae93de75c90d29300751a5aacefc0a3ed1b1d8edb28f08b/greenlet-3.2.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:500b8689aa9dd1ab26872a34084503aeddefcb438e2e7317b89b11eaea1901ad", size = 270732 }, + { url = "https://files.pythonhosted.org/packages/48/ae/91a957ba60482d3fecf9be49bc3948f341d706b52ddb9d83a70d42abd498/greenlet-3.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a07d3472c2a93117af3b0136f246b2833fdc0b542d4a9799ae5f41c28323faef", size = 639033 }, + { url = "https://files.pythonhosted.org/packages/6f/df/20ffa66dd5a7a7beffa6451bdb7400d66251374ab40b99981478c69a67a8/greenlet-3.2.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:8704b3768d2f51150626962f4b9a9e4a17d2e37c8a8d9867bbd9fa4eb938d3b3", size = 652999 }, + { url = "https://files.pythonhosted.org/packages/51/b4/ebb2c8cb41e521f1d72bf0465f2f9a2fd803f674a88db228887e6847077e/greenlet-3.2.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5035d77a27b7c62db6cf41cf786cfe2242644a7a337a0e155c80960598baab95", size = 647368 }, + { url = "https://files.pythonhosted.org/packages/8e/6a/1e1b5aa10dced4ae876a322155705257748108b7fd2e4fae3f2a091fe81a/greenlet-3.2.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2d8aa5423cd4a396792f6d4580f88bdc6efcb9205891c9d40d20f6e670992efb", size = 650037 }, + { url = "https://files.pythonhosted.org/packages/26/f2/ad51331a157c7015c675702e2d5230c243695c788f8f75feba1af32b3617/greenlet-3.2.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c724620a101f8170065d7dded3f962a2aea7a7dae133a009cada42847e04a7b", size = 608402 }, + { url = "https://files.pythonhosted.org/packages/26/bc/862bd2083e6b3aff23300900a956f4ea9a4059de337f5c8734346b9b34fc/greenlet-3.2.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:873abe55f134c48e1f2a6f53f7d1419192a3d1a4e873bace00499a4e45ea6af0", size = 1119577 }, + { url = "https://files.pythonhosted.org/packages/86/94/1fc0cc068cfde885170e01de40a619b00eaa8f2916bf3541744730ffb4c3/greenlet-3.2.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:024571bbce5f2c1cfff08bf3fbaa43bbc7444f580ae13b0099e95d0e6e67ed36", size = 1147121 }, + { url = "https://files.pythonhosted.org/packages/27/1a/199f9587e8cb08a0658f9c30f3799244307614148ffe8b1e3aa22f324dea/greenlet-3.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5195fb1e75e592dd04ce79881c8a22becdfa3e6f500e7feb059b1e6fdd54d3e3", size = 297603 }, + { url = "https://files.pythonhosted.org/packages/d8/ca/accd7aa5280eb92b70ed9e8f7fd79dc50a2c21d8c73b9a0856f5b564e222/greenlet-3.2.3-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:3d04332dddb10b4a211b68111dabaee2e1a073663d117dc10247b5b1642bac86", size = 271479 }, + { url = "https://files.pythonhosted.org/packages/55/71/01ed9895d9eb49223280ecc98a557585edfa56b3d0e965b9fa9f7f06b6d9/greenlet-3.2.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8186162dffde068a465deab08fc72c767196895c39db26ab1c17c0b77a6d8b97", size = 683952 }, + { url = "https://files.pythonhosted.org/packages/ea/61/638c4bdf460c3c678a0a1ef4c200f347dff80719597e53b5edb2fb27ab54/greenlet-3.2.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f4bfbaa6096b1b7a200024784217defedf46a07c2eee1a498e94a1b5f8ec5728", size = 696917 }, + { url = "https://files.pythonhosted.org/packages/22/cc/0bd1a7eb759d1f3e3cc2d1bc0f0b487ad3cc9f34d74da4b80f226fde4ec3/greenlet-3.2.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:ed6cfa9200484d234d8394c70f5492f144b20d4533f69262d530a1a082f6ee9a", size = 692443 }, + { url = "https://files.pythonhosted.org/packages/67/10/b2a4b63d3f08362662e89c103f7fe28894a51ae0bc890fabf37d1d780e52/greenlet-3.2.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:02b0df6f63cd15012bed5401b47829cfd2e97052dc89da3cfaf2c779124eb892", size = 692995 }, + { url = "https://files.pythonhosted.org/packages/5a/c6/ad82f148a4e3ce9564056453a71529732baf5448ad53fc323e37efe34f66/greenlet-3.2.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86c2d68e87107c1792e2e8d5399acec2487a4e993ab76c792408e59394d52141", size = 655320 }, + { url = "https://files.pythonhosted.org/packages/5c/4f/aab73ecaa6b3086a4c89863d94cf26fa84cbff63f52ce9bc4342b3087a06/greenlet-3.2.3-cp314-cp314-win_amd64.whl", hash = "sha256:8c47aae8fbbfcf82cc13327ae802ba13c9c36753b67e760023fd116bc124a62a", size = 301236 }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, +] + +[[package]] +name = "h2" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hpack" }, + { name = "hyperframe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/38/d7f80fd13e6582fb8e0df8c9a653dcc02b03ca34f4d72f34869298c5baf8/h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f", size = 2150682 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0", size = 60957 }, +] + +[[package]] +name = "h5netcdf" +version = "1.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h5py" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/9b/16f61d006227059451ad7388235a547a2dcf6f36503545d06df36bb4da97/h5netcdf-1.6.3.tar.gz", hash = "sha256:a8fededcc30f933389168ece94963bee54545546772d9e4dccadbb990dd5651e", size = 65622 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/27/ca28b77f4cf613282e4ea9bac360cdfa8db8b4d2154e2f0bd82fd26baeb3/h5netcdf-1.6.3-py3-none-any.whl", hash = "sha256:b79bc24d1f8b1cdc1f16b213753209411e08af04e35b0bcf5ce5cf7fb4023572", size = 50531 }, +] + +[[package]] +name = "h5py" +version = "3.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/57/dfb3c5c3f1bf5f5ef2e59a22dec4ff1f3d7408b55bfcefcfb0ea69ef21c6/h5py-3.14.0.tar.gz", hash = "sha256:2372116b2e0d5d3e5e705b7f663f7c8d96fa79a4052d250484ef91d24d6a08f4", size = 424323 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/77/8f651053c1843391e38a189ccf50df7e261ef8cd8bfd8baba0cbe694f7c3/h5py-3.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e0045115d83272090b0717c555a31398c2c089b87d212ceba800d3dc5d952e23", size = 3312740 }, + { url = "https://files.pythonhosted.org/packages/ff/10/20436a6cf419b31124e59fefc78d74cb061ccb22213226a583928a65d715/h5py-3.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6da62509b7e1d71a7d110478aa25d245dd32c8d9a1daee9d2a42dba8717b047a", size = 2829207 }, + { url = "https://files.pythonhosted.org/packages/3f/19/c8bfe8543bfdd7ccfafd46d8cfd96fce53d6c33e9c7921f375530ee1d39a/h5py-3.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554ef0ced3571366d4d383427c00c966c360e178b5fb5ee5bb31a435c424db0c", size = 4708455 }, + { url = "https://files.pythonhosted.org/packages/86/f9/f00de11c82c88bfc1ef22633557bfba9e271e0cb3189ad704183fc4a2644/h5py-3.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cbd41f4e3761f150aa5b662df991868ca533872c95467216f2bec5fcad84882", size = 4929422 }, + { url = "https://files.pythonhosted.org/packages/7a/6d/6426d5d456f593c94b96fa942a9b3988ce4d65ebaf57d7273e452a7222e8/h5py-3.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:bf4897d67e613ecf5bdfbdab39a1158a64df105827da70ea1d90243d796d367f", size = 2862845 }, + { url = "https://files.pythonhosted.org/packages/6c/c2/7efe82d09ca10afd77cd7c286e42342d520c049a8c43650194928bcc635c/h5py-3.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:aa4b7bbce683379b7bf80aaba68e17e23396100336a8d500206520052be2f812", size = 3289245 }, + { url = "https://files.pythonhosted.org/packages/4f/31/f570fab1239b0d9441024b92b6ad03bb414ffa69101a985e4c83d37608bd/h5py-3.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9603a501a04fcd0ba28dd8f0995303d26a77a980a1f9474b3417543d4c6174", size = 2807335 }, + { url = "https://files.pythonhosted.org/packages/0d/ce/3a21d87896bc7e3e9255e0ad5583ae31ae9e6b4b00e0bcb2a67e2b6acdbc/h5py-3.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8cbaf6910fa3983c46172666b0b8da7b7bd90d764399ca983236f2400436eeb", size = 4700675 }, + { url = "https://files.pythonhosted.org/packages/e7/ec/86f59025306dcc6deee5fda54d980d077075b8d9889aac80f158bd585f1b/h5py-3.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d90e6445ab7c146d7f7981b11895d70bc1dd91278a4f9f9028bc0c95e4a53f13", size = 4921632 }, + { url = "https://files.pythonhosted.org/packages/3f/6d/0084ed0b78d4fd3e7530c32491f2884140d9b06365dac8a08de726421d4a/h5py-3.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:ae18e3de237a7a830adb76aaa68ad438d85fe6e19e0d99944a3ce46b772c69b3", size = 2852929 }, +] + +[[package]] +name = "hexbytes" +version = "0.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/94/fbfd526e8964652eec6a7b74ae18d1426e225ab602553858531ec6567d05/hexbytes-0.3.1.tar.gz", hash = "sha256:a3fe35c6831ee8fafd048c4c086b986075fc14fd46258fa24ecb8d65745f9a9d", size = 6188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/9e/fdfe374c28d448a58563e7e43f569f8cf8cf600db092efac2e8ac2f86782/hexbytes-0.3.1-py3-none-any.whl", hash = "sha256:383595ad75026cf00abd570f44b368c6cdac0c6becfae5c39ff88829877f8a59", size = 5944 }, +] + +[[package]] +name = "hpack" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357 }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[package.optional-dependencies] +http2 = [ + { name = "h2" }, +] + +[[package]] +name = "hyperframe" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007 }, +] + +[[package]] +name = "identify" +version = "2.6.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/98/a71ab060daec766acc30fb47dfca219d03de34a70d616a79a38c6066c5bf/identify-2.6.9.tar.gz", hash = "sha256:d40dfe3142a1421d8518e3d3985ef5ac42890683e32306ad614a29490abeb6bf", size = 99249 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/ce/0845144ed1f0e25db5e7a79c2354c1da4b5ce392b8966449d5db8dca18f1/identify-2.6.9-py2.py3-none-any.whl", hash = "sha256:c98b4322da415a8e5a70ff6e51fbc2d2932c015532d77e9f8537b4ba7813b150", size = 99101 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "ijson" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/4f/1cfeada63f5fce87536651268ddf5cca79b8b4bbb457aee4e45777964a0a/ijson-3.4.0.tar.gz", hash = "sha256:5f74dcbad9d592c428d3ca3957f7115a42689ee7ee941458860900236ae9bb13", size = 65782 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/ec/317ee5b2d13e50448833ead3aa906659a32b376191f6abc2a7c6112d2b27/ijson-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:956b148f88259a80a9027ffbe2d91705fae0c004fbfba3e5a24028fbe72311a9", size = 87212 }, + { url = "https://files.pythonhosted.org/packages/f8/43/b06c96ced30cacecc5d518f89b0fd1c98c294a30ff88848b70ed7b7f72a1/ijson-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06b89960f5c721106394c7fba5760b3f67c515b8eb7d80f612388f5eca2f4621", size = 59175 }, + { url = "https://files.pythonhosted.org/packages/e9/df/b4aeafb7ecde463130840ee9be36130823ec94a00525049bf700883378b8/ijson-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a0bb591cf250dd7e9dfab69d634745a7f3272d31cfe879f9156e0a081fd97ee", size = 59011 }, + { url = "https://files.pythonhosted.org/packages/e3/7c/a80b8e361641609507f62022089626d4b8067f0826f51e1c09e4ba86eba8/ijson-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72e92de999977f4c6b660ffcf2b8d59604ccd531edcbfde05b642baf283e0de8", size = 146094 }, + { url = "https://files.pythonhosted.org/packages/01/44/fa416347b9a802e3646c6ff377fc3278bd7d6106e17beb339514b6a3184e/ijson-3.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e9602157a5b869d44b6896e64f502c712a312fcde044c2e586fccb85d3e316e", size = 137903 }, + { url = "https://files.pythonhosted.org/packages/24/c6/41a9ad4d42df50ff6e70fdce79b034f09b914802737ebbdc141153d8d791/ijson-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e83660edb931a425b7ff662eb49db1f10d30ca6d4d350e5630edbed098bc01", size = 148339 }, + { url = "https://files.pythonhosted.org/packages/5f/6f/7d01efda415b8502dce67e067ed9e8a124f53e763002c02207e542e1a2f1/ijson-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:49bf8eac1c7b7913073865a859c215488461f7591b4fa6a33c14b51cb73659d0", size = 149383 }, + { url = "https://files.pythonhosted.org/packages/95/6c/0d67024b9ecb57916c5e5ab0350251c9fe2f86dc9c8ca2b605c194bdad6a/ijson-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:160b09273cb42019f1811469508b0a057d19f26434d44752bde6f281da6d3f32", size = 141580 }, + { url = "https://files.pythonhosted.org/packages/06/43/e10edcc1c6a3b619294de835e7678bfb3a1b8a75955f3689fd66a1e9e7b4/ijson-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2019ff4e6f354aa00c76c8591bd450899111c61f2354ad55cc127e2ce2492c44", size = 150280 }, + { url = "https://files.pythonhosted.org/packages/07/84/1cbeee8e8190a1ebe6926569a92cf1fa80ddb380c129beb6f86559e1bb24/ijson-3.4.0-cp312-cp312-win32.whl", hash = "sha256:931c007bf6bb8330705429989b2deed6838c22b63358a330bf362b6e458ba0bf", size = 51512 }, + { url = "https://files.pythonhosted.org/packages/66/13/530802bc391c95be6fe9f96e9aa427d94067e7c0b7da7a9092344dc44c4b/ijson-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:71523f2b64cb856a820223e94d23e88369f193017ecc789bb4de198cc9d349eb", size = 54081 }, + { url = "https://files.pythonhosted.org/packages/77/b3/b1d2eb2745e5204ec7a25365a6deb7868576214feb5e109bce368fb692c9/ijson-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e8d96f88d75196a61c9d9443de2b72c2d4a7ba9456ff117b57ae3bba23a54256", size = 87216 }, + { url = "https://files.pythonhosted.org/packages/b1/cd/cd6d340087617f8cc9bedbb21d974542fe2f160ed0126b8288d3499a469b/ijson-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c45906ce2c1d3b62f15645476fc3a6ca279549127f01662a39ca5ed334a00cf9", size = 59170 }, + { url = "https://files.pythonhosted.org/packages/3e/4d/32d3a9903b488d3306e3c8288f6ee4217d2eea82728261db03a1045eb5d1/ijson-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4ab4bc2119b35c4363ea49f29563612237cae9413d2fbe54b223be098b97bc9e", size = 59013 }, + { url = "https://files.pythonhosted.org/packages/d5/c8/db15465ab4b0b477cee5964c8bfc94bf8c45af8e27a23e1ad78d1926e587/ijson-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97b0a9b5a15e61dfb1f14921ea4e0dba39f3a650df6d8f444ddbc2b19b479ff1", size = 146564 }, + { url = "https://files.pythonhosted.org/packages/c4/d8/0755545bc122473a9a434ab90e0f378780e603d75495b1ca3872de757873/ijson-3.4.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3047bb994dabedf11de11076ed1147a307924b6e5e2df6784fb2599c4ad8c60", size = 137917 }, + { url = "https://files.pythonhosted.org/packages/d0/c6/aeb89c8939ebe3f534af26c8c88000c5e870dbb6ae33644c21a4531f87d2/ijson-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68c83161b052e9f5dc8191acbc862bb1e63f8a35344cb5cd0db1afd3afd487a6", size = 148897 }, + { url = "https://files.pythonhosted.org/packages/be/0e/7ef6e9b372106f2682a4a32b3c65bf86bb471a1670e4dac242faee4a7d3f/ijson-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1eebd9b6c20eb1dffde0ae1f0fbb4aeacec2eb7b89adb5c7c0449fc9fd742760", size = 149711 }, + { url = "https://files.pythonhosted.org/packages/d1/5d/9841c3ed75bcdabf19b3202de5f862a9c9c86ce5c7c9d95fa32347fdbf5f/ijson-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:13fb6d5c35192c541421f3ee81239d91fc15a8d8f26c869250f941f4b346a86c", size = 141691 }, + { url = "https://files.pythonhosted.org/packages/d5/d2/ce74e17218dba292e9be10a44ed0c75439f7958cdd263adb0b5b92d012d5/ijson-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:28b7196ff7b37c4897c547a28fa4876919696739fc91c1f347651c9736877c69", size = 150738 }, + { url = "https://files.pythonhosted.org/packages/4e/43/dcc480f94453b1075c9911d4755b823f3ace275761bb37b40139f22109ca/ijson-3.4.0-cp313-cp313-win32.whl", hash = "sha256:3c2691d2da42629522140f77b99587d6f5010440d58d36616f33bc7bdc830cc3", size = 51512 }, + { url = "https://files.pythonhosted.org/packages/35/dd/d8c5f15efd85ba51e6e11451ebe23d779361a9ec0d192064c2a8c3cdfcb8/ijson-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:c4554718c275a044c47eb3874f78f2c939f300215d9031e785a6711cc51b83fc", size = 54074 }, + { url = "https://files.pythonhosted.org/packages/79/73/24ad8cd106203419c4d22bed627e02e281d66b83e91bc206a371893d0486/ijson-3.4.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:915a65e3f3c0eee2ea937bc62aaedb6c14cc1e8f0bb9f3f4fb5a9e2bbfa4b480", size = 91694 }, + { url = "https://files.pythonhosted.org/packages/17/2d/f7f680984bcb7324a46a4c2df3bd73cf70faef0acfeb85a3f811abdfd590/ijson-3.4.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:afbe9748707684b6c5adc295c4fdcf27765b300aec4d484e14a13dca4e5c0afa", size = 61390 }, + { url = "https://files.pythonhosted.org/packages/09/a1/f3ca7bab86f95bdb82494739e71d271410dfefce4590785d511669127145/ijson-3.4.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d823f8f321b4d8d5fa020d0a84f089fec5d52b7c0762430476d9f8bf95bbc1a9", size = 61140 }, + { url = "https://files.pythonhosted.org/packages/51/79/dd340df3d4fc7771c95df29997956b92ed0570fe7b616d1792fea9ad93f2/ijson-3.4.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a0a2c54f3becf76881188beefd98b484b1d3bd005769a740d5b433b089fa23", size = 214739 }, + { url = "https://files.pythonhosted.org/packages/59/f0/85380b7f51d1f5fb7065d76a7b623e02feca920cc678d329b2eccc0011e0/ijson-3.4.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ced19a83ab09afa16257a0b15bc1aa888dbc555cb754be09d375c7f8d41051f2", size = 198338 }, + { url = "https://files.pythonhosted.org/packages/a5/cd/313264cf2ec42e0f01d198c49deb7b6fadeb793b3685e20e738eb6b3fa13/ijson-3.4.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8100f9885eff1f38d35cef80ef759a1bbf5fc946349afa681bd7d0e681b7f1a0", size = 207515 }, + { url = "https://files.pythonhosted.org/packages/12/94/bf14457aa87ea32641f2db577c9188ef4e4ae373478afef422b31fc7f309/ijson-3.4.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d7bcc3f7f21b0f703031ecd15209b1284ea51b2a329d66074b5261de3916c1eb", size = 210081 }, + { url = "https://files.pythonhosted.org/packages/7d/b4/eaee39e290e40e52d665db9bd1492cfdce86bd1e47948e0440db209c6023/ijson-3.4.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2dcb190227b09dd171bdcbfe4720fddd574933c66314818dfb3960c8a6246a77", size = 199253 }, + { url = "https://files.pythonhosted.org/packages/c5/9c/e09c7b9ac720a703ab115b221b819f149ed54c974edfff623c1e925e57da/ijson-3.4.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:eda4cfb1d49c6073a901735aaa62e39cb7ab47f3ad7bb184862562f776f1fa8a", size = 203816 }, + { url = "https://files.pythonhosted.org/packages/7c/14/acd304f412e32d16a2c12182b9d78206bb0ae35354d35664f45db05c1b3b/ijson-3.4.0-cp313-cp313t-win32.whl", hash = "sha256:0772638efa1f3b72b51736833404f1cbd2f5beeb9c1a3d392e7d385b9160cba7", size = 53760 }, + { url = "https://files.pythonhosted.org/packages/2f/24/93dd0a467191590a5ed1fc2b35842bca9d09900d001e00b0b497c0208ef6/ijson-3.4.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3d8a0d67f36e4fb97c61a724456ef0791504b16ce6f74917a31c2e92309bbeb9", size = 56948 }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, +] + +[[package]] +name = "ipython" +version = "8.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864 }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "jmespath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256 }, +] + +[[package]] +name = "jsonschema" +version = "4.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/d3/1cf5326b923a53515d8f3a2cd442e6d7e94fcc444716e879ea70a0ce3177/jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196", size = 353480 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/3d/023389198f69c722d039351050738d6755376c8fd343e91dc493ea485905/jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d", size = 88709 }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437 }, +] + +[[package]] +name = "kiwisolver" +version = "1.4.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", size = 97538 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/aa/cea685c4ab647f349c3bc92d2daf7ae34c8e8cf405a6dcd3a497f58a2ac3/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502", size = 124152 }, + { url = "https://files.pythonhosted.org/packages/c5/0b/8db6d2e2452d60d5ebc4ce4b204feeb16176a851fd42462f66ade6808084/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31", size = 66555 }, + { url = "https://files.pythonhosted.org/packages/60/26/d6a0db6785dd35d3ba5bf2b2df0aedc5af089962c6eb2cbf67a15b81369e/kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb", size = 65067 }, + { url = "https://files.pythonhosted.org/packages/c9/ed/1d97f7e3561e09757a196231edccc1bcf59d55ddccefa2afc9c615abd8e0/kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f", size = 1378443 }, + { url = "https://files.pythonhosted.org/packages/29/61/39d30b99954e6b46f760e6289c12fede2ab96a254c443639052d1b573fbc/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc", size = 1472728 }, + { url = "https://files.pythonhosted.org/packages/0c/3e/804163b932f7603ef256e4a715e5843a9600802bb23a68b4e08c8c0ff61d/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a", size = 1478388 }, + { url = "https://files.pythonhosted.org/packages/8a/9e/60eaa75169a154700be74f875a4d9961b11ba048bef315fbe89cb6999056/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a", size = 1413849 }, + { url = "https://files.pythonhosted.org/packages/bc/b3/9458adb9472e61a998c8c4d95cfdfec91c73c53a375b30b1428310f923e4/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a", size = 1475533 }, + { url = "https://files.pythonhosted.org/packages/e4/7a/0a42d9571e35798de80aef4bb43a9b672aa7f8e58643d7bd1950398ffb0a/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3", size = 2268898 }, + { url = "https://files.pythonhosted.org/packages/d9/07/1255dc8d80271400126ed8db35a1795b1a2c098ac3a72645075d06fe5c5d/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b", size = 2425605 }, + { url = "https://files.pythonhosted.org/packages/84/df/5a3b4cf13780ef6f6942df67b138b03b7e79e9f1f08f57c49957d5867f6e/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4", size = 2375801 }, + { url = "https://files.pythonhosted.org/packages/8f/10/2348d068e8b0f635c8c86892788dac7a6b5c0cb12356620ab575775aad89/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d", size = 2520077 }, + { url = "https://files.pythonhosted.org/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", size = 2338410 }, + { url = "https://files.pythonhosted.org/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", size = 71853 }, + { url = "https://files.pythonhosted.org/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", size = 65424 }, + { url = "https://files.pythonhosted.org/packages/79/b3/e62464a652f4f8cd9006e13d07abad844a47df1e6537f73ddfbf1bc997ec/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09", size = 124156 }, + { url = "https://files.pythonhosted.org/packages/8d/2d/f13d06998b546a2ad4f48607a146e045bbe48030774de29f90bdc573df15/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1", size = 66555 }, + { url = "https://files.pythonhosted.org/packages/59/e3/b8bd14b0a54998a9fd1e8da591c60998dc003618cb19a3f94cb233ec1511/kiwisolver-1.4.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c", size = 65071 }, + { url = "https://files.pythonhosted.org/packages/f0/1c/6c86f6d85ffe4d0ce04228d976f00674f1df5dc893bf2dd4f1928748f187/kiwisolver-1.4.8-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b", size = 1378053 }, + { url = "https://files.pythonhosted.org/packages/4e/b9/1c6e9f6dcb103ac5cf87cb695845f5fa71379021500153566d8a8a9fc291/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47", size = 1472278 }, + { url = "https://files.pythonhosted.org/packages/ee/81/aca1eb176de671f8bda479b11acdc42c132b61a2ac861c883907dde6debb/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16", size = 1478139 }, + { url = "https://files.pythonhosted.org/packages/49/f4/e081522473671c97b2687d380e9e4c26f748a86363ce5af48b4a28e48d06/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc", size = 1413517 }, + { url = "https://files.pythonhosted.org/packages/8f/e9/6a7d025d8da8c4931522922cd706105aa32b3291d1add8c5427cdcd66e63/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246", size = 1474952 }, + { url = "https://files.pythonhosted.org/packages/82/13/13fa685ae167bee5d94b415991c4fc7bb0a1b6ebea6e753a87044b209678/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794", size = 2269132 }, + { url = "https://files.pythonhosted.org/packages/ef/92/bb7c9395489b99a6cb41d502d3686bac692586db2045adc19e45ee64ed23/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b", size = 2425997 }, + { url = "https://files.pythonhosted.org/packages/ed/12/87f0e9271e2b63d35d0d8524954145837dd1a6c15b62a2d8c1ebe0f182b4/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3", size = 2376060 }, + { url = "https://files.pythonhosted.org/packages/02/6e/c8af39288edbce8bf0fa35dee427b082758a4b71e9c91ef18fa667782138/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957", size = 2520471 }, + { url = "https://files.pythonhosted.org/packages/13/78/df381bc7b26e535c91469f77f16adcd073beb3e2dd25042efd064af82323/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb", size = 2338793 }, + { url = "https://files.pythonhosted.org/packages/d0/dc/c1abe38c37c071d0fc71c9a474fd0b9ede05d42f5a458d584619cfd2371a/kiwisolver-1.4.8-cp313-cp313-win_amd64.whl", hash = "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2", size = 71855 }, + { url = "https://files.pythonhosted.org/packages/a0/b6/21529d595b126ac298fdd90b705d87d4c5693de60023e0efcb4f387ed99e/kiwisolver-1.4.8-cp313-cp313-win_arm64.whl", hash = "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30", size = 65430 }, + { url = "https://files.pythonhosted.org/packages/34/bd/b89380b7298e3af9b39f49334e3e2a4af0e04819789f04b43d560516c0c8/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c", size = 126294 }, + { url = "https://files.pythonhosted.org/packages/83/41/5857dc72e5e4148eaac5aa76e0703e594e4465f8ab7ec0fc60e3a9bb8fea/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc", size = 67736 }, + { url = "https://files.pythonhosted.org/packages/e1/d1/be059b8db56ac270489fb0b3297fd1e53d195ba76e9bbb30e5401fa6b759/kiwisolver-1.4.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712", size = 66194 }, + { url = "https://files.pythonhosted.org/packages/e1/83/4b73975f149819eb7dcf9299ed467eba068ecb16439a98990dcb12e63fdd/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e", size = 1465942 }, + { url = "https://files.pythonhosted.org/packages/c7/2c/30a5cdde5102958e602c07466bce058b9d7cb48734aa7a4327261ac8e002/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880", size = 1595341 }, + { url = "https://files.pythonhosted.org/packages/ff/9b/1e71db1c000385aa069704f5990574b8244cce854ecd83119c19e83c9586/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062", size = 1598455 }, + { url = "https://files.pythonhosted.org/packages/85/92/c8fec52ddf06231b31cbb779af77e99b8253cd96bd135250b9498144c78b/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7", size = 1522138 }, + { url = "https://files.pythonhosted.org/packages/0b/51/9eb7e2cd07a15d8bdd976f6190c0164f92ce1904e5c0c79198c4972926b7/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed", size = 1582857 }, + { url = "https://files.pythonhosted.org/packages/0f/95/c5a00387a5405e68ba32cc64af65ce881a39b98d73cc394b24143bebc5b8/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d", size = 2293129 }, + { url = "https://files.pythonhosted.org/packages/44/83/eeb7af7d706b8347548313fa3a3a15931f404533cc54fe01f39e830dd231/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165", size = 2421538 }, + { url = "https://files.pythonhosted.org/packages/05/f9/27e94c1b3eb29e6933b6986ffc5fa1177d2cd1f0c8efc5f02c91c9ac61de/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6", size = 2390661 }, + { url = "https://files.pythonhosted.org/packages/d9/d4/3c9735faa36ac591a4afcc2980d2691000506050b7a7e80bcfe44048daa7/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90", size = 2546710 }, + { url = "https://files.pythonhosted.org/packages/4c/fa/be89a49c640930180657482a74970cdcf6f7072c8d2471e1babe17a222dc/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85", size = 2349213 }, +] + +[[package]] +name = "lazyasd" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/15/c9f68c4f477fbaa9fc5ea5e271547956da321787cf7aef79d6971dcb4c84/lazyasd-0.1.4.tar.gz", hash = "sha256:a3196f05cff27f952ad05767e5735fd564b4ea4e89b23f5ea1887229c3db145b", size = 8380 } + +[[package]] +name = "llvmlite" +version = "0.44.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/89/6a/95a3d3610d5c75293d5dbbb2a76480d5d4eeba641557b69fe90af6c5b84e/llvmlite-0.44.0.tar.gz", hash = "sha256:07667d66a5d150abed9157ab6c0b9393c9356f229784a4385c02f99e94fc94d4", size = 171880 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/86/e3c3195b92e6e492458f16d233e58a1a812aa2bfbef9bdd0fbafcec85c60/llvmlite-0.44.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:1d671a56acf725bf1b531d5ef76b86660a5ab8ef19bb6a46064a705c6ca80aad", size = 28132297 }, + { url = "https://files.pythonhosted.org/packages/d6/53/373b6b8be67b9221d12b24125fd0ec56b1078b660eeae266ec388a6ac9a0/llvmlite-0.44.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f79a728e0435493611c9f405168682bb75ffd1fbe6fc360733b850c80a026db", size = 26201105 }, + { url = "https://files.pythonhosted.org/packages/cb/da/8341fd3056419441286c8e26bf436923021005ece0bff5f41906476ae514/llvmlite-0.44.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0143a5ef336da14deaa8ec26c5449ad5b6a2b564df82fcef4be040b9cacfea9", size = 42361901 }, + { url = "https://files.pythonhosted.org/packages/53/ad/d79349dc07b8a395a99153d7ce8b01d6fcdc9f8231355a5df55ded649b61/llvmlite-0.44.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d752f89e31b66db6f8da06df8b39f9b91e78c5feea1bf9e8c1fba1d1c24c065d", size = 41184247 }, + { url = "https://files.pythonhosted.org/packages/e2/3b/a9a17366af80127bd09decbe2a54d8974b6d8b274b39bf47fbaedeec6307/llvmlite-0.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:eae7e2d4ca8f88f89d315b48c6b741dcb925d6a1042da694aa16ab3dd4cbd3a1", size = 30332380 }, + { url = "https://files.pythonhosted.org/packages/89/24/4c0ca705a717514c2092b18476e7a12c74d34d875e05e4d742618ebbf449/llvmlite-0.44.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:319bddd44e5f71ae2689859b7203080716448a3cd1128fb144fe5c055219d516", size = 28132306 }, + { url = "https://files.pythonhosted.org/packages/01/cf/1dd5a60ba6aee7122ab9243fd614abcf22f36b0437cbbe1ccf1e3391461c/llvmlite-0.44.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c58867118bad04a0bb22a2e0068c693719658105e40009ffe95c7000fcde88e", size = 26201090 }, + { url = "https://files.pythonhosted.org/packages/d2/1b/656f5a357de7135a3777bd735cc7c9b8f23b4d37465505bd0eaf4be9befe/llvmlite-0.44.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46224058b13c96af1365290bdfebe9a6264ae62fb79b2b55693deed11657a8bf", size = 42361904 }, + { url = "https://files.pythonhosted.org/packages/d8/e1/12c5f20cb9168fb3464a34310411d5ad86e4163c8ff2d14a2b57e5cc6bac/llvmlite-0.44.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0097052c32bf721a4efc03bd109d335dfa57d9bffb3d4c24cc680711b8b4fc", size = 41184245 }, + { url = "https://files.pythonhosted.org/packages/d0/81/e66fc86539293282fd9cb7c9417438e897f369e79ffb62e1ae5e5154d4dd/llvmlite-0.44.0-cp313-cp313-win_amd64.whl", hash = "sha256:2fb7c4f2fb86cbae6dca3db9ab203eeea0e22d73b99bc2341cdf9de93612e930", size = 30331193 }, +] + +[[package]] +name = "locket" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/83/97b29fe05cb6ae28d2dbd30b81e2e402a3eed5f460c26e9eaa5895ceacf5/locket-1.0.0.tar.gz", hash = "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632", size = 4350 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl", hash = "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3", size = 4398 }, +] + +[[package]] +name = "lru-dict" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/63/21480e8ecc218b9b15672d194ea79da8a7389737c21d8406254306733cac/lru-dict-1.2.0.tar.gz", hash = "sha256:13c56782f19d68ddf4d8db0170041192859616514c706b126d0df2ec72a11bd7", size = 10895 } + +[[package]] +name = "lz4" +version = "4.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/5a/945f5086326d569f14c84ac6f7fcc3229f0b9b1e8cc536b951fd53dfb9e1/lz4-4.4.4.tar.gz", hash = "sha256:070fd0627ec4393011251a094e08ed9fdcc78cb4e7ab28f507638eee4e39abda", size = 171884 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/2d/5523b4fabe11cd98f040f715728d1932eb7e696bfe94391872a823332b94/lz4-4.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:23ae267494fdd80f0d2a131beff890cf857f1b812ee72dbb96c3204aab725553", size = 220669 }, + { url = "https://files.pythonhosted.org/packages/91/06/1a5bbcacbfb48d8ee5b6eb3fca6aa84143a81d92946bdb5cd6b005f1863e/lz4-4.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fff9f3a1ed63d45cb6514bfb8293005dc4141341ce3500abdfeb76124c0b9b2e", size = 189661 }, + { url = "https://files.pythonhosted.org/packages/fa/08/39eb7ac907f73e11a69a11576a75a9e36406b3241c0ba41453a7eb842abb/lz4-4.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ea7f07329f85a8eda4d8cf937b87f27f0ac392c6400f18bea2c667c8b7f8ecc", size = 1238775 }, + { url = "https://files.pythonhosted.org/packages/e9/26/05840fbd4233e8d23e88411a066ab19f1e9de332edddb8df2b6a95c7fddc/lz4-4.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ccab8f7f7b82f9fa9fc3b0ba584d353bd5aa818d5821d77d5b9447faad2aaad", size = 1265143 }, + { url = "https://files.pythonhosted.org/packages/b7/5d/5f2db18c298a419932f3ab2023deb689863cf8fd7ed875b1c43492479af2/lz4-4.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43e9d48b2daf80e486213128b0763deed35bbb7a59b66d1681e205e1702d735", size = 1185032 }, + { url = "https://files.pythonhosted.org/packages/c4/e6/736ab5f128694b0f6aac58343bcf37163437ac95997276cd0be3ea4c3342/lz4-4.4.4-cp312-cp312-win32.whl", hash = "sha256:33e01e18e4561b0381b2c33d58e77ceee850a5067f0ece945064cbaac2176962", size = 88284 }, + { url = "https://files.pythonhosted.org/packages/40/b8/243430cb62319175070e06e3a94c4c7bd186a812e474e22148ae1290d47d/lz4-4.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d21d1a2892a2dcc193163dd13eaadabb2c1b803807a5117d8f8588b22eaf9f12", size = 99918 }, + { url = "https://files.pythonhosted.org/packages/6c/e1/0686c91738f3e6c2e1a243e0fdd4371667c4d2e5009b0a3605806c2aa020/lz4-4.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:2f4f2965c98ab254feddf6b5072854a6935adab7bc81412ec4fe238f07b85f62", size = 89736 }, + { url = "https://files.pythonhosted.org/packages/3b/3c/d1d1b926d3688263893461e7c47ed7382a969a0976fc121fc678ec325fc6/lz4-4.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed6eb9f8deaf25ee4f6fad9625d0955183fdc90c52b6f79a76b7f209af1b6e54", size = 220678 }, + { url = "https://files.pythonhosted.org/packages/26/89/8783d98deb058800dabe07e6cdc90f5a2a8502a9bad8c5343c641120ace2/lz4-4.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:18ae4fe3bafb344dbd09f976d45cbf49c05c34416f2462828f9572c1fa6d5af7", size = 189670 }, + { url = "https://files.pythonhosted.org/packages/22/ab/a491ace69a83a8914a49f7391e92ca0698f11b28d5ce7b2ececa2be28e9a/lz4-4.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fd20c5fc1a49d1bbd170836fccf9a338847e73664f8e313dce6ac91b8c1e02", size = 1238746 }, + { url = "https://files.pythonhosted.org/packages/97/12/a1f2f4fdc6b7159c0d12249456f9fe454665b6126e98dbee9f2bd3cf735c/lz4-4.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9cb387c33f014dae4db8cb4ba789c8d2a0a6d045ddff6be13f6c8d9def1d2a6", size = 1265119 }, + { url = "https://files.pythonhosted.org/packages/50/6e/e22e50f5207649db6ea83cd31b79049118305be67e96bec60becf317afc6/lz4-4.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0be9f68240231e1e44118a4ebfecd8a5d4184f0bdf5c591c98dd6ade9720afd", size = 1184954 }, + { url = "https://files.pythonhosted.org/packages/4c/c4/2a458039645fcc6324ece731d4d1361c5daf960b553d1fcb4261ba07d51c/lz4-4.4.4-cp313-cp313-win32.whl", hash = "sha256:e9ec5d45ea43684f87c316542af061ef5febc6a6b322928f059ce1fb289c298a", size = 88289 }, + { url = "https://files.pythonhosted.org/packages/00/96/b8e24ea7537ab418074c226279acfcaa470e1ea8271003e24909b6db942b/lz4-4.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:a760a175b46325b2bb33b1f2bbfb8aa21b48e1b9653e29c10b6834f9bb44ead4", size = 99925 }, + { url = "https://files.pythonhosted.org/packages/a5/a5/f9838fe6aa132cfd22733ed2729d0592259fff074cefb80f19aa0607367b/lz4-4.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:f4c21648d81e0dda38b4720dccc9006ae33b0e9e7ffe88af6bf7d4ec124e2fba", size = 89743 }, ] [[package]] @@ -591,6 +1730,93 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, ] +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "matplotlib" +version = "3.10.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/91/d49359a21893183ed2a5b6c76bec40e0b1dcbf8ca148f864d134897cfc75/matplotlib-3.10.3.tar.gz", hash = "sha256:2f82d2c5bb7ae93aaaa4cd42aca65d76ce6376f83304fa3a630b569aca274df0", size = 34799811 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/43/6b80eb47d1071f234ef0c96ca370c2ca621f91c12045f1401b5c9b28a639/matplotlib-3.10.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ab1affc11d1f495ab9e6362b8174a25afc19c081ba5b0775ef00533a4236eea", size = 8179689 }, + { url = "https://files.pythonhosted.org/packages/0f/70/d61a591958325c357204870b5e7b164f93f2a8cca1dc6ce940f563909a13/matplotlib-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2a818d8bdcafa7ed2eed74487fdb071c09c1ae24152d403952adad11fa3c65b4", size = 8050466 }, + { url = "https://files.pythonhosted.org/packages/e7/75/70c9d2306203148cc7902a961240c5927dd8728afedf35e6a77e105a2985/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748ebc3470c253e770b17d8b0557f0aa85cf8c63fd52f1a61af5b27ec0b7ffee", size = 8456252 }, + { url = "https://files.pythonhosted.org/packages/c4/91/ba0ae1ff4b3f30972ad01cd4a8029e70a0ec3b8ea5be04764b128b66f763/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed70453fd99733293ace1aec568255bc51c6361cb0da94fa5ebf0649fdb2150a", size = 8601321 }, + { url = "https://files.pythonhosted.org/packages/d2/88/d636041eb54a84b889e11872d91f7cbf036b3b0e194a70fa064eb8b04f7a/matplotlib-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dbed9917b44070e55640bd13419de83b4c918e52d97561544814ba463811cbc7", size = 9406972 }, + { url = "https://files.pythonhosted.org/packages/b1/79/0d1c165eac44405a86478082e225fce87874f7198300bbebc55faaf6d28d/matplotlib-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:cf37d8c6ef1a48829443e8ba5227b44236d7fcaf7647caa3178a4ff9f7a5be05", size = 8067954 }, + { url = "https://files.pythonhosted.org/packages/3b/c1/23cfb566a74c696a3b338d8955c549900d18fe2b898b6e94d682ca21e7c2/matplotlib-3.10.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9f2efccc8dcf2b86fc4ee849eea5dcaecedd0773b30f47980dc0cbeabf26ec84", size = 8180318 }, + { url = "https://files.pythonhosted.org/packages/6c/0c/02f1c3b66b30da9ee343c343acbb6251bef5b01d34fad732446eaadcd108/matplotlib-3.10.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ddbba06a6c126e3301c3d272a99dcbe7f6c24c14024e80307ff03791a5f294e", size = 8051132 }, + { url = "https://files.pythonhosted.org/packages/b4/ab/8db1a5ac9b3a7352fb914133001dae889f9fcecb3146541be46bed41339c/matplotlib-3.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748302b33ae9326995b238f606e9ed840bf5886ebafcb233775d946aa8107a15", size = 8457633 }, + { url = "https://files.pythonhosted.org/packages/f5/64/41c4367bcaecbc03ef0d2a3ecee58a7065d0a36ae1aa817fe573a2da66d4/matplotlib-3.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a80fcccbef63302c0efd78042ea3c2436104c5b1a4d3ae20f864593696364ac7", size = 8601031 }, + { url = "https://files.pythonhosted.org/packages/12/6f/6cc79e9e5ab89d13ed64da28898e40fe5b105a9ab9c98f83abd24e46d7d7/matplotlib-3.10.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:55e46cbfe1f8586adb34f7587c3e4f7dedc59d5226719faf6cb54fc24f2fd52d", size = 9406988 }, + { url = "https://files.pythonhosted.org/packages/b1/0f/eed564407bd4d935ffabf561ed31099ed609e19287409a27b6d336848653/matplotlib-3.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:151d89cb8d33cb23345cd12490c76fd5d18a56581a16d950b48c6ff19bb2ab93", size = 8068034 }, + { url = "https://files.pythonhosted.org/packages/3e/e5/2f14791ff69b12b09e9975e1d116d9578ac684460860ce542c2588cb7a1c/matplotlib-3.10.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c26dd9834e74d164d06433dc7be5d75a1e9890b926b3e57e74fa446e1a62c3e2", size = 8218223 }, + { url = "https://files.pythonhosted.org/packages/5c/08/30a94afd828b6e02d0a52cae4a29d6e9ccfcf4c8b56cc28b021d3588873e/matplotlib-3.10.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:24853dad5b8c84c8c2390fc31ce4858b6df504156893292ce8092d190ef8151d", size = 8094985 }, + { url = "https://files.pythonhosted.org/packages/89/44/f3bc6b53066c889d7a1a3ea8094c13af6a667c5ca6220ec60ecceec2dabe/matplotlib-3.10.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68f7878214d369d7d4215e2a9075fef743be38fa401d32e6020bab2dfabaa566", size = 8483109 }, + { url = "https://files.pythonhosted.org/packages/ba/c7/473bc559beec08ebee9f86ca77a844b65747e1a6c2691e8c92e40b9f42a8/matplotlib-3.10.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6929fc618cb6db9cb75086f73b3219bbb25920cb24cee2ea7a12b04971a4158", size = 8618082 }, + { url = "https://files.pythonhosted.org/packages/d8/e9/6ce8edd264c8819e37bbed8172e0ccdc7107fe86999b76ab5752276357a4/matplotlib-3.10.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c7818292a5cc372a2dc4c795e5c356942eb8350b98ef913f7fda51fe175ac5d", size = 9413699 }, + { url = "https://files.pythonhosted.org/packages/1b/92/9a45c91089c3cf690b5badd4be81e392ff086ccca8a1d4e3a08463d8a966/matplotlib-3.10.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4f23ffe95c5667ef8a2b56eea9b53db7f43910fa4a2d5472ae0f72b64deab4d5", size = 8139044 }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, +] + [[package]] name = "mdurl" version = "0.1.2" @@ -640,6 +1866,42 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/71/4ad9a42f2772793a03cb698f0fc42499f04e6e8d2560ba2f7da0fb059a8e/mmh3-5.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:b22fe2e54be81f6c07dcb36b96fa250fb72effe08aa52fbb83eade6e1e2d5fd7", size = 38890 }, ] +[[package]] +name = "morphys" +version = "1.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/4f/cb781d0ac5d079adabc77dc4f0bc99fc81c390029bd33c6e70552139e762/morphys-1.0-py2.py3-none-any.whl", hash = "sha256:76d6dbaa4d65f597e59d332c81da786d83e4669387b9b2a750cfec74e7beec20", size = 5618 }, +] + +[[package]] +name = "msgpack" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/b1/ea4f68038a18c77c9467400d166d74c4ffa536f34761f7983a104357e614/msgpack-1.1.1.tar.gz", hash = "sha256:77b79ce34a2bdab2594f490c8e80dd62a02d650b91a75159a63ec413b8d104cd", size = 173555 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/26/389b9c593eda2b8551b2e7126ad3a06af6f9b44274eb3a4f054d48ff7e47/msgpack-1.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae497b11f4c21558d95de9f64fff7053544f4d1a17731c866143ed6bb4591238", size = 82359 }, + { url = "https://files.pythonhosted.org/packages/ab/65/7d1de38c8a22cf8b1551469159d4b6cf49be2126adc2482de50976084d78/msgpack-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:33be9ab121df9b6b461ff91baac6f2731f83d9b27ed948c5b9d1978ae28bf157", size = 79172 }, + { url = "https://files.pythonhosted.org/packages/0f/bd/cacf208b64d9577a62c74b677e1ada005caa9b69a05a599889d6fc2ab20a/msgpack-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f64ae8fe7ffba251fecb8408540c34ee9df1c26674c50c4544d72dbf792e5ce", size = 425013 }, + { url = "https://files.pythonhosted.org/packages/4d/ec/fd869e2567cc9c01278a736cfd1697941ba0d4b81a43e0aa2e8d71dab208/msgpack-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a494554874691720ba5891c9b0b39474ba43ffb1aaf32a5dac874effb1619e1a", size = 426905 }, + { url = "https://files.pythonhosted.org/packages/55/2a/35860f33229075bce803a5593d046d8b489d7ba2fc85701e714fc1aaf898/msgpack-1.1.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb643284ab0ed26f6957d969fe0dd8bb17beb567beb8998140b5e38a90974f6c", size = 407336 }, + { url = "https://files.pythonhosted.org/packages/8c/16/69ed8f3ada150bf92745fb4921bd621fd2cdf5a42e25eb50bcc57a5328f0/msgpack-1.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d275a9e3c81b1093c060c3837e580c37f47c51eca031f7b5fb76f7b8470f5f9b", size = 409485 }, + { url = "https://files.pythonhosted.org/packages/c6/b6/0c398039e4c6d0b2e37c61d7e0e9d13439f91f780686deb8ee64ecf1ae71/msgpack-1.1.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fd6b577e4541676e0cc9ddc1709d25014d3ad9a66caa19962c4f5de30fc09ef", size = 412182 }, + { url = "https://files.pythonhosted.org/packages/b8/d0/0cf4a6ecb9bc960d624c93effaeaae75cbf00b3bc4a54f35c8507273cda1/msgpack-1.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb29aaa613c0a1c40d1af111abf025f1732cab333f96f285d6a93b934738a68a", size = 419883 }, + { url = "https://files.pythonhosted.org/packages/62/83/9697c211720fa71a2dfb632cad6196a8af3abea56eece220fde4674dc44b/msgpack-1.1.1-cp312-cp312-win32.whl", hash = "sha256:870b9a626280c86cff9c576ec0d9cbcc54a1e5ebda9cd26dab12baf41fee218c", size = 65406 }, + { url = "https://files.pythonhosted.org/packages/c0/23/0abb886e80eab08f5e8c485d6f13924028602829f63b8f5fa25a06636628/msgpack-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:5692095123007180dca3e788bb4c399cc26626da51629a31d40207cb262e67f4", size = 72558 }, + { url = "https://files.pythonhosted.org/packages/a1/38/561f01cf3577430b59b340b51329803d3a5bf6a45864a55f4ef308ac11e3/msgpack-1.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3765afa6bd4832fc11c3749be4ba4b69a0e8d7b728f78e68120a157a4c5d41f0", size = 81677 }, + { url = "https://files.pythonhosted.org/packages/09/48/54a89579ea36b6ae0ee001cba8c61f776451fad3c9306cd80f5b5c55be87/msgpack-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8ddb2bcfd1a8b9e431c8d6f4f7db0773084e107730ecf3472f1dfe9ad583f3d9", size = 78603 }, + { url = "https://files.pythonhosted.org/packages/a0/60/daba2699b308e95ae792cdc2ef092a38eb5ee422f9d2fbd4101526d8a210/msgpack-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:196a736f0526a03653d829d7d4c5500a97eea3648aebfd4b6743875f28aa2af8", size = 420504 }, + { url = "https://files.pythonhosted.org/packages/20/22/2ebae7ae43cd8f2debc35c631172ddf14e2a87ffcc04cf43ff9df9fff0d3/msgpack-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d592d06e3cc2f537ceeeb23d38799c6ad83255289bb84c2e5792e5a8dea268a", size = 423749 }, + { url = "https://files.pythonhosted.org/packages/40/1b/54c08dd5452427e1179a40b4b607e37e2664bca1c790c60c442c8e972e47/msgpack-1.1.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4df2311b0ce24f06ba253fda361f938dfecd7b961576f9be3f3fbd60e87130ac", size = 404458 }, + { url = "https://files.pythonhosted.org/packages/2e/60/6bb17e9ffb080616a51f09928fdd5cac1353c9becc6c4a8abd4e57269a16/msgpack-1.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e4141c5a32b5e37905b5940aacbc59739f036930367d7acce7a64e4dec1f5e0b", size = 405976 }, + { url = "https://files.pythonhosted.org/packages/ee/97/88983e266572e8707c1f4b99c8fd04f9eb97b43f2db40e3172d87d8642db/msgpack-1.1.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b1ce7f41670c5a69e1389420436f41385b1aa2504c3b0c30620764b15dded2e7", size = 408607 }, + { url = "https://files.pythonhosted.org/packages/bc/66/36c78af2efaffcc15a5a61ae0df53a1d025f2680122e2a9eb8442fed3ae4/msgpack-1.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4147151acabb9caed4e474c3344181e91ff7a388b888f1e19ea04f7e73dc7ad5", size = 424172 }, + { url = "https://files.pythonhosted.org/packages/8c/87/a75eb622b555708fe0427fab96056d39d4c9892b0c784b3a721088c7ee37/msgpack-1.1.1-cp313-cp313-win32.whl", hash = "sha256:500e85823a27d6d9bba1d057c871b4210c1dd6fb01fbb764e37e4e8847376323", size = 65347 }, + { url = "https://files.pythonhosted.org/packages/ca/91/7dc28d5e2a11a5ad804cf2b7f7a5fcb1eb5a4966d66a5d2b41aee6376543/msgpack-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:6d489fba546295983abd142812bda76b57e33d0b9f5d5b71c09a583285506f69", size = 72341 }, +] + [[package]] name = "msgspec" version = "0.19.0" @@ -752,6 +2014,52 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f3/9b/c21a9c1d5ea4847989f1eb00e3147e38e79aaea7c4b4d1cbd4f1afae9740/multiformats_config-0.3.1-py3-none-any.whl", hash = "sha256:dec4c9d42ed0d9305889b67440f72e8e8d74b82b80abd7219667764b5b0a8e1d", size = 17153 }, ] +[[package]] +name = "narwhals" +version = "1.46.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/7f/dd8c5f7978c3136de4d660877a5279e4688ad0c56dbc15ee003c2fe981cd/narwhals-1.46.0.tar.gz", hash = "sha256:fd7e53860b233c2b5566d8b4e1b3e8e9c01b5a87649a9f9a322742000f207a60", size = 512060 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/64/c46ba7517d90e330c4f35af1256d4b12ba037e2ef17d4aa4d4f11b4a143d/narwhals-1.46.0-py3-none-any.whl", hash = "sha256:f15d2255695d7e99f624f76aa5b765eb3fff8a509d3215049707af3a3feebc90", size = 373394 }, +] + +[[package]] +name = "nc-time-axis" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cftime" }, + { name = "matplotlib" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/c7/ceaba2047ef4e08660a5a89a71cea30547bddb0e51236dab2dcb771a6fe1/nc-time-axis-1.4.1.tar.gz", hash = "sha256:72d80f492f34bbf59490838d8cb3d92f14e88900b0ee35498b2b33c82795eb81", size = 66231 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/89/dbeab77a217f8fbda97a637acf1e3f0ce8c9c9fb3f5e5d1ff843da859520/nc_time_axis-1.4.1-py3-none-any.whl", hash = "sha256:96a6fb28cede0d07998fcd666599f76e51a086e1929fbcbfb758c1d0f3e7b0d1", size = 17757 }, +] + +[[package]] +name = "netcdf4" +version = "1.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "cftime" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/ed/4d27fcfa40ebfdad3d2088a3de7ee48dbff7f35163e815ec1870d2a7398c/netcdf4-1.7.2.tar.gz", hash = "sha256:a4c6375540b19989896136943abb6d44850ff6f1fa7d3f063253b1ad3f8b7fce", size = 835064 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/7f/3a0f18a39efca0e093b54d634b66573c25ecab5c482d73138ae14aa55c6d/netCDF4-1.7.2-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:e73e3baa0b74afc414e53ff5095748fdbec7fb346eda351e567c23f2f0d247f1", size = 2952127 }, + { url = "https://files.pythonhosted.org/packages/ed/c4/8aac0f8ca95a41bdf1364d34ff4e9bcc24494bfe69a1157301d884c2e392/netCDF4-1.7.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a51da09258b31776f474c1d47e484fc7214914cdc59edf4cee789ba632184591", size = 2460781 }, + { url = "https://files.pythonhosted.org/packages/2d/1a/32b7427aaf62fed3d4e4456f874b25ce39373dbddf6cfde9edbcfc2417fc/netCDF4-1.7.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb95b11804fe051897d1f2044b05d82a1847bc2549631cdd2f655dde7de77a9c", size = 9377415 }, + { url = "https://files.pythonhosted.org/packages/fd/bf/5e671495c8bdf6b628e091aa8980793579474a10e51bc6ba302a3af6a778/netCDF4-1.7.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d8a848373723f41ef662590b4f5e1832227501c9fd4513e8ad8da58c269977", size = 9260579 }, + { url = "https://files.pythonhosted.org/packages/d4/57/0a0bcdebcfaf72e96e7bcaa512f80ee096bf71945a3318d38253338e9c25/netCDF4-1.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:568ea369e00b581302d77fc5fd0b8f78e520c7e08d0b5af5219ba51f3f1cd694", size = 6991523 }, + { url = "https://files.pythonhosted.org/packages/e6/7a/ce4f9038d8726c9c90e07b2d3a404ae111a27720d712cfcded0c8ef160e8/netCDF4-1.7.2-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:205a5f1de3ddb993c7c97fb204a923a22408cc2e5facf08d75a8eb89b3e7e1a8", size = 2948911 }, + { url = "https://files.pythonhosted.org/packages/58/3e/5736880a607edabca4c4fc49f1ccf9a2bb2485f84478e4cd19ba11c3b803/netCDF4-1.7.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:96653fc75057df196010818367c63ba6d7e9af603df0a7fe43fcdad3fe0e9e56", size = 2455078 }, + { url = "https://files.pythonhosted.org/packages/71/96/d5d8859a6dac29f8ebc815ff8e75770bd513db9f08d7a711e21ae562a948/netCDF4-1.7.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30d20e56b9ba2c48884eb89c91b63e6c0612b4927881707e34402719153ef17f", size = 9378149 }, + { url = "https://files.pythonhosted.org/packages/d1/80/b9c19f1bb4ac6c5fa6f94a4f278bc68a778473d1814a86a375d7cffa193a/netCDF4-1.7.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d6bfd38ba0bde04d56f06c1554714a2ea9dab75811c89450dc3ec57a9d36b80", size = 9254471 }, + { url = "https://files.pythonhosted.org/packages/66/b5/e04550fd53de57001dbd5a87242da7ff784c80790adc48897977b6ccf891/netCDF4-1.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:5c5fbee6134ee1246c397e1508e5297d825aa19221fdf3fa8dc9727ad824d7a5", size = 6990521 }, +] + [[package]] name = "nodeenv" version = "1.9.1" @@ -761,6 +2069,41 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, ] +[[package]] +name = "numba" +version = "0.61.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "llvmlite" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/a0/e21f57604304aa03ebb8e098429222722ad99176a4f979d34af1d1ee80da/numba-0.61.2.tar.gz", hash = "sha256:8750ee147940a6637b80ecf7f95062185ad8726c8c28a2295b8ec1160a196f7d", size = 2820615 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/a0/c6b7b9c615cfa3b98c4c63f4316e3f6b3bbe2387740277006551784218cd/numba-0.61.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:34fba9406078bac7ab052efbf0d13939426c753ad72946baaa5bf9ae0ebb8dd2", size = 2776626 }, + { url = "https://files.pythonhosted.org/packages/92/4a/fe4e3c2ecad72d88f5f8cd04e7f7cff49e718398a2fac02d2947480a00ca/numba-0.61.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ddce10009bc097b080fc96876d14c051cc0c7679e99de3e0af59014dab7dfe8", size = 2779287 }, + { url = "https://files.pythonhosted.org/packages/9a/2d/e518df036feab381c23a624dac47f8445ac55686ec7f11083655eb707da3/numba-0.61.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b1bb509d01f23d70325d3a5a0e237cbc9544dd50e50588bc581ba860c213546", size = 3885928 }, + { url = "https://files.pythonhosted.org/packages/10/0f/23cced68ead67b75d77cfcca3df4991d1855c897ee0ff3fe25a56ed82108/numba-0.61.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:48a53a3de8f8793526cbe330f2a39fe9a6638efcbf11bd63f3d2f9757ae345cd", size = 3577115 }, + { url = "https://files.pythonhosted.org/packages/68/1d/ddb3e704c5a8fb90142bf9dc195c27db02a08a99f037395503bfbc1d14b3/numba-0.61.2-cp312-cp312-win_amd64.whl", hash = "sha256:97cf4f12c728cf77c9c1d7c23707e4d8fb4632b46275f8f3397de33e5877af18", size = 2831929 }, + { url = "https://files.pythonhosted.org/packages/0b/f3/0fe4c1b1f2569e8a18ad90c159298d862f96c3964392a20d74fc628aee44/numba-0.61.2-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:3a10a8fc9afac40b1eac55717cece1b8b1ac0b946f5065c89e00bde646b5b154", size = 2771785 }, + { url = "https://files.pythonhosted.org/packages/e9/71/91b277d712e46bd5059f8a5866862ed1116091a7cb03bd2704ba8ebe015f/numba-0.61.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d3bcada3c9afba3bed413fba45845f2fb9cd0d2b27dd58a1be90257e293d140", size = 2773289 }, + { url = "https://files.pythonhosted.org/packages/0d/e0/5ea04e7ad2c39288c0f0f9e8d47638ad70f28e275d092733b5817cf243c9/numba-0.61.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bdbca73ad81fa196bd53dc12e3aaf1564ae036e0c125f237c7644fe64a4928ab", size = 3893918 }, + { url = "https://files.pythonhosted.org/packages/17/58/064f4dcb7d7e9412f16ecf80ed753f92297e39f399c905389688cf950b81/numba-0.61.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5f154aaea625fb32cfbe3b80c5456d514d416fcdf79733dd69c0df3a11348e9e", size = 3584056 }, + { url = "https://files.pythonhosted.org/packages/af/a4/6d3a0f2d3989e62a18749e1e9913d5fa4910bbb3e3311a035baea6caf26d/numba-0.61.2-cp313-cp313-win_amd64.whl", hash = "sha256:59321215e2e0ac5fa928a8020ab00b8e57cda8a97384963ac0dfa4d4e6aa54e7", size = 2831846 }, +] + +[[package]] +name = "numbagg" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numba" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/6f/6e2c5004fdf26691c2ef645208b2f365508c57611cd24c79aae90e8cb3bf/numbagg-0.9.0.tar.gz", hash = "sha256:45ba41077b7a621e35eaa4c294d90b22e75e8513b8c211f59d2b9be840fc1175", size = 97455 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/06/9016e13b12ddc4353880479ef570d2b988e1adf6c68b977a42647a2267ee/numbagg-0.9.0-py3-none-any.whl", hash = "sha256:000968facb1b433a0722e99664f9a6077f061edb3625d992084188607e016e91", size = 51041 }, +] + [[package]] name = "numcodecs" version = "0.15.1" @@ -824,13 +2167,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3e/05/eb7eec66b95cf697f08c754ef26c3549d03ebd682819f794cb039574a0a6/numpy-2.2.4-cp313-cp313t-win_amd64.whl", hash = "sha256:188dcbca89834cc2e14eb2f106c96d6d46f200fe0200310fc29089657379c58d", size = 12739119 }, ] +[[package]] +name = "numpy-groupies" +version = "0.11.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/ff/0559b586423d9a59feac52c2261501106dcd61e45214862de5fbb03b78cb/numpy_groupies-0.11.3.tar.gz", hash = "sha256:aed4afdad55e856b9e737fe4b4673c77e47c2f887c3663a18baaa200407c23e0", size = 159159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/e0/760e73c111193db5ca37712a148e4807d1b0c60302ab31e4ada6528ca34d/numpy_groupies-0.11.3-py3-none-any.whl", hash = "sha256:d4065dd5d56fda941ad5a7c80a7f80b49f671ed148aaa3e243a0e4caa71adcb3", size = 40784 }, +] + +[[package]] +name = "opt-einsum" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/b9/2ac072041e899a52f20cf9510850ff58295003aa75525e58343591b0cbfb/opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac", size = 63004 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd", size = 71932 }, +] + [[package]] name = "packaging" -version = "24.2" +version = "23.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/2b/9b9c33ffed44ee921d0967086d653047286054117d584f1b1a7c22ceaf7b/packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", size = 146714 } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, + { url = "https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7", size = 53011 }, ] [[package]] @@ -867,6 +2231,118 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, ] +[[package]] +name = "parsimonious" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7b/91/abdc50c4ef06fdf8d047f60ee777ca9b2a7885e1a9cea81343fbecda52d7/parsimonious-0.10.0.tar.gz", hash = "sha256:8281600da180ec8ae35427a4ab4f7b82bfec1e3d1e52f80cb60ea82b9512501c", size = 52172 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/0f/c8b64d9b54ea631fcad4e9e3c8dbe8c11bb32a623be94f22974c88e71eaf/parsimonious-0.10.0-py3-none-any.whl", hash = "sha256:982ab435fabe86519b57f6b35610aa4e4e977e9f02a14353edf4bbc75369fc0f", size = 48427 }, +] + +[[package]] +name = "parso" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, +] + +[[package]] +name = "partd" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "locket" }, + { name = "toolz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/3a/3f06f34820a31257ddcabdfafc2672c5816be79c7e353b02c1f318daa7d4/partd-1.4.2.tar.gz", hash = "sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c", size = 21029 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/e7/40fb618334dcdf7c5a316c0e7343c5cd82d3d866edc100d98e29bc945ecd/partd-1.4.2-py3-none-any.whl", hash = "sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f", size = 18905 }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, +] + +[[package]] +name = "pillow" +version = "11.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800 }, + { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296 }, + { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726 }, + { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652 }, + { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787 }, + { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236 }, + { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950 }, + { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358 }, + { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079 }, + { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324 }, + { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067 }, + { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328 }, + { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652 }, + { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443 }, + { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474 }, + { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038 }, + { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407 }, + { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094 }, + { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503 }, + { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574 }, + { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060 }, + { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407 }, + { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841 }, + { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450 }, + { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055 }, + { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110 }, + { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547 }, + { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554 }, + { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132 }, + { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001 }, + { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814 }, + { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124 }, + { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186 }, + { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546 }, + { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102 }, + { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803 }, + { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520 }, + { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116 }, + { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597 }, + { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246 }, + { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336 }, + { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699 }, + { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789 }, + { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386 }, + { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911 }, + { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383 }, + { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385 }, + { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129 }, + { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580 }, + { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860 }, + { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694 }, + { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888 }, + { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330 }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089 }, + { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206 }, + { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370 }, + { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500 }, + { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835 }, +] + [[package]] name = "platformdirs" version = "4.3.7" @@ -885,6 +2361,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, ] +[[package]] +name = "pooch" +version = "1.8.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "platformdirs" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/77/b3d3e00c696c16cf99af81ef7b1f5fe73bd2a307abca41bd7605429fe6e5/pooch-1.8.2.tar.gz", hash = "sha256:76561f0de68a01da4df6af38e9955c4c9d1a5c90da73f7e40276a5728ec83d10", size = 59353 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-py3-none-any.whl", hash = "sha256:3529a57096f7198778a5ceefd5ac3ef0e4d06a6ddaf9fc2d609b806f25302c47", size = 64574 }, +] + [[package]] name = "pre-commit" version = "4.2.0" @@ -901,6 +2391,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707 }, ] +[[package]] +name = "prompt-toolkit" +version = "3.0.51" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/6e/9d084c929dfe9e3bfe0c6a47e31f78a25c54627d64a66e884a8bf5474f1c/prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed", size = 428940 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810 }, +] + [[package]] name = "propcache" version = "0.3.1" @@ -958,19 +2460,184 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b8/d3/c3cb8f1d6ae3b37f83e1de806713a9b3642c5895f0215a62e1a4bd6e5e34/propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40", size = 12376 }, ] +[[package]] +name = "protobuf" +version = "6.31.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603 }, + { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283 }, + { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604 }, + { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115 }, + { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070 }, + { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724 }, +] + +[[package]] +name = "psutil" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, +] + +[[package]] +name = "py-cid" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "base58" }, + { name = "morphys" }, + { name = "py-multibase" }, + { name = "py-multicodec" }, + { name = "py-multihash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/65/1d57b5519b4201f39bf4a9cc3e2d554f25446c56df88c0e9abbffe28cdbe/py-cid-0.3.0.tar.gz", hash = "sha256:22f432cc6fb68d12a9c35dbdc92c95484fc49e31dfcb9e0efb0082233c5394e3", size = 24147 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/72/8fd8ac6e79b00d171499a1808d5af99c109d9780ca4982ff69f1baf784e0/py_cid-0.3.0-py2.py3-none-any.whl", hash = "sha256:7c48a6ee0bc50fd114d4b24849cd689a31d3ad5bdf8fa073bf68f846fd58c5da", size = 7893 }, +] + +[[package]] +name = "py-ecc" +version = "8.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "eth-typing" }, + { name = "eth-utils" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/96/e73075d5c885274efada2fbc5db6377022036c2f5b4b470dbcf4106e07d5/py_ecc-8.0.0.tar.gz", hash = "sha256:56aca19e5dc37294f60c1cc76666c03c2276e7666412b9a559fa0145d099933d", size = 51193 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/58/383335eac96d2f1aba78741c6ce128c54e7eba2ea1dc47408257d751d35c/py_ecc-8.0.0-py3-none-any.whl", hash = "sha256:c0b2dfc4bde67a55122a392591a10e851a986d5128f680628c80b405f7663e13", size = 47814 }, +] + +[[package]] +name = "py-evm" +version = "0.10.1b1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cached-property" }, + { name = "ckzg" }, + { name = "eth-bloom" }, + { name = "eth-keys" }, + { name = "eth-typing" }, + { name = "eth-utils" }, + { name = "lru-dict" }, + { name = "py-ecc" }, + { name = "rlp" }, + { name = "trie" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/b4/0dd6f0660c93123685f9d162f94aa98bb2de211d8edb74a1fbfb667853c4/py_evm-0.10.1b1.tar.gz", hash = "sha256:aeb889514af12b6a8cb5091fe93008642eadf7c19999859dad3191eaf451647c", size = 608366 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/6c/8811c3eaeeb432bd4c9614c4d1b064a4a344910d610fe858878636dc03d3/py_evm-0.10.1b1-py3-none-any.whl", hash = "sha256:f0fc4a4b904917b40e6a06f87925017dc48ea6582e95f88d28be38f3566e2bae", size = 551293 }, +] + +[[package]] +name = "py-geth" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "semantic-version" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/4b/3229e00852ac771a942feb8e3676d5203508ef8dea72dbf7f5cf444c7b1e/py-geth-4.4.0.tar.gz", hash = "sha256:c08d84f6dad4f86a9b8ffd74c0a0f160d600db0ee45dfc2a66d5e13522aeb039", size = 30465 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/9f/7ea1741c5eb80a99540f17a22562455d41da2237e0ac9435e6ac6e576887/py_geth-4.4.0-py3-none-any.whl", hash = "sha256:ae8771b028c68f6710e6434d2aa1310ce2ba3cc9099d244d9bb5a9e340786a92", size = 24379 }, +] + [[package]] name = "py-hamt" -version = "2.4.0" -source = { git = "https://github.com/dClimate/py-hamt#3ac5d99d537b4727480ff388f91d2dc0e7b9a58b" } +version = "3.2.0" +source = { git = "https://github.com/dClimate/py-hamt?rev=a60039b4e2305847c8b9241557a35f96038d7ca0#a60039b4e2305847c8b9241557a35f96038d7ca0" } dependencies = [ { name = "dag-cbor" }, + { name = "httpx", extra = ["http2"] }, { name = "msgspec" }, { name = "multiformats", extra = ["full"] }, { name = "pycryptodome" }, - { name = "requests" }, { name = "zarr" }, ] +[[package]] +name = "py-multibase" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "morphys" }, + { name = "python-baseconv" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/9c/8b1b78e900f5e94b1d48359efffcaa1ba9ddd0448e6aac94d4d27dbd67b7/py-multibase-1.0.3.tar.gz", hash = "sha256:d28a20efcbb61eec28f55827a0bf329c7cea80fffd933aecaea6ae8431267fe4", size = 23376 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/21/7b6b3bba9c702daf6991f2bd89c92db6fa899a444fab24ba5b99f1f486f7/py_multibase-1.0.3-py2.py3-none-any.whl", hash = "sha256:2677c1fafcc0ae15ddb9c7f444c5becc2530b3889124fd4fa2959ddfefb8c15b", size = 6378 }, +] + +[[package]] +name = "py-multicodec" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "morphys" }, + { name = "six" }, + { name = "varint" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/33/ff388b34002239c367f6544678667efff079d29aabf7e3c4a3ec392310be/py-multicodec-0.2.1.tar.gz", hash = "sha256:83021ffe8c0e272d19b5b86bc5b39efa67c8e9f4735ce6cafdbc1ace767ec647", size = 22542 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/96/3ffb721c2fe100f6613a0d2db3b4b3229c6de52b279fd41aab0e84ea0725/py_multicodec-0.2.1-py2.py3-none-any.whl", hash = "sha256:55b6bb53088a63e56c434cb11b29795e8805652bac43d50a8f2a9bcf5ca84e1f", size = 8536 }, +] + +[[package]] +name = "py-multihash" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "base58" }, + { name = "morphys" }, + { name = "six" }, + { name = "varint" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/e5/d9bb73cc981d29446bfc3b547ddc67a1b5d07f27b277add2f46c12392fa0/py-multihash-0.2.3.tar.gz", hash = "sha256:f0ade4de820afdc4b4aaa40464ec86c9da5cae3a4578cda2daab4b0eb7e5b18d", size = 21358 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/04/9c0d173afcf3f06149b35de179e0e5d174db2c9e6466bac01215b45e9bb4/py_multihash-0.2.3-py2.py3-none-any.whl", hash = "sha256:a0602c99093587dfbf1634e2e8c7726de39374b0d68587a36093b4c237af6969", size = 7929 }, +] + +[[package]] +name = "py-solc-x" +version = "2.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/72/92c6c96584cf9b1e7f1da50978d44910e7545ce0df646041ce683993ee18/py-solc-x-2.0.4.tar.gz", hash = "sha256:6f6f7f47c8520bde73679624568ec343db9baca6914212b371896fad8697deca", size = 77779 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/3a/063e034e0b283933fc1b8359ba5982b5cb853520e32829f1d0e7881fe47c/py_solc_x-2.0.4-py3-none-any.whl", hash = "sha256:fe7baf788ed2f23b3c793c20661560cceca6f8a1de9d6d479fe3ea9c6c1b2a9e", size = 18560 }, +] + [[package]] name = "pyarrow" version = "19.0.1" @@ -1035,6 +2702,77 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/15/c8/79ab16e5b95a8988caee792236a776beceabcaa2518979d4e21b6ee20f57/pycryptodomex-3.22.0-cp37-abi3-win_amd64.whl", hash = "sha256:ff46212fda7ee86ec2f4a64016c994e8ad80f11ef748131753adb67e9b722ebd", size = 1797989 }, ] +[[package]] +name = "pydantic" +version = "2.11.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782 }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000 }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996 }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957 }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199 }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296 }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109 }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028 }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044 }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881 }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034 }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187 }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628 }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866 }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894 }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, +] + +[[package]] +name = "pydantic-settings" +version = "2.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235 }, +] + [[package]] name = "pygments" version = "2.19.1" @@ -1105,6 +2843,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/98/df/68a2b7f5fb6400c64aad82d72bcc4bc531775e62eedff993a77c780defd0/pyproj-3.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:d3caac7473be22b6d6e102dde6c46de73b96bc98334e577dfaee9886f102ea2e", size = 6266573 }, ] +[[package]] +name = "pyshp" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/9f/0dd21250c60375a532c35e89fad8d5e8a3f1a2e3f7c389ccc5a60b05263e/pyshp-2.3.1.tar.gz", hash = "sha256:4caec82fd8dd096feba8217858068bacb2a3b5950f43c048c6dc32a3489d5af1", size = 1731544 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/2f/68116db5b36b895c0450e3072b8cb6c2fac0359279b182ea97014d3c8ac0/pyshp-2.3.1-py2.py3-none-any.whl", hash = "sha256:67024c0ccdc352ba5db777c4e968483782dfa78f8e200672a90d2d30fd8b7b49", size = 46537 }, +] + [[package]] name = "pyskein" version = "1.0" @@ -1126,6 +2873,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, ] +[[package]] +name = "pytest-asyncio" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/d4/14f53324cb1a6381bef29d698987625d80052bb33932d8e7cbf9b337b17c/pytest_asyncio-1.0.0.tar.gz", hash = "sha256:d15463d13f4456e1ead2594520216b225a16f781e144f8fdf6c5bb4667c48b3f", size = 46960 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/05/ce271016e351fddc8399e546f6e23761967ee09c8c568bbfbecb0c150171/pytest_asyncio-1.0.0-py3-none-any.whl", hash = "sha256:4f024da9f1ef945e680dc68610b52550e36590a67fd31bb3b4943979a1f90ef3", size = 15976 }, +] + [[package]] name = "pytest-cov" version = "6.0.0" @@ -1151,6 +2910,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 }, ] +[[package]] +name = "python-baseconv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/d0/9297d7d8dd74767b4d5560d834b30b2fff17d39987c23ed8656f476e0d9b/python-baseconv-1.2.2.tar.gz", hash = "sha256:0539f8bd0464013b05ad62e0a1673f0ac9086c76b43ebf9f833053527cd9931b", size = 4929 } + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -1163,6 +2928,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, ] +[[package]] +name = "python-dotenv" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556 }, +] + [[package]] name = "pytz" version = "2025.2" @@ -1172,6 +2946,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, ] +[[package]] +name = "pyunormalize" +version = "16.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/08/568036c725dac746ecb267bb749ef930fb7907454fe69fce83c8557287fb/pyunormalize-16.0.0.tar.gz", hash = "sha256:2e1dfbb4a118154ae26f70710426a52a364b926c9191f764601f5a8cb12761f7", size = 49968 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/f9/9d86e56f716e0651194a5ad58be9c146fcaf1de6901ac6f3cd3affeeb74e/pyunormalize-16.0.0-py3-none-any.whl", hash = "sha256:c647d95e5d1e2ea9a2f448d1d95d8518348df24eab5c3fd32d2b5c3300a49152", size = 49173 }, +] + +[[package]] +name = "pywin32" +version = "310" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/ec/4fdbe47932f671d6e348474ea35ed94227fb5df56a7c30cbbb42cd396ed0/pywin32-310-cp312-cp312-win32.whl", hash = "sha256:8a75a5cc3893e83a108c05d82198880704c44bbaee4d06e442e471d3c9ea4f3d", size = 8796239 }, + { url = "https://files.pythonhosted.org/packages/e3/e5/b0627f8bb84e06991bea89ad8153a9e50ace40b2e1195d68e9dff6b03d0f/pywin32-310-cp312-cp312-win_amd64.whl", hash = "sha256:bf5c397c9a9a19a6f62f3fb821fbf36cac08f03770056711f765ec1503972060", size = 9503839 }, + { url = "https://files.pythonhosted.org/packages/1f/32/9ccf53748df72301a89713936645a664ec001abd35ecc8578beda593d37d/pywin32-310-cp312-cp312-win_arm64.whl", hash = "sha256:2349cc906eae872d0663d4d6290d13b90621eaf78964bb1578632ff20e152966", size = 8459470 }, + { url = "https://files.pythonhosted.org/packages/1c/09/9c1b978ffc4ae53999e89c19c77ba882d9fce476729f23ef55211ea1c034/pywin32-310-cp313-cp313-win32.whl", hash = "sha256:5d241a659c496ada3253cd01cfaa779b048e90ce4b2b38cd44168ad555ce74ab", size = 8794384 }, + { url = "https://files.pythonhosted.org/packages/45/3c/b4640f740ffebadd5d34df35fecba0e1cfef8fde9f3e594df91c28ad9b50/pywin32-310-cp313-cp313-win_amd64.whl", hash = "sha256:667827eb3a90208ddbdcc9e860c81bde63a135710e21e4cb3348968e4bd5249e", size = 9503039 }, + { url = "https://files.pythonhosted.org/packages/b4/f4/f785020090fb050e7fb6d34b780f2231f302609dc964672f72bfaeb59a28/pywin32-310-cp313-cp313-win_arm64.whl", hash = "sha256:e308f831de771482b7cf692a1f308f8fca701b2d8f9dde6cc440c7da17e47b33", size = 8458152 }, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -1224,6 +3020,58 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/88/9db5f49ebfdd9c12365e4cac76c34ccb1a642b1c8cbab4124b3c681495de/rasterio-1.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:1839960e2f3057a6daa323ccf67b330f8f2f0dbd4a50cc7031e88e649301c5c0", size = 25424949 }, ] +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 }, +] + +[[package]] +name = "regex" +version = "2024.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781 }, + { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455 }, + { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759 }, + { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976 }, + { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077 }, + { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160 }, + { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896 }, + { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997 }, + { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725 }, + { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481 }, + { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896 }, + { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138 }, + { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692 }, + { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135 }, + { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567 }, + { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 }, + { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 }, + { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 }, + { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 }, + { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 }, + { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 }, + { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 }, + { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 }, + { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 }, + { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 }, + { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 }, + { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 }, + { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 }, + { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, + { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, +] + [[package]] name = "requests" version = "2.32.3" @@ -1268,6 +3116,94 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/b0/2c74f302512fbd24d68fbba0ec6b650b33ef83e398daeb0a2bb1a4cd641c/rioxarray-0.18.2-py3-none-any.whl", hash = "sha256:f351c15fc682081ac2cd2c8db367ef0a7ed5acdea29b9e43a6d7bc2ebc5ec6e5", size = 61943 }, ] +[[package]] +name = "rlp" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "eth-utils" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/2d/439b0728a92964a04d9c88ea1ca9ebb128893fbbd5834faa31f987f2fd4c/rlp-4.1.0.tar.gz", hash = "sha256:be07564270a96f3e225e2c107db263de96b5bc1f27722d2855bd3459a08e95a9", size = 33429 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/fb/e4c0ced9893b84ac95b7181d69a9786ce5879aeb3bbbcbba80a164f85d6a/rlp-4.1.0-py3-none-any.whl", hash = "sha256:8eca394c579bad34ee0b937aecb96a57052ff3716e19c7a578883e767bc5da6f", size = 19973 }, +] + +[[package]] +name = "rpds-py" +version = "0.26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/aa/4456d84bbb54adc6a916fb10c9b374f78ac840337644e4a5eda229c81275/rpds_py-0.26.0.tar.gz", hash = "sha256:20dae58a859b0906f0685642e591056f1e787f3a8b39c8e8749a45dc7d26bdb0", size = 27385 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/86/90eb87c6f87085868bd077c7a9938006eb1ce19ed4d06944a90d3560fce2/rpds_py-0.26.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:894514d47e012e794f1350f076c427d2347ebf82f9b958d554d12819849a369d", size = 363933 }, + { url = "https://files.pythonhosted.org/packages/63/78/4469f24d34636242c924626082b9586f064ada0b5dbb1e9d096ee7a8e0c6/rpds_py-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc921b96fa95a097add244da36a1d9e4f3039160d1d30f1b35837bf108c21136", size = 350447 }, + { url = "https://files.pythonhosted.org/packages/ad/91/c448ed45efdfdade82348d5e7995e15612754826ea640afc20915119734f/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1157659470aa42a75448b6e943c895be8c70531c43cb78b9ba990778955582", size = 384711 }, + { url = "https://files.pythonhosted.org/packages/ec/43/e5c86fef4be7f49828bdd4ecc8931f0287b1152c0bb0163049b3218740e7/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:521ccf56f45bb3a791182dc6b88ae5f8fa079dd705ee42138c76deb1238e554e", size = 400865 }, + { url = "https://files.pythonhosted.org/packages/55/34/e00f726a4d44f22d5c5fe2e5ddd3ac3d7fd3f74a175607781fbdd06fe375/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9def736773fd56b305c0eef698be5192c77bfa30d55a0e5885f80126c4831a15", size = 517763 }, + { url = "https://files.pythonhosted.org/packages/52/1c/52dc20c31b147af724b16104500fba13e60123ea0334beba7b40e33354b4/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdad4ea3b4513b475e027be79e5a0ceac8ee1c113a1a11e5edc3c30c29f964d8", size = 406651 }, + { url = "https://files.pythonhosted.org/packages/2e/77/87d7bfabfc4e821caa35481a2ff6ae0b73e6a391bb6b343db2c91c2b9844/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82b165b07f416bdccf5c84546a484cc8f15137ca38325403864bfdf2b5b72f6a", size = 386079 }, + { url = "https://files.pythonhosted.org/packages/e3/d4/7f2200c2d3ee145b65b3cddc4310d51f7da6a26634f3ac87125fd789152a/rpds_py-0.26.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d04cab0a54b9dba4d278fe955a1390da3cf71f57feb78ddc7cb67cbe0bd30323", size = 421379 }, + { url = "https://files.pythonhosted.org/packages/ae/13/9fdd428b9c820869924ab62236b8688b122baa22d23efdd1c566938a39ba/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:79061ba1a11b6a12743a2b0f72a46aa2758613d454aa6ba4f5a265cc48850158", size = 562033 }, + { url = "https://files.pythonhosted.org/packages/f3/e1/b69686c3bcbe775abac3a4c1c30a164a2076d28df7926041f6c0eb5e8d28/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f405c93675d8d4c5ac87364bb38d06c988e11028a64b52a47158a355079661f3", size = 591639 }, + { url = "https://files.pythonhosted.org/packages/5c/c9/1e3d8c8863c84a90197ac577bbc3d796a92502124c27092413426f670990/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dafd4c44b74aa4bed4b250f1aed165b8ef5de743bcca3b88fc9619b6087093d2", size = 557105 }, + { url = "https://files.pythonhosted.org/packages/9f/c5/90c569649057622959f6dcc40f7b516539608a414dfd54b8d77e3b201ac0/rpds_py-0.26.0-cp312-cp312-win32.whl", hash = "sha256:3da5852aad63fa0c6f836f3359647870e21ea96cf433eb393ffa45263a170d44", size = 223272 }, + { url = "https://files.pythonhosted.org/packages/7d/16/19f5d9f2a556cfed454eebe4d354c38d51c20f3db69e7b4ce6cff904905d/rpds_py-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf47cfdabc2194a669dcf7a8dbba62e37a04c5041d2125fae0233b720da6f05c", size = 234995 }, + { url = "https://files.pythonhosted.org/packages/83/f0/7935e40b529c0e752dfaa7880224771b51175fce08b41ab4a92eb2fbdc7f/rpds_py-0.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:20ab1ae4fa534f73647aad289003f1104092890849e0266271351922ed5574f8", size = 223198 }, + { url = "https://files.pythonhosted.org/packages/6a/67/bb62d0109493b12b1c6ab00de7a5566aa84c0e44217c2d94bee1bd370da9/rpds_py-0.26.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:696764a5be111b036256c0b18cd29783fab22154690fc698062fc1b0084b511d", size = 363917 }, + { url = "https://files.pythonhosted.org/packages/4b/f3/34e6ae1925a5706c0f002a8d2d7f172373b855768149796af87bd65dcdb9/rpds_py-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6c15d2080a63aaed876e228efe4f814bc7889c63b1e112ad46fdc8b368b9e1", size = 350073 }, + { url = "https://files.pythonhosted.org/packages/75/83/1953a9d4f4e4de7fd0533733e041c28135f3c21485faaef56a8aadbd96b5/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390e3170babf42462739a93321e657444f0862c6d722a291accc46f9d21ed04e", size = 384214 }, + { url = "https://files.pythonhosted.org/packages/48/0e/983ed1b792b3322ea1d065e67f4b230f3b96025f5ce3878cc40af09b7533/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7da84c2c74c0f5bc97d853d9e17bb83e2dcafcff0dc48286916001cc114379a1", size = 400113 }, + { url = "https://files.pythonhosted.org/packages/69/7f/36c0925fff6f660a80be259c5b4f5e53a16851f946eb080351d057698528/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c5fe114a6dd480a510b6d3661d09d67d1622c4bf20660a474507aaee7eeeee9", size = 515189 }, + { url = "https://files.pythonhosted.org/packages/13/45/cbf07fc03ba7a9b54662c9badb58294ecfb24f828b9732970bd1a431ed5c/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3100b3090269f3a7ea727b06a6080d4eb7439dca4c0e91a07c5d133bb1727ea7", size = 406998 }, + { url = "https://files.pythonhosted.org/packages/6c/b0/8fa5e36e58657997873fd6a1cf621285ca822ca75b4b3434ead047daa307/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c03c9b0c64afd0320ae57de4c982801271c0c211aa2d37f3003ff5feb75bb04", size = 385903 }, + { url = "https://files.pythonhosted.org/packages/4b/f7/b25437772f9f57d7a9fbd73ed86d0dcd76b4c7c6998348c070d90f23e315/rpds_py-0.26.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5963b72ccd199ade6ee493723d18a3f21ba7d5b957017607f815788cef50eaf1", size = 419785 }, + { url = "https://files.pythonhosted.org/packages/a7/6b/63ffa55743dfcb4baf2e9e77a0b11f7f97ed96a54558fcb5717a4b2cd732/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9da4e873860ad5bab3291438525cae80169daecbfafe5657f7f5fb4d6b3f96b9", size = 561329 }, + { url = "https://files.pythonhosted.org/packages/2f/07/1f4f5e2886c480a2346b1e6759c00278b8a69e697ae952d82ae2e6ee5db0/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5afaddaa8e8c7f1f7b4c5c725c0070b6eed0228f705b90a1732a48e84350f4e9", size = 590875 }, + { url = "https://files.pythonhosted.org/packages/cc/bc/e6639f1b91c3a55f8c41b47d73e6307051b6e246254a827ede730624c0f8/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4916dc96489616a6f9667e7526af8fa693c0fdb4f3acb0e5d9f4400eb06a47ba", size = 556636 }, + { url = "https://files.pythonhosted.org/packages/05/4c/b3917c45566f9f9a209d38d9b54a1833f2bb1032a3e04c66f75726f28876/rpds_py-0.26.0-cp313-cp313-win32.whl", hash = "sha256:2a343f91b17097c546b93f7999976fd6c9d5900617aa848c81d794e062ab302b", size = 222663 }, + { url = "https://files.pythonhosted.org/packages/e0/0b/0851bdd6025775aaa2365bb8de0697ee2558184c800bfef8d7aef5ccde58/rpds_py-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:0a0b60701f2300c81b2ac88a5fb893ccfa408e1c4a555a77f908a2596eb875a5", size = 234428 }, + { url = "https://files.pythonhosted.org/packages/ed/e8/a47c64ed53149c75fb581e14a237b7b7cd18217e969c30d474d335105622/rpds_py-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:257d011919f133a4746958257f2c75238e3ff54255acd5e3e11f3ff41fd14256", size = 222571 }, + { url = "https://files.pythonhosted.org/packages/89/bf/3d970ba2e2bcd17d2912cb42874107390f72873e38e79267224110de5e61/rpds_py-0.26.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:529c8156d7506fba5740e05da8795688f87119cce330c244519cf706a4a3d618", size = 360475 }, + { url = "https://files.pythonhosted.org/packages/82/9f/283e7e2979fc4ec2d8ecee506d5a3675fce5ed9b4b7cb387ea5d37c2f18d/rpds_py-0.26.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f53ec51f9d24e9638a40cabb95078ade8c99251945dad8d57bf4aabe86ecee35", size = 346692 }, + { url = "https://files.pythonhosted.org/packages/e3/03/7e50423c04d78daf391da3cc4330bdb97042fc192a58b186f2d5deb7befd/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab504c4d654e4a29558eaa5bb8cea5fdc1703ea60a8099ffd9c758472cf913f", size = 379415 }, + { url = "https://files.pythonhosted.org/packages/57/00/d11ee60d4d3b16808432417951c63df803afb0e0fc672b5e8d07e9edaaae/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd0641abca296bc1a00183fe44f7fced8807ed49d501f188faa642d0e4975b83", size = 391783 }, + { url = "https://files.pythonhosted.org/packages/08/b3/1069c394d9c0d6d23c5b522e1f6546b65793a22950f6e0210adcc6f97c3e/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b312fecc1d017b5327afa81d4da1480f51c68810963a7336d92203dbb3d4f1", size = 512844 }, + { url = "https://files.pythonhosted.org/packages/08/3b/c4fbf0926800ed70b2c245ceca99c49f066456755f5d6eb8863c2c51e6d0/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c741107203954f6fc34d3066d213d0a0c40f7bb5aafd698fb39888af277c70d8", size = 402105 }, + { url = "https://files.pythonhosted.org/packages/1c/b0/db69b52ca07413e568dae9dc674627a22297abb144c4d6022c6d78f1e5cc/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3e55a7db08dc9a6ed5fb7103019d2c1a38a349ac41901f9f66d7f95750942f", size = 383440 }, + { url = "https://files.pythonhosted.org/packages/4c/e1/c65255ad5b63903e56b3bb3ff9dcc3f4f5c3badde5d08c741ee03903e951/rpds_py-0.26.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e851920caab2dbcae311fd28f4313c6953993893eb5c1bb367ec69d9a39e7ed", size = 412759 }, + { url = "https://files.pythonhosted.org/packages/e4/22/bb731077872377a93c6e93b8a9487d0406c70208985831034ccdeed39c8e/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dfbf280da5f876d0b00c81f26bedce274e72a678c28845453885a9b3c22ae632", size = 556032 }, + { url = "https://files.pythonhosted.org/packages/e0/8b/393322ce7bac5c4530fb96fc79cc9ea2f83e968ff5f6e873f905c493e1c4/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1cc81d14ddfa53d7f3906694d35d54d9d3f850ef8e4e99ee68bc0d1e5fed9a9c", size = 585416 }, + { url = "https://files.pythonhosted.org/packages/49/ae/769dc372211835bf759319a7aae70525c6eb523e3371842c65b7ef41c9c6/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dca83c498b4650a91efcf7b88d669b170256bf8017a5db6f3e06c2bf031f57e0", size = 554049 }, + { url = "https://files.pythonhosted.org/packages/6b/f9/4c43f9cc203d6ba44ce3146246cdc38619d92c7bd7bad4946a3491bd5b70/rpds_py-0.26.0-cp313-cp313t-win32.whl", hash = "sha256:4d11382bcaf12f80b51d790dee295c56a159633a8e81e6323b16e55d81ae37e9", size = 218428 }, + { url = "https://files.pythonhosted.org/packages/7e/8b/9286b7e822036a4a977f2f1e851c7345c20528dbd56b687bb67ed68a8ede/rpds_py-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff110acded3c22c033e637dd8896e411c7d3a11289b2edf041f86663dbc791e9", size = 231524 }, + { url = "https://files.pythonhosted.org/packages/55/07/029b7c45db910c74e182de626dfdae0ad489a949d84a468465cd0ca36355/rpds_py-0.26.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:da619979df60a940cd434084355c514c25cf8eb4cf9a508510682f6c851a4f7a", size = 364292 }, + { url = "https://files.pythonhosted.org/packages/13/d1/9b3d3f986216b4d1f584878dca15ce4797aaf5d372d738974ba737bf68d6/rpds_py-0.26.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ea89a2458a1a75f87caabefe789c87539ea4e43b40f18cff526052e35bbb4fdf", size = 350334 }, + { url = "https://files.pythonhosted.org/packages/18/98/16d5e7bc9ec715fa9668731d0cf97f6b032724e61696e2db3d47aeb89214/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feac1045b3327a45944e7dcbeb57530339f6b17baff154df51ef8b0da34c8c12", size = 384875 }, + { url = "https://files.pythonhosted.org/packages/f9/13/aa5e2b1ec5ab0e86a5c464d53514c0467bec6ba2507027d35fc81818358e/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b818a592bd69bfe437ee8368603d4a2d928c34cffcdf77c2e761a759ffd17d20", size = 399993 }, + { url = "https://files.pythonhosted.org/packages/17/03/8021810b0e97923abdbab6474c8b77c69bcb4b2c58330777df9ff69dc559/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a8b0dd8648709b62d9372fc00a57466f5fdeefed666afe3fea5a6c9539a0331", size = 516683 }, + { url = "https://files.pythonhosted.org/packages/dc/b1/da8e61c87c2f3d836954239fdbbfb477bb7b54d74974d8f6fcb34342d166/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d3498ad0df07d81112aa6ec6c95a7e7b1ae00929fb73e7ebee0f3faaeabad2f", size = 408825 }, + { url = "https://files.pythonhosted.org/packages/38/bc/1fc173edaaa0e52c94b02a655db20697cb5fa954ad5a8e15a2c784c5cbdd/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24a4146ccb15be237fdef10f331c568e1b0e505f8c8c9ed5d67759dac58ac246", size = 387292 }, + { url = "https://files.pythonhosted.org/packages/7c/eb/3a9bb4bd90867d21916f253caf4f0d0be7098671b6715ad1cead9fe7bab9/rpds_py-0.26.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9a63785467b2d73635957d32a4f6e73d5e4df497a16a6392fa066b753e87387", size = 420435 }, + { url = "https://files.pythonhosted.org/packages/cd/16/e066dcdb56f5632713445271a3f8d3d0b426d51ae9c0cca387799df58b02/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:de4ed93a8c91debfd5a047be327b7cc8b0cc6afe32a716bbbc4aedca9e2a83af", size = 562410 }, + { url = "https://files.pythonhosted.org/packages/60/22/ddbdec7eb82a0dc2e455be44c97c71c232983e21349836ce9f272e8a3c29/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:caf51943715b12af827696ec395bfa68f090a4c1a1d2509eb4e2cb69abbbdb33", size = 590724 }, + { url = "https://files.pythonhosted.org/packages/2c/b4/95744085e65b7187d83f2fcb0bef70716a1ea0a9e5d8f7f39a86e5d83424/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4a59e5bc386de021f56337f757301b337d7ab58baa40174fb150accd480bc953", size = 558285 }, + { url = "https://files.pythonhosted.org/packages/37/37/6309a75e464d1da2559446f9c811aa4d16343cebe3dbb73701e63f760caa/rpds_py-0.26.0-cp314-cp314-win32.whl", hash = "sha256:92c8db839367ef16a662478f0a2fe13e15f2227da3c1430a782ad0f6ee009ec9", size = 223459 }, + { url = "https://files.pythonhosted.org/packages/d9/6f/8e9c11214c46098b1d1391b7e02b70bb689ab963db3b19540cba17315291/rpds_py-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:b0afb8cdd034150d4d9f53926226ed27ad15b7f465e93d7468caaf5eafae0d37", size = 236083 }, + { url = "https://files.pythonhosted.org/packages/47/af/9c4638994dd623d51c39892edd9d08e8be8220a4b7e874fa02c2d6e91955/rpds_py-0.26.0-cp314-cp314-win_arm64.whl", hash = "sha256:ca3f059f4ba485d90c8dc75cb5ca897e15325e4e609812ce57f896607c1c0867", size = 223291 }, + { url = "https://files.pythonhosted.org/packages/4d/db/669a241144460474aab03e254326b32c42def83eb23458a10d163cb9b5ce/rpds_py-0.26.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:5afea17ab3a126006dc2f293b14ffc7ef3c85336cf451564a0515ed7648033da", size = 361445 }, + { url = "https://files.pythonhosted.org/packages/3b/2d/133f61cc5807c6c2fd086a46df0eb8f63a23f5df8306ff9f6d0fd168fecc/rpds_py-0.26.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:69f0c0a3df7fd3a7eec50a00396104bb9a843ea6d45fcc31c2d5243446ffd7a7", size = 347206 }, + { url = "https://files.pythonhosted.org/packages/05/bf/0e8fb4c05f70273469eecf82f6ccf37248558526a45321644826555db31b/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:801a71f70f9813e82d2513c9a96532551fce1e278ec0c64610992c49c04c2dad", size = 380330 }, + { url = "https://files.pythonhosted.org/packages/d4/a8/060d24185d8b24d3923322f8d0ede16df4ade226a74e747b8c7c978e3dd3/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df52098cde6d5e02fa75c1f6244f07971773adb4a26625edd5c18fee906fa84d", size = 392254 }, + { url = "https://files.pythonhosted.org/packages/b9/7b/7c2e8a9ee3e6bc0bae26bf29f5219955ca2fbb761dca996a83f5d2f773fe/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bc596b30f86dc6f0929499c9e574601679d0341a0108c25b9b358a042f51bca", size = 516094 }, + { url = "https://files.pythonhosted.org/packages/75/d6/f61cafbed8ba1499b9af9f1777a2a199cd888f74a96133d8833ce5eaa9c5/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9dfbe56b299cf5875b68eb6f0ebaadc9cac520a1989cac0db0765abfb3709c19", size = 402889 }, + { url = "https://files.pythonhosted.org/packages/92/19/c8ac0a8a8df2dd30cdec27f69298a5c13e9029500d6d76718130f5e5be10/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac64f4b2bdb4ea622175c9ab7cf09444e412e22c0e02e906978b3b488af5fde8", size = 384301 }, + { url = "https://files.pythonhosted.org/packages/41/e1/6b1859898bc292a9ce5776016c7312b672da00e25cec74d7beced1027286/rpds_py-0.26.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:181ef9b6bbf9845a264f9aa45c31836e9f3c1f13be565d0d010e964c661d1e2b", size = 412891 }, + { url = "https://files.pythonhosted.org/packages/ef/b9/ceb39af29913c07966a61367b3c08b4f71fad841e32c6b59a129d5974698/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:49028aa684c144ea502a8e847d23aed5e4c2ef7cadfa7d5eaafcb40864844b7a", size = 557044 }, + { url = "https://files.pythonhosted.org/packages/2f/27/35637b98380731a521f8ec4f3fd94e477964f04f6b2f8f7af8a2d889a4af/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e5d524d68a474a9688336045bbf76cb0def88549c1b2ad9dbfec1fb7cfbe9170", size = 585774 }, + { url = "https://files.pythonhosted.org/packages/52/d9/3f0f105420fecd18551b678c9a6ce60bd23986098b252a56d35781b3e7e9/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c1851f429b822831bd2edcbe0cfd12ee9ea77868f8d3daf267b189371671c80e", size = 554886 }, + { url = "https://files.pythonhosted.org/packages/6b/c5/347c056a90dc8dd9bc240a08c527315008e1b5042e7a4cf4ac027be9d38a/rpds_py-0.26.0-cp314-cp314t-win32.whl", hash = "sha256:7bdb17009696214c3b66bb3590c6d62e14ac5935e53e929bcdbc5a495987a84f", size = 219027 }, + { url = "https://files.pythonhosted.org/packages/75/04/5302cea1aa26d886d34cadbf2dc77d90d7737e576c0065f357b96dc7a1a6/rpds_py-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f14440b9573a6f76b4ee4770c13f0b5921f71dde3b6fcb8dabbefd13b7fe05d7", size = 232821 }, +] + [[package]] name = "ruff" version = "0.11.2" @@ -1307,6 +3243,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/3f/35f4041a82a68df89fe4af97c8bb44aa492dad924799cbb02078e9e303e6/s3fs-2025.3.0-py3-none-any.whl", hash = "sha256:88d803615baa04945156ca0e1498009b7acd3132c07198bd81b3e874846e0aa2", size = 30454 }, ] +[[package]] +name = "safe-pysha3" +version = "1.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a4/27/a942b8a95b97c5fb8d27d3b86b8d269546cf21f87d9ec6b44c3702ce67cc/safe_pysha3-1.0.5.tar.gz", hash = "sha256:88ceaad6af4b6bdecd2f54b31ad0e5e5e210d4f5ecabb1bd1fd3539ad61b7bf1", size = 827796 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/59/8d7f64a3eb6ebbde248cc0347a67d7825e2ba6edfa2bd180ebdfb599b3c5/safe_pysha3-1.0.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d137a73029c6c5a1db5791ae9fa62373827eee5226d19b79b836a6cf48b6b197", size = 190427 }, + { url = "https://files.pythonhosted.org/packages/da/6a/25e926ab9c8d714be2ee1d40f45eb425f438c968d97ede69a7cbff054094/safe_pysha3-1.0.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a0cb37252a8767992f354d7d2af2ef04730032927eb6af2057e71744c741287", size = 184478 }, + { url = "https://files.pythonhosted.org/packages/3a/da/7e1f252e6984f4b82393e4e083d3a48806e8a7225651559e50dec0000c68/safe_pysha3-1.0.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2019065f1b7d3db37cc52d091c9d5526d5d36a3e1b9efcf0b345c24e03755bff", size = 190543 }, + { url = "https://files.pythonhosted.org/packages/e6/a6/e134e353a4961090caf4f429044b845584fe11d2217960a2549943ef01ea/safe_pysha3-1.0.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa37d5d6138d5dd01d1035dba019b7525ad7c55669ded4524f589cddd13ea13b", size = 367223 }, +] + [[package]] name = "scipy" version = "1.15.2" @@ -1345,6 +3293,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0a/c8/b3f566db71461cabd4b2d5b39bcc24a7e1c119535c8361f81426be39bb47/scipy-1.15.2-cp313-cp313t-win_amd64.whl", hash = "sha256:fe8a9eb875d430d81755472c5ba75e84acc980e4a8f6204d402849234d3017db", size = 40477705 }, ] +[[package]] +name = "seaborn" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "numpy" }, + { name = "pandas" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914 }, +] + +[[package]] +name = "semantic-version" +version = "2.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/31/f2289ce78b9b473d582568c234e104d2a342fd658cc288a7553d83bb8595/semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c", size = 52289 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177", size = 15552 }, +] + [[package]] name = "shapely" version = "2.0.7" @@ -1377,6 +3348,154 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, ] +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 }, +] + +[[package]] +name = "sparse" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numba" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/74/5c674277fc3d61bd1863d233a8e1f7ddf35cb1adeeaf9973888629e7a9b1/sparse-0.17.0.tar.gz", hash = "sha256:6b1ad51a810c5be40b6f95e28513ec810fe1c785923bd83b2e4839a751df4bf7", size = 642387 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/fd/42a1720542199ae6ff0f9c37bbd55dd3033ddd7bbe00d68cde09d6824887/sparse-0.17.0-py2.py3-none-any.whl", hash = "sha256:1922d1d97f692b1061c4f03a1dd6ee21850aedc88e171aa845715f5069952f18", size = 259370 }, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.41" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/66/45b165c595ec89aa7dcc2c1cd222ab269bc753f1fc7a1e68f8481bd957bf/sqlalchemy-2.0.41.tar.gz", hash = "sha256:edba70118c4be3c2b1f90754d308d0b79c6fe2c0fdc52d8ddf603916f83f4db9", size = 9689424 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/2a/f1f4e068b371154740dd10fb81afb5240d5af4aa0087b88d8b308b5429c2/sqlalchemy-2.0.41-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:81f413674d85cfd0dfcd6512e10e0f33c19c21860342a4890c3a2b59479929f9", size = 2119645 }, + { url = "https://files.pythonhosted.org/packages/9b/e8/c664a7e73d36fbfc4730f8cf2bf930444ea87270f2825efbe17bf808b998/sqlalchemy-2.0.41-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:598d9ebc1e796431bbd068e41e4de4dc34312b7aa3292571bb3674a0cb415dd1", size = 2107399 }, + { url = "https://files.pythonhosted.org/packages/5c/78/8a9cf6c5e7135540cb682128d091d6afa1b9e48bd049b0d691bf54114f70/sqlalchemy-2.0.41-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a104c5694dfd2d864a6f91b0956eb5d5883234119cb40010115fd45a16da5e70", size = 3293269 }, + { url = "https://files.pythonhosted.org/packages/3c/35/f74add3978c20de6323fb11cb5162702670cc7a9420033befb43d8d5b7a4/sqlalchemy-2.0.41-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6145afea51ff0af7f2564a05fa95eb46f542919e6523729663a5d285ecb3cf5e", size = 3303364 }, + { url = "https://files.pythonhosted.org/packages/6a/d4/c990f37f52c3f7748ebe98883e2a0f7d038108c2c5a82468d1ff3eec50b7/sqlalchemy-2.0.41-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b46fa6eae1cd1c20e6e6f44e19984d438b6b2d8616d21d783d150df714f44078", size = 3229072 }, + { url = "https://files.pythonhosted.org/packages/15/69/cab11fecc7eb64bc561011be2bd03d065b762d87add52a4ca0aca2e12904/sqlalchemy-2.0.41-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41836fe661cc98abfae476e14ba1906220f92c4e528771a8a3ae6a151242d2ae", size = 3268074 }, + { url = "https://files.pythonhosted.org/packages/5c/ca/0c19ec16858585d37767b167fc9602593f98998a68a798450558239fb04a/sqlalchemy-2.0.41-cp312-cp312-win32.whl", hash = "sha256:a8808d5cf866c781150d36a3c8eb3adccfa41a8105d031bf27e92c251e3969d6", size = 2084514 }, + { url = "https://files.pythonhosted.org/packages/7f/23/4c2833d78ff3010a4e17f984c734f52b531a8c9060a50429c9d4b0211be6/sqlalchemy-2.0.41-cp312-cp312-win_amd64.whl", hash = "sha256:5b14e97886199c1f52c14629c11d90c11fbb09e9334fa7bb5f6d068d9ced0ce0", size = 2111557 }, + { url = "https://files.pythonhosted.org/packages/d3/ad/2e1c6d4f235a97eeef52d0200d8ddda16f6c4dd70ae5ad88c46963440480/sqlalchemy-2.0.41-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4eeb195cdedaf17aab6b247894ff2734dcead6c08f748e617bfe05bd5a218443", size = 2115491 }, + { url = "https://files.pythonhosted.org/packages/cf/8d/be490e5db8400dacc89056f78a52d44b04fbf75e8439569d5b879623a53b/sqlalchemy-2.0.41-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d4ae769b9c1c7757e4ccce94b0641bc203bbdf43ba7a2413ab2523d8d047d8dc", size = 2102827 }, + { url = "https://files.pythonhosted.org/packages/a0/72/c97ad430f0b0e78efaf2791342e13ffeafcbb3c06242f01a3bb8fe44f65d/sqlalchemy-2.0.41-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a62448526dd9ed3e3beedc93df9bb6b55a436ed1474db31a2af13b313a70a7e1", size = 3225224 }, + { url = "https://files.pythonhosted.org/packages/5e/51/5ba9ea3246ea068630acf35a6ba0d181e99f1af1afd17e159eac7e8bc2b8/sqlalchemy-2.0.41-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc56c9788617b8964ad02e8fcfeed4001c1f8ba91a9e1f31483c0dffb207002a", size = 3230045 }, + { url = "https://files.pythonhosted.org/packages/78/2f/8c14443b2acea700c62f9b4a8bad9e49fc1b65cfb260edead71fd38e9f19/sqlalchemy-2.0.41-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c153265408d18de4cc5ded1941dcd8315894572cddd3c58df5d5b5705b3fa28d", size = 3159357 }, + { url = "https://files.pythonhosted.org/packages/fc/b2/43eacbf6ccc5276d76cea18cb7c3d73e294d6fb21f9ff8b4eef9b42bbfd5/sqlalchemy-2.0.41-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f67766965996e63bb46cfbf2ce5355fc32d9dd3b8ad7e536a920ff9ee422e23", size = 3197511 }, + { url = "https://files.pythonhosted.org/packages/fa/2e/677c17c5d6a004c3c45334ab1dbe7b7deb834430b282b8a0f75ae220c8eb/sqlalchemy-2.0.41-cp313-cp313-win32.whl", hash = "sha256:bfc9064f6658a3d1cadeaa0ba07570b83ce6801a1314985bf98ec9b95d74e15f", size = 2082420 }, + { url = "https://files.pythonhosted.org/packages/e9/61/e8c1b9b6307c57157d328dd8b8348ddc4c47ffdf1279365a13b2b98b8049/sqlalchemy-2.0.41-cp313-cp313-win_amd64.whl", hash = "sha256:82ca366a844eb551daff9d2e6e7a9e5e76d2612c8564f58db6c19a726869c1df", size = 2108329 }, + { url = "https://files.pythonhosted.org/packages/1c/fc/9ba22f01b5cdacc8f5ed0d22304718d2c758fce3fd49a5372b886a86f37c/sqlalchemy-2.0.41-py3-none-any.whl", hash = "sha256:57df5dc6fdb5ed1a88a1ed2195fd31927e705cad62dedd86b46972752a80f576", size = 1911224 }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, +] + +[[package]] +name = "tblib" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/95/4b3044ec4bf248186769629bbfb495a458deb6e4c1f9eff7f298ae1e336e/tblib-3.1.0.tar.gz", hash = "sha256:06404c2c9f07f66fee2d7d6ad43accc46f9c3361714d9b8426e7f47e595cd652", size = 30766 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/44/aa5c8b10b2cce7a053018e0d132bd58e27527a0243c4985383d5b6fd93e9/tblib-3.1.0-py3-none-any.whl", hash = "sha256:670bb4582578134b3d81a84afa1b016128b429f3d48e6cbbaecc9d15675e984e", size = 12552 }, +] + +[[package]] +name = "toolz" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/0b/d80dfa675bf592f636d1ea0b835eab4ec8df6e9415d8cfd766df54456123/toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02", size = 66790 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/98/eb27cc78ad3af8e302c9d8ff4977f5026676e130d28dd7578132a457170c/toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236", size = 56383 }, +] + +[[package]] +name = "tornado" +version = "6.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/89/c72771c81d25d53fe33e3dca61c233b665b2780f21820ba6fd2c6793c12b/tornado-6.5.1.tar.gz", hash = "sha256:84ceece391e8eb9b2b95578db65e920d2a61070260594819589609ba9bc6308c", size = 509934 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/89/f4532dee6843c9e0ebc4e28d4be04c67f54f60813e4bf73d595fe7567452/tornado-6.5.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d50065ba7fd11d3bd41bcad0825227cc9a95154bad83239357094c36708001f7", size = 441948 }, + { url = "https://files.pythonhosted.org/packages/15/9a/557406b62cffa395d18772e0cdcf03bed2fff03b374677348eef9f6a3792/tornado-6.5.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9e9ca370f717997cb85606d074b0e5b247282cf5e2e1611568b8821afe0342d6", size = 440112 }, + { url = "https://files.pythonhosted.org/packages/55/82/7721b7319013a3cf881f4dffa4f60ceff07b31b394e459984e7a36dc99ec/tornado-6.5.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b77e9dfa7ed69754a54c89d82ef746398be82f749df69c4d3abe75c4d1ff4888", size = 443672 }, + { url = "https://files.pythonhosted.org/packages/7d/42/d11c4376e7d101171b94e03cef0cbce43e823ed6567ceda571f54cf6e3ce/tornado-6.5.1-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:253b76040ee3bab8bcf7ba9feb136436a3787208717a1fb9f2c16b744fba7331", size = 443019 }, + { url = "https://files.pythonhosted.org/packages/7d/f7/0c48ba992d875521ac761e6e04b0a1750f8150ae42ea26df1852d6a98942/tornado-6.5.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:308473f4cc5a76227157cdf904de33ac268af770b2c5f05ca6c1161d82fdd95e", size = 443252 }, + { url = "https://files.pythonhosted.org/packages/89/46/d8d7413d11987e316df4ad42e16023cd62666a3c0dfa1518ffa30b8df06c/tornado-6.5.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:caec6314ce8a81cf69bd89909f4b633b9f523834dc1a352021775d45e51d9401", size = 443930 }, + { url = "https://files.pythonhosted.org/packages/78/b2/f8049221c96a06df89bed68260e8ca94beca5ea532ffc63b1175ad31f9cc/tornado-6.5.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:13ce6e3396c24e2808774741331638ee6c2f50b114b97a55c5b442df65fd9692", size = 443351 }, + { url = "https://files.pythonhosted.org/packages/76/ff/6a0079e65b326cc222a54720a748e04a4db246870c4da54ece4577bfa702/tornado-6.5.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5cae6145f4cdf5ab24744526cc0f55a17d76f02c98f4cff9daa08ae9a217448a", size = 443328 }, + { url = "https://files.pythonhosted.org/packages/49/18/e3f902a1d21f14035b5bc6246a8c0f51e0eef562ace3a2cea403c1fb7021/tornado-6.5.1-cp39-abi3-win32.whl", hash = "sha256:e0a36e1bc684dca10b1aa75a31df8bdfed656831489bc1e6a6ebed05dc1ec365", size = 444396 }, + { url = "https://files.pythonhosted.org/packages/7b/09/6526e32bf1049ee7de3bebba81572673b19a2a8541f795d887e92af1a8bc/tornado-6.5.1-cp39-abi3-win_amd64.whl", hash = "sha256:908e7d64567cecd4c2b458075589a775063453aeb1d2a1853eedb806922f568b", size = 444840 }, + { url = "https://files.pythonhosted.org/packages/55/a7/535c44c7bea4578e48281d83c615219f3ab19e6abc67625ef637c73987be/tornado-6.5.1-cp39-abi3-win_arm64.whl", hash = "sha256:02420a0eb7bf617257b9935e2b754d1b63897525d8a289c9d65690d580b4dcf7", size = 443596 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, +] + +[[package]] +name = "trie" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "eth-hash" }, + { name = "eth-utils" }, + { name = "hexbytes" }, + { name = "rlp" }, + { name = "sortedcontainers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/2f/5ec6be52952de47e79b1d250c00a922365a76503e3c75605c1cd890c61aa/trie-3.1.0.tar.gz", hash = "sha256:b31fd3376d6dccfe8ad13b525e233f2c268d5c48afb90a4de09672423d4b1026", size = 72800 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/79/e6e105236eb1bb0c3ac82c968c143de456a3192c17d60df2f8c528eb3323/trie-3.1.0-py3-none-any.whl", hash = "sha256:dfc3e6ac0e76f0efa900ec1bfd082f0f1ba87f95cbfd81cc12338b03f4c679c4", size = 38940 }, +] + [[package]] name = "typing-extensions" version = "4.13.0" @@ -1386,6 +3505,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/86/39b65d676ec5732de17b7e3c476e45bb80ec64eb50737a8dce1a4178aba1/typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5", size = 45683 }, ] +[[package]] +name = "typing-inspection" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552 }, +] + [[package]] name = "typing-validation" version = "1.2.12" @@ -1413,6 +3544,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, ] +[[package]] +name = "varint" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/fe/1ea0ba0896dfa47186692655b86db3214c4b7c9e0e76c7b1dc257d101ab1/varint-1.0.2.tar.gz", hash = "sha256:a6ecc02377ac5ee9d65a6a8ad45c9ff1dac8ccee19400a5950fb51d594214ca5", size = 1886 } + [[package]] name = "virtualenv" version = "20.29.3" @@ -1427,6 +3564,96 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c2/eb/c6db6e3001d58c6a9e67c74bb7b4206767caa3ccc28c6b9eaf4c23fb4e34/virtualenv-20.29.3-py3-none-any.whl", hash = "sha256:3e3d00f5807e83b234dfb6122bf37cfadf4be216c53a49ac059d02414f819170", size = 4301458 }, ] +[[package]] +name = "watchdog" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/a6/d6ef450393dac5734c63c40a131f66808d2e6f59f6165ab38c98fbe4e6ec/watchdog-3.0.0.tar.gz", hash = "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9", size = 124593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/28/631872d7fbc45527037060db8c838b47a129a6c09d2297d6dddcfa283cf2/watchdog-3.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a", size = 82049 }, + { url = "https://files.pythonhosted.org/packages/c0/a2/4e3230bdc1fb878b152a2c66aa941732776f4545bd68135d490591d66713/watchdog-3.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44", size = 82049 }, + { url = "https://files.pythonhosted.org/packages/21/72/46fd174352cd88b9157ade77e3b8835125d4b1e5186fc7f1e8c44664e029/watchdog-3.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a", size = 82052 }, + { url = "https://files.pythonhosted.org/packages/74/3c/e4b77f4f069aca2b6e35925db7a1aa6cb600dcb52fc3e962284640ca37f3/watchdog-3.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709", size = 82050 }, + { url = "https://files.pythonhosted.org/packages/71/3a/b12740f4f60861240d57b42a2ac6ac0a2821db506c4435f7872c1fad867d/watchdog-3.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83", size = 82050 }, + { url = "https://files.pythonhosted.org/packages/40/1b/4e6d3e0f587587931f590531b4ed08070d71a9efb35541d792a68d8ee593/watchdog-3.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d", size = 82049 }, + { url = "https://files.pythonhosted.org/packages/2b/f0/456948b865ab259784f774154e7d65844fa9757522fdb11533fbf8ae7aca/watchdog-3.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33", size = 82051 }, + { url = "https://files.pythonhosted.org/packages/55/0d/bfc2a0d425b12444a2dc245a934c065bbb7bd9833fff071cba79c21bb76e/watchdog-3.0.0-py3-none-win32.whl", hash = "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f", size = 82038 }, + { url = "https://files.pythonhosted.org/packages/9b/6e/ce8d124d03cd3f2941365d9c81d62e3afe43f2dc7e6e86274fa9c2ec2d5b/watchdog-3.0.0-py3-none-win_amd64.whl", hash = "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c", size = 82040 }, + { url = "https://files.pythonhosted.org/packages/ba/0c/cd0337069c468f22ef256e768ece74c78b511092f1004ab260268e1af4a9/watchdog-3.0.0-py3-none-win_ia64.whl", hash = "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759", size = 82040 }, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, +] + +[[package]] +name = "web3" +version = "6.20.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "eth-abi" }, + { name = "eth-account" }, + { name = "eth-hash", extra = ["pycryptodome"] }, + { name = "eth-typing" }, + { name = "eth-utils" }, + { name = "hexbytes" }, + { name = "jsonschema" }, + { name = "lru-dict" }, + { name = "protobuf" }, + { name = "pyunormalize" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "requests" }, + { name = "typing-extensions" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/c1/9fc3a82718e5de39454e8b1558d4b2d41b947c860eb8f4850bebff5ee00a/web3-6.20.1.tar.gz", hash = "sha256:a29bc1863734e1c05f128ddbc56878f299ea71776806e667b581a83b5d5be0ed", size = 2302112 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/20/aae5c425460205b98823a5f6e7aca013d9e8ca93aedf353675d9ba4e1ba4/web3-6.20.1-py3-none-any.whl", hash = "sha256:16fe72aeb48bbd5f7e7e64b323a0d3a16522a28eb4f19ef9f9dd6ce7ee813c82", size = 1610958 }, +] + +[package.optional-dependencies] +tester = [ + { name = "eth-tester", extra = ["py-evm"] }, + { name = "py-geth" }, +] + +[[package]] +name = "websockets" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437 }, + { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096 }, + { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332 }, + { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152 }, + { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096 }, + { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523 }, + { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790 }, + { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165 }, + { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160 }, + { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395 }, + { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841 }, + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440 }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098 }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329 }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111 }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054 }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496 }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829 }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217 }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195 }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393 }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837 }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 }, +] + [[package]] name = "wrapt" version = "1.17.2" @@ -1483,6 +3710,37 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/91/19/5dbac77ca82f009f8267d0e7cca4fd3be2a435d83ed98f6f8b9c5cb1747e/xarray-2025.3.0-py3-none-any.whl", hash = "sha256:022b7eac5ebc26d70d34211de732ec1a15127a40455f9492acdde7fbe5bbf091", size = 1270849 }, ] +[package.optional-dependencies] +complete = [ + { name = "bottleneck" }, + { name = "cartopy" }, + { name = "cftime" }, + { name = "dask", extra = ["complete"] }, + { name = "flox" }, + { name = "fsspec" }, + { name = "h5netcdf" }, + { name = "matplotlib" }, + { name = "nc-time-axis" }, + { name = "netcdf4" }, + { name = "numba" }, + { name = "numbagg" }, + { name = "opt-einsum" }, + { name = "pooch" }, + { name = "scipy" }, + { name = "seaborn" }, + { name = "sparse" }, + { name = "zarr" }, +] + +[[package]] +name = "xyzservices" +version = "2025.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/af/c0f7f97bb320d14c089476f487b81f733238cc5603e0914f2e409f49d589/xyzservices-2025.4.0.tar.gz", hash = "sha256:6fe764713648fac53450fbc61a3c366cb6ae5335a1b2ae0c3796b495de3709d8", size = 1134722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/7d/b77455d7c7c51255b2992b429107fab811b2e36ceaf76da1e55a045dc568/xyzservices-2025.4.0-py3-none-any.whl", hash = "sha256:8d4db9a59213ccb4ce1cf70210584f30b10795bff47627cdfb862b39ff6e10c9", size = 90391 }, +] + [[package]] name = "yarl" version = "1.18.3" @@ -1531,7 +3789,7 @@ wheels = [ [[package]] name = "zarr" -version = "3.0.6" +version = "3.0.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "donfig" }, @@ -1540,7 +3798,16 @@ dependencies = [ { name = "packaging" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6f/bf/2820e8ad5f086ea5cd4c4d65eb5e0113f98145ddf5810cec1c75fb10fb98/zarr-3.0.6.tar.gz", hash = "sha256:6ef23c740e34917a2a1099471361537732942e49f0cabe95c9b7124cd0d6d84f", size = 240812 } +sdist = { url = "https://files.pythonhosted.org/packages/07/10/a1b6eabeb5a8681916568a7c6a7a1849c952131be127ccbd57e05d47d43e/zarr-3.0.10.tar.gz", hash = "sha256:1fd1318ade646f692d8f604be0e0ad125675a061196e612e3f7a2cfa9e957d1c", size = 263594 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/57/3329346940f78de49047ddcb03fdbca9e16450c3a942688bf24201a322e5/zarr-3.0.10-py3-none-any.whl", hash = "sha256:110724c045fbe4ff5509a8a2a6b6098cb244a6af43da85eaeecef9821473163f", size = 209508 }, +] + +[[package]] +name = "zict" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/ac/3c494dd7ec5122cff8252c1a209b282c0867af029f805ae9befd73ae37eb/zict-3.0.0.tar.gz", hash = "sha256:e321e263b6a97aafc0790c3cfb3c04656b7066e6738c37fffcca95d803c9fba5", size = 33238 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/38/56a1c129577d20dc975a934ccc3f7f16276eab04ff0ffbe02ea348407a37/zarr-3.0.6-py3-none-any.whl", hash = "sha256:dba078726b6e4defb0ae6a852b7e7035ce163d89d485961681ece49191dcce82", size = 196357 }, + { url = "https://files.pythonhosted.org/packages/80/ab/11a76c1e2126084fde2639514f24e6111b789b0bfa4fc6264a8975c7e1f1/zict-3.0.0-py2.py3-none-any.whl", hash = "sha256:5796e36bd0e0cc8cf0fbc1ace6a68912611c1dbd74750a3f3026b9b9d6a327ae", size = 43332 }, ]