17
17
18
18
19
19
from collections import deque
20
+ from warnings import warn
20
21
21
22
from ..._async_compat .util import AsyncUtil
22
23
from ...data import DataDehydrator
@@ -248,11 +249,11 @@ async def _buffer(self, n=None):
248
249
record_buffer .append (record )
249
250
if n is not None and len (record_buffer ) >= n :
250
251
break
251
- self ._exhausted = False
252
252
if n is None :
253
253
self ._record_buffer = record_buffer
254
254
else :
255
255
self ._record_buffer .extend (record_buffer )
256
+ self ._exhausted = not self ._record_buffer
256
257
257
258
async def _buffer_all (self ):
258
259
"""Sets the Result object in an detached state by fetching all records
@@ -286,12 +287,20 @@ def keys(self):
286
287
"""
287
288
return self ._keys
288
289
290
+ async def _exhaust (self ):
291
+ # Exhaust the result, ditching all remaining records.
292
+ if not self ._exhausted :
293
+ self ._discarding = True
294
+ self ._record_buffer .clear ()
295
+ async for _ in self :
296
+ pass
297
+
289
298
async def _tx_end (self ):
290
299
# Handle closure of the associated transaction.
291
300
#
292
301
# This will consume the result and mark it at out of scope.
293
302
# Subsequent calls to `next` will raise a ResultConsumedError.
294
- await self .consume ()
303
+ await self ._exhaust ()
295
304
self ._out_of_scope = True
296
305
297
306
async def consume (self ):
@@ -329,43 +338,93 @@ async def get_two_tx(tx):
329
338
values, info = session.read_transaction(get_two_tx)
330
339
331
340
:returns: The :class:`neo4j.ResultSummary` for this result
341
+
342
+ :raises ResultConsumedError: if the transaction from which this result
343
+ was obtained has been closed.
344
+
345
+ .. versionchanged:: 5.0
346
+ Can raise :exc:`ResultConsumedError`.
332
347
"""
333
- if self ._exhausted is False :
334
- self . _discarding = True
335
- async for _ in self :
336
- pass
348
+ if self ._out_of_scope :
349
+ raise ResultConsumedError ( self , _RESULT_OUT_OF_SCOPE_ERROR )
350
+ if self . _consumed :
351
+ return self . _obtain_summary ()
337
352
353
+ await self ._exhaust ()
338
354
summary = self ._obtain_summary ()
339
355
self ._consumed = True
340
356
return summary
341
357
342
- async def single (self ):
343
- """Obtain the next and only remaining record from this result if available else return None.
358
+ async def single (self , strict = False ):
359
+ """Obtain the next and only remaining record or None.
360
+
344
361
Calling this method always exhausts the result.
345
362
346
363
A warning is generated if more than one record is available but
347
364
the first of these is still returned.
348
365
349
- :returns: the next :class:`neo4j.AsyncRecord`.
366
+ :param strict:
367
+ If :const:`True`, raise a :class:`neo4j.ResultNotSingleError`
368
+ instead of returning None if there is more than one record or
369
+ warning if there are more than 1 record.
370
+ :const:`False` by default.
371
+ :type strict: bool
372
+
373
+ :returns: the next :class:`neo4j.Record` or :const:`None` if none remain
374
+ :warns: if more than one record is available
375
+
376
+ :raises ResultNotSingleError:
377
+ If ``strict=True`` and not exactly one record is available.
378
+ :raises ResultConsumedError: if the transaction from which this result
379
+ was obtained has been closed or the Result has been explicitly
380
+ consumed.
350
381
351
- :raises ResultNotSingleError: if not exactly one record is available.
352
- :raises ResultConsumedError: if the transaction from which this result was
353
- obtained has been closed.
382
+ .. versionchanged:: 5.0
383
+ Added ``strict`` parameter.
384
+ .. versionchanged:: 5.0
385
+ Can raise :exc:`ResultConsumedError`.
354
386
"""
355
387
await self ._buffer (2 )
356
- if not self ._record_buffer :
388
+ buffer = self ._record_buffer
389
+ self ._record_buffer = deque ()
390
+ await self ._exhaust ()
391
+ if not buffer :
392
+ if not strict :
393
+ return None
357
394
raise ResultNotSingleError (
358
395
self ,
359
396
"No records found. "
360
397
"Make sure your query returns exactly one record."
361
398
)
362
- elif len (self ._record_buffer ) > 1 :
363
- raise ResultNotSingleError (
364
- self ,
365
- "More than one record found. "
366
- "Make sure your query returns exactly one record."
367
- )
368
- return self ._record_buffer .popleft ()
399
+ elif len (buffer ) > 1 :
400
+ res = buffer .popleft ()
401
+ if not strict :
402
+ warn ("Expected a result with a single record, "
403
+ "but found multiple." )
404
+ return res
405
+ else :
406
+ raise ResultNotSingleError (
407
+ self ,
408
+ "More than one record found. "
409
+ "Make sure your query returns exactly one record."
410
+ )
411
+ return buffer .popleft ()
412
+
413
+ async def fetch (self , n ):
414
+ """Obtain up to n records from this result.
415
+
416
+ :param n: the maximum number of records to fetch.
417
+ :type n: int
418
+
419
+ :returns: list of :class:`neo4j.AsyncRecord`
420
+
421
+ .. versionadded:: 5.0
422
+ """
423
+ await self ._buffer (n )
424
+ return [
425
+ self ._record_buffer .popleft ()
426
+ for _ in range (min (n , len (self ._record_buffer )))
427
+ ]
369
428
370
429
async def peek (self ):
371
430
"""Obtain the next record from this result without consuming it.
@@ -376,6 +435,9 @@ async def peek(self):
376
435
:raises ResultConsumedError: if the transaction from which this result
377
436
was obtained has been closed or the Result has been explicitly
378
437
consumed.
438
+
439
+ .. versionchanged:: 5.0
440
+ Can raise :exc:`ResultConsumedError`.
379
441
"""
380
442
await self ._buffer (1 )
381
443
if self ._record_buffer :
@@ -392,6 +454,9 @@ async def graph(self):
392
454
:raises ResultConsumedError: if the transaction from which this result
393
455
was obtained has been closed or the Result has been explicitly
394
456
consumed.
457
+
458
+ .. versionchanged:: 5.0
459
+ Can raise :exc:`ResultConsumedError`.
395
460
"""
396
461
await self ._buffer_all ()
397
462
return self ._hydrant .graph
@@ -410,6 +475,9 @@ async def value(self, key=0, default=None):
410
475
:raises ResultConsumedError: if the transaction from which this result
411
476
was obtained has been closed or the Result has been explicitly
412
477
consumed.
478
+
479
+ .. versionchanged:: 5.0
480
+ Can raise :exc:`ResultConsumedError`.
413
481
"""
414
482
return [record .value (key , default ) async for record in self ]
415
483
@@ -426,6 +494,9 @@ async def values(self, *keys):
426
494
:raises ResultConsumedError: if the transaction from which this result
427
495
was obtained has been closed or the Result has been explicitly
428
496
consumed.
497
+
498
+ .. versionchanged:: 5.0
499
+ Can raise :exc:`ResultConsumedError`.
429
500
"""
430
501
return [record .values (* keys ) async for record in self ]
431
502
@@ -439,8 +510,12 @@ async def data(self, *keys):
439
510
:returns: list of dictionaries
440
511
:rtype: list
441
512
442
- :raises ResultConsumedError: if the transaction from which this result was
443
- obtained has been closed.
513
+ :raises ResultConsumedError: if the transaction from which this result
514
+ was obtained has been closed or the Result has been explicitly
515
+ consumed.
516
+
517
+ .. versionchanged:: 5.0
518
+ Can raise :exc:`ResultConsumedError`.
444
519
"""
445
520
return [record .data (* keys ) async for record in self ]
446
521
0 commit comments