From 806eea8c40cc7e374d04086defae4e660a471488 Mon Sep 17 00:00:00 2001 From: Sergey Rybakov Date: Tue, 2 Apr 2024 19:54:08 +0300 Subject: [PATCH 01/19] attributes invokation fixed --- deker/ABC/base_array.py | 60 +- deker/__init__.py | 9 +- deker/client.py | 4 +- deker/integrity.py | 13 +- deker/locks.py | 2 +- deker/log.py | 1 + deker/validators.py | 50 +- poetry.lock | 766 +++++++++--------- pyproject.toml | 2 +- tests/parameters/array_params.py | 1 - .../test_arrays/test_array_methods.py | 77 +- .../test_arrays/test_varray_methods.py | 1 - .../test_client/test_client_methods.py | 5 +- .../test_collection_methods.py | 1 - .../test_concurrency/test_in_processes.py | 7 +- .../test_schemas/test_array_schema.py | 58 +- .../test_schemas/test_attributes_schema.py | 145 +++- ...est_time_dimension_attributes_validator.py | 39 +- 18 files changed, 775 insertions(+), 466 deletions(-) diff --git a/deker/ABC/base_array.py b/deker/ABC/base_array.py index a435d59..301f4a9 100644 --- a/deker/ABC/base_array.py +++ b/deker/ABC/base_array.py @@ -38,7 +38,7 @@ from deker.tools.schema import create_dimensions from deker.types.private.classes import ArrayMeta, Serializer from deker.types.private.typings import FancySlice, Numeric, Slice -from deker.validators import process_attributes, is_valid_uuid, validate_custom_attributes_update +from deker.validators import is_valid_uuid, process_attributes, validate_custom_attributes_update if TYPE_CHECKING: @@ -392,26 +392,54 @@ def update_custom_attributes(self, attributes: dict) -> None: self.logger.info(f"{self!s} custom attributes updated: {attributes}") def _create_meta(self) -> str: - """Serialize array into metadata string.""" + """Serialize array into metadata JSON string.""" + + def serialize_value( + val: Any, + ) -> Union[Tuple[str, int, float, tuple], str, int, float, tuple]: + """Jsonify attribute value. + + :param val: complex number + """ + if isinstance(val, datetime): + return val.isoformat() + if isinstance(val, np.ndarray): + return val.tolist() # type: ignore[attr-defined] + if isinstance(val, complex): + return str(val) + if isinstance(val, np.integer): + return int(val) + if isinstance(val, np.floating): + return float(val) + if isinstance(val, np.complexfloating): + return str(complex(val)) + if isinstance(val, (list, tuple)): + return serialize_nested_tuples(val) # type: ignore[arg-type] + + return val + + def serialize_nested_tuples( + attr: tuple, + ) -> Tuple[Union[Tuple[str, int, float, tuple], str, int, float, tuple], ...]: + """Jsonify nested tuples. + + :param attr: tuple instance + """ + elements = [] + for val in attr: + if isinstance(val, tuple): + elements.append(serialize_nested_tuples(val)) + else: + elements.append(serialize_value(val)) # type: ignore[arg-type] + return tuple(elements) + primary_attrs, custom_attrs = deepcopy(self.primary_attributes), deepcopy( self.custom_attributes ) for attrs in (primary_attrs, custom_attrs): for key, value in attrs.items(): - if isinstance(value, datetime): - attrs[key] = value.isoformat() - elif isinstance(value, np.ndarray): - attrs[key] = value.tolist() - elif isinstance(value, (list, tuple)): - elements = [] - for element in value: - if isinstance(element, np.integer): - elements.append(int(element)) - else: - elements.append(element) - attrs[key] = tuple(elements) - else: - attrs[key] = value + attrs[key] = serialize_value(value) + return json.dumps( { "id": self.id, diff --git a/deker/__init__.py b/deker/__init__.py index 3a2dcaf..5f39d0c 100644 --- a/deker/__init__.py +++ b/deker/__init__.py @@ -17,11 +17,6 @@ # nopycln: file # isort: skip_file -from deker_local_adapters.storage_adapters.hdf5.hdf5_options import ( - HDF5Options, - HDF5CompressionOpts, -) - from deker.arrays import Array, VArray from deker.client import Client from deker.collection import Collection @@ -36,6 +31,10 @@ ) from deker.subset import Subset, VSubset from deker.types.public.classes import Scale +from deker_local_adapters.storage_adapters.hdf5.hdf5_options import ( + HDF5Options, + HDF5CompressionOpts, +) __all__ = ( # deker.adapters.hdf5 diff --git a/deker/client.py b/deker/client.py index 6924f38..a8fda4f 100644 --- a/deker/client.py +++ b/deker/client.py @@ -27,8 +27,8 @@ from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Tuple, Type, Union from deker_tools.data import convert_size_to_human -from deker_tools.path import is_path_valid from deker_tools.log import set_logger +from deker_tools.path import is_path_valid from psutil import swap_memory, virtual_memory from tqdm import tqdm @@ -43,7 +43,7 @@ ) from deker.integrity import IntegrityChecker from deker.locks import META_DIVIDER -from deker.log import SelfLoggerMixin, set_logging_level, format_string +from deker.log import SelfLoggerMixin, format_string, set_logging_level from deker.schemas import ArraySchema, VArraySchema from deker.tools import convert_human_memory_to_bytes from deker.types import ArrayLockMeta, CollectionLockMeta, LocksExtensions, LocksTypes, StorageSize diff --git a/deker/integrity.py b/deker/integrity.py index 17d70fa..746640a 100644 --- a/deker/integrity.py +++ b/deker/integrity.py @@ -33,6 +33,7 @@ from deker.tools import get_main_path, get_symlink_path from deker.types.private.enums import LocksExtensions + if TYPE_CHECKING: from deker.client import Client @@ -187,15 +188,15 @@ def _check_varrays_or_arrays( except DekerBaseApplicationError as e: if self.stop_on_error: raise DekerIntegrityError(str(e)) - self.errors[ - f"Collection {collection.name} arrays integrity errors:" - ].append(str(e)) + self.errors[f"Collection {collection.name} arrays integrity errors:"].append( + str(e) + ) except DekerMetaDataError as e: if self.stop_on_error: raise e - self.errors[ - f"Collection {collection.name} (V)Arrays initialization errors:" - ].append(str(e)) + self.errors[f"Collection {collection.name} (V)Arrays initialization errors:"].append( + str(e) + ) def check(self, collection: Collection) -> None: """Check if Arrays or VArrays and their locks in Collection are valid. diff --git a/deker/locks.py b/deker/locks.py index ac1053a..32339ac 100644 --- a/deker/locks.py +++ b/deker/locks.py @@ -23,7 +23,7 @@ from pathlib import Path from threading import get_native_id from time import sleep -from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Sequence, Union, Tuple +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Sequence, Tuple, Union from uuid import uuid4 from deker.ABC.base_locks import BaseLock diff --git a/deker/log.py b/deker/log.py index bd18745..0f99f63 100644 --- a/deker/log.py +++ b/deker/log.py @@ -19,6 +19,7 @@ from logging import Logger + _ROOT_DEKER_LOGGER_NAME = "Deker" _level = os.getenv("DEKER_LOGLEVEL", "WARNING") diff --git a/deker/validators.py b/deker/validators.py index 0be2e7e..3468507 100644 --- a/deker/validators.py +++ b/deker/validators.py @@ -23,6 +23,8 @@ from deker.dimensions import Dimension, TimeDimension from deker.errors import DekerValidationError +from deker.types import DTypeEnum + if TYPE_CHECKING: from deker.schemas import ArraySchema, AttributeSchema, VArraySchema @@ -42,17 +44,37 @@ def process_time_dimension_attrs(attributes: dict, attr_name: str) -> datetime.d return time_attribute +def __validate_attribute_type(attribute: object) -> None: + """Validate attribute type over allowed types. + + :param attribute: attribute object + """ + if isinstance(attribute, tuple): + for attr in attribute: + __validate_attribute_type(attr) + + dtype = type(attribute) + try: + DTypeEnum(dtype).value + except (ValueError, KeyError): + raise DekerValidationError(f"Invalid dtype value {dtype}") + + def __process_attributes_types( attrs_schema: Tuple["AttributeSchema", ...], primary_attributes: dict, custom_attributes: dict, + dimensions: list, ) -> None: """Validate attributes types over schema and update dicts if needed. :param attrs_schema: attributes schema :param primary_attributes: primary attributes to validate :param custom_attributes: custom attributes to validate + :param dimensions: list of dimensions """ + from deker.schemas import TimeDimensionSchema + attributes = {**primary_attributes, **custom_attributes} for attr in attrs_schema: if attr.primary: @@ -65,6 +87,10 @@ def __process_attributes_types( f"expected {attr.dtype}" ) + # validate tuples contents type + if isinstance(primary_attributes[attr.name], tuple): + __validate_attribute_type(primary_attributes[attr.name]) + else: # check if custom attribute is not missing and its type custom_attribute = custom_attributes.get(attr.name) @@ -76,11 +102,27 @@ def __process_attributes_types( if custom_attribute is None: if attr.dtype == datetime.datetime: - raise DekerValidationError(f'Custom attribute "{attr.name}" cannot be None') + for d in dimensions: + if ( + isinstance(d, TimeDimensionSchema) + and isinstance(d.start_value, str) + and d.start_value.startswith("$" + attr.name) + ): + raise DekerValidationError( + f'Custom attribute "{attr.name}" cannot be None' + ) custom_attributes[attr.name] = None - # convert attribute with datetime to utc if needed - if attr.dtype == datetime.datetime and attr.name in attributes: + # validate tuples + if isinstance(custom_attributes[attr.name], tuple): + __validate_attribute_type(custom_attributes[attr.name]) + + # convert datetime attribute with set datetime value to utc if needed + if ( + attr.dtype == datetime.datetime + and attr.name in attributes + and attributes[attr.name] is not None + ): try: utc = get_utc(attributes[attr.name]) if attr.primary: @@ -138,7 +180,7 @@ def process_attributes( f"Invalid attributes: {sorted(extra_names)}" ) __process_attributes_types( - attrs_schema, primary_attributes, custom_attributes # type: ignore[arg-type] + attrs_schema, primary_attributes, custom_attributes, schema.dimensions # type: ignore[arg-type] ) return primary_attributes, custom_attributes diff --git a/poetry.lock b/poetry.lock index 6748a90..2ae9f50 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,25 +1,25 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. [[package]] name = "alabaster" -version = "0.7.13" -description = "A configurable sidebar-enabled Sphinx theme" +version = "0.7.16" +description = "A light, configurable Sphinx theme" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" files = [ - {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, - {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, + {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, + {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, ] [[package]] name = "anyio" -version = "4.2.0" +version = "4.3.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = true python-versions = ">=3.8" files = [ - {file = "anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee"}, - {file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"}, + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, ] [package.dependencies] @@ -56,13 +56,13 @@ limiter = ["cachecontrol[filecache] (>=0.12.6)", "lockfile (>=0.12.2)"] [[package]] name = "apeye-core" -version = "1.1.4" +version = "1.1.5" description = "Core (offline) functionality for the apeye library." optional = false python-versions = ">=3.6.1" files = [ - {file = "apeye_core-1.1.4-py3-none-any.whl", hash = "sha256:084bc696448d3ac428fece41c1f2eb08fa9d9ce1d1b2f4d43187e3def4528a60"}, - {file = "apeye_core-1.1.4.tar.gz", hash = "sha256:72bb89fed3baa647cb81aa28e1d851787edcbf9573853b5d2b5f87c02f50eaf5"}, + {file = "apeye_core-1.1.5-py3-none-any.whl", hash = "sha256:dc27a93f8c9e246b3b238c5ea51edf6115ab2618ef029b9f2d9a190ec8228fbf"}, + {file = "apeye_core-1.1.5.tar.gz", hash = "sha256:5de72ed3d00cc9b20fea55e54b7ab8f5ef8500eb33a5368bc162a5585e238a55"}, ] [package.dependencies] @@ -82,31 +82,32 @@ files = [ [[package]] name = "attrs" -version = "23.1.0" +version = "23.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] +dev = ["attrs[tests]", "pre-commit"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] name = "autodocsumm" -version = "0.2.11" +version = "0.2.12" description = "Extended sphinx autodoc including automatic autosummaries" optional = false python-versions = ">=3.7" files = [ - {file = "autodocsumm-0.2.11-py3-none-any.whl", hash = "sha256:f1d0a623bf1ad64d979a9e23fd360d1fb1b8f869beaf3197f711552cddc174e2"}, - {file = "autodocsumm-0.2.11.tar.gz", hash = "sha256:183212bd9e9f3b58a96bb21b7958ee4e06224107aa45b2fd894b61b83581b9a9"}, + {file = "autodocsumm-0.2.12-py3-none-any.whl", hash = "sha256:b842b53c686c07a4f174721ca4e729b027367703dbf42e2508863a3c6d6c049c"}, + {file = "autodocsumm-0.2.12.tar.gz", hash = "sha256:848fe8c38df433c6635489499b969cb47cc389ed3d7b6e75c8ccbc94d4b3bf9e"}, ] [package.dependencies] @@ -151,19 +152,22 @@ yaml = ["PyYAML"] [[package]] name = "beautifulsoup4" -version = "4.12.2" +version = "4.12.3" description = "Screen-scraping library" optional = false python-versions = ">=3.6.0" files = [ - {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, - {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, ] [package.dependencies] soupsieve = ">1.2" [package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] @@ -218,34 +222,34 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "cachecontrol" -version = "0.13.1" +version = "0.14.0" description = "httplib2 caching for requests" optional = false python-versions = ">=3.7" files = [ - {file = "cachecontrol-0.13.1-py3-none-any.whl", hash = "sha256:95dedbec849f46dda3137866dc28b9d133fc9af55f5b805ab1291833e4457aa4"}, - {file = "cachecontrol-0.13.1.tar.gz", hash = "sha256:f012366b79d2243a6118309ce73151bf52a38d4a5dac8ea57f09bd29087e506b"}, + {file = "cachecontrol-0.14.0-py3-none-any.whl", hash = "sha256:f5bf3f0620c38db2e5122c0726bdebb0d16869de966ea6a2befe92470b740ea0"}, + {file = "cachecontrol-0.14.0.tar.gz", hash = "sha256:7db1195b41c81f8274a7bbd97c956f44e8348265a1bc7641c37dfebc39f0c938"}, ] [package.dependencies] filelock = {version = ">=3.8.0", optional = true, markers = "extra == \"filecache\""} -msgpack = ">=0.5.2" +msgpack = ">=0.5.2,<2.0.0" requests = ">=2.16.0" [package.extras] -dev = ["CacheControl[filecache,redis]", "black", "build", "cherrypy", "mypy", "pytest", "pytest-cov", "sphinx", "tox", "types-redis", "types-requests"] +dev = ["CacheControl[filecache,redis]", "black", "build", "cherrypy", "furo", "mypy", "pytest", "pytest-cov", "sphinx", "sphinx-copybutton", "tox", "types-redis", "types-requests"] filecache = ["filelock (>=3.8.0)"] redis = ["redis (>=2.10.5)"] [[package]] name = "certifi" -version = "2023.11.17" +version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] @@ -451,18 +455,18 @@ toml = ["tomli"] [[package]] name = "cssutils" -version = "2.9.0" +version = "2.10.2" description = "A CSS Cascading Style Sheets library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "cssutils-2.9.0-py3-none-any.whl", hash = "sha256:f8b013169e281c0c6083207366c5005f5dd4549055f7aba840384fb06a78745c"}, - {file = "cssutils-2.9.0.tar.gz", hash = "sha256:89477b3d17d790e97b9fb4def708767061055795aae6f7c82ae32e967c9be4cd"}, + {file = "cssutils-2.10.2-py3-none-any.whl", hash = "sha256:4ad7d2f29270b22cf199f65a6b5e795f2c3130f3b9fb50c3d45e5054ef86e41a"}, + {file = "cssutils-2.10.2.tar.gz", hash = "sha256:93cf92a350b1c123b17feff042e212f94d960975a3ed145743d84ebe8ccec7ab"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["cssselect", "importlib-resources", "jaraco.test (>=5.1)", "lxml", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["cssselect", "importlib-resources", "jaraco.test (>=5.1)", "lxml", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [[package]] name = "darglint" @@ -511,12 +515,12 @@ numpy = ">=1.18,<2.0" [[package]] name = "deker-server-adapters" -version = "1.0.3" +version = "1.0.4" description = "Plugin with server adapters for Deker" optional = true python-versions = ">=3.9,<4.0" files = [ - {file = "deker_server_adapters-1.0.3.tar.gz", hash = "sha256:421caf459c3dedf4f1950d56b86079ab73150def1e5cfe84b79627b72b11bfb5"}, + {file = "deker_server_adapters-1.0.4.tar.gz", hash = "sha256:08c067f4fff39896eede75b2c883d23250444b957d55d649946973e0748bf95a"}, ] [package.dependencies] @@ -524,15 +528,16 @@ httpx = {version = ">=0.24.1,<0.25.0", extras = ["http2"]} [[package]] name = "deker-shell" -version = "1.0.1" +version = "1.1.0" description = "Interactive shell for Deker" optional = true python-versions = ">=3.9,<4.0" files = [ - {file = "deker_shell-1.0.1.tar.gz", hash = "sha256:663a9557e3eaac093ace1f5e375d6188de66de0f3075cd7086e5d7058ce43cbf"}, + {file = "deker_shell-1.1.0.tar.gz", hash = "sha256:959adc4eb3aa719ad87f56dcc0540ce86b783b4560e5288133b10cba1d7b2240"}, ] [package.dependencies] +click = "8.1.7" ptpython = "3.0.23" [[package]] @@ -656,18 +661,18 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.13.1" +version = "3.13.3" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, + {file = "filelock-3.13.3-py3-none-any.whl", hash = "sha256:5ffa845303983e7a0b7ae17636509bc97997d58afeafa72fb141a17b152284cb"}, + {file = "filelock-3.13.3.tar.gz", hash = "sha256:a79895a25bbefdf55d1a2a0a80968f7dbb28edcd6d4234a0afb3f37ecde4b546"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] typing = ["typing-extensions (>=4.8)"] [[package]] @@ -792,20 +797,21 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.40" +version = "3.1.43" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" files = [ - {file = "GitPython-3.1.40-py3-none-any.whl", hash = "sha256:cf14627d5a8049ffbf49915732e5eddbe8134c3bdb9d476e6182b676fc573f8a"}, - {file = "GitPython-3.1.40.tar.gz", hash = "sha256:22b126e9ffb671fdd0c129796343a02bf67bf2994b35449ffc9321aa755e18a4"}, + {file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"}, + {file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"}, ] [package.dependencies] gitdb = ">=4.0.1,<5" [package.extras] -test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-instafail", "pytest-subtests", "pytest-sugar"] +doc = ["sphinx (==4.3.2)", "sphinx-autodoc-typehints", "sphinx-rtd-theme", "sphinxcontrib-applehelp (>=1.0.2,<=1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (>=2.0.0,<=2.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)"] +test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions"] [[package]] name = "h11" @@ -872,17 +878,17 @@ numpy = ">=1.17.3" [[package]] name = "hdf5plugin" -version = "4.3.0" +version = "4.4.0" description = "HDF5 Plugins for Windows, MacOS, and Linux" optional = false python-versions = ">=3.7" files = [ - {file = "hdf5plugin-4.3.0-py3-none-macosx_10_9_universal2.whl", hash = "sha256:958b7c1d86617d360647ada72d9b07e854dca2a772f06e3bc9a67de653bc297d"}, - {file = "hdf5plugin-4.3.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:da7fc3d9cb5a7532efe7e7aa04c58e1463905e3834e50d241d4aab98507db1cd"}, - {file = "hdf5plugin-4.3.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:231c3f7e2e0ca48ad3fbf44413fc3b008f5d3737b4ab7cd0e7603a6a42cd7d54"}, - {file = "hdf5plugin-4.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeb9e16355eb0330e8158d2cae9f1d0c283843670e2fd1e8d9cc7515464e36f4"}, - {file = "hdf5plugin-4.3.0-py3-none-win_amd64.whl", hash = "sha256:cb0562ac61aa9144a5e1a53eb228559ad78d9feac3dd4bedf37f1e3d2f3e760e"}, - {file = "hdf5plugin-4.3.0.tar.gz", hash = "sha256:0face8ee85817f168ab97cd72bd83dfd2803adbe7fc4881c5edf5b064ecb3684"}, + {file = "hdf5plugin-4.4.0-py3-none-macosx_10_9_universal2.whl", hash = "sha256:4bb9c0d65d3b76de0079242cd4cc5f94260e7eee80e22e09c4e6bcf0d91a6392"}, + {file = "hdf5plugin-4.4.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c75f91e3a80941e547e6510504a00b39482106c77d03e73ebe85dc7e88fb07ea"}, + {file = "hdf5plugin-4.4.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:847035458e0304164883faf24ced00f1d1574644310ae71303c489679ddd33aa"}, + {file = "hdf5plugin-4.4.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ca4a2d3077b6652111d9e3870935cc50b6df5fc76675df1dc325d52a1f06974"}, + {file = "hdf5plugin-4.4.0-py3-none-win_amd64.whl", hash = "sha256:843aed853d7cc09461ab58ecd836e0679c698816e46ab28f93e986bc76342fb3"}, + {file = "hdf5plugin-4.4.0.tar.gz", hash = "sha256:4142f54170843782eda7456b8f47d15910879ceb2608025aebf9464b7163913a"}, ] [package.dependencies] @@ -890,6 +896,7 @@ h5py = "*" [package.extras] dev = ["sphinx", "sphinx-rtd-theme"] +test = ["blosc2 (>=2.5.1)", "blosc2-grok (>=0.2.2)"] [[package]] name = "hpack" @@ -981,13 +988,13 @@ files = [ [[package]] name = "identify" -version = "2.5.33" +version = "2.5.35" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"}, - {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"}, + {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, + {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, ] [package.extras] @@ -1017,22 +1024,22 @@ files = [ [[package]] name = "importlib-metadata" -version = "7.0.1" +version = "7.1.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, - {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "iniconfig" @@ -1083,13 +1090,13 @@ testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] [[package]] name = "jinja2" -version = "3.0.3" +version = "3.1.3" description = "A very fast and expressive template engine." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, - {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] @@ -1186,61 +1193,71 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" -version = "2.1.3" +version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] [[package]] @@ -1278,67 +1295,67 @@ files = [ [[package]] name = "msgpack" -version = "1.0.7" +version = "1.0.8" description = "MessagePack serializer" optional = false python-versions = ">=3.8" files = [ - {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862"}, - {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329"}, - {file = "msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b"}, - {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6"}, - {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee"}, - {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d"}, - {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d"}, - {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1"}, - {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681"}, - {file = "msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9"}, - {file = "msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415"}, - {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84"}, - {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93"}, - {file = "msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8"}, - {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46"}, - {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b"}, - {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e"}, - {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002"}, - {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c"}, - {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e"}, - {file = "msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1"}, - {file = "msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82"}, - {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b"}, - {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4"}, - {file = "msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee"}, - {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5"}, - {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672"}, - {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075"}, - {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba"}, - {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c"}, - {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5"}, - {file = "msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9"}, - {file = "msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf"}, - {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95"}, - {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0"}, - {file = "msgpack-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7"}, - {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d"}, - {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524"}, - {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc"}, - {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc"}, - {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf"}, - {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c"}, - {file = "msgpack-1.0.7-cp38-cp38-win32.whl", hash = "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2"}, - {file = "msgpack-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c"}, - {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f"}, - {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81"}, - {file = "msgpack-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc"}, - {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d"}, - {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7"}, - {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61"}, - {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819"}, - {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd"}, - {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f"}, - {file = "msgpack-1.0.7-cp39-cp39-win32.whl", hash = "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad"}, - {file = "msgpack-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3"}, - {file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, + {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, + {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, + {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, + {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, + {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, + {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, + {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, + {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, + {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, + {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, + {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, ] [[package]] @@ -1429,47 +1446,47 @@ setuptools = "*" [[package]] name = "numpy" -version = "1.26.2" +version = "1.26.4" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" files = [ - {file = "numpy-1.26.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3703fc9258a4a122d17043e57b35e5ef1c5a5837c3db8be396c82e04c1cf9b0f"}, - {file = "numpy-1.26.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cc392fdcbd21d4be6ae1bb4475a03ce3b025cd49a9be5345d76d7585aea69440"}, - {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36340109af8da8805d8851ef1d74761b3b88e81a9bd80b290bbfed61bd2b4f75"}, - {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcc008217145b3d77abd3e4d5ef586e3bdfba8fe17940769f8aa09b99e856c00"}, - {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ced40d4e9e18242f70dd02d739e44698df3dcb010d31f495ff00a31ef6014fe"}, - {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b272d4cecc32c9e19911891446b72e986157e6a1809b7b56518b4f3755267523"}, - {file = "numpy-1.26.2-cp310-cp310-win32.whl", hash = "sha256:22f8fc02fdbc829e7a8c578dd8d2e15a9074b630d4da29cda483337e300e3ee9"}, - {file = "numpy-1.26.2-cp310-cp310-win_amd64.whl", hash = "sha256:26c9d33f8e8b846d5a65dd068c14e04018d05533b348d9eaeef6c1bd787f9919"}, - {file = "numpy-1.26.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b96e7b9c624ef3ae2ae0e04fa9b460f6b9f17ad8b4bec6d7756510f1f6c0c841"}, - {file = "numpy-1.26.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aa18428111fb9a591d7a9cc1b48150097ba6a7e8299fb56bdf574df650e7d1f1"}, - {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06fa1ed84aa60ea6ef9f91ba57b5ed963c3729534e6e54055fc151fad0423f0a"}, - {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96ca5482c3dbdd051bcd1fce8034603d6ebfc125a7bd59f55b40d8f5d246832b"}, - {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:854ab91a2906ef29dc3925a064fcd365c7b4da743f84b123002f6139bcb3f8a7"}, - {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f43740ab089277d403aa07567be138fc2a89d4d9892d113b76153e0e412409f8"}, - {file = "numpy-1.26.2-cp311-cp311-win32.whl", hash = "sha256:a2bbc29fcb1771cd7b7425f98b05307776a6baf43035d3b80c4b0f29e9545186"}, - {file = "numpy-1.26.2-cp311-cp311-win_amd64.whl", hash = "sha256:2b3fca8a5b00184828d12b073af4d0fc5fdd94b1632c2477526f6bd7842d700d"}, - {file = "numpy-1.26.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a4cd6ed4a339c21f1d1b0fdf13426cb3b284555c27ac2f156dfdaaa7e16bfab0"}, - {file = "numpy-1.26.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d5244aabd6ed7f312268b9247be47343a654ebea52a60f002dc70c769048e75"}, - {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a3cdb4d9c70e6b8c0814239ead47da00934666f668426fc6e94cce869e13fd7"}, - {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa317b2325f7aa0a9471663e6093c210cb2ae9c0ad824732b307d2c51983d5b6"}, - {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:174a8880739c16c925799c018f3f55b8130c1f7c8e75ab0a6fa9d41cab092fd6"}, - {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f79b231bf5c16b1f39c7f4875e1ded36abee1591e98742b05d8a0fb55d8a3eec"}, - {file = "numpy-1.26.2-cp312-cp312-win32.whl", hash = "sha256:4a06263321dfd3598cacb252f51e521a8cb4b6df471bb12a7ee5cbab20ea9167"}, - {file = "numpy-1.26.2-cp312-cp312-win_amd64.whl", hash = "sha256:b04f5dc6b3efdaab541f7857351aac359e6ae3c126e2edb376929bd3b7f92d7e"}, - {file = "numpy-1.26.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4eb8df4bf8d3d90d091e0146f6c28492b0be84da3e409ebef54349f71ed271ef"}, - {file = "numpy-1.26.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a13860fdcd95de7cf58bd6f8bc5a5ef81c0b0625eb2c9a783948847abbef2c2"}, - {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64308ebc366a8ed63fd0bf426b6a9468060962f1a4339ab1074c228fa6ade8e3"}, - {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baf8aab04a2c0e859da118f0b38617e5ee65d75b83795055fb66c0d5e9e9b818"}, - {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d73a3abcac238250091b11caef9ad12413dab01669511779bc9b29261dd50210"}, - {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b361d369fc7e5e1714cf827b731ca32bff8d411212fccd29ad98ad622449cc36"}, - {file = "numpy-1.26.2-cp39-cp39-win32.whl", hash = "sha256:bd3f0091e845164a20bd5a326860c840fe2af79fa12e0469a12768a3ec578d80"}, - {file = "numpy-1.26.2-cp39-cp39-win_amd64.whl", hash = "sha256:2beef57fb031dcc0dc8fa4fe297a742027b954949cabb52a2a376c144e5e6060"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1cc3d5029a30fb5f06704ad6b23b35e11309491c999838c31f124fee32107c79"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94cc3c222bb9fb5a12e334d0479b97bb2df446fbe622b470928f5284ffca3f8d"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe6b44fb8fcdf7eda4ef4461b97b3f63c466b27ab151bec2366db8b197387841"}, - {file = "numpy-1.26.2.tar.gz", hash = "sha256:f65738447676ab5777f11e6bbbdb8ce11b785e105f690bc45966574816b6d3ea"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] [[package]] @@ -1488,47 +1505,51 @@ dev = ["black", "mypy", "pytest"] [[package]] name = "packaging" -version = "23.2" +version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] name = "pandas" -version = "2.1.4" +version = "2.2.1" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" files = [ - {file = "pandas-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bdec823dc6ec53f7a6339a0e34c68b144a7a1fd28d80c260534c39c62c5bf8c9"}, - {file = "pandas-2.1.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:294d96cfaf28d688f30c918a765ea2ae2e0e71d3536754f4b6de0ea4a496d034"}, - {file = "pandas-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b728fb8deba8905b319f96447a27033969f3ea1fea09d07d296c9030ab2ed1d"}, - {file = "pandas-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00028e6737c594feac3c2df15636d73ace46b8314d236100b57ed7e4b9ebe8d9"}, - {file = "pandas-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:426dc0f1b187523c4db06f96fb5c8d1a845e259c99bda74f7de97bd8a3bb3139"}, - {file = "pandas-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:f237e6ca6421265643608813ce9793610ad09b40154a3344a088159590469e46"}, - {file = "pandas-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b7d852d16c270e4331f6f59b3e9aa23f935f5c4b0ed2d0bc77637a8890a5d092"}, - {file = "pandas-2.1.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd7d5f2f54f78164b3d7a40f33bf79a74cdee72c31affec86bfcabe7e0789821"}, - {file = "pandas-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0aa6e92e639da0d6e2017d9ccff563222f4eb31e4b2c3cf32a2a392fc3103c0d"}, - {file = "pandas-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d797591b6846b9db79e65dc2d0d48e61f7db8d10b2a9480b4e3faaddc421a171"}, - {file = "pandas-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2d3e7b00f703aea3945995ee63375c61b2e6aa5aa7871c5d622870e5e137623"}, - {file = "pandas-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:dc9bf7ade01143cddc0074aa6995edd05323974e6e40d9dbde081021ded8510e"}, - {file = "pandas-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:482d5076e1791777e1571f2e2d789e940dedd927325cc3cb6d0800c6304082f6"}, - {file = "pandas-2.1.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8a706cfe7955c4ca59af8c7a0517370eafbd98593155b48f10f9811da440248b"}, - {file = "pandas-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0513a132a15977b4a5b89aabd304647919bc2169eac4c8536afb29c07c23540"}, - {file = "pandas-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9f17f2b6fc076b2a0078862547595d66244db0f41bf79fc5f64a5c4d635bead"}, - {file = "pandas-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:45d63d2a9b1b37fa6c84a68ba2422dc9ed018bdaa668c7f47566a01188ceeec1"}, - {file = "pandas-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:f69b0c9bb174a2342818d3e2778584e18c740d56857fc5cdb944ec8bbe4082cf"}, - {file = "pandas-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3f06bda01a143020bad20f7a85dd5f4a1600112145f126bc9e3e42077c24ef34"}, - {file = "pandas-2.1.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab5796839eb1fd62a39eec2916d3e979ec3130509930fea17fe6f81e18108f6a"}, - {file = "pandas-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edbaf9e8d3a63a9276d707b4d25930a262341bca9874fcb22eff5e3da5394732"}, - {file = "pandas-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ebfd771110b50055712b3b711b51bee5d50135429364d0498e1213a7adc2be8"}, - {file = "pandas-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8ea107e0be2aba1da619cc6ba3f999b2bfc9669a83554b1904ce3dd9507f0860"}, - {file = "pandas-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:d65148b14788b3758daf57bf42725caa536575da2b64df9964c563b015230984"}, - {file = "pandas-2.1.4.tar.gz", hash = "sha256:fcb68203c833cc735321512e13861358079a96c174a61f5116a1de89c58c0ef7"}, + {file = "pandas-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8df8612be9cd1c7797c93e1c5df861b2ddda0b48b08f2c3eaa0702cf88fb5f88"}, + {file = "pandas-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0f573ab277252ed9aaf38240f3b54cfc90fff8e5cab70411ee1d03f5d51f3944"}, + {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f02a3a6c83df4026e55b63c1f06476c9aa3ed6af3d89b4f04ea656ccdaaaa359"}, + {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c38ce92cb22a4bea4e3929429aa1067a454dcc9c335799af93ba9be21b6beb51"}, + {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c2ce852e1cf2509a69e98358e8458775f89599566ac3775e70419b98615f4b06"}, + {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53680dc9b2519cbf609c62db3ed7c0b499077c7fefda564e330286e619ff0dd9"}, + {file = "pandas-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:94e714a1cca63e4f5939cdce5f29ba8d415d85166be3441165edd427dc9f6bc0"}, + {file = "pandas-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f821213d48f4ab353d20ebc24e4faf94ba40d76680642fb7ce2ea31a3ad94f9b"}, + {file = "pandas-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70e00c2d894cb230e5c15e4b1e1e6b2b478e09cf27cc593a11ef955b9ecc81a"}, + {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e97fbb5387c69209f134893abc788a6486dbf2f9e511070ca05eed4b930b1b02"}, + {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101d0eb9c5361aa0146f500773395a03839a5e6ecde4d4b6ced88b7e5a1a6403"}, + {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7d2ed41c319c9fb4fd454fe25372028dfa417aacb9790f68171b2e3f06eae8cd"}, + {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:af5d3c00557d657c8773ef9ee702c61dd13b9d7426794c9dfeb1dc4a0bf0ebc7"}, + {file = "pandas-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:06cf591dbaefb6da9de8472535b185cba556d0ce2e6ed28e21d919704fef1a9e"}, + {file = "pandas-2.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:88ecb5c01bb9ca927ebc4098136038519aa5d66b44671861ffab754cae75102c"}, + {file = "pandas-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:04f6ec3baec203c13e3f8b139fb0f9f86cd8c0b94603ae3ae8ce9a422e9f5bee"}, + {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a935a90a76c44fe170d01e90a3594beef9e9a6220021acfb26053d01426f7dc2"}, + {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c391f594aae2fd9f679d419e9a4d5ba4bce5bb13f6a989195656e7dc4b95c8f0"}, + {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9d1265545f579edf3f8f0cb6f89f234f5e44ba725a34d86535b1a1d38decbccc"}, + {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11940e9e3056576ac3244baef2fedade891977bcc1cb7e5cc8f8cc7d603edc89"}, + {file = "pandas-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:4acf681325ee1c7f950d058b05a820441075b0dd9a2adf5c4835b9bc056bf4fb"}, + {file = "pandas-2.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9bd8a40f47080825af4317d0340c656744f2bfdb6819f818e6ba3cd24c0e1397"}, + {file = "pandas-2.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df0c37ebd19e11d089ceba66eba59a168242fc6b7155cba4ffffa6eccdfb8f16"}, + {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:739cc70eaf17d57608639e74d63387b0d8594ce02f69e7a0b046f117974b3019"}, + {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d3558d263073ed95e46f4650becff0c5e1ffe0fc3a015de3c79283dfbdb3df"}, + {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4aa1d8707812a658debf03824016bf5ea0d516afdea29b7dc14cf687bc4d4ec6"}, + {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:76f27a809cda87e07f192f001d11adc2b930e93a2b0c4a236fde5429527423be"}, + {file = "pandas-2.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:1ba21b1d5c0e43416218db63037dbe1a01fc101dc6e6024bcad08123e48004ab"}, + {file = "pandas-2.2.1.tar.gz", hash = "sha256:0ab90f87093c13f3e8fa45b48ba9f39181046e8f3317d3aadb2fffbb1b978572"}, ] [package.dependencies] @@ -1539,31 +1560,32 @@ numpy = [ ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" -tzdata = ">=2022.1" +tzdata = ">=2022.7" [package.extras] -all = ["PyQt5 (>=5.15.6)", "SQLAlchemy (>=1.4.36)", "beautifulsoup4 (>=4.11.1)", "bottleneck (>=1.3.4)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=0.8.1)", "fsspec (>=2022.05.0)", "gcsfs (>=2022.05.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.8.0)", "matplotlib (>=3.6.1)", "numba (>=0.55.2)", "numexpr (>=2.8.0)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pandas-gbq (>=0.17.5)", "psycopg2 (>=2.9.3)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.5)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "pyxlsb (>=1.0.9)", "qtpy (>=2.2.0)", "s3fs (>=2022.05.0)", "scipy (>=1.8.1)", "tables (>=3.7.0)", "tabulate (>=0.8.10)", "xarray (>=2022.03.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)", "zstandard (>=0.17.0)"] -aws = ["s3fs (>=2022.05.0)"] -clipboard = ["PyQt5 (>=5.15.6)", "qtpy (>=2.2.0)"] -compression = ["zstandard (>=0.17.0)"] -computation = ["scipy (>=1.8.1)", "xarray (>=2022.03.0)"] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] consortium-standard = ["dataframe-api-compat (>=0.1.7)"] -excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pyxlsb (>=1.0.9)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)"] -feather = ["pyarrow (>=7.0.0)"] -fss = ["fsspec (>=2022.05.0)"] -gcp = ["gcsfs (>=2022.05.0)", "pandas-gbq (>=0.17.5)"] -hdf5 = ["tables (>=3.7.0)"] -html = ["beautifulsoup4 (>=4.11.1)", "html5lib (>=1.1)", "lxml (>=4.8.0)"] -mysql = ["SQLAlchemy (>=1.4.36)", "pymysql (>=1.0.2)"] -output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.8.10)"] -parquet = ["pyarrow (>=7.0.0)"] -performance = ["bottleneck (>=1.3.4)", "numba (>=0.55.2)", "numexpr (>=2.8.0)"] -plot = ["matplotlib (>=3.6.1)"] -postgresql = ["SQLAlchemy (>=1.4.36)", "psycopg2 (>=2.9.3)"] -spss = ["pyreadstat (>=1.1.5)"] -sql-other = ["SQLAlchemy (>=1.4.36)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] -xml = ["lxml (>=4.8.0)"] +xml = ["lxml (>=4.9.2)"] [[package]] name = "parso" @@ -1618,28 +1640,28 @@ flake8 = ">=5.0.0" [[package]] name = "platformdirs" -version = "4.1.0" +version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, - {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -1680,27 +1702,27 @@ wcwidth = "*" [[package]] name = "psutil" -version = "5.9.7" +version = "5.9.8" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "psutil-5.9.7-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0bd41bf2d1463dfa535942b2a8f0e958acf6607ac0be52265ab31f7923bcd5e6"}, - {file = "psutil-5.9.7-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:5794944462509e49d4d458f4dbfb92c47539e7d8d15c796f141f474010084056"}, - {file = "psutil-5.9.7-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:fe361f743cb3389b8efda21980d93eb55c1f1e3898269bc9a2a1d0bb7b1f6508"}, - {file = "psutil-5.9.7-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:e469990e28f1ad738f65a42dcfc17adaed9d0f325d55047593cb9033a0ab63df"}, - {file = "psutil-5.9.7-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:3c4747a3e2ead1589e647e64aad601981f01b68f9398ddf94d01e3dc0d1e57c7"}, - {file = "psutil-5.9.7-cp27-none-win32.whl", hash = "sha256:1d4bc4a0148fdd7fd8f38e0498639ae128e64538faa507df25a20f8f7fb2341c"}, - {file = "psutil-5.9.7-cp27-none-win_amd64.whl", hash = "sha256:4c03362e280d06bbbfcd52f29acd79c733e0af33d707c54255d21029b8b32ba6"}, - {file = "psutil-5.9.7-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e"}, - {file = "psutil-5.9.7-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284"}, - {file = "psutil-5.9.7-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe"}, - {file = "psutil-5.9.7-cp36-cp36m-win32.whl", hash = "sha256:b27f8fdb190c8c03914f908a4555159327d7481dac2f01008d483137ef3311a9"}, - {file = "psutil-5.9.7-cp36-cp36m-win_amd64.whl", hash = "sha256:44969859757f4d8f2a9bd5b76eba8c3099a2c8cf3992ff62144061e39ba8568e"}, - {file = "psutil-5.9.7-cp37-abi3-win32.whl", hash = "sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68"}, - {file = "psutil-5.9.7-cp37-abi3-win_amd64.whl", hash = "sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414"}, - {file = "psutil-5.9.7-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340"}, - {file = "psutil-5.9.7.tar.gz", hash = "sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c"}, + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, ] [package.extras] @@ -1889,13 +1911,13 @@ termcolor = ">=1.1.0" [[package]] name = "python-dateutil" -version = "2.8.2" +version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] [package.dependencies] @@ -1903,13 +1925,13 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2023.3.post1" +version = "2024.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, - {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] [[package]] @@ -1997,13 +2019,13 @@ docutils = ">=0.11,<1.0" [[package]] name = "rich" -version = "13.7.0" +version = "13.7.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, - {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, ] [package.dependencies] @@ -2015,13 +2037,13 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruamel-yaml" -version = "0.18.5" +version = "0.18.6" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" optional = false python-versions = ">=3.7" files = [ - {file = "ruamel.yaml-0.18.5-py3-none-any.whl", hash = "sha256:a013ac02f99a69cdd6277d9664689eb1acba07069f912823177c5eced21a6ada"}, - {file = "ruamel.yaml-0.18.5.tar.gz", hash = "sha256:61917e3a35a569c1133a8f772e1226961bf5a1198bea7e23f06a0841dea1ab0e"}, + {file = "ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636"}, + {file = "ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b"}, ] [package.dependencies] @@ -2040,24 +2062,24 @@ python-versions = ">=3.6" files = [ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d92f81886165cb14d7b067ef37e142256f1c6a90a65cd156b063a43da1708cfd"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b5edda50e5e9e15e54a6a8a0070302b00c518a9d32accc2346ad6c984aacd279"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:7048c338b6c86627afb27faecf418768acb6331fc24cfa56c93e8c9780f815fa"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, @@ -2065,7 +2087,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fcc54cb0c8b811ff66082de1680b4b14cf8a81dce0d4fbf665c2265a81e07a1"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, @@ -2073,7 +2095,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:665f58bfd29b167039f714c6998178d27ccd83984084c286110ef26b230f259f"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, @@ -2081,7 +2103,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9eb5dee2772b0f704ca2e45b1713e4e5198c18f515b52743576d196348f374d3"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, @@ -2092,19 +2114,19 @@ files = [ [[package]] name = "setuptools" -version = "69.0.3" +version = "69.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, - {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -2130,13 +2152,13 @@ files = [ [[package]] name = "sniffio" -version = "1.3.0" +version = "1.3.1" description = "Sniff out which async library your code is running under" optional = true python-versions = ">=3.7" files = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] [[package]] @@ -2265,24 +2287,23 @@ dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] [[package]] name = "sphinx-tabs" -version = "3.4.0" +version = "3.4.5" description = "Tabbed views for Sphinx" optional = false python-versions = "~=3.7" files = [ - {file = "sphinx-tabs-3.4.0.tar.gz", hash = "sha256:75e97ce10b74700deaf87b662539a293c8afc9dfa9d21f126b860118064cb0c5"}, - {file = "sphinx_tabs-3.4.0-py3-none-any.whl", hash = "sha256:31dbe7594b5ef4cfa76a7960448d4607dca167ff21467000213920572c302072"}, + {file = "sphinx-tabs-3.4.5.tar.gz", hash = "sha256:ba9d0c1e3e37aaadd4b5678449eb08176770e0fc227e769b6ce747df3ceea531"}, + {file = "sphinx_tabs-3.4.5-py3-none-any.whl", hash = "sha256:92cc9473e2ecf1828ca3f6617d0efc0aa8acb06b08c56ba29d1413f2f0f6cf09"}, ] [package.dependencies] -docutils = ">=0.17.0,<0.18.0" -jinja2 = "<3.1.0" +docutils = "*" pygments = "*" -sphinx = ">=2,<6" +sphinx = "*" [package.extras] code-style = ["pre-commit (==2.13.0)"] -testing = ["bs4", "coverage", "pygments", "pytest (>=7.1,<8)", "pytest-cov", "pytest-regressions", "rinohtype", "sphinx-testing"] +testing = ["bs4", "coverage", "pygments", "pytest (>=7.1,<8)", "pytest-cov", "pytest-regressions", "rinohtype"] [[package]] name = "sphinx-toolbox" @@ -2320,56 +2341,50 @@ testing = ["coincidence (>=0.4.3)", "pygments (>=2.7.4,<=2.13.0)"] [[package]] name = "sphinxcontrib-applehelp" -version = "1.0.7" +version = "1.0.8" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_applehelp-1.0.7-py3-none-any.whl", hash = "sha256:094c4d56209d1734e7d252f6e0b3ccc090bd52ee56807a5d9315b19c122ab15d"}, - {file = "sphinxcontrib_applehelp-1.0.7.tar.gz", hash = "sha256:39fdc8d762d33b01a7d8f026a3b7d71563ea3b72787d5f00ad8465bd9d6dfbfa"}, + {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, + {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-devhelp" -version = "1.0.5" +version = "1.0.6" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_devhelp-1.0.5-py3-none-any.whl", hash = "sha256:fe8009aed765188f08fcaadbb3ea0d90ce8ae2d76710b7e29ea7d047177dae2f"}, - {file = "sphinxcontrib_devhelp-1.0.5.tar.gz", hash = "sha256:63b41e0d38207ca40ebbeabcf4d8e51f76c03e78cd61abe118cf4435c73d4212"}, + {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, + {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" -version = "2.0.4" +version = "2.0.5" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_htmlhelp-2.0.4-py3-none-any.whl", hash = "sha256:8001661c077a73c29beaf4a79968d0726103c5605e27db92b9ebed8bab1359e9"}, - {file = "sphinxcontrib_htmlhelp-2.0.4.tar.gz", hash = "sha256:6c26a118a05b76000738429b724a0568dbde5b72391a688577da08f11891092a"}, + {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, + {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["html5lib", "pytest"] [[package]] @@ -2402,38 +2417,34 @@ test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" -version = "1.0.6" +version = "1.0.7" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_qthelp-1.0.6-py3-none-any.whl", hash = "sha256:bf76886ee7470b934e363da7a954ea2825650013d367728588732c7350f49ea4"}, - {file = "sphinxcontrib_qthelp-1.0.6.tar.gz", hash = "sha256:62b9d1a186ab7f5ee3356d906f648cacb7a6bdb94d201ee7adf26db55092982d"}, + {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, + {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-serializinghtml" -version = "1.1.9" +version = "1.1.10" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_serializinghtml-1.1.9-py3-none-any.whl", hash = "sha256:9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1"}, - {file = "sphinxcontrib_serializinghtml-1.1.9.tar.gz", hash = "sha256:0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54"}, + {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, + {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] @@ -2451,13 +2462,13 @@ pathspec = ">=0.9.0" [[package]] name = "stevedore" -version = "5.1.0" +version = "5.2.0" description = "Manage dynamic plugins for Python applications" optional = false python-versions = ">=3.8" files = [ - {file = "stevedore-5.1.0-py3-none-any.whl", hash = "sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d"}, - {file = "stevedore-5.1.0.tar.gz", hash = "sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"}, + {file = "stevedore-5.2.0-py3-none-any.whl", hash = "sha256:1c15d95766ca0569cad14cb6272d4d31dae66b011a929d7c18219c176ea1b5c9"}, + {file = "stevedore-5.2.0.tar.gz", hash = "sha256:46b93ca40e1114cea93d738a6c1e365396981bb6bb78c27045b7587c9473544d"}, ] [package.dependencies] @@ -2515,13 +2526,13 @@ files = [ [[package]] name = "tqdm" -version = "4.66.1" +version = "4.66.2" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"}, - {file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"}, + {file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"}, + {file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"}, ] [package.dependencies] @@ -2555,13 +2566,13 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. [[package]] name = "typing-extensions" -version = "4.9.0" +version = "4.10.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, ] [[package]] @@ -2581,40 +2592,41 @@ typing-extensions = ">=3.7.4" [[package]] name = "tzdata" -version = "2023.3" +version = "2024.1" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, - {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] [[package]] name = "urllib3" -version = "2.1.0" +version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, - {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.25.0" +version = "20.25.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, - {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, + {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, + {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, ] [package.dependencies] @@ -2628,13 +2640,13 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [[package]] name = "wcwidth" -version = "0.2.12" +version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = true python-versions = "*" files = [ - {file = "wcwidth-0.2.12-py2.py3-none-any.whl", hash = "sha256:f26ec43d96c8cbfed76a5075dac87680124fa84e0855195a6184da9c187f133c"}, - {file = "wcwidth-0.2.12.tar.gz", hash = "sha256:f01c104efdf57971bcb756f054dd58ddec5204dd15fa31d6503ea57947d97c02"}, + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] [[package]] @@ -2673,18 +2685,18 @@ viz = ["matplotlib", "nc-time-axis", "seaborn"] [[package]] name = "zipp" -version = "3.17.0" +version = "3.18.1" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, + {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, + {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [extras] all = ["deker-server-adapters", "deker-shell", "xarray"] diff --git a/pyproject.toml b/pyproject.toml index af0aa8f..de44a77 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -173,7 +173,7 @@ disallow_untyped_defs = false # https://pypi.org/project/flake8-pytest-style/ # https://pypi.org/project/pep8-naming/ min_python_version = 3.9 -max-complexity = 15 +max-complexity = 16 max-doc-length = 120 max-line-length = 120 strictness = "full" diff --git a/tests/parameters/array_params.py b/tests/parameters/array_params.py index 12fd53f..b119aa7 100644 --- a/tests/parameters/array_params.py +++ b/tests/parameters/array_params.py @@ -26,6 +26,5 @@ def attributes_validation_params() -> List[dict]: "time_attr_name": datetime.now(), }, }, - {"custom_attributes": {"time_attr_name": None}, **default_attributes}, {"custom_attributes": {"time_attr_name": "word"}, **default_attributes}, ] diff --git a/tests/test_cases/test_arrays/test_array_methods.py b/tests/test_cases/test_arrays/test_array_methods.py index c5b400c..4c77993 100644 --- a/tests/test_cases/test_arrays/test_array_methods.py +++ b/tests/test_cases/test_arrays/test_array_methods.py @@ -20,10 +20,10 @@ from deker.client import Client from deker.collection import Collection from deker.dimensions import TimeDimension -from deker.errors import DekerMemoryError, DekerValidationError -from deker.schemas import ArraySchema, DimensionSchema +from deker.errors import DekerCollectionAlreadyExistsError, DekerMemoryError, DekerValidationError +from deker.schemas import ArraySchema, AttributeSchema, DimensionSchema from deker.tools import get_paths -from deker.types.private.typings import FancySlice, Slice +from deker.types.private.typings import FancySlice, NumericDtypes, Slice @pytest.mark.asyncio() @@ -192,7 +192,7 @@ def test_update_array(self, inserted_array: Array, array_data: np.ndarray, index assert (data == update).all() def test_update_custom_attributes(self, inserted_array_with_attributes: Array): - """Tests array update method. + """Tests array update_custom_attributes method. :param inserted_array_with_attributes: Array object """ @@ -215,6 +215,74 @@ def test_update_custom_attributes(self, inserted_array_with_attributes: Array): if isinstance(d, TimeDimension): assert d.start_value == new_custom_attributes["time_attr_name"] + @pytest.mark.parametrize("dtype", [datetime, str, tuple] + NumericDtypes) + def test_custom_attributes_set_to_none(self, client: Client, dtype: object): + """Tests array is created with custom attribute set to None. + + :param client: Deker Client instance + """ + schema = ArraySchema( + dimensions=[DimensionSchema(name="x", size=1)], + dtype=int, + attributes=[AttributeSchema(name="some_attr", dtype=dtype, primary=False)], + ) + col_name = "test_custom_attrs_values_are_None" + try: + col = client.create_collection(col_name, schema) + except DekerCollectionAlreadyExistsError: + col = client.get_collection(col_name) + col.clear() + try: + key = "custom_attributes" + attrs = {key: {schema.attributes[0].name: None}} + array = col.create(**attrs) + assert array + attr = getattr(array, key) + assert attr[schema.attributes[0].name] is None + except Exception: + raise + finally: + col.delete() + + @pytest.mark.parametrize("dtype", NumericDtypes + [str, tuple, datetime]) + def test_custom_attributes_updated_to_none(self, client: Client, dtype: object): + """Tests array custom attribute is updated to None. + + :param client: Deker Client instance + """ + schema = ArraySchema( + dimensions=[DimensionSchema(name="x", size=1)], + dtype=int, + attributes=[AttributeSchema(name="some_attr", dtype=dtype, primary=False)], + ) + col_name = "test_custom_attrs_values_are_None" + try: + col = client.create_collection(col_name, schema) + except DekerCollectionAlreadyExistsError: + col = client.get_collection(col_name) + col.clear() + + try: + key = "custom_attributes" + if dtype is tuple: + val = (1,) + elif dtype is datetime: + val = datetime(2024, 1, 1, 0, 0, tzinfo=timezone.utc) + else: + val = dtype(1) # type: ignore + attrs = {key: {schema.attributes[0].name: val}} + array = col.create(**attrs) + assert array + attr = getattr(array, key) + assert attr[schema.attributes[0].name] == val + array.update_custom_attributes({schema.attributes[0].name: None}) + attr = getattr(array, key) + assert attr[schema.attributes[0].name] is None + except Exception: + raise + finally: + col.delete() + @pytest.mark.parametrize( "new_custom_attributes", [ @@ -237,7 +305,6 @@ def test_update_custom_attributes(self, inserted_array_with_attributes: Array): "time_attr_name": datetime.now(timezone.utc), }, {"custom_attribute": 0.6, "time_attr_name": datetime.now(timezone.utc).isoformat()}, - {"custom_attribute": 0.6, "time_attr_name": None}, {"custom_attribute": 0.6, "time_attr_name": timezone.utc}, {"custom_attribute": 0.6, "time_attr_name": ""}, {"custom_attribute": 0.6, "time_attr_name": " "}, diff --git a/tests/test_cases/test_arrays/test_varray_methods.py b/tests/test_cases/test_arrays/test_varray_methods.py index 66950f4..69871c4 100644 --- a/tests/test_cases/test_arrays/test_varray_methods.py +++ b/tests/test_cases/test_arrays/test_varray_methods.py @@ -292,7 +292,6 @@ def test_update_custom_attributes(self, inserted_array_with_attributes: VArray): "time_attr_name": datetime.now(timezone.utc), }, {"custom_attribute": 0.6, "time_attr_name": datetime.now(timezone.utc).isoformat()}, - {"custom_attribute": 0.6, "time_attr_name": None}, {"custom_attribute": 0.6, "time_attr_name": timezone.utc}, {"custom_attribute": 0.6, "time_attr_name": ""}, {"custom_attribute": 0.6, "time_attr_name": " "}, diff --git a/tests/test_cases/test_client/test_client_methods.py b/tests/test_cases/test_client/test_client_methods.py index 2eb2921..f5b5182 100644 --- a/tests/test_cases/test_client/test_client_methods.py +++ b/tests/test_cases/test_client/test_client_methods.py @@ -382,7 +382,10 @@ def test_client_check_integrity_collection( try: client.check_integrity(2, stop_on_error=False, collection=collection_1.name) except Exception as e: - assert str(e) == f"Collection \"{collection_1.name}\" metadata is invalid/corrupted: 'test'" + assert ( + str(e) + == f"Collection \"{collection_1.name}\" metadata is invalid/corrupted: 'test'" + ) errors = capsys.readouterr().out collection_1.delete() collection_2.delete() diff --git a/tests/test_cases/test_collections/test_collection_methods.py b/tests/test_cases/test_collections/test_collection_methods.py index 8dda18a..b899055 100644 --- a/tests/test_cases/test_collections/test_collection_methods.py +++ b/tests/test_cases/test_collections/test_collection_methods.py @@ -4,7 +4,6 @@ from io import BytesIO import hdf5plugin -import numpy as np import pytest from deker_local_adapters import HDF5StorageAdapter, LocalCollectionAdapter diff --git a/tests/test_cases/test_concurrency/test_in_processes.py b/tests/test_cases/test_concurrency/test_in_processes.py index ccb0e5b..43e8e67 100644 --- a/tests/test_cases/test_concurrency/test_in_processes.py +++ b/tests/test_cases/test_concurrency/test_in_processes.py @@ -6,16 +6,13 @@ correctly. """ import os -import time import traceback from multiprocessing import Event, Manager, Process, cpu_count from multiprocessing.pool import Pool from pathlib import Path -from threading import get_native_id from typing import Callable, Dict, Literal from unittest.mock import patch -from uuid import uuid4 import h5py import numpy as np @@ -28,15 +25,15 @@ from deker.arrays import Array, VArray from deker.client import Client from deker.collection import Collection -from deker.errors import DekerLockError, DekerVSubsetError +from deker.errors import DekerLockError from deker.locks import ( CollectionLock, + CreateArrayLock, Flock, ReadArrayLock, UpdateMetaAttributeLock, WriteArrayLock, WriteVarrayLock, - CreateArrayLock, ) from deker.schemas import ArraySchema, DimensionSchema, VArraySchema from deker.tools import get_paths diff --git a/tests/test_cases/test_schemas/test_array_schema.py b/tests/test_cases/test_schemas/test_array_schema.py index 5776aa9..d7021fa 100644 --- a/tests/test_cases/test_schemas/test_array_schema.py +++ b/tests/test_cases/test_schemas/test_array_schema.py @@ -6,7 +6,7 @@ from tests.parameters.schemas_params import ArraySchemaCreationParams -from deker.errors import DekerValidationError +from deker.errors import DekerCollectionAlreadyExistsError, DekerValidationError from deker.schemas import ArraySchema, AttributeSchema, DimensionSchema, TimeDimensionSchema @@ -151,5 +151,61 @@ def test_array_schema_shape_is_python_int(array_schema: ArraySchema): assert type(i) == int +@pytest.mark.parametrize("primary", [False, True]) +def test_schema_with_TD_and_start_value_link_fail_on_attr_is_None(client, primary): + schema = ArraySchema( + dimensions=[ + TimeDimensionSchema(name="x", size=1, start_value="$some_attr", step=timedelta(1)) + ], + dtype=int, + attributes=[AttributeSchema(name="some_attr", dtype=datetime, primary=primary)], + ) + col_name = "test_TD_start_value_linked_attr_value_can_not_be_None" + try: + col = client.create_collection(col_name, schema) + except DekerCollectionAlreadyExistsError: + col = client.get_collection(col_name) + col.clear() + try: + if primary: + key = "primary_attributes" + else: + key = "custom_attributes" + attrs = {key: {schema.attributes[0].name: None}} + with pytest.raises(DekerValidationError): + assert col.create(**attrs) + finally: + col.delete() + + +def test_schema_with_TD_and_start_value_link_fail_on_custom_attr_is_set_to_None(client): + schema = ArraySchema( + dimensions=[ + TimeDimensionSchema(name="x", size=1, start_value="$some_attr", step=timedelta(1)) + ], + dtype=int, + attributes=[AttributeSchema(name="some_attr", dtype=datetime, primary=False)], + ) + col_name = "test_TD_start_value_linked_attr_value_can_not_be_set_to_None" + try: + col = client.create_collection(col_name, schema) + except DekerCollectionAlreadyExistsError: + col = client.get_collection(col_name) + col.clear() + try: + key = "custom_attributes" + attrs = {key: {schema.attributes[0].name: datetime(2024, 1, 1, tzinfo=timezone.utc)}} + array = col.create(**attrs) + assert array + assert ( + array.custom_attributes[schema.attributes[0].name] + == attrs[key][schema.attributes[0].name] + ) + with pytest.raises(DekerValidationError): + assert array.update_custom_attributes({schema.attributes[0].name: None}) + finally: + col.delete() + + if __name__ == "__main__": pytest.main() diff --git a/tests/test_cases/test_schemas/test_attributes_schema.py b/tests/test_cases/test_schemas/test_attributes_schema.py index a95c13e..5b0cebd 100644 --- a/tests/test_cases/test_schemas/test_attributes_schema.py +++ b/tests/test_cases/test_schemas/test_attributes_schema.py @@ -4,6 +4,7 @@ from datetime import datetime from typing import FrozenSet +import numpy as np import pytest from deker_local_adapters import LocalArrayAdapter @@ -12,7 +13,7 @@ from deker.arrays import Array, VArray from deker.collection import Collection from deker.dimensions import Dimension, TimeDimension -from deker.errors import DekerValidationError +from deker.errors import DekerCollectionAlreadyExistsError, DekerValidationError from deker.schemas import ArraySchema, AttributeSchema from deker.types.private.typings import NumericDtypes @@ -167,11 +168,151 @@ def test_attributes_schema_primary_raises_type(self, primary): assert AttributeSchema(name="some_attr", dtype=float, primary=primary) @pytest.mark.parametrize("primary", [True, False]) - def test_attributes_schema_primary_ok(self, primary): + def test_attributes_schema_primary_param_ok(self, primary): a = AttributeSchema(name="some_attr", dtype=int, primary=primary) assert a assert a.primary == primary + @pytest.mark.parametrize("primary", [False, True]) + @pytest.mark.parametrize( + ("dtype", "value"), + [ + (str, "123"), + (np.int8, np.int8(1)), + (np.int16, np.int16(-130)), + (np.int32, np.int32(-9999)), + (np.int64, np.int64(99999999)), + (int, 1), + (int, 0), + (int, -1), + (float, 0.1), + (float, -0.1), + (np.float16, np.float16(1.0)), + (np.float32, np.float32(-130)), + (np.float64, np.float64(-9999)), + (np.float128, np.float128(99999999)), + (complex, complex(0.0000000000001)), + (complex, complex(-0.0000000000001)), + (np.complex64, np.complex64(1.0)), + (np.complex128, np.complex128(-130)), + (np.complex256, np.complex256(-9999)), + (tuple, tuple("abc")), + (tuple, tuple({"abc", "def"})), + (tuple, (1, 2, 3, 4)), + (tuple, (1, 0.1, complex(0.0000000000001), complex(-0.0000000000001), -0.1, -1)), + ( + tuple, + ( + (1, 0.1, complex(0.0000000000001), complex(-0.0000000000001), -0.1, -1), + (1, 0.1, complex(0.0000000000001), complex(-0.0000000000001), -0.1, -1), + ), + ), + ( + tuple, + ( + ( + 1, + 0.1, + complex(0.0000000000001), + complex(-0.0000000000001), + -0.1, + -1, + ), + (1, 0.1, complex(0.0000000000001), complex(-0.0000000000001), -0.1, -1), + ), + ), + ( + tuple, + ( + np.int8(1), + np.int16(-130), + np.int32(-9999), + np.int64(99999999), + np.float16(1.0), + np.float32(-130), + np.float64(-9999), + np.float128(99999999), + np.complex64(1.0), + np.complex128(-130), + np.complex256(-9999), + ), + ), + ], + ) + def test_attributes_values_serialize_deserialize_ok(self, client, primary, dtype, value): + schema = ArraySchema( + dimensions=[DimensionSchema(name="x", size=1)], + dtype=int, + attributes=[AttributeSchema(name="some_attr", dtype=dtype, primary=primary)], + ) + col_name = "test_attrs_values_validation" + try: + col = client.create_collection(col_name, schema) + except DekerCollectionAlreadyExistsError: + col = client.get_collection(col_name) + col.clear() + try: + if primary: + key = "primary_attributes" + else: + key = "custom_attributes" + attrs = {key: {schema.attributes[0].name: value}} + array = col.create(**attrs) + assert array + attr = getattr(array, key) + assert attr[schema.attributes[0].name] == value + except Exception: + raise + finally: + col.delete() + + @pytest.mark.parametrize("primary", [False, True]) + @pytest.mark.parametrize( + ("dtype", "value"), + [ + (tuple, set("abc")), + (tuple, list({"abc", "def"})), + (tuple, ({1: 2}, {3: 4})), + (tuple, ({1, 2}, {3, 4})), + (tuple, ([1, 2], [3, 4])), + (tuple, ([1, 2], [3, 4])), + ], + ) + def test_attributes_schema_raise_on_tuples_values(self, client, primary, dtype, value): + schema = ArraySchema( + dimensions=[DimensionSchema(name="x", size=1)], + dtype=int, + attributes=[AttributeSchema(name="some_attr", dtype=dtype, primary=primary)], + ) + col_name = "test_attrs_values_validation" + try: + col = client.create_collection(col_name, schema) + except DekerCollectionAlreadyExistsError: + col = client.get_collection(col_name) + col.clear() + + try: + if primary: + key = "primary_attributes" + else: + key = "custom_attributes" + + attrs = {key: {"some_attr": value}} + with pytest.raises(DekerValidationError): + assert col.create(**attrs) + + finally: + col.delete() + + @pytest.mark.parametrize("primary", [False, True]) + @pytest.mark.parametrize( + "dtype", + [set, dict, None, list], + ) + def test_attributes_schema_raises_on_dtype(self, primary, dtype): + with pytest.raises(DekerValidationError): + AttributeSchema(name="some_attr", dtype=dtype, primary=primary) + @pytest.mark.parametrize("primary", (True, False)) def test_schema_conversion( diff --git a/tests/test_cases/test_validators/test_time_dimension_attributes_validator.py b/tests/test_cases/test_validators/test_time_dimension_attributes_validator.py index f59bdf4..94be9e8 100644 --- a/tests/test_cases/test_validators/test_time_dimension_attributes_validator.py +++ b/tests/test_cases/test_validators/test_time_dimension_attributes_validator.py @@ -1,13 +1,10 @@ -from datetime import datetime, timedelta +from datetime import timedelta import pytest from tests.parameters.common import random_string -from deker.client import Client -from deker.collection import Collection -from deker.errors import DekerCollectionAlreadyExistsError, DekerValidationError -from deker.schemas import ArraySchema, AttributeSchema, DimensionSchema, TimeDimensionSchema +from deker.schemas import DimensionSchema, TimeDimensionSchema @pytest.mark.asyncio() @@ -42,38 +39,6 @@ def coll_time_params(self) -> dict: ), } - @pytest.mark.parametrize( - "custom_attributes", - [ - {}, - {"start1": None}, - {"start2": None}, - {"start1": None, "start2": None}, - ], - ) - def test_time_custom_attributes_None(self, client: Client, custom_attributes: dict): - """Tests errors on None custom attributes for time dimensions.""" - - coll_params = self.coll_time_params() - coll_params["schema"]["attributes"].extend( - [ - AttributeSchema(name="start1", dtype=datetime, primary=False), - AttributeSchema(name="start2", dtype=datetime, primary=False), - ] - ) - coll_params["schema"] = ArraySchema(**coll_params["schema"]) # type: ignore - try: - collection: Collection = client.create_collection(**coll_params) - except DekerCollectionAlreadyExistsError: - coll = client.get_collection(coll_params["name"]) - coll.delete() - collection: Collection = client.create_collection(**coll_params) - try: - with pytest.raises(DekerValidationError): - assert collection.create(custom_attributes=custom_attributes) - finally: - collection.delete() - if __name__ == "__main__": pytest.main() From 667ff9109c059a163a66019a4d64d104e7f42958 Mon Sep 17 00:00:00 2001 From: Sergey Rybakov Date: Tue, 2 Apr 2024 22:10:24 +0300 Subject: [PATCH 02/19] test cases added --- tests/test_cases/test_schemas/test_attributes_schema.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_cases/test_schemas/test_attributes_schema.py b/tests/test_cases/test_schemas/test_attributes_schema.py index 5b0cebd..146f0e8 100644 --- a/tests/test_cases/test_schemas/test_attributes_schema.py +++ b/tests/test_cases/test_schemas/test_attributes_schema.py @@ -178,6 +178,8 @@ def test_attributes_schema_primary_param_ok(self, primary): ("dtype", "value"), [ (str, "123"), + (str, str(complex(-125.000000000001 - 0.123456789j))), + (str, "-125.000000000001-0.123456789j"), (np.int8, np.int8(1)), (np.int16, np.int16(-130)), (np.int32, np.int32(-9999)), From b47fb096c06cd4d702d29056954f94abac16595d Mon Sep 17 00:00:00 2001 From: Sergey Rybakov Date: Wed, 3 Apr 2024 10:57:45 +0300 Subject: [PATCH 03/19] fix test --- tests/test_cases/test_schemas/test_attributes_schema.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_cases/test_schemas/test_attributes_schema.py b/tests/test_cases/test_schemas/test_attributes_schema.py index 146f0e8..334e566 100644 --- a/tests/test_cases/test_schemas/test_attributes_schema.py +++ b/tests/test_cases/test_schemas/test_attributes_schema.py @@ -261,6 +261,7 @@ def test_attributes_values_serialize_deserialize_ok(self, client, primary, dtype attrs = {key: {schema.attributes[0].name: value}} array = col.create(**attrs) assert array + array = [a for a in col][0] attr = getattr(array, key) assert attr[schema.attributes[0].name] == value except Exception: From fec10a8e6f19f908a188178889738994aced2cbb Mon Sep 17 00:00:00 2001 From: Sergey Rybakov Date: Wed, 3 Apr 2024 18:29:26 +0300 Subject: [PATCH 04/19] conversion fixed --- deker/ABC/base_array.py | 60 +-------- deker/tools/attributes.py | 114 ++++++++++++++++++ deker/validators.py | 9 +- .../test_schemas/test_attributes_schema.py | 96 --------------- .../test_attributes_validator.py | 106 ++++++++++++++++ 5 files changed, 231 insertions(+), 154 deletions(-) create mode 100644 deker/tools/attributes.py diff --git a/deker/ABC/base_array.py b/deker/ABC/base_array.py index 301f4a9..c9907eb 100644 --- a/deker/ABC/base_array.py +++ b/deker/ABC/base_array.py @@ -35,6 +35,7 @@ from deker.schemas import ArraySchema, VArraySchema from deker.subset import Subset, VSubset from deker.tools.array import check_memory, get_id +from deker.tools.attributes import deserialize_attribute_value, serialize_attribute_value from deker.tools.schema import create_dimensions from deker.types.private.classes import ArrayMeta, Serializer from deker.types.private.typings import FancySlice, Numeric, Slice @@ -393,52 +394,12 @@ def update_custom_attributes(self, attributes: dict) -> None: def _create_meta(self) -> str: """Serialize array into metadata JSON string.""" - - def serialize_value( - val: Any, - ) -> Union[Tuple[str, int, float, tuple], str, int, float, tuple]: - """Jsonify attribute value. - - :param val: complex number - """ - if isinstance(val, datetime): - return val.isoformat() - if isinstance(val, np.ndarray): - return val.tolist() # type: ignore[attr-defined] - if isinstance(val, complex): - return str(val) - if isinstance(val, np.integer): - return int(val) - if isinstance(val, np.floating): - return float(val) - if isinstance(val, np.complexfloating): - return str(complex(val)) - if isinstance(val, (list, tuple)): - return serialize_nested_tuples(val) # type: ignore[arg-type] - - return val - - def serialize_nested_tuples( - attr: tuple, - ) -> Tuple[Union[Tuple[str, int, float, tuple], str, int, float, tuple], ...]: - """Jsonify nested tuples. - - :param attr: tuple instance - """ - elements = [] - for val in attr: - if isinstance(val, tuple): - elements.append(serialize_nested_tuples(val)) - else: - elements.append(serialize_value(val)) # type: ignore[arg-type] - return tuple(elements) - primary_attrs, custom_attrs = deepcopy(self.primary_attributes), deepcopy( self.custom_attributes ) for attrs in (primary_attrs, custom_attrs): for key, value in attrs.items(): - attrs[key] = serialize_value(value) + attrs[key] = serialize_attribute_value(value) return json.dumps( { @@ -505,20 +466,11 @@ def _create_from_meta( ) value = attributes[attr.name] + if value is None and not attr.primary: + attributes[attr.name] = value + continue - if attr.dtype == datetime: - attributes[attr.name] = get_utc(value) - if attr.dtype == tuple: - if ( - attr.primary or (not attr.primary and value is not None) - ) and not isinstance(value, list): - raise DekerMetaDataError( - f"Collection '{collection.name}' metadata is corrupted: " - f"attribute '{attr.name}' has invalid type '{type(value)}'; '{attr.dtype}' expected" - ) - - if attr.primary or (not attr.primary and value is not None): - attributes[attr.name] = tuple(value) + attributes[attr.name] = deserialize_attribute_value(value, attr.dtype, False) arr_params = { "collection": collection, diff --git a/deker/tools/attributes.py b/deker/tools/attributes.py new file mode 100644 index 0000000..91f2d68 --- /dev/null +++ b/deker/tools/attributes.py @@ -0,0 +1,114 @@ +# deker - multidimensional arrays storage engine +# Copyright (C) 2023 OpenWeather +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import re + +from datetime import datetime +from typing import Any, Tuple, Type, Union + +import numpy as np + +from deker_tools.time import get_utc + + +def serialize_attribute_value( + val: Any, +) -> Union[Tuple[str, int, float, tuple], str, int, float, tuple]: + """Serialize attribute value. + + :param val: complex number + """ + if isinstance(val, datetime): + return val.isoformat() + if isinstance(val, np.ndarray): + return val.tolist() # type: ignore[attr-defined] + if isinstance(val, complex): + return str(val) + if isinstance(val, np.integer): + return int(val) + if isinstance(val, np.floating): + return float(val) + if isinstance(val, np.complexfloating): + return str(complex(val)) + if isinstance(val, (list, tuple)): + return serialize_attribute_nested_tuples(val) # type: ignore[arg-type] + + return val + + +def serialize_attribute_nested_tuples(value: Union[tuple, list]) -> Tuple[Any, ...]: + """Serialize attribute nested tuples and their elements. + + :param value: tuple instance + """ + serialized = [] + for el in value: + if isinstance(el, (list, tuple)): + val = serialize_attribute_nested_tuples(el) + else: + val = serialize_attribute_value(el) + serialized.append(val) # type: ignore[arg-type] + return tuple(serialized) + + +def deserialize_attribute_value(val: Any, dtype: Type, from_tuple: bool) -> Any: + """Deserialize attribute value. + + :param val: attribute value + :param dtype: attribute dtype from schema or type of tuple element + :param from_tuple: flag for tuple inner elements + """ + if dtype == datetime: + val = get_utc(val) + else: + val = dtype(val) + + if isinstance(val, (list, tuple)) and dtype == tuple: + return deserialize_attribute_nested_tuples(val) # type: ignore[arg-type] + + if dtype == str: + # if the value comes from a tuple as one of its elements + if from_tuple: + # it may be a serialized string representation of a complex number + complex_number_regex = re.compile( + r"^(\()([+-]?)\d+(?:\.\d+)?(e?)([+-]?)(\d+)?" + r"([+-]?)\d+(?:\.\d+)?(e?)([+-]?)(\d+)?j(\))$" + ) + # as far as we don't exactly know what it is + # we try to catch it by a regular expression + if re.findall(complex_number_regex, val): # type: ignore[arg-type] + try: + # and to convert it to a complex number if there's a match + return complex(val) # type: ignore[arg-type] + except ValueError: + # if conversion fails we return string + return val + + return val + + +def deserialize_attribute_nested_tuples(value: Tuple[Any, ...]) -> Tuple[Any, ...]: + """Deserialize attribute nested tuples and their elements. + + :param value: attribute tuple value + """ + deserialized = [] + for el in value: + if isinstance(el, (tuple, list)): + value = deserialize_attribute_nested_tuples(el) # type: ignore[arg-type] + else: + value = deserialize_attribute_value(el, type(el), True) + deserialized.append(value) + return tuple(deserialized) diff --git a/deker/validators.py b/deker/validators.py index 3468507..e8dbdce 100644 --- a/deker/validators.py +++ b/deker/validators.py @@ -54,10 +54,11 @@ def __validate_attribute_type(attribute: object) -> None: __validate_attribute_type(attr) dtype = type(attribute) - try: - DTypeEnum(dtype).value - except (ValueError, KeyError): - raise DekerValidationError(f"Invalid dtype value {dtype}") + if attribute is not None: + try: + DTypeEnum(dtype).value + except (ValueError, KeyError): + raise DekerValidationError(f"Invalid dtype value {dtype}") def __process_attributes_types( diff --git a/tests/test_cases/test_schemas/test_attributes_schema.py b/tests/test_cases/test_schemas/test_attributes_schema.py index 334e566..1a4e981 100644 --- a/tests/test_cases/test_schemas/test_attributes_schema.py +++ b/tests/test_cases/test_schemas/test_attributes_schema.py @@ -173,102 +173,6 @@ def test_attributes_schema_primary_param_ok(self, primary): assert a assert a.primary == primary - @pytest.mark.parametrize("primary", [False, True]) - @pytest.mark.parametrize( - ("dtype", "value"), - [ - (str, "123"), - (str, str(complex(-125.000000000001 - 0.123456789j))), - (str, "-125.000000000001-0.123456789j"), - (np.int8, np.int8(1)), - (np.int16, np.int16(-130)), - (np.int32, np.int32(-9999)), - (np.int64, np.int64(99999999)), - (int, 1), - (int, 0), - (int, -1), - (float, 0.1), - (float, -0.1), - (np.float16, np.float16(1.0)), - (np.float32, np.float32(-130)), - (np.float64, np.float64(-9999)), - (np.float128, np.float128(99999999)), - (complex, complex(0.0000000000001)), - (complex, complex(-0.0000000000001)), - (np.complex64, np.complex64(1.0)), - (np.complex128, np.complex128(-130)), - (np.complex256, np.complex256(-9999)), - (tuple, tuple("abc")), - (tuple, tuple({"abc", "def"})), - (tuple, (1, 2, 3, 4)), - (tuple, (1, 0.1, complex(0.0000000000001), complex(-0.0000000000001), -0.1, -1)), - ( - tuple, - ( - (1, 0.1, complex(0.0000000000001), complex(-0.0000000000001), -0.1, -1), - (1, 0.1, complex(0.0000000000001), complex(-0.0000000000001), -0.1, -1), - ), - ), - ( - tuple, - ( - ( - 1, - 0.1, - complex(0.0000000000001), - complex(-0.0000000000001), - -0.1, - -1, - ), - (1, 0.1, complex(0.0000000000001), complex(-0.0000000000001), -0.1, -1), - ), - ), - ( - tuple, - ( - np.int8(1), - np.int16(-130), - np.int32(-9999), - np.int64(99999999), - np.float16(1.0), - np.float32(-130), - np.float64(-9999), - np.float128(99999999), - np.complex64(1.0), - np.complex128(-130), - np.complex256(-9999), - ), - ), - ], - ) - def test_attributes_values_serialize_deserialize_ok(self, client, primary, dtype, value): - schema = ArraySchema( - dimensions=[DimensionSchema(name="x", size=1)], - dtype=int, - attributes=[AttributeSchema(name="some_attr", dtype=dtype, primary=primary)], - ) - col_name = "test_attrs_values_validation" - try: - col = client.create_collection(col_name, schema) - except DekerCollectionAlreadyExistsError: - col = client.get_collection(col_name) - col.clear() - try: - if primary: - key = "primary_attributes" - else: - key = "custom_attributes" - attrs = {key: {schema.attributes[0].name: value}} - array = col.create(**attrs) - assert array - array = [a for a in col][0] - attr = getattr(array, key) - assert attr[schema.attributes[0].name] == value - except Exception: - raise - finally: - col.delete() - @pytest.mark.parametrize("primary", [False, True]) @pytest.mark.parametrize( ("dtype", "value"), diff --git a/tests/test_cases/test_validators/test_attributes_validator.py b/tests/test_cases/test_validators/test_attributes_validator.py index 6ad7442..77622c9 100644 --- a/tests/test_cases/test_validators/test_attributes_validator.py +++ b/tests/test_cases/test_validators/test_attributes_validator.py @@ -1,9 +1,11 @@ import random +import numpy as np import pytest from tests.parameters.collection_params import ClientParams +from deker import ArraySchema, AttributeSchema, DimensionSchema from deker.client import Client from deker.collection import Collection from deker.errors import DekerCollectionAlreadyExistsError, DekerValidationError @@ -617,5 +619,109 @@ def test_no_attributes(self, client): collection.delete() +class TestAttributesValues: + @pytest.mark.parametrize("primary", [False, True]) + @pytest.mark.parametrize( + ("dtype", "value"), + [ + (str, "123"), + ( + str, + "-125.000000000001-0.123456789j", + ), # it's not an exact string representation of a complex number + (np.int8, np.int8(1)), + (np.int16, np.int16(-130)), + (np.int32, np.int32(-9999)), + (np.int64, np.int64(99999999)), + (int, 1), + (int, 0), + (int, -1), + (float, 0.1), + (float, -0.1), + (np.float16, np.float16(1.0)), + (np.float32, np.float32(-130)), + (np.float64, np.float64(-9999)), + (np.float128, np.float128(99999999)), + (complex, complex(0.0000000000001, 9.000000005)), + (complex, complex(-0.0000000000001, -1.000000009)), + (np.complex64, np.complex64(1.0)), + (np.complex128, np.complex128(-130)), + (np.complex256, np.complex256(-9999)), + (tuple, tuple("abc")), + (tuple, tuple({"abc", "def"})), + (tuple, (1, 2, 3, 4)), + ( + tuple, + ( + np.int8(1), + np.int16(-130), + np.int32(-9999), + np.int64(99999999), + np.float16(1.0), + np.float32(-130), + np.float64(-9999), + np.float128(99999999), + np.complex64(1.0), + np.complex128(-130), + np.complex256(-9999), + ), + ), + ( + tuple, + ( + 1, + 0.1, + complex(0.0000000000001, 0.0000000000001), + complex(-0.00000000000012567, -0.0000000000001), + -0.1, + -1, + ), + ), + (tuple, (1, 0.1, complex(0.0000000000001), complex(-0.0000000000001), -0.1, -1)), + ( + tuple, + ( + (1, 0.1, complex(0.0000000000001), complex(-0.0000000000001), -0.1, -1), + (1, 0.1, complex(0.0000000000001), complex(-0.0000000000001), -0.1, -1), + ), + ), + ( + tuple, + ( + (1, 0.1, complex(0.0000000000001), complex(-0.0000000000001), -0.1, -1), + (1, 0.1, complex(0.0000000000001), complex(-0.0000000000001), -0.1, -1), + ), + ), + ], + ) + def test_attributes_values_serialize_deserialize_ok(self, client, primary, dtype, value): + schema = ArraySchema( + dimensions=[DimensionSchema(name="x", size=1)], + dtype=int, + attributes=[AttributeSchema(name="some_attr", dtype=dtype, primary=primary)], + ) + col_name = "test_attrs_values_validation" + try: + col = client.create_collection(col_name, schema) + except DekerCollectionAlreadyExistsError: + col = client.get_collection(col_name) + col.clear() + try: + if primary: + key = "primary_attributes" + else: + key = "custom_attributes" + attrs = {key: {schema.attributes[0].name: value}} + array = col.create(**attrs) + assert array + array = [a for a in col][0] + attr = getattr(array, key) + assert attr[schema.attributes[0].name] == value + except Exception: + raise + finally: + col.delete() + + if __name__ == "__main__": pytest.main() From e64ca9f952c225a42277449586bfef934300b90b Mon Sep 17 00:00:00 2001 From: Sergey Rybakov Date: Wed, 3 Apr 2024 18:39:23 +0300 Subject: [PATCH 05/19] cherry-picked changes from dev --- deker/locks.py | 2 +- deker/log.py | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/deker/locks.py b/deker/locks.py index 32339ac..6319f34 100644 --- a/deker/locks.py +++ b/deker/locks.py @@ -323,7 +323,7 @@ def check_existing_lock(self, func_args: Sequence, func_kwargs: Dict) -> None: # Iterate over Arrays in VArray and try to lock them. If locking fails - wait. # If it fails again - release all locks. currently_locked = self.check_arrays_locks(arrays_positions, adapter, varray) - if not currently_locked and (len(self.locks) == len(arrays_positions)): + if not currently_locked: # Release array locks return diff --git a/deker/log.py b/deker/log.py index 0f99f63..b0f6d7f 100644 --- a/deker/log.py +++ b/deker/log.py @@ -13,20 +13,18 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - import logging -import os -from logging import Logger +from typing import Optional _ROOT_DEKER_LOGGER_NAME = "Deker" -_level = os.getenv("DEKER_LOGLEVEL", "WARNING") format_string = "%(levelname)s | %(asctime)s | %(name)s | %(message)s" fmter = logging.Formatter(fmt=format_string) _logger = logging.getLogger(_ROOT_DEKER_LOGGER_NAME) +_logger.propagate = False _handler = logging.StreamHandler() _handler.setFormatter(fmter) _logger.addHandler(_handler) @@ -35,10 +33,10 @@ class SelfLoggerMixin(object): """Mixin with a logger object with a possibility to log its actions.""" - __logger: Logger = None + __logger: Optional[logging.Logger] = None @property - def logger(self) -> Logger: + def logger(self) -> logging.Logger: """Lazy deker logger property.""" if not self.__logger: self.__logger = _logger.getChild(self.__class__.__name__) From e30580e65077150b002c2fe26c2980278c12087b Mon Sep 17 00:00:00 2001 From: Sergey Rybakov Date: Thu, 4 Apr 2024 12:03:15 +0300 Subject: [PATCH 06/19] DekerVSubsetError is an ExceptionGroup --- deker/errors.py | 34 ++++++++++++++- deker/subset.py | 38 +++++++++-------- pyproject.toml | 1 + .../test_concurrency/test_in_processes.py | 2 +- .../test_varray_subset_methods.py | 41 ++++++++++++++++++- 5 files changed, 95 insertions(+), 21 deletions(-) diff --git a/deker/errors.py b/deker/errors.py index cae065f..2b169aa 100644 --- a/deker/errors.py +++ b/deker/errors.py @@ -13,6 +13,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from typing import List class DekerBaseApplicationError(Exception): @@ -107,7 +108,38 @@ class DekerSubsetError(DekerArrayError): class DekerVSubsetError(DekerSubsetError): - """If something goes wrong while VSubset managing.""" + """If something goes wrong while VSubset managing. + + Regarding that VSubset's inner Subsets' processing + is made in an Executor, this exception actually is + an `exceptions messages group`. + + If one or more threads finished with any exception, + name, message and reasonable tracebacks of all + of these exceptions shall be collected in a list + and passed to this class along with some message. + + ``` + futures = [executor.submit(func, arg) for arg in iterable] + + exceptions = [] + for future in futures: + try: + future.result() + except Exception as e: + exceptions.append(repr(e) + "\n" + traceback.format_exc(-1)) + ``` + """ + + def __init__(self, message: str, exceptions: List[str]): + self.message = message + self.exceptions = exceptions + super().__init__(message) + + def __str__(self) -> str: + enumerated = [f"{num}) {e}" for num, e in enumerate(self.exceptions, start=1)] + joined = "\n".join(str(e) for e in enumerated) + return f"{self.message}; exceptions:\n\n{joined} " class DekerMemoryError(DekerBaseApplicationError, MemoryError): diff --git a/deker/subset.py b/deker/subset.py index 15a4598..850ff72 100644 --- a/deker/subset.py +++ b/deker/subset.py @@ -15,6 +15,7 @@ # along with this program. If not, see . import builtins +import traceback from typing import TYPE_CHECKING, Iterator, List, Optional, Tuple, Union @@ -24,7 +25,7 @@ from numpy import ndarray from deker.ABC.base_subset import BaseSubset -from deker.errors import DekerArrayError, DekerLockError, DekerVSubsetError +from deker.errors import DekerArrayError, DekerVSubsetError from deker.locks import WriteVarrayLock from deker.schemas import TimeDimensionSchema from deker.tools import not_deleted @@ -621,20 +622,23 @@ def _update(array_data: ArrayPositionedData) -> None: self.__array.dtype, self.__array.shape, data, self.__bounds ) - results = self.__adapter.executor.map( - _update, - [ - ArrayPositionedData(vpos, array_bounds, data[data_bounds]) - for vpos, array_bounds, data_bounds in self.__arrays - ], - ) - try: - list(results) - except Exception as e: - if isinstance(e, DekerLockError): - raise e - else: - raise DekerVSubsetError( - f"ATTENTION: Data in {self!s} IS NOW CORRUPTED due to the exception above" - ).with_traceback(e.__traceback__) + positions = [ + ArrayPositionedData(vpos, array_bounds, data[data_bounds]) + for vpos, array_bounds, data_bounds in self.__arrays + ] + futures = [self.__adapter.executor.submit(_update, position) for position in positions] + + exceptions = [] + for future in futures: + try: + future.result() + except Exception as e: + exceptions.append(repr(e) + "\n" + traceback.format_exc(-1)) + + if exceptions: + raise DekerVSubsetError( + f"ATTENTION: Data in {self!s} MAY BE NOW CORRUPTED due to the exceptions occurred in threads", + exceptions, + ) + self.logger.info(f"{self!s} data updated OK") diff --git a/pyproject.toml b/pyproject.toml index de44a77..869a727 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -202,6 +202,7 @@ per-file-ignores = [ "deker/private/types/shell.py: D205, D400", "deker/collection.py: D401", "deker/subset.py: DAR101, E203", + "deker/errors.py: D301", ] ignore = [ "B012", diff --git a/tests/test_cases/test_concurrency/test_in_processes.py b/tests/test_cases/test_concurrency/test_in_processes.py index 43e8e67..d83379a 100644 --- a/tests/test_cases/test_concurrency/test_in_processes.py +++ b/tests/test_cases/test_concurrency/test_in_processes.py @@ -417,7 +417,7 @@ def test_intersecting_vsubsets_update_fail( (np.index_exp[:5, 3:, :], 500), # shall be blocked (np.index_exp[8:, 8:, 8:], blocking_value), # shall proceed as non-locked ) - # Call read process to lock arrays for reading + # Call read process to lock arrays proc = Process( target=call_array_method, args=( diff --git a/tests/test_cases/test_subsets/test_varray_subset_methods.py b/tests/test_cases/test_subsets/test_varray_subset_methods.py index 1b2bbb9..0719a48 100644 --- a/tests/test_cases/test_subsets/test_varray_subset_methods.py +++ b/tests/test_cases/test_subsets/test_varray_subset_methods.py @@ -1,5 +1,10 @@ +import time +import traceback + from collections import OrderedDict +from concurrent.futures import ThreadPoolExecutor from datetime import datetime, timedelta, timezone +from random import randint from typing import Tuple import hdf5plugin @@ -10,15 +15,15 @@ from tests.parameters.index_exp_params import valid_index_exp_params -from deker import Scale from deker.arrays import VArray from deker.client import Client from deker.collection import Collection -from deker.errors import DekerArrayError, DekerSubsetError +from deker.errors import DekerArrayError, DekerSubsetError, DekerVSubsetError from deker.schemas import AttributeSchema, DimensionSchema, TimeDimensionSchema, VArraySchema from deker.subset import VSubset from deker.types.private.classes import ArrayPosition from deker.types.private.typings import Slice +from deker.types.public.classes import Scale class TestVArraySubset: @@ -523,6 +528,38 @@ def test_vsubset_arrays_calc_3dim(self, client): coll.delete() + def test_DekerVSubsetError_exception_group( + self, inserted_varray: VArray, root_path, varray_collection: Collection + ): + """Test raise of DekerVSubsetError.""" + excs = [ValueError, TypeError, FileExistsError, FileNotFoundError, EOFError] + sleep_time = randint(1, 5) + + def raise_exc(exc): + time.sleep(sleep_time) + raise exc(f"This is {exc.__name__}") + + def catch_errors(threads_results): + exceptions = [] + for result in threads_results: + try: + result.result() + except Exception as e: + exceptions.append(repr(e) + "\n" + traceback.format_exc(-1)) + if exceptions: + raise DekerVSubsetError("ExceptionGroup works", exceptions) + + timer = time.monotonic() + with ThreadPoolExecutor(len(excs)) as pool: + futures = [pool.submit(raise_exc, exc) for exc in excs] + + with pytest.raises(DekerVSubsetError): + assert catch_errors(threads_results=futures) + # catch_errors(threads_results=futures) + + test_time = int(time.monotonic() - timer) + assert test_time <= sleep_time + class TestVSubsetForXArray: @pytest.fixture(scope="class") From f3040c7486f7d1f8db1a59804260d0dd7ceb202f Mon Sep 17 00:00:00 2001 From: matveyvarg Date: Tue, 9 Apr 2024 13:58:25 +0200 Subject: [PATCH 07/19] Changes before moving --- deker/ABC/base_locks.py | 14 +- deker/errors.py | 3 + deker/locks.py | 276 ++++++++++-------- deker/tools/path.py | 1 + deker/types/private/enums.py | 2 + .../test_client/test_client_methods.py | 24 +- tests/test_cases/test_locking/test_locks.py | 28 +- 7 files changed, 205 insertions(+), 143 deletions(-) diff --git a/deker/ABC/base_locks.py b/deker/ABC/base_locks.py index 590a94f..cc2ea60 100644 --- a/deker/ABC/base_locks.py +++ b/deker/ABC/base_locks.py @@ -15,7 +15,7 @@ # along with this program. If not, see . """Abstract interfaces for locks.""" - +# TODO: MOVED from abc import ABC, abstractmethod from functools import wraps from pathlib import Path @@ -31,16 +31,15 @@ class BaseLock(SelfLoggerMixin, ABC): ALLOWED_TYPES: List[str] = [] lock: Optional[Union[Flock, Path]] = None instance: Optional[Any] = None + args: Optional[List[Any]] = None + kwargs: Optional[Dict] = None @abstractmethod - def get_path(self, func_args: Sequence, func_kwargs: Dict) -> Optional[Path]: + def get_path(self) -> Optional[Path]: """Get path to the lock file. For Arrays it shall be .arrlock (array read lock) or path to the file (array write lock) For VArrays there are no specific locks for reading, for writing - lock on .json - - :param func_args: Arguments of method call - :param func_kwargs: Keyword arguments of method call """ pass @@ -98,7 +97,7 @@ def _inner_method_logic(lock: "BaseLock", args: Sequence, kwargs: Dict, func: Ca :param kwargs: Keyword arguments of decorated function """ lock.check_existing_lock(args, kwargs) - path = lock.get_path(args, kwargs) + path = lock.get_path() lock.acquire(path) try: # Logic is in the separate method, so we can override its behavior @@ -129,7 +128,8 @@ def inner(*args: Sequence, **kwargs: Dict[str, Any]) -> Any: lock = self.__class__() # as we wrap methods, we should have access to 'self' objects lock.instance = kwargs.get("self") or args[0] - + lock.args = args + lock.kwargs = kwargs # Check that we don't have lock on anything that besides methods that require lock lock.check_type() diff --git a/deker/errors.py b/deker/errors.py index cae065f..0801ff0 100644 --- a/deker/errors.py +++ b/deker/errors.py @@ -15,6 +15,7 @@ # along with this program. If not, see . +# TODO: MOVED class DekerBaseApplicationError(Exception): """Base attribute exception.""" @@ -83,6 +84,7 @@ class DekerIntegrityError(DekerBaseApplicationError): pass +# TODO: MOVED class DekerLockError(DekerBaseApplicationError): """If a Collection or a Array or VArray instance is locked.""" @@ -110,5 +112,6 @@ class DekerVSubsetError(DekerSubsetError): """If something goes wrong while VSubset managing.""" +# TODO: MOVED class DekerMemoryError(DekerBaseApplicationError, MemoryError): """Early memory overflow exception.""" diff --git a/deker/locks.py b/deker/locks.py index 5aaa723..4bbb708 100644 --- a/deker/locks.py +++ b/deker/locks.py @@ -19,11 +19,24 @@ import fcntl import os import time +from contextlib import contextmanager from pathlib import Path from threading import get_native_id from time import sleep -from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Sequence, Union, Tuple +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + List, + Optional, + Sequence, + Union, + Tuple, + TypeVar, + Generic, +) from uuid import uuid4 from deker.ABC.base_locks import BaseLock @@ -40,40 +53,109 @@ from deker.types.private.classes import ArrayPositionedData META_DIVIDER = ":" +ArrayFromArgs = Union[Path, Union["Array", "VArray"]] +T = TypeVar("T") -class ReadArrayLock(BaseLock): +def _get_lock_filename(id_: str, lock_ext: LocksExtensions) -> str: + """Get filename for lockfile + + :param id_: ID of array + :param lock_ext: Extension of lock + :return: + """ + name = META_DIVIDER.join([f"{id_}", f"{uuid4()}", f"{os.getpid()}", f"{get_native_id()}"]) + return str(name + lock_ext.value) + + +def _check_write_locks(dir_path: Path, id_: str) -> bool: + """Checks write locks from VArrays that differs from current. + + :param dir_path: Dir where locks are stored (the one with hdf file) + """ + for file in dir_path.iterdir(): + # Skip lock from current process. + # Used when you have to read meta inside .update operation of varray + if file.name.endswith(f"{os.getpid()}{LocksExtensions.varray_lock.value}"): + return True + # If we've found another varray lock, that not from current process. + if file.name.endswith(LocksExtensions.varray_lock.value): # type: ignore + raise DekerLockError(f"Array {id_} is locked with {file.name}") + return False + + +class LockWithArrayMixin(Generic[T]): + """Base class with getter of array.""" + + args: Optional[List[Any]] + kwargs: Optional[Dict] + instance: Optional[Any] + is_locked_with_varray: bool = False + + @property + def array_id(self) -> str: + """Return id of an Array""" + return self.array.id + + @property + def array(self) -> T: + """Parse array from args and save ref to it.""" + array = self.kwargs.get("array") or self.args[1] # zero arg is 'self' + return array + + @property + def dir_path(self) -> Path: + """Path to directory with main file.""" + return get_main_path(self.array_id, self.instance.collection_path / self.instance.data_dir) + + +def wait_for_unlock(check_func: Callable, check_func_args: tuple, timeout, interval: float) -> bool: + """Waiting while there is no locks + + :param instance: + :param check_func: + :param check_func_args: + :return: + """ + start_time = time.monotonic() + while (time.monotonic() - start_time) <= timeout: + if check_func(*check_func_args): + return True + sleep(interval) + return False + + +class ReadArrayLock(LockWithArrayMixin[ArrayFromArgs], BaseLock): """Read lock for Array.""" ALLOWED_TYPES = ["LocalArrayAdapter"] - def get_path(self, func_args: Sequence, func_kwargs: Dict) -> Path: + @property + def array_id(self) -> str: + """Get if from Array, or Path to the array.""" + # Get instance of the array + if isinstance(self.array, Path): + path = self.array + id_ = path.name.split(".")[0] + else: + id_ = self.array.id + return id_ + + def get_path(self) -> Path: """Get path to read-lock file. It's only the case for arrays, varrays don't have read locks. :param func_args: arguments of method call :param func_kwargs: keyword arguments of method call """ - # Get instance of the array - array: Union[Path, Union[Array, VArray]] = ( - func_kwargs.get("array") or func_args[1] - ) # zero arg is 'self' - if isinstance(array, Path): - path = array - id_ = path.name.split(".")[0] - else: - id_ = array.id # Get file directory - dir_path = get_main_path(id_, self.instance.collection_path / self.instance.data_dir) - filename = ( - META_DIVIDER.join([f"{id_}", f"{uuid4()}", f"{os.getpid()}", f"{get_native_id()}"]) - + LocksExtensions.array_read_lock.value - ) + filename = _get_lock_filename(self.array_id, LocksExtensions.array_read_lock) + # Create read lock file path - path = dir_path / f"{filename}" + path = self.dir_path / f"{filename}" - self.logger.debug(f"Got path for array.id {id_} lock file: {path}") + self.logger.debug(f"Got path for array.id {self.array_id} lock file: {path}") return path def check_existing_lock(self, func_args: Sequence, func_kwargs: Dict) -> None: @@ -83,32 +165,22 @@ def check_existing_lock(self, func_args: Sequence, func_kwargs: Dict) -> None: :param func_args: arguments for called function. :param func_kwargs: keyword arguments for called function. """ - array: Union[Path, Union[Array, VArray]] = ( - func_kwargs.get("array") or func_args[1] - ) # zero arg is 'self' - if isinstance(array, Path): - path = array - id_ = path.name.split(".")[0] - else: - id_ = array.id - - dir_path = get_main_path(id_, self.instance.collection_path / self.instance.data_dir) - path = dir_path / (id_ + self.instance.file_ext) - for file in dir_path.iterdir(): - # Skip lock from current process. - if file.name.endswith(f"{os.getpid()}{LocksExtensions.varray_lock.value}"): - self.is_locked_with_varray = True - return - # If we've found another varray lock, that not from current process. - if file.name.endswith(LocksExtensions.varray_lock.value): # type: ignore - raise DekerLockError(f"Array {array} is locked with {file.name}") + # Check write locks + if _check_write_locks(self.dir_path, self.array_id): + self.is_locked_with_varray = True + # File was locked with VArray from current process. + return + # No write locks found + path = self.dir_path / (self.array_id + self.instance.file_ext) try: with open(path, "r") as f: fcntl.flock(f, fcntl.LOCK_SH | fcntl.LOCK_NB) self.logger.debug(f"Set shared flock for {path}") except BlockingIOError: - raise DekerLockError(f"Array {id_} is locked for update operation, cannot be read.") + raise DekerLockError( + f"Array {self.array_id} is locked for update operation, cannot be read." + ) def acquire(self, path: Union[str, Path]) -> Any: """Read files will not be flocked - only created. @@ -118,7 +190,7 @@ def acquire(self, path: Union[str, Path]) -> Any: # Create lock file self.lock = path open(path, "a").close() - self.logger.debug(f"Acquired lock for {self.lock}") + self.logger.debug(f"Acquired read lock for {self.lock}") def release(self, e: Optional[Exception] = None) -> None: # noqa[ARG002] """Release lock by deleting file. @@ -127,27 +199,19 @@ def release(self, e: Optional[Exception] = None) -> None: # noqa[ARG002] """ if self.lock and self.lock.exists(): self.lock.unlink() + self.logger.debug(f"Releasing read lock for {self.lock}") self.lock = None - self.logger.debug(f"Released lock for {self.lock}") -class WriteArrayLock(BaseLock): +class WriteArrayLock(LockWithArrayMixin["Array"], BaseLock): """Write lock for arrays.""" ALLOWED_TYPES = ["LocalArrayAdapter"] - is_locked_with_varray: bool = False - - def get_path(self, func_args: Sequence, func_kwargs: Dict) -> Path: - """Get path to the file for locking. - - :param func_args: arguments of method call - :param func_kwargs: keyword arguments of method call - """ - array = func_kwargs.get("array") or func_args[1] # zero arg is 'self' - dir_path = get_main_path(array.id, self.instance.collection_path / self.instance.data_dir) - path = dir_path / (array.id + self.instance.file_ext) - self.logger.debug(f"Got path for array.id {array.id} lock file: {path}") + def get_path(self) -> Path: + """Get path to the file for locking.""" + path = self.dir_path / (self.array.id + self.instance.file_ext) + self.logger.debug(f"Got path for array.id {self.array.id} lock file: {path}") return path def check_existing_lock(self, func_args: Sequence, func_kwargs: Dict) -> None: @@ -156,38 +220,29 @@ def check_existing_lock(self, func_args: Sequence, func_kwargs: Dict) -> None: :param func_args: arguments of method call :param func_kwargs: keyword arguments of method call """ - # Check current read locks - array = func_kwargs.get("array") or func_args[1] # zero arg is 'self' - dir_path = get_main_path(array.id, self.instance.collection_path / self.instance.data_dir) - # If array belongs to varray, we should check if varray is also locked - if array._vid: - for file in dir_path.iterdir(): - # Skip lock from current process. - if file.name.endswith(f"{os.getpid()}{LocksExtensions.varray_lock.value}"): - self.is_locked_with_varray = True - return - # If we've found another varray lock, that not from current process. - if file.name.endswith(LocksExtensions.varray_lock.value): # type: ignore - raise DekerLockError(f"Array {array} is locked with {file.name}") + if self.array._vid: + self.is_locked_with_varray = _check_write_locks(self.dir_path, self.array.id) # Increment write lock, to prevent more read locks coming. - self.acquire(self.get_path(func_args, func_kwargs)) + self.acquire(self.get_path()) # Pattern that has to find any read locks - glob_pattern = f"{array.id}:*{LocksExtensions.array_read_lock.value}" + glob_pattern = f"{self.array.id}:*{LocksExtensions.array_read_lock.value}" # Wait till there are no more read locks - start_time = time.monotonic() - while (time.monotonic() - start_time) <= self.instance.ctx.config.write_lock_timeout: - if not list(dir_path.rglob(glob_pattern)): - return - - sleep(self.instance.ctx.config.write_lock_check_interval) + if wait_for_unlock( + lambda path, pattern: not list(path.rglob(pattern)), + (self.dir_path, glob_pattern), + self.instance.ctx.config.write_lock_timeout, + self.instance.ctx.config.write_lock_check_interval, + ): + # If all locks are released, go further + return # If we hit the timeout, release write lock and raise DekerLockError self.release() - raise DekerLockError(f"Array {array} is locked with read locks") + raise DekerLockError(f"Array {self.array} is locked with read locks") def release(self, e: Optional[Exception] = None) -> None: """Release Flock. @@ -237,7 +292,7 @@ def check_type(self) -> None: if not is_running_on_local: self.skip_lock = True - def get_path(self, func_args: Sequence, func_kwargs: Dict) -> Optional[Path]: # noqa[ARG002] + def get_path(self) -> Optional[Path]: # noqa[ARG002] """Path of json Varray file. :param func_args: arguments of the function that has been called. @@ -273,7 +328,6 @@ def check_locks_for_array_and_set_flock(self, filename: Path) -> Flock: def check_arrays_locks( self, - arrays_positions: List[ArrayPositionedData], adapter: LocalArrayAdapter, varray: VArray, ) -> List[Path]: @@ -286,7 +340,7 @@ def check_arrays_locks( currently_locked = [] collection = varray._VArray__collection # type: ignore[attr-defined] - for array_position in arrays_positions: + for array_position in self.instance._VSubset__arrays: filename = adapter._get_symlink_filename( varray.id, array_position.vposition, @@ -314,25 +368,25 @@ def check_existing_lock(self, func_args: Sequence, func_kwargs: Dict) -> None: adapter = self.instance._VSubset__array_adapter varray = self.instance._VSubset__array - arrays_positions: List[ArrayPositionedData] = self.instance._VSubset__arrays - # Clear links to locks self.locks = [] # Locks that have been acquired by third party process # Iterate over Arrays in VArray and try to lock them. If locking fails - wait. # If it fails again - release all locks. - currently_locked = self.check_arrays_locks(arrays_positions, adapter, varray) + currently_locked = self.check_arrays_locks(adapter, varray) if not currently_locked: # Release array locks return # Wait till there are no more read locks - start_time = time.monotonic() - while (time.monotonic() - start_time) <= adapter.ctx.config.write_lock_timeout: - if not self.check_arrays_locks(arrays_positions, adapter, varray): - return - sleep(adapter.ctx.config.write_lock_check_interval) + if wait_for_unlock( + check_func=lambda: not self.check_arrays_locks(adapter, varray), + check_func_args=tuple(), + timeout=adapter.ctx.config.write_lock_timeout, + interval=adapter.ctx.config.write_lock_check_interval, + ): + return # Release all locks self.release() raise DekerLockError(f"VArray {varray} is locked") @@ -376,39 +430,25 @@ def _inner_method_logic( return super()._inner_method_logic(lock, args, kwargs, func) -class CreateArrayLock(BaseLock): +class CreateArrayLock(LockWithArrayMixin[Union["Array", "VArray", dict]], BaseLock): """Lock that we set when we want to create an array.""" ALLOWED_TYPES = ["LocalArrayAdapter", "LocalVArrayAdapter"] path: Optional[Path] = None - def get_path(self, func_args: Sequence, func_kwargs: Dict) -> Path: - """Return path to the file that should be locked. - - :param func_args: arguments for called method - :param func_kwargs: keyword arguments for called method - :return: - """ - array = func_kwargs.get("array") or func_args[1] # zero arg is 'self' - + def get_path(self) -> Path: + """Return path to the file that should be locked.""" # Get file directory path dir_path = self.instance.collection_path - filename = META_DIVIDER.join( - [ - f"{array.id}", - f"{uuid4()}", - f"{os.getpid()}", - f"{get_native_id()}", - ] - ) + # Create lock file - path = dir_path / f"{filename}{LocksExtensions.array_lock.value}" + path = dir_path / _get_lock_filename(self.array.id, LocksExtensions.array_lock) if not path.exists(): path.open("w").close() self.path = path - self.logger.debug(f"got path for array.id {array.id} lock file: {path}") + self.logger.debug(f"got path for array.id {self.array.id} lock file: {path}") return path def check_existing_lock(self, func_args: Sequence, func_kwargs: Dict) -> None: @@ -419,8 +459,8 @@ def check_existing_lock(self, func_args: Sequence, func_kwargs: Dict) -> None: """ from deker.arrays import Array, VArray + array = self.array # Check current read locks - array = func_kwargs.get("array") or func_args[1] # zero arg is 'self' if isinstance(array, dict): adapter = array["adapter"].__class__.__name__ if adapter not in self.ALLOWED_TYPES: @@ -435,7 +475,7 @@ def check_existing_lock(self, func_args: Sequence, func_kwargs: Dict) -> None: # Pattern that has to find any create locks glob_pattern = f"{array.id}:*{LocksExtensions.array_lock.value}" for _ in dir_path.rglob(glob_pattern): - raise DekerLockError(f"Array {array} is locked for creating") + raise DekerLockError(f"Array {array.id} is locked for creating") func_kwargs["array"] = array @@ -464,24 +504,24 @@ def release(self, e: Optional[Exception] = None) -> None: super().release(e) -class UpdateMetaAttributeLock(BaseLock): +class UpdateMetaAttributeLock(LockWithArrayMixin[Union["Array", "VArray"]], BaseLock): """Lock for updating meta.""" ALLOWED_TYPES = ["LocalArrayAdapter", "LocalVArrayAdapter"] - def get_path(self, func_args: Sequence, func_kwargs: Dict) -> Path: + def get_path(self) -> Path: """Return path to the file that should be locked. :param func_args: arguments for called method :param func_kwargs: keyword arguments for called method :return: """ - array = func_kwargs.get("array") or func_args[1] # zero arg is 'self' - # Get directory where file locates - dir_path = get_main_path(array.id, self.instance.collection_path / self.instance.data_dir) - path = dir_path / f"{array.id}{self.instance.file_ext}" - self.logger.debug(f"Got path for array.id {array.id} lock file: {path}") + dir_path = get_main_path( + self.array.id, self.instance.collection_path / self.instance.data_dir + ) + path = dir_path / f"{self.array.id}{self.instance.file_ext}" + self.logger.debug(f"Got path for array.id {self.array.id} lock file: {path}") return path @@ -490,13 +530,13 @@ class CollectionLock(BaseLock): ALLOWED_TYPES = ["LocalCollectionAdapter"] - def get_path(self, func_args: Sequence, func_kwargs: Dict) -> Path: # noqa[ARG002] + def get_path(self) -> Path: # noqa[ARG002] """Return path to collection lock file. :param func_args: arguments for called method :param func_kwargs: keyword arguments for called method """ - collection = func_args[1] + collection = self.args[1] path = self.instance.collections_resource / (collection.name + ".lock") self.logger.debug(f"Got path for collection {collection.name} lock file: {path}") return path diff --git a/deker/tools/path.py b/deker/tools/path.py index 58ad76d..8deac53 100644 --- a/deker/tools/path.py +++ b/deker/tools/path.py @@ -54,6 +54,7 @@ def get_symlink_path( return symlink_path +# TODO: MOVED def get_main_path(array_id: str, data_directory: Path) -> Path: """Generate main path for the given array id by its type. diff --git a/deker/types/private/enums.py b/deker/types/private/enums.py index 882f6ed..14d3acb 100644 --- a/deker/types/private/enums.py +++ b/deker/types/private/enums.py @@ -88,6 +88,7 @@ class DimensionType(str, Enum): time = "time" +# TODO: MOVED class LocksExtensions(str, Enum): """Extensions for lock files.""" @@ -97,6 +98,7 @@ class LocksExtensions(str, Enum): varray_lock = ".varraylock" +# TODO: MOVED class LocksTypes(str, Enum): """Locks enum.""" diff --git a/tests/test_cases/test_client/test_client_methods.py b/tests/test_cases/test_client/test_client_methods.py index 2eb2921..8d63cc7 100644 --- a/tests/test_cases/test_client/test_client_methods.py +++ b/tests/test_cases/test_client/test_client_methods.py @@ -382,7 +382,10 @@ def test_client_check_integrity_collection( try: client.check_integrity(2, stop_on_error=False, collection=collection_1.name) except Exception as e: - assert str(e) == f"Collection \"{collection_1.name}\" metadata is invalid/corrupted: 'test'" + assert ( + str(e) + == f"Collection \"{collection_1.name}\" metadata is invalid/corrupted: 'test'" + ) errors = capsys.readouterr().out collection_1.delete() collection_2.delete() @@ -771,7 +774,9 @@ def test_client_get_locks_array_read_lock( self, client: Client, params, read_array_lock, inserted_array ): client.create_collection(**params) - file = read_array_lock.get_path(func_args=[], func_kwargs={"array": inserted_array}) + read_array_lock.args = [] + read_array_lock.kwargs = {"array": inserted_array} + file = read_array_lock.get_path() file.touch() meta = file.name.split(":") assert { @@ -796,7 +801,9 @@ def test_client_get_locks_type_array_read_lock( self, client: Client, params, read_array_lock, inserted_array ): client.create_collection(**params) - file = read_array_lock.get_path(func_args=[], func_kwargs={"array": inserted_array}) + read_array_lock.args = [] + read_array_lock.kwargs = {"array": inserted_array} + file = read_array_lock.get_path() file.touch() meta = file.name.split(":") assert [ @@ -823,7 +830,9 @@ def test_client_get_locks_skip_lock( self, client: Client, params, read_array_lock, inserted_array ): client.create_collection(**params) - file = read_array_lock.get_path(func_args=[], func_kwargs={"array": inserted_array}) + read_array_lock.args = [] + read_array_lock.kwargs = {"array": inserted_array} + file = read_array_lock.get_path() open(f"{file}:{os.getpid()}{LocksExtensions.varray_lock.value}", "w").close() assert not client._get_locks(lock_type=LocksTypes.varray_lock) @@ -849,7 +858,9 @@ def test_client_clear_locks_collection_empty(self, client: Client, params): def test_client_clear_locks_inserted_array( self, client: Client, root_path, read_array_lock, inserted_array ): - filepath = read_array_lock.get_path(func_args=[], func_kwargs={"array": inserted_array}) + read_array_lock.args = [] + read_array_lock.kwargs = {"array": inserted_array} + filepath = read_array_lock.get_path() filepath.touch() assert list(Path.rglob(root_path, f"*{LocksExtensions.array_read_lock.value}")) client.clear_locks() @@ -858,7 +869,8 @@ def test_client_clear_locks_inserted_array( def test_client_clear_locks_inserted_array_multiple_locks( self, client: Client, root_path, read_array_lock, inserted_array ): - filepath = read_array_lock.get_path(func_args=[], func_kwargs={"array": inserted_array}) + read_array_lock.kwargs = {"array": inserted_array} + filepath = read_array_lock.get_path() open(f"{filepath}:{os.getpid()}{LocksExtensions.varray_lock.value}", "w").close() filepath.touch() assert list(Path.rglob(root_path, f"*{LocksExtensions.array_read_lock.value}")) diff --git a/tests/test_cases/test_locking/test_locks.py b/tests/test_cases/test_locking/test_locks.py index 34f19fb..f9b2a9a 100644 --- a/tests/test_cases/test_locking/test_locks.py +++ b/tests/test_cases/test_locking/test_locks.py @@ -93,9 +93,9 @@ def test_read_array_lock_path( ) ) ) - assert pattern.match( - str(read_array_lock.get_path(func_args=[], func_kwargs={"array": inserted_array})) - ) + read_array_lock.args = [] + read_array_lock.kwargs = {"array": inserted_array} + assert pattern.match(str(read_array_lock.get_path())) def test_read_array_check_existing_lock( self, @@ -110,6 +110,7 @@ def test_read_array_check_existing_lock( hdf_path = dir_path / (inserted_array.id + local_array_adapter.file_ext) with Flock(hdf_path): with pytest.raises(DekerLockError): + read_array_lock.kwargs = {"array": inserted_array} read_array_lock.check_existing_lock( func_args=[], func_kwargs={"array": inserted_array} ) @@ -128,7 +129,8 @@ def test_read_array_locks_create_files( mocker.patch("deker.locks.uuid4", lambda: uuid) # Get path of the file that should be created - filepath = read_array_lock.get_path(func_args=[], func_kwargs={"array": inserted_array}) + read_array_lock.kwargs = {"array": inserted_array} + filepath = read_array_lock.get_path() # Mocked method to test def check_file(self, *args, **kwargs): @@ -175,10 +177,8 @@ def test_write_array_lock_lock_path( inserted_array.id, local_array_adapter.collection_path / local_array_adapter.data_dir ) hdf_path = dir_path / (inserted_array.id + local_array_adapter.file_ext) - - assert hdf_path == write_array_lock.get_path( - func_args=[], func_kwargs={"array": inserted_array} - ) + write_array_lock.kwargs = {"array": inserted_array} + assert hdf_path == write_array_lock.get_path() def test_write_array_lock_check_existing_lock_no_read_locks( self, @@ -191,7 +191,7 @@ def test_write_array_lock_check_existing_lock_no_read_locks( # Mock acquire as we do not want to lock file here. acquire = mocker.patch.object(write_array_lock, "acquire", autospec=True) release = mocker.patch.object(write_array_lock, "release", autospec=True) - + write_array_lock.kwargs = {"array": inserted_array} write_array_lock.check_existing_lock(func_args=[], func_kwargs={"array": inserted_array}) acquire.assert_called() release.assert_not_called() @@ -213,7 +213,8 @@ def test_write_array_lock_check_existing_lock_read_locks_success( release = mocker.patch.object(write_array_lock, "release", autospec=True) # Make read lock - filepath = write_array_lock.get_path(func_args=[], func_kwargs={"array": inserted_array}) + write_array_lock.kwargs = {"array": inserted_array} + filepath = write_array_lock.get_path() process = Process(target=make_read_lock, args=(filepath, inserted_array.id, file_created)) process.start() @@ -242,12 +243,14 @@ def test_write_array_lock_check_existing_lock_read_locks_fail( release = mocker.patch.object(write_array_lock, "release", autospec=True) # Make read lock - filepath = write_array_lock.get_path(func_args=[], func_kwargs={"array": inserted_array}) + write_array_lock.kwargs = {"array": inserted_array} + filepath = write_array_lock.get_path() process = Process(target=make_read_lock, args=(filepath, inserted_array.id, file_created)) process.start() # Call check existing with pytest.raises(DekerLockError): + write_array_lock.kwargs = {"array": inserted_array} write_array_lock.check_existing_lock( func_args=[], func_kwargs={"array": inserted_array} ) @@ -308,7 +311,8 @@ def test_check_get_path( path = get_main_path( inserted_varray.id, varray_collection.path / local_varray_adapter.data_dir ) / (inserted_varray.id + local_varray_adapter.file_ext) - assert path == write_varray_lock.get_path([], {}) + write_varray_lock.kwargs = {"array": inserted_varray} + assert path == write_varray_lock.get_path() def test_check_existing_locks_fail( self, From 3aacf3dd8a224092791bdd835763362ac23509b3 Mon Sep 17 00:00:00 2001 From: matveyvarg Date: Mon, 15 Apr 2024 14:50:59 +0200 Subject: [PATCH 08/19] Locks and ordered dict --- deker/ABC/base_array.py | 39 ++++++++++++------- deker/locks.py | 34 +++++----------- .../test_arrays/test_array_methods.py | 39 ++++++++++++++++++- 3 files changed, 73 insertions(+), 39 deletions(-) diff --git a/deker/ABC/base_array.py b/deker/ABC/base_array.py index a435d59..e8adaa5 100644 --- a/deker/ABC/base_array.py +++ b/deker/ABC/base_array.py @@ -470,34 +470,45 @@ def _create_from_meta( attrs_schema = collection.varray_schema.attributes else: attrs_schema = collection.array_schema.attributes + try: - for attr in attrs_schema: - attributes = ( - meta["primary_attributes"] if attr.primary else meta["custom_attributes"] - ) + # To ensure the order of attributes + primary_attributes, custom_attributes = OrderedDict(), OrderedDict() + + # Iterate over every attribute in schema: + for attr_schema in attrs_schema: + if attr_schema.primary: + attributes_from_meta = meta["primary_attributes"] + result_attributes = primary_attributes + else: + attributes_from_meta = meta["custom_attributes"] + result_attributes = custom_attributes - value = attributes[attr.name] + value = attributes_from_meta[attr_schema.name] - if attr.dtype == datetime: - attributes[attr.name] = get_utc(value) - if attr.dtype == tuple: + if attr_schema.dtype == datetime: + result_attributes[attr_schema.name] = get_utc(value) + elif attr_schema.dtype == tuple: if ( - attr.primary or (not attr.primary and value is not None) + attr_schema.primary or (not attr_schema.primary and value is not None) ) and not isinstance(value, list): raise DekerMetaDataError( f"Collection '{collection.name}' metadata is corrupted: " - f"attribute '{attr.name}' has invalid type '{type(value)}'; '{attr.dtype}' expected" + f"attribute '{attr_schema.name}' has invalid type '{type(value)}';" + f"'{attr_schema.dtype}' expected" ) - if attr.primary or (not attr.primary and value is not None): - attributes[attr.name] = tuple(value) + if attr_schema.primary or (not attr_schema.primary and value is not None): + result_attributes[attr_schema.name] = tuple(value) + else: + result_attributes[attr_schema.name] = value arr_params = { "collection": collection, "adapter": array_adapter, "id_": meta["id"], - "primary_attributes": meta.get("primary_attributes"), - "custom_attributes": meta.get("custom_attributes"), + "primary_attributes": primary_attributes, + "custom_attributes": custom_attributes, } if varray_adapter: arr_params.update({"adapter": varray_adapter, "array_adapter": array_adapter}) diff --git a/deker/locks.py b/deker/locks.py index 4bbb708..054273d 100644 --- a/deker/locks.py +++ b/deker/locks.py @@ -19,7 +19,6 @@ import fcntl import os import time -from contextlib import contextmanager from pathlib import Path from threading import get_native_id @@ -50,7 +49,6 @@ from deker_local_adapters import LocalArrayAdapter from deker.arrays import Array, VArray - from deker.types.private.classes import ArrayPositionedData META_DIVIDER = ":" ArrayFromArgs = Union[Path, Union["Array", "VArray"]] @@ -94,8 +92,14 @@ class LockWithArrayMixin(Generic[T]): @property def array_id(self) -> str: - """Return id of an Array""" - return self.array.id + """Get if from Array, or Path to the array.""" + # Get instance of the array + if isinstance(self.array, Path): + path = self.array + id_ = path.name.split(".")[0] + else: + id_ = self.array.id + return id_ @property def array(self) -> T: @@ -128,19 +132,6 @@ def wait_for_unlock(check_func: Callable, check_func_args: tuple, timeout, inter class ReadArrayLock(LockWithArrayMixin[ArrayFromArgs], BaseLock): """Read lock for Array.""" - ALLOWED_TYPES = ["LocalArrayAdapter"] - - @property - def array_id(self) -> str: - """Get if from Array, or Path to the array.""" - # Get instance of the array - if isinstance(self.array, Path): - path = self.array - id_ = path.name.split(".")[0] - else: - id_ = self.array.id - return id_ - def get_path(self) -> Path: """Get path to read-lock file. @@ -206,11 +197,9 @@ def release(self, e: Optional[Exception] = None) -> None: # noqa[ARG002] class WriteArrayLock(LockWithArrayMixin["Array"], BaseLock): """Write lock for arrays.""" - ALLOWED_TYPES = ["LocalArrayAdapter"] - def get_path(self) -> Path: """Get path to the file for locking.""" - path = self.dir_path / (self.array.id + self.instance.file_ext) + path = self.dir_path / (self.array_id + self.instance.file_ext) self.logger.debug(f"Got path for array.id {self.array.id} lock file: {path}") return path @@ -221,8 +210,7 @@ def check_existing_lock(self, func_args: Sequence, func_kwargs: Dict) -> None: :param func_kwargs: keyword arguments of method call """ # If array belongs to varray, we should check if varray is also locked - if self.array._vid: - self.is_locked_with_varray = _check_write_locks(self.dir_path, self.array.id) + self.is_locked_with_varray = _check_write_locks(self.dir_path, self.array_id) # Increment write lock, to prevent more read locks coming. self.acquire(self.get_path()) @@ -275,8 +263,6 @@ class WriteVarrayLock(BaseLock): which managed to obtain all Array locks, will survive. """ - ALLOWED_TYPES = ["VSubset"] - # Locks that have been acquired by varray locks: List[Tuple[Flock, Path]] = [] skip_lock: bool = False # shows that we must skip this lock (e.g server adapters for subset) diff --git a/tests/test_cases/test_arrays/test_array_methods.py b/tests/test_cases/test_arrays/test_array_methods.py index c5b400c..a45a1f5 100644 --- a/tests/test_cases/test_arrays/test_array_methods.py +++ b/tests/test_cases/test_arrays/test_array_methods.py @@ -1,17 +1,20 @@ import os import string +from copy import deepcopy from datetime import datetime, timedelta, timezone from pathlib import Path +from random import shuffle from typing import Any import numpy as np import pytest -from deker_local_adapters import HDF5StorageAdapter +from deker_local_adapters import HDF5StorageAdapter, LocalArrayAdapter, LocalVArrayAdapter from deker_local_adapters.factory import AdaptersFactory from numpy import ndarray +from deker.types import ArrayMeta from tests.parameters.array_params import attributes_validation_params from tests.parameters.index_exp_params import invalid_index_params, valid_index_exp_params from tests.parameters.uri import embedded_uri @@ -717,6 +720,40 @@ def test_step_validator(self, array: Array, index_exp): with pytest.raises(IndexError): array[index_exp] + def test_create_from_meta_ordered( + self, + array_collection_with_attributes: Collection, + local_array_adapter: LocalArrayAdapter, + local_varray_adapter: LocalVArrayAdapter, + array_with_attributes: Array, + ): + meta: ArrayMeta = array_with_attributes.as_dict + + primary_attribute_keys = list(meta["primary_attributes"].keys()) + shuffle(primary_attribute_keys) + + custom_attribute_keys = list(meta["custom_attributes"].keys()) + shuffle(custom_attribute_keys) + + primary_attributes, custom_attributes = {}, {} + for key in primary_attribute_keys: + primary_attributes[key] = meta["primary_attributes"][key] + + for key in custom_attribute_keys: + custom_attributes[key] = meta["custom_attributes"][key] + + meta["primary_attributes"] = primary_attributes + meta["custom_attributes"] = custom_attributes + + array = Array._create_from_meta( + array_collection_with_attributes, + meta=meta, + array_adapter=local_array_adapter, + varray_adapter=None, + ) + assert array.primary_attributes == array_with_attributes.primary_attributes + assert array.custom_attributes == array_with_attributes.custom_attributes + if __name__ == "__main__": pytest.main() From ffc4dea39ba40c6e41e999a0b15bb78a6191f768 Mon Sep 17 00:00:00 2001 From: matveyvarg Date: Mon, 15 Apr 2024 22:12:23 +0200 Subject: [PATCH 09/19] tests & locks fix --- deker/locks.py | 4 ++ .../test_arrays/test_varray_methods.py | 38 ++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/deker/locks.py b/deker/locks.py index ef00b70..da34768 100644 --- a/deker/locks.py +++ b/deker/locks.py @@ -132,6 +132,8 @@ def wait_for_unlock(check_func: Callable, check_func_args: tuple, timeout, inter class ReadArrayLock(LockWithArrayMixin[ArrayFromArgs], BaseLock): """Read lock for Array.""" + ALLOWED_TYPES = ["LocalArrayAdapter"] + def get_path(self) -> Path: """Get path to read-lock file. @@ -197,6 +199,8 @@ def release(self, e: Optional[Exception] = None) -> None: # noqa[ARG002] class WriteArrayLock(LockWithArrayMixin["Array"], BaseLock): """Write lock for arrays.""" + ALLOWED_TYPES = ["LocalArrayAdapter"] + def get_path(self) -> Path: """Get path to the file for locking.""" path = self.dir_path / (self.array_id + self.instance.file_ext) diff --git a/tests/test_cases/test_arrays/test_varray_methods.py b/tests/test_cases/test_arrays/test_varray_methods.py index 69871c4..7fc6807 100644 --- a/tests/test_cases/test_arrays/test_varray_methods.py +++ b/tests/test_cases/test_arrays/test_varray_methods.py @@ -3,16 +3,18 @@ from datetime import datetime, timedelta, timezone from pathlib import Path +from random import shuffle from typing import Any import numpy as np import pytest -from deker_local_adapters import HDF5StorageAdapter +from deker_local_adapters import HDF5StorageAdapter, LocalArrayAdapter, LocalVArrayAdapter from deker_local_adapters.factory import AdaptersFactory from deker_tools.path import is_empty from numpy import ndarray +from deker.types import ArrayMeta from tests.parameters.array_params import attributes_validation_params from tests.parameters.index_exp_params import invalid_index_params, valid_index_exp_params from tests.parameters.uri import embedded_uri @@ -783,6 +785,40 @@ def test_step_validaor(self, varray: VArray, index_exp): with pytest.raises(IndexError): varray[index_exp] + def test_create_from_meta_ordered( + self, + varray_collection_with_attributes: Collection, + local_array_adapter: LocalArrayAdapter, + local_varray_adapter: LocalVArrayAdapter, + varray_with_attributes: VArray, + ): + meta: ArrayMeta = varray_with_attributes.as_dict + + primary_attribute_keys = list(meta["primary_attributes"].keys()) + shuffle(primary_attribute_keys) + + custom_attribute_keys = list(meta["custom_attributes"].keys()) + shuffle(custom_attribute_keys) + + primary_attributes, custom_attributes = {}, {} + for key in primary_attribute_keys: + primary_attributes[key] = meta["primary_attributes"][key] + + for key in custom_attribute_keys: + custom_attributes[key] = meta["custom_attributes"][key] + + meta["primary_attributes"] = primary_attributes + meta["custom_attributes"] = custom_attributes + + array = VArray._create_from_meta( + varray_collection_with_attributes, + meta=meta, + array_adapter=local_array_adapter, + varray_adapter=local_varray_adapter, + ) + assert array.primary_attributes == varray_with_attributes.primary_attributes + assert array.custom_attributes == varray_with_attributes.custom_attributes + if __name__ == "__main__": pytest.main() From 3f15040bf326f66eb6a6972b99e9146f1bcd6fcf Mon Sep 17 00:00:00 2001 From: matveyvarg Date: Tue, 16 Apr 2024 09:23:40 +0200 Subject: [PATCH 10/19] linters fix --- deker/locks.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/deker/locks.py b/deker/locks.py index da34768..2dc0fb3 100644 --- a/deker/locks.py +++ b/deker/locks.py @@ -56,7 +56,7 @@ def _get_lock_filename(id_: str, lock_ext: LocksExtensions) -> str: - """Get filename for lockfile + """Get filename for lockfile. :param id_: ID of array :param lock_ext: Extension of lock @@ -70,6 +70,7 @@ def _check_write_locks(dir_path: Path, id_: str) -> bool: """Checks write locks from VArrays that differs from current. :param dir_path: Dir where locks are stored (the one with hdf file) + :param id_: Id of array """ for file in dir_path.iterdir(): # Skip lock from current process. @@ -113,12 +114,15 @@ def dir_path(self) -> Path: return get_main_path(self.array_id, self.instance.collection_path / self.instance.data_dir) -def wait_for_unlock(check_func: Callable, check_func_args: tuple, timeout, interval: float) -> bool: +def wait_for_unlock( + check_func: Callable, check_func_args: tuple, timeout: int, interval: float +) -> bool: """Waiting while there is no locks - :param instance: - :param check_func: - :param check_func_args: + :param check_func: Func that check if lock has been releases + :param check_func_args: Args for func + :param timeout: For how long we should wait lock release + :param interval: How often we check locks :return: """ start_time = time.monotonic() @@ -138,10 +142,7 @@ def get_path(self) -> Path: """Get path to read-lock file. It's only the case for arrays, varrays don't have read locks. - :param func_args: arguments of method call - :param func_kwargs: keyword arguments of method call """ - # Get file directory filename = _get_lock_filename(self.array_id, LocksExtensions.array_read_lock) From 3c1fcc18c6bd3f24a1c7c93fab44eda4deafcedd Mon Sep 17 00:00:00 2001 From: matveyvarg Date: Tue, 16 Apr 2024 09:35:58 +0200 Subject: [PATCH 11/19] linters fix --- deker/ABC/base_array.py | 3 ++- deker/locks.py | 29 +++++++---------------------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/deker/ABC/base_array.py b/deker/ABC/base_array.py index d6ebeef..e73366c 100644 --- a/deker/ABC/base_array.py +++ b/deker/ABC/base_array.py @@ -462,7 +462,8 @@ def _create_from_meta( try: # To ensure the order of attributes - primary_attributes, custom_attributes = OrderedDict(), OrderedDict() + primary_attributes: OrderedDict = OrderedDict() + custom_attributes: OrderedDict = OrderedDict() # Iterate over every attribute in schema: for attr_schema in attrs_schema: diff --git a/deker/locks.py b/deker/locks.py index 2dc0fb3..1aeca16 100644 --- a/deker/locks.py +++ b/deker/locks.py @@ -28,13 +28,13 @@ Any, Callable, Dict, + Generic, List, Optional, Sequence, - Union, Tuple, TypeVar, - Generic, + Union, ) from uuid import uuid4 @@ -99,7 +99,7 @@ def array_id(self) -> str: path = self.array id_ = path.name.split(".")[0] else: - id_ = self.array.id + id_ = self.array.id # type: ignore[attr-defined] return id_ @property @@ -117,7 +117,7 @@ def dir_path(self) -> Path: def wait_for_unlock( check_func: Callable, check_func_args: tuple, timeout: int, interval: float ) -> bool: - """Waiting while there is no locks + """Waiting while there is no locks. :param check_func: Func that check if lock has been releases :param check_func_args: Args for func @@ -286,11 +286,7 @@ def check_type(self) -> None: self.skip_lock = True def get_path(self) -> Optional[Path]: # noqa[ARG002] - """Path of json Varray file. - - :param func_args: arguments of the function that has been called. - :param func_kwargs: keyword arguments of the function that has been called. - """ + """Path of json Varray file.""" array = self.instance._VSubset__array adapter = self.instance._VSubset__adapter path = get_main_path( @@ -303,7 +299,6 @@ def check_locks_for_array_and_set_flock(self, filename: Path) -> Flock: """Check if there is no read lock. :param filename: Path to file that should be flocked - :return: """ # Check read lock first array_id = filename.name.split(".")[0] @@ -326,7 +321,6 @@ def check_arrays_locks( ) -> List[Path]: """Check all Arrays that are in current VArray. - :param arrays_positions: Arrays' positions in VArray :param adapter: Array Adapter instance :param varray: VArray """ @@ -503,12 +497,7 @@ class UpdateMetaAttributeLock(LockWithArrayMixin[Union["Array", "VArray"]], Base ALLOWED_TYPES = ["LocalArrayAdapter", "LocalVArrayAdapter"] def get_path(self) -> Path: - """Return path to the file that should be locked. - - :param func_args: arguments for called method - :param func_kwargs: keyword arguments for called method - :return: - """ + """Return path to the file that should be locked.""" # Get directory where file locates dir_path = get_main_path( self.array.id, self.instance.collection_path / self.instance.data_dir @@ -524,11 +513,7 @@ class CollectionLock(BaseLock): ALLOWED_TYPES = ["LocalCollectionAdapter"] def get_path(self) -> Path: # noqa[ARG002] - """Return path to collection lock file. - - :param func_args: arguments for called method - :param func_kwargs: keyword arguments for called method - """ + """Return path to collection lock file.""" collection = self.args[1] path = self.instance.collections_resource / (collection.name + ".lock") self.logger.debug(f"Got path for collection {collection.name} lock file: {path}") From afd511d542095078f29d5c6edcc38151db78d745 Mon Sep 17 00:00:00 2001 From: matveyvarg Date: Tue, 16 Apr 2024 11:42:37 +0200 Subject: [PATCH 12/19] todos removed & dict returned to ordered dict --- deker/ABC/base_array.py | 4 ++-- deker/ABC/base_locks.py | 1 - deker/errors.py | 3 --- deker/tools/path.py | 1 - deker/types/private/enums.py | 2 -- 5 files changed, 2 insertions(+), 9 deletions(-) diff --git a/deker/ABC/base_array.py b/deker/ABC/base_array.py index e73366c..d395cdd 100644 --- a/deker/ABC/base_array.py +++ b/deker/ABC/base_array.py @@ -462,8 +462,8 @@ def _create_from_meta( try: # To ensure the order of attributes - primary_attributes: OrderedDict = OrderedDict() - custom_attributes: OrderedDict = OrderedDict() + primary_attributes: dict = dict() + custom_attributes: dict = dict() # Iterate over every attribute in schema: for attr_schema in attrs_schema: diff --git a/deker/ABC/base_locks.py b/deker/ABC/base_locks.py index cc2ea60..2d90782 100644 --- a/deker/ABC/base_locks.py +++ b/deker/ABC/base_locks.py @@ -15,7 +15,6 @@ # along with this program. If not, see . """Abstract interfaces for locks.""" -# TODO: MOVED from abc import ABC, abstractmethod from functools import wraps from pathlib import Path diff --git a/deker/errors.py b/deker/errors.py index 8991fd5..2b169aa 100644 --- a/deker/errors.py +++ b/deker/errors.py @@ -16,7 +16,6 @@ from typing import List -# TODO: MOVED class DekerBaseApplicationError(Exception): """Base attribute exception.""" @@ -85,7 +84,6 @@ class DekerIntegrityError(DekerBaseApplicationError): pass -# TODO: MOVED class DekerLockError(DekerBaseApplicationError): """If a Collection or a Array or VArray instance is locked.""" @@ -144,6 +142,5 @@ def __str__(self) -> str: return f"{self.message}; exceptions:\n\n{joined} " -# TODO: MOVED class DekerMemoryError(DekerBaseApplicationError, MemoryError): """Early memory overflow exception.""" diff --git a/deker/tools/path.py b/deker/tools/path.py index 8deac53..58ad76d 100644 --- a/deker/tools/path.py +++ b/deker/tools/path.py @@ -54,7 +54,6 @@ def get_symlink_path( return symlink_path -# TODO: MOVED def get_main_path(array_id: str, data_directory: Path) -> Path: """Generate main path for the given array id by its type. diff --git a/deker/types/private/enums.py b/deker/types/private/enums.py index 14d3acb..882f6ed 100644 --- a/deker/types/private/enums.py +++ b/deker/types/private/enums.py @@ -88,7 +88,6 @@ class DimensionType(str, Enum): time = "time" -# TODO: MOVED class LocksExtensions(str, Enum): """Extensions for lock files.""" @@ -98,7 +97,6 @@ class LocksExtensions(str, Enum): varray_lock = ".varraylock" -# TODO: MOVED class LocksTypes(str, Enum): """Locks enum.""" From a996ad53f0017292ad0bfedf9a54f8fe1486f891 Mon Sep 17 00:00:00 2001 From: matveyvarg Date: Wed, 17 Apr 2024 09:36:32 +0200 Subject: [PATCH 13/19] todos removed & dict returned to ordered dict --- deker/ABC/base_array.py | 33 ++++++----------------- deker/errors.py | 20 ++++++++------ deker/tools/attributes.py | 43 +++++++++++++++++++++++++++++- tests/parameters/schemas_params.py | 2 +- 4 files changed, 63 insertions(+), 35 deletions(-) diff --git a/deker/ABC/base_array.py b/deker/ABC/base_array.py index d395cdd..dce3819 100644 --- a/deker/ABC/base_array.py +++ b/deker/ABC/base_array.py @@ -19,7 +19,6 @@ import json from abc import ABC, abstractmethod -from collections import OrderedDict from copy import deepcopy from datetime import datetime, timedelta from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Type, Union @@ -35,7 +34,7 @@ from deker.schemas import ArraySchema, VArraySchema from deker.subset import Subset, VSubset from deker.tools.array import check_memory, get_id -from deker.tools.attributes import deserialize_attribute_value, serialize_attribute_value +from deker.tools.attributes import make_ordered_dict, serialize_attribute_value from deker.tools.schema import create_dimensions from deker.types.private.classes import ArrayMeta, Serializer from deker.types.private.typings import FancySlice, Numeric, Slice @@ -296,10 +295,9 @@ def __init__( self.schema, primary_attributes, custom_attributes ) - self.primary_attributes: OrderedDict = ( - OrderedDict({**primary_attributes}) if primary_attributes else OrderedDict() + self.primary_attributes, self.custom_attributes = make_ordered_dict( + primary_attributes, custom_attributes, self.schema.attributes # type: ignore[arg-type] ) - self.custom_attributes: dict = custom_attributes if custom_attributes else {} def __del__(self) -> None: del self.__adapter @@ -462,26 +460,11 @@ def _create_from_meta( try: # To ensure the order of attributes - primary_attributes: dict = dict() - custom_attributes: dict = dict() - - # Iterate over every attribute in schema: - for attr_schema in attrs_schema: - if attr_schema.primary: - attributes_from_meta = meta["primary_attributes"] - result_attributes = primary_attributes - else: - attributes_from_meta = meta["custom_attributes"] - result_attributes = custom_attributes - - value = attributes_from_meta[attr_schema.name] - if value is None and not attr_schema.primary: - result_attributes[attr_schema.name] = value - continue - - result_attributes[attr_schema.name] = deserialize_attribute_value( - value, attr_schema.dtype, False - ) + primary_attributes, custom_attributes = make_ordered_dict( + meta["primary_attributes"], + meta["custom_attributes"], + attrs_schema, # type: ignore[arg-type] + ) arr_params = { "collection": collection, diff --git a/deker/errors.py b/deker/errors.py index 2b169aa..596dcc1 100644 --- a/deker/errors.py +++ b/deker/errors.py @@ -107,15 +107,10 @@ class DekerSubsetError(DekerArrayError): """If something goes wrong while Subset managing.""" -class DekerVSubsetError(DekerSubsetError): - """If something goes wrong while VSubset managing. +class DekerExceptionGroup(DekerBaseApplicationError): + """If one or more threads finished with any exception. - Regarding that VSubset's inner Subsets' processing - is made in an Executor, this exception actually is - an `exceptions messages group`. - - If one or more threads finished with any exception, - name, message and reasonable tracebacks of all + Name, message and reasonable tracebacks of all of these exceptions shall be collected in a list and passed to this class along with some message. @@ -144,3 +139,12 @@ def __str__(self) -> str: class DekerMemoryError(DekerBaseApplicationError, MemoryError): """Early memory overflow exception.""" + + +class DekerVSubsetError(DekerSubsetError, DekerExceptionGroup): + """If something goes wrong while VSubset managing. + + Regarding that VSubset's inner Subsets' processing + is made in an Executor, this exception actually is + an `exceptions messages group`. + """ diff --git a/deker/tools/attributes.py b/deker/tools/attributes.py index 91f2d68..8689d38 100644 --- a/deker/tools/attributes.py +++ b/deker/tools/attributes.py @@ -15,14 +15,19 @@ # along with this program. If not, see . import re +from collections import OrderedDict from datetime import datetime -from typing import Any, Tuple, Type, Union +from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Type, Union import numpy as np from deker_tools.time import get_utc +if TYPE_CHECKING: + from deker import AttributeSchema + + def serialize_attribute_value( val: Any, ) -> Union[Tuple[str, int, float, tuple], str, int, float, tuple]: @@ -112,3 +117,39 @@ def deserialize_attribute_nested_tuples(value: Tuple[Any, ...]) -> Tuple[Any, .. value = deserialize_attribute_value(el, type(el), True) deserialized.append(value) return tuple(deserialized) + + +def make_ordered_dict( + primary_attributes: Optional[dict], + custom_attributes: Optional[dict], + attrs_schema: Union[List["AttributeSchema"], Tuple[AttributeSchema, ...]], +) -> Tuple[OrderedDict, OrderedDict]: + """Ensure that attributes in dict are located in correct order (Based on schema). + + :param primary_attributes: Primary attributes dict + :param custom_attributes: Custom attributes dict + :param attrs_schema: Schema of attributes to get order + """ + # To ensure the order of attributes + ordered_primary_attributes: OrderedDict = OrderedDict() + ordered_custom_attributes: OrderedDict = OrderedDict() + + # Iterate over every attribute in schema: + for attr_schema in attrs_schema: + if attr_schema.primary: + attributes_from_meta = primary_attributes + result_attributes = ordered_primary_attributes + else: + attributes_from_meta = custom_attributes + result_attributes = ordered_custom_attributes + + value = attributes_from_meta[attr_schema.name] + if value is None and not attr_schema.primary: + result_attributes[attr_schema.name] = value + continue + + result_attributes[attr_schema.name] = deserialize_attribute_value( + value, attr_schema.dtype, False + ) + + return ordered_primary_attributes, ordered_custom_attributes diff --git a/tests/parameters/schemas_params.py b/tests/parameters/schemas_params.py index 039c6d2..1263a13 100644 --- a/tests/parameters/schemas_params.py +++ b/tests/parameters/schemas_params.py @@ -569,7 +569,7 @@ def WRONG_params_dataclass_raises(cls) -> List[Any]: *cls._generate_types( base_dict={"dtype": dtype, "dimensions": dimensions, "vgrid": vgrid}, key="attributes", - exception_types=[tuple, list], + exception_types=[tuple, list, None], ), # wrong vgrid *cls._generate_types( From 2af076174e81cb1b733611c1f05954f963daa685 Mon Sep 17 00:00:00 2001 From: matveyvarg Date: Wed, 17 Apr 2024 09:55:54 +0200 Subject: [PATCH 14/19] skip "Error" suffix --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 869a727..de50866 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -229,6 +229,7 @@ ignore = [ "I202", "N807", "N813", + "N818", "W503", "W504", ] From 98b22722e37b598606fa00de734851adaa2c1205 Mon Sep 17 00:00:00 2001 From: matveyvarg Date: Wed, 17 Apr 2024 10:08:07 +0200 Subject: [PATCH 15/19] tests typing fix --- deker/tools/attributes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deker/tools/attributes.py b/deker/tools/attributes.py index 8689d38..54f6957 100644 --- a/deker/tools/attributes.py +++ b/deker/tools/attributes.py @@ -122,7 +122,7 @@ def deserialize_attribute_nested_tuples(value: Tuple[Any, ...]) -> Tuple[Any, .. def make_ordered_dict( primary_attributes: Optional[dict], custom_attributes: Optional[dict], - attrs_schema: Union[List["AttributeSchema"], Tuple[AttributeSchema, ...]], + attrs_schema: Union[List["AttributeSchema"], Tuple["AttributeSchema", ...]], ) -> Tuple[OrderedDict, OrderedDict]: """Ensure that attributes in dict are located in correct order (Based on schema). From e7b0da4e5b55d2b4c92614c65e8a1ff3af4afa53 Mon Sep 17 00:00:00 2001 From: Sergey Rybakov Date: Tue, 7 May 2024 18:32:39 +0300 Subject: [PATCH 16/19] CreateArrayLock removed --- deker/locks.py | 74 ------ deker/managers.py | 4 +- deker/subset.py | 23 +- poetry.lock | 233 +++++++++--------- pyproject.toml | 2 +- .../test_arrays/test_array_methods.py | 3 +- .../test_arrays/test_varray_methods.py | 2 +- .../test_concurrency/test_in_processes.py | 145 ----------- .../test_schemas/test_attributes_schema.py | 1 - 9 files changed, 135 insertions(+), 352 deletions(-) diff --git a/deker/locks.py b/deker/locks.py index 1aeca16..c17e3b2 100644 --- a/deker/locks.py +++ b/deker/locks.py @@ -417,80 +417,6 @@ def _inner_method_logic( return super()._inner_method_logic(lock, args, kwargs, func) -class CreateArrayLock(LockWithArrayMixin[Union["Array", "VArray", dict]], BaseLock): - """Lock that we set when we want to create an array.""" - - ALLOWED_TYPES = ["LocalArrayAdapter", "LocalVArrayAdapter"] - - path: Optional[Path] = None - - def get_path(self) -> Path: - """Return path to the file that should be locked.""" - # Get file directory path - dir_path = self.instance.collection_path - - # Create lock file - path = dir_path / _get_lock_filename(self.array.id, LocksExtensions.array_lock) - if not path.exists(): - path.open("w").close() - - self.path = path - self.logger.debug(f"got path for array.id {self.array.id} lock file: {path}") - return path - - def check_existing_lock(self, func_args: Sequence, func_kwargs: Dict) -> None: - """Check if there is currently lock for array creating. - - :param func_args: arguments for called method - :param func_kwargs: keyword arguments for called method - """ - from deker.arrays import Array, VArray - - array = self.array - # Check current read locks - if isinstance(array, dict): - adapter = array["adapter"].__class__.__name__ - if adapter not in self.ALLOWED_TYPES: - raise DekerLockError(f"Adapter {adapter} is not allowed to create locks for arrays") - - # TODO: figure out a way to avoid constructing Array object here - array_type = Array if adapter == self.ALLOWED_TYPES[0] else VArray - array = array_type(**array) - - dir_path = self.instance.collection_path - - # Pattern that has to find any create locks - glob_pattern = f"{array.id}:*{LocksExtensions.array_lock.value}" - for _ in dir_path.rglob(glob_pattern): - raise DekerLockError(f"Array {array.id} is locked for creating") - - func_kwargs["array"] = array - - def get_result(self, func: Callable, args: Any, kwargs: Any) -> Any: - """Call func, and get its result. - - :param func: decorated function - :param args: arguments of decorated function - :param kwargs: keyword arguments of decorated function - """ - if kw_array := kwargs.pop("array"): - args = list(args) - # First elem is self, so for functions with array, set array as next arg. - args[1] = kw_array - result = func(*tuple(args), **kwargs) - else: - result = func(*args, **kwargs) - return result - - def release(self, e: Optional[Exception] = None) -> None: - """Release Flock. - - :param e: exception that might have been raised - """ - self.path.unlink(missing_ok=True) - super().release(e) - - class UpdateMetaAttributeLock(LockWithArrayMixin[Union["Array", "VArray"]], BaseLock): """Lock for updating meta.""" diff --git a/deker/managers.py b/deker/managers.py index 32c8cf4..15559e2 100644 --- a/deker/managers.py +++ b/deker/managers.py @@ -132,9 +132,11 @@ def _create( # type: ignore "array_adapter": self.__array_adapter, # type: ignore[dict-item] } ) + array = VArray(**arr_params) # type: ignore[arg-type] else: arr_params.update({"adapter": self.__array_adapter}) # type: ignore[dict-item] - array = self._adapter.create(arr_params) + array = Array(**arr_params) # type: ignore[arg-type] + self._adapter.create(array) return array def create( diff --git a/deker/subset.py b/deker/subset.py index 850ff72..1bd2ba4 100644 --- a/deker/subset.py +++ b/deker/subset.py @@ -578,6 +578,7 @@ def update(self, data: Data) -> None: :param data: new data which shall match subset slicing """ + from deker.arrays import Array def _update(array_data: ArrayPositionedData) -> None: """If there is a need in the future to calculate Array's time dimension start value. # noqa: DAR101, D400 @@ -600,17 +601,17 @@ def _update(array_data: ArrayPositionedData) -> None: pos = array_data.vposition[n] custom_attributes[attr_name] = dim.start_value + dim.step * pos # type: ignore[operator] - array = self.__array_adapter.create( - { - "collection": self.__collection, - "adapter": self.__array_adapter, - "primary_attributes": { - "vid": self.__array.id, - "v_position": array_data.vposition, - }, - "custom_attributes": custom_attributes, - } - ) + kwargs = { + "collection": self.__collection, + "adapter": self.__array_adapter, + "primary_attributes": { + "vid": self.__array.id, + "v_position": array_data.vposition, + }, + "custom_attributes": custom_attributes, + } + array = Array(**kwargs) # type: ignore[arg-type] + self.__array_adapter.create(array) subset = array[array_data.bounds] subset.update(array_data.data) diff --git a/poetry.lock b/poetry.lock index 2ae9f50..a0ad07e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -115,13 +115,13 @@ Sphinx = ">=2.2,<8.0" [[package]] name = "babel" -version = "2.14.0" +version = "2.15.0" description = "Internationalization utilities" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, - {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, + {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, + {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, ] [package.extras] @@ -499,12 +499,12 @@ optimize = ["orjson"] [[package]] name = "deker-local-adapters" -version = "1.1.0" +version = "1.1.1b0" description = "Plugin with local adapters for deker" optional = false python-versions = ">=3.9,<4.0" files = [ - {file = "deker_local_adapters-1.1.0.tar.gz", hash = "sha256:ddbc9197f687a6f2121471fb2a60bd951b37e331bce76471b1726cbe6c4e39b3"}, + {file = "deker_local_adapters-1.1.1b0.tar.gz", hash = "sha256:415e3d74410c0f8f04cea6ee72b289b261bcbf801565f7c79b68ef901c8de24e"}, ] [package.dependencies] @@ -513,14 +513,19 @@ h5py = ">=3.8.0,<4.0.0" hdf5plugin = ">=4.0.1,<5.0.0" numpy = ">=1.18,<2.0" +[package.source] +type = "legacy" +url = "https://test.pypi.org/simple" +reference = "testpypi" + [[package]] name = "deker-server-adapters" -version = "1.0.4" +version = "1.0.5" description = "Plugin with server adapters for Deker" optional = true -python-versions = ">=3.9,<4.0" +python-versions = "<4.0,>=3.9" files = [ - {file = "deker_server_adapters-1.0.4.tar.gz", hash = "sha256:08c067f4fff39896eede75b2c883d23250444b957d55d649946973e0748bf95a"}, + {file = "deker_server_adapters-1.0.5.tar.gz", hash = "sha256:4e3982a6236599e6c55c70cf42b105133c7c7b0eeab8c6cadb91774a141efad2"}, ] [package.dependencies] @@ -647,13 +652,13 @@ sphinx = ["sphinx (>=3.2.0)", "sphinx-jinja2-compat (>=0.1.1)", "sphinx-toolbox [[package]] name = "exceptiongroup" -version = "1.2.0" +version = "1.2.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, ] [package.extras] @@ -661,13 +666,13 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.13.3" +version = "3.14.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.3-py3-none-any.whl", hash = "sha256:5ffa845303983e7a0b7ae17636509bc97997d58afeafa72fb141a17b152284cb"}, - {file = "filelock-3.13.3.tar.gz", hash = "sha256:a79895a25bbefdf55d1a2a0a80968f7dbb28edcd6d4234a0afb3f37ecde4b546"}, + {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, + {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, ] [package.extras] @@ -841,36 +846,32 @@ hyperframe = ">=6.0,<7" [[package]] name = "h5py" -version = "3.10.0" +version = "3.11.0" description = "Read and write HDF5 files from Python" optional = false python-versions = ">=3.8" files = [ - {file = "h5py-3.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b963fb772964fc1d1563c57e4e2e874022ce11f75ddc6df1a626f42bd49ab99f"}, - {file = "h5py-3.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:012ab448590e3c4f5a8dd0f3533255bc57f80629bf7c5054cf4c87b30085063c"}, - {file = "h5py-3.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:781a24263c1270a62cd67be59f293e62b76acfcc207afa6384961762bb88ea03"}, - {file = "h5py-3.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f42e6c30698b520f0295d70157c4e202a9e402406f50dc08f5a7bc416b24e52d"}, - {file = "h5py-3.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:93dd840bd675787fc0b016f7a05fc6efe37312a08849d9dd4053fd0377b1357f"}, - {file = "h5py-3.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2381e98af081b6df7f6db300cd88f88e740649d77736e4b53db522d8874bf2dc"}, - {file = "h5py-3.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:667fe23ab33d5a8a6b77970b229e14ae3bb84e4ea3382cc08567a02e1499eedd"}, - {file = "h5py-3.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90286b79abd085e4e65e07c1bd7ee65a0f15818ea107f44b175d2dfe1a4674b7"}, - {file = "h5py-3.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c013d2e79c00f28ffd0cc24e68665ea03ae9069e167087b2adb5727d2736a52"}, - {file = "h5py-3.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:92273ce69ae4983dadb898fd4d3bea5eb90820df953b401282ee69ad648df684"}, - {file = "h5py-3.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c97d03f87f215e7759a354460fb4b0d0f27001450b18b23e556e7856a0b21c3"}, - {file = "h5py-3.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86df4c2de68257b8539a18646ceccdcf2c1ce6b1768ada16c8dcfb489eafae20"}, - {file = "h5py-3.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba9ab36be991119a3ff32d0c7cbe5faf9b8d2375b5278b2aea64effbeba66039"}, - {file = "h5py-3.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c8e4fda19eb769e9a678592e67eaec3a2f069f7570c82d2da909c077aa94339"}, - {file = "h5py-3.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:492305a074327e8d2513011fa9fffeb54ecb28a04ca4c4227d7e1e9616d35641"}, - {file = "h5py-3.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9450464b458cca2c86252b624279115dcaa7260a40d3cb1594bf2b410a2bd1a3"}, - {file = "h5py-3.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd6f6d1384a9f491732cee233b99cd4bfd6e838a8815cc86722f9d2ee64032af"}, - {file = "h5py-3.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3074ec45d3dc6e178c6f96834cf8108bf4a60ccb5ab044e16909580352010a97"}, - {file = "h5py-3.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:212bb997a91e6a895ce5e2f365ba764debeaef5d2dca5c6fb7098d66607adf99"}, - {file = "h5py-3.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5dfc65ac21fa2f630323c92453cadbe8d4f504726ec42f6a56cf80c2f90d6c52"}, - {file = "h5py-3.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d4682b94fd36ab217352be438abd44c8f357c5449b8995e63886b431d260f3d3"}, - {file = "h5py-3.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aece0e2e1ed2aab076c41802e50a0c3e5ef8816d60ece39107d68717d4559824"}, - {file = "h5py-3.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43a61b2c2ad65b1fabc28802d133eed34debcc2c8b420cb213d3d4ef4d3e2229"}, - {file = "h5py-3.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:ae2f0201c950059676455daf92700eeb57dcf5caaf71b9e1328e6e6593601770"}, - {file = "h5py-3.10.0.tar.gz", hash = "sha256:d93adc48ceeb33347eb24a634fb787efc7ae4644e6ea4ba733d099605045c049"}, + {file = "h5py-3.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1625fd24ad6cfc9c1ccd44a66dac2396e7ee74940776792772819fc69f3a3731"}, + {file = "h5py-3.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c072655ad1d5fe9ef462445d3e77a8166cbfa5e599045f8aa3c19b75315f10e5"}, + {file = "h5py-3.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77b19a40788e3e362b54af4dcf9e6fde59ca016db2c61360aa30b47c7b7cef00"}, + {file = "h5py-3.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:ef4e2f338fc763f50a8113890f455e1a70acd42a4d083370ceb80c463d803972"}, + {file = "h5py-3.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbd732a08187a9e2a6ecf9e8af713f1d68256ee0f7c8b652a32795670fb481ba"}, + {file = "h5py-3.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75bd7b3d93fbeee40860fd70cdc88df4464e06b70a5ad9ce1446f5f32eb84007"}, + {file = "h5py-3.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c416f8eb0daae39dabe71415cb531f95dce2d81e1f61a74537a50c63b28ab3"}, + {file = "h5py-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:083e0329ae534a264940d6513f47f5ada617da536d8dccbafc3026aefc33c90e"}, + {file = "h5py-3.11.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a76cae64080210389a571c7d13c94a1a6cf8cb75153044fd1f822a962c97aeab"}, + {file = "h5py-3.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3736fe21da2b7d8a13fe8fe415f1272d2a1ccdeff4849c1421d2fb30fd533bc"}, + {file = "h5py-3.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa6ae84a14103e8dc19266ef4c3e5d7c00b68f21d07f2966f0ca7bdb6c2761fb"}, + {file = "h5py-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:21dbdc5343f53b2e25404673c4f00a3335aef25521bd5fa8c707ec3833934892"}, + {file = "h5py-3.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:754c0c2e373d13d6309f408325343b642eb0f40f1a6ad21779cfa9502209e150"}, + {file = "h5py-3.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:731839240c59ba219d4cb3bc5880d438248533366f102402cfa0621b71796b62"}, + {file = "h5py-3.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ec9df3dd2018904c4cc06331951e274f3f3fd091e6d6cc350aaa90fa9b42a76"}, + {file = "h5py-3.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:55106b04e2c83dfb73dc8732e9abad69d83a436b5b82b773481d95d17b9685e1"}, + {file = "h5py-3.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f4e025e852754ca833401777c25888acb96889ee2c27e7e629a19aee288833f0"}, + {file = "h5py-3.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c4b760082626120031d7902cd983d8c1f424cdba2809f1067511ef283629d4b"}, + {file = "h5py-3.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67462d0669f8f5459529de179f7771bd697389fcb3faab54d63bf788599a48ea"}, + {file = "h5py-3.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:d9c944d364688f827dc889cf83f1fca311caf4fa50b19f009d1f2b525edd33a3"}, + {file = "h5py-3.11.0.tar.gz", hash = "sha256:7b7e8f78072a2edec87c9836f25f34203fd492a4475709a18b417a33cfb21fa9"}, ] [package.dependencies] @@ -988,13 +989,13 @@ files = [ [[package]] name = "identify" -version = "2.5.35" +version = "2.5.36" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, - {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, + {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, + {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, ] [package.extras] @@ -1002,13 +1003,13 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.6" +version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] @@ -1090,13 +1091,13 @@ testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] [[package]] name = "jinja2" -version = "3.1.3" +version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] @@ -1516,47 +1517,47 @@ files = [ [[package]] name = "pandas" -version = "2.2.1" +version = "2.2.2" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" files = [ - {file = "pandas-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8df8612be9cd1c7797c93e1c5df861b2ddda0b48b08f2c3eaa0702cf88fb5f88"}, - {file = "pandas-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0f573ab277252ed9aaf38240f3b54cfc90fff8e5cab70411ee1d03f5d51f3944"}, - {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f02a3a6c83df4026e55b63c1f06476c9aa3ed6af3d89b4f04ea656ccdaaaa359"}, - {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c38ce92cb22a4bea4e3929429aa1067a454dcc9c335799af93ba9be21b6beb51"}, - {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c2ce852e1cf2509a69e98358e8458775f89599566ac3775e70419b98615f4b06"}, - {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53680dc9b2519cbf609c62db3ed7c0b499077c7fefda564e330286e619ff0dd9"}, - {file = "pandas-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:94e714a1cca63e4f5939cdce5f29ba8d415d85166be3441165edd427dc9f6bc0"}, - {file = "pandas-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f821213d48f4ab353d20ebc24e4faf94ba40d76680642fb7ce2ea31a3ad94f9b"}, - {file = "pandas-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70e00c2d894cb230e5c15e4b1e1e6b2b478e09cf27cc593a11ef955b9ecc81a"}, - {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e97fbb5387c69209f134893abc788a6486dbf2f9e511070ca05eed4b930b1b02"}, - {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101d0eb9c5361aa0146f500773395a03839a5e6ecde4d4b6ced88b7e5a1a6403"}, - {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7d2ed41c319c9fb4fd454fe25372028dfa417aacb9790f68171b2e3f06eae8cd"}, - {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:af5d3c00557d657c8773ef9ee702c61dd13b9d7426794c9dfeb1dc4a0bf0ebc7"}, - {file = "pandas-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:06cf591dbaefb6da9de8472535b185cba556d0ce2e6ed28e21d919704fef1a9e"}, - {file = "pandas-2.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:88ecb5c01bb9ca927ebc4098136038519aa5d66b44671861ffab754cae75102c"}, - {file = "pandas-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:04f6ec3baec203c13e3f8b139fb0f9f86cd8c0b94603ae3ae8ce9a422e9f5bee"}, - {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a935a90a76c44fe170d01e90a3594beef9e9a6220021acfb26053d01426f7dc2"}, - {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c391f594aae2fd9f679d419e9a4d5ba4bce5bb13f6a989195656e7dc4b95c8f0"}, - {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9d1265545f579edf3f8f0cb6f89f234f5e44ba725a34d86535b1a1d38decbccc"}, - {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11940e9e3056576ac3244baef2fedade891977bcc1cb7e5cc8f8cc7d603edc89"}, - {file = "pandas-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:4acf681325ee1c7f950d058b05a820441075b0dd9a2adf5c4835b9bc056bf4fb"}, - {file = "pandas-2.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9bd8a40f47080825af4317d0340c656744f2bfdb6819f818e6ba3cd24c0e1397"}, - {file = "pandas-2.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df0c37ebd19e11d089ceba66eba59a168242fc6b7155cba4ffffa6eccdfb8f16"}, - {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:739cc70eaf17d57608639e74d63387b0d8594ce02f69e7a0b046f117974b3019"}, - {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d3558d263073ed95e46f4650becff0c5e1ffe0fc3a015de3c79283dfbdb3df"}, - {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4aa1d8707812a658debf03824016bf5ea0d516afdea29b7dc14cf687bc4d4ec6"}, - {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:76f27a809cda87e07f192f001d11adc2b930e93a2b0c4a236fde5429527423be"}, - {file = "pandas-2.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:1ba21b1d5c0e43416218db63037dbe1a01fc101dc6e6024bcad08123e48004ab"}, - {file = "pandas-2.2.1.tar.gz", hash = "sha256:0ab90f87093c13f3e8fa45b48ba9f39181046e8f3317d3aadb2fffbb1b978572"}, + {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, + {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, + {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, + {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, + {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, + {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, + {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, ] [package.dependencies] numpy = [ - {version = ">=1.22.4,<2", markers = "python_version < \"3.11\""}, - {version = ">=1.23.2,<2", markers = "python_version == \"3.11\""}, - {version = ">=1.26.0,<2", markers = "python_version >= \"3.12\""}, + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -1589,18 +1590,18 @@ xml = ["lxml (>=4.9.2)"] [[package]] name = "parso" -version = "0.8.3" +version = "0.8.4" description = "A Python Parser" optional = true python-versions = ">=3.6" files = [ - {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, - {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, + {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, + {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, ] [package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["docopt", "pytest (<6.0.0)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] [[package]] name = "pathspec" @@ -1640,28 +1641,29 @@ flake8 = ">=5.0.0" [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, + {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -1808,17 +1810,16 @@ files = [ [[package]] name = "pygments" -version = "2.17.2" +version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, ] [package.extras] -plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] @@ -2114,18 +2115,18 @@ files = [ [[package]] name = "setuptools" -version = "69.2.0" +version = "69.5.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, - {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, + {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, + {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -2526,13 +2527,13 @@ files = [ [[package]] name = "tqdm" -version = "4.66.2" +version = "4.66.4" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"}, - {file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"}, + {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, + {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, ] [package.dependencies] @@ -2566,13 +2567,13 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. [[package]] name = "typing-extensions" -version = "4.10.0" +version = "4.11.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] [[package]] @@ -2620,13 +2621,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.25.1" +version = "20.26.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, - {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, + {file = "virtualenv-20.26.1-py3-none-any.whl", hash = "sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75"}, + {file = "virtualenv-20.26.1.tar.gz", hash = "sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b"}, ] [package.dependencies] @@ -2635,7 +2636,7 @@ filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<5" [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] @@ -2707,4 +2708,4 @@ xarray = ["xarray"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "d5be6ceef0a2525bbc779fe5809b7f395ee33a4628abb5e43e8564fba7b7c651" +content-hash = "3a5894b98d081188ff6c25d1b44a957bdda286119a0ce25e9c0818238d1b178a" diff --git a/pyproject.toml b/pyproject.toml index de50866..f2dbc4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,7 +61,7 @@ attrs = "^23.1.0" typing-extensions = "^4.4.0" tqdm = "^4.64.1" psutil = "^5.9.5" -deker-local-adapters = "^1.0.1" +deker-local-adapters = { version = "^1.1.1b", source = "testpypi" } deker-server-adapters = {version = "^1.0.0", optional = true} deker-shell = { version = "^1.0.0", optional = true } xarray = {version = "^2023.5.0", optional = true} diff --git a/tests/test_cases/test_arrays/test_array_methods.py b/tests/test_cases/test_arrays/test_array_methods.py index 080b8d2..b4370d2 100644 --- a/tests/test_cases/test_arrays/test_array_methods.py +++ b/tests/test_cases/test_arrays/test_array_methods.py @@ -1,6 +1,5 @@ import os import string -from copy import deepcopy from datetime import datetime, timedelta, timezone from pathlib import Path @@ -14,7 +13,6 @@ from deker_local_adapters.factory import AdaptersFactory from numpy import ndarray -from deker.types import ArrayMeta from tests.parameters.array_params import attributes_validation_params from tests.parameters.index_exp_params import invalid_index_params, valid_index_exp_params from tests.parameters.uri import embedded_uri @@ -26,6 +24,7 @@ from deker.errors import DekerCollectionAlreadyExistsError, DekerMemoryError, DekerValidationError from deker.schemas import ArraySchema, AttributeSchema, DimensionSchema from deker.tools import get_paths +from deker.types import ArrayMeta from deker.types.private.typings import FancySlice, NumericDtypes, Slice diff --git a/tests/test_cases/test_arrays/test_varray_methods.py b/tests/test_cases/test_arrays/test_varray_methods.py index 7fc6807..4c3d7e3 100644 --- a/tests/test_cases/test_arrays/test_varray_methods.py +++ b/tests/test_cases/test_arrays/test_varray_methods.py @@ -14,7 +14,6 @@ from deker_tools.path import is_empty from numpy import ndarray -from deker.types import ArrayMeta from tests.parameters.array_params import attributes_validation_params from tests.parameters.index_exp_params import invalid_index_params, valid_index_exp_params from tests.parameters.uri import embedded_uri @@ -26,6 +25,7 @@ from deker.errors import DekerMemoryError, DekerValidationError from deker.schemas import DimensionSchema, VArraySchema from deker.tools import get_paths +from deker.types import ArrayMeta from deker.types.private.typings import FancySlice, Slice diff --git a/tests/test_cases/test_concurrency/test_in_processes.py b/tests/test_cases/test_concurrency/test_in_processes.py index d83379a..50032a8 100644 --- a/tests/test_cases/test_concurrency/test_in_processes.py +++ b/tests/test_cases/test_concurrency/test_in_processes.py @@ -14,7 +14,6 @@ from typing import Callable, Dict, Literal from unittest.mock import patch -import h5py import numpy as np import pytest @@ -28,7 +27,6 @@ from deker.errors import DekerLockError from deker.locks import ( CollectionLock, - CreateArrayLock, Flock, ReadArrayLock, UpdateMetaAttributeLock, @@ -36,7 +34,6 @@ WriteVarrayLock, ) from deker.schemas import ArraySchema, DimensionSchema, VArraySchema -from deker.tools import get_paths from deker.types import LocksExtensions, Numeric @@ -100,21 +97,6 @@ def call_array_method( subset_slice = slice_converter[subset_slice] # Get Array object - if method == "create": - with patch.object( - CreateArrayLock, - "release", - wait_unlock(CreateArrayLock.release, lock_set, funcs_finished, wait), - ): - with patch("deker.ABC.base_array.get_id", lambda *a: id_): - try: - array = collection.create(primary_attributes, custom_attributes) - except DekerLockError: - return DekerLockError - except Exception: - traceback.print_exc() - return None - return try: array = collection.filter({"id": id_}).last() except DekerLockError: @@ -551,60 +533,6 @@ def test_varray_update_meta_lock( finally: proc.kill() - def test_varray_with_attributes_create_lock( - self, - client: Client, - array_schema_with_attributes: ArraySchema, - root_path, - varray_with_attributes: VArray, - ): - """Test create lock.""" - manager = Manager() - lock_set = manager.Event() - func_finished = manager.Event() - proc = Process( - target=call_array_method, - args=( - varray_with_attributes.collection, - str(embedded_uri(root_path)), - varray_with_attributes.id, - "create", - lock_set, - func_finished, - True, - True, - varray_with_attributes.primary_attributes, - varray_with_attributes.custom_attributes, - ), - ) - proc.start() - lock_set.wait() - try: - methods = ["create"] * 3 - with Pool(WORKERS) as pool: - result = pool.starmap( - call_array_method, - [ - ( - varray_with_attributes.collection, - str(embedded_uri(root_path)), - varray_with_attributes.id, - method, - lock_set, - func_finished, - False, - True, - varray_with_attributes.primary_attributes, - varray_with_attributes.custom_attributes, - ) - for method in methods - ], - ) - assert result.count(DekerLockError) == len(methods) - func_finished.set() - finally: - proc.kill() - def test_collection_create(self, client: Client, root_path): """Test if collection is locked on creation.""" manager = Manager() @@ -640,78 +568,5 @@ def test_collection_create(self, client: Client, root_path): proc.kill() -class TestMethods: - """Test if methods work correctly with multiple processes.""" - - def test_array_with_attributes_create_multiple_processes( - self, - client: Client, - array_schema_with_attributes: ArraySchema, - root_path, - array_with_attributes: Array, - array_data: np.ndarray, - storage_adapter, - ctx, - ): - manager = Manager() - lock_set = manager.Event() - func_finished = manager.Event() - proc = Process( - target=call_array_method, - args=( - array_with_attributes.collection, - str(embedded_uri(root_path)), - array_with_attributes.id, - "create", - lock_set, - func_finished, - True, - False, - array_with_attributes.primary_attributes, - array_with_attributes.custom_attributes, - ), - ) - proc.start() - lock_set.wait() - - methods = ["create"] * 3 - with Pool(WORKERS - 1) as pool: - result = pool.starmap( - call_array_method, - [ - ( - array_with_attributes.collection, - str(embedded_uri(root_path)), - array_with_attributes.id, - method, - lock_set, - func_finished, - False, - False, - array_with_attributes.primary_attributes, - array_with_attributes.custom_attributes, - ) - for method in methods - ], - ) - lock_set.wait() - assert result.count(DekerLockError) == len(methods) - func_finished.set() - - paths = get_paths( - array_with_attributes, - root_path / ctx.config.collections_directory / array_with_attributes.collection, - ) - filename = str(array_with_attributes.id) + storage_adapter.file_ext - file_path = paths.main / filename - symlink = paths.symlink / filename - assert file_path.exists() - assert symlink.exists() - array_with_attributes[:].update(array_data) - - with h5py.File(file_path) as f: - assert np.allclose(f["data"][:], np.asarray(array_data), equal_nan=True) - - if __name__ == "__main__": pytest.main() diff --git a/tests/test_cases/test_schemas/test_attributes_schema.py b/tests/test_cases/test_schemas/test_attributes_schema.py index 1a4e981..247ea53 100644 --- a/tests/test_cases/test_schemas/test_attributes_schema.py +++ b/tests/test_cases/test_schemas/test_attributes_schema.py @@ -4,7 +4,6 @@ from datetime import datetime from typing import FrozenSet -import numpy as np import pytest from deker_local_adapters import LocalArrayAdapter From 8255e275c7bfd272a757d3f8c1c2f4fc01e77943 Mon Sep 17 00:00:00 2001 From: Sergey Rybakov Date: Tue, 7 May 2024 18:35:18 +0300 Subject: [PATCH 17/19] CreateArrayLock removed --- deker/managers.py | 6 +++--- deker/subset.py | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/deker/managers.py b/deker/managers.py index 15559e2..855401c 100644 --- a/deker/managers.py +++ b/deker/managers.py @@ -132,11 +132,11 @@ def _create( # type: ignore "array_adapter": self.__array_adapter, # type: ignore[dict-item] } ) - array = VArray(**arr_params) # type: ignore[arg-type] + obj = VArray else: arr_params.update({"adapter": self.__array_adapter}) # type: ignore[dict-item] - array = Array(**arr_params) # type: ignore[arg-type] - self._adapter.create(array) + obj = Array + array = self._adapter.create(obj(**arr_params)) # type: ignore[arg-type] return array def create( diff --git a/deker/subset.py b/deker/subset.py index 1bd2ba4..de7b1aa 100644 --- a/deker/subset.py +++ b/deker/subset.py @@ -610,8 +610,7 @@ def _update(array_data: ArrayPositionedData) -> None: }, "custom_attributes": custom_attributes, } - array = Array(**kwargs) # type: ignore[arg-type] - self.__array_adapter.create(array) + array = self.__array_adapter.create(Array(**kwargs)) # type: ignore[arg-type] subset = array[array_data.bounds] subset.update(array_data.data) From 3104a746fb5ec3a9fde6a519c2818e40de6d7341 Mon Sep 17 00:00:00 2001 From: Sergey Rybakov Date: Wed, 8 May 2024 11:04:22 +0300 Subject: [PATCH 18/19] no return from adapters' create methods --- deker/managers.py | 6 +++--- deker/subset.py | 3 ++- poetry.lock | 6 +++--- pyproject.toml | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/deker/managers.py b/deker/managers.py index 855401c..15559e2 100644 --- a/deker/managers.py +++ b/deker/managers.py @@ -132,11 +132,11 @@ def _create( # type: ignore "array_adapter": self.__array_adapter, # type: ignore[dict-item] } ) - obj = VArray + array = VArray(**arr_params) # type: ignore[arg-type] else: arr_params.update({"adapter": self.__array_adapter}) # type: ignore[dict-item] - obj = Array - array = self._adapter.create(obj(**arr_params)) # type: ignore[arg-type] + array = Array(**arr_params) # type: ignore[arg-type] + self._adapter.create(array) return array def create( diff --git a/deker/subset.py b/deker/subset.py index de7b1aa..1bd2ba4 100644 --- a/deker/subset.py +++ b/deker/subset.py @@ -610,7 +610,8 @@ def _update(array_data: ArrayPositionedData) -> None: }, "custom_attributes": custom_attributes, } - array = self.__array_adapter.create(Array(**kwargs)) # type: ignore[arg-type] + array = Array(**kwargs) # type: ignore[arg-type] + self.__array_adapter.create(array) subset = array[array_data.bounds] subset.update(array_data.data) diff --git a/poetry.lock b/poetry.lock index a0ad07e..4ee79d8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -499,12 +499,12 @@ optimize = ["orjson"] [[package]] name = "deker-local-adapters" -version = "1.1.1b0" +version = "1.1.1b1" description = "Plugin with local adapters for deker" optional = false python-versions = ">=3.9,<4.0" files = [ - {file = "deker_local_adapters-1.1.1b0.tar.gz", hash = "sha256:415e3d74410c0f8f04cea6ee72b289b261bcbf801565f7c79b68ef901c8de24e"}, + {file = "deker_local_adapters-1.1.1b1.tar.gz", hash = "sha256:532c0f732fb0f47b4a14ecc7dbbb2ec32761fb00e322262e39fcdddd525fc86d"}, ] [package.dependencies] @@ -2708,4 +2708,4 @@ xarray = ["xarray"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "3a5894b98d081188ff6c25d1b44a957bdda286119a0ce25e9c0818238d1b178a" +content-hash = "f68067cb2c72456d22a10c64da6512d49d0c29e11569cd22a6b97b158d9ab09f" diff --git a/pyproject.toml b/pyproject.toml index f2dbc4c..246b7c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,7 +61,7 @@ attrs = "^23.1.0" typing-extensions = "^4.4.0" tqdm = "^4.64.1" psutil = "^5.9.5" -deker-local-adapters = { version = "^1.1.1b", source = "testpypi" } +deker-local-adapters = { version = "^1.1.1b-1", source = "testpypi" } deker-server-adapters = {version = "^1.0.0", optional = true} deker-shell = { version = "^1.0.0", optional = true } xarray = {version = "^2023.5.0", optional = true} From 7874452ee56f9d17dcc735516a1237a48423063b Mon Sep 17 00:00:00 2001 From: Sergey Rybakov Date: Wed, 8 May 2024 11:34:16 +0300 Subject: [PATCH 19/19] actions/download-artifact upgraded to v4; poetry locked --- .github/workflows/on_release.yml | 2 +- .github/workflows/on_test_release.yml | 2 +- poetry.lock | 13 ++++--------- pyproject.toml | 2 +- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.github/workflows/on_release.yml b/.github/workflows/on_release.yml index 5efecad..e440ae0 100644 --- a/.github/workflows/on_release.yml +++ b/.github/workflows/on_release.yml @@ -38,7 +38,7 @@ jobs: url: https://pypi.org/p/${{ vars.PACKAGE_NAME }} steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: # unpacks default artifact into dist/ # if `name: artifact` is omitted, the action will create extra parent dir diff --git a/.github/workflows/on_test_release.yml b/.github/workflows/on_test_release.yml index 7472e6c..446324c 100644 --- a/.github/workflows/on_test_release.yml +++ b/.github/workflows/on_test_release.yml @@ -21,7 +21,7 @@ jobs: url: https://test.pypi.org/p/${{ vars.PACKAGE_NAME }} steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: # unpacks default artifact into dist/ # if `name: artifact` is omitted, the action will create extra parent dir diff --git a/poetry.lock b/poetry.lock index 4ee79d8..47dc925 100644 --- a/poetry.lock +++ b/poetry.lock @@ -499,12 +499,12 @@ optimize = ["orjson"] [[package]] name = "deker-local-adapters" -version = "1.1.1b1" +version = "1.1.1" description = "Plugin with local adapters for deker" optional = false -python-versions = ">=3.9,<4.0" +python-versions = "<4.0,>=3.9" files = [ - {file = "deker_local_adapters-1.1.1b1.tar.gz", hash = "sha256:532c0f732fb0f47b4a14ecc7dbbb2ec32761fb00e322262e39fcdddd525fc86d"}, + {file = "deker_local_adapters-1.1.1.tar.gz", hash = "sha256:c3d5ec08bf479e5c7852a263f127e6896b38507ec34f7337eb6bc41305cb1143"}, ] [package.dependencies] @@ -513,11 +513,6 @@ h5py = ">=3.8.0,<4.0.0" hdf5plugin = ">=4.0.1,<5.0.0" numpy = ">=1.18,<2.0" -[package.source] -type = "legacy" -url = "https://test.pypi.org/simple" -reference = "testpypi" - [[package]] name = "deker-server-adapters" version = "1.0.5" @@ -2708,4 +2703,4 @@ xarray = ["xarray"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "f68067cb2c72456d22a10c64da6512d49d0c29e11569cd22a6b97b158d9ab09f" +content-hash = "a94291f635fab7ce16a9164c854bd3150468072cc9c9d400da90b204bc05595d" diff --git a/pyproject.toml b/pyproject.toml index 246b7c8..761d030 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,7 +61,7 @@ attrs = "^23.1.0" typing-extensions = "^4.4.0" tqdm = "^4.64.1" psutil = "^5.9.5" -deker-local-adapters = { version = "^1.1.1b-1", source = "testpypi" } +deker-local-adapters = "^1.1.1" deker-server-adapters = {version = "^1.0.0", optional = true} deker-shell = { version = "^1.0.0", optional = true } xarray = {version = "^2023.5.0", optional = true}