Skip to content

Commit

Permalink
Docs: Changes are reverted if exception during iterall (#6128)
Browse files Browse the repository at this point in the history
An explicit test is added to guarantee that changes made while looping
over the result of `iterall` or `iterdict` are reverted if an exception
is raised and not caught before the end of the iterator. A note is added
to the how-to section of the `QueryBuilder`.
  • Loading branch information
sphuber committed Sep 26, 2023
1 parent 1619ff7 commit 17c5d87
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/source/howto/query.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ For example, you can iterate over the results of your query in a for loop:

When looping over the result of a query, use the ``iterall`` (or ``iterdict``) generator instead of ``all`` (or ``dict``).
This avoids loading the entire query result into memory, and it also delays committing changes made to AiiDA objects inside the loop until the end of the loop is reached.
If an exception is raised before the loop ends, all changes are reverted.


.. _how-to:query:filters:
Expand Down
25 changes: 25 additions & 0 deletions tests/orm/test_querybuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -1475,6 +1475,31 @@ def test_len_results(self):
qb.append(orm.Data, with_incoming='parent')
assert len(qb.all()) == qb.count()

@pytest.mark.usefixtures('aiida_profile_clean')
def test_iterall_except(self):
"""Test ``QueryBuilder.iterall`` uses a transaction and if interrupted, changes are reverted.
In this test, 10 nodes are created which are then looped over and for each one, another node is stored, but
before the loop can finish, an exception is raised. At then, the number of nodes should still be ten as the new
ones that were being created in the transaction should have been reverted.
"""
assert orm.QueryBuilder().append(orm.Data).count() == 0

count = 10

for _ in range(count):
orm.Data().store()

try:
for index, _ in enumerate(orm.QueryBuilder().append(orm.Data).iterall()):
orm.Data().store()
if index >= count - 2:
raise RuntimeError('some error')
except RuntimeError:
pass

assert orm.QueryBuilder().append(orm.Data).count() == count

def test_iterall_with_mutation(self):
"""Test that nodes can be mutated while being iterated using ``QueryBuilder.iterall``.
Expand Down

0 comments on commit 17c5d87

Please sign in to comment.