aboutsummaryrefslogtreecommitdiff
path: root/factory/containers.py
diff options
context:
space:
mode:
Diffstat (limited to 'factory/containers.py')
-rw-r--r--factory/containers.py67
1 files changed, 55 insertions, 12 deletions
diff --git a/factory/containers.py b/factory/containers.py
index e280162..2f92f62 100644
--- a/factory/containers.py
+++ b/factory/containers.py
@@ -45,16 +45,29 @@ class LazyStub(object):
__values (dict): maps attribute name to computed value
__pending (str list): names of the attributes whose value is being
computed. This allows to detect cyclic lazy attribute definition.
+ __containers (LazyStub list): "parents" of the LazyStub being built.
+ This allows to have the field of a field depend on the value of
+ another field
+ __target_class (type): the target class to build.
"""
__initialized = False
- def __init__(self, attrs):
+ def __init__(self, attrs, containers=(), target_class=object):
self.__attrs = attrs
self.__values = {}
self.__pending = []
+ self.__containers = containers
+ self.__target_class = target_class
self.__initialized = True
+ def __repr__(self):
+ return '<LazyStub for %s>' % self.__target_class.__name__
+
+ def __str__(self):
+ return '<LazyStub for %s with %s>' % (
+ self.__target_class.__name__, self.__attrs.keys())
+
def __fill__(self):
"""Fill this LazyStub, computing values of all defined attributes.
@@ -82,7 +95,7 @@ class LazyStub(object):
val = self.__attrs[name]
if isinstance(val, LazyValue):
self.__pending.append(name)
- val = val.evaluate(self)
+ val = val.evaluate(self, self.__containers)
assert name == self.__pending.pop()
self.__values[name] = val
return val
@@ -103,20 +116,35 @@ class LazyStub(object):
class DeclarationDict(dict):
"""Slightly extended dict to work with OrderedDeclaration."""
+ def is_declaration(self, name, value):
+ """Determines if a class attribute is a field value declaration.
+
+ Based on the name and value of the class attribute, return ``True`` if
+ it looks like a declaration of a default field value, ``False`` if it
+ is private (name starts with '_') or a classmethod or staticmethod.
+
+ """
+ if isinstance(value, (classmethod, staticmethod)):
+ return False
+ elif isinstance(value, declarations.OrderedDeclaration):
+ return True
+ return (not name.startswith("_"))
+
def update_with_public(self, d):
"""Updates the DeclarationDict from a class definition dict.
Takes into account all public attributes and OrderedDeclaration
- instances; ignores all attributes starting with '_'.
+ instances; ignores all class/staticmethods and private attributes
+ (starting with '_').
Returns a dict containing all remaining elements.
"""
remaining = {}
for k, v in d.iteritems():
- if k.startswith('_') and not isinstance(v, declarations.OrderedDeclaration):
- remaining[k] = v
- else:
+ if self.is_declaration(k, v):
self[k] = v
+ else:
+ remaining[k] = v
return remaining
def copy(self, extra=None):
@@ -135,7 +163,7 @@ class DeclarationDict(dict):
class LazyValue(object):
"""Some kind of "lazy evaluating" object."""
- def evaluate(self, obj):
+ def evaluate(self, obj, containers=()):
"""Compute the value, using the given object."""
raise NotImplementedError("This is an abstract method.")
@@ -156,8 +184,12 @@ class SubFactoryWrapper(LazyValue):
self.subfields = subfields
self.create = create
- def evaluate(self, obj):
- return self.subfactory.evaluate(self.create, self.subfields)
+ def evaluate(self, obj, containers=()):
+ expanded_containers = (obj,)
+ if containers:
+ expanded_containers += tuple(containers)
+ return self.subfactory.evaluate(self.create, self.subfields,
+ expanded_containers)
class OrderedDeclarationWrapper(LazyValue):
@@ -175,8 +207,15 @@ class OrderedDeclarationWrapper(LazyValue):
self.declaration = declaration
self.sequence = sequence
- def evaluate(self, obj):
- return self.declaration.evaluate(self.sequence, obj)
+ def evaluate(self, obj, containers=()):
+ """Lazily evaluate the attached OrderedDeclaration.
+
+ Args:
+ obj (LazyStub): the object being built
+ containers (object list): the chain of containers of the object
+ being built, its immediate holder being first.
+ """
+ return self.declaration.evaluate(self.sequence, obj, containers)
class AttributeBuilder(object):
@@ -195,7 +234,9 @@ class AttributeBuilder(object):
if not extra:
extra = {}
+
self.factory = factory
+ self._containers = extra.pop('__containers', None)
self._attrs = factory.declarations(extra)
self._subfields = self._extract_subfields()
@@ -229,7 +270,9 @@ class AttributeBuilder(object):
v = OrderedDeclarationWrapper(v, self.factory.sequence)
wrapped_attrs[k] = v
- return LazyStub(wrapped_attrs).__fill__()
+ stub = LazyStub(wrapped_attrs, containers=self._containers,
+ target_class=self.factory)
+ return stub.__fill__()
class StubObject(object):