summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphaël Barrois <raphael.barrois@polytechnique.org>2016-04-07 00:11:16 +0200
committerRaphaël Barrois <raphael.barrois@polytechnique.org>2016-04-07 00:11:16 +0200
commita17b036d0b12a35a24864ec6fb9dee8d3d54ff03 (patch)
tree27ca2935a365dcf3096f579043769a4d7881b880
parent872718c1eb7865878ff17c8ce7dbeb004a089e40 (diff)
parentf023e5a477668b8374a75c78e87d946b21a27f15 (diff)
downloadfactory-boy-a17b036d0b12a35a24864ec6fb9dee8d3d54ff03.tar
factory-boy-a17b036d0b12a35a24864ec6fb9dee8d3d54ff03.tar.gz
Merge pull request #256 from koterpillar/cyclic-definition-rescue
Don't leave AttributeBuilder in an inconsistent state on exceptions
-rw-r--r--factory/containers.py6
-rw-r--r--tests/test_containers.py36
2 files changed, 33 insertions, 9 deletions
diff --git a/factory/containers.py b/factory/containers.py
index d3f39c4..4961115 100644
--- a/factory/containers.py
+++ b/factory/containers.py
@@ -99,8 +99,10 @@ class LazyStub(object):
val = self.__attrs[name]
if isinstance(val, LazyValue):
self.__pending.append(name)
- val = val.evaluate(self, self.__containers)
- last = self.__pending.pop()
+ try:
+ val = val.evaluate(self, self.__containers)
+ finally:
+ last = self.__pending.pop()
assert name == last
self.__values[name] = val
return val
diff --git a/tests/test_containers.py b/tests/test_containers.py
index a308353..ea6ddff 100644
--- a/tests/test_containers.py
+++ b/tests/test_containers.py
@@ -79,18 +79,40 @@ class LazyStubTestCase(unittest.TestCase):
self.assertEqual(2, stub.factory_parent.rank)
self.assertEqual(1, stub.factory_parent.factory_parent.rank)
- def test_cyclic_definition(self):
- class LazyAttr(containers.LazyValue):
- def __init__(self, attrname):
- self.attrname = attrname
+ class LazyAttr(containers.LazyValue):
+ def __init__(self, attrname):
+ self.attrname = attrname
- def evaluate(self, obj, container=None):
- return 1 + getattr(obj, self.attrname)
+ def evaluate(self, obj, container=None):
+ return 1 + getattr(obj, self.attrname)
- stub = containers.LazyStub({'one': LazyAttr('two'), 'two': LazyAttr('one')})
+ def test_cyclic_definition(self):
+ stub = containers.LazyStub({
+ 'one': self.LazyAttr('two'),
+ 'two': self.LazyAttr('one'),
+ })
self.assertRaises(errors.CyclicDefinitionError, getattr, stub, 'one')
+ def test_cyclic_definition_rescue(self):
+ class LazyAttrDefault(self.LazyAttr):
+ def __init__(self, attname, defvalue):
+ super(LazyAttrDefault, self).__init__(attname)
+ self.defvalue = defvalue
+ def evaluate(self, obj, container=None):
+ try:
+ return super(LazyAttrDefault, self).evaluate(obj, container)
+ except containers.CyclicDefinitionError:
+ return self.defvalue
+
+ stub = containers.LazyStub({
+ 'one': LazyAttrDefault('two', 10),
+ 'two': self.LazyAttr('one'),
+ })
+
+ self.assertEqual(10, stub.one)
+ self.assertEqual(11, stub.two)
+
def test_representation(self):
class RandomObj(object):
pass