From 17c5d872495fbb1b6a80d985cb71088095083bb9 Mon Sep 17 00:00:00 2001 From: Sebastiaan Huber Date: Tue, 26 Sep 2023 21:16:14 +0200 Subject: [PATCH] Docs: Changes are reverted if exception during `iterall` (#6128) 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`. --- docs/source/howto/query.rst | 1 + tests/orm/test_querybuilder.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/docs/source/howto/query.rst b/docs/source/howto/query.rst index 711067ce2a..4916cb508e 100644 --- a/docs/source/howto/query.rst +++ b/docs/source/howto/query.rst @@ -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: diff --git a/tests/orm/test_querybuilder.py b/tests/orm/test_querybuilder.py index d532fd99c1..341e08836c 100644 --- a/tests/orm/test_querybuilder.py +++ b/tests/orm/test_querybuilder.py @@ -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``.