From aec57258b3b33fe070ebb54f31f1627db07f072b Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 8 Jul 2018 19:10:36 -0400 Subject: [PATCH] Check tokens in chop path for inspectionattr before calling is_mapper Fixed regression in 1.2.9 due to :ticket:`4287` where using a :class::`.Load` option in conjunction with a string wildcard would result in a TypeError. Change-Id: I2997ead0b8b9fa0edd009aa6f3161f4618fab97b Fixes: #4298 --- doc/build/changelog/unreleased_12/4298.rst | 7 +++ lib/sqlalchemy/orm/strategy_options.py | 6 ++- test/orm/test_options.py | 52 +++++++++++++++++++++- 3 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 doc/build/changelog/unreleased_12/4298.rst diff --git a/doc/build/changelog/unreleased_12/4298.rst b/doc/build/changelog/unreleased_12/4298.rst new file mode 100644 index 0000000000..8066ccdd07 --- /dev/null +++ b/doc/build/changelog/unreleased_12/4298.rst @@ -0,0 +1,7 @@ +.. change:: + :tags: bug, orm + :tickets: 4298 + + Fixed regression in 1.2.9 due to :ticket:`4287` where using a + :class::`.Load` option in conjunction with a string wildcard would result + in a TypeError. diff --git a/lib/sqlalchemy/orm/strategy_options.py b/lib/sqlalchemy/orm/strategy_options.py index 90d14075cb..af9bd71b6a 100644 --- a/lib/sqlalchemy/orm/strategy_options.py +++ b/lib/sqlalchemy/orm/strategy_options.py @@ -13,7 +13,8 @@ from .attributes import QueryableAttribute from .. import util from ..sql.base import _generative, Generative from .. import exc as sa_exc, inspect -from .base import _is_aliased_class, _class_to_mapper, _is_mapped_class +from .base import _is_aliased_class, _class_to_mapper, _is_mapped_class, \ + InspectionAttr from . import util as orm_util from .path_registry import PathRegistry, TokenRegistry, \ _WILDCARD_TOKEN, _DEFAULT_TOKEN @@ -385,7 +386,8 @@ class Load(Generative, MapperOption): if c_token is p_token: continue - elif c_token.is_mapper and p_token.is_mapper and \ + elif isinstance(c_token, InspectionAttr) and \ + c_token.is_mapper and p_token.is_mapper and \ c_token.isa(p_token): continue else: diff --git a/test/orm/test_options.py b/test/orm/test_options.py index 3c1f6527db..e94b9e6b63 100644 --- a/test/orm/test_options.py +++ b/test/orm/test_options.py @@ -2,7 +2,7 @@ from sqlalchemy import inspect from sqlalchemy.orm import attributes, mapper, relationship, backref, \ configure_mappers, create_session, synonym, Session, class_mapper, \ aliased, column_property, joinedload_all, joinedload, Query,\ - util as orm_util, Load, defer, defaultload + util as orm_util, Load, defer, defaultload, lazyload from sqlalchemy.orm.query import QueryContext from sqlalchemy.orm import strategy_options import sqlalchemy as sa @@ -1228,6 +1228,56 @@ class CacheKeyTest(PathTest, QueryTest): None ) + def test_bound_cache_key_wildcard_one(self): + # do not change this test, it is testing + # a specific condition in Load._chop_path(). + User, Address = self.classes('User', 'Address') + + query_path = self._make_path_registry([User, "addresses"]) + + opt = Load(User).lazyload("*") + eq_( + opt._generate_cache_key(query_path), + None + ) + + def test_unbound_cache_key_wildcard_one(self): + User, Address = self.classes('User', 'Address') + + query_path = self._make_path_registry([User, "addresses"]) + + opt = lazyload("*") + eq_( + opt._generate_cache_key(query_path), + (('relationship:_sa_default', ('lazy', 'select')),) + ) + + def test_bound_cache_key_wildcard_two(self): + User, Address, Order, Item, SubItem, Keyword = self.classes( + 'User', 'Address', 'Order', 'Item', 'SubItem', "Keyword") + + query_path = self._make_path_registry([User]) + + opt = Load(User).lazyload("orders").lazyload("*") + eq_( + opt._generate_cache_key(query_path), + (('orders', Order, ('lazy', 'select')), + ('orders', Order, 'relationship:*', ('lazy', 'select'))) + ) + + def test_unbound_cache_key_wildcard_two(self): + User, Address, Order, Item, SubItem, Keyword = self.classes( + 'User', 'Address', 'Order', 'Item', 'SubItem', "Keyword") + + query_path = self._make_path_registry([User]) + + opt = lazyload("orders").lazyload("*") + eq_( + opt._generate_cache_key(query_path), + (('orders', Order, ('lazy', 'select')), + ('orders', Order, 'relationship:*', ('lazy', 'select'))) + ) + def test_unbound_cache_key_of_type_subclass_relationship(self): User, Address, Order, Item, SubItem, Keyword = self.classes( 'User', 'Address', 'Order', 'Item', 'SubItem', "Keyword") -- 2.47.2