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:

  1. 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.

  2. 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.

class ievv_opensource.utils.class_registry_singleton.RegistryItemWrapper(cls, default_instance_kwargs)[source]

Registry item wrapper.

When you add a AbstractRegistryItem to a ClassRegistrySingleton, 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 merge kwargs with default_instance_kwargs.

Returns

The full kwargs fo the instance.

Return type

dict

get_instance(**kwargs)[source]

Get an instance of the AbstractRegistryItem class initialized with the provided **kwargs.

The provided **kwargs is merged with the default_instance_kwargs, with **kwargs overriding any keys also in default_instance_kwargs.

Parameters

**kwargs – Kwargs for the class constructor.

Returns

A class instance.

Return type

AbstractRegistryItem

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

RegistryItemWrapper

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 each AbstractRegistryItem in the registry. The iterator is sorted by get_registry_key().

add(cls, **default_instance_kwargs)[source]

Add the provided cls to the registry. :param cls: A AbstractRegistryItem 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 another cls is already registered with the same key, 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.

remove_if_in_registry(key)[source]

Works just like remove(), but if the key is not in the registry this method just does nothing instead of raising KeyError.

get_registry_item_instance(key, **kwargs)[source]

Just a shortcut for singleton[key].get_instance(**kwargs).