diff options
Diffstat (limited to 'factory/containers.py')
-rw-r--r-- | factory/containers.py | 181 |
1 files changed, 65 insertions, 116 deletions
diff --git a/factory/containers.py b/factory/containers.py index 8012444..62c8847 100644 --- a/factory/containers.py +++ b/factory/containers.py @@ -23,7 +23,12 @@ from declarations import OrderedDeclaration, SubFactory ATTR_SPLITTER = '__' -class ObjectParamsWrapper(object): + +class CyclicDefinitionError(Exception): + """Raised when cyclic definition were found.""" + + +class LazyStub(object): """A generic container that allows for getting but not setting of attributes. Attributes are set at initialization time.""" @@ -31,107 +36,45 @@ class ObjectParamsWrapper(object): initialized = False def __init__(self, attrs): - self.attrs = attrs + self.__attrs = attrs + self.__values = {} + self.__pending = [] self.initialized = True - def __setattr__(self, name, value): - if not self.initialized: - return super(ObjectParamsWrapper, self).__setattr__(name, value) - else: - raise AttributeError('Setting of object attributes is not allowed') + def __fill__(self): + res = {} + for attr in self.__attrs: + res[attr] = getattr(self, attr) + return res def __getattr__(self, name): - try: - return self.attrs[name] - except KeyError: - raise AttributeError("The param '{0}' does not exist. Perhaps your declarations are out of order?".format(name)) - - -class OrderedDict(object): - def __init__(self, **kwargs): - self._order = {} - self._values = {} - for k, v in kwargs.iteritems(): - self[k] = v - - def __contains__(self, key): - return key in self._values - - def __getitem__(self, key): - return self._values[key] - - def __setitem__(self, key, val): - if key in self: - del self[key] - self._values[key] = val - self._order.setdefault(val.order, set()).add(key) - - def __delitem__(self, key): - self.pop(key) - - def pop(self, key): - val = self._values.pop(key) - self._order[val.order].remove(key) - return val - - def items(self): - return list(self.iteritems()) - - def iteritems(self): - order = sorted(self._order.keys()) - for i in order: - for key in self._order[i]: - yield (key, self._values[key]) - - def __iter__(self): - order = sorted(self._order.keys()) - for i in order: - for k in self._order[i]: - yield k - - -class DeclarationDict(object): - """Holds a dict of declarations, keeping OrderedDeclaration at the end.""" - def __init__(self, extra=None): - if not extra: - extra = {} - self._ordered = OrderedDict() - self._unordered = {} - self.update(extra) + if name in self.__pending: + raise CyclicDefinitionError( + "Cyclic lazy attribute definition for %s. Current cycle is %r." % + (name, self.__pending)) + elif name in self.__values: + return self.__values[name] + elif name in self.__attrs: + val = self.__attrs[name] + if isinstance(val, LazyValue): + self.__pending.append(name) + val = val.evaluate(self) + assert name == self.__pending.pop() + self.__values[name] = val + return val + else: + raise AttributeError( + "The parameter %s is unknown. Evaluated attributes are %r, definitions are %r." % (name, self.__values, self.__attrs)) - def __setitem__(self, key, value): - if key in self: - del self[key] - if isinstance(value, OrderedDeclaration): - self._ordered[key] = value - else: - self._unordered[key] = value - - def __getitem__(self, key): - """Try in _unordered first, then in _ordered.""" - try: - return self._unordered[key] - except KeyError: - return self._ordered[key] - - def __delitem__(self, key): - if key in self._unordered: - del self._unordered[key] + def __setattr__(self, name, value): + if not self.initialized: + return super(LazyStub, self).__setattr__(name, value) else: - del self._ordered[key] - - def pop(self, key, *args): - assert len(args) <= 1 - try: - return self._unordered.pop(key) - except KeyError: - return self._ordered.pop(key, *args) + raise AttributeError('Setting of object attributes is not allowed') - def update(self, d): - for k in d: - self[k] = d[k] +class DeclarationDict(dict): def update_with_public(self, d): """Updates the DeclarationDict from a class definition dict. @@ -155,23 +98,29 @@ class DeclarationDict(object): new.update(extra) return new - def __contains__(self, key): - return key in self._unordered or key in self._ordered - def items(self): - return list(self.iteritems()) +class LazyValue(object): + def evaluate(self, obj): + raise NotImplementedError("This is an abstract method.") + + +class SubFactoryWrapper(LazyValue): + def __init__(self, subfactory, subfields, create): + self.subfactory = subfactory + self.subfields = subfields + self.create = create + + def evaluate(self, obj): + return self.subfactory.evaluate(self.create, self.subfields) + - def iteritems(self): - for pair in self._unordered.iteritems(): - yield pair - for pair in self._ordered.iteritems(): - yield pair +class OrderedDeclarationWrapper(LazyValue): + def __init__(self, declaration, sequence): + self.declaration = declaration + self.sequence = sequence - def __iter__(self): - for k in self._unordered: - yield k - for k in self._ordered: - yield k + def evaluate(self, obj): + return self.declaration.evaluate(self.sequence, obj) class AttributeBuilder(object): @@ -196,16 +145,16 @@ class AttributeBuilder(object): def build(self, create): self.factory.sequence = self.factory._generate_next_sequence() - attributes = {} - for key, val in self._attrs.iteritems(): - if isinstance(val, SubFactory): - val = val.evaluate(self.factory, create, self._subfields.get(key, {})) - elif isinstance(val, OrderedDeclaration): - wrapper = ObjectParamsWrapper(attributes) - val = val.evaluate(self.factory, wrapper) - attributes[key] = val + wrapped_attrs = {} + for k, v in self._attrs.iteritems(): + if isinstance(v, SubFactory): + v = SubFactoryWrapper(v, self._subfields.get(k, {}), create) + elif isinstance(v, OrderedDeclaration): + v = OrderedDeclarationWrapper(v, self.factory.sequence) + wrapped_attrs[k] = v - return attributes + stub = LazyStub(wrapped_attrs) + return stub.__fill__() class StubObject(object): |