Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/row.py: 67%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

126 statements  

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 Mapping 

22from typing import Optional 

23from typing import Sequence 

24from typing import Tuple 

25from typing import TYPE_CHECKING 

26 

27from ._row_cy import BaseRow as BaseRow 

28from ..sql import util as sql_util 

29from ..util import deprecated 

30from ..util.typing import TypeVarTuple 

31from ..util.typing import Unpack 

32 

33if TYPE_CHECKING: 

34 from typing import Tuple as _RowBase 

35 

36 from .result import _KeyType 

37 from .result import _ProcessorsType 

38 from .result import RMKeyView 

39else: 

40 _RowBase = Sequence 

41 

42 

43_Ts = TypeVarTuple("_Ts") 

44 

45 

46class Row(BaseRow, _RowBase[Unpack[_Ts]], Generic[Unpack[_Ts]]): 

47 """Represent a single result row. 

48 

49 The :class:`.Row` object represents a row of a database result. It is 

50 typically associated in the 1.x series of SQLAlchemy with the 

51 :class:`_engine.CursorResult` object, however is also used by the ORM for 

52 tuple-like results as of SQLAlchemy 1.4. 

53 

54 The :class:`.Row` object seeks to act as much like a Python named 

55 tuple as possible. For mapping (i.e. dictionary) behavior on a row, 

56 such as testing for containment of keys, refer to the :attr:`.Row._mapping` 

57 attribute. 

58 

59 .. seealso:: 

60 

61 :ref:`tutorial_selecting_data` - includes examples of selecting 

62 rows from SELECT statements. 

63 

64 .. versionchanged:: 1.4 

65 

66 Renamed ``RowProxy`` to :class:`.Row`. :class:`.Row` is no longer a 

67 "proxy" object in that it contains the final form of data within it, 

68 and now acts mostly like a named tuple. Mapping-like functionality is 

69 moved to the :attr:`.Row._mapping` attribute. See 

70 :ref:`change_4710_core` for background on this change. 

71 

72 """ 

73 

74 __slots__ = () 

75 

76 @deprecated( 

77 "2.1.0", 

78 "The :meth:`.Row._tuple` method is deprecated, :class:`.Row` " 

79 "now behaves like a tuple and can unpack types directly.", 

80 ) 

81 def _tuple(self) -> Tuple[Unpack[_Ts]]: 

82 """Return a 'tuple' form of this :class:`.Row`. 

83 

84 At runtime, this method returns "self"; the :class:`.Row` object is 

85 already a named tuple. However, at the typing level, if this 

86 :class:`.Row` is typed, the "tuple" return type will be a :pep:`484` 

87 ``Tuple`` datatype that contains typing information about individual 

88 elements, supporting typed unpacking and attribute access. 

89 

90 .. versionadded:: 2.0.19 - The :meth:`.Row._tuple` method supersedes 

91 the previous :meth:`.Row.tuple` method, which is now underscored 

92 to avoid name conflicts with column names in the same way as other 

93 named-tuple methods on :class:`.Row`. 

94 

95 .. seealso:: 

96 

97 :ref:`change_10635` - describes a migration path from this 

98 workaround for SQLAlchemy 2.1. 

99 

100 :attr:`.Row._t` - shorthand attribute notation 

101 

102 :meth:`.Result.tuples` 

103 

104 

105 """ 

106 return self 

107 

108 @deprecated( 

109 "2.0.19", 

110 "The :meth:`.Row.tuple` method is deprecated in favor of " 

111 ":meth:`.Row._tuple`; all :class:`.Row` " 

112 "methods and library-level attributes are intended to be underscored " 

113 "to avoid name conflicts. Please use :meth:`Row._tuple`.", 

114 ) 

115 def tuple(self) -> Tuple[Unpack[_Ts]]: 

116 """Return a 'tuple' form of this :class:`.Row`. 

117 

118 .. versionadded:: 2.0 

119 

120 .. seealso:: 

121 

122 :ref:`change_10635` - describes a migration path from this 

123 workaround for SQLAlchemy 2.1. 

124 

125 """ 

126 return self._tuple() 

127 

128 @property 

129 @deprecated( 

130 "2.1.0", 

131 "The :attr:`.Row._t` attribute is deprecated, :class:`.Row` " 

132 "now behaves like a tuple and can unpack types directly.", 

133 ) 

134 def _t(self) -> Tuple[Unpack[_Ts]]: 

135 """A synonym for :meth:`.Row._tuple`. 

136 

137 .. versionadded:: 2.0.19 - The :attr:`.Row._t` attribute supersedes 

138 the previous :attr:`.Row.t` attribute, which is now underscored 

139 to avoid name conflicts with column names in the same way as other 

140 named-tuple methods on :class:`.Row`. 

141 

142 .. seealso:: 

143 

144 :ref:`change_10635` - describes a migration path from this 

145 workaround for SQLAlchemy 2.1. 

146 

147 :attr:`.Result.t` 

148 """ 

149 return self 

150 

151 @property 

152 @deprecated( 

153 "2.0.19", 

154 "The :attr:`.Row.t` attribute is deprecated in favor of " 

155 ":attr:`.Row._t`; all :class:`.Row` " 

156 "methods and library-level attributes are intended to be underscored " 

157 "to avoid name conflicts. Please use :attr:`Row._t`.", 

158 ) 

159 def t(self) -> Tuple[Unpack[_Ts]]: 

160 """A synonym for :meth:`.Row._tuple`. 

161 

162 .. versionadded:: 2.0 

163 

164 .. seealso:: 

165 

166 :ref:`change_10635` - describes a migration path from this 

167 workaround for SQLAlchemy 2.1. 

168 

169 """ 

170 return self._t 

171 

172 @property 

173 def _mapping(self) -> RowMapping: 

174 """Return a :class:`.RowMapping` for this :class:`.Row`. 

175 

176 This object provides a consistent Python mapping (i.e. dictionary) 

177 interface for the data contained within the row. The :class:`.Row` 

178 by itself behaves like a named tuple. 

179 

180 .. seealso:: 

181 

182 :attr:`.Row._fields` 

183 

184 .. versionadded:: 1.4 

185 

186 """ 

187 return RowMapping(self._parent, None, self._key_to_index, self._data) 

188 

189 def _filter_on_values( 

190 self, processor: Optional[_ProcessorsType] 

191 ) -> Row[Unpack[_Ts]]: 

192 return Row(self._parent, processor, self._key_to_index, self._data) 

193 

194 if not TYPE_CHECKING: 

195 

196 def _special_name_accessor(name: str) -> Any: 

197 """Handle ambiguous names such as "count" and "index" """ 

198 

199 @property 

200 def go(self: Row) -> Any: 

201 if self._parent._has_key(name): 

202 return self.__getattr__(name) 

203 else: 

204 

205 def meth(*arg: Any, **kw: Any) -> Any: 

206 return getattr(collections_abc.Sequence, name)( 

207 self, *arg, **kw 

208 ) 

209 

210 return meth 

211 

212 return go 

213 

214 count = _special_name_accessor("count") 

215 index = _special_name_accessor("index") 

216 

217 def _op(self, other: Any, op: Callable[[Any, Any], bool]) -> bool: 

218 return ( 

219 op(self._to_tuple_instance(), other._to_tuple_instance()) 

220 if isinstance(other, Row) 

221 else op(self._to_tuple_instance(), other) 

222 ) 

223 

224 __hash__ = BaseRow.__hash__ 

225 

226 def __lt__(self, other: Any) -> bool: 

227 return self._op(other, operator.lt) 

228 

229 def __le__(self, other: Any) -> bool: 

230 return self._op(other, operator.le) 

231 

232 def __ge__(self, other: Any) -> bool: 

233 return self._op(other, operator.ge) 

234 

235 def __gt__(self, other: Any) -> bool: 

236 return self._op(other, operator.gt) 

237 

238 def __eq__(self, other: Any) -> bool: 

239 return self._op(other, operator.eq) 

240 

241 def __ne__(self, other: Any) -> bool: 

242 return self._op(other, operator.ne) 

243 

244 def __repr__(self) -> str: 

245 return repr(sql_util._repr_row(self)) 

246 

247 @property 

248 def _fields(self) -> Tuple[str, ...]: 

249 """Return a tuple of string keys as represented by this 

250 :class:`.Row`. 

251 

252 The keys can represent the labels of the columns returned by a core 

253 statement or the names of the orm classes returned by an orm 

254 execution. 

255 

256 This attribute is analogous to the Python named tuple ``._fields`` 

257 attribute. 

258 

259 .. versionadded:: 1.4 

260 

261 .. seealso:: 

262 

263 :attr:`.Row._mapping` 

264 

265 """ 

266 return tuple([k for k in self._parent.keys if k is not None]) 

267 

268 def _asdict(self) -> Dict[str, Any]: 

269 """Return a new dict which maps field names to their corresponding 

270 values. 

271 

272 This method is analogous to the Python named tuple ``._asdict()`` 

273 method, and works by applying the ``dict()`` constructor to the 

274 :attr:`.Row._mapping` attribute. 

275 

276 .. versionadded:: 1.4 

277 

278 .. seealso:: 

279 

280 :attr:`.Row._mapping` 

281 

282 """ 

283 return dict(self._mapping) 

284 

285 

286BaseRowProxy = BaseRow 

287RowProxy = Row 

288 

289 

290class ROMappingView(ABC): 

291 __slots__ = () 

292 

293 _items: Sequence[Any] 

294 _mapping: Mapping["_KeyType", Any] 

295 

296 def __init__( 

297 self, mapping: Mapping["_KeyType", Any], items: Sequence[Any] 

298 ): 

299 self._mapping = mapping # type: ignore[misc] 

300 self._items = items # type: ignore[misc] 

301 

302 def __len__(self) -> int: 

303 return len(self._items) 

304 

305 def __repr__(self) -> str: 

306 return "{0.__class__.__name__}({0._mapping!r})".format(self) 

307 

308 def __iter__(self) -> Iterator[Any]: 

309 return iter(self._items) 

310 

311 def __contains__(self, item: Any) -> bool: 

312 return item in self._items 

313 

314 def __eq__(self, other: Any) -> bool: 

315 return list(other) == list(self) 

316 

317 def __ne__(self, other: Any) -> bool: 

318 return list(other) != list(self) 

319 

320 

321class ROMappingKeysValuesView( 

322 ROMappingView, typing.KeysView["_KeyType"], typing.ValuesView[Any] 

323): 

324 __slots__ = ("_items",) # mapping slot is provided by KeysView 

325 

326 

327class ROMappingItemsView(ROMappingView, typing.ItemsView["_KeyType", Any]): 

328 __slots__ = ("_items",) # mapping slot is provided by ItemsView 

329 

330 

331class RowMapping(BaseRow, typing.Mapping["_KeyType", Any]): 

332 """A ``Mapping`` that maps column names and objects to :class:`.Row` 

333 values. 

334 

335 The :class:`.RowMapping` is available from a :class:`.Row` via the 

336 :attr:`.Row._mapping` attribute, as well as from the iterable interface 

337 provided by the :class:`.MappingResult` object returned by the 

338 :meth:`_engine.Result.mappings` method. 

339 

340 :class:`.RowMapping` supplies Python mapping (i.e. dictionary) access to 

341 the contents of the row. This includes support for testing of 

342 containment of specific keys (string column names or objects), as well 

343 as iteration of keys, values, and items:: 

344 

345 for row in result: 

346 if "a" in row._mapping: 

347 print("Column 'a': %s" % row._mapping["a"]) 

348 

349 print("Column b: %s" % row._mapping[table.c.b]) 

350 

351 .. versionadded:: 1.4 The :class:`.RowMapping` object replaces the 

352 mapping-like access previously provided by a database result row, 

353 which now seeks to behave mostly like a named tuple. 

354 

355 """ 

356 

357 __slots__ = () 

358 

359 if TYPE_CHECKING: 

360 

361 def __getitem__(self, key: _KeyType) -> Any: ... 

362 

363 else: 

364 __getitem__ = BaseRow._get_by_key_impl_mapping 

365 

366 def __iter__(self) -> Iterator[str]: 

367 return (k for k in self._parent.keys if k is not None) 

368 

369 def __contains__(self, key: object) -> bool: 

370 return self._parent._has_key(key) 

371 

372 def __repr__(self) -> str: 

373 return repr(dict(self)) 

374 

375 def items(self) -> ROMappingItemsView: 

376 """Return a view of key/value tuples for the elements in the 

377 underlying :class:`.Row`. 

378 

379 """ 

380 return ROMappingItemsView( 

381 self, [(key, self[key]) for key in self.keys()] 

382 ) 

383 

384 def keys(self) -> RMKeyView: 

385 """Return a view of 'keys' for string column names represented 

386 by the underlying :class:`.Row`. 

387 

388 """ 

389 

390 return self._parent.keys 

391 

392 def values(self) -> ROMappingKeysValuesView: 

393 """Return a view of values for the values represented in the 

394 underlying :class:`.Row`. 

395 

396 """ 

397 return ROMappingKeysValuesView(self, self._values_impl())