Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
interfaces.py1470 linesDownload Raw Back to orm
1# orm/interfaces.py
2# Copyright (C) 2005-2024 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
7
8"""
9
10Contains various base classes used throughout the ORM.
11
12Defines some key base classes prominent within the internals.
13
14This module and the classes within are mostly private, though some attributes
15are exposed when inspecting mappings.
16
17"""
18
19from __future__ import annotations
20
21import collections
22import dataclasses
23import typing
24from typing import Any
25from typing import Callable
26from typing import cast
27from typing import ClassVar
28from typing import Dict
29from typing import Generic
30from typing import Iterator
31from typing import List
32from typing import NamedTuple
33from typing import NoReturn
34from typing import Optional
35from typing import Sequence
36from typing import Set
37from typing import Tuple
38from typing import Type
39from typing import TYPE_CHECKING
40from typing import TypeVar
41from typing import Union
42
43from . import exc as orm_exc
44from . import path_registry
45from .base import _MappedAttribute as _MappedAttribute
46from .base import EXT_CONTINUE as EXT_CONTINUE  # noqa: F401
47from .base import EXT_SKIP as EXT_SKIP  # noqa: F401
48from .base import EXT_STOP as EXT_STOP  # noqa: F401
49from .base import InspectionAttr as InspectionAttr  # noqa: F401
50from .base import InspectionAttrInfo as InspectionAttrInfo
51from .base import MANYTOMANY as MANYTOMANY  # noqa: F401
52from .base import MANYTOONE as MANYTOONE  # noqa: F401
53from .base import NO_KEY as NO_KEY  # noqa: F401
54from .base import NO_VALUE as NO_VALUE  # noqa: F401
55from .base import NotExtension as NotExtension  # noqa: F401
56from .base import ONETOMANY as ONETOMANY  # noqa: F401
57from .base import RelationshipDirection as RelationshipDirection  # noqa: F401
58from .base import SQLORMOperations
59from .. import ColumnElement
60from .. import exc as sa_exc
61from .. import inspection
62from .. import util
63from ..sql import operators
64from ..sql import roles
65from ..sql import visitors
66from ..sql.base import _NoArg
67from ..sql.base import ExecutableOption
68from ..sql.cache_key import HasCacheKey
69from ..sql.operators import ColumnOperators
70from ..sql.schema import Column
71from ..sql.type_api import TypeEngine
72from ..util import warn_deprecated
73from ..util.typing import RODescriptorReference
74from ..util.typing import TypedDict
75
76if typing.TYPE_CHECKING:
77    from ._typing import _EntityType
78    from ._typing import _IdentityKeyType
79    from ._typing import _InstanceDict
80    from ._typing import _InternalEntityType
81    from ._typing import _ORMAdapterProto
82    from .attributes import InstrumentedAttribute
83    from .base import Mapped
84    from .context import _MapperEntity
85    from .context import ORMCompileState
86    from .context import QueryContext
87    from .decl_api import RegistryType
88    from .decl_base import _ClassScanMapperConfig
89    from .loading import _PopulatorDict
90    from .mapper import Mapper
91    from .path_registry import AbstractEntityRegistry
92    from .query import Query
93    from .session import Session
94    from .state import InstanceState
95    from .strategy_options import _LoadElement
96    from .util import AliasedInsp
97    from .util import ORMAdapter
98    from ..engine.result import Result
99    from ..sql._typing import _ColumnExpressionArgument
100    from ..sql._typing import _ColumnsClauseArgument
101    from ..sql._typing import _DMLColumnArgument
102    from ..sql._typing import _InfoType
103    from ..sql.operators import OperatorType
104    from ..sql.visitors import _TraverseInternalsType
105    from ..util.typing import _AnnotationScanType
106
107_StrategyKey = Tuple[Any, ...]
108
109_T = TypeVar("_T", bound=Any)
110_T_co = TypeVar("_T_co", bound=Any, covariant=True)
111
112_TLS = TypeVar("_TLS", bound="Type[LoaderStrategy]")
113
114
115class ORMStatementRole(roles.StatementRole):
116    __slots__ = ()
117    _role_name = (
118        "Executable SQL or text() construct, including ORM aware objects"
119    )
120
121
122class ORMColumnsClauseRole(
123    roles.ColumnsClauseRole, roles.TypedColumnsClauseRole[_T]
124):
125    __slots__ = ()
126    _role_name = "ORM mapped entity, aliased entity, or Column expression"
127
128
129class ORMEntityColumnsClauseRole(ORMColumnsClauseRole[_T]):
130    __slots__ = ()
131    _role_name = "ORM mapped or aliased entity"
132
133
134class ORMFromClauseRole(roles.StrictFromClauseRole):
135    __slots__ = ()
136    _role_name = "ORM mapped entity, aliased entity, or FROM expression"
137
138
139class ORMColumnDescription(TypedDict):
140    name: str
141    # TODO: add python_type and sql_type here; combining them
142    # into "type" is a bad idea
143    type: Union[Type[Any], TypeEngine[Any]]
144    aliased: bool
145    expr: _ColumnsClauseArgument[Any]
146    entity: Optional[_ColumnsClauseArgument[Any]]
147
148
149class _IntrospectsAnnotations:
150    __slots__ = ()
151
152    @classmethod
153    def _mapper_property_name(cls) -> str:
154        return cls.__name__
155
156    def found_in_pep593_annotated(self) -> Any:
157        """return a copy of this object to use in declarative when the
158        object is found inside of an Annotated object."""
159
160        raise NotImplementedError(
161            f"Use of the {self._mapper_property_name()!r} "
162            "construct inside of an Annotated object is not yet supported."
163        )
164
165    def declarative_scan(
166        self,
167        decl_scan: _ClassScanMapperConfig,
168        registry: RegistryType,
169        cls: Type[Any],
170        originating_module: Optional[str],
171        key: str,
172        mapped_container: Optional[Type[Mapped[Any]]],
173        annotation: Optional[_AnnotationScanType],
174        extracted_mapped_annotation: Optional[_AnnotationScanType],
175        is_dataclass_field: bool,
176    ) -> None:
177        """Perform class-specific initializaton at early declarative scanning
178        time.
179
180        .. versionadded:: 2.0
181
182        """
183
184    def _raise_for_required(self, key: str, cls: Type[Any]) -> NoReturn:
185        raise sa_exc.ArgumentError(
186            f"Python typing annotation is required for attribute "
187            f'"{cls.__name__}.{key}" when primary argument(s) for '
188            f'"{self._mapper_property_name()}" '
189            "construct are None or not present"
190        )
191
192
193class _AttributeOptions(NamedTuple):
194    """define Python-local attribute behavior options common to all
195    :class:`.MapperProperty` objects.
196
197    Currently this includes dataclass-generation arguments.
198
199    .. versionadded:: 2.0
200
201    """
202
203    dataclasses_init: Union[_NoArg, bool]
204    dataclasses_repr: Union[_NoArg, bool]
205    dataclasses_default: Union[_NoArg, Any]
206    dataclasses_default_factory: Union[_NoArg, Callable[[], Any]]
207    dataclasses_compare: Union[_NoArg, bool]
208    dataclasses_kw_only: Union[_NoArg, bool]
209
210    def _as_dataclass_field(self, key: str) -> Any:
211        """Return a ``dataclasses.Field`` object given these arguments."""
212
213        kw: Dict[str, Any] = {}
214        if self.dataclasses_default_factory is not _NoArg.NO_ARG:
215            kw["default_factory"] = self.dataclasses_default_factory
216        if self.dataclasses_default is not _NoArg.NO_ARG:
217            kw["default"] = self.dataclasses_default
218        if self.dataclasses_init is not _NoArg.NO_ARG:
219            kw["init"] = self.dataclasses_init
220        if self.dataclasses_repr is not _NoArg.NO_ARG:
221            kw["repr"] = self.dataclasses_repr
222        if self.dataclasses_compare is not _NoArg.NO_ARG:
223            kw["compare"] = self.dataclasses_compare
224        if self.dataclasses_kw_only is not _NoArg.NO_ARG:
225            kw["kw_only"] = self.dataclasses_kw_only
226
227        if "default" in kw and callable(kw["default"]):
228            # callable defaults are ambiguous. deprecate them in favour of
229            # insert_default or default_factory. #9936
230            warn_deprecated(
231                f"Callable object passed to the ``default`` parameter for "
232                f"attribute {key!r} in a ORM-mapped Dataclasses context is "
233                "ambiguous, "
234                "and this use will raise an error in a future release.  "
235                "If this callable is intended to produce Core level INSERT "
236                "default values for an underlying ``Column``, use "
237                "the ``mapped_column.insert_default`` parameter instead.  "
238                "To establish this callable as providing a default value "
239                "for instances of the dataclass itself, use the "
240                "``default_factory`` dataclasses parameter.",
241                "2.0",
242            )
243
244        if (
245            "init" in kw
246            and not kw["init"]
247            and "default" in kw
248            and not callable(kw["default"])  # ignore callable defaults. #9936
249            and "default_factory" not in kw  # illegal but let dc.field raise
250        ):
251            # fix for #9879
252            default = kw.pop("default")
253            kw["default_factory"] = lambda: default
254
255        return dataclasses.field(**kw)
256
257    @classmethod
258    def _get_arguments_for_make_dataclass(
259        cls,
260        key: str,
261        annotation: _AnnotationScanType,
262        mapped_container: Optional[Any],
263        elem: _T,
264    ) -> Union[
265        Tuple[str, _AnnotationScanType],
266        Tuple[str, _AnnotationScanType, dataclasses.Field[Any]],
267    ]:
268        """given attribute key, annotation, and value from a class, return
269        the argument tuple we would pass to dataclasses.make_dataclass()
270        for this attribute.
271
272        """
273        if isinstance(elem, _DCAttributeOptions):
274            dc_field = elem._attribute_options._as_dataclass_field(key)
275
276            return (key, annotation, dc_field)
277        elif elem is not _NoArg.NO_ARG:
278            # why is typing not erroring on this?
279            return (key, annotation, elem)
280        elif mapped_container is not None:
281            # it's Mapped[], but there's no "element", which means declarative
282            # did not actually do anything for this field.  this shouldn't
283            # happen.
284            # previously, this would occur because _scan_attributes would
285            # skip a field that's on an already mapped superclass, but it
286            # would still include it in the annotations, leading
287            # to issue #8718
288
289            assert False, "Mapped[] received without a mapping declaration"
290
291        else:
292            # plain dataclass field, not mapped.  Is only possible
293            # if __allow_unmapped__ is set up.  I can see this mode causing
294            # problems...
295            return (key, annotation)
296
297
298_DEFAULT_ATTRIBUTE_OPTIONS = _AttributeOptions(
299    _NoArg.NO_ARG,
300    _NoArg.NO_ARG,
301    _NoArg.NO_ARG,
302    _NoArg.NO_ARG,
303    _NoArg.NO_ARG,
304    _NoArg.NO_ARG,
305)
306
307_DEFAULT_READONLY_ATTRIBUTE_OPTIONS = _AttributeOptions(
308    False,
309    _NoArg.NO_ARG,
310    _NoArg.NO_ARG,
311    _NoArg.NO_ARG,
312    _NoArg.NO_ARG,
313    _NoArg.NO_ARG,
314)
315
316
317class _DCAttributeOptions:
318    """mixin for descriptors or configurational objects that include dataclass
319    field options.
320
321    This includes :class:`.MapperProperty`, :class:`._MapsColumn` within
322    the ORM, but also includes :class:`.AssociationProxy` within ext.
323    Can in theory be used for other descriptors that serve a similar role
324    as association proxy.   (*maybe* hybrids, not sure yet.)
325
326    """
327
328    __slots__ = ()
329
330    _attribute_options: _AttributeOptions
331    """behavioral options for ORM-enabled Python attributes
332
333    .. versionadded:: 2.0
334
335    """
336
337    _has_dataclass_arguments: bool
338
339
340class _MapsColumns(_DCAttributeOptions, _MappedAttribute[_T]):
341    """interface for declarative-capable construct that delivers one or more
342    Column objects to the declarative process to be part of a Table.
343    """
344
345    __slots__ = ()
346
347    @property
348    def mapper_property_to_assign(self) -> Optional[MapperProperty[_T]]:
349        """return a MapperProperty to be assigned to the declarative mapping"""
350        raise NotImplementedError()
351
352    @property
353    def columns_to_assign(self) -> List[Tuple[Column[_T], int]]:
354        """A list of Column objects that should be declaratively added to the
355        new Table object.
356
357        """
358        raise NotImplementedError()
359
360
361# NOTE: MapperProperty needs to extend _MappedAttribute so that declarative
362# typing works, i.e. "Mapped[A] = relationship()".   This introduces an
363# inconvenience which is that all the MapperProperty objects are treated
364# as descriptors by typing tools, which are misled by this as assignment /
365# access to a descriptor attribute wants to move through __get__.
366# Therefore, references to MapperProperty as an instance variable, such
367# as in PropComparator, may have some special typing workarounds such as the
368# use of sqlalchemy.util.typing.DescriptorReference to avoid mis-interpretation
369# by typing tools
370@inspection._self_inspects
371class MapperProperty(
372    HasCacheKey,
373    _DCAttributeOptions,
374    _MappedAttribute[_T],
375    InspectionAttrInfo,
376    util.MemoizedSlots,
377):
378    """Represent a particular class attribute mapped by :class:`_orm.Mapper`.
379
380    The most common occurrences of :class:`.MapperProperty` are the
381    mapped :class:`_schema.Column`, which is represented in a mapping as
382    an instance of :class:`.ColumnProperty`,
383    and a reference to another class produced by :func:`_orm.relationship`,
384    represented in the mapping as an instance of
385    :class:`.Relationship`.
386
387    """
388
389    __slots__ = (
390        "_configure_started",
391        "_configure_finished",
392        "_attribute_options",
393        "_has_dataclass_arguments",
394        "parent",
395        "key",
396        "info",
397        "doc",
398    )
399
400    _cache_key_traversal: _TraverseInternalsType = [
401        ("parent", visitors.ExtendedInternalTraversal.dp_has_cache_key),
402        ("key", visitors.ExtendedInternalTraversal.dp_string),
403    ]
404
405    if not TYPE_CHECKING:
406        cascade = None
407
408    is_property = True
409    """Part of the InspectionAttr interface; states this object is a
410    mapper property.
411
412    """
413
414    comparator: PropComparator[_T]
415    """The :class:`_orm.PropComparator` instance that implements SQL
416    expression construction on behalf of this mapped attribute."""
417
418    key: str
419    """name of class attribute"""
420
421    parent: Mapper[Any]
422    """the :class:`.Mapper` managing this property."""
423
424    _is_relationship = False
425
426    _links_to_entity: bool
427    """True if this MapperProperty refers to a mapped entity.
428
429    Should only be True for Relationship, False for all others.
430
431    """
432
433    doc: Optional[str]
434    """optional documentation string"""
435
436    info: _InfoType
437    """Info dictionary associated with the object, allowing user-defined
438    data to be associated with this :class:`.InspectionAttr`.
439
440    The dictionary is generated when first accessed.  Alternatively,
441    it can be specified as a constructor argument to the
442    :func:`.column_property`, :func:`_orm.relationship`, or :func:`.composite`
443    functions.
444
445    .. seealso::
446
447        :attr:`.QueryableAttribute.info`
448
449        :attr:`.SchemaItem.info`
450
451    """
452
453    def _memoized_attr_info(self) -> _InfoType:
454        """Info dictionary associated with the object, allowing user-defined
455        data to be associated with this :class:`.InspectionAttr`.
456
457        The dictionary is generated when first accessed.  Alternatively,
458        it can be specified as a constructor argument to the
459        :func:`.column_property`, :func:`_orm.relationship`, or
460        :func:`.composite`
461        functions.
462
463        .. seealso::
464
465            :attr:`.QueryableAttribute.info`
466
467            :attr:`.SchemaItem.info`
468
469        """
470        return {}
471
472    def setup(
473        self,
474        context: ORMCompileState,
475        query_entity: _MapperEntity,
476        path: AbstractEntityRegistry,
477        adapter: Optional[ORMAdapter],
478        **kwargs: Any,
479    ) -> None:
480        """Called by Query for the purposes of constructing a SQL statement.
481
482        Each MapperProperty associated with the target mapper processes the
483        statement referenced by the query context, adding columns and/or
484        criterion as appropriate.
485
486        """
487
488    def create_row_processor(
489        self,
490        context: ORMCompileState,
491        query_entity: _MapperEntity,
492        path: AbstractEntityRegistry,
493        mapper: Mapper[Any],
494        result: Result[Any],
495        adapter: Optional[ORMAdapter],
496        populators: _PopulatorDict,
497    ) -> None:
498        """Produce row processing functions and append to the given
499        set of populators lists.
500
501        """
502
503    def cascade_iterator(
504        self,
505        type_: str,
506        state: InstanceState[Any],
507        dict_: _InstanceDict,
508        visited_states: Set[InstanceState[Any]],
509        halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None,
510    ) -> Iterator[
511        Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict]
512    ]:
513        """Iterate through instances related to the given instance for
514        a particular 'cascade', starting with this MapperProperty.
515
516        Return an iterator3-tuples (instance, mapper, state).
517
518        Note that the 'cascade' collection on this MapperProperty is
519        checked first for the given type before cascade_iterator is called.
520
521        This method typically only applies to Relationship.
522
523        """
524
525        return iter(())
526
527    def set_parent(self, parent: Mapper[Any], init: bool) -> None:
528        """Set the parent mapper that references this MapperProperty.
529
530        This method is overridden by some subclasses to perform extra
531        setup when the mapper is first known.
532
533        """
534        self.parent = parent
535
536    def instrument_class(self, mapper: Mapper[Any]) -> None:
537        """Hook called by the Mapper to the property to initiate
538        instrumentation of the class attribute managed by this
539        MapperProperty.
540
541        The MapperProperty here will typically call out to the
542        attributes module to set up an InstrumentedAttribute.
543
544        This step is the first of two steps to set up an InstrumentedAttribute,
545        and is called early in the mapper setup process.
546
547        The second step is typically the init_class_attribute step,
548        called from StrategizedProperty via the post_instrument_class()
549        hook.  This step assigns additional state to the InstrumentedAttribute
550        (specifically the "impl") which has been determined after the
551        MapperProperty has determined what kind of persistence
552        management it needs to do (e.g. scalar, object, collection, etc).
553
554        """
555
556    def __init__(
557        self,
558        attribute_options: Optional[_AttributeOptions] = None,
559        _assume_readonly_dc_attributes: bool = False,
560    ) -> None:
561        self._configure_started = False
562        self._configure_finished = False
563
564        if _assume_readonly_dc_attributes:
565            default_attrs = _DEFAULT_READONLY_ATTRIBUTE_OPTIONS
566        else:
567            default_attrs = _DEFAULT_ATTRIBUTE_OPTIONS
568
569        if attribute_options and attribute_options != default_attrs:
570            self._has_dataclass_arguments = True
571            self._attribute_options = attribute_options
572        else:
573            self._has_dataclass_arguments = False
574            self._attribute_options = default_attrs
575
576    def init(self) -> None:
577        """Called after all mappers are created to assemble
578        relationships between mappers and perform other post-mapper-creation
579        initialization steps.
580
581
582        """
583        self._configure_started = True
584        self.do_init()
585        self._configure_finished = True
586
587    @property
588    def class_attribute(self) -> InstrumentedAttribute[_T]:
589        """Return the class-bound descriptor corresponding to this
590        :class:`.MapperProperty`.
591
592        This is basically a ``getattr()`` call::
593
594            return getattr(self.parent.class_, self.key)
595
596        I.e. if this :class:`.MapperProperty` were named ``addresses``,
597        and the class to which it is mapped is ``User``, this sequence
598        is possible::
599
600            >>> from sqlalchemy import inspect
601            >>> mapper = inspect(User)
602            >>> addresses_property = mapper.attrs.addresses
603            >>> addresses_property.class_attribute is User.addresses
604            True
605            >>> User.addresses.property is addresses_property
606            True
607
608
609        """
610
611        return getattr(self.parent.class_, self.key)  # type: ignore
612
613    def do_init(self) -> None:
614        """Perform subclass-specific initialization post-mapper-creation
615        steps.
616
617        This is a template method called by the ``MapperProperty``
618        object's init() method.
619
620        """
621
622    def post_instrument_class(self, mapper: Mapper[Any]) -> None:
623        """Perform instrumentation adjustments that need to occur
624        after init() has completed.
625
626        The given Mapper is the Mapper invoking the operation, which
627        may not be the same Mapper as self.parent in an inheritance
628        scenario; however, Mapper will always at least be a sub-mapper of
629        self.parent.
630
631        This method is typically used by StrategizedProperty, which delegates
632        it to LoaderStrategy.init_class_attribute() to perform final setup
633        on the class-bound InstrumentedAttribute.
634
635        """
636
637    def merge(
638        self,
639        session: Session,
640        source_state: InstanceState[Any],
641        source_dict: _InstanceDict,
642        dest_state: InstanceState[Any],
643        dest_dict: _InstanceDict,
644        load: bool,
645        _recursive: Dict[Any, object],
646        _resolve_conflict_map: Dict[_IdentityKeyType[Any], object],
647    ) -> None:
648        """Merge the attribute represented by this ``MapperProperty``
649        from source to destination object.
650
651        """
652
653    def __repr__(self) -> str:
654        return "<%s at 0x%x; %s>" % (
655            self.__class__.__name__,
656            id(self),
657            getattr(self, "key", "no key"),
658        )
659
660
661@inspection._self_inspects
662class PropComparator(SQLORMOperations[_T_co], Generic[_T_co], ColumnOperators):
663    r"""Defines SQL operations for ORM mapped attributes.
664
665    SQLAlchemy allows for operators to
666    be redefined at both the Core and ORM level.  :class:`.PropComparator`
667    is the base class of operator redefinition for ORM-level operations,
668    including those of :class:`.ColumnProperty`,
669    :class:`.Relationship`, and :class:`.Composite`.
670
671    User-defined subclasses of :class:`.PropComparator` may be created. The
672    built-in Python comparison and math operator methods, such as
673    :meth:`.operators.ColumnOperators.__eq__`,
674    :meth:`.operators.ColumnOperators.__lt__`, and
675    :meth:`.operators.ColumnOperators.__add__`, can be overridden to provide
676    new operator behavior. The custom :class:`.PropComparator` is passed to
677    the :class:`.MapperProperty` instance via the ``comparator_factory``
678    argument. In each case,
679    the appropriate subclass of :class:`.PropComparator` should be used::
680
681        # definition of custom PropComparator subclasses
682
683        from sqlalchemy.orm.properties import \
684                                ColumnProperty,\
685                                Composite,\
686                                Relationship
687
688        class MyColumnComparator(ColumnProperty.Comparator):
689            def __eq__(self, other):
690                return self.__clause_element__() == other
691
692        class MyRelationshipComparator(Relationship.Comparator):
693            def any(self, expression):
694                "define the 'any' operation"
695                # ...
696
697        class MyCompositeComparator(Composite.Comparator):
698            def __gt__(self, other):
699                "redefine the 'greater than' operation"
700
701                return sql.and_(*[a>b for a, b in
702                                  zip(self.__clause_element__().clauses,
703                                      other.__composite_values__())])
704
705
706        # application of custom PropComparator subclasses
707
708        from sqlalchemy.orm import column_property, relationship, composite
709        from sqlalchemy import Column, String
710
711        class SomeMappedClass(Base):
712            some_column = column_property(Column("some_column", String),
713                                comparator_factory=MyColumnComparator)
714
715            some_relationship = relationship(SomeOtherClass,
716                                comparator_factory=MyRelationshipComparator)
717
718            some_composite = composite(
719                    Column("a", String), Column("b", String),
720                    comparator_factory=MyCompositeComparator
721                )
722
723    Note that for column-level operator redefinition, it's usually
724    simpler to define the operators at the Core level, using the
725    :attr:`.TypeEngine.comparator_factory` attribute.  See
726    :ref:`types_operators` for more detail.
727
728    .. seealso::
729
730        :class:`.ColumnProperty.Comparator`
731
732        :class:`.Relationship.Comparator`
733
734        :class:`.Composite.Comparator`
735
736        :class:`.ColumnOperators`
737
738        :ref:`types_operators`
739
740        :attr:`.TypeEngine.comparator_factory`
741
742    """
743
744    __slots__ = "prop", "_parententity", "_adapt_to_entity"
745
746    __visit_name__ = "orm_prop_comparator"
747
748    _parententity: _InternalEntityType[Any]
749    _adapt_to_entity: Optional[AliasedInsp[Any]]
750    prop: RODescriptorReference[MapperProperty[_T_co]]
751
752    def __init__(
753        self,
754        prop: MapperProperty[_T],
755        parentmapper: _InternalEntityType[Any],
756        adapt_to_entity: Optional[AliasedInsp[Any]] = None,
757    ):
758        self.prop = prop
759        self._parententity = adapt_to_entity or parentmapper
760        self._adapt_to_entity = adapt_to_entity
761
762    @util.non_memoized_property
763    def property(self) -> MapperProperty[_T_co]:
764        """Return the :class:`.MapperProperty` associated with this
765        :class:`.PropComparator`.
766
767
768        Return values here will commonly be instances of
769        :class:`.ColumnProperty` or :class:`.Relationship`.
770
771
772        """
773        return self.prop
774
775    def __clause_element__(self) -> roles.ColumnsClauseRole:
776        raise NotImplementedError("%r" % self)
777
778    def _bulk_update_tuples(
779        self, value: Any
780    ) -> Sequence[Tuple[_DMLColumnArgument, Any]]:
781        """Receive a SQL expression that represents a value in the SET
782        clause of an UPDATE statement.
783
784        Return a tuple that can be passed to a :class:`_expression.Update`
785        construct.
786
787        """
788
789        return [(cast("_DMLColumnArgument", self.__clause_element__()), value)]
790
791    def adapt_to_entity(
792        self, adapt_to_entity: AliasedInsp[Any]
793    ) -> PropComparator[_T_co]:
794        """Return a copy of this PropComparator which will use the given
795        :class:`.AliasedInsp` to produce corresponding expressions.
796        """
797        return self.__class__(self.prop, self._parententity, adapt_to_entity)
798
799    @util.ro_non_memoized_property
800    def _parentmapper(self) -> Mapper[Any]:
801        """legacy; this is renamed to _parententity to be
802        compatible with QueryableAttribute."""
803        return self._parententity.mapper
804
805    def _criterion_exists(
806        self,
807        criterion: Optional[_ColumnExpressionArgument[bool]] = None,
808        **kwargs: Any,
809    ) -> ColumnElement[Any]:
810        return self.prop.comparator._criterion_exists(criterion, **kwargs)
811
812    @util.ro_non_memoized_property
813    def adapter(self) -> Optional[_ORMAdapterProto]:
814        """Produce a callable that adapts column expressions
815        to suit an aliased version of this comparator.
816
817        """
818        if self._adapt_to_entity is None:
819            return None
820        else:
821            return self._adapt_to_entity._orm_adapt_element
822
823    @util.ro_non_memoized_property
824    def info(self) -> _InfoType:
825        return self.prop.info
826
827    @staticmethod
828    def _any_op(a: Any, b: Any, **kwargs: Any) -> Any:
829        return a.any(b, **kwargs)
830
831    @staticmethod
832    def _has_op(left: Any, other: Any, **kwargs: Any) -> Any:
833        return left.has(other, **kwargs)
834
835    @staticmethod
836    def _of_type_op(a: Any, class_: Any) -> Any:
837        return a.of_type(class_)
838
839    any_op = cast(operators.OperatorType, _any_op)
840    has_op = cast(operators.OperatorType, _has_op)
841    of_type_op = cast(operators.OperatorType, _of_type_op)
842
843    if typing.TYPE_CHECKING:
844
845        def operate(
846            self, op: OperatorType, *other: Any, **kwargs: Any
847        ) -> ColumnElement[Any]: ...
848
849        def reverse_operate(
850            self, op: OperatorType, other: Any, **kwargs: Any
851        ) -> ColumnElement[Any]: ...
852
853    def of_type(self, class_: _EntityType[Any]) -> PropComparator[_T_co]:
854        r"""Redefine this object in terms of a polymorphic subclass,
855        :func:`_orm.with_polymorphic` construct, or :func:`_orm.aliased`
856        construct.
857
858        Returns a new PropComparator from which further criterion can be
859        evaluated.
860
861        e.g.::
862
863            query.join(Company.employees.of_type(Engineer)).\
864               filter(Engineer.name=='foo')
865
866        :param \class_: a class or mapper indicating that criterion will be
867            against this specific subclass.
868
869        .. seealso::
870
871            :ref:`orm_queryguide_joining_relationships_aliased` - in the
872            :ref:`queryguide_toplevel`
873
874            :ref:`inheritance_of_type`
875
876        """
877
878        return self.operate(PropComparator.of_type_op, class_)  # type: ignore
879
880    def and_(
881        self, *criteria: _ColumnExpressionArgument[bool]
882    ) -> PropComparator[bool]:
883        """Add additional criteria to the ON clause that's represented by this
884        relationship attribute.
885
886        E.g.::
887
888
889            stmt = select(User).join(
890                User.addresses.and_(Address.email_address != 'foo')
891            )
892
893            stmt = select(User).options(
894                joinedload(User.addresses.and_(Address.email_address != 'foo'))
895            )
896
897        .. versionadded:: 1.4
898
899        .. seealso::
900
901            :ref:`orm_queryguide_join_on_augmented`
902
903            :ref:`loader_option_criteria`
904
905            :func:`.with_loader_criteria`
906
907        """
908        return self.operate(operators.and_, *criteria)  # type: ignore
909
910    def any(
911        self,
912        criterion: Optional[_ColumnExpressionArgument[bool]] = None,
913        **kwargs: Any,
914    ) -> ColumnElement[bool]:
915        r"""Return a SQL expression representing true if this element
916        references a member which meets the given criterion.
917
918        The usual implementation of ``any()`` is
919        :meth:`.Relationship.Comparator.any`.
920
921        :param criterion: an optional ClauseElement formulated against the
922          member class' table or attributes.
923
924        :param \**kwargs: key/value pairs corresponding to member class
925          attribute names which will be compared via equality to the
926          corresponding values.
927
928        """
929
930        return self.operate(PropComparator.any_op, criterion, **kwargs)
931
932    def has(
933        self,
934        criterion: Optional[_ColumnExpressionArgument[bool]] = None,
935        **kwargs: Any,
936    ) -> ColumnElement[bool]:
937        r"""Return a SQL expression representing true if this element
938        references a member which meets the given criterion.
939
940        The usual implementation of ``has()`` is
941        :meth:`.Relationship.Comparator.has`.
942
943        :param criterion: an optional ClauseElement formulated against the
944          member class' table or attributes.
945
946        :param \**kwargs: key/value pairs corresponding to member class
947          attribute names which will be compared via equality to the
948          corresponding values.
949
950        """
951
952        return self.operate(PropComparator.has_op, criterion, **kwargs)
953
954
955class StrategizedProperty(MapperProperty[_T]):
956    """A MapperProperty which uses selectable strategies to affect
957    loading behavior.
958
959    There is a single strategy selected by default.  Alternate
960    strategies can be selected at Query time through the usage of
961    ``StrategizedOption`` objects via the Query.options() method.
962
963    The mechanics of StrategizedProperty are used for every Query
964    invocation for every mapped attribute participating in that Query,
965    to determine first how the attribute will be rendered in SQL
966    and secondly how the attribute will retrieve a value from a result
967    row and apply it to a mapped object.  The routines here are very
968    performance-critical.
969
970    """
971
972    __slots__ = (
973        "_strategies",
974        "strategy",
975        "_wildcard_token",
976        "_default_path_loader_key",
977        "strategy_key",
978    )
979    inherit_cache = True
980    strategy_wildcard_key: ClassVar[str]
981
982    strategy_key: _StrategyKey
983
984    _strategies: Dict[_StrategyKey, LoaderStrategy]
985
986    def _memoized_attr__wildcard_token(self) -> Tuple[str]:
987        return (
988            f"{self.strategy_wildcard_key}:{path_registry._WILDCARD_TOKEN}",
989        )
990
991    def _memoized_attr__default_path_loader_key(
992        self,
993    ) -> Tuple[str, Tuple[str]]:
994        return (
995            "loader",
996            (f"{self.strategy_wildcard_key}:{path_registry._DEFAULT_TOKEN}",),
997        )
998
999    def _get_context_loader(
1000        self, context: ORMCompileState, path: AbstractEntityRegistry
1001    ) -> Optional[_LoadElement]:
1002        load: Optional[_LoadElement] = None
1003
1004        search_path = path[self]
1005
1006        # search among: exact match, "attr.*", "default" strategy
1007        # if any.
1008        for path_key in (
1009            search_path._loader_key,
1010            search_path._wildcard_path_loader_key,
1011            search_path._default_path_loader_key,
1012        ):
1013            if path_key in context.attributes:
1014                load = context.attributes[path_key]
1015                break
1016
1017                # note that if strategy_options.Load is placing non-actionable
1018                # objects in the context like defaultload(), we would
1019                # need to continue the loop here if we got such an
1020                # option as below.
1021                # if load.strategy or load.local_opts:
1022                #    break
1023
1024        return load
1025
1026    def _get_strategy(self, key: _StrategyKey) -> LoaderStrategy:
1027        try:
1028            return self._strategies[key]
1029        except KeyError:
1030            pass
1031
1032        # run outside to prevent transfer of exception context
1033        cls = self._strategy_lookup(self, *key)
1034        # this previously was setting self._strategies[cls], that's
1035        # a bad idea; should use strategy key at all times because every
1036        # strategy has multiple keys at this point
1037        self._strategies[key] = strategy = cls(self, key)
1038        return strategy
1039
1040    def setup(
1041        self,
1042        context: ORMCompileState,
1043        query_entity: _MapperEntity,
1044        path: AbstractEntityRegistry,
1045        adapter: Optional[ORMAdapter],
1046        **kwargs: Any,
1047    ) -> None:
1048        loader = self._get_context_loader(context, path)
1049        if loader and loader.strategy:
1050            strat = self._get_strategy(loader.strategy)
1051        else:
1052            strat = self.strategy
1053        strat.setup_query(
1054            context, query_entity, path, loader, adapter, **kwargs
1055        )
1056
1057    def create_row_processor(
1058        self,
1059        context: ORMCompileState,
1060        query_entity: _MapperEntity,
1061        path: AbstractEntityRegistry,
1062        mapper: Mapper[Any],
1063        result: Result[Any],
1064        adapter: Optional[ORMAdapter],
1065        populators: _PopulatorDict,
1066    ) -> None:
1067        loader = self._get_context_loader(context, path)
1068        if loader and loader.strategy:
1069            strat = self._get_strategy(loader.strategy)
1070        else:
1071            strat = self.strategy
1072        strat.create_row_processor(
1073            context,
1074            query_entity,
1075            path,
1076            loader,
1077            mapper,
1078            result,
1079            adapter,
1080            populators,
1081        )
1082
1083    def do_init(self) -> None:
1084        self._strategies = {}
1085        self.strategy = self._get_strategy(self.strategy_key)
1086
1087    def post_instrument_class(self, mapper: Mapper[Any]) -> None:
1088        if (
1089            not self.parent.non_primary
1090            and not mapper.class_manager._attr_has_impl(self.key)
1091        ):
1092            self.strategy.init_class_attribute(mapper)
1093
1094    _all_strategies: collections.defaultdict[
1095        Type[MapperProperty[Any]], Dict[_StrategyKey, Type[LoaderStrategy]]
1096    ] = collections.defaultdict(dict)
1097
1098    @classmethod
1099    def strategy_for(cls, **kw: Any) -> Callable[[_TLS], _TLS]:
1100        def decorate(dec_cls: _TLS) -> _TLS:
1101            # ensure each subclass of the strategy has its
1102            # own _strategy_keys collection
1103            if "_strategy_keys" not in dec_cls.__dict__:
1104                dec_cls._strategy_keys = []
1105            key = tuple(sorted(kw.items()))
1106            cls._all_strategies[cls][key] = dec_cls
1107            dec_cls._strategy_keys.append(key)
1108            return dec_cls
1109
1110        return decorate
1111
1112    @classmethod
1113    def _strategy_lookup(
1114        cls, requesting_property: MapperProperty[Any], *key: Any
1115    ) -> Type[LoaderStrategy]:
1116        requesting_property.parent._with_polymorphic_mappers
1117
1118        for prop_cls in cls.__mro__:
1119            if prop_cls in cls._all_strategies:
1120                if TYPE_CHECKING:
1121                    assert issubclass(prop_cls, MapperProperty)
1122                strategies = cls._all_strategies[prop_cls]
1123                try:
1124                    return strategies[key]
1125                except KeyError:
1126                    pass
1127
1128        for property_type, strats in cls._all_strategies.items():
1129            if key in strats:
1130                intended_property_type = property_type
1131                actual_strategy = strats[key]
1132                break
1133        else:
1134            intended_property_type = None
1135            actual_strategy = None
1136
1137        raise orm_exc.LoaderStrategyException(
1138            cls,
1139            requesting_property,
1140            intended_property_type,
1141            actual_strategy,
1142            key,
1143        )
1144
1145
1146class ORMOption(ExecutableOption):
1147    """Base class for option objects that are passed to ORM queries.
1148
1149    These options may be consumed by :meth:`.Query.options`,
1150    :meth:`.Select.options`, or in a more general sense by any
1151    :meth:`.Executable.options` method.   They are interpreted at
1152    statement compile time or execution time in modern use.  The
1153    deprecated :class:`.MapperOption` is consumed at ORM query construction
1154    time.
1155
1156    .. versionadded:: 1.4
1157
1158    """
1159
1160    __slots__ = ()
1161
1162    _is_legacy_option = False
1163
1164    propagate_to_loaders = False
1165    """if True, indicate this option should be carried along
1166    to "secondary" SELECT statements that occur for relationship
1167    lazy loaders as well as attribute load / refresh operations.
1168
1169    """
1170
1171    _is_core = False
1172
1173    _is_user_defined = False
1174
1175    _is_compile_state = False
1176
1177    _is_criteria_option = False
1178
1179    _is_strategy_option = False
1180
1181    def _adapt_cached_option_to_uncached_option(
1182        self, context: QueryContext, uncached_opt: ORMOption
1183    ) -> ORMOption:
1184        """adapt this option to the "uncached" version of itself in a
1185        loader strategy context.
1186
1187        given "self" which is an option from a cached query, as well as the
1188        corresponding option from the uncached version of the same query,
1189        return the option we should use in a new query, in the context of a
1190        loader strategy being asked to load related rows on behalf of that
1191        cached query, which is assumed to be building a new query based on
1192        entities passed to us from the cached query.
1193
1194        Currently this routine chooses between "self" and "uncached" without
1195        manufacturing anything new. If the option is itself a loader strategy
1196        option which has a path, that path needs to match to the entities being
1197        passed to us by the cached query, so the :class:`_orm.Load` subclass
1198        overrides this to return "self". For all other options, we return the
1199        uncached form which may have changing state, such as a
1200        with_loader_criteria() option which will very often have new state.

Showing the first 1,200 of 1470 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai