1# ext/asyncio/result.py
2# Copyright (C) 2020-2025 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7from __future__ import annotations
8
9import operator
10from typing import Any
11from typing import AsyncIterator
12from typing import Optional
13from typing import overload
14from typing import Sequence
15from typing import Tuple
16from typing import TYPE_CHECKING
17from typing import TypeVar
18
19from . import exc as async_exc
20from ... import util
21from ...engine import Result
22from ...engine.result import _NO_ROW
23from ...engine.result import _R
24from ...engine.result import _WithKeys
25from ...engine.result import FilterResult
26from ...engine.result import FrozenResult
27from ...engine.result import ResultMetaData
28from ...engine.row import Row
29from ...engine.row import RowMapping
30from ...sql.base import _generative
31from ...util.concurrency import greenlet_spawn
32from ...util.typing import Literal
33from ...util.typing import Self
34
35if TYPE_CHECKING:
36 from ...engine import CursorResult
37 from ...engine.result import _KeyIndexType
38 from ...engine.result import _UniqueFilterType
39
40_T = TypeVar("_T", bound=Any)
41_TP = TypeVar("_TP", bound=Tuple[Any, ...])
42
43
44class AsyncCommon(FilterResult[_R]):
45 __slots__ = ()
46
47 _real_result: Result[Any]
48 _metadata: ResultMetaData
49
50 async def close(self) -> None: # type: ignore[override]
51 """Close this result."""
52
53 await greenlet_spawn(self._real_result.close)
54
55 @property
56 def closed(self) -> bool:
57 """proxies the .closed attribute of the underlying result object,
58 if any, else raises ``AttributeError``.
59
60 .. versionadded:: 2.0.0b3
61
62 """
63 return self._real_result.closed
64
65
66class AsyncResult(_WithKeys, AsyncCommon[Row[_TP]]):
67 """An asyncio wrapper around a :class:`_result.Result` object.
68
69 The :class:`_asyncio.AsyncResult` only applies to statement executions that
70 use a server-side cursor. It is returned only from the
71 :meth:`_asyncio.AsyncConnection.stream` and
72 :meth:`_asyncio.AsyncSession.stream` methods.
73
74 .. note:: As is the case with :class:`_engine.Result`, this object is
75 used for ORM results returned by :meth:`_asyncio.AsyncSession.execute`,
76 which can yield instances of ORM mapped objects either individually or
77 within tuple-like rows. Note that these result objects do not
78 deduplicate instances or rows automatically as is the case with the
79 legacy :class:`_orm.Query` object. For in-Python de-duplication of
80 instances or rows, use the :meth:`_asyncio.AsyncResult.unique` modifier
81 method.
82
83 .. versionadded:: 1.4
84
85 """
86
87 __slots__ = ()
88
89 _real_result: Result[_TP]
90
91 def __init__(self, real_result: Result[_TP]):
92 self._real_result = real_result
93
94 self._metadata = real_result._metadata
95 self._unique_filter_state = real_result._unique_filter_state
96 self._source_supports_scalars = real_result._source_supports_scalars
97 self._post_creational_filter = None
98
99 # BaseCursorResult pre-generates the "_row_getter". Use that
100 # if available rather than building a second one
101 if "_row_getter" in real_result.__dict__:
102 self._set_memoized_attribute(
103 "_row_getter", real_result.__dict__["_row_getter"]
104 )
105
106 @property
107 def t(self) -> AsyncTupleResult[_TP]:
108 """Apply a "typed tuple" typing filter to returned rows.
109
110 The :attr:`_asyncio.AsyncResult.t` attribute is a synonym for
111 calling the :meth:`_asyncio.AsyncResult.tuples` method.
112
113 .. versionadded:: 2.0
114
115 """
116 return self # type: ignore
117
118 def tuples(self) -> AsyncTupleResult[_TP]:
119 """Apply a "typed tuple" typing filter to returned rows.
120
121 This method returns the same :class:`_asyncio.AsyncResult` object
122 at runtime,
123 however annotates as returning a :class:`_asyncio.AsyncTupleResult`
124 object that will indicate to :pep:`484` typing tools that plain typed
125 ``Tuple`` instances are returned rather than rows. This allows
126 tuple unpacking and ``__getitem__`` access of :class:`_engine.Row`
127 objects to by typed, for those cases where the statement invoked
128 itself included typing information.
129
130 .. versionadded:: 2.0
131
132 :return: the :class:`_result.AsyncTupleResult` type at typing time.
133
134 .. seealso::
135
136 :attr:`_asyncio.AsyncResult.t` - shorter synonym
137
138 :attr:`_engine.Row.t` - :class:`_engine.Row` version
139
140 """
141
142 return self # type: ignore
143
144 @_generative
145 def unique(self, strategy: Optional[_UniqueFilterType] = None) -> Self:
146 """Apply unique filtering to the objects returned by this
147 :class:`_asyncio.AsyncResult`.
148
149 Refer to :meth:`_engine.Result.unique` in the synchronous
150 SQLAlchemy API for a complete behavioral description.
151
152 """
153 self._unique_filter_state = (set(), strategy)
154 return self
155
156 def columns(self, *col_expressions: _KeyIndexType) -> Self:
157 r"""Establish the columns that should be returned in each row.
158
159 Refer to :meth:`_engine.Result.columns` in the synchronous
160 SQLAlchemy API for a complete behavioral description.
161
162 """
163 return self._column_slices(col_expressions)
164
165 async def partitions(
166 self, size: Optional[int] = None
167 ) -> AsyncIterator[Sequence[Row[_TP]]]:
168 """Iterate through sub-lists of rows of the size given.
169
170 An async iterator is returned::
171
172 async def scroll_results(connection):
173 result = await connection.stream(select(users_table))
174
175 async for partition in result.partitions(100):
176 print("list of rows: %s" % partition)
177
178 Refer to :meth:`_engine.Result.partitions` in the synchronous
179 SQLAlchemy API for a complete behavioral description.
180
181 """
182
183 getter = self._manyrow_getter
184
185 while True:
186 partition = await greenlet_spawn(getter, self, size)
187 if partition:
188 yield partition
189 else:
190 break
191
192 async def fetchall(self) -> Sequence[Row[_TP]]:
193 """A synonym for the :meth:`_asyncio.AsyncResult.all` method.
194
195 .. versionadded:: 2.0
196
197 """
198
199 return await greenlet_spawn(self._allrows)
200
201 async def fetchone(self) -> Optional[Row[_TP]]:
202 """Fetch one row.
203
204 When all rows are exhausted, returns None.
205
206 This method is provided for backwards compatibility with
207 SQLAlchemy 1.x.x.
208
209 To fetch the first row of a result only, use the
210 :meth:`_asyncio.AsyncResult.first` method. To iterate through all
211 rows, iterate the :class:`_asyncio.AsyncResult` object directly.
212
213 :return: a :class:`_engine.Row` object if no filters are applied,
214 or ``None`` if no rows remain.
215
216 """
217 row = await greenlet_spawn(self._onerow_getter, self)
218 if row is _NO_ROW:
219 return None
220 else:
221 return row
222
223 async def fetchmany(
224 self, size: Optional[int] = None
225 ) -> Sequence[Row[_TP]]:
226 """Fetch many rows.
227
228 When all rows are exhausted, returns an empty list.
229
230 This method is provided for backwards compatibility with
231 SQLAlchemy 1.x.x.
232
233 To fetch rows in groups, use the
234 :meth:`._asyncio.AsyncResult.partitions` method.
235
236 :return: a list of :class:`_engine.Row` objects.
237
238 .. seealso::
239
240 :meth:`_asyncio.AsyncResult.partitions`
241
242 """
243
244 return await greenlet_spawn(self._manyrow_getter, self, size)
245
246 async def all(self) -> Sequence[Row[_TP]]:
247 """Return all rows in a list.
248
249 Closes the result set after invocation. Subsequent invocations
250 will return an empty list.
251
252 :return: a list of :class:`_engine.Row` objects.
253
254 """
255
256 return await greenlet_spawn(self._allrows)
257
258 def __aiter__(self) -> AsyncResult[_TP]:
259 return self
260
261 async def __anext__(self) -> Row[_TP]:
262 row = await greenlet_spawn(self._onerow_getter, self)
263 if row is _NO_ROW:
264 raise StopAsyncIteration()
265 else:
266 return row
267
268 async def first(self) -> Optional[Row[_TP]]:
269 """Fetch the first row or ``None`` if no row is present.
270
271 Closes the result set and discards remaining rows.
272
273 .. note:: This method returns one **row**, e.g. tuple, by default.
274 To return exactly one single scalar value, that is, the first
275 column of the first row, use the
276 :meth:`_asyncio.AsyncResult.scalar` method,
277 or combine :meth:`_asyncio.AsyncResult.scalars` and
278 :meth:`_asyncio.AsyncResult.first`.
279
280 Additionally, in contrast to the behavior of the legacy ORM
281 :meth:`_orm.Query.first` method, **no limit is applied** to the
282 SQL query which was invoked to produce this
283 :class:`_asyncio.AsyncResult`;
284 for a DBAPI driver that buffers results in memory before yielding
285 rows, all rows will be sent to the Python process and all but
286 the first row will be discarded.
287
288 .. seealso::
289
290 :ref:`migration_20_unify_select`
291
292 :return: a :class:`_engine.Row` object, or None
293 if no rows remain.
294
295 .. seealso::
296
297 :meth:`_asyncio.AsyncResult.scalar`
298
299 :meth:`_asyncio.AsyncResult.one`
300
301 """
302 return await greenlet_spawn(self._only_one_row, False, False, False)
303
304 async def one_or_none(self) -> Optional[Row[_TP]]:
305 """Return at most one result or raise an exception.
306
307 Returns ``None`` if the result has no rows.
308 Raises :class:`.MultipleResultsFound`
309 if multiple rows are returned.
310
311 .. versionadded:: 1.4
312
313 :return: The first :class:`_engine.Row` or ``None`` if no row
314 is available.
315
316 :raises: :class:`.MultipleResultsFound`
317
318 .. seealso::
319
320 :meth:`_asyncio.AsyncResult.first`
321
322 :meth:`_asyncio.AsyncResult.one`
323
324 """
325 return await greenlet_spawn(self._only_one_row, True, False, False)
326
327 @overload
328 async def scalar_one(self: AsyncResult[Tuple[_T]]) -> _T: ...
329
330 @overload
331 async def scalar_one(self) -> Any: ...
332
333 async def scalar_one(self) -> Any:
334 """Return exactly one scalar result or raise an exception.
335
336 This is equivalent to calling :meth:`_asyncio.AsyncResult.scalars` and
337 then :meth:`_asyncio.AsyncScalarResult.one`.
338
339 .. seealso::
340
341 :meth:`_asyncio.AsyncScalarResult.one`
342
343 :meth:`_asyncio.AsyncResult.scalars`
344
345 """
346 return await greenlet_spawn(self._only_one_row, True, True, True)
347
348 @overload
349 async def scalar_one_or_none(
350 self: AsyncResult[Tuple[_T]],
351 ) -> Optional[_T]: ...
352
353 @overload
354 async def scalar_one_or_none(self) -> Optional[Any]: ...
355
356 async def scalar_one_or_none(self) -> Optional[Any]:
357 """Return exactly one scalar result or ``None``.
358
359 This is equivalent to calling :meth:`_asyncio.AsyncResult.scalars` and
360 then :meth:`_asyncio.AsyncScalarResult.one_or_none`.
361
362 .. seealso::
363
364 :meth:`_asyncio.AsyncScalarResult.one_or_none`
365
366 :meth:`_asyncio.AsyncResult.scalars`
367
368 """
369 return await greenlet_spawn(self._only_one_row, True, False, True)
370
371 async def one(self) -> Row[_TP]:
372 """Return exactly one row or raise an exception.
373
374 Raises :class:`.NoResultFound` if the result returns no
375 rows, or :class:`.MultipleResultsFound` if multiple rows
376 would be returned.
377
378 .. note:: This method returns one **row**, e.g. tuple, by default.
379 To return exactly one single scalar value, that is, the first
380 column of the first row, use the
381 :meth:`_asyncio.AsyncResult.scalar_one` method, or combine
382 :meth:`_asyncio.AsyncResult.scalars` and
383 :meth:`_asyncio.AsyncResult.one`.
384
385 .. versionadded:: 1.4
386
387 :return: The first :class:`_engine.Row`.
388
389 :raises: :class:`.MultipleResultsFound`, :class:`.NoResultFound`
390
391 .. seealso::
392
393 :meth:`_asyncio.AsyncResult.first`
394
395 :meth:`_asyncio.AsyncResult.one_or_none`
396
397 :meth:`_asyncio.AsyncResult.scalar_one`
398
399 """
400 return await greenlet_spawn(self._only_one_row, True, True, False)
401
402 @overload
403 async def scalar(self: AsyncResult[Tuple[_T]]) -> Optional[_T]: ...
404
405 @overload
406 async def scalar(self) -> Any: ...
407
408 async def scalar(self) -> Any:
409 """Fetch the first column of the first row, and close the result set.
410
411 Returns ``None`` if there are no rows to fetch.
412
413 No validation is performed to test if additional rows remain.
414
415 After calling this method, the object is fully closed,
416 e.g. the :meth:`_engine.CursorResult.close`
417 method will have been called.
418
419 :return: a Python scalar value, or ``None`` if no rows remain.
420
421 """
422 return await greenlet_spawn(self._only_one_row, False, False, True)
423
424 async def freeze(self) -> FrozenResult[_TP]:
425 """Return a callable object that will produce copies of this
426 :class:`_asyncio.AsyncResult` when invoked.
427
428 The callable object returned is an instance of
429 :class:`_engine.FrozenResult`.
430
431 This is used for result set caching. The method must be called
432 on the result when it has been unconsumed, and calling the method
433 will consume the result fully. When the :class:`_engine.FrozenResult`
434 is retrieved from a cache, it can be called any number of times where
435 it will produce a new :class:`_engine.Result` object each time
436 against its stored set of rows.
437
438 .. seealso::
439
440 :ref:`do_orm_execute_re_executing` - example usage within the
441 ORM to implement a result-set cache.
442
443 """
444
445 return await greenlet_spawn(FrozenResult, self)
446
447 @overload
448 def scalars(
449 self: AsyncResult[Tuple[_T]], index: Literal[0]
450 ) -> AsyncScalarResult[_T]: ...
451
452 @overload
453 def scalars(self: AsyncResult[Tuple[_T]]) -> AsyncScalarResult[_T]: ...
454
455 @overload
456 def scalars(self, index: _KeyIndexType = 0) -> AsyncScalarResult[Any]: ...
457
458 def scalars(self, index: _KeyIndexType = 0) -> AsyncScalarResult[Any]:
459 """Return an :class:`_asyncio.AsyncScalarResult` filtering object which
460 will return single elements rather than :class:`_row.Row` objects.
461
462 Refer to :meth:`_result.Result.scalars` in the synchronous
463 SQLAlchemy API for a complete behavioral description.
464
465 :param index: integer or row key indicating the column to be fetched
466 from each row, defaults to ``0`` indicating the first column.
467
468 :return: a new :class:`_asyncio.AsyncScalarResult` filtering object
469 referring to this :class:`_asyncio.AsyncResult` object.
470
471 """
472 return AsyncScalarResult(self._real_result, index)
473
474 def mappings(self) -> AsyncMappingResult:
475 """Apply a mappings filter to returned rows, returning an instance of
476 :class:`_asyncio.AsyncMappingResult`.
477
478 When this filter is applied, fetching rows will return
479 :class:`_engine.RowMapping` objects instead of :class:`_engine.Row`
480 objects.
481
482 :return: a new :class:`_asyncio.AsyncMappingResult` filtering object
483 referring to the underlying :class:`_result.Result` object.
484
485 """
486
487 return AsyncMappingResult(self._real_result)
488
489
490class AsyncScalarResult(AsyncCommon[_R]):
491 """A wrapper for a :class:`_asyncio.AsyncResult` that returns scalar values
492 rather than :class:`_row.Row` values.
493
494 The :class:`_asyncio.AsyncScalarResult` object is acquired by calling the
495 :meth:`_asyncio.AsyncResult.scalars` method.
496
497 Refer to the :class:`_result.ScalarResult` object in the synchronous
498 SQLAlchemy API for a complete behavioral description.
499
500 .. versionadded:: 1.4
501
502 """
503
504 __slots__ = ()
505
506 _generate_rows = False
507
508 def __init__(self, real_result: Result[Any], index: _KeyIndexType):
509 self._real_result = real_result
510
511 if real_result._source_supports_scalars:
512 self._metadata = real_result._metadata
513 self._post_creational_filter = None
514 else:
515 self._metadata = real_result._metadata._reduce([index])
516 self._post_creational_filter = operator.itemgetter(0)
517
518 self._unique_filter_state = real_result._unique_filter_state
519
520 def unique(
521 self,
522 strategy: Optional[_UniqueFilterType] = None,
523 ) -> Self:
524 """Apply unique filtering to the objects returned by this
525 :class:`_asyncio.AsyncScalarResult`.
526
527 See :meth:`_asyncio.AsyncResult.unique` for usage details.
528
529 """
530 self._unique_filter_state = (set(), strategy)
531 return self
532
533 async def partitions(
534 self, size: Optional[int] = None
535 ) -> AsyncIterator[Sequence[_R]]:
536 """Iterate through sub-lists of elements of the size given.
537
538 Equivalent to :meth:`_asyncio.AsyncResult.partitions` except that
539 scalar values, rather than :class:`_engine.Row` objects,
540 are returned.
541
542 """
543
544 getter = self._manyrow_getter
545
546 while True:
547 partition = await greenlet_spawn(getter, self, size)
548 if partition:
549 yield partition
550 else:
551 break
552
553 async def fetchall(self) -> Sequence[_R]:
554 """A synonym for the :meth:`_asyncio.AsyncScalarResult.all` method."""
555
556 return await greenlet_spawn(self._allrows)
557
558 async def fetchmany(self, size: Optional[int] = None) -> Sequence[_R]:
559 """Fetch many objects.
560
561 Equivalent to :meth:`_asyncio.AsyncResult.fetchmany` except that
562 scalar values, rather than :class:`_engine.Row` objects,
563 are returned.
564
565 """
566 return await greenlet_spawn(self._manyrow_getter, self, size)
567
568 async def all(self) -> Sequence[_R]:
569 """Return all scalar values in a list.
570
571 Equivalent to :meth:`_asyncio.AsyncResult.all` except that
572 scalar values, rather than :class:`_engine.Row` objects,
573 are returned.
574
575 """
576 return await greenlet_spawn(self._allrows)
577
578 def __aiter__(self) -> AsyncScalarResult[_R]:
579 return self
580
581 async def __anext__(self) -> _R:
582 row = await greenlet_spawn(self._onerow_getter, self)
583 if row is _NO_ROW:
584 raise StopAsyncIteration()
585 else:
586 return row
587
588 async def first(self) -> Optional[_R]:
589 """Fetch the first object or ``None`` if no object is present.
590
591 Equivalent to :meth:`_asyncio.AsyncResult.first` except that
592 scalar values, rather than :class:`_engine.Row` objects,
593 are returned.
594
595 """
596 return await greenlet_spawn(self._only_one_row, False, False, False)
597
598 async def one_or_none(self) -> Optional[_R]:
599 """Return at most one object or raise an exception.
600
601 Equivalent to :meth:`_asyncio.AsyncResult.one_or_none` except that
602 scalar values, rather than :class:`_engine.Row` objects,
603 are returned.
604
605 """
606 return await greenlet_spawn(self._only_one_row, True, False, False)
607
608 async def one(self) -> _R:
609 """Return exactly one object or raise an exception.
610
611 Equivalent to :meth:`_asyncio.AsyncResult.one` except that
612 scalar values, rather than :class:`_engine.Row` objects,
613 are returned.
614
615 """
616 return await greenlet_spawn(self._only_one_row, True, True, False)
617
618
619class AsyncMappingResult(_WithKeys, AsyncCommon[RowMapping]):
620 """A wrapper for a :class:`_asyncio.AsyncResult` that returns dictionary
621 values rather than :class:`_engine.Row` values.
622
623 The :class:`_asyncio.AsyncMappingResult` object is acquired by calling the
624 :meth:`_asyncio.AsyncResult.mappings` method.
625
626 Refer to the :class:`_result.MappingResult` object in the synchronous
627 SQLAlchemy API for a complete behavioral description.
628
629 .. versionadded:: 1.4
630
631 """
632
633 __slots__ = ()
634
635 _generate_rows = True
636
637 _post_creational_filter = operator.attrgetter("_mapping")
638
639 def __init__(self, result: Result[Any]):
640 self._real_result = result
641 self._unique_filter_state = result._unique_filter_state
642 self._metadata = result._metadata
643 if result._source_supports_scalars:
644 self._metadata = self._metadata._reduce([0])
645
646 def unique(
647 self,
648 strategy: Optional[_UniqueFilterType] = None,
649 ) -> Self:
650 """Apply unique filtering to the objects returned by this
651 :class:`_asyncio.AsyncMappingResult`.
652
653 See :meth:`_asyncio.AsyncResult.unique` for usage details.
654
655 """
656 self._unique_filter_state = (set(), strategy)
657 return self
658
659 def columns(self, *col_expressions: _KeyIndexType) -> Self:
660 r"""Establish the columns that should be returned in each row."""
661 return self._column_slices(col_expressions)
662
663 async def partitions(
664 self, size: Optional[int] = None
665 ) -> AsyncIterator[Sequence[RowMapping]]:
666 """Iterate through sub-lists of elements of the size given.
667
668 Equivalent to :meth:`_asyncio.AsyncResult.partitions` except that
669 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
670 objects, are returned.
671
672 """
673
674 getter = self._manyrow_getter
675
676 while True:
677 partition = await greenlet_spawn(getter, self, size)
678 if partition:
679 yield partition
680 else:
681 break
682
683 async def fetchall(self) -> Sequence[RowMapping]:
684 """A synonym for the :meth:`_asyncio.AsyncMappingResult.all` method."""
685
686 return await greenlet_spawn(self._allrows)
687
688 async def fetchone(self) -> Optional[RowMapping]:
689 """Fetch one object.
690
691 Equivalent to :meth:`_asyncio.AsyncResult.fetchone` except that
692 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
693 objects, are returned.
694
695 """
696
697 row = await greenlet_spawn(self._onerow_getter, self)
698 if row is _NO_ROW:
699 return None
700 else:
701 return row
702
703 async def fetchmany(
704 self, size: Optional[int] = None
705 ) -> Sequence[RowMapping]:
706 """Fetch many rows.
707
708 Equivalent to :meth:`_asyncio.AsyncResult.fetchmany` except that
709 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
710 objects, are returned.
711
712 """
713
714 return await greenlet_spawn(self._manyrow_getter, self, size)
715
716 async def all(self) -> Sequence[RowMapping]:
717 """Return all rows in a list.
718
719 Equivalent to :meth:`_asyncio.AsyncResult.all` except that
720 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
721 objects, are returned.
722
723 """
724
725 return await greenlet_spawn(self._allrows)
726
727 def __aiter__(self) -> AsyncMappingResult:
728 return self
729
730 async def __anext__(self) -> RowMapping:
731 row = await greenlet_spawn(self._onerow_getter, self)
732 if row is _NO_ROW:
733 raise StopAsyncIteration()
734 else:
735 return row
736
737 async def first(self) -> Optional[RowMapping]:
738 """Fetch the first object or ``None`` if no object is present.
739
740 Equivalent to :meth:`_asyncio.AsyncResult.first` except that
741 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
742 objects, are returned.
743
744 """
745 return await greenlet_spawn(self._only_one_row, False, False, False)
746
747 async def one_or_none(self) -> Optional[RowMapping]:
748 """Return at most one object or raise an exception.
749
750 Equivalent to :meth:`_asyncio.AsyncResult.one_or_none` except that
751 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
752 objects, are returned.
753
754 """
755 return await greenlet_spawn(self._only_one_row, True, False, False)
756
757 async def one(self) -> RowMapping:
758 """Return exactly one object or raise an exception.
759
760 Equivalent to :meth:`_asyncio.AsyncResult.one` except that
761 :class:`_engine.RowMapping` values, rather than :class:`_engine.Row`
762 objects, are returned.
763
764 """
765 return await greenlet_spawn(self._only_one_row, True, True, False)
766
767
768class AsyncTupleResult(AsyncCommon[_R], util.TypingOnly):
769 """A :class:`_asyncio.AsyncResult` that's typed as returning plain
770 Python tuples instead of rows.
771
772 Since :class:`_engine.Row` acts like a tuple in every way already,
773 this class is a typing only class, regular :class:`_asyncio.AsyncResult` is
774 still used at runtime.
775
776 """
777
778 __slots__ = ()
779
780 if TYPE_CHECKING:
781
782 async def partitions(
783 self, size: Optional[int] = None
784 ) -> AsyncIterator[Sequence[_R]]:
785 """Iterate through sub-lists of elements of the size given.
786
787 Equivalent to :meth:`_result.Result.partitions` except that
788 tuple values, rather than :class:`_engine.Row` objects,
789 are returned.
790
791 """
792 ...
793
794 async def fetchone(self) -> Optional[_R]:
795 """Fetch one tuple.
796
797 Equivalent to :meth:`_result.Result.fetchone` except that
798 tuple values, rather than :class:`_engine.Row`
799 objects, are returned.
800
801 """
802 ...
803
804 async def fetchall(self) -> Sequence[_R]:
805 """A synonym for the :meth:`_engine.ScalarResult.all` method."""
806 ...
807
808 async def fetchmany(self, size: Optional[int] = None) -> Sequence[_R]:
809 """Fetch many objects.
810
811 Equivalent to :meth:`_result.Result.fetchmany` except that
812 tuple values, rather than :class:`_engine.Row` objects,
813 are returned.
814
815 """
816 ...
817
818 async def all(self) -> Sequence[_R]: # noqa: A001
819 """Return all scalar values in a list.
820
821 Equivalent to :meth:`_result.Result.all` except that
822 tuple values, rather than :class:`_engine.Row` objects,
823 are returned.
824
825 """
826 ...
827
828 def __aiter__(self) -> AsyncIterator[_R]: ...
829
830 async def __anext__(self) -> _R: ...
831
832 async def first(self) -> Optional[_R]:
833 """Fetch the first object or ``None`` if no object is present.
834
835 Equivalent to :meth:`_result.Result.first` except that
836 tuple values, rather than :class:`_engine.Row` objects,
837 are returned.
838
839
840 """
841 ...
842
843 async def one_or_none(self) -> Optional[_R]:
844 """Return at most one object or raise an exception.
845
846 Equivalent to :meth:`_result.Result.one_or_none` except that
847 tuple values, rather than :class:`_engine.Row` objects,
848 are returned.
849
850 """
851 ...
852
853 async def one(self) -> _R:
854 """Return exactly one object or raise an exception.
855
856 Equivalent to :meth:`_result.Result.one` except that
857 tuple values, rather than :class:`_engine.Row` objects,
858 are returned.
859
860 """
861 ...
862
863 @overload
864 async def scalar_one(self: AsyncTupleResult[Tuple[_T]]) -> _T: ...
865
866 @overload
867 async def scalar_one(self) -> Any: ...
868
869 async def scalar_one(self) -> Any:
870 """Return exactly one scalar result or raise an exception.
871
872 This is equivalent to calling :meth:`_engine.Result.scalars`
873 and then :meth:`_engine.AsyncScalarResult.one`.
874
875 .. seealso::
876
877 :meth:`_engine.AsyncScalarResult.one`
878
879 :meth:`_engine.Result.scalars`
880
881 """
882 ...
883
884 @overload
885 async def scalar_one_or_none(
886 self: AsyncTupleResult[Tuple[_T]],
887 ) -> Optional[_T]: ...
888
889 @overload
890 async def scalar_one_or_none(self) -> Optional[Any]: ...
891
892 async def scalar_one_or_none(self) -> Optional[Any]:
893 """Return exactly one or no scalar result.
894
895 This is equivalent to calling :meth:`_engine.Result.scalars`
896 and then :meth:`_engine.AsyncScalarResult.one_or_none`.
897
898 .. seealso::
899
900 :meth:`_engine.AsyncScalarResult.one_or_none`
901
902 :meth:`_engine.Result.scalars`
903
904 """
905 ...
906
907 @overload
908 async def scalar(
909 self: AsyncTupleResult[Tuple[_T]],
910 ) -> Optional[_T]: ...
911
912 @overload
913 async def scalar(self) -> Any: ...
914
915 async def scalar(self) -> Any:
916 """Fetch the first column of the first row, and close the result
917 set.
918
919 Returns ``None`` if there are no rows to fetch.
920
921 No validation is performed to test if additional rows remain.
922
923 After calling this method, the object is fully closed,
924 e.g. the :meth:`_engine.CursorResult.close`
925 method will have been called.
926
927 :return: a Python scalar value , or ``None`` if no rows remain.
928
929 """
930 ...
931
932
933_RT = TypeVar("_RT", bound="Result[Any]")
934
935
936async def _ensure_sync_result(result: _RT, calling_method: Any) -> _RT:
937 cursor_result: CursorResult[Any]
938
939 try:
940 is_cursor = result._is_cursor
941 except AttributeError:
942 # legacy execute(DefaultGenerator) case
943 return result
944
945 if not is_cursor:
946 cursor_result = getattr(result, "raw", None) # type: ignore
947 else:
948 cursor_result = result # type: ignore
949 if cursor_result and cursor_result.context._is_server_side:
950 await greenlet_spawn(cursor_result.close)
951 raise async_exc.AsyncMethodRequired(
952 "Can't use the %s.%s() method with a "
953 "server-side cursor. "
954 "Use the %s.stream() method for an async "
955 "streaming result set."
956 % (
957 calling_method.__self__.__class__.__name__,
958 calling_method.__name__,
959 calling_method.__self__.__class__.__name__,
960 )
961 )
962
963 if is_cursor and cursor_result.cursor is not None:
964 await cursor_result.cursor._async_soft_close()
965 return result