summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/util.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2013-08-14 19:58:34 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2013-08-14 19:58:34 -0400
commit59141d360e70d1a762719206e3cb0220b4c53fef (patch)
tree954d39dfa15a5c7b3970549dd77ec96a72444876 /lib/sqlalchemy/orm/util.py
parent688d799814fff2642926d3bce93b45965cf262da (diff)
downloadsqlalchemy-59141d360e70d1a762719206e3cb0220b4c53fef.tar.gz
- apply an import refactoring to the ORM as well
- rework the event system so that event modules load after their targets, dependencies are reversed - create an improved strategy lookup system for the ORM - rework the ORM to have very few import cycles - move out "importlater" to just util.dependency - other tricks to cross-populate modules in as clear a way as possible
Diffstat (limited to 'lib/sqlalchemy/orm/util.py')
-rw-r--r--lib/sqlalchemy/orm/util.py429
1 files changed, 7 insertions, 422 deletions
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py
index ae1ca2013..d10a78dd7 100644
--- a/lib/sqlalchemy/orm/util.py
+++ b/lib/sqlalchemy/orm/util.py
@@ -7,21 +7,19 @@
from .. import sql, util, event, exc as sa_exc, inspection
from ..sql import expression, util as sql_util, operators
-from .interfaces import PropComparator, MapperProperty, _InspectionAttr
-from itertools import chain
-from . import attributes, exc
+from .interfaces import PropComparator, MapperProperty
+from . import attributes
import re
-mapperlib = util.importlater("sqlalchemy.orm", "mapperlib")
+from .base import instance_str, state_str, state_class_str, attribute_str, \
+ state_attribute_str, object_mapper, object_state, _none_set
+from .base import class_mapper, _class_to_mapper
+from .base import _InspectionAttr
all_cascades = frozenset(("delete", "delete-orphan", "all", "merge",
"expunge", "save-update", "refresh-expire",
"none"))
-_INSTRUMENTOR = ('mapper', 'instrumentor')
-
-_none_set = frozenset([None])
-
class CascadeOptions(frozenset):
"""Keeps track of the options sent to relationship().cascade"""
@@ -245,211 +243,7 @@ class ORMAdapter(sql_util.ColumnAdapter):
else:
return None
-def _unreduce_path(path):
- return PathRegistry.deserialize(path)
-
-class PathRegistry(object):
- """Represent query load paths and registry functions.
-
- Basically represents structures like:
-
- (<User mapper>, "orders", <Order mapper>, "items", <Item mapper>)
-
- These structures are generated by things like
- query options (joinedload(), subqueryload(), etc.) and are
- used to compose keys stored in the query._attributes dictionary
- for various options.
-
- They are then re-composed at query compile/result row time as
- the query is formed and as rows are fetched, where they again
- serve to compose keys to look up options in the context.attributes
- dictionary, which is copied from query._attributes.
-
- The path structure has a limited amount of caching, where each
- "root" ultimately pulls from a fixed registry associated with
- the first mapper, that also contains elements for each of its
- property keys. However paths longer than two elements, which
- are the exception rather than the rule, are generated on an
- as-needed basis.
-
- """
-
- def __eq__(self, other):
- return other is not None and \
- self.path == other.path
-
- def set(self, attributes, key, value):
- attributes[(key, self.path)] = value
-
- def setdefault(self, attributes, key, value):
- attributes.setdefault((key, self.path), value)
-
- def get(self, attributes, key, value=None):
- key = (key, self.path)
- if key in attributes:
- return attributes[key]
- else:
- return value
-
- def __len__(self):
- return len(self.path)
-
- @property
- def length(self):
- return len(self.path)
-
- def pairs(self):
- path = self.path
- for i in range(0, len(path), 2):
- yield path[i], path[i + 1]
-
- def contains_mapper(self, mapper):
- for path_mapper in [
- self.path[i] for i in range(0, len(self.path), 2)
- ]:
- if isinstance(path_mapper, mapperlib.Mapper) and \
- path_mapper.isa(mapper):
- return True
- else:
- return False
-
- def contains(self, attributes, key):
- return (key, self.path) in attributes
-
- def __reduce__(self):
- return _unreduce_path, (self.serialize(), )
-
- def serialize(self):
- path = self.path
- return list(zip(
- [m.class_ for m in [path[i] for i in range(0, len(path), 2)]],
- [path[i].key for i in range(1, len(path), 2)] + [None]
- ))
-
- @classmethod
- def deserialize(cls, path):
- if path is None:
- return None
-
- p = tuple(chain(*[(class_mapper(mcls),
- class_mapper(mcls).attrs[key]
- if key is not None else None)
- for mcls, key in path]))
- if p and p[-1] is None:
- p = p[0:-1]
- return cls.coerce(p)
-
- @classmethod
- def per_mapper(cls, mapper):
- return EntityRegistry(
- cls.root, mapper
- )
-
- @classmethod
- def coerce(cls, raw):
- return util.reduce(lambda prev, next: prev[next], raw, cls.root)
-
- @classmethod
- def token(cls, token):
- return TokenRegistry(cls.root, token)
-
- def __add__(self, other):
- return util.reduce(
- lambda prev, next: prev[next],
- other.path, self)
-
- def __repr__(self):
- return "%s(%r)" % (self.__class__.__name__, self.path, )
-
-
-class RootRegistry(PathRegistry):
- """Root registry, defers to mappers so that
- paths are maintained per-root-mapper.
-
- """
- path = ()
-
- def __getitem__(self, entity):
- return entity._path_registry
-PathRegistry.root = RootRegistry()
-
-class TokenRegistry(PathRegistry):
- def __init__(self, parent, token):
- self.token = token
- self.parent = parent
- self.path = parent.path + (token,)
-
- def __getitem__(self, entity):
- raise NotImplementedError()
-
-class PropRegistry(PathRegistry):
- def __init__(self, parent, prop):
- # restate this path in terms of the
- # given MapperProperty's parent.
- insp = inspection.inspect(parent[-1])
- if not insp.is_aliased_class or insp._use_mapper_path:
- parent = parent.parent[prop.parent]
- elif insp.is_aliased_class and insp.with_polymorphic_mappers:
- if prop.parent is not insp.mapper and \
- prop.parent in insp.with_polymorphic_mappers:
- subclass_entity = parent[-1]._entity_for_mapper(prop.parent)
- parent = parent.parent[subclass_entity]
-
- self.prop = prop
- self.parent = parent
- self.path = parent.path + (prop,)
-
- def __getitem__(self, entity):
- if isinstance(entity, (int, slice)):
- return self.path[entity]
- else:
- return EntityRegistry(
- self, entity
- )
-
-
-class EntityRegistry(PathRegistry, dict):
- is_aliased_class = False
-
- def __init__(self, parent, entity):
- self.key = entity
- self.parent = parent
- self.is_aliased_class = entity.is_aliased_class
-
- self.path = parent.path + (entity,)
-
- def __bool__(self):
- return True
- __nonzero__ = __bool__
-
- def __getitem__(self, entity):
- if isinstance(entity, (int, slice)):
- return self.path[entity]
- else:
- return dict.__getitem__(self, entity)
-
- def _inlined_get_for(self, prop, context, key):
- """an inlined version of:
-
- cls = path[mapperproperty].get(context, key)
-
- Skips the isinstance() check in __getitem__
- and the extra method call for get().
- Used by StrategizedProperty for its
- very frequent lookup.
-
- """
- path = dict.__getitem__(self, prop)
- path_key = (key, path.path)
- if path_key in context.attributes:
- return context.attributes[path_key]
- else:
- return None
-
- def __missing__(self, key):
- self[key] = item = PropRegistry(self, key)
- return item
-
+from .path_registry import _unreduce_path, PathRegistry, RootRegistry, TokenRegistry, PropRegistry, EntityRegistry
class AliasedClass(object):
"""Represents an "aliased" form of a mapped class for usage with Query.
@@ -1053,186 +847,6 @@ def with_parent(instance, prop):
value_is_parent=True)
-def _attr_as_key(attr):
- if hasattr(attr, 'key'):
- return attr.key
- else:
- return expression._column_as_key(attr)
-
-
-_state_mapper = util.dottedgetter('manager.mapper')
-
-
-@inspection._inspects(object)
-def _inspect_mapped_object(instance):
- try:
- return attributes.instance_state(instance)
- # TODO: whats the py-2/3 syntax to catch two
- # different kinds of exceptions at once ?
- except exc.UnmappedClassError:
- return None
- except exc.NO_STATE:
- return None
-
-
-@inspection._inspects(type)
-def _inspect_mapped_class(class_, configure=False):
- try:
- class_manager = attributes.manager_of_class(class_)
- if not class_manager.is_mapped:
- return None
- mapper = class_manager.mapper
- if configure and mapperlib.module._new_mappers:
- mapperlib.configure_mappers()
- return mapper
-
- except exc.NO_STATE:
- return None
-
-
-def object_mapper(instance):
- """Given an object, return the primary Mapper associated with the object
- instance.
-
- Raises :class:`sqlalchemy.orm.exc.UnmappedInstanceError`
- if no mapping is configured.
-
- This function is available via the inspection system as::
-
- inspect(instance).mapper
-
- Using the inspection system will raise
- :class:`sqlalchemy.exc.NoInspectionAvailable` if the instance is
- not part of a mapping.
-
- """
- return object_state(instance).mapper
-
-
-def object_state(instance):
- """Given an object, return the :class:`.InstanceState`
- associated with the object.
-
- Raises :class:`sqlalchemy.orm.exc.UnmappedInstanceError`
- if no mapping is configured.
-
- Equivalent functionality is available via the :func:`.inspect`
- function as::
-
- inspect(instance)
-
- Using the inspection system will raise
- :class:`sqlalchemy.exc.NoInspectionAvailable` if the instance is
- not part of a mapping.
-
- """
- state = _inspect_mapped_object(instance)
- if state is None:
- raise exc.UnmappedInstanceError(instance)
- else:
- return state
-
-
-def class_mapper(class_, configure=True):
- """Given a class, return the primary :class:`.Mapper` associated
- with the key.
-
- Raises :class:`.UnmappedClassError` if no mapping is configured
- on the given class, or :class:`.ArgumentError` if a non-class
- object is passed.
-
- Equivalent functionality is available via the :func:`.inspect`
- function as::
-
- inspect(some_mapped_class)
-
- Using the inspection system will raise
- :class:`sqlalchemy.exc.NoInspectionAvailable` if the class is not mapped.
-
- """
- mapper = _inspect_mapped_class(class_, configure=configure)
- if mapper is None:
- if not isinstance(class_, type):
- raise sa_exc.ArgumentError(
- "Class object expected, got '%r'." % class_)
- raise exc.UnmappedClassError(class_)
- else:
- return mapper
-
-
-def _class_to_mapper(class_or_mapper):
- insp = inspection.inspect(class_or_mapper, False)
- if insp is not None:
- return insp.mapper
- else:
- raise exc.UnmappedClassError(class_or_mapper)
-
-
-def _mapper_or_none(entity):
- """Return the :class:`.Mapper` for the given class or None if the
- class is not mapped."""
-
- insp = inspection.inspect(entity, False)
- if insp is not None:
- return insp.mapper
- else:
- return None
-
-
-def _is_mapped_class(entity):
- """Return True if the given object is a mapped class,
- :class:`.Mapper`, or :class:`.AliasedClass`."""
-
- insp = inspection.inspect(entity, False)
- return insp is not None and \
- hasattr(insp, "mapper") and \
- (
- insp.is_mapper
- or insp.is_aliased_class
- )
-
-
-def _is_aliased_class(entity):
- insp = inspection.inspect(entity, False)
- return insp is not None and \
- getattr(insp, "is_aliased_class", False)
-
-
-def _entity_descriptor(entity, key):
- """Return a class attribute given an entity and string name.
-
- May return :class:`.InstrumentedAttribute` or user-defined
- attribute.
-
- """
- insp = inspection.inspect(entity)
- if insp.is_selectable:
- description = entity
- entity = insp.c
- elif insp.is_aliased_class:
- entity = insp.entity
- description = entity
- elif hasattr(insp, "mapper"):
- description = entity = insp.mapper.class_
- else:
- description = entity
-
- try:
- return getattr(entity, key)
- except AttributeError:
- raise sa_exc.InvalidRequestError(
- "Entity '%s' has no property '%s'" %
- (description, key)
- )
-
-
-def _orm_columns(entity):
- insp = inspection.inspect(entity, False)
- if hasattr(insp, 'selectable'):
- return [c for c in insp.selectable.c]
- else:
- return [entity]
-
def has_identity(object):
"""Return True if the given object has a database
@@ -1260,36 +874,7 @@ def was_deleted(object):
state = attributes.instance_state(object)
return state.deleted
-def instance_str(instance):
- """Return a string describing an instance."""
-
- return state_str(attributes.instance_state(instance))
-
-
-def state_str(state):
- """Return a string describing an instance via its InstanceState."""
-
- if state is None:
- return "None"
- else:
- return '<%s at 0x%x>' % (state.class_.__name__, id(state.obj()))
-
-
-def state_class_str(state):
- """Return a string describing an instance's class via its InstanceState."""
-
- if state is None:
- return "None"
- else:
- return '<%s>' % (state.class_.__name__, )
-
-
-def attribute_str(instance, attribute):
- return instance_str(instance) + "." + attribute
-
-def state_attribute_str(state, attribute):
- return state_str(state) + "." + attribute
def randomize_unitofwork():