From 0deac5bcc8e6c42eb455115602d335458bf869bb Mon Sep 17 00:00:00 2001 From: Fridolin Glatter <83776373+glatterf42@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:41:31 +0100 Subject: [PATCH] DRY optimization core layer (#96) * Add abstraction layer for core optimization reusability * Add annotations --- ixmp4/core/optimization/base.py | 94 +++++++++++++++++++ ixmp4/core/optimization/equation.py | 44 +++------ ixmp4/core/optimization/indexset.py | 49 +++------- ixmp4/core/optimization/parameter.py | 44 +++------ ixmp4/core/optimization/scalar.py | 39 +++----- ixmp4/core/optimization/table.py | 40 +++----- ixmp4/core/optimization/variable.py | 39 +++----- ixmp4/data/abstract/__init__.py | 1 + ixmp4/data/abstract/optimization/__init__.py | 1 + ixmp4/data/abstract/optimization/base.py | 46 +++++++++ ixmp4/data/abstract/optimization/equation.py | 2 + ixmp4/data/abstract/optimization/indexset.py | 2 + ixmp4/data/abstract/optimization/parameter.py | 2 + ixmp4/data/abstract/optimization/scalar.py | 10 +- ixmp4/data/abstract/optimization/table.py | 2 + ixmp4/data/abstract/optimization/variable.py | 2 + 16 files changed, 232 insertions(+), 185 deletions(-) create mode 100644 ixmp4/core/optimization/base.py create mode 100644 ixmp4/data/abstract/optimization/base.py diff --git a/ixmp4/core/optimization/base.py b/ixmp4/core/optimization/base.py new file mode 100644 index 00000000..68fe3b8d --- /dev/null +++ b/ixmp4/core/optimization/base.py @@ -0,0 +1,94 @@ +from typing import TYPE_CHECKING, Generic, TypeVar + +if TYPE_CHECKING: + from . import InitKwargs + + +import pandas as pd + +# TODO Import this from typing when dropping Python 3.11 +from typing_extensions import TypedDict, Unpack + +from ixmp4.core.base import BaseFacade, BaseModelFacade +from ixmp4.data import abstract + +FacadeOptimizationModelType = TypeVar( + "FacadeOptimizationModelType", bound=BaseModelFacade +) +AbstractOptimizationModelType = TypeVar( + "AbstractOptimizationModelType", bound=abstract.BaseModel +) + + +class OptimizationBaseRepository( + BaseFacade, Generic[FacadeOptimizationModelType, AbstractOptimizationModelType] +): + _run: abstract.Run + _backend_repository: abstract.BackendBaseRepository[AbstractOptimizationModelType] + _model_type: type[FacadeOptimizationModelType] + + def __init__(self, _run: abstract.Run, **kwargs: Unpack["InitKwargs"]) -> None: + super().__init__(**kwargs) + self._run = _run + + +class Creator( + OptimizationBaseRepository[ + FacadeOptimizationModelType, AbstractOptimizationModelType + ], + abstract.Creator, +): + def create( + self, name: str, **kwargs: Unpack["abstract.optimization.base.CreateKwargs"] + ) -> FacadeOptimizationModelType: + model = self._backend_repository.create( + run_id=self._run.id, name=name, **kwargs + ) + return self._model_type(_backend=self.backend, _model=model) + + +class Retriever( + OptimizationBaseRepository[ + FacadeOptimizationModelType, AbstractOptimizationModelType + ], + abstract.Retriever, +): + def get(self, name: str) -> FacadeOptimizationModelType: + model = self._backend_repository.get(run_id=self._run.id, name=name) + return self._model_type(_backend=self.backend, _model=model) + + +class Lister( + OptimizationBaseRepository[ + FacadeOptimizationModelType, AbstractOptimizationModelType + ], + abstract.Lister, +): + def list(self, name: str | None = None) -> list[FacadeOptimizationModelType]: + models = self._backend_repository.list(run_id=self._run.id, name=name) + return [self._model_type(_backend=self.backend, _model=m) for m in models] + + +class Tabulator( + OptimizationBaseRepository[ + FacadeOptimizationModelType, AbstractOptimizationModelType + ], + abstract.Tabulator, +): + def tabulate(self, name: str | None = None) -> pd.DataFrame: + return self._backend_repository.tabulate(run_id=self._run.id, name=name) + + +class EnumerateKwargs(TypedDict, total=False): + name: str | None + + +class Enumerator( + Lister[FacadeOptimizationModelType, AbstractOptimizationModelType], + Tabulator[FacadeOptimizationModelType, AbstractOptimizationModelType], + abstract.Enumerator, +): + def enumerate( + self, table: bool = False, **kwargs: Unpack[EnumerateKwargs] + ) -> list[FacadeOptimizationModelType] | pd.DataFrame: + return self.tabulate(**kwargs) if table else self.list(**kwargs) diff --git a/ixmp4/core/optimization/equation.py b/ixmp4/core/optimization/equation.py index 1cb3ed30..2d6e2cdf 100644 --- a/ixmp4/core/optimization/equation.py +++ b/ixmp4/core/optimization/equation.py @@ -1,4 +1,3 @@ -from collections.abc import Iterable from datetime import datetime from typing import TYPE_CHECKING, Any, ClassVar @@ -10,12 +9,14 @@ # TODO Import this from typing when dropping Python 3.11 from typing_extensions import Unpack -from ixmp4.core.base import BaseFacade, BaseModelFacade +from ixmp4.core.base import BaseModelFacade from ixmp4.data.abstract import Docs as DocsModel from ixmp4.data.abstract import Equation as EquationModel from ixmp4.data.abstract import Run from ixmp4.data.abstract.optimization import Column +from .base import Creator, Lister, Retriever, Tabulator + class Equation(BaseModelFacade): _model: EquationModel @@ -106,12 +107,16 @@ def __str__(self) -> str: return f"" -class EquationRepository(BaseFacade): - _run: Run - +class EquationRepository( + Creator[Equation, EquationModel], + Retriever[Equation, EquationModel], + Lister[Equation, EquationModel], + Tabulator[Equation, EquationModel], +): def __init__(self, _run: Run, **kwargs: Unpack["InitKwargs"]) -> None: - super().__init__(**kwargs) - self._run = _run + super().__init__(_run=_run, **kwargs) + self._backend_repository = self.backend.optimization.equations + self._model_type = Equation def create( self, @@ -119,31 +124,8 @@ def create( constrained_to_indexsets: list[str], column_names: list[str] | None = None, ) -> Equation: - model = self.backend.optimization.equations.create( + return super().create( name=name, - run_id=self._run.id, constrained_to_indexsets=constrained_to_indexsets, column_names=column_names, ) - return Equation(_backend=self.backend, _model=model) - - def get(self, name: str) -> Equation: - model = self.backend.optimization.equations.get(run_id=self._run.id, name=name) - return Equation(_backend=self.backend, _model=model) - - def list(self, name: str | None = None) -> Iterable[Equation]: - equations = self.backend.optimization.equations.list( - run_id=self._run.id, name=name - ) - return [ - Equation( - _backend=self.backend, - _model=i, - ) - for i in equations - ] - - def tabulate(self, name: str | None = None) -> pd.DataFrame: - return self.backend.optimization.equations.tabulate( - run_id=self._run.id, name=name - ) diff --git a/ixmp4/core/optimization/indexset.py b/ixmp4/core/optimization/indexset.py index 36a3fc5f..cefe403f 100644 --- a/ixmp4/core/optimization/indexset.py +++ b/ixmp4/core/optimization/indexset.py @@ -4,16 +4,16 @@ if TYPE_CHECKING: from . import InitKwargs -import pandas as pd - # TODO Import this from typing when dropping Python 3.11 from typing_extensions import Unpack -from ixmp4.core.base import BaseFacade, BaseModelFacade +from ixmp4.core.base import BaseModelFacade from ixmp4.data.abstract import Docs as DocsModel from ixmp4.data.abstract import IndexSet as IndexSetModel from ixmp4.data.abstract import Run +from .base import Creator, Lister, Retriever, Tabulator + class IndexSet(BaseModelFacade): _model: IndexSetModel @@ -81,39 +81,16 @@ def __str__(self) -> str: return f"" -class IndexSetRepository(BaseFacade): - _run: Run - +class IndexSetRepository( + Creator[IndexSet, IndexSetModel], + Retriever[IndexSet, IndexSetModel], + Lister[IndexSet, IndexSetModel], + Tabulator[IndexSet, IndexSetModel], +): def __init__(self, _run: Run, **kwargs: Unpack["InitKwargs"]) -> None: - super().__init__(**kwargs) - self._run = _run + super().__init__(_run=_run, **kwargs) + self._backend_repository = self.backend.optimization.indexsets + self._model_type = IndexSet def create(self, name: str) -> IndexSet: - indexset = self.backend.optimization.indexsets.create( - run_id=self._run.id, - name=name, - ) - return IndexSet(_backend=self.backend, _model=indexset) - - def get(self, name: str) -> IndexSet: - indexset = self.backend.optimization.indexsets.get( - run_id=self._run.id, name=name - ) - return IndexSet(_backend=self.backend, _model=indexset) - - def list(self, name: str | None = None) -> list[IndexSet]: - indexsets = self.backend.optimization.indexsets.list( - run_id=self._run.id, name=name - ) - return [ - IndexSet( - _backend=self.backend, - _model=i, - ) - for i in indexsets - ] - - def tabulate(self, name: str | None = None) -> pd.DataFrame: - return self.backend.optimization.indexsets.tabulate( - run_id=self._run.id, name=name - ) + return super().create(name=name) diff --git a/ixmp4/core/optimization/parameter.py b/ixmp4/core/optimization/parameter.py index 1e9fcdaa..25f6f054 100644 --- a/ixmp4/core/optimization/parameter.py +++ b/ixmp4/core/optimization/parameter.py @@ -1,4 +1,3 @@ -from collections.abc import Iterable from datetime import datetime from typing import TYPE_CHECKING, Any, ClassVar @@ -10,12 +9,14 @@ # TODO Import this from typing when dropping Python 3.11 from typing_extensions import Unpack -from ixmp4.core.base import BaseFacade, BaseModelFacade +from ixmp4.core.base import BaseModelFacade from ixmp4.data.abstract import Docs as DocsModel from ixmp4.data.abstract import Parameter as ParameterModel from ixmp4.data.abstract import Run, Unit from ixmp4.data.abstract.optimization import Column +from .base import Creator, Lister, Retriever, Tabulator + class Parameter(BaseModelFacade): _model: ParameterModel @@ -99,12 +100,16 @@ def __str__(self) -> str: return f"" -class ParameterRepository(BaseFacade): - _run: Run - +class ParameterRepository( + Creator[Parameter, ParameterModel], + Retriever[Parameter, ParameterModel], + Lister[Parameter, ParameterModel], + Tabulator[Parameter, ParameterModel], +): def __init__(self, _run: Run, **kwargs: Unpack["InitKwargs"]) -> None: - super().__init__(**kwargs) - self._run = _run + super().__init__(_run=_run, **kwargs) + self._backend_repository = self.backend.optimization.parameters + self._model_type = Parameter def create( self, @@ -112,31 +117,8 @@ def create( constrained_to_indexsets: list[str], column_names: list[str] | None = None, ) -> Parameter: - model = self.backend.optimization.parameters.create( + return super().create( name=name, - run_id=self._run.id, constrained_to_indexsets=constrained_to_indexsets, column_names=column_names, ) - return Parameter(_backend=self.backend, _model=model) - - def get(self, name: str) -> Parameter: - model = self.backend.optimization.parameters.get(run_id=self._run.id, name=name) - return Parameter(_backend=self.backend, _model=model) - - def list(self, name: str | None = None) -> Iterable[Parameter]: - parameters = self.backend.optimization.parameters.list( - run_id=self._run.id, name=name - ) - return [ - Parameter( - _backend=self.backend, - _model=i, - ) - for i in parameters - ] - - def tabulate(self, name: str | None = None) -> pd.DataFrame: - return self.backend.optimization.parameters.tabulate( - run_id=self._run.id, name=name - ) diff --git a/ixmp4/core/optimization/scalar.py b/ixmp4/core/optimization/scalar.py index bfd3d196..1586c2f4 100644 --- a/ixmp4/core/optimization/scalar.py +++ b/ixmp4/core/optimization/scalar.py @@ -1,22 +1,21 @@ -from collections.abc import Iterable from datetime import datetime from typing import TYPE_CHECKING, ClassVar if TYPE_CHECKING: from . import InitKwargs -import pandas as pd - # TODO Import this from typing when dropping Python 3.11 from typing_extensions import Unpack -from ixmp4.core.base import BaseFacade, BaseModelFacade +from ixmp4.core.base import BaseModelFacade from ixmp4.core.unit import Unit from ixmp4.data.abstract import Docs as DocsModel from ixmp4.data.abstract import Run from ixmp4.data.abstract import Scalar as ScalarModel from ixmp4.data.abstract import Unit as UnitModel +from .base import Lister, Retriever, Tabulator + class Scalar(BaseModelFacade): _model: ScalarModel @@ -99,12 +98,15 @@ def __str__(self) -> str: return f"" -class ScalarRepository(BaseFacade): - _run: Run - +class ScalarRepository( + Retriever[Scalar, ScalarModel], + Lister[Scalar, ScalarModel], + Tabulator[Scalar, ScalarModel], +): def __init__(self, _run: Run, **kwargs: Unpack["InitKwargs"]) -> None: - super().__init__(**kwargs) - self._run = _run + super().__init__(_run=_run, **kwargs) + self._backend_repository = self.backend.optimization.scalars + self._model_type = Scalar def create(self, name: str, value: float, unit: str | Unit | None = None) -> Scalar: if isinstance(unit, Unit): @@ -127,22 +129,3 @@ def create(self, name: str, value: float, unit: str | Unit | None = None) -> Sca "run.optimization.scalars.update()?" ) from e return Scalar(_backend=self.backend, _model=model) - - def get(self, name: str) -> Scalar: - model = self.backend.optimization.scalars.get(run_id=self._run.id, name=name) - return Scalar(_backend=self.backend, _model=model) - - def list(self, name: str | None = None) -> Iterable[Scalar]: - scalars = self.backend.optimization.scalars.list(run_id=self._run.id, name=name) - return [ - Scalar( - _backend=self.backend, - _model=i, - ) - for i in scalars - ] - - def tabulate(self, name: str | None = None) -> pd.DataFrame: - return self.backend.optimization.scalars.tabulate( - run_id=self._run.id, name=name - ) diff --git a/ixmp4/core/optimization/table.py b/ixmp4/core/optimization/table.py index 3374c209..fb1d6316 100644 --- a/ixmp4/core/optimization/table.py +++ b/ixmp4/core/optimization/table.py @@ -1,4 +1,3 @@ -from collections.abc import Iterable from datetime import datetime from typing import TYPE_CHECKING, Any, ClassVar @@ -10,12 +9,14 @@ # TODO Import this from typing when dropping Python 3.11 from typing_extensions import Unpack -from ixmp4.core.base import BaseFacade, BaseModelFacade +from ixmp4.core.base import BaseModelFacade from ixmp4.data.abstract import Docs as DocsModel from ixmp4.data.abstract import Run from ixmp4.data.abstract import Table as TableModel from ixmp4.data.abstract.optimization import Column +from .base import Creator, Lister, Retriever, Tabulator + class Table(BaseModelFacade): _model: TableModel @@ -87,12 +88,16 @@ def __str__(self) -> str: return f"" -class TableRepository(BaseFacade): - _run: Run - +class TableRepository( + Creator[Table, TableModel], + Retriever[Table, TableModel], + Lister[Table, TableModel], + Tabulator[Table, TableModel], +): def __init__(self, _run: Run, **kwargs: Unpack["InitKwargs"]) -> None: - super().__init__(**kwargs) - self._run = _run + super().__init__(_run=_run, **kwargs) + self._backend_repository = self.backend.optimization.tables + self._model_type = Table def create( self, @@ -100,27 +105,8 @@ def create( constrained_to_indexsets: list[str], column_names: list[str] | None = None, ) -> Table: - model = self.backend.optimization.tables.create( + return super().create( name=name, - run_id=self._run.id, constrained_to_indexsets=constrained_to_indexsets, column_names=column_names, ) - return Table(_backend=self.backend, _model=model) - - def get(self, name: str) -> Table: - model = self.backend.optimization.tables.get(run_id=self._run.id, name=name) - return Table(_backend=self.backend, _model=model) - - def list(self, name: str | None = None) -> Iterable[Table]: - tables = self.backend.optimization.tables.list(run_id=self._run.id, name=name) - return [ - Table( - _backend=self.backend, - _model=i, - ) - for i in tables - ] - - def tabulate(self, name: str | None = None) -> pd.DataFrame: - return self.backend.optimization.tables.tabulate(run_id=self._run.id, name=name) diff --git a/ixmp4/core/optimization/variable.py b/ixmp4/core/optimization/variable.py index 5c1b4146..2bb7cde8 100644 --- a/ixmp4/core/optimization/variable.py +++ b/ixmp4/core/optimization/variable.py @@ -1,4 +1,3 @@ -from collections.abc import Iterable from datetime import datetime from typing import TYPE_CHECKING, Any, ClassVar @@ -10,12 +9,14 @@ # TODO Import this from typing when dropping Python 3.11 from typing_extensions import Unpack -from ixmp4.core.base import BaseFacade, BaseModelFacade +from ixmp4.core.base import BaseModelFacade from ixmp4.data.abstract import Docs as DocsModel from ixmp4.data.abstract import OptimizationVariable as VariableModel from ixmp4.data.abstract import Run from ixmp4.data.abstract.optimization import Column +from .base import Lister, Retriever, Tabulator + class Variable(BaseModelFacade): _model: VariableModel @@ -110,12 +111,15 @@ def __str__(self) -> str: return f"" -class VariableRepository(BaseFacade): - _run: Run - +class VariableRepository( + Retriever[Variable, VariableModel], + Lister[Variable, VariableModel], + Tabulator[Variable, VariableModel], +): def __init__(self, _run: Run, **kwargs: Unpack["InitKwargs"]) -> None: - super().__init__(**kwargs) - self._run = _run + super().__init__(_run=_run, **kwargs) + self._backend_repository = self.backend.optimization.variables + self._model_type = Variable def create( self, @@ -130,24 +134,3 @@ def create( column_names=column_names, ) return Variable(_backend=self.backend, _model=model) - - def get(self, name: str) -> Variable: - model = self.backend.optimization.variables.get(run_id=self._run.id, name=name) - return Variable(_backend=self.backend, _model=model) - - def list(self, name: str | None = None) -> Iterable[Variable]: - variables = self.backend.optimization.variables.list( - run_id=self._run.id, name=name - ) - return [ - Variable( - _backend=self.backend, - _model=i, - ) - for i in variables - ] - - def tabulate(self, name: str | None = None) -> pd.DataFrame: - return self.backend.optimization.variables.tabulate( - run_id=self._run.id, name=name - ) diff --git a/ixmp4/data/abstract/__init__.py b/ixmp4/data/abstract/__init__.py index adf7ef7d..787bccee 100644 --- a/ixmp4/data/abstract/__init__.py +++ b/ixmp4/data/abstract/__init__.py @@ -30,6 +30,7 @@ from .meta import MetaValue, RunMetaEntry, RunMetaEntryRepository, StrictMetaValue from .model import Model, ModelRepository from .optimization import ( + BackendBaseRepository, Equation, EquationRepository, IndexSet, diff --git a/ixmp4/data/abstract/optimization/__init__.py b/ixmp4/data/abstract/optimization/__init__.py index 8d95e132..97ace0ef 100644 --- a/ixmp4/data/abstract/optimization/__init__.py +++ b/ixmp4/data/abstract/optimization/__init__.py @@ -1,6 +1,7 @@ from collections.abc import Iterable from ..annotations import HasIdFilter, HasNameFilter, HasRunIdFilter +from .base import BackendBaseRepository from .column import Column from .equation import Equation, EquationRepository from .indexset import IndexSet, IndexSetRepository diff --git a/ixmp4/data/abstract/optimization/base.py b/ixmp4/data/abstract/optimization/base.py new file mode 100644 index 00000000..05de64fe --- /dev/null +++ b/ixmp4/data/abstract/optimization/base.py @@ -0,0 +1,46 @@ +from collections.abc import Iterable +from typing import TYPE_CHECKING, Generic, Protocol, TypeVar + +if TYPE_CHECKING: + from . import EnumerateKwargs + +import pandas as pd + +# TODO Import this from typing when dropping Python 3.11 +from typing_extensions import TypedDict, Unpack + +from ixmp4.data.abstract.unit import Unit + +from .. import base +from ..docs import DocsRepository + +BackendModelType = TypeVar("BackendModelType", bound=base.BaseModel, covariant=True) + + +class CreateKwargs(TypedDict, total=False): + value: float + unit: str | Unit | None + constrained_to_indexsets: str | list[str] | None + column_names: list[str] | None + + +class BackendBaseRepository( + Generic[BackendModelType], + base.Creator, + base.Retriever, + base.Enumerator, + Protocol, +): + docs: DocsRepository + + def create( + self, run_id: int, name: str, **kwargs: Unpack["CreateKwargs"] + ) -> BackendModelType: ... + + def get(self, run_id: int, name: str) -> BackendModelType: ... + + def list( + self, **kwargs: Unpack["EnumerateKwargs"] + ) -> Iterable[BackendModelType]: ... + + def tabulate(self, **kwargs: Unpack["EnumerateKwargs"]) -> pd.DataFrame: ... diff --git a/ixmp4/data/abstract/optimization/equation.py b/ixmp4/data/abstract/optimization/equation.py index a781cd3c..ee66c4eb 100644 --- a/ixmp4/data/abstract/optimization/equation.py +++ b/ixmp4/data/abstract/optimization/equation.py @@ -13,6 +13,7 @@ from .. import base from ..docs import DocsRepository +from .base import BackendBaseRepository from .column import Column @@ -39,6 +40,7 @@ def __str__(self) -> str: class EquationRepository( + BackendBaseRepository[Equation], base.Creator, base.Retriever, base.Enumerator, diff --git a/ixmp4/data/abstract/optimization/indexset.py b/ixmp4/data/abstract/optimization/indexset.py index cfacc96b..3a68c483 100644 --- a/ixmp4/data/abstract/optimization/indexset.py +++ b/ixmp4/data/abstract/optimization/indexset.py @@ -12,6 +12,7 @@ from .. import base from ..docs import DocsRepository +from .base import BackendBaseRepository class IndexSet(base.BaseModel, Protocol): @@ -36,6 +37,7 @@ def __str__(self) -> str: class IndexSetRepository( + BackendBaseRepository[IndexSet], base.Creator, base.Retriever, base.Enumerator, diff --git a/ixmp4/data/abstract/optimization/parameter.py b/ixmp4/data/abstract/optimization/parameter.py index 94fa7537..ec6025d6 100644 --- a/ixmp4/data/abstract/optimization/parameter.py +++ b/ixmp4/data/abstract/optimization/parameter.py @@ -13,6 +13,7 @@ from .. import base from ..docs import DocsRepository +from .base import BackendBaseRepository from .column import Column @@ -39,6 +40,7 @@ def __str__(self) -> str: class ParameterRepository( + BackendBaseRepository[Parameter], base.Creator, base.Retriever, base.Enumerator, diff --git a/ixmp4/data/abstract/optimization/scalar.py b/ixmp4/data/abstract/optimization/scalar.py index aab3da88..7533c690 100644 --- a/ixmp4/data/abstract/optimization/scalar.py +++ b/ixmp4/data/abstract/optimization/scalar.py @@ -19,6 +19,7 @@ class EnumerateKwargs(BaseEnumerateKwargs, HasUnitIdFilter, total=False): ... from .. import base from ..docs import DocsRepository from ..unit import Unit +from .base import BackendBaseRepository class Scalar(base.BaseModel, Protocol): @@ -45,6 +46,7 @@ def __str__(self) -> str: class ScalarRepository( + BackendBaseRepository[Scalar], base.Creator, base.Retriever, base.Enumerator, @@ -52,11 +54,14 @@ class ScalarRepository( ): docs: DocsRepository - def create(self, name: str, value: float, unit_name: str, run_id: int) -> Scalar: + def create(self, run_id: int, name: str, value: float, unit_name: str) -> Scalar: """Creates a Scalar. Parameters ---------- + run_id : int + The id of the :class:`ixmp4.data.abstract.Run` for which this Scalar is + defined. name : str The name of the Scalar. value : float @@ -64,9 +69,6 @@ def create(self, name: str, value: float, unit_name: str, run_id: int) -> Scalar unit_name : str The name of the :class:`ixmp4.data.abstract.Unit` for which this Scalar is defined. - run_id : int - The id of the :class:`ixmp4.data.abstract.Run` for which this Scalar is - defined. Raises ------ diff --git a/ixmp4/data/abstract/optimization/table.py b/ixmp4/data/abstract/optimization/table.py index 7c2a71e3..d6721d2b 100644 --- a/ixmp4/data/abstract/optimization/table.py +++ b/ixmp4/data/abstract/optimization/table.py @@ -13,6 +13,7 @@ from .. import base from ..docs import DocsRepository +from .base import BackendBaseRepository from .column import Column @@ -39,6 +40,7 @@ def __str__(self) -> str: class TableRepository( + BackendBaseRepository[Table], base.Creator, base.Retriever, base.Enumerator, diff --git a/ixmp4/data/abstract/optimization/variable.py b/ixmp4/data/abstract/optimization/variable.py index 39e86aaf..9a50b9fb 100644 --- a/ixmp4/data/abstract/optimization/variable.py +++ b/ixmp4/data/abstract/optimization/variable.py @@ -12,6 +12,7 @@ from .. import base from ..docs import DocsRepository +from .base import BackendBaseRepository from .column import Column @@ -38,6 +39,7 @@ def __str__(self) -> str: class VariableRepository( + BackendBaseRepository[Variable], base.Creator, base.Retriever, base.Enumerator,