summaryrefslogtreecommitdiff
path: root/factory
diff options
context:
space:
mode:
authorRaphaël Barrois <raphael.barrois@polytechnique.org>2012-08-09 02:16:42 +0200
committerRaphaël Barrois <raphael.barrois@polytechnique.org>2012-08-09 02:16:42 +0200
commita31e87f19b7c193b980d0f54971c12a60e8c7263 (patch)
tree2ccf86db6921f19363176db6de561a452a212aea /factory
parente0e628ec54b8a4d7f41f86f9f535d791a437b13c (diff)
downloadfactory-boy-a31e87f19b7c193b980d0f54971c12a60e8c7263.tar
factory-boy-a31e87f19b7c193b980d0f54971c12a60e8c7263.tar.gz
Introduce 'CircularSubFactory.
Signed-off-by: Raphaël Barrois <raphael.barrois@polytechnique.org>
Diffstat (limited to 'factory')
-rw-r--r--factory/__init__.py1
-rw-r--r--factory/declarations.py105
-rw-r--r--factory/utils.py12
3 files changed, 100 insertions, 18 deletions
diff --git a/factory/__init__.py b/factory/__init__.py
index 3753461..789d88e 100644
--- a/factory/__init__.py
+++ b/factory/__init__.py
@@ -60,6 +60,7 @@ from declarations import (
SelfAttribute,
ContainerAttribute,
SubFactory,
+ CircularSubFactory,
PostGeneration,
RelatedFactory,
diff --git a/factory/declarations.py b/factory/declarations.py
index 83c32ab..5e45255 100644
--- a/factory/declarations.py
+++ b/factory/declarations.py
@@ -208,46 +208,115 @@ class ContainerAttribute(OrderedDeclaration):
return self.function(obj, containers)
-class SubFactory(OrderedDeclaration):
- """Base class for attributes based upon a sub-factory.
+class ParameteredAttribute(OrderedDeclaration):
+ """Base class for attributes expecting parameters.
Attributes:
- defaults (dict): Overrides to the defaults defined in the wrapped
- factory
- factory (base.Factory): the wrapped factory
+ defaults (dict): Default values for the paramters.
+ May be overridden by call-time parameters.
+
+ Class attributes:
+ CONTAINERS_FIELD (str): name of the field, if any, where container
+ information (e.g for SubFactory) should be stored. If empty,
+ containers data isn't merged into generate() parameters.
"""
- def __init__(self, factory, **kwargs):
- super(SubFactory, self).__init__()
+ CONTAINERS_FIELD = '__containers'
+
+ def __init__(self, **kwargs):
+ super(ParameteredAttribute, self).__init__()
self.defaults = kwargs
- self.factory = factory
def evaluate(self, create, extra, containers):
"""Evaluate the current definition and fill its attributes.
Uses attributes definition in the following order:
- - attributes defined in the wrapped factory class
- - values defined when defining the SubFactory
- - additional values defined in attributes
+ - values defined when defining the ParameteredAttribute
+ - additional values defined when instantiating the containing factory
Args:
- create (bool): whether the subfactory should call 'build' or
- 'create'
+ create (bool): whether the parent factory is being 'built' or
+ 'created'
extra (containers.DeclarationDict): extra values that should
- override the wrapped factory's defaults
+ override the defaults
containers (list of LazyStub): List of LazyStub for the chain of
factories being evaluated, the calling stub being first.
"""
-
defaults = dict(self.defaults)
if extra:
defaults.update(extra)
- defaults['__containers'] = containers
+ if self.CONTAINERS_FIELD:
+ defaults[self.CONTAINERS_FIELD] = containers
+
+ return self.generate(create, defaults)
+
+ def generate(self, create, params):
+ """Actually generate the related attribute.
+
+ Args:
+ create (bool): whether the calling factory was in 'create' or
+ 'build' mode
+ params (dict): parameters inherited from init and evaluation-time
+ overrides.
+
+ Returns:
+ Computed value for the current declaration.
+ """
+ raise NotImplementedError()
+
+
+class SubFactory(ParameteredAttribute):
+ """Base class for attributes based upon a sub-factory.
+ Attributes:
+ defaults (dict): Overrides to the defaults defined in the wrapped
+ factory
+ factory (base.Factory): the wrapped factory
+ """
+
+ def __init__(self, factory, **kwargs):
+ super(SubFactory, self).__init__(**kwargs)
+ self.factory = factory
+
+ def get_factory(self):
+ """Retrieve the wrapped factory.Factory subclass."""
+ return self.factory
+
+ def generate(self, create, params):
+ """Evaluate the current definition and fill its attributes.
+
+ Args:
+ create (bool): whether the subfactory should call 'build' or
+ 'create'
+ params (containers.DeclarationDict): extra values that should
+ override the wrapped factory's defaults
+ """
+ subfactory = self.get_factory()
if create:
- return self.factory.create(**defaults)
+ return subfactory.create(**params)
else:
- return self.factory.build(**defaults)
+ return subfactory.build(**params)
+
+
+class CircularSubFactory(SubFactory):
+ """Use to solve circular dependencies issues."""
+ def __init__(self, module_name, factory_name, **kwargs):
+ super(CircularSubFactory, self).__init__(None, **kwargs)
+ self.module_name = module_name
+ self.factory_name = factory_name
+
+ def get_factory(self):
+ """Retrieve the factory.Factory subclass.
+
+ Its value is cached in the 'factory' attribute, and retrieved through
+ the factory_getter callable.
+ """
+ if self.factory is None:
+ factory_class = utils.import_object(
+ self.module_name, self.factory_name)
+
+ self.factory = factory_class
+ return self.factory
class PostGenerationDeclaration(object):
diff --git a/factory/utils.py b/factory/utils.py
index c592da4..e7cdf5f 100644
--- a/factory/utils.py
+++ b/factory/utils.py
@@ -81,3 +81,15 @@ def multi_extract_dict(prefixes, kwargs, pop=True, exclude=()):
['%s%s%s' % (prefix, ATTR_SPLITTER, key) for key in extracted])
return results
+
+
+def import_object(module_name, attribute_name):
+ """Import an object from its absolute path.
+
+ Example:
+ >>> import_object('datetime', 'datetime')
+ <type 'datetime.datetime'>
+ """
+ module = __import__(module_name, {}, {}, [attribute_name], 0)
+ return getattr(module, attribute_name)
+