diff options
Diffstat (limited to 'factory/containers.py')
-rw-r--r-- | factory/containers.py | 67 |
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): |