summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/engine/row.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2021-04-07 13:57:39 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2021-04-08 16:06:41 -0400
commitdca3a43de60b064d863abb9b86b1f629fbd4b14d (patch)
tree09d531c9e0a4b0178343d2942c0e7e6d7d2cf846 /lib/sqlalchemy/engine/row.py
parent4d21920638af4729b6ff09b1ac8c3a20878bd922 (diff)
downloadsqlalchemy-dca3a43de60b064d863abb9b86b1f629fbd4b14d.tar.gz
Fix LegacyRow/Row index access
Fixed up the behavior of the :class:`_result.Row` object when dictionary access is used upon it, meaning converting to a dict via ``dict(row)`` or accessing members using strings or other objects i.e. ``row["some_key"]`` works as it would with a dictionary, rather than raising ``TypeError`` as would be the case with a tuple, whether or not the C extensions are in place. This was originally supposed to emit a 2.0 deprecation warning for the "non-future" case using :class:`_result.LegacyRow`, and was to raise ``TypeError`` for the "future" :class:`_result.Row` class. However, the C version of :class:`_result.Row` was failing to raise this ``TypeError``, and to complicate matters, the :meth:`_orm.Session.execute` method now returns :class:`_result.Row` in all cases to maintain consistency with the ORM result case, so users who didn't have C extensions installed would see different behavior in this one case for existing pre-1.4 style code. Therefore, in order to soften the overall upgrade scheme as most users have not been exposed to the more strict behavior of :class:`_result.Row` up through 1.4.6, :class:`_result.LegacyRow` and :class:`_result.Row` both provide for string-key access as well as support for ``dict(row)``, in all cases emitting the 2.0 deprecation warning when ``SQLALCHEMY_WARN_20`` is enabled. The :class:`_result.Row` object still uses tuple-like behavior for ``__contains__``, which is probably the only noticeable behavioral change compared to :class:`_result.LegacyRow`, other than the removal of dictionary-style methods ``values()`` and ``items()``. Also remove filters for result set warnings. callcounts updated for 2.7/ 3.9, am pushing jenkins to use python 3.9 now Fixes: #6218 Change-Id: Ia69b974f3dbc46943c57423f57ec82323c8ae63b
Diffstat (limited to 'lib/sqlalchemy/engine/row.py')
-rw-r--r--lib/sqlalchemy/engine/row.py32
1 files changed, 26 insertions, 6 deletions
diff --git a/lib/sqlalchemy/engine/row.py b/lib/sqlalchemy/engine/row.py
index b870e6534..7c15b7f6e 100644
--- a/lib/sqlalchemy/engine/row.py
+++ b/lib/sqlalchemy/engine/row.py
@@ -40,9 +40,18 @@ except ImportError:
KEY_INTEGER_ONLY = 0
+"""__getitem__ only allows integer values, raises TypeError otherwise"""
+
KEY_OBJECTS_ONLY = 1
+"""__getitem__ only allows string/object values, raises TypeError otherwise"""
+
KEY_OBJECTS_BUT_WARN = 2
+"""__getitem__ allows integer or string/object values, but emits a 2.0
+deprecation warning if string/object is passed"""
+
KEY_OBJECTS_NO_WARN = 3
+"""__getitem__ allows integer or string/object values with no warnings
+or errors."""
try:
from sqlalchemy.cresultproxy import BaseRow
@@ -100,15 +109,16 @@ except ImportError:
def __hash__(self):
return hash(self._data)
- def __getitem__(self, key):
+ def _get_by_int_impl(self, key):
return self._data[key]
- _get_by_int_impl = __getitem__
-
def _get_by_key_impl(self, key):
if int in key.__class__.__mro__:
return self._data[key]
+ if self._key_style == KEY_INTEGER_ONLY:
+ self._parent._raise_for_nonint(key)
+
# the following is all LegacyRow support. none of this
# should be called if not LegacyRow
# assert isinstance(self, LegacyRow)
@@ -132,6 +142,12 @@ except ImportError:
return self._data[mdindex]
+ # The original 1.4 plan was that Row would not allow row["str"]
+ # access, however as the C extensions were inadvertently allowing
+ # this coupled with the fact that orm Session sets future=True,
+ # this allows a softer upgrade path. see #6218
+ __getitem__ = _get_by_key_impl
+
def _get_by_key_impl_mapping(self, key):
try:
rec = self._keymap[key]
@@ -192,7 +208,8 @@ class Row(BaseRow, collections_abc.Sequence):
__slots__ = ()
- _default_key_style = KEY_INTEGER_ONLY
+ # in 2.0, this should be KEY_INTEGER_ONLY
+ _default_key_style = KEY_OBJECTS_BUT_WARN
@property
def _mapping(self):
@@ -384,8 +401,11 @@ class LegacyRow(Row):
def __contains__(self, key):
return self._parent._contains(key, self)
- if not _baserow_usecext:
- __getitem__ = BaseRow._get_by_key_impl
+ # prior to #6218, LegacyRow would redirect the behavior of __getitem__
+ # for the non C version of BaseRow. This is now set up by Python BaseRow
+ # in all cases
+ # if not _baserow_usecext:
+ # __getitem__ = BaseRow._get_by_key_impl
@util.deprecated(
"1.4",