diff --git a/ixmp4/core/optimization/data.py b/ixmp4/core/optimization/data.py index 1bf5a295..3d085e78 100644 --- a/ixmp4/core/optimization/data.py +++ b/ixmp4/core/optimization/data.py @@ -2,6 +2,7 @@ from ..base import BaseFacade from .indexset import IndexSetRepository +from .parameter import ParameterRepository from .scalar import ScalarRepository from .table import TableRepository @@ -11,11 +12,13 @@ class OptimizationData(BaseFacade): IndexSet, Table, Variable, etc.""" indexsets: IndexSetRepository + parameters: ParameterRepository scalars: ScalarRepository tables: TableRepository def __init__(self, *args, run: Run, **kwargs) -> None: super().__init__(*args, **kwargs) self.indexsets = IndexSetRepository(_backend=self.backend, _run=run) + self.parameters = ParameterRepository(_backend=self.backend, _run=run) self.scalars = ScalarRepository(_backend=self.backend, _run=run) self.tables = TableRepository(_backend=self.backend, _run=run) diff --git a/ixmp4/core/optimization/parameter.py b/ixmp4/core/optimization/parameter.py index abd4c0cf..6612be41 100644 --- a/ixmp4/core/optimization/parameter.py +++ b/ixmp4/core/optimization/parameter.py @@ -34,12 +34,20 @@ def data(self) -> dict[str, Any]: def add(self, data: dict[str, Any] | pd.DataFrame) -> None: """Adds data to an existing Parameter.""" self.backend.optimization.parameters.add_data( - table_id=self._model.id, data=data + parameter_id=self._model.id, data=data ) self._model.data = self.backend.optimization.parameters.get( run_id=self._model.run__id, name=self._model.name ).data + @property + def values(self) -> list: + return self._model.data.get("values", []) + + @property + def units(self) -> list: + return self._model.data.get("units", []) + @property def constrained_to_indexsets(self) -> list[str]: return [column.indexset.name for column in self._model.columns] diff --git a/ixmp4/data/abstract/optimization/column.py b/ixmp4/data/abstract/optimization/column.py index 856fc116..029d2361 100644 --- a/ixmp4/data/abstract/optimization/column.py +++ b/ixmp4/data/abstract/optimization/column.py @@ -14,8 +14,10 @@ class Column(base.BaseModel, Protocol): """Unique name of the Column.""" dtype: types.String """Type of the Column's data.""" - table__id: types.Integer + table__id: types.Mapped[int | None] """Foreign unique integer id of a Table.""" + parameter__id: types.Mapped[int | None] + """Foreign unique integer id of a Parameter.""" indexset: types.Mapped[IndexSet] """Associated IndexSet.""" constrained_to_indexset: types.Integer diff --git a/ixmp4/data/abstract/optimization/parameter.py b/ixmp4/data/abstract/optimization/parameter.py index 86e8eda3..01e1cacc 100644 --- a/ixmp4/data/abstract/optimization/parameter.py +++ b/ixmp4/data/abstract/optimization/parameter.py @@ -170,7 +170,7 @@ def tabulate(self, *, name: str | None = None, **kwargs) -> pd.DataFrame: # TODO Once present, state how to check which IndexSets are linked and which values # they permit - def add_data(self, table_id: int, data: dict[str, Any] | pd.DataFrame) -> None: + def add_data(self, parameter_id: int, data: dict[str, Any] | pd.DataFrame) -> None: r"""Adds data to a Parameter. The data will be validated with the linked constrained @@ -183,7 +183,7 @@ def add_data(self, table_id: int, data: dict[str, Any] | pd.DataFrame) -> None: Parameters ---------- - table_id : int + parameter_id : int The id of the :class:`ixmp4.data.abstract.optimization.Parameter`. data : dict[str, Any] | pandas.DataFrame The data to be added. diff --git a/ixmp4/data/api/optimization/column.py b/ixmp4/data/api/optimization/column.py index 229bb5cf..582c2314 100644 --- a/ixmp4/data/api/optimization/column.py +++ b/ixmp4/data/api/optimization/column.py @@ -14,7 +14,8 @@ class Column(base.BaseModel): id: int name: str dtype: str - table__id: int + table__id: int | None + parameter__id: int | None indexset: IndexSet constrained_to_indexset: int unique: bool diff --git a/ixmp4/data/api/optimization/parameter.py b/ixmp4/data/api/optimization/parameter.py index 0c272e78..32b21cc1 100644 --- a/ixmp4/data/api/optimization/parameter.py +++ b/ixmp4/data/api/optimization/parameter.py @@ -57,13 +57,13 @@ def create( column_names=column_names, ) - def add_data(self, table_id: int, data: dict[str, Any] | pd.DataFrame) -> None: + def add_data(self, parameter_id: int, data: dict[str, Any] | pd.DataFrame) -> None: if isinstance(data, pd.DataFrame): # data will always contains str, not only Hashable data: dict[str, Any] = data.to_dict(orient="list") # type: ignore kwargs = {"data": data} self._request( - method="PATCH", path=self.prefix + str(table_id) + "/data/", json=kwargs + method="PATCH", path=self.prefix + str(parameter_id) + "/data/", json=kwargs ) def get(self, run_id: int, name: str) -> Parameter: diff --git a/ixmp4/data/db/iamc/base.py b/ixmp4/data/db/iamc/base.py index af057f86..1d8b80b9 100644 --- a/ixmp4/data/db/iamc/base.py +++ b/ixmp4/data/db/iamc/base.py @@ -7,7 +7,6 @@ Deleter, Enumerator, Lister, - NameMixin, Retriever, Selecter, Tabulator, diff --git a/ixmp4/data/db/optimization/base.py b/ixmp4/data/db/optimization/base.py index 2ef34ec6..8dfe5ac1 100644 --- a/ixmp4/data/db/optimization/base.py +++ b/ixmp4/data/db/optimization/base.py @@ -8,18 +8,14 @@ Deleter, Enumerator, Lister, - OptimizationDataMixin, - OptimizationNameMixin, Retriever, - RunIDMixin, Selecter, Tabulator, TimestampMixin, - UniqueNameRunIDMixin, ) -class BaseModel(RootBaseModel, OptimizationNameMixin, TimestampMixin): +class BaseModel(RootBaseModel, TimestampMixin): __abstract__ = True table_prefix = "optimization_" diff --git a/ixmp4/data/db/optimization/column/model.py b/ixmp4/data/db/optimization/column/model.py index 04002b5e..d1551013 100644 --- a/ixmp4/data/db/optimization/column/model.py +++ b/ixmp4/data/db/optimization/column/model.py @@ -1,7 +1,5 @@ from typing import ClassVar -from sqlalchemy import UniqueConstraint - from ixmp4 import db from ixmp4.data import types from ixmp4.data.abstract import optimization as abstract @@ -34,4 +32,4 @@ class Column(base.BaseModel): # Currently not in use: unique: types.Boolean = db.Column(db.Boolean, default=True) - __table_args__ = (UniqueConstraint("name", "table__id", "parameter__id"),) + __table_args__ = (db.UniqueConstraint("name", "table__id"),) diff --git a/ixmp4/data/db/optimization/indexset/model.py b/ixmp4/data/db/optimization/indexset/model.py index 9e0f5a19..e772f6aa 100644 --- a/ixmp4/data/db/optimization/indexset/model.py +++ b/ixmp4/data/db/optimization/indexset/model.py @@ -9,7 +9,7 @@ from .. import base -class IndexSet(base.BaseModel, base.UniqueNameRunIDMixin): +class IndexSet(base.BaseModel): NotFound: ClassVar = abstract.IndexSet.NotFound NotUnique: ClassVar = abstract.IndexSet.NotUnique DeletionPrevented: ClassVar = abstract.IndexSet.DeletionPrevented diff --git a/ixmp4/data/db/optimization/parameter/model.py b/ixmp4/data/db/optimization/parameter/model.py index 6bdcf67a..3199675d 100644 --- a/ixmp4/data/db/optimization/parameter/model.py +++ b/ixmp4/data/db/optimization/parameter/model.py @@ -1,16 +1,16 @@ +import copy from typing import Any, ClassVar -import pandas as pd from sqlalchemy.orm import validates from ixmp4 import db from ixmp4.data import types from ixmp4.data.abstract import optimization as abstract -from .. import Column, base +from .. import Column, base, utils -class Parameter(base.BaseModel, base.OptimizationDataMixin, base.UniqueNameRunIDMixin): +class Parameter(base.BaseModel): # NOTE: These might be mixin-able, but would require some abstraction NotFound: ClassVar = abstract.Parameter.NotFound NotUnique: ClassVar = abstract.Parameter.NotUnique @@ -18,13 +18,20 @@ class Parameter(base.BaseModel, base.OptimizationDataMixin, base.UniqueNameRunID # constrained_to_indexsets: ClassVar[list[str] | None] = None - # TODO Same as in table/model.py - columns: types.Mapped[list["Column"]] = db.relationship() # type: ignore + run__id: types.RunId + columns: types.Mapped[list["Column"]] = db.relationship() + data: types.JsonDict = db.Column(db.JsonType, nullable=False, default={}) @validates("data") def validate_data(self, key, data: dict[str, Any]): - data_frame: pd.DataFrame = pd.DataFrame.from_dict(data) - data_frame_to_validate = data_frame.drop(columns=["values", "units"]) - - self._validate_data(data_frame=data_frame_to_validate, data=data) - return data_frame.to_dict(orient="list") + data_to_validate = copy.deepcopy(data) + del data_to_validate["values"] + del data_to_validate["units"] + _ = utils.validate_data( + key=key, + data=data_to_validate, + columns=self.columns, + ) + return data + + __table_args__ = (db.UniqueConstraint("name", "run__id"),) diff --git a/ixmp4/data/db/optimization/parameter/repository.py b/ixmp4/data/db/optimization/parameter/repository.py index 48a3c1f1..735194fe 100644 --- a/ixmp4/data/db/optimization/parameter/repository.py +++ b/ixmp4/data/db/optimization/parameter/repository.py @@ -74,7 +74,8 @@ def add( run_id: int, name: str, ) -> Parameter: - parameter = Parameter(name=name, run__id=run_id, **self.get_creation_info()) + parameter = Parameter(name=name, run__id=run_id) + parameter.set_creation_info(auth_context=self.backend.auth_context) self.session.add(parameter) return parameter @@ -154,7 +155,7 @@ def add_data(self, parameter_id: int, data: dict[str, Any] | pd.DataFrame) -> No missing_columns = set(["values", "units"]) - set(data.columns) assert ( not missing_columns - ), f"Parameter.data must include the column(s): {' ,'.join(missing_columns)}!" + ), f"Parameter.data must include the column(s): {', '.join(missing_columns)}!" # Can use a set for now, need full column if we care about order for unit_name in set(data["units"]): diff --git a/poetry.lock b/poetry.lock index 1c615801..5332c684 100644 --- a/poetry.lock +++ b/poetry.lock @@ -508,6 +508,26 @@ files = [ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + [[package]] name = "docutils" version = "0.20.1" @@ -519,6 +539,21 @@ files = [ {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] +[[package]] +name = "email-validator" +version = "2.1.1" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, + {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + [[package]] name = "et-xmlfile" version = "1.1.0" @@ -560,22 +595,46 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "fastapi" -version = "0.110.1" +version = "0.111.0" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.110.1-py3-none-any.whl", hash = "sha256:5df913203c482f820d31f48e635e022f8cbfe7350e4830ef05a3163925b1addc"}, - {file = "fastapi-0.110.1.tar.gz", hash = "sha256:6feac43ec359dfe4f45b2c18ec8c94edb8dc2dfc461d417d9e626590c071baad"}, + {file = "fastapi-0.111.0-py3-none-any.whl", hash = "sha256:97ecbf994be0bcbdadedf88c3150252bed7b2087075ac99735403b1b76cc8fc0"}, + {file = "fastapi-0.111.0.tar.gz", hash = "sha256:b9db9dd147c91cb8b769f7183535773d8741dd46f9dc6676cd82eab510228cd7"}, ] [package.dependencies] +email_validator = ">=2.0.0" +fastapi-cli = ">=0.0.2" +httpx = ">=0.23.0" +jinja2 = ">=2.11.2" +orjson = ">=3.2.1" pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +python-multipart = ">=0.0.7" starlette = ">=0.37.2,<0.38.0" typing-extensions = ">=4.8.0" +ujson = ">=4.0.1,<4.0.2 || >4.0.2,<4.1.0 || >4.1.0,<4.2.0 || >4.2.0,<4.3.0 || >4.3.0,<5.0.0 || >5.0.0,<5.1.0 || >5.1.0" +uvicorn = {version = ">=0.12.0", extras = ["standard"]} [package.extras] -all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "fastapi-cli" +version = "0.0.2" +description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi_cli-0.0.2-py3-none-any.whl", hash = "sha256:d7a8ec89fd52ad16c52de9fe7299e4b22c7a32e1bf28aa886e5517e4927423dd"}, + {file = "fastapi_cli-0.0.2.tar.gz", hash = "sha256:589565ba758432632eadcf7b950e0ec76bb283b549784d9df17f261a8a9de476"}, +] + +[package.dependencies] +fastapi = "*" +typer = ">=0.12.3" +uvicorn = {version = ">=0.29.0", extras = ["standard"]} [[package]] name = "filelock" @@ -1368,6 +1427,61 @@ files = [ [package.dependencies] et-xmlfile = "*" +[[package]] +name = "orjson" +version = "3.10.3" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9fb6c3f9f5490a3eb4ddd46fc1b6eadb0d6fc16fb3f07320149c3286a1409dd8"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:252124b198662eee80428f1af8c63f7ff077c88723fe206a25df8dc57a57b1fa"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f3e87733823089a338ef9bbf363ef4de45e5c599a9bf50a7a9b82e86d0228da"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8334c0d87103bb9fbbe59b78129f1f40d1d1e8355bbed2ca71853af15fa4ed3"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1952c03439e4dce23482ac846e7961f9d4ec62086eb98ae76d97bd41d72644d7"}, + {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c0403ed9c706dcd2809f1600ed18f4aae50be263bd7112e54b50e2c2bc3ebd6d"}, + {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:382e52aa4270a037d41f325e7d1dfa395b7de0c367800b6f337d8157367bf3a7"}, + {file = "orjson-3.10.3-cp310-none-win32.whl", hash = "sha256:be2aab54313752c04f2cbaab4515291ef5af8c2256ce22abc007f89f42f49109"}, + {file = "orjson-3.10.3-cp310-none-win_amd64.whl", hash = "sha256:416b195f78ae461601893f482287cee1e3059ec49b4f99479aedf22a20b1098b"}, + {file = "orjson-3.10.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:73100d9abbbe730331f2242c1fc0bcb46a3ea3b4ae3348847e5a141265479700"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a12eee96e3ab828dbfcb4d5a0023aa971b27143a1d35dc214c176fdfb29b3"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520de5e2ef0b4ae546bea25129d6c7c74edb43fc6cf5213f511a927f2b28148b"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccaa0a401fc02e8828a5bedfd80f8cd389d24f65e5ca3954d72c6582495b4bcf"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7bc9e8bc11bac40f905640acd41cbeaa87209e7e1f57ade386da658092dc16"}, + {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3582b34b70543a1ed6944aca75e219e1192661a63da4d039d088a09c67543b08"}, + {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c23dfa91481de880890d17aa7b91d586a4746a4c2aa9a145bebdbaf233768d5"}, + {file = "orjson-3.10.3-cp311-none-win32.whl", hash = "sha256:1770e2a0eae728b050705206d84eda8b074b65ee835e7f85c919f5705b006c9b"}, + {file = "orjson-3.10.3-cp311-none-win_amd64.whl", hash = "sha256:93433b3c1f852660eb5abdc1f4dd0ced2be031ba30900433223b28ee0140cde5"}, + {file = "orjson-3.10.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a39aa73e53bec8d410875683bfa3a8edf61e5a1c7bb4014f65f81d36467ea098"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0943a96b3fa09bee1afdfccc2cb236c9c64715afa375b2af296c73d91c23eab2"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e852baafceff8da3c9defae29414cc8513a1586ad93e45f27b89a639c68e8176"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18566beb5acd76f3769c1d1a7ec06cdb81edc4d55d2765fb677e3eaa10fa99e0"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd2218d5a3aa43060efe649ec564ebedec8ce6ae0a43654b81376216d5ebd42"}, + {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf20465e74c6e17a104ecf01bf8cd3b7b252565b4ccee4548f18b012ff2f8069"}, + {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7f67aa7f983c4345eeda16054a4677289011a478ca947cd69c0a86ea45e534"}, + {file = "orjson-3.10.3-cp312-none-win32.whl", hash = "sha256:17e0713fc159abc261eea0f4feda611d32eabc35708b74bef6ad44f6c78d5ea0"}, + {file = "orjson-3.10.3-cp312-none-win_amd64.whl", hash = "sha256:4c895383b1ec42b017dd2c75ae8a5b862fc489006afde06f14afbdd0309b2af0"}, + {file = "orjson-3.10.3-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:be2719e5041e9fb76c8c2c06b9600fe8e8584e6980061ff88dcbc2691a16d20d"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0175a5798bdc878956099f5c54b9837cb62cfbf5d0b86ba6d77e43861bcec2"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:978be58a68ade24f1af7758626806e13cff7748a677faf95fbb298359aa1e20d"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16bda83b5c61586f6f788333d3cf3ed19015e3b9019188c56983b5a299210eb5"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad1f26bea425041e0a1adad34630c4825a9e3adec49079b1fb6ac8d36f8b754"}, + {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9e253498bee561fe85d6325ba55ff2ff08fb5e7184cd6a4d7754133bd19c9195"}, + {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a62f9968bab8a676a164263e485f30a0b748255ee2f4ae49a0224be95f4532b"}, + {file = "orjson-3.10.3-cp38-none-win32.whl", hash = "sha256:8d0b84403d287d4bfa9bf7d1dc298d5c1c5d9f444f3737929a66f2fe4fb8f134"}, + {file = "orjson-3.10.3-cp38-none-win_amd64.whl", hash = "sha256:8bc7a4df90da5d535e18157220d7915780d07198b54f4de0110eca6b6c11e290"}, + {file = "orjson-3.10.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9059d15c30e675a58fdcd6f95465c1522b8426e092de9fff20edebfdc15e1cb0"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d40c7f7938c9c2b934b297412c067936d0b54e4b8ab916fd1a9eb8f54c02294"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a654ec1de8fdaae1d80d55cee65893cb06494e124681ab335218be6a0691e7"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:831c6ef73f9aa53c5f40ae8f949ff7681b38eaddb6904aab89dca4d85099cb78"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99b880d7e34542db89f48d14ddecbd26f06838b12427d5a25d71baceb5ba119d"}, + {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e5e176c994ce4bd434d7aafb9ecc893c15f347d3d2bbd8e7ce0b63071c52e25"}, + {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b69a58a37dab856491bf2d3bbf259775fdce262b727f96aafbda359cb1d114d8"}, + {file = "orjson-3.10.3-cp39-none-win32.whl", hash = "sha256:b8d4d1a6868cde356f1402c8faeb50d62cee765a1f7ffcfd6de732ab0581e063"}, + {file = "orjson-3.10.3-cp39-none-win_amd64.whl", hash = "sha256:5102f50c5fc46d94f2033fe00d392588564378260d64377aec702f21a7a22912"}, + {file = "orjson-3.10.3.tar.gz", hash = "sha256:2b166507acae7ba2f7c315dcf185a9111ad5e992ac81f2d507aac39193c2c818"}, +] + [[package]] name = "packaging" version = "24.0" @@ -1381,47 +1495,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" @@ -1469,13 +1583,13 @@ types-pytz = ">=2022.1.1" [[package]] name = "pandera" -version = "0.18.3" +version = "0.19.0" description = "A light-weight and flexible data validation and testing tool for statistical data objects." optional = false python-versions = ">=3.7" files = [ - {file = "pandera-0.18.3-py3-none-any.whl", hash = "sha256:376faf746db57517f7fd85f234fa193e34266e7bcd1781e5c5912befbc180b98"}, - {file = "pandera-0.18.3.tar.gz", hash = "sha256:47dd2e10ed7d2a2bc9cfd154b76b8bcd065a9bf1413d00de902d31c7afeec014"}, + {file = "pandera-0.19.0-py3-none-any.whl", hash = "sha256:ca7410b69b45758097b908c4f02cf06ac8329122a99e16760b1ec797952e3251"}, + {file = "pandera-0.19.0.tar.gz", hash = "sha256:f68d95f2b78e6d465aeb6ed800fdb7b7e3bbf88978f4f0d334ded287e7bd4243"}, ] [package.dependencies] @@ -1484,21 +1598,22 @@ numpy = ">=1.19.0" packaging = ">=20.0" pandas = ">=1.2.0" pydantic = "*" -typeguard = ">=3.0.2" +typeguard = "*" typing-inspect = ">=0.6.0" wrapt = "*" [package.extras] -all = ["black", "dask", "fastapi", "frictionless (<=4.40.8)", "geopandas", "hypothesis (>=6.92.7)", "modin", "pandas-stubs", "pyspark (>=3.2.0)", "pyyaml (>=5.1)", "ray", "scipy", "shapely"] -dask = ["dask"] +all = ["black", "dask[dataframe]", "fastapi", "frictionless (<=4.40.8)", "geopandas", "hypothesis (>=6.92.7)", "modin", "pandas-stubs", "polars (>=0.20.0)", "pyspark (>=3.2.0)", "pyyaml (>=5.1)", "ray", "scipy", "shapely"] +dask = ["dask[dataframe]"] fastapi = ["fastapi"] geopandas = ["geopandas", "shapely"] hypotheses = ["scipy"] io = ["black", "frictionless (<=4.40.8)", "pyyaml (>=5.1)"] -modin = ["dask", "modin", "ray"] -modin-dask = ["dask", "modin"] +modin = ["dask[dataframe]", "modin", "ray"] +modin-dask = ["dask[dataframe]", "modin"] modin-ray = ["modin", "ray"] mypy = ["pandas-stubs"] +polars = ["polars (>=0.20.0)"] pyspark = ["pyspark (>=3.2.0)"] strategies = ["hypothesis (>=6.92.7)"] @@ -1559,13 +1674,13 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest- [[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] @@ -1847,18 +1962,18 @@ files = [ [[package]] name = "pydantic" -version = "2.7.0" +version = "2.7.1" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.0-py3-none-any.whl", hash = "sha256:9dee74a271705f14f9a1567671d144a851c675b072736f0a7b2608fd9e495352"}, - {file = "pydantic-2.7.0.tar.gz", hash = "sha256:b5ecdd42262ca2462e2624793551e80911a1e989f462910bb81aef974b4bb383"}, + {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"}, + {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.18.1" +pydantic-core = "2.18.2" typing-extensions = ">=4.6.1" [package.extras] @@ -1866,90 +1981,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.18.1" +version = "2.18.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ee9cf33e7fe14243f5ca6977658eb7d1042caaa66847daacbd2117adb258b226"}, - {file = "pydantic_core-2.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6b7bbb97d82659ac8b37450c60ff2e9f97e4eb0f8a8a3645a5568b9334b08b50"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df4249b579e75094f7e9bb4bd28231acf55e308bf686b952f43100a5a0be394c"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d0491006a6ad20507aec2be72e7831a42efc93193d2402018007ff827dc62926"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ae80f72bb7a3e397ab37b53a2b49c62cc5496412e71bc4f1277620a7ce3f52b"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58aca931bef83217fca7a390e0486ae327c4af9c3e941adb75f8772f8eeb03a1"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1be91ad664fc9245404a789d60cba1e91c26b1454ba136d2a1bf0c2ac0c0505a"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:667880321e916a8920ef49f5d50e7983792cf59f3b6079f3c9dac2b88a311d17"}, - {file = "pydantic_core-2.18.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f7054fdc556f5421f01e39cbb767d5ec5c1139ea98c3e5b350e02e62201740c7"}, - {file = "pydantic_core-2.18.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:030e4f9516f9947f38179249778709a460a3adb516bf39b5eb9066fcfe43d0e6"}, - {file = "pydantic_core-2.18.1-cp310-none-win32.whl", hash = "sha256:2e91711e36e229978d92642bfc3546333a9127ecebb3f2761372e096395fc649"}, - {file = "pydantic_core-2.18.1-cp310-none-win_amd64.whl", hash = "sha256:9a29726f91c6cb390b3c2338f0df5cd3e216ad7a938762d11c994bb37552edb0"}, - {file = "pydantic_core-2.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9ece8a49696669d483d206b4474c367852c44815fca23ac4e48b72b339807f80"}, - {file = "pydantic_core-2.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a5d83efc109ceddb99abd2c1316298ced2adb4570410defe766851a804fcd5b"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7973c381283783cd1043a8c8f61ea5ce7a3a58b0369f0ee0ee975eaf2f2a1b"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:54c7375c62190a7845091f521add19b0f026bcf6ae674bdb89f296972272e86d"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd63cec4e26e790b70544ae5cc48d11b515b09e05fdd5eff12e3195f54b8a586"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:561cf62c8a3498406495cfc49eee086ed2bb186d08bcc65812b75fda42c38294"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68717c38a68e37af87c4da20e08f3e27d7e4212e99e96c3d875fbf3f4812abfc"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d5728e93d28a3c63ee513d9ffbac9c5989de8c76e049dbcb5bfe4b923a9739d"}, - {file = "pydantic_core-2.18.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f0f17814c505f07806e22b28856c59ac80cee7dd0fbb152aed273e116378f519"}, - {file = "pydantic_core-2.18.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d816f44a51ba5175394bc6c7879ca0bd2be560b2c9e9f3411ef3a4cbe644c2e9"}, - {file = "pydantic_core-2.18.1-cp311-none-win32.whl", hash = "sha256:09f03dfc0ef8c22622eaa8608caa4a1e189cfb83ce847045eca34f690895eccb"}, - {file = "pydantic_core-2.18.1-cp311-none-win_amd64.whl", hash = "sha256:27f1009dc292f3b7ca77feb3571c537276b9aad5dd4efb471ac88a8bd09024e9"}, - {file = "pydantic_core-2.18.1-cp311-none-win_arm64.whl", hash = "sha256:48dd883db92e92519201f2b01cafa881e5f7125666141a49ffba8b9facc072b0"}, - {file = "pydantic_core-2.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b6b0e4912030c6f28bcb72b9ebe4989d6dc2eebcd2a9cdc35fefc38052dd4fe8"}, - {file = "pydantic_core-2.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3202a429fe825b699c57892d4371c74cc3456d8d71b7f35d6028c96dfecad31"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3982b0a32d0a88b3907e4b0dc36809fda477f0757c59a505d4e9b455f384b8b"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25595ac311f20e5324d1941909b0d12933f1fd2171075fcff763e90f43e92a0d"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14fe73881cf8e4cbdaded8ca0aa671635b597e42447fec7060d0868b52d074e6"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca976884ce34070799e4dfc6fbd68cb1d181db1eefe4a3a94798ddfb34b8867f"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684d840d2c9ec5de9cb397fcb3f36d5ebb6fa0d94734f9886032dd796c1ead06"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:54764c083bbe0264f0f746cefcded6cb08fbbaaf1ad1d78fb8a4c30cff999a90"}, - {file = "pydantic_core-2.18.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:201713f2f462e5c015b343e86e68bd8a530a4f76609b33d8f0ec65d2b921712a"}, - {file = "pydantic_core-2.18.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fd1a9edb9dd9d79fbeac1ea1f9a8dd527a6113b18d2e9bcc0d541d308dae639b"}, - {file = "pydantic_core-2.18.1-cp312-none-win32.whl", hash = "sha256:d5e6b7155b8197b329dc787356cfd2684c9d6a6b1a197f6bbf45f5555a98d411"}, - {file = "pydantic_core-2.18.1-cp312-none-win_amd64.whl", hash = "sha256:9376d83d686ec62e8b19c0ac3bf8d28d8a5981d0df290196fb6ef24d8a26f0d6"}, - {file = "pydantic_core-2.18.1-cp312-none-win_arm64.whl", hash = "sha256:c562b49c96906b4029b5685075fe1ebd3b5cc2601dfa0b9e16c2c09d6cbce048"}, - {file = "pydantic_core-2.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3e352f0191d99fe617371096845070dee295444979efb8f27ad941227de6ad09"}, - {file = "pydantic_core-2.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0295d52b012cbe0d3059b1dba99159c3be55e632aae1999ab74ae2bd86a33d7"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56823a92075780582d1ffd4489a2e61d56fd3ebb4b40b713d63f96dd92d28144"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd3f79e17b56741b5177bcc36307750d50ea0698df6aa82f69c7db32d968c1c2"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38a5024de321d672a132b1834a66eeb7931959c59964b777e8f32dbe9523f6b1"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ce426ee691319d4767748c8e0895cfc56593d725594e415f274059bcf3cb76"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2adaeea59849ec0939af5c5d476935f2bab4b7f0335b0110f0f069a41024278e"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9b6431559676a1079eac0f52d6d0721fb8e3c5ba43c37bc537c8c83724031feb"}, - {file = "pydantic_core-2.18.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:85233abb44bc18d16e72dc05bf13848a36f363f83757541f1a97db2f8d58cfd9"}, - {file = "pydantic_core-2.18.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:641a018af4fe48be57a2b3d7a1f0f5dbca07c1d00951d3d7463f0ac9dac66622"}, - {file = "pydantic_core-2.18.1-cp38-none-win32.whl", hash = "sha256:63d7523cd95d2fde0d28dc42968ac731b5bb1e516cc56b93a50ab293f4daeaad"}, - {file = "pydantic_core-2.18.1-cp38-none-win_amd64.whl", hash = "sha256:907a4d7720abfcb1c81619863efd47c8a85d26a257a2dbebdb87c3b847df0278"}, - {file = "pydantic_core-2.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:aad17e462f42ddbef5984d70c40bfc4146c322a2da79715932cd8976317054de"}, - {file = "pydantic_core-2.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:94b9769ba435b598b547c762184bcfc4783d0d4c7771b04a3b45775c3589ca44"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80e0e57cc704a52fb1b48f16d5b2c8818da087dbee6f98d9bf19546930dc64b5"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76b86e24039c35280ceee6dce7e62945eb93a5175d43689ba98360ab31eebc4a"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a05db5013ec0ca4a32cc6433f53faa2a014ec364031408540ba858c2172bb0"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:250ae39445cb5475e483a36b1061af1bc233de3e9ad0f4f76a71b66231b07f88"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a32204489259786a923e02990249c65b0f17235073149d0033efcebe80095570"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6395a4435fa26519fd96fdccb77e9d00ddae9dd6c742309bd0b5610609ad7fb2"}, - {file = "pydantic_core-2.18.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2533ad2883f001efa72f3d0e733fb846710c3af6dcdd544fe5bf14fa5fe2d7db"}, - {file = "pydantic_core-2.18.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b560b72ed4816aee52783c66854d96157fd8175631f01ef58e894cc57c84f0f6"}, - {file = "pydantic_core-2.18.1-cp39-none-win32.whl", hash = "sha256:582cf2cead97c9e382a7f4d3b744cf0ef1a6e815e44d3aa81af3ad98762f5a9b"}, - {file = "pydantic_core-2.18.1-cp39-none-win_amd64.whl", hash = "sha256:ca71d501629d1fa50ea7fa3b08ba884fe10cefc559f5c6c8dfe9036c16e8ae89"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e178e5b66a06ec5bf51668ec0d4ac8cfb2bdcb553b2c207d58148340efd00143"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:72722ce529a76a4637a60be18bd789d8fb871e84472490ed7ddff62d5fed620d"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fe0c1ce5b129455e43f941f7a46f61f3d3861e571f2905d55cdbb8b5c6f5e2c"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4284c621f06a72ce2cb55f74ea3150113d926a6eb78ab38340c08f770eb9b4d"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a0c3e718f4e064efde68092d9d974e39572c14e56726ecfaeebbe6544521f47"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2027493cc44c23b598cfaf200936110433d9caa84e2c6cf487a83999638a96ac"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:76909849d1a6bffa5a07742294f3fa1d357dc917cb1fe7b470afbc3a7579d539"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ee7ccc7fb7e921d767f853b47814c3048c7de536663e82fbc37f5eb0d532224b"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ee2794111c188548a4547eccc73a6a8527fe2af6cf25e1a4ebda2fd01cdd2e60"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a139fe9f298dc097349fb4f28c8b81cc7a202dbfba66af0e14be5cfca4ef7ce5"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d074b07a10c391fc5bbdcb37b2f16f20fcd9e51e10d01652ab298c0d07908ee2"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c69567ddbac186e8c0aadc1f324a60a564cfe25e43ef2ce81bcc4b8c3abffbae"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf1c7b78cddb5af00971ad5294a4583188bda1495b13760d9f03c9483bb6203"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2684a94fdfd1b146ff10689c6e4e815f6a01141781c493b97342cdc5b06f4d5d"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:73c1bc8a86a5c9e8721a088df234265317692d0b5cd9e86e975ce3bc3db62a59"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e60defc3c15defb70bb38dd605ff7e0fae5f6c9c7cbfe0ad7868582cb7e844a6"}, - {file = "pydantic_core-2.18.1.tar.gz", hash = "sha256:de9d3e8717560eb05e28739d1b35e4eac2e458553a52a301e51352a7ffc86a35"}, + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"}, + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"}, + {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"}, + {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"}, + {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"}, + {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"}, + {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"}, + {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"}, + {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"}, + {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"}, + {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"}, + {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"}, + {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"}, + {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"}, + {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"}, ] [package.dependencies] @@ -2022,13 +2137,13 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [[package]] name = "pytest" -version = "8.1.1" +version = "8.2.0" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, + {file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"}, + {file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"}, ] [package.dependencies] @@ -2036,11 +2151,11 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.4,<2.0" +pluggy = ">=1.5,<2.0" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-benchmark" @@ -2108,6 +2223,20 @@ files = [ [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "python-multipart" +version = "0.0.9" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"}, + {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, +] + +[package.extras] +dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"] + [[package]] name = "pytz" version = "2024.1" @@ -2471,28 +2600,28 @@ files = [ [[package]] name = "ruff" -version = "0.3.5" +version = "0.4.3" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.3.5-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:aef5bd3b89e657007e1be6b16553c8813b221ff6d92c7526b7e0227450981eac"}, - {file = "ruff-0.3.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:89b1e92b3bd9fca249153a97d23f29bed3992cff414b222fcd361d763fc53f12"}, - {file = "ruff-0.3.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e55771559c89272c3ebab23326dc23e7f813e492052391fe7950c1a5a139d89"}, - {file = "ruff-0.3.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dabc62195bf54b8a7876add6e789caae0268f34582333cda340497c886111c39"}, - {file = "ruff-0.3.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a05f3793ba25f194f395578579c546ca5d83e0195f992edc32e5907d142bfa3"}, - {file = "ruff-0.3.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dfd3504e881082959b4160ab02f7a205f0fadc0a9619cc481982b6837b2fd4c0"}, - {file = "ruff-0.3.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87258e0d4b04046cf1d6cc1c56fadbf7a880cc3de1f7294938e923234cf9e498"}, - {file = "ruff-0.3.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:712e71283fc7d9f95047ed5f793bc019b0b0a29849b14664a60fd66c23b96da1"}, - {file = "ruff-0.3.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a532a90b4a18d3f722c124c513ffb5e5eaff0cc4f6d3aa4bda38e691b8600c9f"}, - {file = "ruff-0.3.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:122de171a147c76ada00f76df533b54676f6e321e61bd8656ae54be326c10296"}, - {file = "ruff-0.3.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d80a6b18a6c3b6ed25b71b05eba183f37d9bc8b16ace9e3d700997f00b74660b"}, - {file = "ruff-0.3.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a7b6e63194c68bca8e71f81de30cfa6f58ff70393cf45aab4c20f158227d5936"}, - {file = "ruff-0.3.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a759d33a20c72f2dfa54dae6e85e1225b8e302e8ac655773aff22e542a300985"}, - {file = "ruff-0.3.5-py3-none-win32.whl", hash = "sha256:9d8605aa990045517c911726d21293ef4baa64f87265896e491a05461cae078d"}, - {file = "ruff-0.3.5-py3-none-win_amd64.whl", hash = "sha256:dc56bb16a63c1303bd47563c60482a1512721053d93231cf7e9e1c6954395a0e"}, - {file = "ruff-0.3.5-py3-none-win_arm64.whl", hash = "sha256:faeeae9905446b975dcf6d4499dc93439b131f1443ee264055c5716dd947af55"}, - {file = "ruff-0.3.5.tar.gz", hash = "sha256:a067daaeb1dc2baf9b82a32dae67d154d95212080c80435eb052d95da647763d"}, + {file = "ruff-0.4.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b70800c290f14ae6fcbb41bbe201cf62dfca024d124a1f373e76371a007454ce"}, + {file = "ruff-0.4.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:08a0d6a22918ab2552ace96adeaca308833873a4d7d1d587bb1d37bae8728eb3"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba1f14df3c758dd7de5b55fbae7e1c8af238597961e5fb628f3de446c3c40c5"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:819fb06d535cc76dfddbfe8d3068ff602ddeb40e3eacbc90e0d1272bb8d97113"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bfc9e955e6dc6359eb6f82ea150c4f4e82b660e5b58d9a20a0e42ec3bb6342b"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:510a67d232d2ebe983fddea324dbf9d69b71c4d2dfeb8a862f4a127536dd4cfb"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9ff11cd9a092ee7680a56d21f302bdda14327772cd870d806610a3503d001f"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29efff25bf9ee685c2c8390563a5b5c006a3fee5230d28ea39f4f75f9d0b6f2f"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b00e0bcccf0fc8d7186ed21e311dffd19761cb632241a6e4fe4477cc80ef6e"}, + {file = "ruff-0.4.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:262f5635e2c74d80b7507fbc2fac28fe0d4fef26373bbc62039526f7722bca1b"}, + {file = "ruff-0.4.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7363691198719c26459e08cc17c6a3dac6f592e9ea3d2fa772f4e561b5fe82a3"}, + {file = "ruff-0.4.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eeb039f8428fcb6725bb63cbae92ad67b0559e68b5d80f840f11914afd8ddf7f"}, + {file = "ruff-0.4.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:927b11c1e4d0727ce1a729eace61cee88a334623ec424c0b1c8fe3e5f9d3c865"}, + {file = "ruff-0.4.3-py3-none-win32.whl", hash = "sha256:25cacda2155778beb0d064e0ec5a3944dcca9c12715f7c4634fd9d93ac33fd30"}, + {file = "ruff-0.4.3-py3-none-win_amd64.whl", hash = "sha256:7a1c3a450bc6539ef00da6c819fb1b76b6b065dec585f91456e7c0d6a0bbc725"}, + {file = "ruff-0.4.3-py3-none-win_arm64.whl", hash = "sha256:71ca5f8ccf1121b95a59649482470c5601c60a416bf189d553955b0338e34614"}, + {file = "ruff-0.4.3.tar.gz", hash = "sha256:ff0a3ef2e3c4b6d133fbedcf9586abfbe38d076041f2dc18ffb2c7e0485d5a07"}, ] [[package]] @@ -3115,6 +3244,80 @@ files = [ {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] +[[package]] +name = "ujson" +version = "5.9.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ujson-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab71bf27b002eaf7d047c54a68e60230fbd5cd9da60de7ca0aa87d0bccead8fa"}, + {file = "ujson-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a365eac66f5aa7a7fdf57e5066ada6226700884fc7dce2ba5483538bc16c8c5"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e015122b337858dba5a3dc3533af2a8fc0410ee9e2374092f6a5b88b182e9fcc"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:779a2a88c53039bebfbccca934430dabb5c62cc179e09a9c27a322023f363e0d"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10ca3c41e80509fd9805f7c149068fa8dbee18872bbdc03d7cca928926a358d5"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a566e465cb2fcfdf040c2447b7dd9718799d0d90134b37a20dff1e27c0e9096"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f833c529e922577226a05bc25b6a8b3eb6c4fb155b72dd88d33de99d53113124"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b68a0caab33f359b4cbbc10065c88e3758c9f73a11a65a91f024b2e7a1257106"}, + {file = "ujson-5.9.0-cp310-cp310-win32.whl", hash = "sha256:7cc7e605d2aa6ae6b7321c3ae250d2e050f06082e71ab1a4200b4ae64d25863c"}, + {file = "ujson-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6d3f10eb8ccba4316a6b5465b705ed70a06011c6f82418b59278fbc919bef6f"}, + {file = "ujson-5.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b23bbb46334ce51ddb5dded60c662fbf7bb74a37b8f87221c5b0fec1ec6454b"}, + {file = "ujson-5.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6974b3a7c17bbf829e6c3bfdc5823c67922e44ff169851a755eab79a3dd31ec0"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5964ea916edfe24af1f4cc68488448fbb1ec27a3ddcddc2b236da575c12c8ae"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba7cac47dd65ff88571eceeff48bf30ed5eb9c67b34b88cb22869b7aa19600d"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bbd91a151a8f3358c29355a491e915eb203f607267a25e6ab10531b3b157c5e"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:829a69d451a49c0de14a9fecb2a2d544a9b2c884c2b542adb243b683a6f15908"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a807ae73c46ad5db161a7e883eec0fbe1bebc6a54890152ccc63072c4884823b"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8fc2aa18b13d97b3c8ccecdf1a3c405f411a6e96adeee94233058c44ff92617d"}, + {file = "ujson-5.9.0-cp311-cp311-win32.whl", hash = "sha256:70e06849dfeb2548be48fdd3ceb53300640bc8100c379d6e19d78045e9c26120"}, + {file = "ujson-5.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:7309d063cd392811acc49b5016728a5e1b46ab9907d321ebbe1c2156bc3c0b99"}, + {file = "ujson-5.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:20509a8c9f775b3a511e308bbe0b72897ba6b800767a7c90c5cca59d20d7c42c"}, + {file = "ujson-5.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b28407cfe315bd1b34f1ebe65d3bd735d6b36d409b334100be8cdffae2177b2f"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d302bd17989b6bd90d49bade66943c78f9e3670407dbc53ebcf61271cadc399"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f21315f51e0db8ee245e33a649dd2d9dce0594522de6f278d62f15f998e050e"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5635b78b636a54a86fdbf6f027e461aa6c6b948363bdf8d4fbb56a42b7388320"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82b5a56609f1235d72835ee109163c7041b30920d70fe7dac9176c64df87c164"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5ca35f484622fd208f55041b042d9d94f3b2c9c5add4e9af5ee9946d2d30db01"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:829b824953ebad76d46e4ae709e940bb229e8999e40881338b3cc94c771b876c"}, + {file = "ujson-5.9.0-cp312-cp312-win32.whl", hash = "sha256:25fa46e4ff0a2deecbcf7100af3a5d70090b461906f2299506485ff31d9ec437"}, + {file = "ujson-5.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:60718f1720a61560618eff3b56fd517d107518d3c0160ca7a5a66ac949c6cf1c"}, + {file = "ujson-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d581db9db9e41d8ea0b2705c90518ba623cbdc74f8d644d7eb0d107be0d85d9c"}, + {file = "ujson-5.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ff741a5b4be2d08fceaab681c9d4bc89abf3c9db600ab435e20b9b6d4dfef12e"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdcb02cabcb1e44381221840a7af04433c1dc3297af76fde924a50c3054c708c"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e208d3bf02c6963e6ef7324dadf1d73239fb7008491fdf523208f60be6437402"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4b3917296630a075e04d3d07601ce2a176479c23af838b6cf90a2d6b39b0d95"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0c4d6adb2c7bb9eb7c71ad6f6f612e13b264942e841f8cc3314a21a289a76c4e"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0b159efece9ab5c01f70b9d10bbb77241ce111a45bc8d21a44c219a2aec8ddfd"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0cb4a7814940ddd6619bdce6be637a4b37a8c4760de9373bac54bb7b229698b"}, + {file = "ujson-5.9.0-cp38-cp38-win32.whl", hash = "sha256:dc80f0f5abf33bd7099f7ac94ab1206730a3c0a2d17549911ed2cb6b7aa36d2d"}, + {file = "ujson-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:506a45e5fcbb2d46f1a51fead991c39529fc3737c0f5d47c9b4a1d762578fc30"}, + {file = "ujson-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0fd2eba664a22447102062814bd13e63c6130540222c0aa620701dd01f4be81"}, + {file = "ujson-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bdf7fc21a03bafe4ba208dafa84ae38e04e5d36c0e1c746726edf5392e9f9f36"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f909bc08ce01f122fd9c24bc6f9876aa087188dfaf3c4116fe6e4daf7e194f"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd4ea86c2afd41429751d22a3ccd03311c067bd6aeee2d054f83f97e41e11d8f"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63fb2e6599d96fdffdb553af0ed3f76b85fda63281063f1cb5b1141a6fcd0617"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:32bba5870c8fa2a97f4a68f6401038d3f1922e66c34280d710af00b14a3ca562"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:37ef92e42535a81bf72179d0e252c9af42a4ed966dc6be6967ebfb929a87bc60"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f69f16b8f1c69da00e38dc5f2d08a86b0e781d0ad3e4cc6a13ea033a439c4844"}, + {file = "ujson-5.9.0-cp39-cp39-win32.whl", hash = "sha256:3382a3ce0ccc0558b1c1668950008cece9bf463ebb17463ebf6a8bfc060dae34"}, + {file = "ujson-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:6adef377ed583477cf005b58c3025051b5faa6b8cc25876e594afbb772578f21"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ffdfebd819f492e48e4f31c97cb593b9c1a8251933d8f8972e81697f00326ff1"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4eec2ddc046360d087cf35659c7ba0cbd101f32035e19047013162274e71fcf"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbb90aa5c23cb3d4b803c12aa220d26778c31b6e4b7a13a1f49971f6c7d088e"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0823cb70866f0d6a4ad48d998dd338dce7314598721bc1b7986d054d782dfd"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4e35d7885ed612feb6b3dd1b7de28e89baaba4011ecdf995e88be9ac614765e9"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b048aa93eace8571eedbd67b3766623e7f0acbf08ee291bef7d8106210432427"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:323279e68c195110ef85cbe5edce885219e3d4a48705448720ad925d88c9f851"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ac92d86ff34296f881e12aa955f7014d276895e0e4e868ba7fddebbde38e378"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6eecbd09b316cea1fd929b1e25f70382917542ab11b692cb46ec9b0a26c7427f"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:473fb8dff1d58f49912323d7cb0859df5585cfc932e4b9c053bf8cf7f2d7c5c4"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f91719c6abafe429c1a144cfe27883eace9fb1c09a9c5ef1bcb3ae80a3076a4e"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1c0991c4fe256f5fdb19758f7eac7f47caac29a6c57d0de16a19048eb86bad"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ea0f55a1396708e564595aaa6696c0d8af532340f477162ff6927ecc46e21"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:07e0cfdde5fd91f54cd2d7ffb3482c8ff1bf558abf32a8b953a5d169575ae1cd"}, + {file = "ujson-5.9.0.tar.gz", hash = "sha256:89cc92e73d5501b8a7f48575eeb14ad27156ad092c2e9fc7e3cf949f07e75532"}, +] + [[package]] name = "urllib3" version = "2.2.1" diff --git a/tests/api/test_model.py b/tests/api/test_model.py index 66cf56c6..d57d51a9 100644 --- a/tests/api/test_model.py +++ b/tests/api/test_model.py @@ -20,16 +20,16 @@ def test_index_model(test_mp, request): table_endpoint, json={"run": {"scenario": {"name__in": ["Scenario 1"]}}} ) - assert res.json()["results"]["data"][0][1] == "Model 1" + assert res.json()["results"]["data"][0][0] == "Model 1" res = test_mp.backend.client.patch( table_endpoint, json={"run": {"scenario": {"name": "Scenario 2"}}} ) - assert res.json()["results"]["data"][0][1] == "Model 2" + assert res.json()["results"]["data"][0][0] == "Model 2" res = test_mp.backend.client.patch( table_endpoint, json={"variable": {"name": "Variable 4"}} ) - assert res.json()["results"]["data"][0][1] == "Model 2" + assert res.json()["results"]["data"][0][0] == "Model 2" diff --git a/tests/api/test_scenario.py b/tests/api/test_scenario.py index 3e29818c..956c65cd 100644 --- a/tests/api/test_scenario.py +++ b/tests/api/test_scenario.py @@ -20,16 +20,16 @@ def test_index_scenario(test_mp, request): table_endpoint, json={"run": {"model": {"name__in": ["Model 1"]}}} ) - assert res.json()["results"]["data"][0][1] == "Scenario 1" + assert res.json()["results"]["data"][0][0] == "Scenario 1" res = test_mp.backend.client.patch( table_endpoint, json={"run": {"model": {"name": "Model 2"}}} ) - assert res.json()["results"]["data"][0][1] == "Scenario 2" + assert res.json()["results"]["data"][0][0] == "Scenario 2" res = test_mp.backend.client.patch( table_endpoint, json={"variable": {"name": "Variable 4"}} ) - assert res.json()["results"]["data"][0][1] == "Scenario 2" + assert res.json()["results"]["data"][0][0] == "Scenario 2" diff --git a/tests/core/test_indexset.py b/tests/core/test_indexset.py index 0260f191..ad7931ca 100644 --- a/tests/core/test_indexset.py +++ b/tests/core/test_indexset.py @@ -2,7 +2,7 @@ import pandas.testing as pdt import pytest -from ixmp4 import IndexSet +from ixmp4 import IndexSet, Platform from ..utils import all_platforms @@ -36,7 +36,7 @@ def df_from_list(indexsets: list[IndexSet]): @all_platforms class TestCoreIndexSet: def test_create_indexset(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.runs.create("Model", "Scenario") indexset_1 = run.optimization.indexsets.create("IndexSet 1") assert indexset_1.id == 1 @@ -49,7 +49,7 @@ def test_create_indexset(self, test_mp, request): _ = run.optimization.indexsets.create("IndexSet 1") def test_get_indexset(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.runs.create("Model", "Scenario") _ = run.optimization.indexsets.create("IndexSet 1") indexset = run.optimization.indexsets.get("IndexSet 1") @@ -60,7 +60,7 @@ def test_get_indexset(self, test_mp, request): _ = run.optimization.indexsets.get("Foo") def test_add_elements(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.runs.create("Model", "Scenario") test_elements = ["foo", "bar"] indexset_1 = run.optimization.indexsets.create("IndexSet 1") @@ -88,7 +88,7 @@ def test_add_elements(self, test_mp, request): assert indexset_5.elements == test_elements_2 def test_list_indexsets(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.runs.create("Model", "Scenario") # Per default, list() lists only `default` version runs: run.set_as_default() @@ -107,7 +107,7 @@ def test_list_indexsets(self, test_mp, request): assert not (set(expected_id) ^ set(list_id)) def test_tabulate_indexsets(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.runs.create("Model", "Scenario") # Per default, tabulate() lists only `default` version runs: run.set_as_default() @@ -124,7 +124,7 @@ def test_tabulate_indexsets(self, test_mp, request): pdt.assert_frame_equal(expected, result) def test_indexset_docs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.runs.create("Model", "Scenario") indexset_1 = run.optimization.indexsets.create("IndexSet 1") docs = "Documentation of IndexSet 1" diff --git a/tests/core/test_optimization_parameter.py b/tests/core/test_optimization_parameter.py new file mode 100644 index 00000000..63a3da5d --- /dev/null +++ b/tests/core/test_optimization_parameter.py @@ -0,0 +1,323 @@ +import pandas as pd +import pytest + +from ixmp4 import Parameter, Platform + +from ..utils import all_platforms + + +def df_from_list(parameters: list): + return pd.DataFrame( + [ + [ + parameter.run_id, + parameter.data, + parameter.name, + parameter.id, + parameter.created_at, + parameter.created_by, + ] + for parameter in parameters + ], + columns=[ + "run__id", + "data", + "name", + "id", + "created_at", + "created_by", + ], + ) + + +@all_platforms +class TestDataOptimizationParameter: + def test_create_parameter(self, test_mp, request): + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore + run = test_mp.runs.create("Model", "Scenario") + + # Test normal creation + indexset_1 = run.optimization.indexsets.create("Indexset") + parameter = run.optimization.parameters.create( + name="Parameter", + constrained_to_indexsets=["Indexset"], + ) + + assert parameter.run_id == run.id + assert parameter.name == "Parameter" + assert parameter.data == {} # JsonDict type currently requires a dict, not None + assert parameter.columns[0].name == "Indexset" + assert parameter.constrained_to_indexsets == [indexset_1.name] + assert parameter.values == [] + assert parameter.units == [] + + # Test duplicate name raises + with pytest.raises(Parameter.NotUnique): + _ = run.optimization.parameters.create( + "Parameter", constrained_to_indexsets=["Indexset"] + ) + + # Test mismatch in constrained_to_indexsets and column_names raises + with pytest.raises(ValueError, match="not equal in length"): + _ = run.optimization.parameters.create( + "Parameter 2", + constrained_to_indexsets=["Indexset"], + column_names=["Dimension 1", "Dimension 2"], + ) + + # Test columns_names are used for names if given + parameter_2 = run.optimization.parameters.create( + "Parameter 2", + constrained_to_indexsets=[indexset_1.name], + column_names=["Column 1"], + ) + assert parameter_2.columns[0].name == "Column 1" + + # Test duplicate column_names raise + with pytest.raises(ValueError, match="`column_names` are not unique"): + _ = run.optimization.parameters.create( + name="Parameter 3", + constrained_to_indexsets=[indexset_1.name, indexset_1.name], + column_names=["Column 1", "Column 1"], + ) + + # Test column.dtype is registered correctly + indexset_2 = run.optimization.indexsets.create("Indexset 2") + indexset_2.add(elements=2024) + parameter_3 = run.optimization.parameters.create( + "Parameter 5", + constrained_to_indexsets=["Indexset", indexset_2.name], + ) + # If indexset doesn't have elements, a generic dtype is registered + assert parameter_3.columns[0].dtype == "object" + assert parameter_3.columns[1].dtype == "int64" + + def test_get_parameter(self, test_mp, request): + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore + run = test_mp.runs.create("Model", "Scenario") + indexset = run.optimization.indexsets.create("Indexset") + _ = run.optimization.parameters.create( + name="Parameter", constrained_to_indexsets=["Indexset"] + ) + parameter = run.optimization.parameters.get(name="Parameter") + assert parameter.run_id == run.id + assert parameter.id == 1 + assert parameter.name == "Parameter" + assert parameter.data == {} + assert parameter.values == [] + assert parameter.units == [] + assert parameter.columns[0].name == indexset.name + assert parameter.constrained_to_indexsets == [indexset.name] + + with pytest.raises(Parameter.NotFound): + _ = run.optimization.parameters.get("Parameter 2") + + def test_parameter_add_data(self, test_mp, request): + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore + run = test_mp.runs.create("Model", "Scenario") + unit = test_mp.units.create("Unit") + indexset_1 = run.optimization.indexsets.create("Indexset") + indexset_1.add(elements=["foo", "bar", ""]) + indexset_2 = run.optimization.indexsets.create("Indexset 2") + indexset_2.add(elements=[1, 2, 3]) + # pandas can only convert dicts to dataframes if the values are lists + # or if index is given. But maybe using read_json instead of from_dict + # can remedy this. Or maybe we want to catch the resulting + # "ValueError: If using all scalar values, you must pass an index" and + # reraise a custom informative error? + test_data_1 = { + "Indexset": ["foo"], + "Indexset 2": [1], + "values": [3.14], + "units": [unit.name], + } + parameter = run.optimization.parameters.create( + "Parameter", + constrained_to_indexsets=[indexset_1.name, indexset_2.name], + ) + parameter.add(data=test_data_1) + assert parameter.data == test_data_1 + assert parameter.values == test_data_1["values"] + assert parameter.units == test_data_1["units"] + + parameter_2 = run.optimization.parameters.create( + name="Parameter 2", + constrained_to_indexsets=[indexset_1.name, indexset_2.name], + ) + + with pytest.raises( + AssertionError, match=r"must include the column\(s\): values!" + ): + parameter_2.add( + pd.DataFrame( + { + "Indexset": [None], + "Indexset 2": [2], + "units": [unit.name], + } + ), + ) + + with pytest.raises( + AssertionError, match=r"must include the column\(s\): units!" + ): + parameter_2.add( + data=pd.DataFrame( + { + "Indexset": [None], + "Indexset 2": [2], + "values": [""], + } + ), + ) + + # By converting data to pd.DataFrame, we automatically enforce equal length + # of new columns, raises All arrays must be of the same length otherwise: + with pytest.raises(ValueError, match="All arrays must be of the same length"): + parameter_2.add( + data={ + "Indexset": ["foo", "foo"], + "Indexset 2": [2, 2], + "values": [1, 2], + "units": [unit.name], + }, + ) + + with pytest.raises(ValueError, match="contains duplicate rows"): + parameter_2.add( + data={ + "Indexset": ["foo", "foo"], + "Indexset 2": [2, 2], + "values": [1, 2], + "units": [unit.name, unit.name], + }, + ) + + # Test that order is conserved + test_data_2 = { + "Indexset": ["", "", "foo", "foo", "bar", "bar"], + "Indexset 2": [3, 1, 2, 1, 2, 3], + "values": [6, 5, 4, 3, 2, 1], + "units": [unit.name] * 6, + } + parameter_2.add(test_data_2) + assert parameter_2.data == test_data_2 + assert parameter_2.values == test_data_2["values"] + assert parameter_2.units == test_data_2["units"] + + # Test order is conserved with varying types and upon later addition of data + parameter_3 = run.optimization.parameters.create( + name="Parameter 3", + constrained_to_indexsets=[indexset_1.name, indexset_2.name], + column_names=["Column 1", "Column 2"], + ) + unit_2 = test_mp.units.create("Unit 2") + unit_3 = test_mp.units.create("Unit 3") + + test_data_3 = { + "Column 1": ["bar", "foo", ""], + "Column 2": [2, 3, 1], + "values": ["3", 2.0, 1], + "units": [unit_3.name, unit_2.name, unit.name], + } + parameter_3.add(data=test_data_3) + assert parameter_3.data == test_data_3 + assert parameter_3.values == test_data_3["values"] + assert parameter_3.units == test_data_3["units"] + + test_data_4 = { + "Column 1": ["foo", "", "bar"], + "Column 2": [2, 3, 1], + "values": [3.14, 2, "1"], + "units": [unit_2.name, unit.name, unit_3.name], + } + parameter_3.add(data=test_data_4) + test_data_5 = test_data_3.copy() + for key, value in test_data_4.items(): + test_data_5[key].extend(value) + assert parameter_3.data == test_data_5 + assert parameter_3.values == test_data_5["values"] + assert parameter_3.units == test_data_5["units"] + + def test_list_parameter(self, test_mp, request): + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore + run = test_mp.runs.create("Model", "Scenario") + # Per default, list() lists scalars for `default` version runs: + run.set_as_default() + _ = run.optimization.indexsets.create("Indexset") + _ = run.optimization.indexsets.create("Indexset 2") + parameter = run.optimization.parameters.create( + "Parameter", constrained_to_indexsets=["Indexset"] + ) + parameter_2 = run.optimization.parameters.create( + "Parameter 2", constrained_to_indexsets=["Indexset 2"] + ) + expected_ids = [parameter.id, parameter_2.id] + list_ids = [parameter.id for parameter in run.optimization.parameters.list()] + assert not (set(expected_ids) ^ set(list_ids)) + + # Test retrieving just one result by providing a name + expected_id = [parameter.id] + list_id = [ + parameter.id + for parameter in run.optimization.parameters.list(name="Parameter") + ] + assert not (set(expected_id) ^ set(list_id)) + + def test_tabulate_parameter(self, test_mp, request): + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore + run = test_mp.runs.create("Model", "Scenario") + # Per default, tabulate() lists scalars for `default` version runs: + run.set_as_default() + indexset = run.optimization.indexsets.create("Indexset") + indexset_2 = run.optimization.indexsets.create("Indexset 2") + parameter = run.optimization.parameters.create( + name="Parameter", + constrained_to_indexsets=["Indexset", "Indexset 2"], + ) + parameter_2 = run.optimization.parameters.create( + name="Parameter 2", + constrained_to_indexsets=["Indexset", "Indexset 2"], + ) + pd.testing.assert_frame_equal( + df_from_list([parameter_2]), + run.optimization.parameters.tabulate(name="Parameter 2"), + ) + + unit = test_mp.units.create("Unit") + unit_2 = test_mp.units.create("Unit 2") + indexset.add(elements=["foo", "bar"]) + indexset_2.add(elements=[1, 2, 3]) + test_data_1 = { + "Indexset": ["foo"], + "Indexset 2": [1], + "values": ["value"], + "units": [unit.name], + } + parameter.add(data=test_data_1) + + test_data_2 = { + "Indexset 2": [2, 3], + "Indexset": ["foo", "bar"], + "values": [1, "value"], + "units": [unit.name, unit_2.name], + } + parameter_2.add(data=test_data_2) + pd.testing.assert_frame_equal( + df_from_list([parameter, parameter_2]), + run.optimization.parameters.tabulate(), + ) + + def test_parameter_docs(self, test_mp, request): + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore + run = test_mp.runs.create("Model", "Scenario") + indexset = run.optimization.indexsets.create("Indexset") + parameter_1 = run.optimization.parameters.create( + "Parameter 1", constrained_to_indexsets=[indexset.name] + ) + docs = "Documentation of Parameter 1" + parameter_1.docs = docs + assert parameter_1.docs == docs + + parameter_1.docs = None + assert parameter_1.docs is None diff --git a/tests/data/test_docs.py b/tests/data/test_docs.py index d23c0b8a..6abfe5e4 100644 --- a/tests/data/test_docs.py +++ b/tests/data/test_docs.py @@ -1,5 +1,6 @@ import pytest +from ixmp4 import Platform from ixmp4.data.abstract import Docs from ..utils import all_platforms @@ -8,7 +9,7 @@ @all_platforms class TestDataDocs: def test_get_and_set_modeldocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore model = test_mp.backend.models.create("Model") docs_model = test_mp.backend.models.docs.set(model.id, "Description of Model") @@ -16,7 +17,7 @@ def test_get_and_set_modeldocs(self, test_mp, request): assert docs_model == docs_model1 def test_change_empty_modeldocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore model = test_mp.backend.models.create("Model") with pytest.raises(Docs.NotFound): @@ -35,7 +36,7 @@ def test_change_empty_modeldocs(self, test_mp, request): assert test_mp.backend.models.docs.get(model.id) == docs_model2 def test_delete_modeldocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore model = test_mp.backend.models.create("Model") docs_model = test_mp.backend.models.docs.set( model.id, "Description of test Model" @@ -49,7 +50,7 @@ def test_delete_modeldocs(self, test_mp, request): test_mp.backend.models.docs.get(model.id) def test_get_and_set_regiondocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore region = test_mp.backend.regions.create("Region", "Hierarchy") docs_region = test_mp.backend.regions.docs.set( region.id, "Description of test Region" @@ -59,7 +60,7 @@ def test_get_and_set_regiondocs(self, test_mp, request): assert docs_region == docs_region1 def test_change_empty_regiondocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore region = test_mp.backend.regions.create("Region", "Hierarchy") with pytest.raises(Docs.NotFound): @@ -78,7 +79,7 @@ def test_change_empty_regiondocs(self, test_mp, request): assert test_mp.backend.regions.docs.get(region.id) == docs_region2 def test_delete_regiondocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore region = test_mp.backend.regions.create("Region", "Hierarchy") docs_region = test_mp.backend.regions.docs.set( region.id, "Description of test region" @@ -92,7 +93,7 @@ def test_delete_regiondocs(self, test_mp, request): test_mp.backend.regions.docs.get(region.id) def test_get_and_set_scenariodocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore scenario = test_mp.backend.scenarios.create("Scenario") docs_scenario = test_mp.backend.scenarios.docs.set( scenario.id, "Description of Scenario" @@ -101,7 +102,7 @@ def test_get_and_set_scenariodocs(self, test_mp, request): assert docs_scenario == docs_scenario1 def test_change_empty_scenariodocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore scenario = test_mp.backend.scenarios.create("Scenario") with pytest.raises(Docs.NotFound): @@ -120,7 +121,7 @@ def test_change_empty_scenariodocs(self, test_mp, request): assert test_mp.backend.scenarios.docs.get(scenario.id) == docs_scenario2 def test_delete_scenariodocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore scenario = test_mp.backend.scenarios.create("Scenario") docs_scenario = test_mp.backend.scenarios.docs.set( scenario.id, "Description of test Scenario" @@ -134,7 +135,7 @@ def test_delete_scenariodocs(self, test_mp, request): test_mp.backend.scenarios.docs.get(scenario.id) def test_get_and_set_unitdocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore unit = test_mp.backend.units.create("Unit") docs_unit = test_mp.backend.units.docs.set(unit.id, "Description of test Unit") docs_unit1 = test_mp.backend.units.docs.get(unit.id) @@ -142,7 +143,7 @@ def test_get_and_set_unitdocs(self, test_mp, request): assert docs_unit == docs_unit1 def test_change_empty_unitdocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore unit = test_mp.backend.units.create("Unit") with pytest.raises(Docs.NotFound): @@ -159,7 +160,7 @@ def test_change_empty_unitdocs(self, test_mp, request): assert test_mp.backend.units.docs.get(unit.id) == docs_unit2 def test_delete_unitdocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore unit = test_mp.backend.units.create("Unit") docs_unit = test_mp.backend.units.docs.set(unit.id, "Description of test Unit") @@ -171,7 +172,7 @@ def test_delete_unitdocs(self, test_mp, request): test_mp.backend.units.docs.get(unit.id) def test_get_and_set_variabledocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore variable = test_mp.backend.iamc.variables.create("Variable") docs_variable = test_mp.backend.iamc.variables.docs.set( variable.id, "Description of test Variable" @@ -181,7 +182,7 @@ def test_get_and_set_variabledocs(self, test_mp, request): assert docs_variable == docs_variables1 def test_change_empty_variabledocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore variable = test_mp.backend.iamc.variables.create("Variable") with pytest.raises(Docs.NotFound): @@ -200,7 +201,7 @@ def test_change_empty_variabledocs(self, test_mp, request): assert test_mp.backend.iamc.variables.docs.get(variable.id) == docs_variable2 def test_delete_variabledocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore variable = test_mp.backend.iamc.variables.create("Variable") docs_variable = test_mp.backend.iamc.variables.docs.set( variable.id, "Description of test Variable" @@ -214,7 +215,7 @@ def test_delete_variabledocs(self, test_mp, request): test_mp.backend.iamc.variables.docs.get(variable.id) def test_get_and_set_indexsetdocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.backend.runs.create("Model", "Scenario") indexset = test_mp.backend.optimization.indexsets.create( run_id=run.id, name="IndexSet" @@ -227,7 +228,7 @@ def test_get_and_set_indexsetdocs(self, test_mp, request): assert docs_indexset == docs_indexset1 def test_change_empty_indexsetdocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.backend.runs.create("Model", "Scenario") indexset = test_mp.backend.optimization.indexsets.create( run_id=run.id, name="IndexSet" @@ -255,7 +256,7 @@ def test_change_empty_indexsetdocs(self, test_mp, request): ) def test_delete_indexsetdocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.backend.runs.create("Model", "Scenario") indexset = test_mp.backend.optimization.indexsets.create( run_id=run.id, name="IndexSet" @@ -275,7 +276,7 @@ def test_delete_indexsetdocs(self, test_mp, request): test_mp.backend.optimization.indexsets.docs.get(indexset.id) def test_get_and_set_scalardocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.backend.runs.create("Model", "Scenario") unit = test_mp.backend.units.create("Unit") scalar = test_mp.backend.optimization.scalars.create( @@ -289,7 +290,7 @@ def test_get_and_set_scalardocs(self, test_mp, request): assert docs_scalar == docs_scalar1 def test_change_empty_scalardocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.backend.runs.create("Model", "Scenario") unit = test_mp.backend.units.create("Unit") scalar = test_mp.backend.optimization.scalars.create( @@ -312,7 +313,7 @@ def test_change_empty_scalardocs(self, test_mp, request): assert test_mp.backend.optimization.scalars.docs.get(scalar.id) == docs_scalar2 def test_delete_scalardocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.backend.runs.create("Model", "Scenario") unit = test_mp.backend.units.create("Unit") scalar = test_mp.backend.optimization.scalars.create( @@ -330,7 +331,7 @@ def test_delete_scalardocs(self, test_mp, request): test_mp.backend.optimization.scalars.docs.get(scalar.id) def test_get_and_set_tabledocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.backend.runs.create("Model", "Scenario") _ = test_mp.backend.optimization.indexsets.create( run_id=run.id, name="Indexset" @@ -346,7 +347,7 @@ def test_get_and_set_tabledocs(self, test_mp, request): assert docs_table == docs_table1 def test_change_empty_tabledocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.backend.runs.create("Model", "Scenario") _ = test_mp.backend.optimization.indexsets.create( run_id=run.id, name="Indexset" @@ -371,7 +372,7 @@ def test_change_empty_tabledocs(self, test_mp, request): assert test_mp.backend.optimization.tables.docs.get(table.id) == docs_table2 def test_delete_tabledocs(self, test_mp, request): - test_mp = request.getfixturevalue(test_mp) + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore run = test_mp.backend.runs.create("Model", "Scenario") _ = test_mp.backend.optimization.indexsets.create( run_id=run.id, name="Indexset" @@ -389,3 +390,73 @@ def test_delete_tabledocs(self, test_mp, request): with pytest.raises(Docs.NotFound): test_mp.backend.optimization.tables.docs.get(table.id) + + def test_get_and_set_parameterdocs(self, test_mp, request): + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore + run = test_mp.backend.runs.create("Model", "Scenario") + _ = test_mp.backend.optimization.indexsets.create( + run_id=run.id, name="Indexset" + ) + parameter = test_mp.backend.optimization.parameters.create( + run_id=run.id, name="Parameter", constrained_to_indexsets=["Indexset"] + ) + docs_parameter = test_mp.backend.optimization.parameters.docs.set( + parameter.id, "Description of test Parameter" + ) + docs_parameter1 = test_mp.backend.optimization.parameters.docs.get(parameter.id) + + assert docs_parameter == docs_parameter1 + + def test_change_empty_parameterdocs(self, test_mp, request): + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore + run = test_mp.backend.runs.create("Model", "Scenario") + _ = test_mp.backend.optimization.indexsets.create( + run_id=run.id, name="Indexset" + ) + parameter = test_mp.backend.optimization.parameters.create( + run_id=run.id, name="Parameter", constrained_to_indexsets=["Indexset"] + ) + + with pytest.raises(Docs.NotFound): + test_mp.backend.optimization.parameters.docs.get(parameter.id) + + docs_parameter1 = test_mp.backend.optimization.parameters.docs.set( + parameter.id, "Description of test Parameter" + ) + + assert ( + test_mp.backend.optimization.parameters.docs.get(parameter.id) + == docs_parameter1 + ) + + docs_parameter2 = test_mp.backend.optimization.parameters.docs.set( + parameter.id, "Different description of test Parameter" + ) + + assert ( + test_mp.backend.optimization.parameters.docs.get(parameter.id) + == docs_parameter2 + ) + + def test_delete_parameterdocs(self, test_mp, request): + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore + run = test_mp.backend.runs.create("Model", "Scenario") + _ = test_mp.backend.optimization.indexsets.create( + run_id=run.id, name="Indexset" + ) + parameter = test_mp.backend.optimization.parameters.create( + run_id=run.id, name="Parameter", constrained_to_indexsets=["Indexset"] + ) + docs_parameter = test_mp.backend.optimization.parameters.docs.set( + parameter.id, "Description of test Parameter" + ) + + assert ( + test_mp.backend.optimization.parameters.docs.get(parameter.id) + == docs_parameter + ) + + test_mp.backend.optimization.parameters.docs.delete(parameter.id) + + with pytest.raises(Docs.NotFound): + test_mp.backend.optimization.parameters.docs.get(parameter.id) diff --git a/tests/data/test_optimization_parameter.py b/tests/data/test_optimization_parameter.py index 9dffc28a..260539f6 100644 --- a/tests/data/test_optimization_parameter.py +++ b/tests/data/test_optimization_parameter.py @@ -3,34 +3,34 @@ from ixmp4 import Parameter, Platform -from ..utils import database_platforms +from ..utils import all_platforms def df_from_list(parameters: list): return pd.DataFrame( [ [ - parameter.name, - parameter.data, parameter.run__id, + parameter.data, + parameter.name, + parameter.id, parameter.created_at, parameter.created_by, - parameter.id, ] for parameter in parameters ], columns=[ - "name", - "data", "run__id", + "data", + "name", + "id", "created_at", "created_by", - "id", ], ) -@database_platforms +@all_platforms class TestDataOptimizationParameter: def test_create_parameter(self, test_mp, request): test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore @@ -275,80 +275,92 @@ def test_parameter_add_data(self, test_mp, request): test_data_5[key].extend(value) assert parameter_3.data == test_data_5 - # def test_list_parameter(self, test_mp, request): - # test_mp = request.getfixturevalue(test_mp) - # run = test_mp.backend.runs.create("Model", "Scenario") - # # Per default, list() lists scalars for `default` version runs: - # test_mp.backend.runs.set_as_default_version(run.id) - # _ = test_mp.backend.optimization.indexsets.create( - # run_id=run.id, name="Indexset" - # ) - # _ = test_mp.backend.optimization.indexsets.create( - # run_id=run.id, name="Indexset 2" - # ) - # parameter = test_mp.backend.optimization.parameters.create( - # run_id=run.id, name="Parameter", constrained_to_indexsets=["Indexset"] - # ) - # parameter_2 = test_mp.backend.optimization.parameters.create( - # run_id=run.id, name="Parameter 2", constrained_to_indexsets=["Indexset 2"] - # ) - # assert [ - # parameter, - # parameter_2, - # ] == test_mp.backend.optimization.parameters.list() + def test_list_parameter(self, test_mp, request): + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore + run = test_mp.backend.runs.create("Model", "Scenario") + # Per default, list() lists scalars for `default` version runs: + test_mp.backend.runs.set_as_default_version(run.id) + _ = test_mp.backend.optimization.indexsets.create( + run_id=run.id, name="Indexset" + ) + _ = test_mp.backend.optimization.indexsets.create( + run_id=run.id, name="Indexset 2" + ) + parameter = test_mp.backend.optimization.parameters.create( + run_id=run.id, name="Parameter", constrained_to_indexsets=["Indexset"] + ) + parameter_2 = test_mp.backend.optimization.parameters.create( + run_id=run.id, name="Parameter 2", constrained_to_indexsets=["Indexset 2"] + ) + assert [ + parameter, + parameter_2, + ] == test_mp.backend.optimization.parameters.list() - # assert [parameter] == test_mp.backend.optimization.parameters.list( - # name="Parameter" - # ) + assert [parameter] == test_mp.backend.optimization.parameters.list( + name="Parameter" + ) - # def test_tabulate_parameter(self, test_mp, request): - # test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore - # run = test_mp.backend.runs.create("Model", "Scenario") - # # Per default, tabulate() lists scalars for `default` version runs: - # test_mp.backend.runs.set_as_default_version(run.id) - # indexset = test_mp.backend.optimization.indexsets.create( - # run_id=run.id, name="Indexset" - # ) - # indexset_2 = test_mp.backend.optimization.indexsets.create( - # run_id=run.id, name="Indexset 2" - # ) - # parameter = test_mp.backend.optimization.parameters.create( - # run_id=run.id, - # name="Parameter", - # constrained_to_indexsets=["Indexset", "Indexset 2"], - # ) - # parameter_2 = test_mp.backend.optimization.parameters.create( - # run_id=run.id, - # name="Parameter 2", - # constrained_to_indexsets=["Indexset", "Indexset 2"], - # ) - # pd.testing.assert_frame_equal( - # df_from_list([parameter_2]), - # test_mp.backend.optimization.parameters.tabulate(name="Parameter 2"), - # ) + def test_tabulate_parameter(self, test_mp, request): + test_mp: Platform = request.getfixturevalue(test_mp) # type: ignore + run = test_mp.backend.runs.create("Model", "Scenario") + # Per default, tabulate() lists scalars for `default` version runs: + test_mp.backend.runs.set_as_default_version(run.id) + indexset = test_mp.backend.optimization.indexsets.create( + run_id=run.id, name="Indexset" + ) + indexset_2 = test_mp.backend.optimization.indexsets.create( + run_id=run.id, name="Indexset 2" + ) + parameter = test_mp.backend.optimization.parameters.create( + run_id=run.id, + name="Parameter", + constrained_to_indexsets=["Indexset", "Indexset 2"], + ) + parameter_2 = test_mp.backend.optimization.parameters.create( + run_id=run.id, + name="Parameter 2", + constrained_to_indexsets=["Indexset", "Indexset 2"], + ) + pd.testing.assert_frame_equal( + df_from_list([parameter_2]), + test_mp.backend.optimization.parameters.tabulate(name="Parameter 2"), + ) - # test_mp.backend.optimization.indexsets.add_elements( - # indexset_id=indexset.id, elements=["foo", "bar"] - # ) - # test_mp.backend.optimization.indexsets.add_elements( - # indexset_id=indexset_2.id, elements=[1, 2, 3] - # ) - # test_data_1 = {"Indexset": ["foo"], "Indexset 2": [1]} - # test_mp.backend.optimization.parameters.add_data( - # parameter_id=parameter.id, data=test_data_1 - # ) - # parameter = test_mp.backend.optimization.parameters.get( - # run_id=run.id, name="Parameter" - # ) + unit = test_mp.backend.units.create("Unit") + unit_2 = test_mp.backend.units.create("Unit 2") + test_mp.backend.optimization.indexsets.add_elements( + indexset_id=indexset.id, elements=["foo", "bar"] + ) + test_mp.backend.optimization.indexsets.add_elements( + indexset_id=indexset_2.id, elements=[1, 2, 3] + ) + test_data_1 = { + "Indexset": ["foo"], + "Indexset 2": [1], + "values": ["value"], + "units": [unit.name], + } + test_mp.backend.optimization.parameters.add_data( + parameter_id=parameter.id, data=test_data_1 + ) + parameter = test_mp.backend.optimization.parameters.get( + run_id=run.id, name="Parameter" + ) - # test_data_2 = {"Indexset 2": [2, 3], "Indexset": ["foo", "bar"]} - # test_mp.backend.optimization.parameters.add_data( - # parameter_id=parameter_2.id, data=test_data_2 - # ) - # parameter_2 = test_mp.backend.optimization.parameters.get( - # run_id=run.id, name="Parameter 2" - # ) - # pd.testing.assert_frame_equal( - # df_from_list([parameter, parameter_2]), - # test_mp.backend.optimization.parameters.tabulate(), - # ) + test_data_2 = { + "Indexset 2": [2, 3], + "Indexset": ["foo", "bar"], + "values": [1, "value"], + "units": [unit.name, unit_2.name], + } + test_mp.backend.optimization.parameters.add_data( + parameter_id=parameter_2.id, data=test_data_2 + ) + parameter_2 = test_mp.backend.optimization.parameters.get( + run_id=run.id, name="Parameter 2" + ) + pd.testing.assert_frame_equal( + df_from_list([parameter, parameter_2]), + test_mp.backend.optimization.parameters.tabulate(), + )