diff options
author | Raphaël Barrois <raphael.barrois@polytechnique.org> | 2012-08-09 02:16:42 +0200 |
---|---|---|
committer | Raphaël Barrois <raphael.barrois@polytechnique.org> | 2012-08-09 02:16:42 +0200 |
commit | a31e87f19b7c193b980d0f54971c12a60e8c7263 (patch) | |
tree | 2ccf86db6921f19363176db6de561a452a212aea /factory | |
parent | e0e628ec54b8a4d7f41f86f9f535d791a437b13c (diff) | |
download | factory-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__.py | 1 | ||||
-rw-r--r-- | factory/declarations.py | 105 | ||||
-rw-r--r-- | factory/utils.py | 12 |
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) + |