"""
Contains a class that is able to provide any RC/FC Evaluator, HintsProvider or Package Resolver.
"""
from abc import ABC, abstractmethod
from typing import Optional, Union
from efoli import EdifactFormat, EdifactFormatVersion
from ahbicht.content_evaluation.evaluators import Evaluator
from ahbicht.content_evaluation.fc_evaluators import FcEvaluator
from ahbicht.content_evaluation.rc_evaluators import RcEvaluator
from ahbicht.expressions.hints_provider import HintsProvider
from ahbicht.expressions.package_expansion import PackageResolver
[docs]
class TokenLogicProvider(ABC):
"""
A TokenLogicProvider is a class that can provide you with the correct evaluator/hints provider/package resolver
for your use case/for any token in the parsed expression tree.
"""
[docs]
@abstractmethod
def get_rc_evaluator(self, edifact_format: EdifactFormat, format_version: EdifactFormatVersion) -> RcEvaluator:
"""
returns an appropriate RC Evaluator for the given edifact_format and edifact_format_version.
The implementing class shall raise a NotImplementedError and not return None.
"""
raise NotImplementedError("The inheriting sub class has to implement this method")
[docs]
@abstractmethod
def get_fc_evaluator(self, edifact_format: EdifactFormat, format_version: EdifactFormatVersion) -> FcEvaluator:
"""
returns an appropriate FC Evaluator for the given edifact_format and edifact_format_version.
The implementing class shall raise a NotImplementedError and not return None.
"""
raise NotImplementedError("The inheriting sub class has to implement this method")
[docs]
@abstractmethod
def get_hints_provider(
self, edifact_format: Optional[EdifactFormat] = None, format_version: Optional[EdifactFormatVersion] = None
) -> HintsProvider:
"""
returns an appropriate HintsProvider for the given edifact_format and edifact_format_version.
The implementing class shall raise a NotImplementedError and not return None.
"""
raise NotImplementedError("The inheriting sub class has to implement this method")
[docs]
@abstractmethod
def get_package_resolver(
self, edifact_format: Optional[EdifactFormat] = None, format_version: Optional[EdifactFormatVersion] = None
) -> PackageResolver:
"""
Returns an appropriate PackageResolver for the given edifact_format and edifact_format_version.
You can provide None as format(version) if there is only 1 package resolver available
The implementing class shall raise a NotImplementedError and not return None.
"""
raise NotImplementedError("The inheriting sub class has to implement this method")
[docs]
class SingletonTokenLogicProvider(TokenLogicProvider):
"""
An TokenLogicProvider that is instantiated with a list of evaluators/providers/resolvers of which the same instances
will be used during the entire uptime of the application (singleton style).
"""
_unknown_key = "undefined"
@staticmethod
def _to_key(edifact_format: Optional[EdifactFormat], format_version: Optional[EdifactFormatVersion]) -> str:
"""
because a tuple for format and format version is not hashable / usable as key in a dict this methods
converts them to a unique and hashable string
"""
if edifact_format is None or format_version is None:
return SingletonTokenLogicProvider._unknown_key
# we don't care what the key is, it just has to be unique and consistent
return f"{edifact_format}-{format_version}"
def __init__(self, inputs: list[Union[Evaluator, PackageResolver, HintsProvider]]) -> None:
self._rc_evaluators: dict[str, RcEvaluator] = {}
self._fc_evaluators: dict[str, FcEvaluator] = {}
self._hints_providers: dict[str, HintsProvider] = {}
self._package_resolvers: dict[str, PackageResolver] = {}
for instance in inputs:
key: str
try:
key = SingletonTokenLogicProvider._to_key(instance.edifact_format, instance.edifact_format_version)
except NotImplementedError:
# this is ok, if there's only one of the kind (e.g. only one package resolver, only one hints provider)
# if the user tries to provide more than 1 instance of the same kind without specifying format(version)
# they'll run into an value error below
key = SingletonTokenLogicProvider._unknown_key
target_dict: (
dict[str, RcEvaluator] | dict[str, FcEvaluator] | dict[str, HintsProvider] | dict[str, PackageResolver]
)
if isinstance(instance, RcEvaluator):
target_dict = self._rc_evaluators
elif isinstance(instance, FcEvaluator):
target_dict = self._fc_evaluators
elif isinstance(instance, HintsProvider):
target_dict = self._hints_providers
elif isinstance(instance, PackageResolver):
target_dict = self._package_resolvers
else:
raise ValueError(f"The type of '{instance}' is not supported. Expected either RC or FC evaluator")
if key in target_dict:
conflict = target_dict[key]
raise ValueError(
f"The key '{key}' is already used by {conflict}. For this reason you cannot add '{instance}'"
)
target_dict[key] = instance # type: ignore[assignment]
[docs]
def get_fc_evaluator(
self, edifact_format: Optional[EdifactFormat] = None, format_version: Optional[EdifactFormatVersion] = None
) -> FcEvaluator:
try:
return self._fc_evaluators[SingletonTokenLogicProvider._to_key(edifact_format, format_version)]
except KeyError as key_error:
raise NotImplementedError(
f"No FC Evaluator has been registered for {edifact_format} in {format_version}"
) from key_error
[docs]
def get_rc_evaluator(
self, edifact_format: Optional[EdifactFormat] = None, format_version: Optional[EdifactFormatVersion] = None
) -> RcEvaluator:
try:
return self._rc_evaluators[SingletonTokenLogicProvider._to_key(edifact_format, format_version)]
except KeyError as key_error:
raise NotImplementedError(
f"No RC Evaluator has been registered for {edifact_format} in {format_version}"
) from key_error
[docs]
def get_hints_provider(
self, edifact_format: Optional[EdifactFormat] = None, format_version: Optional[EdifactFormatVersion] = None
) -> HintsProvider:
try:
return self._hints_providers[SingletonTokenLogicProvider._to_key(edifact_format, format_version)]
except KeyError as key_error:
raise NotImplementedError(
f"No HintsProvider has been registered for {edifact_format} in {format_version}"
) from key_error
[docs]
def get_package_resolver(
self, edifact_format: Optional[EdifactFormat] = None, format_version: Optional[EdifactFormatVersion] = None
) -> PackageResolver:
try:
return self._package_resolvers[SingletonTokenLogicProvider._to_key(edifact_format, format_version)]
except KeyError as key_error:
raise NotImplementedError(
f"No PackageResolver has been registered for {edifact_format} in {format_version}"
) from key_error