]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- totally remove _entity_info and _extended_entity_info, replacing all usage
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 17 Jul 2012 00:09:38 +0000 (20:09 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 17 Jul 2012 00:09:38 +0000 (20:09 -0400)
with inspect()

lib/sqlalchemy/orm/interfaces.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/strategies.py
lib/sqlalchemy/orm/util.py
test/orm/test_inspect.py

index 1e47e661667b964c2da947240ed2af76554d9e10..5b18bc13736df68c4ccdae359c213fd979dfe20c 100644 (file)
@@ -18,7 +18,7 @@ classes within should be considered mostly private.
 from __future__ import absolute_import
 from itertools import chain
 
-from .. import exc as sa_exc, util
+from .. import exc as sa_exc, util, inspect
 from ..sql import operators
 from collections import deque
 #from . import _instrumentation_ext
@@ -575,13 +575,13 @@ class PropertyOption(MapperOption):
 
             if getattr(token, '_of_type', None):
                 ac = token._of_type
-                ext_info = orm_util._extended_entity_info(ac)
+                ext_info = inspect(ac)
                 path_element = mapper = ext_info.mapper
                 if not ext_info.is_aliased_class:
                     ac = orm_util.with_polymorphic(
                                 ext_info.mapper.base_mapper,
                                 ext_info.mapper, aliased=True)
-                    ext_info = orm_util._extended_entity_info(ac)
+                    ext_info = inspect(ac)
                 path.set(query, "path_with_polymorphic", ext_info)
             else:
                 path_element = mapper = getattr(prop, 'mapper', None)
index 01b4d3a0f4f7e9628f7856c4e62f1e65b93039d4..39591ef990bce7739f8cbd5792ac27b2a3d9a5e0 100644 (file)
@@ -213,6 +213,15 @@ class Mapper(_InspectionAttr):
         """
         return self
 
+    @property
+    def entity(self):
+        """Part of the inspection API.
+
+        Returns self.class_.
+
+        """
+        return self.class_
+
     local_table = None
     """The :class:`.Selectable` which this :class:`.Mapper` manages.
 
@@ -1390,15 +1399,17 @@ class Mapper(_InspectionAttr):
 
     """
 
-    selectable = _with_polymorphic_selectable
-    """The :func:`.select` construct this :class:`.Mapper` selects from
-    by default.
+    @property
+    def selectable(self):
+        """The :func:`.select` construct this :class:`.Mapper` selects from
+        by default.
 
-    Normally, this is equivalent to :attr:`.mapped_table`, unless
-    the ``with_polymorphic`` feature is in use, in which case the
-    full "polymoprhic" selectable is returned.
+        Normally, this is equivalent to :attr:`.mapped_table`, unless
+        the ``with_polymorphic`` feature is in use, in which case the
+        full "polymoprhic" selectable is returned.
 
-    """
+        """
+        return self._with_polymorphic_selectable
 
     def _with_polymorphic_args(self, spec=None, selectable=False,
                                 innerjoin=False):
index 6b1dd64629bc3d24394435223a8933b5b6bbc53b..044ba8e2cfa3bba798d1628bdcdc17b6768b47fd 100644 (file)
@@ -11,7 +11,7 @@ mapped attributes.
 
 """
 
-from .. import sql, util, log, exc as sa_exc
+from .. import sql, util, log, exc as sa_exc, inspect
 from ..sql import operators, expression
 from . import (
     attributes, mapper,
@@ -19,7 +19,7 @@ from . import (
     dependency
     )
 from .util import CascadeOptions, \
-        _orm_annotate, _orm_deannotate, _orm_full_deannotate, _entity_info
+        _orm_annotate, _orm_deannotate, _orm_full_deannotate
 
 from .interfaces import MANYTOMANY, MANYTOONE, ONETOMANY,\
         PropComparator, StrategizedProperty
@@ -409,7 +409,9 @@ class RelationshipProperty(StrategizedProperty):
 
         def _criterion_exists(self, criterion=None, **kwargs):
             if getattr(self, '_of_type', None):
-                target_mapper, to_selectable, is_aliased_class = _entity_info(self._of_type)
+                info = inspect(self._of_type)
+                target_mapper, to_selectable, is_aliased_class = \
+                    info.mapper, info.selectable, info.is_aliased_class
                 if self.property._is_self_referential and not is_aliased_class:
                     to_selectable = to_selectable.alias()
 
index f981399db0488b625f5ca967eee5e022da181f29..44f9ba7e5d83f15f60d04fa2f0d8ec766fa99109 100644 (file)
@@ -25,12 +25,11 @@ from . import (
     exc as orm_exc, loading
     )
 from .util import (
-    AliasedClass, ORMAdapter, _entity_descriptor, _entity_info,
-    _extended_entity_info, PathRegistry,
+    AliasedClass, ORMAdapter, _entity_descriptor, PathRegistry,
     _is_aliased_class, _is_mapped_class, _orm_columns, _orm_selectable,
     join as orm_join,with_parent, aliased
     )
-from .. import sql, util, log, exc as sa_exc
+from .. import sql, util, log, exc as sa_exc, inspect
 from ..sql import (
         util as sql_util,
         expression, visitors
@@ -127,7 +126,7 @@ class Query(object):
         for ent in entities:
             for entity in ent.entities:
                 if entity not in d:
-                    ext_info = _extended_entity_info(entity)
+                    ext_info = inspect(entity)
                     if not ext_info.is_aliased_class and \
                         ext_info.mapper.with_polymorphic:
                         if ext_info.mapper.mapped_table not in \
@@ -1077,7 +1076,7 @@ class Query(object):
         :class:`.Table`, :class:`.Alias`, or ORM entity / mapped class
         /etc.
         """
-        mapper, selectable, is_aliased_class = _entity_info(selectable)
+        selectable = inspect(selectable).selectable
 
         self._with_hints += ((selectable, text, dialect_name),)
 
@@ -1719,8 +1718,12 @@ class Query(object):
                         isinstance(onclause, interfaces.PropComparator):
                 left_entity = onclause.parententity
 
+                info = inspect(self._joinpoint_zero())
                 left_mapper, left_selectable, left_is_aliased = \
-                                    _entity_info(self._joinpoint_zero())
+                    getattr(info, 'mapper', None), \
+                    info.selectable, \
+                    getattr(info, 'is_aliased_class', None)
+
                 if left_mapper is left_entity:
                     left_entity = self._joinpoint_zero()
                     descriptor = _entity_descriptor(left_entity,
@@ -1810,10 +1813,15 @@ class Query(object):
 
     def _prepare_right_side(self, right, onclause, outerjoin,
                                 create_aliases, prop):
-        right_mapper, right_selectable, right_is_aliased = _entity_info(right)
+        info = inspect(right)
+
+        right_mapper, right_selectable, right_is_aliased = \
+            getattr(info, 'mapper', None), \
+            info.selectable, \
+            getattr(info, 'is_aliased_class', False)
 
         if right_mapper:
-            self._join_entities += (right, )
+            self._join_entities += (info, )
 
         if right_mapper and prop and \
                 not right_mapper.common_parent(prop.mapper):
@@ -1883,8 +1891,13 @@ class Query(object):
 
         return right, right_is_aliased, onclause
 
-    def _join_to_left(self, left, right, right_is_aliased, onclause, outerjoin):
-        left_mapper, left_selectable, left_is_aliased = _entity_info(left)
+    def _join_to_left(self, left, right, right_is_aliased,
+                                    onclause, outerjoin):
+        info = inspect(left)
+        left_mapper, left_selectable, left_is_aliased = \
+            getattr(info, 'mapper', None),\
+            info.selectable,\
+            getattr(info, 'is_aliased_class', False)
 
         # this is an overly broad assumption here, but there's a
         # very wide variety of situations where we rely upon orm.join's
@@ -1990,11 +2003,12 @@ class Query(object):
         """
         obj = []
         for fo in from_obj:
-            if _is_mapped_class(fo):
-                mapper, selectable, is_aliased_class = _entity_info(fo)
+            info = inspect(fo)
+            if hasattr(info, 'mapper') and \
+                (info.is_mapper or info.is_aliased_class):
                 self._select_from_entity = fo
-                obj.append(selectable)
-            elif not isinstance(fo, expression.FromClause):
+                obj.append(info.selectable)
+            elif not info.is_selectable:
                 raise sa_exc.ArgumentError(
                             "select_from() accepts FromClause objects only.")
             else:
@@ -2669,7 +2683,7 @@ class Query(object):
 
         """
         for (ext_info, adapter) in self._mapper_adapter_map.values():
-            if ext_info.entity in self._join_entities:
+            if ext_info in self._join_entities:
                 continue
             single_crit = ext_info.mapper._single_table_criterion
             if single_crit is not None:
@@ -2718,7 +2732,7 @@ class _MapperEntity(_QueryEntity):
         self.is_aliased_class = ext_info.is_aliased_class
         self._with_polymorphic = ext_info.with_polymorphic_mappers
         self._polymorphic_discriminator = \
-                ext_info.with_polymorphic_discriminator
+                ext_info.polymorphic_on
         if ext_info.is_aliased_class:
             self.entity_zero = ext_info.entity
             self._label_name = self.entity_zero._sa_label_name
@@ -2760,7 +2774,7 @@ class _MapperEntity(_QueryEntity):
         return self.entity_zero
 
     def corresponds_to(self, entity):
-        entity_info = _extended_entity_info(entity)
+        entity_info = inspect(entity)
         if entity_info.is_aliased_class or self.is_aliased_class:
             return entity is self.entity_zero \
                 or \
index 2bd60e76d44cff5e1b9e31aec46b6ea08deac0bf..21214af9a17023d7c990ceec0a3f3fde8b0d2142 100644 (file)
@@ -4,14 +4,14 @@
 # This module is part of SQLAlchemy and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
-"""sqlalchemy.orm.interfaces.LoaderStrategy 
+"""sqlalchemy.orm.interfaces.LoaderStrategy
    implementations, and related MapperOptions."""
 
-from .. import exc as sa_exc
+from .. import exc as sa_exc, inspect
 from .. import util, log, event
 from ..sql import util as sql_util, visitors
 from . import (
-        attributes, interfaces, exc as orm_exc, loading, 
+        attributes, interfaces, exc as orm_exc, loading,
         unitofwork, util as orm_util
     )
 from .util import _none_set
@@ -23,13 +23,13 @@ from .session import _state_session
 import itertools
 
 def _register_attribute(strategy, mapper, useobject,
-        compare_function=None, 
+        compare_function=None,
         typecallable=None,
         uselist=False,
-        callable_=None, 
-        proxy_property=None, 
+        callable_=None,
+        proxy_property=None,
         active_history=False,
-        impl_class=None, 
+        impl_class=None,
         **kw
 ):
 
@@ -45,7 +45,7 @@ def _register_attribute(strategy, mapper, useobject,
     if prop.key in prop.parent.validators:
         fn, include_removes = prop.parent.validators[prop.key]
         listen_hooks.append(
-            lambda desc, prop: orm_util._validator_events(desc, 
+            lambda desc, prop: orm_util._validator_events(desc,
                                 prop.key, fn, include_removes)
             )
 
@@ -57,8 +57,8 @@ def _register_attribute(strategy, mapper, useobject,
     backref = kw.pop('backref', None)
     if backref:
         listen_hooks.append(
-            lambda desc, prop: attributes.backref_listeners(desc, 
-                                backref, 
+            lambda desc, prop: attributes.backref_listeners(desc,
+                                backref,
                                 uselist)
         )
 
@@ -66,17 +66,17 @@ def _register_attribute(strategy, mapper, useobject,
         if prop is m._props.get(prop.key):
 
             desc = attributes.register_attribute_impl(
-                m.class_, 
-                prop.key, 
+                m.class_,
+                prop.key,
                 parent_token=prop,
-                uselist=uselist, 
-                compare_function=compare_function, 
+                uselist=uselist,
+                compare_function=compare_function,
                 useobject=useobject,
-                extension=attribute_ext, 
-                trackparent=useobject and (prop.single_parent 
-                                or prop.direction is interfaces.ONETOMANY), 
+                extension=attribute_ext,
+                trackparent=useobject and (prop.single_parent
+                                or prop.direction is interfaces.ONETOMANY),
                 typecallable=typecallable,
-                callable_=callable_, 
+                callable_=callable_,
                 active_history=active_history,
                 impl_class=impl_class,
                 doc=prop.doc,
@@ -97,7 +97,7 @@ class UninstrumentedColumnLoader(LoaderStrategy):
         super(UninstrumentedColumnLoader, self).__init__(parent)
         self.columns = self.parent_property.columns
 
-    def setup_query(self, context, entity, path, adapter, 
+    def setup_query(self, context, entity, path, adapter,
                             column_collection=None, **kwargs):
         for c in self.columns:
             if adapter:
@@ -115,7 +115,7 @@ class ColumnLoader(LoaderStrategy):
         self.columns = self.parent_property.columns
         self.is_composite = hasattr(self.parent_property, 'composite_class')
 
-    def setup_query(self, context, entity, path, 
+    def setup_query(self, context, entity, path,
                             adapter, column_collection, **kwargs):
         for c in self.columns:
             if adapter:
@@ -134,7 +134,7 @@ class ColumnLoader(LoaderStrategy):
             active_history = active_history
        )
 
-    def create_row_processor(self, context, path, 
+    def create_row_processor(self, context, path,
                                             mapper, row, adapter):
         key = self.key
         # look through list of columns represented here
@@ -195,10 +195,10 @@ class DeferredColumnLoader(LoaderStrategy):
              expire_missing=False
         )
 
-    def setup_query(self, context, entity, path, adapter, 
+    def setup_query(self, context, entity, path, adapter,
                                 only_load_props=None, **kwargs):
         if (
-                self.group is not None and 
+                self.group is not None and
                 context.attributes.get(('undefer', self.group), False)
             ) or (only_load_props and self.key in only_load_props):
             self.parent_property._get_strategy(ColumnLoader).\
@@ -216,10 +216,10 @@ class DeferredColumnLoader(LoaderStrategy):
 
         if self.group:
             toload = [
-                    p.key for p in 
-                    localparent.iterate_properties 
-                    if isinstance(p, StrategizedProperty) and 
-                      isinstance(p.strategy, DeferredColumnLoader) and 
+                    p.key for p in
+                    localparent.iterate_properties
+                    if isinstance(p, StrategizedProperty) and
+                      isinstance(p.strategy, DeferredColumnLoader) and
                       p.group==self.group
                     ]
         else:
@@ -232,12 +232,12 @@ class DeferredColumnLoader(LoaderStrategy):
         if session is None:
             raise orm_exc.DetachedInstanceError(
                 "Parent instance %s is not bound to a Session; "
-                "deferred load operation of attribute '%s' cannot proceed" % 
+                "deferred load operation of attribute '%s' cannot proceed" %
                 (orm_util.state_str(state), self.key)
                 )
 
         query = session.query(localparent)
-        if loading.load_on_ident(query, state.key, 
+        if loading.load_on_ident(query, state.key,
                     only_load_props=group, refresh_state=state) is None:
             raise orm_exc.ObjectDeletedError(state)
 
@@ -294,14 +294,14 @@ class AbstractRelationshipLoader(LoaderStrategy):
 class NoLoader(AbstractRelationshipLoader):
     """Provide loading behavior for a :class:`.RelationshipProperty`
     with "lazy=None".
-    
+
     """
 
     def init_class_attribute(self, mapper):
         self.is_class_level = True
 
         _register_attribute(self, mapper,
-            useobject=True, 
+            useobject=True,
             uselist=self.parent_property.uselist,
             typecallable = self.parent_property.collection_class,
         )
@@ -316,7 +316,7 @@ log.class_logger(NoLoader)
 class LazyLoader(AbstractRelationshipLoader):
     """Provide loading behavior for a :class:`.RelationshipProperty`
     with "lazy=True", that is loads when first accessed.
-    
+
     """
 
     def __init__(self, parent):
@@ -338,8 +338,8 @@ class LazyLoader(AbstractRelationshipLoader):
         #from sqlalchemy.orm import query
         self.use_get = not self.uselist and \
                         self.mapper._get_clause[0].compare(
-                            self._lazywhere, 
-                            use_proxies=True, 
+                            self._lazywhere,
+                            use_proxies=True,
                             equivalents=self.mapper._equivalent_columns
                         )
 
@@ -355,13 +355,13 @@ class LazyLoader(AbstractRelationshipLoader):
     def init_class_attribute(self, mapper):
         self.is_class_level = True
 
-        # MANYTOONE currently only needs the 
+        # MANYTOONE currently only needs the
         # "old" value for delete-orphan
-        # cascades.  the required _SingleParentValidator 
+        # cascades.  the required _SingleParentValidator
         # will enable active_history
-        # in that case.  otherwise we don't need the 
+        # in that case.  otherwise we don't need the
         # "old" value during backref operations.
-        _register_attribute(self, 
+        _register_attribute(self,
                 mapper,
                 useobject=True,
                 callable_=self._load_for_state,
@@ -375,12 +375,12 @@ class LazyLoader(AbstractRelationshipLoader):
                     not self.use_get,
                 )
 
-    def lazy_clause(self, state, reverse_direction=False, 
-                                alias_secondary=False, 
+    def lazy_clause(self, state, reverse_direction=False,
+                                alias_secondary=False,
                                 adapt_source=None):
         if state is None:
             return self._lazy_none_clause(
-                                        reverse_direction, 
+                                        reverse_direction,
                                         adapt_source=adapt_source)
 
         if not reverse_direction:
@@ -411,14 +411,14 @@ class LazyLoader(AbstractRelationshipLoader):
                 if bindparam._identifying_key in bind_to_col:
                     bindparam.callable = \
                         lambda: mapper._get_committed_state_attr_by_column(
-                            state, dict_, 
+                            state, dict_,
                             bind_to_col[bindparam._identifying_key])
         else:
             def visit_bindparam(bindparam):
                 if bindparam._identifying_key in bind_to_col:
                     bindparam.callable = \
                             lambda: mapper._get_state_attr_by_column(
-                                    state, dict_, 
+                                    state, dict_,
                                     bind_to_col[bindparam._identifying_key])
 
 
@@ -477,11 +477,11 @@ class LazyLoader(AbstractRelationshipLoader):
         if not session:
             raise orm_exc.DetachedInstanceError(
                 "Parent instance %s is not bound to a Session; "
-                "lazy load operation of attribute '%s' cannot proceed" % 
+                "lazy load operation of attribute '%s' cannot proceed" %
                 (orm_util.state_str(state), self.key)
             )
 
-        # if we have a simple primary key load, check the 
+        # if we have a simple primary key load, check the
         # identity map without generating a Query at all
         if self.use_get:
             ident = self._get_ident_for_use_get(
@@ -550,7 +550,7 @@ class LazyLoader(AbstractRelationshipLoader):
             q = q.order_by(*util.to_list(self.parent_property.order_by))
 
         for rev in self.parent_property._reverse_property:
-            # reverse props that are MANYTOONE are loading *this* 
+            # reverse props that are MANYTOONE are loading *this*
             # object from get(), so don't need to eager out to those.
             if rev.direction is interfaces.MANYTOONE and \
                         rev._use_get and \
@@ -575,7 +575,7 @@ class LazyLoader(AbstractRelationshipLoader):
                 if l > 1:
                     util.warn(
                         "Multiple rows returned with "
-                        "uselist=False for lazily-loaded attribute '%s' " 
+                        "uselist=False for lazily-loaded attribute '%s' "
                         % self.parent_property)
 
                 return result[0]
@@ -583,30 +583,30 @@ class LazyLoader(AbstractRelationshipLoader):
                 return None
 
 
-    def create_row_processor(self, context, path, 
+    def create_row_processor(self, context, path,
                                     mapper, row, adapter):
         key = self.key
         if not self.is_class_level:
             def set_lazy_callable(state, dict_, row):
-                # we are not the primary manager for this attribute 
+                # we are not the primary manager for this attribute
                 # on this class - set up a
-                # per-instance lazyloader, which will override the 
+                # per-instance lazyloader, which will override the
                 # class-level behavior.
-                # this currently only happens when using a 
+                # this currently only happens when using a
                 # "lazyload" option on a "no load"
-                # attribute - "eager" attributes always have a 
+                # attribute - "eager" attributes always have a
                 # class-level lazyloader installed.
                 state.set_callable(dict_, key, LoadLazyAttribute(state, key))
             return set_lazy_callable, None, None
         else:
             def reset_for_lazy_callable(state, dict_, row):
-                # we are the primary manager for this attribute on 
+                # we are the primary manager for this attribute on
                 # this class - reset its
-                # per-instance attribute state, so that the class-level 
+                # per-instance attribute state, so that the class-level
                 # lazy loader is
                 # executed when next referenced on this instance.
                 # this is needed in
-                # populate_existing() types of scenarios to reset 
+                # populate_existing() types of scenarios to reset
                 # any existing state.
                 state.reset(dict_, key)
 
@@ -637,12 +637,12 @@ class ImmediateLoader(AbstractRelationshipLoader):
                 _get_strategy(LazyLoader).\
                 init_class_attribute(mapper)
 
-    def setup_query(self, context, entity, 
+    def setup_query(self, context, entity,
                         path, adapter, column_collection=None,
                         parentmapper=None, **kwargs):
         pass
 
-    def create_row_processor(self, context, path, 
+    def create_row_processor(self, context, path,
                                 mapper, row, adapter):
         def load_immediate(state, dict_, row):
             state.get_impl(self.key).get(state, dict_)
@@ -659,8 +659,8 @@ class SubqueryLoader(AbstractRelationshipLoader):
                 _get_strategy(LazyLoader).\
                 init_class_attribute(mapper)
 
-    def setup_query(self, context, entity, 
-                        path, adapter, 
+    def setup_query(self, context, entity,
+                        path, adapter,
                         column_collection=None,
                         parentmapper=None, **kwargs):
 
@@ -677,12 +677,12 @@ class SubqueryLoader(AbstractRelationshipLoader):
         else:
             effective_entity = self.mapper
 
-        subq_path = context.attributes.get(('subquery_path', None), 
+        subq_path = context.attributes.get(('subquery_path', None),
                                 orm_util.PathRegistry.root)
 
         subq_path = subq_path + path
 
-        # if not via query option, check for 
+        # if not via query option, check for
         # a cycle
         if not path.contains(context, "loaderstrategy"):
             if self.join_depth:
@@ -695,7 +695,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
                 self._get_leftmost(subq_path)
 
         orig_query = context.attributes.get(
-                                ("orig_query", SubqueryLoader), 
+                                ("orig_query", SubqueryLoader),
                                 context.query)
 
         # generate a new Query from the original, then
@@ -705,7 +705,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
                             leftmost_attr
         )
 
-        # generate another Query that will join the 
+        # generate another Query that will join the
         # left alias to the target relationships.
         # basically doing a longhand
         # "from_self()".  (from_self() itself not quite industrial
@@ -722,13 +722,13 @@ class SubqueryLoader(AbstractRelationshipLoader):
         q = q.order_by(*local_attr)
         q = q.add_columns(*local_attr)
 
-        q = self._apply_joins(q, to_join, left_alias, 
+        q = self._apply_joins(q, to_join, left_alias,
                             parent_alias, effective_entity)
 
         q = self._setup_options(q, subq_path, orig_query, effective_entity)
         q = self._setup_outermost_orderby(q)
 
-        # add new query to attributes to be picked up 
+        # add new query to attributes to be picked up
         # by create_row_processor
         path.set(context, "subquery", q)
 
@@ -761,7 +761,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
         # to look only for significant columns
         q = orig_query._clone().correlate(None)
 
-        # TODO: why does polymporphic etc. require hardcoding 
+        # TODO: why does polymporphic etc. require hardcoding
         # into _adapt_col_list ?  Does query.add_columns(...) work
         # with polymorphic loading ?
         q._set_entities(q._adapt_col_list(leftmost_attr))
@@ -785,7 +785,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
 
         # figure out what's being joined.  a.k.a. the fun part
         to_join = [
-                    (subq_path[i], subq_path[i+1]) 
+                    (subq_path[i], subq_path[i+1])
                     for i in xrange(0, len(subq_path), 2)
                 ]
 
@@ -793,21 +793,21 @@ class SubqueryLoader(AbstractRelationshipLoader):
         # which needs to be aliased.
 
         if len(to_join) > 1:
-            ext = orm_util._extended_entity_info(subq_path[-2])
+            info = inspect(subq_path[-2])
 
         if len(to_join) < 2:
             # in the case of a one level eager load, this is the
             # leftmost "left_alias".
             parent_alias = left_alias
-        elif ext.mapper.isa(self.parent):
+        elif info.mapper.isa(self.parent):
             # In the case of multiple levels, retrieve
-            # it from subq_path[-2]. This is the same as self.parent 
-            # in the vast majority of cases, and [ticket:2014] 
+            # it from subq_path[-2]. This is the same as self.parent
+            # in the vast majority of cases, and [ticket:2014]
             # illustrates a case where sub_path[-2] is a subclass
             # of self.parent
             parent_alias = orm_util.AliasedClass(subq_path[-2])
         else:
-            # if of_type() were used leading to this relationship, 
+            # if of_type() were used leading to this relationship,
             # self.parent is more specific than subq_path[-2]
             parent_alias = orm_util.AliasedClass(self.parent)
 
@@ -819,15 +819,15 @@ class SubqueryLoader(AbstractRelationshipLoader):
         ]
         return to_join, local_attr, parent_alias
 
-    def _apply_joins(self, q, to_join, left_alias, parent_alias, 
+    def _apply_joins(self, q, to_join, left_alias, parent_alias,
                     effective_entity):
         for i, (mapper, key) in enumerate(to_join):
 
             # we need to use query.join() as opposed to
-            # orm.join() here because of the 
-            # rich behavior it brings when dealing with 
+            # orm.join() here because of the
+            # rich behavior it brings when dealing with
             # "with_polymorphic" mappers.  "aliased"
-            # and "from_joinpoint" take care of most of 
+            # and "from_joinpoint" take care of most of
             # the chaining and aliasing for us.
 
             first = i == 0
@@ -857,7 +857,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
         # these will fire relative to subq_path.
         q = q._with_current_path(subq_path)
         q = q._conditional_options(*orig_query._with_options)
-        if orig_query._populate_existing: 
+        if orig_query._populate_existing:
             q._populate_existing = orig_query._populate_existing
 
         return q
@@ -865,7 +865,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
     def _setup_outermost_orderby(self, q):
         if self.parent_property.order_by:
             # if there's an ORDER BY, alias it the same
-            # way joinedloader does, but we have to pull out 
+            # way joinedloader does, but we have to pull out
             # the "eagerjoin" from the query.
             # this really only picks up the "secondary" table
             # right now.
@@ -885,7 +885,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
         if not self.parent.class_manager[self.key].impl.supports_population:
             raise sa_exc.InvalidRequestError(
                         "'%s' does not support object "
-                        "population - eager loading cannot be applied." % 
+                        "population - eager loading cannot be applied." %
                         self)
 
         path = path[self.key]
@@ -902,9 +902,9 @@ class SubqueryLoader(AbstractRelationshipLoader):
         collections = path.get(context, "collections")
         if collections is None:
             collections = dict(
-                    (k, [v[0] for v in v]) 
+                    (k, [v[0] for v in v])
                     for k, v in itertools.groupby(
-                        subq, 
+                        subq,
                         lambda x:x[1:]
                     ))
             path.set(context, 'collections', collections)
@@ -920,7 +920,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
     def _create_collection_loader(self, collections, local_cols):
         def load_collection_from_subq(state, dict_, row):
             collection = collections.get(
-                tuple([row[col] for col in local_cols]), 
+                tuple([row[col] for col in local_cols]),
                 ()
             )
             state.get_impl(self.key).\
@@ -931,7 +931,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
     def _create_scalar_loader(self, collections, local_cols):
         def load_scalar_from_subq(state, dict_, row):
             collection = collections.get(
-                tuple([row[col] for col in local_cols]), 
+                tuple([row[col] for col in local_cols]),
                 (None,)
             )
             if len(collection) > 1:
@@ -951,7 +951,7 @@ log.class_logger(SubqueryLoader)
 class JoinedLoader(AbstractRelationshipLoader):
     """Provide loading behavior for a :class:`.RelationshipProperty`
     using joined eager loading.
-    
+
     """
     def __init__(self, parent):
         super(JoinedLoader, self).__init__(parent)
@@ -974,8 +974,8 @@ class JoinedLoader(AbstractRelationshipLoader):
 
         with_polymorphic = None
 
-        user_defined_adapter = path.get(context, 
-                                "user_defined_eager_row_processor", 
+        user_defined_adapter = path.get(context,
+                                "user_defined_eager_row_processor",
                                 False)
         if user_defined_adapter is not False:
             clauses, adapter, add_to_collection = \
@@ -984,7 +984,7 @@ class JoinedLoader(AbstractRelationshipLoader):
                     user_defined_adapter
                 )
         else:
-            # if not via query option, check for 
+            # if not via query option, check for
             # a cycle
             if not path.contains(context, "loaderstrategy"):
                 if self.join_depth:
@@ -1000,7 +1000,7 @@ class JoinedLoader(AbstractRelationshipLoader):
                 )
 
         with_poly_info = path.get(
-            context, 
+            context,
             "path_with_polymorphic",
             None
         )
@@ -1013,37 +1013,37 @@ class JoinedLoader(AbstractRelationshipLoader):
         for value in self.mapper._iterate_polymorphic_properties(
                                 mappers=with_polymorphic):
             value.setup(
-                context, 
-                entity, 
-                path, 
-                clauses, 
-                parentmapper=self.mapper, 
+                context,
+                entity,
+                path,
+                clauses,
+                parentmapper=self.mapper,
                 column_collection=add_to_collection,
                 allow_innerjoin=allow_innerjoin)
 
-    def _get_user_defined_adapter(self, context, entity, 
+    def _get_user_defined_adapter(self, context, entity,
                                 path, adapter, user_defined_adapter):
 
             adapter = entity._get_entity_clauses(context.query, context)
             if adapter and user_defined_adapter:
                 user_defined_adapter = user_defined_adapter.wrap(adapter)
-                path.set(context, "user_defined_eager_row_processor", 
+                path.set(context, "user_defined_eager_row_processor",
                                         user_defined_adapter)
             elif adapter:
                 user_defined_adapter = adapter
-                path.set(context, "user_defined_eager_row_processor", 
+                path.set(context, "user_defined_eager_row_processor",
                                         user_defined_adapter)
 
             add_to_collection = context.primary_columns
             return user_defined_adapter, adapter, add_to_collection
 
-    def _generate_row_adapter(self, 
+    def _generate_row_adapter(self,
         context, entity, path, adapter,
         column_collection, parentmapper, allow_innerjoin
     ):
         with_poly_info = path.get(
-            context, 
-            "path_with_polymorphic", 
+            context,
+            "path_with_polymorphic",
             None
         )
         if with_poly_info:
@@ -1051,7 +1051,7 @@ class JoinedLoader(AbstractRelationshipLoader):
         else:
             to_adapt = orm_util.AliasedClass(self.mapper)
         clauses = orm_util.ORMAdapter(
-                    to_adapt, 
+                    to_adapt,
                     equivalents=self.mapper._equivalent_columns,
                     adapt_required=True)
         assert clauses.aliased_class is not None
@@ -1060,7 +1060,7 @@ class JoinedLoader(AbstractRelationshipLoader):
             context.multi_row_eager_loaders = True
 
         innerjoin = allow_innerjoin and path.get(context,
-                            "eager_join_type", 
+                            "eager_join_type",
                             self.parent_property.innerjoin)
         if not innerjoin:
             # if this is an outer join, all eager joins from
@@ -1068,8 +1068,8 @@ class JoinedLoader(AbstractRelationshipLoader):
             allow_innerjoin = False
 
         context.create_eager_joins.append(
-            (self._create_eager_join, context, 
-            entity, path, adapter, 
+            (self._create_eager_join, context,
+            entity, path, adapter,
             parentmapper, clauses, innerjoin)
         )
 
@@ -1077,8 +1077,8 @@ class JoinedLoader(AbstractRelationshipLoader):
         path.set(context, "eager_row_processor", clauses)
         return clauses, adapter, add_to_collection, allow_innerjoin
 
-    def _create_eager_join(self, context, entity, 
-                            path, adapter, parentmapper, 
+    def _create_eager_join(self, context, entity,
+                            path, adapter, parentmapper,
                             clauses, innerjoin):
 
         if parentmapper is None:
@@ -1087,7 +1087,7 @@ class JoinedLoader(AbstractRelationshipLoader):
             localparent = parentmapper
 
         # whether or not the Query will wrap the selectable in a subquery,
-        # and then attach eager load joins to that (i.e., in the case of 
+        # and then attach eager load joins to that (i.e., in the case of
         # LIMIT/OFFSET etc.)
         should_nest_selectable = context.multi_row_eager_loaders and \
             context.query._should_nest_selectable
@@ -1103,7 +1103,7 @@ class JoinedLoader(AbstractRelationshipLoader):
             if clause is not None:
                 # join to an existing FROM clause on the query.
                 # key it to its list index in the eager_joins dict.
-                # Query._compile_context will adapt as needed and 
+                # Query._compile_context will adapt as needed and
                 # append to the FROM clause of the select().
                 entity_key, default_towrap = index, clause
 
@@ -1121,14 +1121,14 @@ class JoinedLoader(AbstractRelationshipLoader):
             else:
                 onclause = getattr(
                                 orm_util.AliasedClass(
-                                        self.parent, 
+                                        self.parent,
                                         adapter.selectable
-                                ), 
+                                ),
                                 self.key, self.parent_property
                             )
 
             if onclause is self.parent_property:
-                # TODO: this is a temporary hack to 
+                # TODO: this is a temporary hack to
                 # account for polymorphic eager loads where
                 # the eagerload is referencing via of_type().
                 join_to_left = True
@@ -1138,10 +1138,10 @@ class JoinedLoader(AbstractRelationshipLoader):
         assert clauses.aliased_class is not None
         context.eager_joins[entity_key] = eagerjoin = \
                                 orm_util.join(
-                                            towrap, 
-                                            clauses.aliased_class, 
-                                            onclause, 
-                                            join_to_left=join_to_left, 
+                                            towrap,
+                                            clauses.aliased_class,
+                                            onclause,
+                                            join_to_left=join_to_left,
                                             isouter=not innerjoin
                                             )
 
@@ -1151,11 +1151,11 @@ class JoinedLoader(AbstractRelationshipLoader):
         if self.parent_property.secondary is None and \
                 not parentmapper:
             # for parentclause that is the non-eager end of the join,
-            # ensure all the parent cols in the primaryjoin are actually 
+            # ensure all the parent cols in the primaryjoin are actually
             # in the
-            # columns clause (i.e. are not deferred), so that aliasing applied 
+            # columns clause (i.e. are not deferred), so that aliasing applied
             # by the Query propagates those columns outward.
-            # This has the effect 
+            # This has the effect
             # of "undefering" those columns.
             for col in sql_util.find_columns(
                                 self.parent_property.primaryjoin):
@@ -1175,12 +1175,12 @@ class JoinedLoader(AbstractRelationshipLoader):
 
 
     def _create_eager_adapter(self, context, row, adapter, path):
-        user_defined_adapter = path.get(context, 
-                                "user_defined_eager_row_processor", 
+        user_defined_adapter = path.get(context,
+                                "user_defined_eager_row_processor",
                                 False)
         if user_defined_adapter is not False:
             decorator = user_defined_adapter
-            # user defined eagerloads are part of the "primary" 
+            # user defined eagerloads are part of the "primary"
             # portion of the load.
             # the adapters applied to the Query should be honored.
             if context.adapter and decorator:
@@ -1196,7 +1196,7 @@ class JoinedLoader(AbstractRelationshipLoader):
             self.mapper.identity_key_from_row(row, decorator)
             return decorator
         except KeyError:
-            # no identity key - dont return a row 
+            # no identity key - dont return a row
             # processor, will cause a degrade to lazy
             return False
 
@@ -1204,14 +1204,14 @@ class JoinedLoader(AbstractRelationshipLoader):
         if not self.parent.class_manager[self.key].impl.supports_population:
             raise sa_exc.InvalidRequestError(
                         "'%s' does not support object "
-                        "population - eager loading cannot be applied." % 
+                        "population - eager loading cannot be applied." %
                         self)
 
         our_path = path[self.key]
 
         eager_adapter = self._create_eager_adapter(
-                                                context, 
-                                                row, 
+                                                context,
+                                                row,
                                                 adapter, our_path)
 
         if eager_adapter is not False:
@@ -1219,7 +1219,7 @@ class JoinedLoader(AbstractRelationshipLoader):
 
             _instance = loading.instance_processor(
                                 self.mapper,
-                                context, 
+                                context,
                                 our_path[self.mapper],
                                 eager_adapter)
 
@@ -1346,8 +1346,8 @@ class LoadEagerFromAliasOption(PropertyOption):
         super(LoadEagerFromAliasOption, self).__init__(key)
         if alias is not None:
             if not isinstance(alias, basestring):
-                mapper, alias, is_aliased_class = \
-                        orm_util._entity_info(alias)
+                info = inspect(alias)
+                alias = info.selectable
         self.alias = alias
         self.chained = chained
 
@@ -1357,8 +1357,8 @@ class LoadEagerFromAliasOption(PropertyOption):
                 (root_mapper, propname) = path.path[-2:]
                 prop = root_mapper._props[propname]
                 adapter = query._polymorphic_adapters.get(prop.mapper, None)
-                path.setdefault(query, 
-                            "user_defined_eager_row_processor", 
+                path.setdefault(query,
+                            "user_defined_eager_row_processor",
                             adapter)
 
         root_mapper, propname = paths[-1].path[-2:]
@@ -1366,31 +1366,31 @@ class LoadEagerFromAliasOption(PropertyOption):
         if self.alias is not None:
             if isinstance(self.alias, basestring):
                 self.alias = prop.target.alias(self.alias)
-            paths[-1].set(query, "user_defined_eager_row_processor", 
-                sql_util.ColumnAdapter(self.alias, 
+            paths[-1].set(query, "user_defined_eager_row_processor",
+                sql_util.ColumnAdapter(self.alias,
                                 equivalents=prop.mapper._equivalent_columns)
             )
         else:
             if paths[-1].contains(query, "path_with_polymorphic"):
                 with_poly_info = paths[-1].get(query, "path_with_polymorphic")
                 adapter = orm_util.ORMAdapter(
-                            with_poly_info.entity, 
+                            with_poly_info.entity,
                             equivalents=prop.mapper._equivalent_columns,
                             adapt_required=True)
             else:
                 adapter = query._polymorphic_adapters.get(prop.mapper, None)
-            paths[-1].set(query, "user_defined_eager_row_processor", 
+            paths[-1].set(query, "user_defined_eager_row_processor",
                                     adapter)
 
 def single_parent_validator(desc, prop):
     def _do_check(state, value, oldvalue, initiator):
         if value is not None and initiator.key == prop.key:
             hasparent = initiator.hasparent(attributes.instance_state(value))
-            if hasparent and oldvalue is not value: 
+            if hasparent and oldvalue is not value:
                 raise sa_exc.InvalidRequestError(
                     "Instance %s is already associated with an instance "
                     "of %s via its %s attribute, and is only allowed a "
-                    "single parent." % 
+                    "single parent." %
                     (orm_util.instance_str(value), state.class_, prop)
                 )
         return value
@@ -1401,8 +1401,8 @@ def single_parent_validator(desc, prop):
     def set_(state, value, oldvalue, initiator):
         return _do_check(state, value, oldvalue, initiator)
 
-    event.listen(desc, 'append', append, raw=True, retval=True, 
+    event.listen(desc, 'append', append, raw=True, retval=True,
                             active_history=True)
-    event.listen(desc, 'set', set_, raw=True, retval=True, 
+    event.listen(desc, 'set', set_, raw=True, retval=True,
                             active_history=True)
 
index e9e11ee875d2e0d16ee8f7ce058be8d9ac02ed63..517f1acb48e4364c19c364f2ba41d6579239dce7 100644 (file)
@@ -220,7 +220,11 @@ class ORMAdapter(sql_util.ColumnAdapter):
     """
     def __init__(self, entity, equivalents=None,
                             chain_to=None, adapt_required=False):
-        self.mapper, selectable, is_aliased_class = _entity_info(entity)
+        info = inspection.inspect(entity)
+
+        self.mapper = info.mapper
+        selectable = info.selectable
+        is_aliased_class = info.is_aliased_class
         if is_aliased_class:
             self.aliased_class = entity
         else:
@@ -446,6 +450,7 @@ class AliasedClass(object):
         if alias is None:
             alias = mapper._with_polymorphic_selectable.alias(name=name)
         self._aliased_insp = AliasedInsp(
+            self,
             mapper,
             alias,
             name,
@@ -490,6 +495,7 @@ class AliasedClass(object):
 
     def __setstate__(self, state):
         self._aliased_insp = AliasedInsp(
+            self,
             state['mapper'],
             state['alias'],
             state['name'],
@@ -548,6 +554,7 @@ class AliasedClass(object):
             id(self), self.__target.__name__)
 
 AliasedInsp = util.namedtuple("AliasedInsp", [
+        "entity",
         "mapper",
         "selectable",
         "name",
@@ -674,11 +681,19 @@ class _ORMJoin(expression.Join):
             if join_to_left:
                 adapt_from = left.right
         else:
-            left_mapper, left, left_is_aliased = _entity_info(left)
+            info = inspection.inspect(left)
+            left_mapper = getattr(info, 'mapper', None)
+            left = info.selectable
+            left_is_aliased = getattr(info, 'is_aliased_class', False)
+
             if join_to_left and (left_is_aliased or not left_mapper):
                 adapt_from = left
 
-        right_mapper, right, right_is_aliased = _entity_info(right)
+        info = inspection.inspect(right)
+        right_mapper = getattr(info, 'mapper', None)
+        right = info.selectable
+        right_is_aliased = getattr(info, 'is_aliased_class', False)
+
         if right_is_aliased:
             adapt_to = right
         else:
@@ -939,42 +954,6 @@ def _is_aliased_class(entity):
     return insp is not None and \
         getattr(insp, "is_aliased_class", False)
 
-extended_entity_info = util.namedtuple("extended_entity_info", [
-    "entity",
-    "mapper",
-    "selectable",
-    "is_aliased_class",
-    "with_polymorphic_mappers",
-    "with_polymorphic_discriminator"
-])
-def _extended_entity_info(entity):
-    insp = inspection.inspect(entity)
-    return extended_entity_info(
-        entity,
-        insp.mapper if not insp.is_selectable else None,
-        insp.selectable,
-        insp.is_aliased_class if not insp.is_selectable else False,
-        insp.with_polymorphic_mappers if not insp.is_selectable else None,
-        insp.polymorphic_on if not insp.is_selectable else None
-    )
-
-def _entity_info(entity, compile=True):
-    """Return mapping information given a class, mapper, or AliasedClass.
-
-    Returns 3-tuple of: mapper, mapped selectable, boolean indicating if this
-    is an aliased() construct.
-
-    If the given entity is not a mapper, mapped class, or aliased construct,
-    returns None, the entity, False.  This is typically used to allow
-    unmapped selectables through.
-
-    """
-    insp = inspection.inspect(entity)
-    return \
-        insp.mapper if not insp.is_selectable else None,\
-        insp.selectable,\
-        insp.is_aliased_class if not insp.is_selectable else False,
-
 
 def _entity_descriptor(entity, key):
     """Return a class attribute given an entity and string name.
index 4ee01ac62f8a61a4b7ee1250de85cb3ca950cdc5..487182fac22aa7ed7c527ad0c8f0be12210ccdc6 100644 (file)
@@ -62,6 +62,20 @@ class TestORMInspection(_fixtures.FixtureTest):
         assert not insp.is_selectable
         assert not insp.is_aliased_class
 
+    def test_mapper_selectable_fixed(self):
+        from sqlalchemy.orm import mapper
+        class Foo(object):
+            pass
+        class Bar(Foo):
+            pass
+        user_table = self.tables.users
+        addresses_table = self.tables.addresses
+        mapper(Foo, user_table, with_polymorphic=(Bar,))
+        mapper(Bar, addresses_table, inherits=Foo)
+        i1 = inspect(Foo)
+        i2 = inspect(Foo)
+        assert i1.selectable is i2.selectable
+
     def test_aliased_class(self):
         Address = self.classes.Address
         ualias = aliased(Address)