added Registry class and instances() function, which allow to automatically register/show instances of a class (and optionally its subclasses); for convenience, also added a combined Registry and ABC base class
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
from abc import ABCMeta
|
||||
|
||||
from .registry import RegistryMeta
|
||||
|
||||
|
||||
def combine_classes(*bases):
|
||||
"""
|
||||
Convenience wrapper around type(name, bases, namespace)
|
||||
for dynamically combining bases into a single class
|
||||
"""
|
||||
name = "".join(a.__name__ for a in bases)
|
||||
namespace = {}
|
||||
return type(name, bases, namespace)
|
||||
|
||||
|
||||
|
||||
class RegistryABC(object, metaclass=combine_classes(RegistryMeta, ABCMeta)):
|
||||
"""
|
||||
Helper class that allows adding registry and ABC functionalities via inheritance
|
||||
Does nothing but set a combined RegistryMeta and ABCMeta as metaclass
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
## The above corresponds to:
|
||||
|
||||
#class RegistryMetaABCMeta(RegistryMeta, ABCMeta): # combine metaclasses
|
||||
# pass
|
||||
|
||||
#class RegistryABC(object, metaclass=RegistryMetaABCMeta):
|
||||
# pass
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import weakref
|
||||
|
||||
from .utils import typename
|
||||
|
||||
|
||||
class RegistryMeta(type):
|
||||
"""
|
||||
Metaclass containing the registry logic
|
||||
In most cases, it will be more convenient to inherit from Registry instead of using RegistryMeta directly
|
||||
"""
|
||||
|
||||
def __init__(cls, name, bases, namespace):
|
||||
super().__init__(name, bases, namespace) # creates the class
|
||||
cls.__instances__ = weakref.WeakSet()
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
inst = super().__call__(*args, **kwargs) # creates the instance (calls __new__ and __init__ methods)
|
||||
cls.__instances__.add(inst)
|
||||
return inst
|
||||
|
||||
|
||||
|
||||
class Registry(object, metaclass=RegistryMeta):
|
||||
"""
|
||||
Helper class that allows adding registry functionality via inheritance
|
||||
Does nothing but set RegistryMeta as metaclass
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def instances(reg, recursive=True):
|
||||
"""
|
||||
Return a set of all instances of reg
|
||||
If recursive=True also include instances of subclasses of reg
|
||||
"""
|
||||
res = _collect_instances(reg, recursive=recursive)
|
||||
return set(res)
|
||||
|
||||
|
||||
def _collect_instances(reg, recursive=True):
|
||||
"""
|
||||
Return a weakref.WeakSet of all instances of reg
|
||||
If recursive=True also include instances of subclasses of reg
|
||||
"""
|
||||
_check_registry(reg)
|
||||
instances = reg.__instances__.copy()
|
||||
if recursive:
|
||||
for subcls in reg.__subclasses__():
|
||||
instances |= _collect_instances(subcls, recursive=recursive)
|
||||
return instances
|
||||
|
||||
|
||||
def _check_registry(reg):
|
||||
"""
|
||||
Try whether reg has __instances__ attribute, if missing raise TypeError
|
||||
"""
|
||||
try:
|
||||
reg.__instances__
|
||||
except AttributeError as exc:
|
||||
tname = typename(reg)
|
||||
cname = reg.__name__
|
||||
raise TypeError(f"{tname} object '{cname}' has no registry functionality") from exc
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user