utils.class_registry_singleton — Framework for swappable classes¶
What is this for?¶
If you are creating a library where you need to enable apps using the library to replace or add some classes with injection. There are two main use-cases:
You have a choice field, and you want to bind the choices to values backed by classes (for validation, etc.), AND you want apps using the library to be able to add more choices and/or replace the default choices.
You have some classes, such as adapters working with varying user models, and you need to be able to allow apps to inject their own implementations.
See ievv_opensource.utils.class_registry_singleton.ClassRegistrySingleton
examples.
API docs¶
-
exception
ievv_opensource.utils.class_registry_singleton.
DuplicateKeyError
(registry, key)[source]¶ Raised when adding a key already in a
ClassRegistrySingleton
-
class
ievv_opensource.utils.class_registry_singleton.
AbstractRegistryItem
[source]¶ Base class for
ClassRegistrySingleton
items.-
classmethod
on_add_to_registry
(registry)[source]¶ Called automatically when the class is added to a
ClassRegistrySingleton
.
-
classmethod
on_remove_from_registry
(registry)[source]¶ Called automatically when the class is removed from a
ClassRegistrySingleton
.
-
classmethod
-
class
ievv_opensource.utils.class_registry_singleton.
RegistryItemWrapper
(cls, default_instance_kwargs)[source]¶ Registry item wrapper.
When you add a
AbstractRegistryItem
to aClassRegistrySingleton
, it is stored as an instance of this class.You can use a subclass of this class with your registry singleton by overriding
ClassRegistrySingleton.get_registry_item_wrapper_class()
. This enables you to store extra metadata along with your registry items, and provided extra helper methods.-
cls
= None¶ The
AbstractRegistryItem
class.
-
default_instance_kwargs
= None¶ The default kwargs for instance created with
get_instance()
.
-
make_instance_kwargs
(kwargs)[source]¶ Used by
get_instance()
to mergekwargs
withdefault_instance_kwargs
.- Returns
The full kwargs fo the instance.
- Return type
-
get_instance
(**kwargs)[source]¶ Get an instance of the
AbstractRegistryItem
class initialized with the provided**kwargs
.The provided
**kwargs
is merged with thedefault_instance_kwargs
, with**kwargs
overriding any keys also indefault_instance_kwargs
.- Parameters
**kwargs – Kwargs for the class constructor.
- Returns
A class instance.
- Return type
-
-
class
ievv_opensource.utils.class_registry_singleton.
ClassRegistrySingleton
[source]¶ Base class for class registry singletons - for having a singleton of swappable classes. Useful when creating complex libraries with classes that the apps using the libraries should be able to swap out with their own classes.
Example:
class AbstractMessageGenerator(class_registry_singleton.AbstractRegistryItem): def get_message(self): raise NotImplementedError() class SimpleSadMessageGenerator(AbstractMessageGenerator): @classmethod def get_registry_key(cls): return 'sad' def get_message(self): return 'A sad message' class ComplexSadMessageGenerator(AbstractMessageGenerator): @classmethod def get_registry_key(cls): return 'sad' def get_message(self): return random.choice([ 'Most people are smart, but 60% of people think they are smart.', 'Humanity will probably die off before we become a multi-planet spiecies.', 'We could feed everyone in the world - if we just bothered to share resources.', ]) class SimpleHappyMessageGenerator(AbstractMessageGenerator): @classmethod def get_registry_key(cls): return 'happy' def get_message(self): return 'A happy message' class ComplexHappyMessageGenerator(AbstractMessageGenerator): @classmethod def get_registry_key(cls): return 'happy' def get_message(self): return random.choice([ 'Almost every person you will ever meet are good people.', 'You will very likely live to see people land on mars.', 'Games are good now - just think how good they will be in 10 years!', ]) class MessageGeneratorSingleton(class_registry_singleton.ClassRegistrySingleton): '''' We never use ClassRegistrySingleton directly - we always create a subclass. This is because of the nature of singletons. If you ise ClassRegistrySingleton directly as your singleton, everything added to the registry would be in THE SAME singleton. '''' class DefaultAppConfig(AppConfig): def ready(self): registry = MessageGeneratorSingleton.get_instance() registry.add(SimpleSadMessageGenerator) registry.add(SimpleHappyMessageGenerator) class SomeCustomAppConfig(AppConfig): def ready(self): registry = MessageGeneratorSingleton.get_instance() registry.add_or_replace(ComplexSadMessageGenerator) registry.add_or_replace(ComplexHappyMessageGenerator) # Using the singleton in code registry = MessageGeneratorSingleton.get_instance() print(registry.get_registry_item_instance('sad').get_message()) print(registry.get_registry_item_instance('happy').get_message())
Ensures there is only one instance created. Make sure to use super() in subclasses.
-
get_registry_item_wrapper_class
()[source]¶ Get the registry item wrapper class.
Defaults to
RegistryItemWrapper
which should work well for most use cases.
-
get
(key, fallback=None)[source]¶ Get a class (wrapper) stored in the registry by its key.
- Parameters
key (str) – A registry item class key.
fallback – Fallback value of the
key
is not in the registry
Returns:
- Returns
You can use this to get the class or to get an instance of the class.
- Return type
-
items
()[source]¶ Iterate over all the items in the registry yielding (key, RegistryItemWrapper) tuples.
-
iterwrappers
()[source]¶ Iterate over all the items in the registry yielding RegistryItemWrapper objects.
-
iterchoices
()[source]¶ Iterate over the the classes in the in the registry yielding two-value tuples where both values are the
get_registry_key()
.Useful when rendering in a ChoiceField.
- Returns
An iterator that yields
(<key>, <key>)
tuples for eachAbstractRegistryItem
in the registry. The iterator is sorted byget_registry_key()
.
-
add
(cls, **default_instance_kwargs)[source]¶ Add the provided
cls
to the registry. :param cls: AAbstractRegistryItem
class (NOT AN OBJECT/INSTANCE). :param **default_instance_kwargs: Default instance kwargs.- Raises
DuplicateKeyError – When a class with the same
:raises
get_registry_key()
is already: :raises in the registry.:
-
add_or_replace
(cls, **default_instance_kwargs)[source]¶ Insert the provided
cls
in registry. If anothercls
is already registered with the samekey
, this will be replaced.- Parameters
cls – A
AbstractRegistryItem
class (NOT AN OBJECT/INSTANCE).**default_instance_kwargs – Default instance kwargs.
-
replace
(cls, **default_instance_kwargs)[source]¶ Replace the class currently in the registry with the same key as the provided
cls.get_registry_key()
.- Parameters
cls – A
AbstractRegistryItem
class (NOT AN OBJECT/INSTANCE).**default_instance_kwargs – Default instance kwargs.
- Raises
KeyError – If the
cls.get_registry_key()
is NOT in the registry.
-
remove
(key)[source]¶ Remove the class provided
key
from the registry.- Parameters
key (str) – A
:param
get_registry_key()
.:- Raises
KeyError – When the
key
is not in the registry.
-