1# engine/row.py 
    2# Copyright (C) 2005-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 
    7 
    8"""Define row constructs including :class:`.Row`.""" 
    9 
    10from __future__ import annotations 
    11 
    12from abc import ABC 
    13import collections.abc as collections_abc 
    14import operator 
    15import typing 
    16from typing import Any 
    17from typing import Callable 
    18from typing import Dict 
    19from typing import Generic 
    20from typing import Iterator 
    21from typing import List 
    22from typing import Mapping 
    23from typing import NoReturn 
    24from typing import Optional 
    25from typing import Sequence 
    26from typing import Tuple 
    27from typing import TYPE_CHECKING 
    28 
    29from ._row_cy import BaseRow as BaseRow 
    30from ..sql import util as sql_util 
    31from ..util import deprecated 
    32from ..util.typing import TypeVarTuple 
    33from ..util.typing import Unpack 
    34 
    35if TYPE_CHECKING: 
    36    from typing import Tuple as _RowBase 
    37 
    38    from .result import _KeyType 
    39    from .result import _ProcessorsType 
    40    from .result import RMKeyView 
    41else: 
    42    _RowBase = Sequence 
    43 
    44 
    45_Ts = TypeVarTuple("_Ts") 
    46 
    47 
    48class Row(BaseRow, _RowBase[Unpack[_Ts]], Generic[Unpack[_Ts]]): 
    49    """Represent a single result row. 
    50 
    51    The :class:`.Row` object represents a row of a database result.  It is 
    52    typically associated in the 1.x series of SQLAlchemy with the 
    53    :class:`_engine.CursorResult` object, however is also used by the ORM for 
    54    tuple-like results as of SQLAlchemy 1.4. 
    55 
    56    The :class:`.Row` object seeks to act as much like a Python named 
    57    tuple as possible.   For mapping (i.e. dictionary) behavior on a row, 
    58    such as testing for containment of keys, refer to the :attr:`.Row._mapping` 
    59    attribute. 
    60 
    61    .. seealso:: 
    62 
    63        :ref:`tutorial_selecting_data` - includes examples of selecting 
    64        rows from SELECT statements. 
    65 
    66    .. versionchanged:: 1.4 
    67 
    68        Renamed ``RowProxy`` to :class:`.Row`. :class:`.Row` is no longer a 
    69        "proxy" object in that it contains the final form of data within it, 
    70        and now acts mostly like a named tuple. Mapping-like functionality is 
    71        moved to the :attr:`.Row._mapping` attribute. See 
    72        :ref:`change_4710_core` for background on this change. 
    73 
    74    """ 
    75 
    76    __slots__ = () 
    77 
    78    def __setattr__(self, name: str, value: Any) -> NoReturn: 
    79        raise AttributeError("can't set attribute") 
    80 
    81    def __delattr__(self, name: str) -> NoReturn: 
    82        raise AttributeError("can't delete attribute") 
    83 
    84    @deprecated( 
    85        "2.1.0", 
    86        "The :meth:`.Row._tuple` method is deprecated, :class:`.Row` " 
    87        "now behaves like a tuple and can unpack types directly.", 
    88    ) 
    89    def _tuple(self) -> Tuple[Unpack[_Ts]]: 
    90        """Return a 'tuple' form of this :class:`.Row`. 
    91 
    92        At runtime, this method returns "self"; the :class:`.Row` object is 
    93        already a named tuple. However, at the typing level, if this 
    94        :class:`.Row` is typed, the "tuple" return type will be a :pep:`484` 
    95        ``Tuple`` datatype that contains typing information about individual 
    96        elements, supporting typed unpacking and attribute access. 
    97 
    98        .. versionadded:: 2.0.19 - The :meth:`.Row._tuple` method supersedes 
    99           the previous :meth:`.Row.tuple` method, which is now underscored 
    100           to avoid name conflicts with column names in the same way as other 
    101           named-tuple methods on :class:`.Row`. 
    102 
    103        .. seealso:: 
    104 
    105            :ref:`change_10635` - describes a migration path from this 
    106            workaround for SQLAlchemy 2.1. 
    107 
    108            :attr:`.Row._t` - shorthand attribute notation 
    109 
    110            :meth:`.Result.tuples` 
    111 
    112 
    113        """ 
    114        return self 
    115 
    116    @deprecated( 
    117        "2.0.19", 
    118        "The :meth:`.Row.tuple` method is deprecated in favor of " 
    119        ":meth:`.Row._tuple`; all :class:`.Row` " 
    120        "methods and library-level attributes are intended to be underscored " 
    121        "to avoid name conflicts.  Please use :meth:`Row._tuple`.", 
    122    ) 
    123    def tuple(self) -> Tuple[Unpack[_Ts]]: 
    124        """Return a 'tuple' form of this :class:`.Row`. 
    125 
    126        .. versionadded:: 2.0 
    127 
    128        .. seealso:: 
    129 
    130            :ref:`change_10635` - describes a migration path from this 
    131            workaround for SQLAlchemy 2.1. 
    132 
    133        """ 
    134        return self._tuple() 
    135 
    136    @property 
    137    @deprecated( 
    138        "2.1.0", 
    139        "The :attr:`.Row._t` attribute is deprecated, :class:`.Row` " 
    140        "now behaves like a tuple and can unpack types directly.", 
    141    ) 
    142    def _t(self) -> Tuple[Unpack[_Ts]]: 
    143        """A synonym for :meth:`.Row._tuple`. 
    144 
    145        .. versionadded:: 2.0.19 - The :attr:`.Row._t` attribute supersedes 
    146           the previous :attr:`.Row.t` attribute, which is now underscored 
    147           to avoid name conflicts with column names in the same way as other 
    148           named-tuple methods on :class:`.Row`. 
    149 
    150        .. seealso:: 
    151 
    152            :ref:`change_10635` - describes a migration path from this 
    153            workaround for SQLAlchemy 2.1. 
    154 
    155            :attr:`.Result.t` 
    156        """ 
    157        return self 
    158 
    159    @property 
    160    @deprecated( 
    161        "2.0.19", 
    162        "The :attr:`.Row.t` attribute is deprecated in favor of " 
    163        ":attr:`.Row._t`; all :class:`.Row` " 
    164        "methods and library-level attributes are intended to be underscored " 
    165        "to avoid name conflicts.  Please use :attr:`Row._t`.", 
    166    ) 
    167    def t(self) -> Tuple[Unpack[_Ts]]: 
    168        """A synonym for :meth:`.Row._tuple`. 
    169 
    170        .. versionadded:: 2.0 
    171 
    172        .. seealso:: 
    173 
    174            :ref:`change_10635` - describes a migration path from this 
    175            workaround for SQLAlchemy 2.1. 
    176 
    177        """ 
    178        return self._t 
    179 
    180    @property 
    181    def _mapping(self) -> RowMapping: 
    182        """Return a :class:`.RowMapping` for this :class:`.Row`. 
    183 
    184        This object provides a consistent Python mapping (i.e. dictionary) 
    185        interface for the data contained within the row.   The :class:`.Row` 
    186        by itself behaves like a named tuple. 
    187 
    188        .. seealso:: 
    189 
    190            :attr:`.Row._fields` 
    191 
    192        .. versionadded:: 1.4 
    193 
    194        """ 
    195        return RowMapping(self._parent, None, self._key_to_index, self._data) 
    196 
    197    def _filter_on_values( 
    198        self, processor: Optional[_ProcessorsType] 
    199    ) -> Row[Unpack[_Ts]]: 
    200        return Row(self._parent, processor, self._key_to_index, self._data) 
    201 
    202    if not TYPE_CHECKING: 
    203 
    204        def _special_name_accessor(name: str) -> Any: 
    205            """Handle ambiguous names such as "count" and "index" """ 
    206 
    207            @property 
    208            def go(self: Row) -> Any: 
    209                if self._parent._has_key(name): 
    210                    return self.__getattr__(name) 
    211                else: 
    212 
    213                    def meth(*arg: Any, **kw: Any) -> Any: 
    214                        return getattr(collections_abc.Sequence, name)( 
    215                            self, *arg, **kw 
    216                        ) 
    217 
    218                    return meth 
    219 
    220            return go 
    221 
    222        count = _special_name_accessor("count") 
    223        index = _special_name_accessor("index") 
    224 
    225    def __contains__(self, key: Any) -> bool: 
    226        return key in self._data 
    227 
    228    def _op(self, other: Any, op: Callable[[Any, Any], bool]) -> bool: 
    229        return ( 
    230            op(self._to_tuple_instance(), other._to_tuple_instance()) 
    231            if isinstance(other, Row) 
    232            else op(self._to_tuple_instance(), other) 
    233        ) 
    234 
    235    __hash__ = BaseRow.__hash__ 
    236 
    237    def __lt__(self, other: Any) -> bool: 
    238        return self._op(other, operator.lt) 
    239 
    240    def __le__(self, other: Any) -> bool: 
    241        return self._op(other, operator.le) 
    242 
    243    def __ge__(self, other: Any) -> bool: 
    244        return self._op(other, operator.ge) 
    245 
    246    def __gt__(self, other: Any) -> bool: 
    247        return self._op(other, operator.gt) 
    248 
    249    def __eq__(self, other: Any) -> bool: 
    250        return self._op(other, operator.eq) 
    251 
    252    def __ne__(self, other: Any) -> bool: 
    253        return self._op(other, operator.ne) 
    254 
    255    def __repr__(self) -> str: 
    256        return repr(sql_util._repr_row(self)) 
    257 
    258    @property 
    259    def _fields(self) -> Tuple[str, ...]: 
    260        """Return a tuple of string keys as represented by this 
    261        :class:`.Row`. 
    262 
    263        The keys can represent the labels of the columns returned by a core 
    264        statement or the names of the orm classes returned by an orm 
    265        execution. 
    266 
    267        This attribute is analogous to the Python named tuple ``._fields`` 
    268        attribute. 
    269 
    270        .. versionadded:: 1.4 
    271 
    272        .. seealso:: 
    273 
    274            :attr:`.Row._mapping` 
    275 
    276        """ 
    277        return tuple([k for k in self._parent.keys if k is not None]) 
    278 
    279    def _asdict(self) -> Dict[str, Any]: 
    280        """Return a new dict which maps field names to their corresponding 
    281        values. 
    282 
    283        This method is analogous to the Python named tuple ``._asdict()`` 
    284        method, and works by applying the ``dict()`` constructor to the 
    285        :attr:`.Row._mapping` attribute. 
    286 
    287        .. versionadded:: 1.4 
    288 
    289        .. seealso:: 
    290 
    291            :attr:`.Row._mapping` 
    292 
    293        """ 
    294        return dict(self._mapping) 
    295 
    296 
    297BaseRowProxy = BaseRow 
    298RowProxy = Row 
    299 
    300 
    301class ROMappingView(ABC): 
    302    __slots__ = () 
    303 
    304    _items: Sequence[Any] 
    305    _mapping: Mapping["_KeyType", Any] 
    306 
    307    def __init__( 
    308        self, mapping: Mapping["_KeyType", Any], items: Sequence[Any] 
    309    ): 
    310        self._mapping = mapping  # type: ignore[misc] 
    311        self._items = items  # type: ignore[misc] 
    312 
    313    def __len__(self) -> int: 
    314        return len(self._items) 
    315 
    316    def __repr__(self) -> str: 
    317        return "{0.__class__.__name__}({0._mapping!r})".format(self) 
    318 
    319    def __iter__(self) -> Iterator[Any]: 
    320        return iter(self._items) 
    321 
    322    def __contains__(self, item: Any) -> bool: 
    323        return item in self._items 
    324 
    325    def __eq__(self, other: Any) -> bool: 
    326        return list(other) == list(self) 
    327 
    328    def __ne__(self, other: Any) -> bool: 
    329        return list(other) != list(self) 
    330 
    331 
    332class ROMappingKeysValuesView( 
    333    ROMappingView, typing.KeysView["_KeyType"], typing.ValuesView[Any] 
    334): 
    335    __slots__ = ("_items",)  # mapping slot is provided by KeysView 
    336 
    337 
    338class ROMappingItemsView(ROMappingView, typing.ItemsView["_KeyType", Any]): 
    339    __slots__ = ("_items",)  # mapping slot is provided by ItemsView 
    340 
    341 
    342class RowMapping(BaseRow, typing.Mapping["_KeyType", Any]): 
    343    """A ``Mapping`` that maps column names and objects to :class:`.Row` 
    344    values. 
    345 
    346    The :class:`.RowMapping` is available from a :class:`.Row` via the 
    347    :attr:`.Row._mapping` attribute, as well as from the iterable interface 
    348    provided by the :class:`.MappingResult` object returned by the 
    349    :meth:`_engine.Result.mappings` method. 
    350 
    351    :class:`.RowMapping` supplies Python mapping (i.e. dictionary) access to 
    352    the  contents of the row.   This includes support for testing of 
    353    containment of specific keys (string column names or objects), as well 
    354    as iteration of keys, values, and items:: 
    355 
    356        for row in result: 
    357            if "a" in row._mapping: 
    358                print("Column 'a': %s" % row._mapping["a"]) 
    359 
    360            print("Column b: %s" % row._mapping[table.c.b]) 
    361 
    362    .. versionadded:: 1.4 The :class:`.RowMapping` object replaces the 
    363       mapping-like access previously provided by a database result row, 
    364       which now seeks to behave mostly like a named tuple. 
    365 
    366    """ 
    367 
    368    __slots__ = () 
    369 
    370    if TYPE_CHECKING: 
    371 
    372        def __getitem__(self, key: _KeyType) -> Any: ... 
    373 
    374    else: 
    375        __getitem__ = BaseRow._get_by_key_impl_mapping 
    376 
    377    def _values_impl(self) -> List[Any]: 
    378        return list(self._data) 
    379 
    380    def __iter__(self) -> Iterator[str]: 
    381        return (k for k in self._parent.keys if k is not None) 
    382 
    383    def __len__(self) -> int: 
    384        return len(self._data) 
    385 
    386    def __contains__(self, key: object) -> bool: 
    387        return self._parent._has_key(key) 
    388 
    389    def __repr__(self) -> str: 
    390        return repr(dict(self)) 
    391 
    392    def items(self) -> ROMappingItemsView: 
    393        """Return a view of key/value tuples for the elements in the 
    394        underlying :class:`.Row`. 
    395 
    396        """ 
    397        return ROMappingItemsView( 
    398            self, [(key, self[key]) for key in self.keys()] 
    399        ) 
    400 
    401    def keys(self) -> RMKeyView: 
    402        """Return a view of 'keys' for string column names represented 
    403        by the underlying :class:`.Row`. 
    404 
    405        """ 
    406 
    407        return self._parent.keys 
    408 
    409    def values(self) -> ROMappingKeysValuesView: 
    410        """Return a view of values for the values represented in the 
    411        underlying :class:`.Row`. 
    412 
    413        """ 
    414        return ROMappingKeysValuesView(self, self._values_impl())