Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/sql/base.py: 56%
642 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
1# sql/base.py
2# Copyright (C) 2005-2023 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
8"""Foundational utilities common to many sql modules.
10"""
13import itertools
14import operator
15import re
17from . import roles
18from . import visitors
19from .traversals import HasCacheKey # noqa
20from .traversals import HasCopyInternals # noqa
21from .traversals import MemoizedHasCacheKey # noqa
22from .visitors import ClauseVisitor
23from .visitors import ExtendedInternalTraversal
24from .visitors import InternalTraversal
25from .. import exc
26from .. import util
27from ..util import HasMemoized
28from ..util import hybridmethod
31coercions = None
32elements = None
33type_api = None
35PARSE_AUTOCOMMIT = util.symbol("PARSE_AUTOCOMMIT")
36NO_ARG = util.symbol("NO_ARG")
39class Immutable(object):
40 """mark a ClauseElement as 'immutable' when expressions are cloned."""
42 _is_immutable = True
44 def unique_params(self, *optionaldict, **kwargs):
45 raise NotImplementedError("Immutable objects do not support copying")
47 def params(self, *optionaldict, **kwargs):
48 raise NotImplementedError("Immutable objects do not support copying")
50 def _clone(self, **kw):
51 return self
53 def _copy_internals(self, **kw):
54 pass
57class SingletonConstant(Immutable):
58 """Represent SQL constants like NULL, TRUE, FALSE"""
60 _is_singleton_constant = True
62 def __new__(cls, *arg, **kw):
63 return cls._singleton
65 @classmethod
66 def _create_singleton(cls):
67 obj = object.__new__(cls)
68 obj.__init__()
70 # for a long time this was an empty frozenset, meaning
71 # a SingletonConstant would never be a "corresponding column" in
72 # a statement. This referred to #6259. However, in #7154 we see
73 # that we do in fact need "correspondence" to work when matching cols
74 # in result sets, so the non-correspondence was moved to a more
75 # specific level when we are actually adapting expressions for SQL
76 # render only.
77 obj.proxy_set = frozenset([obj])
78 cls._singleton = obj
81def _from_objects(*elements):
82 return itertools.chain.from_iterable(
83 [element._from_objects for element in elements]
84 )
87def _select_iterables(elements):
88 """expand tables into individual columns in the
89 given list of column expressions.
91 """
92 return itertools.chain.from_iterable(
93 [c._select_iterable for c in elements]
94 )
97def _generative(fn):
98 """non-caching _generative() decorator.
100 This is basically the legacy decorator that copies the object and
101 runs a method on the new copy.
103 """
105 @util.decorator
106 def _generative(fn, self, *args, **kw):
107 """Mark a method as generative."""
109 self = self._generate()
110 x = fn(self, *args, **kw)
111 assert x is None, "generative methods must have no return value"
112 return self
114 decorated = _generative(fn)
115 decorated.non_generative = fn
116 return decorated
119def _exclusive_against(*names, **kw):
120 msgs = kw.pop("msgs", {})
122 defaults = kw.pop("defaults", {})
124 getters = [
125 (name, operator.attrgetter(name), defaults.get(name, None))
126 for name in names
127 ]
129 @util.decorator
130 def check(fn, *args, **kw):
131 # make pylance happy by not including "self" in the argument
132 # list
133 self = args[0]
134 args = args[1:]
135 for name, getter, default_ in getters:
136 if getter(self) is not default_:
137 msg = msgs.get(
138 name,
139 "Method %s() has already been invoked on this %s construct"
140 % (fn.__name__, self.__class__),
141 )
142 raise exc.InvalidRequestError(msg)
143 return fn(self, *args, **kw)
145 return check
148def _clone(element, **kw):
149 return element._clone(**kw)
152def _expand_cloned(elements):
153 """expand the given set of ClauseElements to be the set of all 'cloned'
154 predecessors.
156 """
157 return itertools.chain(*[x._cloned_set for x in elements])
160def _cloned_intersection(a, b):
161 """return the intersection of sets a and b, counting
162 any overlap between 'cloned' predecessors.
164 The returned set is in terms of the entities present within 'a'.
166 """
167 all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b))
168 return set(
169 elem for elem in a if all_overlap.intersection(elem._cloned_set)
170 )
173def _cloned_difference(a, b):
174 all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b))
175 return set(
176 elem for elem in a if not all_overlap.intersection(elem._cloned_set)
177 )
180class _DialectArgView(util.collections_abc.MutableMapping):
181 """A dictionary view of dialect-level arguments in the form
182 <dialectname>_<argument_name>.
184 """
186 def __init__(self, obj):
187 self.obj = obj
189 def _key(self, key):
190 try:
191 dialect, value_key = key.split("_", 1)
192 except ValueError as err:
193 util.raise_(KeyError(key), replace_context=err)
194 else:
195 return dialect, value_key
197 def __getitem__(self, key):
198 dialect, value_key = self._key(key)
200 try:
201 opt = self.obj.dialect_options[dialect]
202 except exc.NoSuchModuleError as err:
203 util.raise_(KeyError(key), replace_context=err)
204 else:
205 return opt[value_key]
207 def __setitem__(self, key, value):
208 try:
209 dialect, value_key = self._key(key)
210 except KeyError as err:
211 util.raise_(
212 exc.ArgumentError(
213 "Keys must be of the form <dialectname>_<argname>"
214 ),
215 replace_context=err,
216 )
217 else:
218 self.obj.dialect_options[dialect][value_key] = value
220 def __delitem__(self, key):
221 dialect, value_key = self._key(key)
222 del self.obj.dialect_options[dialect][value_key]
224 def __len__(self):
225 return sum(
226 len(args._non_defaults)
227 for args in self.obj.dialect_options.values()
228 )
230 def __iter__(self):
231 return (
232 "%s_%s" % (dialect_name, value_name)
233 for dialect_name in self.obj.dialect_options
234 for value_name in self.obj.dialect_options[
235 dialect_name
236 ]._non_defaults
237 )
240class _DialectArgDict(util.collections_abc.MutableMapping):
241 """A dictionary view of dialect-level arguments for a specific
242 dialect.
244 Maintains a separate collection of user-specified arguments
245 and dialect-specified default arguments.
247 """
249 def __init__(self):
250 self._non_defaults = {}
251 self._defaults = {}
253 def __len__(self):
254 return len(set(self._non_defaults).union(self._defaults))
256 def __iter__(self):
257 return iter(set(self._non_defaults).union(self._defaults))
259 def __getitem__(self, key):
260 if key in self._non_defaults:
261 return self._non_defaults[key]
262 else:
263 return self._defaults[key]
265 def __setitem__(self, key, value):
266 self._non_defaults[key] = value
268 def __delitem__(self, key):
269 del self._non_defaults[key]
272@util.preload_module("sqlalchemy.dialects")
273def _kw_reg_for_dialect(dialect_name):
274 dialect_cls = util.preloaded.dialects.registry.load(dialect_name)
275 if dialect_cls.construct_arguments is None:
276 return None
277 return dict(dialect_cls.construct_arguments)
280class DialectKWArgs(object):
281 """Establish the ability for a class to have dialect-specific arguments
282 with defaults and constructor validation.
284 The :class:`.DialectKWArgs` interacts with the
285 :attr:`.DefaultDialect.construct_arguments` present on a dialect.
287 .. seealso::
289 :attr:`.DefaultDialect.construct_arguments`
291 """
293 _dialect_kwargs_traverse_internals = [
294 ("dialect_options", InternalTraversal.dp_dialect_options)
295 ]
297 @classmethod
298 def argument_for(cls, dialect_name, argument_name, default):
299 """Add a new kind of dialect-specific keyword argument for this class.
301 E.g.::
303 Index.argument_for("mydialect", "length", None)
305 some_index = Index('a', 'b', mydialect_length=5)
307 The :meth:`.DialectKWArgs.argument_for` method is a per-argument
308 way adding extra arguments to the
309 :attr:`.DefaultDialect.construct_arguments` dictionary. This
310 dictionary provides a list of argument names accepted by various
311 schema-level constructs on behalf of a dialect.
313 New dialects should typically specify this dictionary all at once as a
314 data member of the dialect class. The use case for ad-hoc addition of
315 argument names is typically for end-user code that is also using
316 a custom compilation scheme which consumes the additional arguments.
318 :param dialect_name: name of a dialect. The dialect must be
319 locatable, else a :class:`.NoSuchModuleError` is raised. The
320 dialect must also include an existing
321 :attr:`.DefaultDialect.construct_arguments` collection, indicating
322 that it participates in the keyword-argument validation and default
323 system, else :class:`.ArgumentError` is raised. If the dialect does
324 not include this collection, then any keyword argument can be
325 specified on behalf of this dialect already. All dialects packaged
326 within SQLAlchemy include this collection, however for third party
327 dialects, support may vary.
329 :param argument_name: name of the parameter.
331 :param default: default value of the parameter.
333 .. versionadded:: 0.9.4
335 """
337 construct_arg_dictionary = DialectKWArgs._kw_registry[dialect_name]
338 if construct_arg_dictionary is None:
339 raise exc.ArgumentError(
340 "Dialect '%s' does have keyword-argument "
341 "validation and defaults enabled configured" % dialect_name
342 )
343 if cls not in construct_arg_dictionary:
344 construct_arg_dictionary[cls] = {}
345 construct_arg_dictionary[cls][argument_name] = default
347 @util.memoized_property
348 def dialect_kwargs(self):
349 """A collection of keyword arguments specified as dialect-specific
350 options to this construct.
352 The arguments are present here in their original ``<dialect>_<kwarg>``
353 format. Only arguments that were actually passed are included;
354 unlike the :attr:`.DialectKWArgs.dialect_options` collection, which
355 contains all options known by this dialect including defaults.
357 The collection is also writable; keys are accepted of the
358 form ``<dialect>_<kwarg>`` where the value will be assembled
359 into the list of options.
361 .. versionadded:: 0.9.2
363 .. versionchanged:: 0.9.4 The :attr:`.DialectKWArgs.dialect_kwargs`
364 collection is now writable.
366 .. seealso::
368 :attr:`.DialectKWArgs.dialect_options` - nested dictionary form
370 """
371 return _DialectArgView(self)
373 @property
374 def kwargs(self):
375 """A synonym for :attr:`.DialectKWArgs.dialect_kwargs`."""
376 return self.dialect_kwargs
378 _kw_registry = util.PopulateDict(_kw_reg_for_dialect)
380 def _kw_reg_for_dialect_cls(self, dialect_name):
381 construct_arg_dictionary = DialectKWArgs._kw_registry[dialect_name]
382 d = _DialectArgDict()
384 if construct_arg_dictionary is None:
385 d._defaults.update({"*": None})
386 else:
387 for cls in reversed(self.__class__.__mro__):
388 if cls in construct_arg_dictionary:
389 d._defaults.update(construct_arg_dictionary[cls])
390 return d
392 @util.memoized_property
393 def dialect_options(self):
394 """A collection of keyword arguments specified as dialect-specific
395 options to this construct.
397 This is a two-level nested registry, keyed to ``<dialect_name>``
398 and ``<argument_name>``. For example, the ``postgresql_where``
399 argument would be locatable as::
401 arg = my_object.dialect_options['postgresql']['where']
403 .. versionadded:: 0.9.2
405 .. seealso::
407 :attr:`.DialectKWArgs.dialect_kwargs` - flat dictionary form
409 """
411 return util.PopulateDict(
412 util.portable_instancemethod(self._kw_reg_for_dialect_cls)
413 )
415 def _validate_dialect_kwargs(self, kwargs):
416 # validate remaining kwargs that they all specify DB prefixes
418 if not kwargs:
419 return
421 for k in kwargs:
422 m = re.match("^(.+?)_(.+)$", k)
423 if not m:
424 raise TypeError(
425 "Additional arguments should be "
426 "named <dialectname>_<argument>, got '%s'" % k
427 )
428 dialect_name, arg_name = m.group(1, 2)
430 try:
431 construct_arg_dictionary = self.dialect_options[dialect_name]
432 except exc.NoSuchModuleError:
433 util.warn(
434 "Can't validate argument %r; can't "
435 "locate any SQLAlchemy dialect named %r"
436 % (k, dialect_name)
437 )
438 self.dialect_options[dialect_name] = d = _DialectArgDict()
439 d._defaults.update({"*": None})
440 d._non_defaults[arg_name] = kwargs[k]
441 else:
442 if (
443 "*" not in construct_arg_dictionary
444 and arg_name not in construct_arg_dictionary
445 ):
446 raise exc.ArgumentError(
447 "Argument %r is not accepted by "
448 "dialect %r on behalf of %r"
449 % (k, dialect_name, self.__class__)
450 )
451 else:
452 construct_arg_dictionary[arg_name] = kwargs[k]
455class CompileState(object):
456 """Produces additional object state necessary for a statement to be
457 compiled.
459 the :class:`.CompileState` class is at the base of classes that assemble
460 state for a particular statement object that is then used by the
461 compiler. This process is essentially an extension of the process that
462 the SQLCompiler.visit_XYZ() method takes, however there is an emphasis
463 on converting raw user intent into more organized structures rather than
464 producing string output. The top-level :class:`.CompileState` for the
465 statement being executed is also accessible when the execution context
466 works with invoking the statement and collecting results.
468 The production of :class:`.CompileState` is specific to the compiler, such
469 as within the :meth:`.SQLCompiler.visit_insert`,
470 :meth:`.SQLCompiler.visit_select` etc. methods. These methods are also
471 responsible for associating the :class:`.CompileState` with the
472 :class:`.SQLCompiler` itself, if the statement is the "toplevel" statement,
473 i.e. the outermost SQL statement that's actually being executed.
474 There can be other :class:`.CompileState` objects that are not the
475 toplevel, such as when a SELECT subquery or CTE-nested
476 INSERT/UPDATE/DELETE is generated.
478 .. versionadded:: 1.4
480 """
482 __slots__ = ("statement",)
484 plugins = {}
486 @classmethod
487 def create_for_statement(cls, statement, compiler, **kw):
488 # factory construction.
490 if statement._propagate_attrs:
491 plugin_name = statement._propagate_attrs.get(
492 "compile_state_plugin", "default"
493 )
494 klass = cls.plugins.get(
495 (plugin_name, statement._effective_plugin_target), None
496 )
497 if klass is None:
498 klass = cls.plugins[
499 ("default", statement._effective_plugin_target)
500 ]
502 else:
503 klass = cls.plugins[
504 ("default", statement._effective_plugin_target)
505 ]
507 if klass is cls:
508 return cls(statement, compiler, **kw)
509 else:
510 return klass.create_for_statement(statement, compiler, **kw)
512 def __init__(self, statement, compiler, **kw):
513 self.statement = statement
515 @classmethod
516 def get_plugin_class(cls, statement):
517 plugin_name = statement._propagate_attrs.get(
518 "compile_state_plugin", None
519 )
521 if plugin_name:
522 key = (plugin_name, statement._effective_plugin_target)
523 if key in cls.plugins:
524 return cls.plugins[key]
526 # there's no case where we call upon get_plugin_class() and want
527 # to get None back, there should always be a default. return that
528 # if there was no plugin-specific class (e.g. Insert with "orm"
529 # plugin)
530 try:
531 return cls.plugins[("default", statement._effective_plugin_target)]
532 except KeyError:
533 return None
535 @classmethod
536 def _get_plugin_class_for_plugin(cls, statement, plugin_name):
537 try:
538 return cls.plugins[
539 (plugin_name, statement._effective_plugin_target)
540 ]
541 except KeyError:
542 return None
544 @classmethod
545 def plugin_for(cls, plugin_name, visit_name):
546 def decorate(cls_to_decorate):
547 cls.plugins[(plugin_name, visit_name)] = cls_to_decorate
548 return cls_to_decorate
550 return decorate
553class Generative(HasMemoized):
554 """Provide a method-chaining pattern in conjunction with the
555 @_generative decorator."""
557 def _generate(self):
558 skip = self._memoized_keys
559 cls = self.__class__
560 s = cls.__new__(cls)
561 if skip:
562 # ensure this iteration remains atomic
563 s.__dict__ = {
564 k: v for k, v in self.__dict__.copy().items() if k not in skip
565 }
566 else:
567 s.__dict__ = self.__dict__.copy()
568 return s
571class InPlaceGenerative(HasMemoized):
572 """Provide a method-chaining pattern in conjunction with the
573 @_generative decorator that mutates in place."""
575 def _generate(self):
576 skip = self._memoized_keys
577 for k in skip:
578 self.__dict__.pop(k, None)
579 return self
582class HasCompileState(Generative):
583 """A class that has a :class:`.CompileState` associated with it."""
585 _compile_state_plugin = None
587 _attributes = util.immutabledict()
589 _compile_state_factory = CompileState.create_for_statement
592class _MetaOptions(type):
593 """metaclass for the Options class."""
595 def __init__(cls, classname, bases, dict_):
596 cls._cache_attrs = tuple(
597 sorted(
598 d
599 for d in dict_
600 if not d.startswith("__")
601 and d not in ("_cache_key_traversal",)
602 )
603 )
604 type.__init__(cls, classname, bases, dict_)
606 def __add__(self, other):
607 o1 = self()
609 if set(other).difference(self._cache_attrs):
610 raise TypeError(
611 "dictionary contains attributes not covered by "
612 "Options class %s: %r"
613 % (self, set(other).difference(self._cache_attrs))
614 )
616 o1.__dict__.update(other)
617 return o1
620class Options(util.with_metaclass(_MetaOptions)):
621 """A cacheable option dictionary with defaults."""
623 def __init__(self, **kw):
624 self.__dict__.update(kw)
626 def __add__(self, other):
627 o1 = self.__class__.__new__(self.__class__)
628 o1.__dict__.update(self.__dict__)
630 if set(other).difference(self._cache_attrs):
631 raise TypeError(
632 "dictionary contains attributes not covered by "
633 "Options class %s: %r"
634 % (self, set(other).difference(self._cache_attrs))
635 )
637 o1.__dict__.update(other)
638 return o1
640 def __eq__(self, other):
641 # TODO: very inefficient. This is used only in test suites
642 # right now.
643 for a, b in util.zip_longest(self._cache_attrs, other._cache_attrs):
644 if getattr(self, a) != getattr(other, b):
645 return False
646 return True
648 def __repr__(self):
649 # TODO: fairly inefficient, used only in debugging right now.
651 return "%s(%s)" % (
652 self.__class__.__name__,
653 ", ".join(
654 "%s=%r" % (k, self.__dict__[k])
655 for k in self._cache_attrs
656 if k in self.__dict__
657 ),
658 )
660 @classmethod
661 def isinstance(cls, klass):
662 return issubclass(cls, klass)
664 @hybridmethod
665 def add_to_element(self, name, value):
666 return self + {name: getattr(self, name) + value}
668 @hybridmethod
669 def _state_dict(self):
670 return self.__dict__
672 _state_dict_const = util.immutabledict()
674 @_state_dict.classlevel
675 def _state_dict(cls):
676 return cls._state_dict_const
678 @classmethod
679 def safe_merge(cls, other):
680 d = other._state_dict()
682 # only support a merge with another object of our class
683 # and which does not have attrs that we don't. otherwise
684 # we risk having state that might not be part of our cache
685 # key strategy
687 if (
688 cls is not other.__class__
689 and other._cache_attrs
690 and set(other._cache_attrs).difference(cls._cache_attrs)
691 ):
692 raise TypeError(
693 "other element %r is not empty, is not of type %s, "
694 "and contains attributes not covered here %r"
695 % (
696 other,
697 cls,
698 set(other._cache_attrs).difference(cls._cache_attrs),
699 )
700 )
701 return cls + d
703 @classmethod
704 def from_execution_options(
705 cls, key, attrs, exec_options, statement_exec_options
706 ):
707 """process Options argument in terms of execution options.
710 e.g.::
712 (
713 load_options,
714 execution_options,
715 ) = QueryContext.default_load_options.from_execution_options(
716 "_sa_orm_load_options",
717 {
718 "populate_existing",
719 "autoflush",
720 "yield_per"
721 },
722 execution_options,
723 statement._execution_options,
724 )
726 get back the Options and refresh "_sa_orm_load_options" in the
727 exec options dict w/ the Options as well
729 """
731 # common case is that no options we are looking for are
732 # in either dictionary, so cancel for that first
733 check_argnames = attrs.intersection(
734 set(exec_options).union(statement_exec_options)
735 )
737 existing_options = exec_options.get(key, cls)
739 if check_argnames:
740 result = {}
741 for argname in check_argnames:
742 local = "_" + argname
743 if argname in exec_options:
744 result[local] = exec_options[argname]
745 elif argname in statement_exec_options:
746 result[local] = statement_exec_options[argname]
748 new_options = existing_options + result
749 exec_options = util.immutabledict().merge_with(
750 exec_options, {key: new_options}
751 )
752 return new_options, exec_options
754 else:
755 return existing_options, exec_options
758class CacheableOptions(Options, HasCacheKey):
759 @hybridmethod
760 def _gen_cache_key(self, anon_map, bindparams):
761 return HasCacheKey._gen_cache_key(self, anon_map, bindparams)
763 @_gen_cache_key.classlevel
764 def _gen_cache_key(cls, anon_map, bindparams):
765 return (cls, ())
767 @hybridmethod
768 def _generate_cache_key(self):
769 return HasCacheKey._generate_cache_key_for_object(self)
772class ExecutableOption(HasCopyInternals):
773 _annotations = util.EMPTY_DICT
775 __visit_name__ = "executable_option"
777 _is_has_cache_key = False
779 def _clone(self, **kw):
780 """Create a shallow copy of this ExecutableOption."""
781 c = self.__class__.__new__(self.__class__)
782 c.__dict__ = dict(self.__dict__)
783 return c
786class Executable(roles.StatementRole, Generative):
787 """Mark a :class:`_expression.ClauseElement` as supporting execution.
789 :class:`.Executable` is a superclass for all "statement" types
790 of objects, including :func:`select`, :func:`delete`, :func:`update`,
791 :func:`insert`, :func:`text`.
793 """
795 supports_execution = True
796 _execution_options = util.immutabledict()
797 _bind = None
798 _with_options = ()
799 _with_context_options = ()
801 _executable_traverse_internals = [
802 ("_with_options", InternalTraversal.dp_executable_options),
803 (
804 "_with_context_options",
805 ExtendedInternalTraversal.dp_with_context_options,
806 ),
807 ("_propagate_attrs", ExtendedInternalTraversal.dp_propagate_attrs),
808 ]
810 is_select = False
811 is_update = False
812 is_insert = False
813 is_text = False
814 is_delete = False
815 is_dml = False
817 @property
818 def _effective_plugin_target(self):
819 return self.__visit_name__
821 @_generative
822 def options(self, *options):
823 """Apply options to this statement.
825 In the general sense, options are any kind of Python object
826 that can be interpreted by the SQL compiler for the statement.
827 These options can be consumed by specific dialects or specific kinds
828 of compilers.
830 The most commonly known kind of option are the ORM level options
831 that apply "eager load" and other loading behaviors to an ORM
832 query. However, options can theoretically be used for many other
833 purposes.
835 For background on specific kinds of options for specific kinds of
836 statements, refer to the documentation for those option objects.
838 .. versionchanged:: 1.4 - added :meth:`.Generative.options` to
839 Core statement objects towards the goal of allowing unified
840 Core / ORM querying capabilities.
842 .. seealso::
844 :ref:`deferred_options` - refers to options specific to the usage
845 of ORM queries
847 :ref:`relationship_loader_options` - refers to options specific
848 to the usage of ORM queries
850 """
851 self._with_options += tuple(
852 coercions.expect(roles.ExecutableOptionRole, opt)
853 for opt in options
854 )
856 @_generative
857 def _set_compile_options(self, compile_options):
858 """Assign the compile options to a new value.
860 :param compile_options: appropriate CacheableOptions structure
862 """
864 self._compile_options = compile_options
866 @_generative
867 def _update_compile_options(self, options):
868 """update the _compile_options with new keys."""
870 self._compile_options += options
872 @_generative
873 def _add_context_option(self, callable_, cache_args):
874 """Add a context option to this statement.
876 These are callable functions that will
877 be given the CompileState object upon compilation.
879 A second argument cache_args is required, which will be combined with
880 the ``__code__`` identity of the function itself in order to produce a
881 cache key.
883 """
884 self._with_context_options += ((callable_, cache_args),)
886 @_generative
887 def execution_options(self, **kw):
888 """Set non-SQL options for the statement which take effect during
889 execution.
891 Execution options can be set on a per-statement or
892 per :class:`_engine.Connection` basis. Additionally, the
893 :class:`_engine.Engine` and ORM :class:`~.orm.query.Query`
894 objects provide
895 access to execution options which they in turn configure upon
896 connections.
898 The :meth:`execution_options` method is generative. A new
899 instance of this statement is returned that contains the options::
901 statement = select(table.c.x, table.c.y)
902 statement = statement.execution_options(autocommit=True)
904 Note that only a subset of possible execution options can be applied
905 to a statement - these include "autocommit" and "stream_results",
906 but not "isolation_level" or "compiled_cache".
907 See :meth:`_engine.Connection.execution_options` for a full list of
908 possible options.
910 .. seealso::
912 :meth:`_engine.Connection.execution_options`
914 :meth:`_query.Query.execution_options`
916 :meth:`.Executable.get_execution_options`
918 """
919 if "isolation_level" in kw:
920 raise exc.ArgumentError(
921 "'isolation_level' execution option may only be specified "
922 "on Connection.execution_options(), or "
923 "per-engine using the isolation_level "
924 "argument to create_engine()."
925 )
926 if "compiled_cache" in kw:
927 raise exc.ArgumentError(
928 "'compiled_cache' execution option may only be specified "
929 "on Connection.execution_options(), not per statement."
930 )
931 self._execution_options = self._execution_options.union(kw)
933 def get_execution_options(self):
934 """Get the non-SQL options which will take effect during execution.
936 .. versionadded:: 1.3
938 .. seealso::
940 :meth:`.Executable.execution_options`
941 """
942 return self._execution_options
944 @util.deprecated_20(
945 ":meth:`.Executable.execute`",
946 alternative="All statement execution in SQLAlchemy 2.0 is performed "
947 "by the :meth:`_engine.Connection.execute` method of "
948 ":class:`_engine.Connection`, "
949 "or in the ORM by the :meth:`.Session.execute` method of "
950 ":class:`.Session`.",
951 )
952 def execute(self, *multiparams, **params):
953 """Compile and execute this :class:`.Executable`."""
954 e = self.bind
955 if e is None:
956 label = (
957 getattr(self, "description", None) or self.__class__.__name__
958 )
959 msg = (
960 "This %s is not directly bound to a Connection or Engine. "
961 "Use the .execute() method of a Connection or Engine "
962 "to execute this construct." % label
963 )
964 raise exc.UnboundExecutionError(msg)
965 return e._execute_clauseelement(
966 self, multiparams, params, util.immutabledict()
967 )
969 @util.deprecated_20(
970 ":meth:`.Executable.scalar`",
971 alternative="Scalar execution in SQLAlchemy 2.0 is performed "
972 "by the :meth:`_engine.Connection.scalar` method of "
973 ":class:`_engine.Connection`, "
974 "or in the ORM by the :meth:`.Session.scalar` method of "
975 ":class:`.Session`.",
976 )
977 def scalar(self, *multiparams, **params):
978 """Compile and execute this :class:`.Executable`, returning the
979 result's scalar representation.
981 """
982 return self.execute(*multiparams, **params).scalar()
984 @property
985 @util.deprecated_20(
986 ":attr:`.Executable.bind`",
987 alternative="Bound metadata is being removed as of SQLAlchemy 2.0.",
988 enable_warnings=False,
989 )
990 def bind(self):
991 """Returns the :class:`_engine.Engine` or :class:`_engine.Connection`
992 to
993 which this :class:`.Executable` is bound, or None if none found.
995 This is a traversal which checks locally, then
996 checks among the "from" clauses of associated objects
997 until a bound engine or connection is found.
999 """
1000 if self._bind is not None:
1001 return self._bind
1003 for f in _from_objects(self):
1004 if f is self:
1005 continue
1006 engine = f.bind
1007 if engine is not None:
1008 return engine
1009 else:
1010 return None
1013class prefix_anon_map(dict):
1014 """A map that creates new keys for missing key access.
1016 Considers keys of the form "<ident> <name>" to produce
1017 new symbols "<name>_<index>", where "index" is an incrementing integer
1018 corresponding to <name>.
1020 Inlines the approach taken by :class:`sqlalchemy.util.PopulateDict` which
1021 is otherwise usually used for this type of operation.
1023 """
1025 def __missing__(self, key):
1026 (ident, derived) = key.split(" ", 1)
1027 anonymous_counter = self.get(derived, 1)
1028 self[derived] = anonymous_counter + 1
1029 value = derived + "_" + str(anonymous_counter)
1030 self[key] = value
1031 return value
1034class SchemaEventTarget(object):
1035 """Base class for elements that are the targets of :class:`.DDLEvents`
1036 events.
1038 This includes :class:`.SchemaItem` as well as :class:`.SchemaType`.
1040 """
1042 def _set_parent(self, parent, **kw):
1043 """Associate with this SchemaEvent's parent object."""
1045 def _set_parent_with_dispatch(self, parent, **kw):
1046 self.dispatch.before_parent_attach(self, parent)
1047 self._set_parent(parent, **kw)
1048 self.dispatch.after_parent_attach(self, parent)
1051class SchemaVisitor(ClauseVisitor):
1052 """Define the visiting for ``SchemaItem`` objects."""
1054 __traverse_options__ = {"schema_visitor": True}
1057class ColumnCollection(object):
1058 """Collection of :class:`_expression.ColumnElement` instances,
1059 typically for
1060 :class:`_sql.FromClause` objects.
1062 The :class:`_sql.ColumnCollection` object is most commonly available
1063 as the :attr:`_schema.Table.c` or :attr:`_schema.Table.columns` collection
1064 on the :class:`_schema.Table` object, introduced at
1065 :ref:`metadata_tables_and_columns`.
1067 The :class:`_expression.ColumnCollection` has both mapping- and sequence-
1068 like behaviors. A :class:`_expression.ColumnCollection` usually stores
1069 :class:`_schema.Column` objects, which are then accessible both via mapping
1070 style access as well as attribute access style.
1072 To access :class:`_schema.Column` objects using ordinary attribute-style
1073 access, specify the name like any other object attribute, such as below
1074 a column named ``employee_name`` is accessed::
1076 >>> employee_table.c.employee_name
1078 To access columns that have names with special characters or spaces,
1079 index-style access is used, such as below which illustrates a column named
1080 ``employee ' payment`` is accessed::
1082 >>> employee_table.c["employee ' payment"]
1084 As the :class:`_sql.ColumnCollection` object provides a Python dictionary
1085 interface, common dictionary method names like
1086 :meth:`_sql.ColumnCollection.keys`, :meth:`_sql.ColumnCollection.values`,
1087 and :meth:`_sql.ColumnCollection.items` are available, which means that
1088 database columns that are keyed under these names also need to use indexed
1089 access::
1091 >>> employee_table.c["values"]
1094 The name for which a :class:`_schema.Column` would be present is normally
1095 that of the :paramref:`_schema.Column.key` parameter. In some contexts,
1096 such as a :class:`_sql.Select` object that uses a label style set
1097 using the :meth:`_sql.Select.set_label_style` method, a column of a certain
1098 key may instead be represented under a particular label name such
1099 as ``tablename_columnname``::
1101 >>> from sqlalchemy import select, column, table
1102 >>> from sqlalchemy import LABEL_STYLE_TABLENAME_PLUS_COL
1103 >>> t = table("t", column("c"))
1104 >>> stmt = select(t).set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
1105 >>> subq = stmt.subquery()
1106 >>> subq.c.t_c
1107 <sqlalchemy.sql.elements.ColumnClause at 0x7f59dcf04fa0; t_c>
1109 :class:`.ColumnCollection` also indexes the columns in order and allows
1110 them to be accessible by their integer position::
1112 >>> cc[0]
1113 Column('x', Integer(), table=None)
1114 >>> cc[1]
1115 Column('y', Integer(), table=None)
1117 .. versionadded:: 1.4 :class:`_expression.ColumnCollection`
1118 allows integer-based
1119 index access to the collection.
1121 Iterating the collection yields the column expressions in order::
1123 >>> list(cc)
1124 [Column('x', Integer(), table=None),
1125 Column('y', Integer(), table=None)]
1127 The base :class:`_expression.ColumnCollection` object can store
1128 duplicates, which can
1129 mean either two columns with the same key, in which case the column
1130 returned by key access is **arbitrary**::
1132 >>> x1, x2 = Column('x', Integer), Column('x', Integer)
1133 >>> cc = ColumnCollection(columns=[(x1.name, x1), (x2.name, x2)])
1134 >>> list(cc)
1135 [Column('x', Integer(), table=None),
1136 Column('x', Integer(), table=None)]
1137 >>> cc['x'] is x1
1138 False
1139 >>> cc['x'] is x2
1140 True
1142 Or it can also mean the same column multiple times. These cases are
1143 supported as :class:`_expression.ColumnCollection`
1144 is used to represent the columns in
1145 a SELECT statement which may include duplicates.
1147 A special subclass :class:`.DedupeColumnCollection` exists which instead
1148 maintains SQLAlchemy's older behavior of not allowing duplicates; this
1149 collection is used for schema level objects like :class:`_schema.Table`
1150 and
1151 :class:`.PrimaryKeyConstraint` where this deduping is helpful. The
1152 :class:`.DedupeColumnCollection` class also has additional mutation methods
1153 as the schema constructs have more use cases that require removal and
1154 replacement of columns.
1156 .. versionchanged:: 1.4 :class:`_expression.ColumnCollection`
1157 now stores duplicate
1158 column keys as well as the same column in multiple positions. The
1159 :class:`.DedupeColumnCollection` class is added to maintain the
1160 former behavior in those cases where deduplication as well as
1161 additional replace/remove operations are needed.
1164 """
1166 __slots__ = "_collection", "_index", "_colset"
1168 def __init__(self, columns=None):
1169 object.__setattr__(self, "_colset", set())
1170 object.__setattr__(self, "_index", {})
1171 object.__setattr__(self, "_collection", [])
1172 if columns:
1173 self._initial_populate(columns)
1175 def _initial_populate(self, iter_):
1176 self._populate_separate_keys(iter_)
1178 @property
1179 def _all_columns(self):
1180 return [col for (k, col) in self._collection]
1182 def keys(self):
1183 """Return a sequence of string key names for all columns in this
1184 collection."""
1185 return [k for (k, col) in self._collection]
1187 def values(self):
1188 """Return a sequence of :class:`_sql.ColumnClause` or
1189 :class:`_schema.Column` objects for all columns in this
1190 collection."""
1191 return [col for (k, col) in self._collection]
1193 def items(self):
1194 """Return a sequence of (key, column) tuples for all columns in this
1195 collection each consisting of a string key name and a
1196 :class:`_sql.ColumnClause` or
1197 :class:`_schema.Column` object.
1198 """
1200 return list(self._collection)
1202 def __bool__(self):
1203 return bool(self._collection)
1205 def __len__(self):
1206 return len(self._collection)
1208 def __iter__(self):
1209 # turn to a list first to maintain over a course of changes
1210 return iter([col for k, col in self._collection])
1212 def __getitem__(self, key):
1213 try:
1214 return self._index[key]
1215 except KeyError as err:
1216 if isinstance(key, util.int_types):
1217 util.raise_(IndexError(key), replace_context=err)
1218 else:
1219 raise
1221 def __getattr__(self, key):
1222 try:
1223 return self._index[key]
1224 except KeyError as err:
1225 util.raise_(AttributeError(key), replace_context=err)
1227 def __contains__(self, key):
1228 if key not in self._index:
1229 if not isinstance(key, util.string_types):
1230 raise exc.ArgumentError(
1231 "__contains__ requires a string argument"
1232 )
1233 return False
1234 else:
1235 return True
1237 def compare(self, other):
1238 """Compare this :class:`_expression.ColumnCollection` to another
1239 based on the names of the keys"""
1241 for l, r in util.zip_longest(self, other):
1242 if l is not r:
1243 return False
1244 else:
1245 return True
1247 def __eq__(self, other):
1248 return self.compare(other)
1250 def get(self, key, default=None):
1251 """Get a :class:`_sql.ColumnClause` or :class:`_schema.Column` object
1252 based on a string key name from this
1253 :class:`_expression.ColumnCollection`."""
1255 if key in self._index:
1256 return self._index[key]
1257 else:
1258 return default
1260 def __str__(self):
1261 return "%s(%s)" % (
1262 self.__class__.__name__,
1263 ", ".join(str(c) for c in self),
1264 )
1266 def __setitem__(self, key, value):
1267 raise NotImplementedError()
1269 def __delitem__(self, key):
1270 raise NotImplementedError()
1272 def __setattr__(self, key, obj):
1273 raise NotImplementedError()
1275 def clear(self):
1276 """Dictionary clear() is not implemented for
1277 :class:`_sql.ColumnCollection`."""
1278 raise NotImplementedError()
1280 def remove(self, column):
1281 """Dictionary remove() is not implemented for
1282 :class:`_sql.ColumnCollection`."""
1283 raise NotImplementedError()
1285 def update(self, iter_):
1286 """Dictionary update() is not implemented for
1287 :class:`_sql.ColumnCollection`."""
1288 raise NotImplementedError()
1290 __hash__ = None
1292 def _populate_separate_keys(self, iter_):
1293 """populate from an iterator of (key, column)"""
1294 cols = list(iter_)
1295 self._collection[:] = cols
1296 self._colset.update(c for k, c in self._collection)
1297 self._index.update(
1298 (idx, c) for idx, (k, c) in enumerate(self._collection)
1299 )
1300 self._index.update({k: col for k, col in reversed(self._collection)})
1302 def add(self, column, key=None):
1303 """Add a column to this :class:`_sql.ColumnCollection`.
1305 .. note::
1307 This method is **not normally used by user-facing code**, as the
1308 :class:`_sql.ColumnCollection` is usually part of an existing
1309 object such as a :class:`_schema.Table`. To add a
1310 :class:`_schema.Column` to an existing :class:`_schema.Table`
1311 object, use the :meth:`_schema.Table.append_column` method.
1313 """
1314 if key is None:
1315 key = column.key
1317 l = len(self._collection)
1318 self._collection.append((key, column))
1319 self._colset.add(column)
1320 self._index[l] = column
1321 if key not in self._index:
1322 self._index[key] = column
1324 def __getstate__(self):
1325 return {"_collection": self._collection, "_index": self._index}
1327 def __setstate__(self, state):
1328 object.__setattr__(self, "_index", state["_index"])
1329 object.__setattr__(self, "_collection", state["_collection"])
1330 object.__setattr__(
1331 self, "_colset", {col for k, col in self._collection}
1332 )
1334 def contains_column(self, col):
1335 """Checks if a column object exists in this collection"""
1336 if col not in self._colset:
1337 if isinstance(col, util.string_types):
1338 raise exc.ArgumentError(
1339 "contains_column cannot be used with string arguments. "
1340 "Use ``col_name in table.c`` instead."
1341 )
1342 return False
1343 else:
1344 return True
1346 def as_immutable(self):
1347 """Return an "immutable" form of this
1348 :class:`_sql.ColumnCollection`."""
1350 return ImmutableColumnCollection(self)
1352 def corresponding_column(self, column, require_embedded=False):
1353 """Given a :class:`_expression.ColumnElement`, return the exported
1354 :class:`_expression.ColumnElement` object from this
1355 :class:`_expression.ColumnCollection`
1356 which corresponds to that original :class:`_expression.ColumnElement`
1357 via a common
1358 ancestor column.
1360 :param column: the target :class:`_expression.ColumnElement`
1361 to be matched.
1363 :param require_embedded: only return corresponding columns for
1364 the given :class:`_expression.ColumnElement`, if the given
1365 :class:`_expression.ColumnElement`
1366 is actually present within a sub-element
1367 of this :class:`_expression.Selectable`.
1368 Normally the column will match if
1369 it merely shares a common ancestor with one of the exported
1370 columns of this :class:`_expression.Selectable`.
1372 .. seealso::
1374 :meth:`_expression.Selectable.corresponding_column`
1375 - invokes this method
1376 against the collection returned by
1377 :attr:`_expression.Selectable.exported_columns`.
1379 .. versionchanged:: 1.4 the implementation for ``corresponding_column``
1380 was moved onto the :class:`_expression.ColumnCollection` itself.
1382 """
1384 def embedded(expanded_proxy_set, target_set):
1385 for t in target_set.difference(expanded_proxy_set):
1386 if not set(_expand_cloned([t])).intersection(
1387 expanded_proxy_set
1388 ):
1389 return False
1390 return True
1392 # don't dig around if the column is locally present
1393 if column in self._colset:
1394 return column
1395 col, intersect = None, None
1396 target_set = column.proxy_set
1397 cols = [c for (k, c) in self._collection]
1398 for c in cols:
1399 expanded_proxy_set = set(_expand_cloned(c.proxy_set))
1400 i = target_set.intersection(expanded_proxy_set)
1401 if i and (
1402 not require_embedded
1403 or embedded(expanded_proxy_set, target_set)
1404 ):
1405 if col is None:
1407 # no corresponding column yet, pick this one.
1409 col, intersect = c, i
1410 elif len(i) > len(intersect):
1412 # 'c' has a larger field of correspondence than
1413 # 'col'. i.e. selectable.c.a1_x->a1.c.x->table.c.x
1414 # matches a1.c.x->table.c.x better than
1415 # selectable.c.x->table.c.x does.
1417 col, intersect = c, i
1418 elif i == intersect:
1419 # they have the same field of correspondence. see
1420 # which proxy_set has fewer columns in it, which
1421 # indicates a closer relationship with the root
1422 # column. Also take into account the "weight"
1423 # attribute which CompoundSelect() uses to give
1424 # higher precedence to columns based on vertical
1425 # position in the compound statement, and discard
1426 # columns that have no reference to the target
1427 # column (also occurs with CompoundSelect)
1429 col_distance = util.reduce(
1430 operator.add,
1431 [
1432 sc._annotations.get("weight", 1)
1433 for sc in col._uncached_proxy_list()
1434 if sc.shares_lineage(column)
1435 ],
1436 )
1437 c_distance = util.reduce(
1438 operator.add,
1439 [
1440 sc._annotations.get("weight", 1)
1441 for sc in c._uncached_proxy_list()
1442 if sc.shares_lineage(column)
1443 ],
1444 )
1445 if c_distance < col_distance:
1446 col, intersect = c, i
1447 return col
1450class DedupeColumnCollection(ColumnCollection):
1451 """A :class:`_expression.ColumnCollection`
1452 that maintains deduplicating behavior.
1454 This is useful by schema level objects such as :class:`_schema.Table` and
1455 :class:`.PrimaryKeyConstraint`. The collection includes more
1456 sophisticated mutator methods as well to suit schema objects which
1457 require mutable column collections.
1459 .. versionadded:: 1.4
1461 """
1463 def add(self, column, key=None):
1465 if key is not None and column.key != key:
1466 raise exc.ArgumentError(
1467 "DedupeColumnCollection requires columns be under "
1468 "the same key as their .key"
1469 )
1470 key = column.key
1472 if key is None:
1473 raise exc.ArgumentError(
1474 "Can't add unnamed column to column collection"
1475 )
1477 if key in self._index:
1479 existing = self._index[key]
1481 if existing is column:
1482 return
1484 self.replace(column)
1486 # pop out memoized proxy_set as this
1487 # operation may very well be occurring
1488 # in a _make_proxy operation
1489 util.memoized_property.reset(column, "proxy_set")
1490 else:
1491 l = len(self._collection)
1492 self._collection.append((key, column))
1493 self._colset.add(column)
1494 self._index[l] = column
1495 self._index[key] = column
1497 def _populate_separate_keys(self, iter_):
1498 """populate from an iterator of (key, column)"""
1499 cols = list(iter_)
1501 replace_col = []
1502 for k, col in cols:
1503 if col.key != k:
1504 raise exc.ArgumentError(
1505 "DedupeColumnCollection requires columns be under "
1506 "the same key as their .key"
1507 )
1508 if col.name in self._index and col.key != col.name:
1509 replace_col.append(col)
1510 elif col.key in self._index:
1511 replace_col.append(col)
1512 else:
1513 self._index[k] = col
1514 self._collection.append((k, col))
1515 self._colset.update(c for (k, c) in self._collection)
1516 self._index.update(
1517 (idx, c) for idx, (k, c) in enumerate(self._collection)
1518 )
1519 for col in replace_col:
1520 self.replace(col)
1522 def extend(self, iter_):
1523 self._populate_separate_keys((col.key, col) for col in iter_)
1525 def remove(self, column):
1526 if column not in self._colset:
1527 raise ValueError(
1528 "Can't remove column %r; column is not in this collection"
1529 % column
1530 )
1531 del self._index[column.key]
1532 self._colset.remove(column)
1533 self._collection[:] = [
1534 (k, c) for (k, c) in self._collection if c is not column
1535 ]
1536 self._index.update(
1537 {idx: col for idx, (k, col) in enumerate(self._collection)}
1538 )
1539 # delete higher index
1540 del self._index[len(self._collection)]
1542 def replace(self, column):
1543 """add the given column to this collection, removing unaliased
1544 versions of this column as well as existing columns with the
1545 same key.
1547 e.g.::
1549 t = Table('sometable', metadata, Column('col1', Integer))
1550 t.columns.replace(Column('col1', Integer, key='columnone'))
1552 will remove the original 'col1' from the collection, and add
1553 the new column under the name 'columnname'.
1555 Used by schema.Column to override columns during table reflection.
1557 """
1559 remove_col = set()
1560 # remove up to two columns based on matches of name as well as key
1561 if column.name in self._index and column.key != column.name:
1562 other = self._index[column.name]
1563 if other.name == other.key:
1564 remove_col.add(other)
1566 if column.key in self._index:
1567 remove_col.add(self._index[column.key])
1569 new_cols = []
1570 replaced = False
1571 for k, col in self._collection:
1572 if col in remove_col:
1573 if not replaced:
1574 replaced = True
1575 new_cols.append((column.key, column))
1576 else:
1577 new_cols.append((k, col))
1579 if remove_col:
1580 self._colset.difference_update(remove_col)
1582 if not replaced:
1583 new_cols.append((column.key, column))
1585 self._colset.add(column)
1586 self._collection[:] = new_cols
1588 self._index.clear()
1589 self._index.update(
1590 {idx: col for idx, (k, col) in enumerate(self._collection)}
1591 )
1592 self._index.update(self._collection)
1595class ImmutableColumnCollection(util.ImmutableContainer, ColumnCollection):
1596 __slots__ = ("_parent",)
1598 def __init__(self, collection):
1599 object.__setattr__(self, "_parent", collection)
1600 object.__setattr__(self, "_colset", collection._colset)
1601 object.__setattr__(self, "_index", collection._index)
1602 object.__setattr__(self, "_collection", collection._collection)
1604 def __getstate__(self):
1605 return {"_parent": self._parent}
1607 def __setstate__(self, state):
1608 parent = state["_parent"]
1609 self.__init__(parent)
1611 add = extend = remove = util.ImmutableContainer._immutable
1614class ColumnSet(util.ordered_column_set):
1615 def contains_column(self, col):
1616 return col in self
1618 def extend(self, cols):
1619 for col in cols:
1620 self.add(col)
1622 def __add__(self, other):
1623 return list(self) + list(other)
1625 def __eq__(self, other):
1626 l = []
1627 for c in other:
1628 for local in self:
1629 if c.shares_lineage(local):
1630 l.append(c == local)
1631 return elements.and_(*l)
1633 def __hash__(self):
1634 return hash(tuple(x for x in self))
1637def _bind_or_error(schemaitem, msg=None):
1639 util.warn_deprecated_20(
1640 "The ``bind`` argument for schema methods that invoke SQL "
1641 "against an engine or connection will be required in SQLAlchemy 2.0."
1642 )
1643 bind = schemaitem.bind
1644 if not bind:
1645 name = schemaitem.__class__.__name__
1646 label = getattr(
1647 schemaitem, "fullname", getattr(schemaitem, "name", None)
1648 )
1649 if label:
1650 item = "%s object %r" % (name, label)
1651 else:
1652 item = "%s object" % name
1653 if msg is None:
1654 msg = (
1655 "%s is not bound to an Engine or Connection. "
1656 "Execution can not proceed without a database to execute "
1657 "against." % item
1658 )
1659 raise exc.UnboundExecutionError(msg)
1660 return bind
1663def _entity_namespace(entity):
1664 """Return the nearest .entity_namespace for the given entity.
1666 If not immediately available, does an iterate to find a sub-element
1667 that has one, if any.
1669 """
1670 try:
1671 return entity.entity_namespace
1672 except AttributeError:
1673 for elem in visitors.iterate(entity):
1674 if hasattr(elem, "entity_namespace"):
1675 return elem.entity_namespace
1676 else:
1677 raise
1680def _entity_namespace_key(entity, key, default=NO_ARG):
1681 """Return an entry from an entity_namespace.
1684 Raises :class:`_exc.InvalidRequestError` rather than attribute error
1685 on not found.
1687 """
1689 try:
1690 ns = _entity_namespace(entity)
1691 if default is not NO_ARG:
1692 return getattr(ns, key, default)
1693 else:
1694 return getattr(ns, key)
1695 except AttributeError as err:
1696 util.raise_(
1697 exc.InvalidRequestError(
1698 'Entity namespace for "%s" has no property "%s"'
1699 % (entity, key)
1700 ),
1701 replace_context=err,
1702 )