Source code for kif_lib.model.value.quantity

# Copyright (C) 2024 IBM Corp.
# SPDX-License-Identifier: Apache-2.0

from __future__ import annotations

import decimal

from ...typing import Any, ClassVar, Location, override, Self, TypeAlias, Union
from ..term import Template, Variable
from .deep_data_value import (
    DeepDataValue,
    DeepDataValueTemplate,
    DeepDataValueVariable,
)
from .item import Item, ItemTemplate, ItemVariable, VItem, VTItem
from .string import String, TString
from .value import Datatype

TDecimal: TypeAlias = Union[decimal.Decimal, float, int, TString]
TQuantity: TypeAlias = Union['Quantity', TDecimal]
VQuantity: TypeAlias =\
    Union['QuantityTemplate', 'QuantityVariable', 'Quantity']
VQuantityContent: TypeAlias = Union['QuantityVariable', decimal.Decimal]
VTQuantityContent: TypeAlias = Union[Variable, TQuantity]


[docs] class QuantityTemplate(DeepDataValueTemplate): """Quantity template. Parameters: amount: Amount or quantity variable. unit: Unit, item template, or item variable. lower_bound: Lower bound or quantity variable. upper_bound: Upper bound or quantity variable. """ object_class: ClassVar[type[Quantity]] # pyright: ignore
[docs] def __init__( self, amount: VTQuantityContent, unit: VTItem | None = None, lower_bound: VTQuantityContent | None = None, upper_bound: VTQuantityContent | None = None ) -> None: super().__init__(amount, unit, lower_bound, upper_bound)
@override def _preprocess_arg(self, arg: Any, i: int) -> Any: if i == 1: # amount if isinstance(arg, Variable): return QuantityVariable.check(arg, type(self), None, i) else: return Quantity._static_preprocess_arg(self, arg, i) elif i == 2: # unit if isinstance(arg, Template): return ItemTemplate.check(arg, type(self), None, i) elif isinstance(arg, Variable): return ItemVariable.check(arg, type(self), None, i) else: return Quantity._static_preprocess_arg(self, arg, i) elif i == 3: # lower-bound if isinstance(arg, Variable): return QuantityVariable.check(arg, type(self), None, i) else: return Quantity._static_preprocess_arg(self, arg, i) elif i == 4: # upper-bound if isinstance(arg, Variable): return QuantityVariable.check(arg, type(self), None, i) else: return Quantity._static_preprocess_arg(self, arg, i) else: raise self._should_not_get_here() @property def amount(self) -> VQuantityContent: """The amount of quantity template.""" return self.get_amount()
[docs] def get_amount(self) -> VQuantityContent: """Gets the amount of quantity template Returns: Amount or quantity variable. """ return self.args[0]
@property def unit(self) -> VItem | None: """The unit of quantity template.""" return self.get_unit()
[docs] def get_unit(self, default: VItem | None = None) -> VItem | None: """Gets the unit of quantity template If the unit is ``None``, returns `default`. Parameters: default: Default unit. Returns: Unit, item template, or item variable. """ return self.get(1, default)
@property def lower_bound(self) -> VQuantityContent | None: """The lower bound of quantity template.""" return self.get_lower_bound()
[docs] def get_lower_bound( self, default: VQuantityContent | None = None ) -> VQuantityContent | None: """Gets the lower bound of quantity template If the lower bound is ``None``, returns `default`. Parameters: default: Default lower bound. Returns: Lower bound or quantity variable. """ return self.get(2, default)
@property def upper_bound(self) -> VQuantityContent | None: """The upper bound of quantity template.""" return self.get_upper_bound()
[docs] def get_upper_bound( self, default: VQuantityContent | None = None ) -> VQuantityContent | None: """Gets the upper bound of quantity template. If the upper bound is ``None``, returns `default`. Parameters: default: Default upper bound. Returns: Upper bound or quantity variable. """ return self.get(3, default)
[docs] class QuantityVariable(DeepDataValueVariable): """Quantity variable. Parameters: name: Name. """ object_class: ClassVar[type[Quantity]] # pyright: ignore
class QuantityDatatype(Datatype): """Quantity datatype.""" value_class: ClassVar[type[Quantity]] # pyright: ignore
[docs] class Quantity( DeepDataValue, datatype_class=QuantityDatatype, template_class=QuantityTemplate, variable_class=QuantityVariable ): """Quantity. Parameters: amount: Amount. unit: Unit. lower_bound: Lower bound. upper_bound: Upper bound. """ datatype_class: ClassVar[type[QuantityDatatype]] # pyright: ignore datatype: ClassVar[QuantityDatatype] # pyright: ignore template_class: ClassVar[type[QuantityTemplate]] # pyright: ignore variable_class: ClassVar[type[QuantityVariable]] # pyright: ignore
[docs] @classmethod @override def check( cls, arg: Any, function: Location | None = None, name: str | None = None, position: int | None = None ) -> Self: if isinstance(arg, cls): return arg elif isinstance(arg, (decimal.Decimal, float, int)): return cls(arg) elif isinstance(arg, str): try: return cls(arg) except ValueError as err: raise cls._check_error( arg, function, name, position, ValueError) from err elif isinstance(arg, String): return cls.check(arg.content, function, name, position) else: raise cls._check_error(arg, function, name, position)
[docs] def __init__( self, amount: VTQuantityContent, unit: VTItem | None = None, lower_bound: VTQuantityContent | None = None, upper_bound: VTQuantityContent | None = None): super().__init__(amount, unit, lower_bound, upper_bound)
@override def _preprocess_arg(self, arg: Any, i: int) -> Any: return self._static_preprocess_arg(self, arg, i) @staticmethod def _static_preprocess_arg(self_, arg: Any, i: int) -> Any: if i == 1 or i == 3 or i == 4: # amount, lower/upper_bound if arg is None and (i == 3 or i == 4): return None if isinstance(arg, Quantity): return arg.amount elif isinstance(arg, (decimal.Decimal, float, int, str)): try: return decimal.Decimal(arg) except decimal.InvalidOperation as err: raise Quantity._check_error( arg, type(self_), None, i, ValueError) from err else: raise Quantity._check_error(arg, type(self_), None, i) elif i == 2: # unit return Item.check_optional(arg, None, type(self_), None, i) else: raise self_._should_not_get_here() @property def amount(self) -> decimal.Decimal: """The amount of quantity.""" return self.get_amount()
[docs] def get_amount(self) -> decimal.Decimal: """Gets the amount of quantity. Returns: Amount. """ return self.args[0]
@property def unit(self) -> Item | None: """The unit of quantity.""" return self.get_unit()
[docs] def get_unit(self, default: Item | None = None) -> Item | None: """Gets the unit of quantity. If the unit is ``None``, returns `default`. Parameters: default: Default unit. Returns: Unit. """ return self.get(1, default)
@property def lower_bound(self) -> decimal.Decimal | None: """The lower bound of quantity.""" return self.get_lower_bound()
[docs] def get_lower_bound( self, default: decimal.Decimal | None = None ) -> decimal.Decimal | None: """Gets the lower bound of quantity. If the lower bound is ``None``, returns `default`. Parameters: default: Default lower bound. Returns: Lower bound. """ return self.get(2, default)
@property def upper_bound(self) -> decimal.Decimal | None: """The upper bound of quantity.""" return self.get_upper_bound()
[docs] def get_upper_bound( self, default: decimal.Decimal | None = None ) -> decimal.Decimal | None: """Gets the upper bound of quantity. If the upper bound is ``None``, returns `default`. Parameters: default: Default upper bound. Returns: Upper bound. """ return self.get(3, default)