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