Source code for qupulse.hardware.feature_awg.base_features

from types import MappingProxyType
from typing import Callable, Generic, Mapping, Optional, Type, TypeVar
from abc import ABC

__all__ = ["Feature", "FeatureAble"]


[docs]class Feature: """ Base class for features of :class:`.FeatureAble`. """ def __init__(self, target_type: Type["FeatureAble"]): self._target_type = target_type @property def target_type(self) -> Type["FeatureAble"]: return self._target_type
FeatureType = TypeVar("FeatureType", bound=Feature) GetItemFeatureType = TypeVar("GetItemFeatureType", bound=Feature)
[docs]class FeatureAble(Generic[FeatureType]): """ Base class for all types that are able to handle features. The features are saved in a dictionary and the methods can be called with the `__getitem__`-operator. """ def __init__(self): super().__init__() self._features = {} def __getitem__(self, feature_type: Type[GetItemFeatureType]) -> GetItemFeatureType: if isinstance(feature_type, str): return self._features[feature_type] if not isinstance(feature_type, type): raise TypeError("Expected type-object as key, got \"{ftt}\" instead".format( ftt=type(feature_type).__name__)) key_type = _get_base_feature_type(feature_type) if key_type is None: raise TypeError("Unexpected type of feature: {ft}".format(ft=feature_type.__name__)) if key_type not in self._features: raise KeyError("Could not get feature for type: {ft}".format(ft=feature_type.__name__)) return self._features[key_type]
[docs] def add_feature(self, feature: FeatureType) -> None: """ The method adds the feature to a Dictionary with all features Args: feature: A certain feature which functions should be added to the dictionary _features """ feature_type = _get_base_feature_type(type(feature)) if feature_type is None: raise TypeError("Unexpected type of feature: {ft}".format(ft=type(feature).__name__)) if not isinstance(self, feature.target_type): raise TypeError("Features with type \"{ft}\" belong to \"{tt}\"-objects".format( ft=type(feature).__name__, tt=feature.target_type.__name__)) if feature_type in self._features: raise KeyError(self, "Feature with type \"{ft}\" already exists".format(ft=feature_type.__name__)) self._features[feature_type] = feature # Also adding the feature with the string as the key. With this you can you the name as a string for __getitem__ self._features[feature_type.__name__] = feature
@property def features(self) -> Mapping[FeatureType, Callable]: """Returns the dictionary with all features of a FeatureAble""" return MappingProxyType(self._features)
def _get_base_feature_type(feature_type: Type[Feature]) -> Type[Optional[Feature]]: """ This function searches for the second inheritance level under `Feature` (i.e. level under `AWGDeviceFeature`, `AWGChannelFeature` or `AWGChannelTupleFeature`). This is done to ensure, that nobody adds the same feature twice, but with a type of a different inheritance level as key. Args: feature_type: Type of the feature Returns: Base type of the feature_type, two inheritance levels under `Feature` """ if not issubclass(feature_type, Feature): return type(None) # Search for base class on the inheritance line of Feature for base in feature_type.__bases__: if issubclass(base, Feature): result_type = base break else: return type(None) if Feature in result_type.__bases__: return feature_type else: return _get_base_feature_type(result_type)