summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2019-11-04 19:50:17 +0000
committerGerrit Code Review <gerrit@bbpush.zzzcomputing.com>2019-11-04 19:50:17 +0000
commit98f7c0c080ebbc8236fc0cc98970cb1b0112c01e (patch)
treef119c0003461e0bd7426bb493391a1c563817bb4 /lib/sqlalchemy/orm
parentbee7c3462d6235b7e108dbdbcd0727cdcbc49eb0 (diff)
parent29330ec1596f12462c501a65404ff52005b16b6c (diff)
downloadsqlalchemy-98f7c0c080ebbc8236fc0cc98970cb1b0112c01e.tar.gz
Merge "Add anonymizing context to cache keys, comparison; convert traversal"
Diffstat (limited to 'lib/sqlalchemy/orm')
-rw-r--r--lib/sqlalchemy/orm/attributes.py10
-rw-r--r--lib/sqlalchemy/orm/base.py1
-rw-r--r--lib/sqlalchemy/orm/interfaces.py13
-rw-r--r--lib/sqlalchemy/orm/mapper.py6
-rw-r--r--lib/sqlalchemy/orm/path_registry.py12
-rw-r--r--lib/sqlalchemy/orm/strategy_options.py38
-rw-r--r--lib/sqlalchemy/orm/util.py10
7 files changed, 78 insertions, 12 deletions
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py
index 83069f113..aa2986205 100644
--- a/lib/sqlalchemy/orm/attributes.py
+++ b/lib/sqlalchemy/orm/attributes.py
@@ -47,6 +47,8 @@ from .base import state_str
from .. import event
from .. import inspection
from .. import util
+from ..sql import base as sql_base
+from ..sql import visitors
@inspection._self_inspects
@@ -54,6 +56,7 @@ class QueryableAttribute(
interfaces._MappedAttribute,
interfaces.InspectionAttr,
interfaces.PropComparator,
+ sql_base.HasCacheKey,
):
"""Base class for :term:`descriptor` objects that intercept
attribute events on behalf of a :class:`.MapperProperty`
@@ -102,6 +105,13 @@ class QueryableAttribute(
if base[key].dispatch._active_history:
self.dispatch._active_history = True
+ _cache_key_traversal = [
+ # ("class_", visitors.ExtendedInternalTraversal.dp_plain_obj),
+ ("key", visitors.ExtendedInternalTraversal.dp_string),
+ ("_parententity", visitors.ExtendedInternalTraversal.dp_multi),
+ ("_of_type", visitors.ExtendedInternalTraversal.dp_multi),
+ ]
+
@util.memoized_property
def _supports_population(self):
return self.impl.supports_population
diff --git a/lib/sqlalchemy/orm/base.py b/lib/sqlalchemy/orm/base.py
index 6f8d19293..a3dea6b0e 100644
--- a/lib/sqlalchemy/orm/base.py
+++ b/lib/sqlalchemy/orm/base.py
@@ -216,7 +216,6 @@ def _assertions(*assertions):
for assertion in assertions:
assertion(self, fn.__name__)
fn(self, *args[1:], **kw)
- return self
return generate
diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py
index e94a81fed..704ce9df7 100644
--- a/lib/sqlalchemy/orm/interfaces.py
+++ b/lib/sqlalchemy/orm/interfaces.py
@@ -36,6 +36,8 @@ from .. import inspect
from .. import inspection
from .. import util
from ..sql import operators
+from ..sql import visitors
+from ..sql.traversals import HasCacheKey
__all__ = (
@@ -54,7 +56,9 @@ __all__ = (
)
-class MapperProperty(_MappedAttribute, InspectionAttr, util.MemoizedSlots):
+class MapperProperty(
+ HasCacheKey, _MappedAttribute, InspectionAttr, util.MemoizedSlots
+):
"""Represent a particular class attribute mapped by :class:`.Mapper`.
The most common occurrences of :class:`.MapperProperty` are the
@@ -74,6 +78,11 @@ class MapperProperty(_MappedAttribute, InspectionAttr, util.MemoizedSlots):
"info",
)
+ _cache_key_traversal = [
+ ("parent", visitors.ExtendedInternalTraversal.dp_has_cache_key),
+ ("key", visitors.ExtendedInternalTraversal.dp_string),
+ ]
+
cascade = frozenset()
"""The set of 'cascade' attribute names.
@@ -647,7 +656,7 @@ class MapperOption(object):
self.process_query(query)
- def _generate_cache_key(self, path):
+ def _generate_path_cache_key(self, path):
"""Used by the "baked lazy loader" to see if this option can be cached.
The "baked lazy loader" refers to the :class:`.Query` that is
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 376ad1923..548eca58d 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -71,7 +71,7 @@ _CONFIGURE_MUTEX = util.threading.RLock()
@inspection._self_inspects
@log.class_logger
-class Mapper(InspectionAttr):
+class Mapper(sql_base.HasCacheKey, InspectionAttr):
"""Define the correlation of class attributes to database table
columns.
@@ -729,6 +729,10 @@ class Mapper(InspectionAttr):
"""
return self
+ _cache_key_traversal = [
+ ("class_", visitors.ExtendedInternalTraversal.dp_plain_obj)
+ ]
+
@property
def entity(self):
r"""Part of the inspection API.
diff --git a/lib/sqlalchemy/orm/path_registry.py b/lib/sqlalchemy/orm/path_registry.py
index 2f680a3a1..585cb80bc 100644
--- a/lib/sqlalchemy/orm/path_registry.py
+++ b/lib/sqlalchemy/orm/path_registry.py
@@ -15,7 +15,8 @@ from .base import class_mapper
from .. import exc
from .. import inspection
from .. import util
-
+from ..sql import visitors
+from ..sql.traversals import HasCacheKey
log = logging.getLogger(__name__)
@@ -28,7 +29,7 @@ _WILDCARD_TOKEN = "*"
_DEFAULT_TOKEN = "_sa_default"
-class PathRegistry(object):
+class PathRegistry(HasCacheKey):
"""Represent query load paths and registry functions.
Basically represents structures like:
@@ -57,6 +58,10 @@ class PathRegistry(object):
is_token = False
is_root = False
+ _cache_key_traversal = [
+ ("path", visitors.ExtendedInternalTraversal.dp_has_cache_key_list)
+ ]
+
def __eq__(self, other):
return other is not None and self.path == other.path
@@ -78,6 +83,9 @@ class PathRegistry(object):
def __len__(self):
return len(self.path)
+ def __hash__(self):
+ return id(self)
+
@property
def length(self):
return len(self.path)
diff --git a/lib/sqlalchemy/orm/strategy_options.py b/lib/sqlalchemy/orm/strategy_options.py
index 26f47f616..99bbbe37c 100644
--- a/lib/sqlalchemy/orm/strategy_options.py
+++ b/lib/sqlalchemy/orm/strategy_options.py
@@ -26,11 +26,13 @@ from .. import inspect
from .. import util
from ..sql import coercions
from ..sql import roles
+from ..sql import visitors
from ..sql.base import _generative
from ..sql.base import Generative
+from ..sql.traversals import HasCacheKey
-class Load(Generative, MapperOption):
+class Load(HasCacheKey, Generative, MapperOption):
"""Represents loader options which modify the state of a
:class:`.Query` in order to affect how various mapped attributes are
loaded.
@@ -70,6 +72,17 @@ class Load(Generative, MapperOption):
"""
+ _cache_key_traversal = [
+ ("path", visitors.ExtendedInternalTraversal.dp_has_cache_key),
+ ("strategy", visitors.ExtendedInternalTraversal.dp_plain_obj),
+ ("_of_type", visitors.ExtendedInternalTraversal.dp_multi),
+ (
+ "_context_cache_key",
+ visitors.ExtendedInternalTraversal.dp_has_cache_key_tuples,
+ ),
+ ("local_opts", visitors.ExtendedInternalTraversal.dp_plain_dict),
+ ]
+
def __init__(self, entity):
insp = inspect(entity)
self.path = insp._path_registry
@@ -89,7 +102,16 @@ class Load(Generative, MapperOption):
load._of_type = None
return load
- def _generate_cache_key(self, path):
+ @property
+ def _context_cache_key(self):
+ serialized = []
+ for (key, loader_path), obj in self.context.items():
+ if key != "loader":
+ continue
+ serialized.append(loader_path + (obj,))
+ return serialized
+
+ def _generate_path_cache_key(self, path):
if path.path[0].is_aliased_class:
return False
@@ -522,9 +544,16 @@ class _UnboundLoad(Load):
self._to_bind = []
self.local_opts = {}
+ _cache_key_traversal = [
+ ("path", visitors.ExtendedInternalTraversal.dp_multi_list),
+ ("strategy", visitors.ExtendedInternalTraversal.dp_plain_obj),
+ ("_to_bind", visitors.ExtendedInternalTraversal.dp_has_cache_key_list),
+ ("local_opts", visitors.ExtendedInternalTraversal.dp_plain_dict),
+ ]
+
_is_chain_link = False
- def _generate_cache_key(self, path):
+ def _generate_path_cache_key(self, path):
serialized = ()
for val in self._to_bind:
for local_elem, val_elem in zip(self.path, val.path):
@@ -533,7 +562,7 @@ class _UnboundLoad(Load):
else:
opt = val._bind_loader([path.path[0]], None, None, False)
if opt:
- c_key = opt._generate_cache_key(path)
+ c_key = opt._generate_path_cache_key(path)
if c_key is False:
return False
elif c_key:
@@ -660,7 +689,6 @@ class _UnboundLoad(Load):
opt = meth(opt, all_tokens[-1], **kw)
opt._is_chain_link = False
-
return opt
def _chop_path(self, to_chop, path):
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py
index 5f0f41e8d..c86993678 100644
--- a/lib/sqlalchemy/orm/util.py
+++ b/lib/sqlalchemy/orm/util.py
@@ -30,10 +30,12 @@ from .. import exc as sa_exc
from .. import inspection
from .. import sql
from .. import util
+from ..sql import base as sql_base
from ..sql import coercions
from ..sql import expression
from ..sql import roles
from ..sql import util as sql_util
+from ..sql import visitors
all_cascades = frozenset(
@@ -530,7 +532,7 @@ class AliasedClass(object):
return str(self._aliased_insp)
-class AliasedInsp(InspectionAttr):
+class AliasedInsp(sql_base.HasCacheKey, InspectionAttr):
"""Provide an inspection interface for an
:class:`.AliasedClass` object.
@@ -627,6 +629,12 @@ class AliasedInsp(InspectionAttr):
def __clause_element__(self):
return self.selectable
+ _cache_key_traversal = [
+ ("name", visitors.ExtendedInternalTraversal.dp_string),
+ ("_adapt_on_names", visitors.ExtendedInternalTraversal.dp_boolean),
+ ("selectable", visitors.ExtendedInternalTraversal.dp_clauseelement),
+ ]
+
@property
def class_(self):
"""Return the mapped class ultimately represented by this