diff --git a/docs/source/api.rst b/docs/source/api.rst index a847bf7c..76b203d4 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -745,6 +745,8 @@ A :class:`neo4j.Result` is attached to an active connection, through a :class:`n .. describe:: iter(result) + .. describe:: next(result) + .. automethod:: keys .. automethod:: consume diff --git a/docs/source/async_api.rst b/docs/source/async_api.rst index 989ab200..5f88aa7a 100644 --- a/docs/source/async_api.rst +++ b/docs/source/async_api.rst @@ -475,7 +475,11 @@ A :class:`neo4j.AsyncResult` is attached to an active connection, through a :cla .. autoclass:: neo4j.AsyncResult() - .. describe:: iter(result) + .. method:: result.__aiter__() + :async: + + .. method:: result.__anext__() + :async: .. automethod:: keys diff --git a/docs/source/results.rst b/docs/source/results.rst deleted file mode 100644 index 577e8659..00000000 --- a/docs/source/results.rst +++ /dev/null @@ -1,110 +0,0 @@ -***************** -Consuming Results -***************** - -Every time a query is executed, a :class:`.Result` is returned. - -This provides a handle to the result of the query, giving access to the records within it as well as the result metadata. - -Each result consists of header metadata, zero or more :class:`.Record` objects and footer metadata (the summary). -Results also contain a buffer that automatically stores unconsumed records when results are consumed out of order. - -A :class:`.Result` is attached to an active connection, through a :class:`.Session`, until all its content has been buffered or consumed. - -.. class:: .Result - - .. describe:: iter(result) - - .. autoattribute:: session - - .. automethod:: attached - - .. automethod:: detach - - .. automethod:: keys - - .. automethod:: records - - .. automethod:: consume - - .. automethod:: single - - .. automethod:: peek - - .. automethod:: graph - - .. automethod:: value - - .. automethod:: values - - .. automethod:: data - - -.. class:: .Record - - A :class:`.Record` is an immutable ordered collection of key-value - pairs. It is generally closer to a :py:class:`namedtuple` than to an - :py:class:`OrderedDict` inasmuch as iteration of the collection will - yield values rather than keys. - - .. describe:: Record(iterable) - - Create a new record based on an dictionary-like iterable. - This can be a dictionary itself, or may be a sequence of key-value pairs, each represented by a tuple. - - .. describe:: record == other - - Compare a record for equality with another value. - The `other` value may be any `Sequence` or `Mapping`, or both. - If comparing with a `Sequence`, the values are compared in order. - If comparing with a `Mapping`, the values are compared based on their keys. - If comparing with a value that exhibits both traits, both comparisons must be true for the values to be considered equal. - - .. describe:: record != other - - Compare a record for inequality with another value. - See above for comparison rules. - - .. describe:: hash(record) - - Create a hash for this record. - This will raise a :exc:`TypeError` if any values within the record are unhashable. - - .. describe:: record[index] - - Obtain a value from the record by index. - This will raise an :exc:`IndexError` if the specified index is out of range. - - .. describe:: record[i:j] - - Derive a sub-record based on a start and end index. - All keys and values within those bounds will be copied across in the same order as in the original record. - - .. describe:: record[key] - - Obtain a value from the record by key. - This will raise a :exc:`KeyError` if the specified key does not exist. - - .. automethod:: get(key, default=None) - - .. automethod:: value(key=0, default=None) - - .. automethod:: index(key) - - .. automethod:: keys - - .. automethod:: values - - .. automethod:: items - - .. automethod:: data - - -Summary Details ---------------- - -.. autoclass:: .ResultSummary - :members: - -.. autoclass:: .SummaryCounters - :members: diff --git a/neo4j/_async/work/result.py b/neo4j/_async/work/result.py index dc3b31a4..4535efdd 100644 --- a/neo4j/_async/work/result.py +++ b/neo4j/_async/work/result.py @@ -197,6 +197,9 @@ async def __aiter__(self): self._closed = True + async def __anext__(self): + return await self.__aiter__().__anext__() + async def _attach(self): """Sets the Result object in an attached state by fetching messages from the connection to the buffer. diff --git a/neo4j/_sync/work/result.py b/neo4j/_sync/work/result.py index 2f9cddca..b22a12b4 100644 --- a/neo4j/_sync/work/result.py +++ b/neo4j/_sync/work/result.py @@ -197,6 +197,9 @@ def __iter__(self): self._closed = True + def __next__(self): + return self.__iter__().__next__() + def _attach(self): """Sets the Result object in an attached state by fetching messages from the connection to the buffer. diff --git a/tests/unit/async_/work/test_result.py b/tests/unit/async_/work/test_result.py index 6c3ec702..3e7ecc29 100644 --- a/tests/unit/async_/work/test_result.py +++ b/tests/unit/async_/work/test_result.py @@ -198,6 +198,15 @@ async def fetch_and_compare_all_records( if limit is None: assert result._closed elif method == "next": + n = len(expected_records) if limit is None else limit + for _ in range(n): + record = await AsyncUtil.next(result) + received_records.append([record.get(key, None)]) + if limit is None: + with pytest.raises(StopAsyncIteration): + await AsyncUtil.next(result) + assert result._closed + elif method == "one iter": iter_ = AsyncUtil.iter(result) n = len(expected_records) if limit is None else limit for _ in range(n): @@ -223,7 +232,8 @@ async def fetch_and_compare_all_records( assert received_records == expected_records -@pytest.mark.parametrize("method", ("for loop", "next", "new iter")) +@pytest.mark.parametrize("method", + ("for loop", "next", "one iter", "new iter")) @pytest.mark.parametrize("records", ( [], [[42]], @@ -237,7 +247,8 @@ async def test_result_iteration(method, records): await fetch_and_compare_all_records(result, "x", records, method) -@pytest.mark.parametrize("method", ("for loop", "next", "new iter")) +@pytest.mark.parametrize("method", + ("for loop", "next", "one iter", "new iter")) @pytest.mark.parametrize("invert_fetch", (True, False)) @mark_async_test async def test_parallel_result_iteration(method, invert_fetch): @@ -266,7 +277,8 @@ async def test_parallel_result_iteration(method, invert_fetch): ) -@pytest.mark.parametrize("method", ("for loop", "next", "new iter")) +@pytest.mark.parametrize("method", + ("for loop", "next", "one iter", "new iter")) @pytest.mark.parametrize("invert_fetch", (True, False)) @mark_async_test async def test_interwoven_result_iteration(method, invert_fetch): diff --git a/tests/unit/sync/work/test_result.py b/tests/unit/sync/work/test_result.py index fc7d2c25..8401db1d 100644 --- a/tests/unit/sync/work/test_result.py +++ b/tests/unit/sync/work/test_result.py @@ -198,6 +198,15 @@ def fetch_and_compare_all_records( if limit is None: assert result._closed elif method == "next": + n = len(expected_records) if limit is None else limit + for _ in range(n): + record = Util.next(result) + received_records.append([record.get(key, None)]) + if limit is None: + with pytest.raises(StopIteration): + Util.next(result) + assert result._closed + elif method == "one iter": iter_ = Util.iter(result) n = len(expected_records) if limit is None else limit for _ in range(n): @@ -223,7 +232,8 @@ def fetch_and_compare_all_records( assert received_records == expected_records -@pytest.mark.parametrize("method", ("for loop", "next", "new iter")) +@pytest.mark.parametrize("method", + ("for loop", "next", "one iter", "new iter")) @pytest.mark.parametrize("records", ( [], [[42]], @@ -237,7 +247,8 @@ def test_result_iteration(method, records): fetch_and_compare_all_records(result, "x", records, method) -@pytest.mark.parametrize("method", ("for loop", "next", "new iter")) +@pytest.mark.parametrize("method", + ("for loop", "next", "one iter", "new iter")) @pytest.mark.parametrize("invert_fetch", (True, False)) @mark_sync_test def test_parallel_result_iteration(method, invert_fetch): @@ -266,7 +277,8 @@ def test_parallel_result_iteration(method, invert_fetch): ) -@pytest.mark.parametrize("method", ("for loop", "next", "new iter")) +@pytest.mark.parametrize("method", + ("for loop", "next", "one iter", "new iter")) @pytest.mark.parametrize("invert_fetch", (True, False)) @mark_sync_test def test_interwoven_result_iteration(method, invert_fetch):