]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Improve error messages in the area of loader options
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 9 Jan 2019 07:01:16 +0000 (02:01 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 26 Jan 2019 01:59:08 +0000 (20:59 -0500)
Improved error messages emitted by the ORM in the area of loader option
traversal.  This includes early detection of mis-matched loader strategies
along with a clearer explanation why these strategies don't match.

Fixes: #4433
Change-Id: I3351b64241f7f62ca141a0be95085e6ef8ca6d32

16 files changed:
doc/build/changelog/unreleased_13/4433.rst [new file with mode: 0644]
lib/sqlalchemy/orm/exc.py
lib/sqlalchemy/orm/interfaces.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/strategy_options.py
lib/sqlalchemy/orm/util.py
lib/sqlalchemy/util/__init__.py
lib/sqlalchemy/util/langhelpers.py
test/ext/declarative/test_basic.py
test/orm/inheritance/test_basic.py
test/orm/test_deprecations.py
test/orm/test_mapper.py
test/orm/test_options.py
test/orm/test_relationships.py
test/orm/test_sync.py
test/profiles.txt

diff --git a/doc/build/changelog/unreleased_13/4433.rst b/doc/build/changelog/unreleased_13/4433.rst
new file mode 100644 (file)
index 0000000..a77354f
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+   :tags: bug, orm
+   :tickets: 4433
+
+   Improved error messages emitted by the ORM in the area of loader option
+   traversal.  This includes early detection of mis-matched loader strategies
+   along with a clearer explanation why these strategies don't match.
+
index e983844f6c74bbac696e7e65ca1b0e45129ca018..f061a6a56deb825ad11d557bc3416e873e635c77 100644 (file)
@@ -150,6 +150,38 @@ class MultipleResultsFound(sa_exc.InvalidRequestError):
     """A single database result was required but more than one were found."""
 
 
+class LoaderStrategyException(sa_exc.InvalidRequestError):
+    """A loader strategy for an attribute does not exist."""
+
+    def __init__(
+        self,
+        applied_to_property_type,
+        requesting_property,
+        applies_to,
+        actual_strategy_type,
+        strategy_key,
+    ):
+        if actual_strategy_type is None:
+            sa_exc.InvalidRequestError.__init__(
+                self,
+                "Can't find strategy %s for %s"
+                % (strategy_key, requesting_property),
+            )
+        else:
+            sa_exc.InvalidRequestError.__init__(
+                self,
+                'Can\'t apply "%s" strategy to property "%s", '
+                'which is a "%s"; this loader strategy is intended '
+                'to be used with a "%s".'
+                % (
+                    util.clsname_as_plain_name(actual_strategy_type),
+                    requesting_property,
+                    util.clsname_as_plain_name(applied_to_property_type),
+                    util.clsname_as_plain_name(applies_to),
+                ),
+            )
+
+
 def _safe_cls_name(cls):
     try:
         cls_name = ".".join((cls.__module__, cls.__name__))
index 87b4cfcde87d79bf8da341a52af5e5fb9b72147c..d2b08a9087b58a2619221f949a8e8dceb6eabb54 100644 (file)
@@ -22,6 +22,7 @@ from __future__ import absolute_import
 
 import collections
 
+from . import exc as orm_exc
 from . import path_registry
 from .base import _MappedAttribute  # noqa
 from .base import EXT_CONTINUE
@@ -536,7 +537,7 @@ class StrategizedProperty(MapperProperty):
         try:
             return self._strategies[key]
         except KeyError:
-            cls = self._strategy_lookup(*key)
+            cls = self._strategy_lookup(self, *key)
             self._strategies[key] = self._strategies[cls] = strategy = cls(
                 self, key
             )
@@ -592,7 +593,7 @@ class StrategizedProperty(MapperProperty):
         return decorate
 
     @classmethod
-    def _strategy_lookup(cls, *key):
+    def _strategy_lookup(cls, requesting_property, *key):
         for prop_cls in cls.__mro__:
             if prop_cls in cls._all_strategies:
                 strategies = cls._all_strategies[prop_cls]
@@ -600,7 +601,23 @@ class StrategizedProperty(MapperProperty):
                     return strategies[key]
                 except KeyError:
                     pass
-        raise Exception("can't locate strategy for %s %s" % (cls, key))
+
+        for property_type, strats in cls._all_strategies.items():
+            if key in strats:
+                intended_property_type = property_type
+                actual_strategy = strats[key]
+                break
+        else:
+            intended_property_type = None
+            actual_strategy = None
+
+        raise orm_exc.LoaderStrategyException(
+            cls,
+            requesting_property,
+            intended_property_type,
+            actual_strategy,
+            key,
+        )
 
 
 class MapperOption(object):
index 12b8b426897b775864dc4b24ce3557af25ba682e..9909724d3c9c1db0c3a3620c53f1e0a663033f6b 100644 (file)
@@ -2001,12 +2001,12 @@ class Mapper(InspectionAttr):
         return "<Mapper at 0x%x; %s>" % (id(self), self.class_.__name__)
 
     def __str__(self):
-        return "Mapper|%s|%s%s" % (
+        return "mapped class %s%s->%s" % (
             self.class_.__name__,
-            self.local_table is not None
-            and self.local_table.description
-            or None,
-            self.non_primary and "|non-primary" or "",
+            self.non_primary and " (non-primary)" or "",
+            self.local_table.description
+            if self.local_table is not None
+            else self.persist_selectable.description,
         )
 
     def _is_orphan(self, state):
index cac23f6be0fc7c5665a631588a346ce9f924205b..19c81860e80f9bf5aa9388b7b51cceec5ef90a5f 100644 (file)
@@ -180,10 +180,11 @@ class Load(Generative, MapperOption):
         else:
             query._attributes.update(self.context)
 
-    def _generate_path(self, path, attr, wildcard_key, raiseerr=True):
+    def _generate_path(
+        self, path, attr, for_strategy, wildcard_key, raiseerr=True
+    ):
         existing_of_type = self._of_type
         self._of_type = None
-
         if raiseerr and not path.has_entity:
             if isinstance(path, TokenRegistry):
                 raise sa_exc.ArgumentError(
@@ -191,9 +192,8 @@ class Load(Generative, MapperOption):
                 )
             else:
                 raise sa_exc.ArgumentError(
-                    "Attribute '%s' of entity '%s' does not "
-                    "refer to a mapped entity"
-                    % (path.prop.key, path.parent.entity)
+                    "Mapped attribute '%s' does not "
+                    "refer to a mapped entity" % (path.prop,)
                 )
 
         if isinstance(attr, util.string_types):
@@ -219,13 +219,13 @@ class Load(Generative, MapperOption):
             except AttributeError:
                 if raiseerr:
                     raise sa_exc.ArgumentError(
-                        "Can't find property named '%s' on the "
-                        "mapped entity %s in this Query. " % (attr, ent)
+                        'Can\'t find property named "%s" on '
+                        "%s in this Query. " % (attr, ent)
                     )
                 else:
                     return None
             else:
-                attr = attr.property
+                attr = found_property = attr.property
 
             path = path[attr]
         elif _is_mapped_class(attr):
@@ -238,7 +238,7 @@ class Load(Generative, MapperOption):
                 else:
                     return None
         else:
-            prop = attr.property
+            prop = found_property = attr.property
 
             if not prop.parent.common_parent(path.mapper):
                 if raiseerr:
@@ -298,6 +298,8 @@ class Load(Generative, MapperOption):
             else:
                 path = path[prop]
 
+        if for_strategy is not None:
+            found_property._get_strategy(for_strategy)
         if path.has_entity:
             path = path.entity_path
         self.path = path
@@ -320,7 +322,7 @@ class Load(Generative, MapperOption):
         self.is_class_strategy = False
         self.propagate_to_loaders = propagate_to_loaders
         # if the path is a wildcard, this will set propagate_to_loaders=False
-        self._generate_path(self.path, attr, "relationship")
+        self._generate_path(self.path, attr, strategy, "relationship")
         self.strategy = strategy
         if strategy is not None:
             self._set_path_strategy()
@@ -333,7 +335,7 @@ class Load(Generative, MapperOption):
         for attr in attrs:
             cloned = self._generate()
             cloned.strategy = strategy
-            cloned._generate_path(self.path, attr, "column")
+            cloned._generate_path(self.path, attr, strategy, "column")
             cloned.propagate_to_loaders = True
             if opts:
                 cloned.local_opts.update(opts)
@@ -347,7 +349,7 @@ class Load(Generative, MapperOption):
         strategy = self._coerce_strat(strategy)
 
         for attr in attrs:
-            path = self._generate_path(self.path, attr, None)
+            path = self._generate_path(self.path, attr, strategy, None)
             cloned = self._generate()
             cloned.strategy = strategy
             cloned.path = path
@@ -359,7 +361,7 @@ class Load(Generative, MapperOption):
         strategy = self._coerce_strat(strategy)
         cloned = self._generate()
         cloned.is_class_strategy = True
-        path = cloned._generate_path(self.path, None, None)
+        path = cloned._generate_path(self.path, None, strategy, None)
         cloned.strategy = strategy
         cloned.path = path
         cloned.propagate_to_loaders = True
@@ -482,7 +484,7 @@ class _UnboundLoad(Load):
     def _set_path_strategy(self):
         self._to_bind.append(self)
 
-    def _generate_path(self, path, attr, wildcard_key):
+    def _generate_path(self, path, attr, for_strategy, wildcard_key):
         if (
             wildcard_key
             and isinstance(attr, util.string_types)
@@ -670,7 +672,7 @@ class _UnboundLoad(Load):
         elif isinstance(token, PropComparator):
             prop = token.property
             entity = self._find_entity_prop_comparator(
-                entities, prop.key, token._parententity, raiseerr
+                entities, prop, token._parententity, raiseerr
             )
         elif self.is_class_strategy and _is_mapped_class(token):
             entity = inspect(token)
@@ -703,9 +705,14 @@ class _UnboundLoad(Load):
         path = loader.path
 
         if not loader.is_class_strategy:
-            for token in start_path:
+            for idx, token in enumerate(start_path):
+
                 if not loader._generate_path(
-                    loader.path, token, None, raiseerr
+                    loader.path,
+                    token,
+                    self.strategy if idx == len(start_path) - 1 else None,
+                    None,
+                    raiseerr,
                 ):
                     return
 
@@ -738,7 +745,7 @@ class _UnboundLoad(Load):
 
         return loader
 
-    def _find_entity_prop_comparator(self, entities, token, mapper, raiseerr):
+    def _find_entity_prop_comparator(self, entities, prop, mapper, raiseerr):
         if _is_aliased_class(mapper):
             searchfor = mapper
         else:
@@ -750,15 +757,18 @@ class _UnboundLoad(Load):
             if raiseerr:
                 if not list(entities):
                     raise sa_exc.ArgumentError(
-                        "Query has only expression-based entities - "
-                        "can't find property named '%s'." % (token,)
+                        "Query has only expression-based entities, "
+                        'which do not apply to %s "%s"'
+                        % (util.clsname_as_plain_name(type(prop)), prop)
                     )
                 else:
                     raise sa_exc.ArgumentError(
-                        "Can't find property '%s' on any entity "
-                        "specified in this Query.  Note the full path "
-                        "from root (%s) to target entity must be specified."
-                        % (token, ",".join(str(x) for x in entities))
+                        'Mapped attribute "%s" does not apply to any of the '
+                        "root entities in this query, e.g. %s. Please "
+                        "specify the full path "
+                        "from one of the root entities to the target "
+                        "attribute. "
+                        % (prop, ", ".join(str(x) for x in entities))
                     )
             else:
                 return None
@@ -768,9 +778,17 @@ class _UnboundLoad(Load):
             if len(list(entities)) != 1:
                 if raiseerr:
                     raise sa_exc.ArgumentError(
-                        "Wildcard loader can only be used with exactly "
-                        "one entity.  Use Load(ent) to specify "
-                        "specific entities."
+                        "Can't apply wildcard ('*') or load_only() "
+                        "loader option to multiple entities %s. Specify "
+                        "loader options for each entity individually, such "
+                        "as %s."
+                        % (
+                            ", ".join(str(ent) for ent in entities),
+                            ", ".join(
+                                "Load(%s).some_option('*')" % ent
+                                for ent in entities
+                            ),
+                        )
                     )
         elif token.endswith(_DEFAULT_TOKEN):
             raiseerr = False
@@ -784,7 +802,7 @@ class _UnboundLoad(Load):
             if raiseerr:
                 raise sa_exc.ArgumentError(
                     "Query has only expression-based entities - "
-                    "can't find property named '%s'." % (token,)
+                    'can\'t find property named "%s".' % (token,)
                 )
             else:
                 return None
index 8873a6a72af2220d5aa5099ea2dcd3d0db9e6050..92dd4c4ecde13fba45f9cd2585b8aeae3f063046 100644 (file)
@@ -530,6 +530,9 @@ class AliasedClass(object):
             self._aliased_insp._target.__name__,
         )
 
+    def __str__(self):
+        return str(self._aliased_insp)
+
 
 class AliasedInsp(InspectionAttr):
     """Provide an inspection interface for an
@@ -714,6 +717,9 @@ class AliasedInsp(InspectionAttr):
             with_poly,
         )
 
+    def __str__(self):
+        return "aliased(%s)" % (self._target.__name__,)
+
 
 inspection._inspects(AliasedClass)(lambda target: target._aliased_insp)
 inspection._inspects(AliasedInsp)(lambda target: target)
index 383c143c4ce554ef5beadaca6d2e681a5f940908..1e54ef80bf53eaf191d97c0575ba0a816f7d2b35 100644 (file)
@@ -102,6 +102,7 @@ from .langhelpers import bool_or_str  # noqa
 from .langhelpers import chop_traceback  # noqa
 from .langhelpers import class_hierarchy  # noqa
 from .langhelpers import classproperty  # noqa
+from .langhelpers import clsname_as_plain_name  # noqa
 from .langhelpers import coerce_kw_type  # noqa
 from .langhelpers import constructor_copy  # noqa
 from .langhelpers import counter  # noqa
index 1bc3c00de27c1d114814954e754f262607436cc7..bfe3fd275cd18553040be5bc091a2e0d43805874 100644 (file)
@@ -78,6 +78,12 @@ class safe_reraise(object):
             compat.reraise(type_, value, traceback)
 
 
+def clsname_as_plain_name(cls):
+    return " ".join(
+        n.lower() for n in re.findall(r"([A-Z][a-z]+)", cls.__name__)
+    )
+
+
 def decode_slice(slc):
     """decode a slice object as sent to __getitem__.
 
index 4406925ffa65b0a4f11136820bb98557c77dc599..8b60a117630ec3ec1268ef47cbfbebc7f1a24b13 100644 (file)
@@ -982,7 +982,7 @@ class DeclarativeTest(DeclarativeTestBase):
                 sa.exc.InvalidRequestError,
                 "^One or more mappers failed to initialize"
                 " - can't proceed with initialization of other mappers. "
-                r"Triggering mapper: 'Mapper\|User\|users'. "
+                r"Triggering mapper: 'mapped class User->users'. "
                 "Original exception was: When initializing.*",
                 configure_mappers,
             )
index 6c9da92d20c29f12c931d9ffe99ef43610532a40..59ecc7c986b22d2b547b4088f85324ab4ff5bf99 100644 (file)
@@ -2010,7 +2010,7 @@ class DistinctPKTest(fixtures.MappedTest):
         )
         assert_raises_message(
             sa_exc.SAWarning,
-            r"On mapper Mapper\|Employee\|employees, "
+            r"On mapper mapped class Employee->employees, "
             "primary key column 'persons.id' is being "
             "combined with distinct primary key column 'employees.eid' "
             "in attribute 'id'. Use explicit properties to give "
index 7ce05345bb12c1929c92994cef8be29e424dbc9c..679b3ec5bd459ce6eeb0dcc2d4e917d3c1b7e0f1 100644 (file)
@@ -1839,8 +1839,8 @@ class DeprecatedOptionAllTest(OptionsPathTest, _fixtures.FixtureTest):
             self._assert_eager_with_entity_exception(
                 [Item],
                 (joinedload_all("keywords.foo"),),
-                r"Can't find property named 'foo' on the mapped entity "
-                r"Mapper\|Keyword\|keywords in this Query.",
+                'Can\'t find property named \\"foo\\" on mapped class '
+                "Keyword->keywords in this Query.",
             )
 
     def test_all_path_vs_chained(self):
index 06929c6bc3dc441b291441c30ee293b94ac47e63..93cf19faea6a5853b6f9faad5018e1fbe00ed2a6 100644 (file)
@@ -187,13 +187,11 @@ class MapperTest(_fixtures.FixtureTest, AssertsCompiledSQL):
         for i in range(3):
             assert_raises_message(
                 sa.exc.InvalidRequestError,
-                "^One or more "
-                "mappers failed to initialize - can't "
-                "proceed with initialization of other "
-                r"mappers. Triggering mapper\: "
-                r"'Mapper\|Address\|addresses'."
-                " Original exception was: Class "
-                "'test.orm._fixtures.User' is not mapped$",
+                "One or more mappers failed to initialize - can't "
+                "proceed with initialization of other mappers. "
+                "Triggering mapper: 'mapped class Address->addresses'. "
+                "Original exception was: Class 'test.orm._fixtures.User' "
+                "is not mapped",
                 configure_mappers,
             )
 
@@ -2540,8 +2538,10 @@ class DeepOptionsTest(_fixtures.FixtureTest):
 
         assert_raises_message(
             sa.exc.ArgumentError,
-            "Can't find property 'items' on any entity "
-            "specified in this Query.",
+            'Mapped attribute "Order.items" does not apply to any of the '
+            "root entities in this query, e.g. mapped class User->users. "
+            "Please specify the full path from one of the root entities "
+            "to the target attribute.",
             sess.query(User).options,
             sa.orm.joinedload(Order.items),
         )
index 4d205e593f757525879d0a9f36280d81d84ca4a6..b4953cd3bca9e12def6c838914501344f7d171d5 100644 (file)
@@ -10,9 +10,11 @@ from sqlalchemy.orm import class_mapper
 from sqlalchemy.orm import column_property
 from sqlalchemy.orm import create_session
 from sqlalchemy.orm import defaultload
+from sqlalchemy.orm import exc as orm_exc
 from sqlalchemy.orm import joinedload
 from sqlalchemy.orm import lazyload
 from sqlalchemy.orm import Load
+from sqlalchemy.orm import load_only
 from sqlalchemy.orm import mapper
 from sqlalchemy.orm import relationship
 from sqlalchemy.orm import Session
@@ -107,7 +109,10 @@ class LoadTest(PathTest, QueryTest):
         result = Load(User)
         eq_(
             result._generate_path(
-                inspect(User)._path_registry, User.addresses, "relationship"
+                inspect(User)._path_registry,
+                User.addresses,
+                None,
+                "relationship",
             ),
             self._make_path_registry([User, "addresses", Address]),
         )
@@ -118,7 +123,7 @@ class LoadTest(PathTest, QueryTest):
         result = Load(User)
         eq_(
             result._generate_path(
-                inspect(User)._path_registry, User.name, "column"
+                inspect(User)._path_registry, User.name, None, "column"
             ),
             self._make_path_registry([User, "name"]),
         )
@@ -130,7 +135,7 @@ class LoadTest(PathTest, QueryTest):
         result = Load(User)
         eq_(
             result._generate_path(
-                inspect(User)._path_registry, "addresses", "relationship"
+                inspect(User)._path_registry, "addresses", None, "relationship"
             ),
             self._make_path_registry([User, "addresses", Address]),
         )
@@ -141,7 +146,7 @@ class LoadTest(PathTest, QueryTest):
         result = Load(User)
         eq_(
             result._generate_path(
-                inspect(User)._path_registry, "name", "column"
+                inspect(User)._path_registry, "name", None, "column"
             ),
             self._make_path_registry([User, "name"]),
         )
@@ -158,6 +163,7 @@ class LoadTest(PathTest, QueryTest):
             result._generate_path,
             result.path,
             User.addresses,
+            None,
             "relationship",
         )
 
@@ -174,6 +180,7 @@ class LoadTest(PathTest, QueryTest):
             result._generate_path,
             inspect(User)._path_registry,
             Order.items,
+            None,
             "relationship",
         )
 
@@ -187,6 +194,7 @@ class LoadTest(PathTest, QueryTest):
             result._generate_path(
                 inspect(User)._path_registry,
                 Order.items,
+                None,
                 "relationship",
                 False,
             ),
@@ -930,8 +938,8 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
         Item = self.classes.Item
 
         message = (
-            "Query has only expression-based entities - "
-            "can't find property named 'keywords'."
+            "Query has only expression-based entities - can't "
+            'find property named "keywords".'
         )
         self._assert_eager_with_just_column_exception(
             Item.id, "keywords", message
@@ -943,8 +951,8 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
         self._assert_eager_with_just_column_exception(
             Item.id,
             Item.keywords,
-            "Query has only expression-based entities "
-            "- can't find property named 'keywords'.",
+            "Query has only expression-based entities, which do not apply "
+            'to relationship property "Item.keywords"',
         )
 
     def test_option_against_nonexistent_PropComparator(self):
@@ -953,10 +961,10 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
         self._assert_eager_with_entity_exception(
             [Keyword],
             (joinedload(Item.keywords),),
-            r"Can't find property 'keywords' on any entity specified "
-            r"in this Query.  Note the full path from root "
-            r"\(Mapper\|Keyword\|keywords\) to target entity must be "
-            r"specified.",
+            'Mapped attribute "Item.keywords" does not apply to any of the '
+            "root entities in this query, e.g. mapped class "
+            "Keyword->keywords. Please specify the full path from one of "
+            "the root entities to the target attribute. ",
         )
 
     def test_option_against_nonexistent_basestring(self):
@@ -964,8 +972,8 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
         self._assert_eager_with_entity_exception(
             [Item],
             (joinedload("foo"),),
-            r"Can't find property named 'foo' on the mapped "
-            r"entity Mapper\|Item\|items in this Query.",
+            'Can\'t find property named "foo" on mapped class '
+            "Item->items in this Query.",
         )
 
     def test_option_against_nonexistent_twolevel_basestring(self):
@@ -973,8 +981,8 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
         self._assert_eager_with_entity_exception(
             [Item],
             (joinedload("keywords.foo"),),
-            r"Can't find property named 'foo' on the mapped entity "
-            r"Mapper\|Keyword\|keywords in this Query.",
+            'Can\'t find property named "foo" on mapped class '
+            "Keyword->keywords in this Query.",
         )
 
     def test_option_against_nonexistent_twolevel_chained(self):
@@ -982,8 +990,8 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
         self._assert_eager_with_entity_exception(
             [Item],
             (joinedload("keywords").joinedload("foo"),),
-            r"Can't find property named 'foo' on the mapped entity "
-            r"Mapper\|Keyword\|keywords in this Query.",
+            'Can\'t find property named "foo" on mapped class '
+            "Keyword->keywords in this Query.",
         )
 
     @testing.fails_if(
@@ -1016,21 +1024,57 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
 
     def test_option_against_wrong_entity_type_basestring(self):
         Item = self.classes.Item
-        self._assert_eager_with_entity_exception(
+        self._assert_loader_strategy_exception(
             [Item],
             (joinedload("id").joinedload("keywords"),),
-            r"Attribute 'id' of entity 'Mapper\|Item\|items' does not "
-            r"refer to a mapped entity",
+            'Can\'t apply "joined loader" strategy to property "Item.id", '
+            'which is a "column property"; this loader strategy is '
+            'intended to be used with a "relationship property".',
+        )
+
+    def test_col_option_against_relationship_basestring(self):
+        Item = self.classes.Item
+        self._assert_loader_strategy_exception(
+            [Item],
+            (load_only("keywords"),),
+            'Can\'t apply "column loader" strategy to property '
+            '"Item.keywords", which is a "relationship property"; this '
+            'loader strategy is intended to be used with a "column property".',
+        )
+
+    def test_load_only_against_multi_entity_attr(self):
+        User = self.classes.User
+        Item = self.classes.Item
+        self._assert_eager_with_entity_exception(
+            [User, Item],
+            (load_only(User.id, Item.id),),
+            r"Can't apply wildcard \('\*'\) or load_only\(\) loader option "
+            "to multiple entities mapped class User->users, mapped class "
+            "Item->items. Specify loader options for each entity "
+            "individually, such as "
+            r"Load\(mapped class User->users\).some_option\('\*'\), "
+            r"Load\(mapped class Item->items\).some_option\('\*'\).",
+        )
+
+    def test_col_option_against_relationship_attr(self):
+        Item = self.classes.Item
+        self._assert_loader_strategy_exception(
+            [Item],
+            (load_only(Item.keywords),),
+            'Can\'t apply "column loader" strategy to property '
+            '"Item.keywords", which is a "relationship property"; this '
+            'loader strategy is intended to be used with a "column property".',
         )
 
     def test_option_against_multi_non_relation_twolevel_basestring(self):
         Item = self.classes.Item
         Keyword = self.classes.Keyword
-        self._assert_eager_with_entity_exception(
+        self._assert_loader_strategy_exception(
             [Keyword, Item],
             (joinedload("id").joinedload("keywords"),),
-            r"Attribute 'id' of entity 'Mapper\|Keyword\|keywords' "
-            "does not refer to a mapped entity",
+            'Can\'t apply "joined loader" strategy to property "Keyword.id", '
+            'which is a "column property"; this loader strategy is intended '
+            'to be used with a "relationship property".',
         )
 
     def test_option_against_multi_nonexistent_basestring(self):
@@ -1039,8 +1083,8 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
         self._assert_eager_with_entity_exception(
             [Keyword, Item],
             (joinedload("description"),),
-            r"Can't find property named 'description' on the mapped "
-            r"entity Mapper\|Keyword\|keywords in this Query.",
+            'Can\'t find property named "description" on mapped class '
+            "Keyword->keywords in this Query.",
         )
 
     def test_option_against_multi_no_entities_basestring(self):
@@ -1050,27 +1094,29 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
             [Keyword.id, Item.id],
             (joinedload("keywords"),),
             r"Query has only expression-based entities - can't find property "
-            "named 'keywords'.",
+            'named "keywords".',
         )
 
     def test_option_against_wrong_multi_entity_type_attr_one(self):
         Item = self.classes.Item
         Keyword = self.classes.Keyword
-        self._assert_eager_with_entity_exception(
+        self._assert_loader_strategy_exception(
             [Keyword, Item],
             (joinedload(Keyword.id).joinedload(Item.keywords),),
-            r"Attribute 'id' of entity 'Mapper\|Keyword\|keywords' "
-            "does not refer to a mapped entity",
+            'Can\'t apply "joined loader" strategy to property "Keyword.id", '
+            'which is a "column property"; this loader strategy is intended '
+            'to be used with a "relationship property".',
         )
 
     def test_option_against_wrong_multi_entity_type_attr_two(self):
         Item = self.classes.Item
         Keyword = self.classes.Keyword
-        self._assert_eager_with_entity_exception(
+        self._assert_loader_strategy_exception(
             [Keyword, Item],
             (joinedload(Keyword.keywords).joinedload(Item.keywords),),
-            r"Attribute 'keywords' of entity 'Mapper\|Keyword\|keywords' "
-            "does not refer to a mapped entity",
+            'Can\'t apply "joined loader" strategy to property '
+            '"Keyword.keywords", which is a "column property"; this loader '
+            'strategy is intended to be used with a "relationship property".',
         )
 
     def test_option_against_wrong_multi_entity_type_attr_three(self):
@@ -1079,8 +1125,8 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
         self._assert_eager_with_entity_exception(
             [Keyword.id, Item.id],
             (joinedload(Keyword.keywords).joinedload(Item.keywords),),
-            r"Query has only expression-based entities - "
-            "can't find property named 'keywords'.",
+            "Query has only expression-based entities, which do not apply to "
+            'column property "Keyword.keywords"',
         )
 
     def test_wrong_type_in_option(self):
@@ -1164,6 +1210,14 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
         key = ("loader", (inspect(Item), inspect(Item).attrs.keywords))
         assert key in q._attributes
 
+    def _assert_loader_strategy_exception(self, entity_list, options, message):
+        assert_raises_message(
+            orm_exc.LoaderStrategyException,
+            message,
+            create_session().query(*entity_list).options,
+            *options
+        )
+
     def _assert_eager_with_entity_exception(
         self, entity_list, options, message
     ):
index 6e1c699c4103345ada832dd72755cc0dc609e82d..652b8bacd657cc88cfbbbe8e3c7e7ccfcccfacd6 100644 (file)
@@ -2142,7 +2142,7 @@ class ManualBackrefTest(_fixtures.FixtureTest):
             r"User.addresses references "
             r"relationship Address.dingaling, "
             r"which does not "
-            r"reference mapper Mapper\|User\|users",
+            r"reference mapper mapped class User->users",
             configure_mappers,
         )
 
index 697a8097e5917a44005c31df6cbc59957f9660ab..880f0bd1888606fd0c1e1a6969b78f6bc798b2a4 100644 (file)
@@ -102,7 +102,7 @@ class SyncTest(
         assert_raises_message(
             orm_exc.UnmappedColumnError,
             "Can't execute sync rule for source column 't2.id'; "
-            r"mapper 'Mapper\|A\|t1' does not map this column.",
+            r"mapper 'mapped class A->t1' does not map this column.",
             sync.populate,
             a1,
             a_mapper,
@@ -120,7 +120,7 @@ class SyncTest(
             orm_exc.UnmappedColumnError,
             r"Can't execute sync rule for destination "
             r"column 't1.id'; "
-            r"mapper 'Mapper\|B\|t2' does not map this column.",
+            r"mapper 'mapped class B->t2' does not map this column.",
             sync.populate,
             a1,
             a_mapper,
@@ -160,7 +160,7 @@ class SyncTest(
         assert_raises_message(
             orm_exc.UnmappedColumnError,
             "Can't execute sync rule for destination "
-            r"column 't1.foo'; mapper 'Mapper\|B\|t2' does not "
+            r"column 't1.foo'; mapper 'mapped class B->t2' does not "
             "map this column.",
             sync.clear,
             b1,
@@ -185,7 +185,7 @@ class SyncTest(
         assert_raises_message(
             orm_exc.UnmappedColumnError,
             "Can't execute sync rule for source column 't2.id'; "
-            r"mapper 'Mapper\|A\|t1' does not map this column.",
+            r"mapper 'mapped class A->t1' does not map this column.",
             sync.update,
             a1,
             a_mapper,
@@ -210,7 +210,7 @@ class SyncTest(
         assert_raises_message(
             orm_exc.UnmappedColumnError,
             "Can't execute sync rule for source column 't2.id'; "
-            r"mapper 'Mapper\|A\|t1' does not map this column.",
+            r"mapper 'mapped class A->t1' does not map this column.",
             sync.populate_dict,
             a1,
             a_mapper,
@@ -263,7 +263,7 @@ class SyncTest(
         assert_raises_message(
             orm_exc.UnmappedColumnError,
             "Can't execute sync rule for source column 't2.id'; "
-            r"mapper 'Mapper\|A\|t1' does not map this column.",
+            r"mapper 'mapped class A->t1' does not map this column.",
             sync.source_modified,
             uowcommit,
             a1,
index 05bc5746acf83388182d10be6cbb7167a974020c..d2bcb7c9b9108d131eaa0b59a52502f12e632730 100644 (file)
@@ -359,127 +359,19 @@ test.aaa_profiling.test_orm.AttributeOverheadTest.test_collection_append_remove
 
 # TEST: test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching
 
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 2.7_mssql_pyodbc_dbapiunicode_cextensions 104
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 2.7_mssql_pyodbc_dbapiunicode_nocextensions 104
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 2.7_mysql_mysqldb_dbapiunicode_cextensions 104
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 2.7_mysql_mysqldb_dbapiunicode_nocextensions 104
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 2.7_oracle_cx_oracle_dbapiunicode_cextensions 104
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 104
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 2.7_postgresql_psycopg2_dbapiunicode_cextensions 104
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 104
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 2.7_sqlite_pysqlite_dbapiunicode_cextensions 104
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 104
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.6_mssql_pyodbc_dbapiunicode_cextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.6_mssql_pyodbc_dbapiunicode_nocextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.6_mysql_mysqldb_dbapiunicode_cextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.6_mysql_mysqldb_dbapiunicode_nocextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.6_oracle_cx_oracle_dbapiunicode_cextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.6_postgresql_psycopg2_dbapiunicode_cextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.6_sqlite_pysqlite_dbapiunicode_cextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.6_sqlite_pysqlite_dbapiunicode_nocextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.7_mysql_mysqldb_dbapiunicode_cextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.7_mysql_mysqldb_dbapiunicode_nocextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.7_oracle_cx_oracle_dbapiunicode_cextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.7_postgresql_psycopg2_dbapiunicode_cextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 81
 test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.7_sqlite_pysqlite_dbapiunicode_cextensions 81
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_bound_branching 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 81
 
 # TEST: test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching
 
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 2.7_mssql_pyodbc_dbapiunicode_cextensions 651
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 2.7_mssql_pyodbc_dbapiunicode_nocextensions 651
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 2.7_mysql_mysqldb_dbapiunicode_cextensions 651
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 2.7_mysql_mysqldb_dbapiunicode_nocextensions 651
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 2.7_oracle_cx_oracle_dbapiunicode_cextensions 651
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 651
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 2.7_postgresql_psycopg2_dbapiunicode_cextensions 651
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 651
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 2.7_sqlite_pysqlite_dbapiunicode_cextensions 651
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 651
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.6_mssql_pyodbc_dbapiunicode_cextensions 624
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.6_mssql_pyodbc_dbapiunicode_nocextensions 624
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.6_mysql_mysqldb_dbapiunicode_cextensions 624
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.6_mysql_mysqldb_dbapiunicode_nocextensions 624
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.6_oracle_cx_oracle_dbapiunicode_cextensions 624
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 624
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.6_postgresql_psycopg2_dbapiunicode_cextensions 624
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 624
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.6_sqlite_pysqlite_dbapiunicode_cextensions 624
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.6_sqlite_pysqlite_dbapiunicode_nocextensions 624
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.7_mysql_mysqldb_dbapiunicode_cextensions 628
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.7_mysql_mysqldb_dbapiunicode_nocextensions 628
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.7_oracle_cx_oracle_dbapiunicode_cextensions 628
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 628
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.7_postgresql_psycopg2_dbapiunicode_cextensions 628
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 628
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.7_sqlite_pysqlite_dbapiunicode_cextensions 628
-test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 628
+test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_cache_key_unbound_branching 3.7_sqlite_pysqlite_dbapiunicode_cextensions 668
 
 # TEST: test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching
 
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_mssql_pyodbc_dbapiunicode_cextensions 45
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_mssql_pyodbc_dbapiunicode_nocextensions 45
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_mysql_mysqldb_dbapiunicode_cextensions 45
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_mysql_mysqldb_dbapiunicode_nocextensions 45
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_oracle_cx_oracle_dbapiunicode_cextensions 45
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 45
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_postgresql_psycopg2_dbapiunicode_cextensions 45
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 45
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_sqlite_pysqlite_dbapiunicode_cextensions 45
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 45
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.6_mssql_pyodbc_dbapiunicode_cextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.6_mssql_pyodbc_dbapiunicode_nocextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.6_mysql_mysqldb_dbapiunicode_cextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.6_mysql_mysqldb_dbapiunicode_nocextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.6_oracle_cx_oracle_dbapiunicode_cextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.6_postgresql_psycopg2_dbapiunicode_cextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.6_sqlite_pysqlite_dbapiunicode_cextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.6_sqlite_pysqlite_dbapiunicode_nocextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.7_mysql_mysqldb_dbapiunicode_cextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.7_mysql_mysqldb_dbapiunicode_nocextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.7_oracle_cx_oracle_dbapiunicode_cextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.7_postgresql_psycopg2_dbapiunicode_cextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 46
 test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.7_sqlite_pysqlite_dbapiunicode_cextensions 46
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 46
 
 # TEST: test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching
 
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_mssql_pyodbc_dbapiunicode_cextensions 460
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_mssql_pyodbc_dbapiunicode_nocextensions 460
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_mysql_mysqldb_dbapiunicode_cextensions 460
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_mysql_mysqldb_dbapiunicode_nocextensions 460
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_oracle_cx_oracle_dbapiunicode_cextensions 460
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 460
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_postgresql_psycopg2_dbapiunicode_cextensions 460
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 460
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_sqlite_pysqlite_dbapiunicode_cextensions 460
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 460
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.6_mssql_pyodbc_dbapiunicode_cextensions 466
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.6_mssql_pyodbc_dbapiunicode_nocextensions 466
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.6_mysql_mysqldb_dbapiunicode_cextensions 466
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.6_mysql_mysqldb_dbapiunicode_nocextensions 466
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.6_oracle_cx_oracle_dbapiunicode_cextensions 466
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 466
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.6_postgresql_psycopg2_dbapiunicode_cextensions 466
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 466
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.6_sqlite_pysqlite_dbapiunicode_cextensions 466
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.6_sqlite_pysqlite_dbapiunicode_nocextensions 466
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.7_mysql_mysqldb_dbapiunicode_cextensions 470
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.7_mysql_mysqldb_dbapiunicode_nocextensions 470
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.7_oracle_cx_oracle_dbapiunicode_cextensions 470
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 470
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.7_postgresql_psycopg2_dbapiunicode_cextensions 470
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 470
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.7_sqlite_pysqlite_dbapiunicode_cextensions 470
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 470
+test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.7_sqlite_pysqlite_dbapiunicode_cextensions 504
 
 # TEST: test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline