Source code for metaclass
#
##
## SPDX-FileCopyrightText: © 2007-2024 Benedict Verhegghe <bverheg@gmail.com>
## SPDX-License-Identifier: GPL-3.0-or-later
##
## This file is part of pyFormex 3.5 (Thu Feb 8 19:11:13 CET 2024)
## pyFormex is a tool for generating, manipulating and transforming 3D
## geometrical models by sequences of mathematical operations.
## Home page: https://pyformex.org
## Project page: https://savannah.nongnu.org/projects/pyformex/
## Development: https://gitlab.com/bverheg/pyformex
## Distributed under the GNU General Public License version 3 or later.
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see http://www.gnu.org/licenses/.
##
"""Metaclasses
This module defines some metaclasses for constructing application classes
with specialized behavior. Currently we have metaclasses to construct
singleton classes and classes with an instance registry.
"""
[docs]class SingletonMeta(type):
"""Metaclass for creating singleton classes
Singleton classes have only a single instance. Every creation of a new
instance just returns the same instance.
Examples
--------
>>> class SingletonClass(metaclass=SingletonMeta): pass
>>> a = SingletonClass()
>>> b = SingletonClass()
>>> print(a is b)
True
"""
_instances = {}
def __call__(cls, *args, **kargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kargs)
return cls._instances[cls]
[docs]class RegistryMeta(type):
"""Metaclass for creating registry classes.
Registry classes register their instances in a dict. The first argument
of the __init__ function must be the key to use for registering the
instance. By default the class will raise an exception when trying to
create an instance with an already registered key. This can be silenced
by passing a parameter ``raise_exist=False`` to the metaclass (see examples).
Registered instances are persistent: they have to be explicitely deleted
from the registry dict.
Examples
--------
>>> class RegistryClass(metaclass=RegistryMeta):
... def __init__(self, key):
... pass
>>> a = RegistryClass('a')
>>> b = RegistryClass('a')
Traceback (most recent call last):
...
ValueError: RegistryClass('a') already exists
>>> b = RegistryClass('b')
>>> print(a is b)
False
>>> class SilentRegistryClass(metaclass=RegistryMeta, raise_exist=False):
... def __init__(self, key):
... pass
>>> a = SilentRegistryClass('a')
>>> b = SilentRegistryClass('a')
>>> a is b
True
"""
def __new__(mcs, name, bases, namespace, raise_exist=True):
"""Create the class"""
clas = super().__new__(mcs, name, bases, namespace)
clas._registry = {}
clas._raise = raise_exist
return clas
def __call__(cls, key, *args, **kargs):
"""Create an instance of cls with given key"""
if key not in cls._registry:
cls._registry[key] = super().__call__(key, *args, **kargs)
elif cls._raise:
raise ValueError(f"{cls.__name__}({key!r}) already exists")
return cls._registry[key]
def __getitem__(cls, key):
"""Return the instance of cls with given key"""
return cls._registry[key]
@property
def dict(cls):
"""Return the cls registry as a dict"""
return cls._registry
# End