Skip to content

hololinked.core.meta.DescriptorRegistry

A registry for the descriptors of a Thing class or Thing instance. Provides a dictionary interface to access the descriptors under the descriptors attribute. Each of properties, actions and events subclasss from here to implement a registry of their available objects.

UML Diagram

Source code in hololinked\core\meta.py
class DescriptorRegistry:
    """
    A registry for the descriptors of a `Thing` class or `Thing` instance. 
    Provides a dictionary interface to access the descriptors under the `descriptors` attribute. 
    Each of properties, actions and events subclasss from here to implement a registry of their available objects. 

    [UML Diagram](http://localhost:8000/UML/PDF/DescriptorRegistry.pdf)
    """

    def __init__(self, owner_cls: ThingMeta, owner_inst = None) -> None:
        """
        Parameters
        ----------
        owner_cls: ThingMeta
            The class/subclass of the `Thing` that owns the registry.
        owner_inst: Thing
            The instance of the `Thing` that owns the registry, optional
        """
        super().__init__()
        self.owner_cls = owner_cls
        self.owner_inst = owner_inst
        self.clear() 


    @property
    def owner(self):
        """
        The owner of the registry - the instance of a `Thing` if a `Thing` has been instantiated 
        or the class/subclass of `Thing` when accessed as a class attribute.
        """
        return self.owner_inst if self.owner_inst is not None else self.owner_cls

    @property   
    def _qualified_prefix(self) -> str:
        """
        A unique prefix for `descriptors` attribute according to the `Thing`'s subclass and instance id. 
        For internal use. 
        """
        try: 
            return self._qualified__prefix
        except AttributeError:
            prefix = inspect.getfile(self.__class__) + self.__class__.__name__.lower()
            if self.owner_inst is not None:
                prefix += f'_{self.owner_inst.id}'
            self._qualified__prefix = prefix
            return prefix

    @property
    def descriptor_object(self) -> type[Property | Action | Event]:
        """The type of descriptor object that this registry holds, i.e. `Property`, `Action` or `Event`"""
        raise NotImplementedError("Implement descriptor_object in subclass")

    @property
    def descriptors(self) -> typing.Dict[str, type[Property | Action | Event]]:
        """A dictionary with all the descriptors as values and their names as keys."""
        raise NotImplementedError("Implement descriptors in subclass")

    @property
    def names(self) -> typing.KeysView[str]:
        """The names of the descriptors objects as a dictionary key view"""
        return self.descriptors.keys()

    @property
    def values(self) -> typing.Dict[str, typing.Any]: 
        """
        The values contained within the descriptors after reading when accessed at instance level, otherwise,
        the descriptor objects as dictionary when accessed at class level.
        """
        raise NotImplementedError("Implement values in subclass")

    def clear(self) -> None:
        """
        Deletes the descriptors dictionary (value of the `descriptors` proeprty) so that it can be recreated. 
        Does not delete the descriptors themselves. Call this method once if new descriptors are added to the 
        class/instance dynamically in runtime.
        """
        for name in ['', '_values']:
            try:
                delattr(self, f'_{self._qualified_prefix}_{self.__class__.__name__.lower()}{name}')    
            except AttributeError:
                pass

    def __getitem__(self, key: str) -> Property | Action | Event:
        """Returns the descriptor object for the given key."""
        raise NotImplementedError("Implement __getitem__ in subclass")

    def __contains__(self, obj: Property | Action | Event) -> bool:
        """Returns True if the descriptor object is in the descriptors dictionary."""
        raise NotImplementedError("contains not implemented yet")

    def __dir__(self) -> typing.List[str]:
        """Adds descriptor object to the dir"""
        return super().__dir__() + self.descriptors.keys() # type: ignore

    def __iter__(self):
        """Iterates over the descriptors of this object."""
        yield from self.descriptors

    def __len__(self) -> int:
        """The number of descriptors in this object."""
        return len(self.descriptors)

    def __hash__(self) -> int:
        return hash(self._qualified__prefix)

    def __str__(self) -> int:
        if self.owner_inst:
            return f"<DescriptorRegistry({self.owner_cls.__name__}({self.owner_inst.id}))>"
        return f"<DescriptorRegistry({self.owner_cls.__name__})>"

    def get_descriptors(self, recreate: bool = False) -> typing.Dict[str, Property | Action | Event]:
        """
        a dictionary with all the descriptors as values and their names as keys.

        Parameters
        ----------
        recreate: bool
            if True, the descriptors dictionary is recreated and returned, otherwise, the cached dictionary is returned.
        """
        if recreate:
            self.clear()
        try:
            return getattr(self, f'_{self._qualified_prefix}_{self.__class__.__name__.lower()}')
        except AttributeError:
            descriptors = dict()
            for name, objekt in inspect._getmembers(
                        self.owner_cls, 
                        lambda f: isinstance(f, self.descriptor_object),
                        getattr_without_descriptor_read
                    ): 
                descriptors[name] = objekt
            setattr(self, f'_{self._qualified_prefix}_{self.__class__.__name__.lower()}', descriptors)
            # We cache the parameters because this method is called often,
            # and parameters are rarely added (and cannot be deleted)      
            return descriptors

    def get_values(self) -> typing.Dict[str, typing.Any]:
        """
        the values contained within the descriptors after reading when accessed at instance level, otherwise, 
        the descriptor objects as dictionary when accessed at class level.
        For example, if a `Thing` instance's property contains a value of 5, this method will return 
        { property_name : 5 } when accessed at instance level, and { property_name : property_object } when accessed 
        at class level.   
        This method is also the getter of the `values` property.
        """
        if self.owner_inst is None: 
            return self.descriptors
        try:
            return getattr(self, f'_{self._qualified_prefix}_{self.__class__.__name__.lower()}_values')
        except AttributeError:
            values = dict()
            for name, value in self.descriptors.items():
                values[name] = value.__get__(self.owner_inst, self.owner_cls)
            setattr(self, f'_{self._qualified_prefix}_{self.__class__.__name__.lower()}_values', values)
            return values

Attributes

owner property

owner

The owner of the registry - the instance of a Thing if a Thing has been instantiated or the class/subclass of Thing when accessed as a class attribute.

descriptor_object property

descriptor_object: type[Property | Action | Event]

The type of descriptor object that this registry holds, i.e. Property, Action or Event

descriptors property

descriptors: Dict[str, type[Property | Action | Event]]

A dictionary with all the descriptors as values and their names as keys.

names property

names: KeysView[str]

The names of the descriptors objects as a dictionary key view

values property

values: Dict[str, Any]

The values contained within the descriptors after reading when accessed at instance level, otherwise, the descriptor objects as dictionary when accessed at class level.

Functions

__init__

__init__(owner_cls: ThingMeta, owner_inst=None) -> None

Parameters:

Name Type Description Default

owner_cls

ThingMeta

The class/subclass of the Thing that owns the registry.

required

owner_inst

The instance of the Thing that owns the registry, optional

None
Source code in hololinked\core\meta.py
def __init__(self, owner_cls: ThingMeta, owner_inst = None) -> None:
    """
    Parameters
    ----------
    owner_cls: ThingMeta
        The class/subclass of the `Thing` that owns the registry.
    owner_inst: Thing
        The instance of the `Thing` that owns the registry, optional
    """
    super().__init__()
    self.owner_cls = owner_cls
    self.owner_inst = owner_inst
    self.clear() 

clear

clear() -> None

Deletes the descriptors dictionary (value of the descriptors proeprty) so that it can be recreated. Does not delete the descriptors themselves. Call this method once if new descriptors are added to the class/instance dynamically in runtime.

Source code in hololinked\core\meta.py
def clear(self) -> None:
    """
    Deletes the descriptors dictionary (value of the `descriptors` proeprty) so that it can be recreated. 
    Does not delete the descriptors themselves. Call this method once if new descriptors are added to the 
    class/instance dynamically in runtime.
    """
    for name in ['', '_values']:
        try:
            delattr(self, f'_{self._qualified_prefix}_{self.__class__.__name__.lower()}{name}')    
        except AttributeError:
            pass

get_descriptors

get_descriptors(recreate: bool = False) -> typing.Dict[str, Property | Action | Event]

a dictionary with all the descriptors as values and their names as keys.

Parameters:

Name Type Description Default

recreate

bool

if True, the descriptors dictionary is recreated and returned, otherwise, the cached dictionary is returned.

False
Source code in hololinked\core\meta.py
def get_descriptors(self, recreate: bool = False) -> typing.Dict[str, Property | Action | Event]:
    """
    a dictionary with all the descriptors as values and their names as keys.

    Parameters
    ----------
    recreate: bool
        if True, the descriptors dictionary is recreated and returned, otherwise, the cached dictionary is returned.
    """
    if recreate:
        self.clear()
    try:
        return getattr(self, f'_{self._qualified_prefix}_{self.__class__.__name__.lower()}')
    except AttributeError:
        descriptors = dict()
        for name, objekt in inspect._getmembers(
                    self.owner_cls, 
                    lambda f: isinstance(f, self.descriptor_object),
                    getattr_without_descriptor_read
                ): 
            descriptors[name] = objekt
        setattr(self, f'_{self._qualified_prefix}_{self.__class__.__name__.lower()}', descriptors)
        # We cache the parameters because this method is called often,
        # and parameters are rarely added (and cannot be deleted)      
        return descriptors

get_values

get_values() -> typing.Dict[str, typing.Any]

the values contained within the descriptors after reading when accessed at instance level, otherwise, the descriptor objects as dictionary when accessed at class level. For example, if a Thing instance's property contains a value of 5, this method will return { property_name : 5 } when accessed at instance level, and { property_name : property_object } when accessed at class level.
This method is also the getter of the values property.

Source code in hololinked\core\meta.py
def get_values(self) -> typing.Dict[str, typing.Any]:
    """
    the values contained within the descriptors after reading when accessed at instance level, otherwise, 
    the descriptor objects as dictionary when accessed at class level.
    For example, if a `Thing` instance's property contains a value of 5, this method will return 
    { property_name : 5 } when accessed at instance level, and { property_name : property_object } when accessed 
    at class level.   
    This method is also the getter of the `values` property.
    """
    if self.owner_inst is None: 
        return self.descriptors
    try:
        return getattr(self, f'_{self._qualified_prefix}_{self.__class__.__name__.lower()}_values')
    except AttributeError:
        values = dict()
        for name, value in self.descriptors.items():
            values[name] = value.__get__(self.owner_inst, self.owner_cls)
        setattr(self, f'_{self._qualified_prefix}_{self.__class__.__name__.lower()}_values', values)
        return values

dunder

Apart from the above __getitem__, __contains__, __dir__, __iter__, __len__, __hash__, __str__ are supported