Skip to content

Commit b152976

Browse files
abetkinkozlovsky
authored andcommitted
Fixed #138: assertion error in obj.flush()
1 parent 39673d1 commit b152976

File tree

2 files changed

+94
-5
lines changed

2 files changed

+94
-5
lines changed

pony/orm/core.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,7 +1593,7 @@ def flush(cache):
15931593
cache.modified = False
15941594

15951595
for obj, status in saved_objects:
1596-
if obj is not None: obj._after_save_(status)
1596+
obj._after_save_(status)
15971597
else:
15981598
if cache.modified: throw(TransactionError,
15991599
'Recursion depth limit reached in obj._after_save_() call')
@@ -4082,6 +4082,7 @@ def populate_criteria_list(criteria_list, columns, converters, params_count=0, t
40824082
statuses = set(['created', 'cancelled', 'loaded', 'modified', 'inserted', 'updated', 'marked_to_delete', 'deleted'])
40834083
del_statuses = set(['marked_to_delete', 'deleted', 'cancelled'])
40844084
created_or_deleted_statuses = set(['created']) | del_statuses
4085+
saved_statuses = set(['inserted', 'updated', 'deleted'])
40854086

40864087
def throw_object_was_deleted(obj):
40874088
assert obj._status_ in del_statuses
@@ -4723,18 +4724,37 @@ def _save_(obj, dependent_objects=None):
47234724
cache = obj._session_cache_
47244725
assert cache.is_alive
47254726
status = obj._status_
4726-
if status in ('loaded', 'inserted', 'updated', 'cancelled'): return
47274727

47284728
if status in ('created', 'modified'):
47294729
obj._save_principal_objects_(dependent_objects)
47304730

4731-
obj._save_pos_ = None
47324731
if status == 'created': obj._save_created_()
47334732
elif status == 'modified': obj._save_updated_()
47344733
elif status == 'marked_to_delete': obj._save_deleted_()
4735-
else: assert False # pragma: no cover
4734+
else: assert False, "_save_() called for object %r with incorrect status %s" % (obj, status) # pragma: no cover
4735+
4736+
assert obj._status_ in saved_statuses
4737+
obj._save_pos_ = None
47364738
def flush(obj):
4737-
with obj._session_cache_.flush_disabled(): obj._save_()
4739+
status = obj._status_
4740+
if status not in ('created', 'modified', 'marked_to_delete'):
4741+
return
4742+
4743+
save_pos = obj._save_pos_
4744+
assert save_pos is not None, 'save_pos is None for %s object' % status
4745+
4746+
with obj._session_cache_.flush_disabled():
4747+
obj._before_save_() # should be inside flush_disabled to prevent infinite recursion
4748+
# TODO: add to documentation that flush is disabled inside before_xxx hooks
4749+
obj._save_()
4750+
4751+
objects_to_save = obj._session_cache_.objects_to_save
4752+
if save_pos == len(objects_to_save) - 1:
4753+
objects_to_save.pop()
4754+
else:
4755+
objects_to_save[save_pos] = None
4756+
4757+
obj._after_save_(status)
47384758
def _before_save_(obj):
47394759
status = obj._status_
47404760
if status == 'created': obj.before_insert()
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
2+
3+
import unittest
4+
5+
6+
from pony.orm import *
7+
8+
db = Database()
9+
10+
class TestPost(db.Entity):
11+
category = Optional('TestCategory')
12+
name = Optional(str, default='Noname')
13+
14+
class TestCategory(db.Entity):
15+
posts = Set(TestPost)
16+
17+
db.bind('sqlite', ':memory:')
18+
db.generate_mapping(create_tables=True)
19+
20+
21+
class EntityStatusTestCase(object):
22+
23+
def make_flush(self, obj=None):
24+
raise NotImplementedError
25+
26+
@db_session
27+
def test_delete_updated(self):
28+
p = TestPost()
29+
self.make_flush(p)
30+
p.name = 'Pony'
31+
self.assertEqual(p._status_, 'modified')
32+
self.make_flush(p)
33+
self.assertEqual(p._status_, 'updated')
34+
p.delete()
35+
self.assertEqual(p._status_, 'marked_to_delete')
36+
self.make_flush(p)
37+
self.assertEqual(p._status_, 'deleted')
38+
39+
@db_session
40+
def test_delete_inserted(self):
41+
p = TestPost()
42+
self.assertEqual(p._status_, 'created')
43+
self.make_flush(p)
44+
self.assertEqual(p._status_, 'inserted')
45+
p.delete()
46+
47+
@db_session
48+
def test_cancelled(self):
49+
p = TestPost()
50+
self.assertEqual(p._status_, 'created')
51+
p.delete()
52+
self.assertEqual(p._status_, 'cancelled')
53+
self.make_flush(p)
54+
self.assertEqual(p._status_, 'cancelled')
55+
56+
57+
58+
class EntityStatusTestCase_ObjectFlush(EntityStatusTestCase,
59+
unittest.TestCase):
60+
61+
def make_flush(self, obj=None):
62+
obj.flush()
63+
64+
65+
class EntityStatusTestCase_FullFlush(EntityStatusTestCase,
66+
unittest.TestCase):
67+
68+
def make_flush(self, obj=None):
69+
flush() # full flush

0 commit comments

Comments
 (0)