Base class for all factories.
Creates an instance of something for the given arguments. If the class that we shall instantiate (the one returned by get()) accepts a factory keyword argument the we will pass it a wekaref.proxy of ourselves so it can know who created and access resources with a long lifecycle (eg: database connections, etc...) common to all products. Arguments used to call the factory will also be tried to be passed to the product’s constructor.
I’m a generic function you can extend to return an appropiate builder for the factory to use to build its products.
A keyword argument named args (which shall be the last positional argument) can be a dict that methods around, before or after the main action can use to stuff keyword arguments that will be used to call the builder that is finally returned. The call will be adapted so it is safe to add keyword arguments in the hope they are accepted.
Register builder as a builder for products of this factory to handle resources of type resource.
This method generates rules to extend get().
Optional parameters:
- pred
- A predicate which shall be satisfied for this builder to be chosen This argument is a string that will be evaluated in the caller’s frame.
- defaults
- A dict with extra keyword arguments to call the builder with before returning the product.
- prio
- In the case the predicate that is generated is not more specific than anyone registered, this (int) argument will be used to disambiguate.
Any other keyword argument which is passed will be matched for equality against the factory’s arguments.
As an example we will create a BaseFactory subclass to produce garments. Note that instead of determining temperature in Celsius or Fahrenheit we’ll have a boolean flag so the example can be followed without a calculator. Humidity is in %:
>>> class GarmentFactory(BaseFactory):
... def get(self, resource, is_cold=False, humidity=None, args={}):
... pass
Now we declare some “resources” the factory will create products for:
>>> class LivingThing(object): pass
>>> class Person(LivingThing): pass
>>> class Animal(LivingThing): pass
Now some products:
>>> class Garment(object):
... def __init__(self): pass
... def __repr__(self):
... return '<A %s>' % self.__class__.__name__
>>> class BareSkin(Garment): pass
>>> class FurCoat(Garment): pass
>>> class RainCoat(Garment): pass
Now we register which producs the factory should create and the conditions it should create them under:
>>> # Living things have their bare skin as a default
>>> GarmentFactory.register(BareSkin, LivingThing)
>>> # Rather be naked than feel stupid with a FurCoat on a warm day...
>>> GarmentFactory.register(BareSkin, Person, is_cold=False)
>>> GarmentFactory.register(FurCoat, Person, is_cold=True)
>>> # Humidty is a little trickier... we can write a predicate for that
>>> GarmentFactory.register(RainCoat, Person, "humidity>80", is_cold=True)
Let’s see the products:
>>> factory = GarmentFactory()
>>> factory(Animal)
<A BareSkin>
>>> factory(Person, is_cold=False)
<A BareSkin>
>>> factory(Person, is_cold=True)
<A FurCoat>
>>> factory(Person, is_cold=True, humidity=90)
<A RainCoat>
Now, let’s say that we can now invent scuba diving suit:
>>> class ScubaDivingSuit(Garment): pass
And we want to use it when humidity is exactly 100%. If we try to register it like this:
>>> # GarmentFactory.register(ScubaDivingSuit, Person, humidity=100)
We would have seen an ugly traceback from an AmbiguousMethods exception. This is because the newly added rule and the rule for the conflict since both conditions apply and none is more specific. We have two ways of fixing this:
- Write a more specific rule, that is one with more conditions being checked.
- Register the new one with a higher priority
In this case we’ll take the easy route:
>>> GarmentFactory.register(ScubaDivingSuit, Person, humidity=100, _prio=10)
>>> factory(Person, is_cold=True, humidity=100)
<A ScubaDivingSuit>
Returns True if this factory knows how to create products for the given arguments.