Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/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

137 statements  

1# engine/row.py 

2# Copyright (C) 2005-2024 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 

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

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

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

366 

367 """ 

368 

369 __slots__ = () 

370 

371 if TYPE_CHECKING: 

372 

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

374 

375 else: 

376 __getitem__ = BaseRow._get_by_key_impl_mapping 

377 

378 def _values_impl(self) -> List[Any]: 

379 return list(self._data) 

380 

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

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

383 

384 def __len__(self) -> int: 

385 return len(self._data) 

386 

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

388 return self._parent._has_key(key) 

389 

390 def __repr__(self) -> str: 

391 return repr(dict(self)) 

392 

393 def items(self) -> ROMappingItemsView: 

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

395 underlying :class:`.Row`. 

396 

397 """ 

398 return ROMappingItemsView( 

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

400 ) 

401 

402 def keys(self) -> RMKeyView: 

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

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

405 

406 """ 

407 

408 return self._parent.keys 

409 

410 def values(self) -> ROMappingKeysValuesView: 

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

412 underlying :class:`.Row`. 

413 

414 """ 

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