Skip to content

Commit 266ffe9

Browse files
zzzeekGerrit Code Review
authored andcommitted
Merge "Provide special row proxies for count and index"
2 parents 8b27555 + aabc72b commit 266ffe9

File tree

3 files changed

+75
-0
lines changed

3 files changed

+75
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.. change::
2+
:tags: bug, engine
3+
:tickets: 6074
4+
5+
The Python ``namedtuple()`` has the behavior such that the names ``count``
6+
and ``index`` will be served as tuple values if the named tuple includes
7+
those names; if they are absent, then their behavior as methods of
8+
``collections.abc.Sequence`` is maintained. Therefore the
9+
:class:`_result.Row` and :class:`_result.LegacyRow` classes have been fixed
10+
so that they work in this same way, maintaining the expected behavior for
11+
database rows that have columns named "index" or "count".

lib/sqlalchemy/engine/row.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,27 @@ def _mapping(self):
220220
self._data,
221221
)
222222

223+
def _special_name_accessor(name):
224+
"""Handle ambiguous names such as "count" and "index" """
225+
226+
@property
227+
def go(self):
228+
if self._parent._has_key(name):
229+
return self.__getattr__(name)
230+
else:
231+
232+
def meth(*arg, **kw):
233+
return getattr(collections_abc.Sequence, name)(
234+
self, *arg, **kw
235+
)
236+
237+
return meth
238+
239+
return go
240+
241+
count = _special_name_accessor("count")
242+
index = _special_name_accessor("index")
243+
223244
def __contains__(self, key):
224245
return key in self._data
225246

test/sql/test_resultset.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
from sqlalchemy.engine import cursor as _cursor
2929
from sqlalchemy.engine import default
3030
from sqlalchemy.engine import Row
31+
from sqlalchemy.engine.result import SimpleResultMetaData
32+
from sqlalchemy.engine.row import LegacyRow
3133
from sqlalchemy.ext.compiler import compiles
3234
from sqlalchemy.sql import ColumnElement
3335
from sqlalchemy.sql import expression
@@ -1324,6 +1326,47 @@ def test_row_is_sequence(self):
13241326
)
13251327
is_true(isinstance(row, collections_abc.Sequence))
13261328

1329+
@testing.combinations((Row,), (LegacyRow,))
1330+
def test_row_special_names(self, row_cls):
1331+
metadata = SimpleResultMetaData(["key", "count", "index"])
1332+
row = row_cls(
1333+
metadata,
1334+
[None, None, None],
1335+
metadata._keymap,
1336+
Row._default_key_style,
1337+
["kv", "cv", "iv"],
1338+
)
1339+
is_true(isinstance(row, collections_abc.Sequence))
1340+
1341+
eq_(row.key, "kv")
1342+
eq_(row.count, "cv")
1343+
eq_(row.index, "iv")
1344+
1345+
if isinstance(row, LegacyRow):
1346+
eq_(row["count"], "cv")
1347+
eq_(row["index"], "iv")
1348+
1349+
eq_(row._mapping["count"], "cv")
1350+
eq_(row._mapping["index"], "iv")
1351+
1352+
metadata = SimpleResultMetaData(["key", "q", "p"])
1353+
1354+
row = row_cls(
1355+
metadata,
1356+
[None, None, None],
1357+
metadata._keymap,
1358+
Row._default_key_style,
1359+
["kv", "cv", "iv"],
1360+
)
1361+
is_true(isinstance(row, collections_abc.Sequence))
1362+
1363+
eq_(row.key, "kv")
1364+
eq_(row.q, "cv")
1365+
eq_(row.p, "iv")
1366+
eq_(row.index("cv"), 1)
1367+
eq_(row.count("cv"), 1)
1368+
eq_(row.count("x"), 0)
1369+
13271370
def test_row_is_hashable(self):
13281371

13291372
row = Row(

0 commit comments

Comments
 (0)