_py_row.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. # engine/_py_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. from __future__ import annotations
  8. import operator
  9. import typing
  10. from typing import Any
  11. from typing import Callable
  12. from typing import Dict
  13. from typing import Iterator
  14. from typing import List
  15. from typing import Mapping
  16. from typing import Optional
  17. from typing import Tuple
  18. from typing import Type
  19. if typing.TYPE_CHECKING:
  20. from .result import _KeyType
  21. from .result import _ProcessorsType
  22. from .result import _RawRowType
  23. from .result import _TupleGetterType
  24. from .result import ResultMetaData
  25. MD_INDEX = 0 # integer index in cursor.description
  26. class BaseRow:
  27. __slots__ = ("_parent", "_data", "_key_to_index")
  28. _parent: ResultMetaData
  29. _key_to_index: Mapping[_KeyType, int]
  30. _data: _RawRowType
  31. def __init__(
  32. self,
  33. parent: ResultMetaData,
  34. processors: Optional[_ProcessorsType],
  35. key_to_index: Mapping[_KeyType, int],
  36. data: _RawRowType,
  37. ):
  38. """Row objects are constructed by CursorResult objects."""
  39. object.__setattr__(self, "_parent", parent)
  40. object.__setattr__(self, "_key_to_index", key_to_index)
  41. if processors:
  42. object.__setattr__(
  43. self,
  44. "_data",
  45. tuple(
  46. [
  47. proc(value) if proc else value
  48. for proc, value in zip(processors, data)
  49. ]
  50. ),
  51. )
  52. else:
  53. object.__setattr__(self, "_data", tuple(data))
  54. def __reduce__(self) -> Tuple[Callable[..., BaseRow], Tuple[Any, ...]]:
  55. return (
  56. rowproxy_reconstructor,
  57. (self.__class__, self.__getstate__()),
  58. )
  59. def __getstate__(self) -> Dict[str, Any]:
  60. return {"_parent": self._parent, "_data": self._data}
  61. def __setstate__(self, state: Dict[str, Any]) -> None:
  62. parent = state["_parent"]
  63. object.__setattr__(self, "_parent", parent)
  64. object.__setattr__(self, "_data", state["_data"])
  65. object.__setattr__(self, "_key_to_index", parent._key_to_index)
  66. def _values_impl(self) -> List[Any]:
  67. return list(self)
  68. def __iter__(self) -> Iterator[Any]:
  69. return iter(self._data)
  70. def __len__(self) -> int:
  71. return len(self._data)
  72. def __hash__(self) -> int:
  73. return hash(self._data)
  74. def __getitem__(self, key: Any) -> Any:
  75. return self._data[key]
  76. def _get_by_key_impl_mapping(self, key: str) -> Any:
  77. try:
  78. return self._data[self._key_to_index[key]]
  79. except KeyError:
  80. pass
  81. self._parent._key_not_found(key, False)
  82. def __getattr__(self, name: str) -> Any:
  83. try:
  84. return self._data[self._key_to_index[name]]
  85. except KeyError:
  86. pass
  87. self._parent._key_not_found(name, True)
  88. def _to_tuple_instance(self) -> Tuple[Any, ...]:
  89. return self._data
  90. # This reconstructor is necessary so that pickles with the Cy extension or
  91. # without use the same Binary format.
  92. def rowproxy_reconstructor(
  93. cls: Type[BaseRow], state: Dict[str, Any]
  94. ) -> BaseRow:
  95. obj = cls.__new__(cls)
  96. obj.__setstate__(state)
  97. return obj
  98. def tuplegetter(*indexes: int) -> _TupleGetterType:
  99. if len(indexes) != 1:
  100. for i in range(1, len(indexes)):
  101. if indexes[i - 1] != indexes[i] - 1:
  102. return operator.itemgetter(*indexes)
  103. # slice form is faster but returns a list if input is list
  104. return operator.itemgetter(slice(indexes[0], indexes[-1] + 1))