Source code for popsynth.utils.meta
from typing import Dict, Optional, Union
from popsynth.utils.logging import setup_logger
log = setup_logger(__name__)
[docs]class Parameter(object):
[docs]    def __init__(
        self,
        default: Optional[float] = None,
        vmin: Optional[float] = None,
        vmax: Optional[float] = None,
        free: bool = True,
        is_normalization: bool = False,
    ):
        """
        Parameter base class.
        :param default: Default parameter value
        :type default: Optional[float]
        :param vmin: Minimum parameter value
        :type vmin: Optional[float]
        :param vmax: Maximum parameter value
        :type vmax: Optional[float]
        :param free: If `True`, parameter is free
        """
        self.name: Optional[float] = None
        self._vmin: Optional[float] = vmin  # type: Optional[float]
        self._vmax: Optional[float] = vmax  # type: Optional[float]
        self._default: Optional[float] = default  # type: Optional[float]
        self._free: bool = free
        self._is_normalization: bool = is_normalization
    @property
    def default(self) -> Optional[float]:
        return self._default
    @property
    def is_normalization(self) -> bool:
        return self._is_normalization
    def __get__(self, obj, type=None) -> object:
        if self._is_normalization:
            obj._normalization_parameter = self.name
        try:
            if obj._parameter_storage[self.name] is None:
                obj._parameter_storage[self.name] = self._default
            assert (
                obj._parameter_storage[self.name] is not None
            ), "parameters must have values!"
            return obj._parameter_storage[self.name]
        except (KeyError):
            obj._parameter_storage[self.name] = self._default
        assert (
            obj._parameter_storage[self.name] is not None
        ), "parameters must have values!"
        return obj._parameter_storage[self.name]
    def __set__(self, obj, value) -> None:
        self._is_set = True
        if self._is_normalization:
            obj._normalization_parameter = self.name
        if not self._free:
            log.error(f"{self.name} is fixed and cannot be set")
            raise RuntimeError()
        if self._vmin is not None:
            if not (value >= self._vmin):
                log.error(
                    f"trying to set {self.name} to a value below {self._vmin} is not allowed"
                )
                raise RuntimeError()
        if self._vmax is not None:
            if not (value <= self._vmax):
                log.error(
                    f"trying to set {self.name} to a value above {self._vmax} is not allowed"
                )
                raise RuntimeError()
        obj._parameter_storage[self.name] = value
        # Define property "free"
    def _set_free(self, value=True):
        self._free = value
    def _get_free(self):
        return self._free
    free = property(
        _get_free,
        _set_free,
        doc="Gets or sets whether the parameter is free or not. Use booleans, like: 'p.free = True' "
        " or 'p.free = False'. ",
    )
    # Define property "fix"
    def _set_fix(self, value=True):
        self._free = not value
    def _get_fix(self):
        return not self._free
    fix = property(
        _get_fix,
        _set_fix,
        doc="Gets or sets whether the parameter is fixed or not. Use booleans, like: 'p.fix = True' "
        " or 'p.fix = False'. ",
    )
[docs]class ParameterMeta(type):
    def __new__(mcls, name, bases, attrs, **kwargs):
        cls = super().__new__(mcls, name, bases, attrs, **kwargs)
        # Compute set of abstract method names
        abstracts = {
            name
            for name, value in attrs.items()
            if getattr(value, "__isabstractmethod__", False)
        }
        for base in bases:
            for name in getattr(base, "__abstractmethods__", set()):
                value = getattr(cls, name, None)
                if getattr(value, "__isabstractmethod__", False):
                    abstracts.add(name)
        cls.__abstractmethods__ = frozenset(abstracts)
        # parameters
        cls._parameter_storage = {}
        norm_counter = 0
        norm = None
        for k, v in attrs.items():
            if isinstance(v, Parameter):
                v.name = k
                if v.is_normalization:
                    if norm_counter > 0:
                        raise AssertionError("Can only have 1 normalization")
        return cls
    def __subclasscheck__(cls, subclass):
        """Override for issubclass(subclass, cls)."""
        if not isinstance(subclass, type):
            raise TypeError("issubclass() arg 1 must be a class")
        # Check cache
        # Check the subclass hook
        ok = cls.__subclasshook__(subclass)
        if ok is not NotImplemented:
            assert isinstance(ok, bool)
            if ok:
                cls._abc_cache.add(subclass)
            else:
                cls._abc_negative_cache.add(subclass)
            return ok
        # Check if it's a direct subclass
        if cls in getattr(subclass, "__mro__", ()):
            cls._abc_cache.add(subclass)
            return True
        # Check if it's a subclass of a registered class (recursive)
        for rcls in cls._abc_registry:
            if issubclass(subclass, rcls):
                cls._abc_cache.add(subclass)
                return True
        # Check if it's a subclass of a subclass (recursive)
        for scls in cls.__subclasses__():
            if issubclass(subclass, scls):
                cls._abc_cache.add(subclass)
                return True
        # No dice; update negative cache
        cls._abc_negative_cache.add(subclass)
        return False