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