software_patterns package

software_patterns.memoize module

Implementation of the Memoize Software Design Pattern.

Memoize is implemented using an Object Pool which is queried by a key which is the result of computing a hash given runtime arguments.

class software_patterns.memoize.ObjectsPool(callback: Callable[[...], ObjectType], hash_callback: Optional[Callable[[...], Union[int, str]]] = None)[source]

Bases: Generic[ObjectType]

Cache objects and allow to query (the pool) using runtime arguments.

Instances of the ObjectsPool class implement the Object Pool Software Design Creational Pattern.

Whenever an object is requested, it is checked whether it exists in the pool by using the runtimetime arguments to query a python dictionary. If it exists, a reference is returned, otherwise a new object is constructed (given the provided callback) and its reference is returned.

Example

>>> from software_patterns import ObjectsPool
>>> class ClientClass:
...  def __init__(self, a: int, b: int):
...   pass
>>> object_pool = ObjectsPool[ClientClass](ClientClass)
>>> obj1 = object_pool.get_object(1, 2)
>>> obj2 = object_pool.get_object(1, 3)
>>> obj3 = object_pool.get_object(1, 2)
>>> id(obj1) == id(obj3)
True
>>> len(object_pool._objects)
2
Parameters
  • callback (Callable[..., ObjectType]) – constructs objects given arguments

  • hash_callback (Optional[RuntimeBuildHashCallable], optional) – option to overide the default hash key computer. Defaults to None.

Returns

[description]

Return type

[type]

get_object(*args: Any, **kwargs: Any) ObjectType[source]

Request an object from the pool.

Get or create an object given the input arguments, which are used to create a unique hash key. The key is used to query a python dictionary and determine whether the object request refers to a cached object.

Returns

the reference to the object that corresponds to the input arguments, regardless of whether it was found in the pool or not

Return type

object (ObjectType)

user_supplied_callback: Dict[bool, Callable] = {False: <function ObjectsPool.<lambda>>, True: <function ObjectsPool.<lambda>>}

software_patterns.notification module

Notification-Listener (aka subject-observer) software design pattern.

Simple implementation of the subject/observers (broadcast/listeners) pattern, exposed as python classes.

This is a Behavioural Pattern which can be used when you want one or more components to be notified and react accordingly, when ‘something happens’.

One entity, known as Subject, is responsible to send out a notification and each entity “subscribed” to the Subject receives it and reacts. Each subscribed entity is known as a Listener or Observer.

The idea is that the Subject is agnostic of its Observers implementation and the client code can “attach” or “detach” (subscribe/unsubscribe) as many of them at runtime.

This module provides the Observer class, serving as the interface that needs to be implemented by concrete classes; the update method needs to be overrode. Concrete Observers react to the notifications/updates issued by the Subject they had been attached/subscribed to.

This module also provides the concrete Subject class, serving with methods to subscribe/unsubscribe (attach/detach) observers and also with a method to “notify” all Observers.

class software_patterns.notification.Observer[source]

Bases: ObserverInterface, ABC

class software_patterns.notification.Subject(*args, **kwargs)[source]

Bases: SubjectInterface, Generic[StateType]

Concrete Subject which owns an important state and notifies observers.

The subject can be used to build the data encapsulating the event being broadcasted.

Both the _state and _observers attributes have a simple implementation, but can be overrode to accommodate for more complex scenarios.

The observers/subscribers are implemented as a python list. In more complex scenarios, the list of subscribers can be stored more comprehensively (categorized by event type, etc.).

The subscription management methods provided are ‘attach’, ‘detach’ (as in the SubjectInterface) and ‘add’, which attached multiple observers at once.

Example

>>> from software_patterns import Subject, Observer
>>> broadcaster = Subject()
>>> class ObserverTypeA(Observer):
...  def update(self, *args, **kwargs):
...   event = args[0].state
...   print(f'observer-type-a reacts to event {event}')
>>> class ObserverTypeB(Observer):
...  def update(self, *args, **kwargs):
...   event = args[0].state
...   print(f'observer-type-b reacts to event {event}')
>>> subscriber_1 = ObserverTypeA()
>>> subscriber_2 = ObserverTypeB()
>>> broadcaster.add(subscriber_2, subscriber_1)
>>> broadcaster.state = 'event-object-A'
>>> broadcaster.notify()
observer-type-b reacts to event event-object-A
observer-type-a reacts to event event-object-A
>>> broadcaster.detach(subscriber_2)
>>> broadcaster.state = 'event-object-B'
>>> broadcaster.notify()
observer-type-a reacts to event event-object-B
add(*observers)[source]

Subscribe multiple observers at once.

attach(observer: ObserverInterface) None[source]

Attach an observer to the subject; subscribe the observer.

detach(observer: ObserverInterface) None[source]

Detach an observer from the subject; unsubscribe the observer.

notify() None[source]

Notify all observers about an event.

property state: Optional[StateType]

Get the state of the Subject.

Returns

the object representing the current state of the Subject

Return type

StateType

software_patterns.proxy module

Proxy structural software pattern.

This module contains boiler-plate code to supply the Proxy structural software design pattern, to the client code.

class software_patterns.proxy.Proxy(runtime_proxy: T)[source]

Bases: ProxySubjectInterface, Generic[T]

The Proxy has an interface identical to the ProxySubject.

Example

>>> from software_patterns import Proxy
>>> class PlusTen(Proxy):
...  def __call__(self, x: int):
...   result = self._proxy_subject(x + 10)
...   return result
>>> def plus_one(x: int):
...  return x + 1
>>> plus_one(2)
3
>>> proxy = PlusTen(plus_one)
>>> proxy(2)
13

software_patterns.subclass_registry module

Exposes the SubclassRegistry that allows to define a single registration point of one or more subclasses of a (common parent) class.

exception software_patterns.subclass_registry.InstantiationError[source]

Bases: Exception

class software_patterns.subclass_registry.SubclassRegistry(*args)[source]

Bases: type, Generic[T]

Subclass Registry

A (parent) class using this class as metaclass gains the ‘subclasses’ class attribute as well as the ‘create’ and ‘register_as_subclass’ class methods.

The ‘subclasses’ attribute is a python dictionary having string identifiers as keys and subclasses of the (parent) class as values.

The ‘register_as_subclass’ class method can be used as a decorator to indicate that a (child) class should belong in the parent’s class registry. An input string argument will be used as the unique key to register the subclass.

The ‘create’ class method can be invoked with a (string) key and suitable constructor arguments to later construct instances of the corresponding child class.

Example

>>> from software_patterns import SubclassRegistry
>>> class ClassRegistry(metaclass=SubclassRegistry):
...  pass
>>> ClassRegistry.subclasses
{}
>>> @ClassRegistry.register_as_subclass('child')
... class ChildClass:
...  def __init__(self, child_attribute):
...   self.attr = child_attribute
>>> child_instance = ClassRegistry.create('child', 'attribute-value')
>>> child_instance.attr
'attribute-value'
>>> type(child_instance).__name__
'ChildClass'
>>> isinstance(child_instance, ChildClass)
True
>>> {k: v.__name__ for k, v in ClassRegistry.subclasses.items()}
{'child': 'ChildClass'}
create(subclass_identifier, *args, **kwargs) T[source]

Create an instance of a registered subclass, given its unique identifier and runtime (constructor) arguments.

Invokes the identified subclass constructor passing any supplied arguments. The user needs to know the arguments to supply depending on the resulting constructor signature.

Parameters

subclass_identifier (str) – the unique identifier under which to look for the corresponding subclass

Raises
  • UnknownClassError – In case the given identifier is unknown to the parent class

  • InstantiationError – In case the runtime args and kwargs do not match the constructor signature

Returns

the instance of the registered subclass

Return type

object

register_as_subclass(subclass_identifier)[source]

Register a class as subclass of the parent class.

Adds the subclass’ constructor in the registry (dict) under the given (str) identifier. Overrides the registry in case of “identifier collision”. Can be used as a python decorator.

Parameters

subclass_identifier (str) – the user-defined identifier, under which to register the subclass

subclasses: Dict[str, type]
exception software_patterns.subclass_registry.UnknownClassError[source]

Bases: Exception