diff options
Diffstat (limited to 'lib/sqlalchemy/orm/interfaces.py')
| -rw-r--r-- | lib/sqlalchemy/orm/interfaces.py | 302 |
1 files changed, 204 insertions, 98 deletions
diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py index abc1300d8..0ca62b7e3 100644 --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@ -21,10 +21,15 @@ from __future__ import annotations import collections import typing from typing import Any +from typing import Callable from typing import cast +from typing import ClassVar +from typing import Dict +from typing import Iterator from typing import List from typing import Optional from typing import Sequence +from typing import Set from typing import Tuple from typing import Type from typing import TypeVar @@ -45,7 +50,6 @@ from .base import NotExtension as NotExtension from .base import ONETOMANY as ONETOMANY from .base import SQLORMOperations from .. import ColumnElement -from .. import inspect from .. import inspection from .. import util from ..sql import operators @@ -53,19 +57,47 @@ from ..sql import roles from ..sql import visitors from ..sql.base import ExecutableOption from ..sql.cache_key import HasCacheKey -from ..sql.elements import SQLCoreOperations from ..sql.schema import Column from ..sql.type_api import TypeEngine from ..util.typing import TypedDict + if typing.TYPE_CHECKING: + from ._typing import _EntityType + from ._typing import _IdentityKeyType + from ._typing import _InstanceDict + from ._typing import _InternalEntityType + from ._typing import _ORMAdapterProto + from ._typing import _ORMColumnExprArgument + from .attributes import InstrumentedAttribute + from .context import _MapperEntity + from .context import ORMCompileState from .decl_api import RegistryType + from .loading import _PopulatorDict + from .mapper import Mapper + from .path_registry import AbstractEntityRegistry + from .path_registry import PathRegistry + from .query import Query + from .session import Session + from .state import InstanceState + from .strategy_options import _LoadElement + from .util import AliasedInsp + from .util import CascadeOptions + from .util import ORMAdapter + from ..engine.result import Result + from ..sql._typing import _ColumnExpressionArgument from ..sql._typing import _ColumnsClauseArgument from ..sql._typing import _DMLColumnArgument from ..sql._typing import _InfoType + from ..sql._typing import _PropagateAttrsType + from ..sql.operators import OperatorType + from ..sql.util import ColumnAdapter + from ..sql.visitors import _TraverseInternalsType _T = TypeVar("_T", bound=Any) +_TLS = TypeVar("_TLS", bound="Type[LoaderStrategy]") + class ORMStatementRole(roles.StatementRole): __slots__ = () @@ -91,7 +123,9 @@ class ORMFromClauseRole(roles.StrictFromClauseRole): class ORMColumnDescription(TypedDict): name: str - type: Union[Type, TypeEngine] + # TODO: add python_type and sql_type here; combining them + # into "type" is a bad idea + type: Union[Type[Any], TypeEngine[Any]] aliased: bool expr: _ColumnsClauseArgument entity: Optional[_ColumnsClauseArgument] @@ -102,10 +136,10 @@ class _IntrospectsAnnotations: def declarative_scan( self, - registry: "RegistryType", - cls: type, + registry: RegistryType, + cls: Type[Any], key: str, - annotation: Optional[type], + annotation: Optional[Type[Any]], is_dataclass_field: Optional[bool], ) -> None: """Perform class-specific initializaton at early declarative scanning @@ -124,12 +158,12 @@ class _MapsColumns(_MappedAttribute[_T]): __slots__ = () @property - def mapper_property_to_assign(self) -> Optional["MapperProperty[_T]"]: + def mapper_property_to_assign(self) -> Optional[MapperProperty[_T]]: """return a MapperProperty to be assigned to the declarative mapping""" raise NotImplementedError() @property - def columns_to_assign(self) -> List[Column]: + def columns_to_assign(self) -> List[Column[_T]]: """A list of Column objects that should be declaratively added to the new Table object. @@ -139,7 +173,10 @@ class _MapsColumns(_MappedAttribute[_T]): @inspection._self_inspects class MapperProperty( - HasCacheKey, _MappedAttribute[_T], InspectionAttr, util.MemoizedSlots + HasCacheKey, + _MappedAttribute[_T], + InspectionAttrInfo, + util.MemoizedSlots, ): """Represent a particular class attribute mapped by :class:`_orm.Mapper`. @@ -160,12 +197,12 @@ class MapperProperty( "info", ) - _cache_key_traversal = [ + _cache_key_traversal: _TraverseInternalsType = [ ("parent", visitors.ExtendedInternalTraversal.dp_has_cache_key), ("key", visitors.ExtendedInternalTraversal.dp_string), ] - cascade = frozenset() + cascade: Optional[CascadeOptions] = None """The set of 'cascade' attribute names. This collection is checked before the 'cascade_iterator' method is called. @@ -184,14 +221,20 @@ class MapperProperty( """The :class:`_orm.PropComparator` instance that implements SQL expression construction on behalf of this mapped attribute.""" - @property - def _links_to_entity(self): - """True if this MapperProperty refers to a mapped entity. + key: str + """name of class attribute""" - Should only be True for Relationship, False for all others. + parent: Mapper[Any] + """the :class:`.Mapper` managing this property.""" - """ - raise NotImplementedError() + _is_relationship = False + + _links_to_entity: bool + """True if this MapperProperty refers to a mapped entity. + + Should only be True for Relationship, False for all others. + + """ def _memoized_attr_info(self) -> _InfoType: """Info dictionary associated with the object, allowing user-defined @@ -217,7 +260,14 @@ class MapperProperty( """ return {} - def setup(self, context, query_entity, path, adapter, **kwargs): + def setup( + self, + context: ORMCompileState, + query_entity: _MapperEntity, + path: PathRegistry, + adapter: Optional[ColumnAdapter], + **kwargs: Any, + ) -> None: """Called by Query for the purposes of constructing a SQL statement. Each MapperProperty associated with the target mapper processes the @@ -227,16 +277,30 @@ class MapperProperty( """ def create_row_processor( - self, context, query_entity, path, mapper, result, adapter, populators - ): + self, + context: ORMCompileState, + query_entity: _MapperEntity, + path: PathRegistry, + mapper: Mapper[Any], + result: Result, + adapter: Optional[ColumnAdapter], + populators: _PopulatorDict, + ) -> None: """Produce row processing functions and append to the given set of populators lists. """ def cascade_iterator( - self, type_, state, dict_, visited_states, halt_on=None - ): + self, + type_: str, + state: InstanceState[Any], + dict_: _InstanceDict, + visited_states: Set[InstanceState[Any]], + halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None, + ) -> Iterator[ + Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict] + ]: """Iterate through instances related to the given instance for a particular 'cascade', starting with this MapperProperty. @@ -251,7 +315,7 @@ class MapperProperty( return iter(()) - def set_parent(self, parent, init): + def set_parent(self, parent: Mapper[Any], init: bool) -> None: """Set the parent mapper that references this MapperProperty. This method is overridden by some subclasses to perform extra @@ -260,7 +324,7 @@ class MapperProperty( """ self.parent = parent - def instrument_class(self, mapper): + def instrument_class(self, mapper: Mapper[Any]) -> None: """Hook called by the Mapper to the property to initiate instrumentation of the class attribute managed by this MapperProperty. @@ -280,11 +344,11 @@ class MapperProperty( """ - def __init__(self): + def __init__(self) -> None: self._configure_started = False self._configure_finished = False - def init(self): + def init(self) -> None: """Called after all mappers are created to assemble relationships between mappers and perform other post-mapper-creation initialization steps. @@ -296,7 +360,7 @@ class MapperProperty( self._configure_finished = True @property - def class_attribute(self): + def class_attribute(self) -> InstrumentedAttribute[_T]: """Return the class-bound descriptor corresponding to this :class:`.MapperProperty`. @@ -319,9 +383,9 @@ class MapperProperty( """ - return getattr(self.parent.class_, self.key) + return getattr(self.parent.class_, self.key) # type: ignore - def do_init(self): + def do_init(self) -> None: """Perform subclass-specific initialization post-mapper-creation steps. @@ -330,7 +394,7 @@ class MapperProperty( """ - def post_instrument_class(self, mapper): + def post_instrument_class(self, mapper: Mapper[Any]) -> None: """Perform instrumentation adjustments that need to occur after init() has completed. @@ -347,21 +411,21 @@ class MapperProperty( def merge( self, - session, - source_state, - source_dict, - dest_state, - dest_dict, - load, - _recursive, - _resolve_conflict_map, - ): + session: Session, + source_state: InstanceState[Any], + source_dict: _InstanceDict, + dest_state: InstanceState[Any], + dest_dict: _InstanceDict, + load: bool, + _recursive: Set[InstanceState[Any]], + _resolve_conflict_map: Dict[_IdentityKeyType[Any], object], + ) -> None: """Merge the attribute represented by this ``MapperProperty`` from source to destination object. """ - def __repr__(self): + def __repr__(self) -> str: return "<%s at 0x%x; %s>" % ( self.__class__.__name__, id(self), @@ -452,21 +516,28 @@ class PropComparator(SQLORMOperations[_T]): """ - __slots__ = "prop", "property", "_parententity", "_adapt_to_entity" + __slots__ = "prop", "_parententity", "_adapt_to_entity" __visit_name__ = "orm_prop_comparator" + _parententity: _InternalEntityType[Any] + _adapt_to_entity: Optional[AliasedInsp[Any]] + def __init__( self, - prop, - parentmapper, - adapt_to_entity=None, + prop: MapperProperty[_T], + parentmapper: _InternalEntityType[Any], + adapt_to_entity: Optional[AliasedInsp[Any]] = None, ): - self.prop = self.property = prop + self.prop = prop self._parententity = adapt_to_entity or parentmapper self._adapt_to_entity = adapt_to_entity - def __clause_element__(self): + @util.ro_non_memoized_property + def property(self) -> Optional[MapperProperty[_T]]: + return self.prop + + def __clause_element__(self) -> _ORMColumnExprArgument[_T]: raise NotImplementedError("%r" % self) def _bulk_update_tuples( @@ -480,22 +551,24 @@ class PropComparator(SQLORMOperations[_T]): """ - return [(self.__clause_element__(), value)] + return [(cast("_DMLColumnArgument", self.__clause_element__()), value)] - def adapt_to_entity(self, adapt_to_entity): + def adapt_to_entity( + self, adapt_to_entity: AliasedInsp[Any] + ) -> PropComparator[_T]: """Return a copy of this PropComparator which will use the given :class:`.AliasedInsp` to produce corresponding expressions. """ return self.__class__(self.prop, self._parententity, adapt_to_entity) - @property - def _parentmapper(self): + @util.ro_non_memoized_property + def _parentmapper(self) -> Mapper[Any]: """legacy; this is renamed to _parententity to be compatible with QueryableAttribute.""" - return inspect(self._parententity).mapper + return self._parententity.mapper - @property - def _propagate_attrs(self): + @util.memoized_property + def _propagate_attrs(self) -> _PropagateAttrsType: # this suits the case in coercions where we don't actually # call ``__clause_element__()`` but still need to get # resolved._propagate_attrs. See #6558. @@ -507,12 +580,14 @@ class PropComparator(SQLORMOperations[_T]): ) def _criterion_exists( - self, criterion: Optional[SQLCoreOperations[Any]] = None, **kwargs: Any + self, + criterion: Optional[_ColumnExpressionArgument[bool]] = None, + **kwargs: Any, ) -> ColumnElement[Any]: return self.prop.comparator._criterion_exists(criterion, **kwargs) - @property - def adapter(self): + @util.ro_non_memoized_property + def adapter(self) -> Optional[_ORMAdapterProto[_T]]: """Produce a callable that adapts column expressions to suit an aliased version of this comparator. @@ -522,20 +597,20 @@ class PropComparator(SQLORMOperations[_T]): else: return self._adapt_to_entity._adapt_element - @util.non_memoized_property + @util.ro_non_memoized_property def info(self) -> _InfoType: - return self.property.info + return self.prop.info @staticmethod - def _any_op(a, b, **kwargs): + def _any_op(a: Any, b: Any, **kwargs: Any) -> Any: return a.any(b, **kwargs) @staticmethod - def _has_op(left, other, **kwargs): + def _has_op(left: Any, other: Any, **kwargs: Any) -> Any: return left.has(other, **kwargs) @staticmethod - def _of_type_op(a, class_): + def _of_type_op(a: Any, class_: Any) -> Any: return a.of_type(class_) any_op = cast(operators.OperatorType, _any_op) @@ -545,16 +620,16 @@ class PropComparator(SQLORMOperations[_T]): if typing.TYPE_CHECKING: def operate( - self, op: operators.OperatorType, *other: Any, **kwargs: Any - ) -> "SQLCoreOperations[Any]": + self, op: OperatorType, *other: Any, **kwargs: Any + ) -> ColumnElement[Any]: ... def reverse_operate( - self, op: operators.OperatorType, other: Any, **kwargs: Any - ) -> "SQLCoreOperations[Any]": + self, op: OperatorType, other: Any, **kwargs: Any + ) -> ColumnElement[Any]: ... - def of_type(self, class_) -> "SQLORMOperations[_T]": + def of_type(self, class_: _EntityType[Any]) -> PropComparator[_T]: r"""Redefine this object in terms of a polymorphic subclass, :func:`_orm.with_polymorphic` construct, or :func:`_orm.aliased` construct. @@ -578,9 +653,11 @@ class PropComparator(SQLORMOperations[_T]): """ - return self.operate(PropComparator.of_type_op, class_) + return self.operate(PropComparator.of_type_op, class_) # type: ignore - def and_(self, *criteria) -> "SQLORMOperations[_T]": + def and_( + self, *criteria: _ColumnExpressionArgument[bool] + ) -> ColumnElement[bool]: """Add additional criteria to the ON clause that's represented by this relationship attribute. @@ -606,10 +683,12 @@ class PropComparator(SQLORMOperations[_T]): :func:`.with_loader_criteria` """ - return self.operate(operators.and_, *criteria) + return self.operate(operators.and_, *criteria) # type: ignore def any( - self, criterion: Optional[SQLCoreOperations[Any]] = None, **kwargs + self, + criterion: Optional[_ColumnExpressionArgument[bool]] = None, + **kwargs: Any, ) -> ColumnElement[bool]: r"""Return a SQL expression representing true if this element references a member which meets the given criterion. @@ -626,10 +705,14 @@ class PropComparator(SQLORMOperations[_T]): """ - return self.operate(PropComparator.any_op, criterion, **kwargs) + return self.operate( # type: ignore + PropComparator.any_op, criterion, **kwargs + ) def has( - self, criterion: Optional[SQLCoreOperations[Any]] = None, **kwargs + self, + criterion: Optional[_ColumnExpressionArgument[bool]] = None, + **kwargs: Any, ) -> ColumnElement[bool]: r"""Return a SQL expression representing true if this element references a member which meets the given criterion. @@ -646,7 +729,9 @@ class PropComparator(SQLORMOperations[_T]): """ - return self.operate(PropComparator.has_op, criterion, **kwargs) + return self.operate( # type: ignore + PropComparator.has_op, criterion, **kwargs + ) class StrategizedProperty(MapperProperty[_T]): @@ -674,23 +759,30 @@ class StrategizedProperty(MapperProperty[_T]): "strategy_key", ) inherit_cache = True - strategy_wildcard_key = None + strategy_wildcard_key: ClassVar[str] strategy_key: Tuple[Any, ...] - def _memoized_attr__wildcard_token(self): + _strategies: Dict[Tuple[Any, ...], LoaderStrategy] + + def _memoized_attr__wildcard_token(self) -> Tuple[str]: return ( f"{self.strategy_wildcard_key}:{path_registry._WILDCARD_TOKEN}", ) - def _memoized_attr__default_path_loader_key(self): + def _memoized_attr__default_path_loader_key( + self, + ) -> Tuple[str, Tuple[str]]: return ( "loader", (f"{self.strategy_wildcard_key}:{path_registry._DEFAULT_TOKEN}",), ) - def _get_context_loader(self, context, path): - load = None + def _get_context_loader( + self, context: ORMCompileState, path: AbstractEntityRegistry + ) -> Optional[_LoadElement]: + + load: Optional[_LoadElement] = None search_path = path[self] @@ -714,7 +806,7 @@ class StrategizedProperty(MapperProperty[_T]): return load - def _get_strategy(self, key): + def _get_strategy(self, key: Tuple[Any, ...]) -> LoaderStrategy: try: return self._strategies[key] except KeyError: @@ -768,11 +860,13 @@ class StrategizedProperty(MapperProperty[_T]): ): self.strategy.init_class_attribute(mapper) - _all_strategies = collections.defaultdict(dict) + _all_strategies: collections.defaultdict[ + Type[Any], Dict[Tuple[Any, ...], Type[LoaderStrategy]] + ] = collections.defaultdict(dict) @classmethod - def strategy_for(cls, **kw): - def decorate(dec_cls): + def strategy_for(cls, **kw: Any) -> Callable[[_TLS], _TLS]: + def decorate(dec_cls: _TLS) -> _TLS: # ensure each subclass of the strategy has its # own _strategy_keys collection if "_strategy_keys" not in dec_cls.__dict__: @@ -785,7 +879,9 @@ class StrategizedProperty(MapperProperty[_T]): return decorate @classmethod - def _strategy_lookup(cls, requesting_property, *key): + def _strategy_lookup( + cls, requesting_property: MapperProperty[Any], *key: Any + ) -> Type[LoaderStrategy]: requesting_property.parent._with_polymorphic_mappers for prop_cls in cls.__mro__: @@ -984,10 +1080,10 @@ class MapperOption(ORMOption): """ - def process_query(self, query): + def process_query(self, query: Query[Any]) -> None: """Apply a modification to the given :class:`_query.Query`.""" - def process_query_conditionally(self, query): + def process_query_conditionally(self, query: Query[Any]) -> None: """same as process_query(), except that this option may not apply to the given query. @@ -1034,7 +1130,11 @@ class LoaderStrategy: "strategy_opts", ) - def __init__(self, parent, strategy_key): + _strategy_keys: ClassVar[List[Tuple[Any, ...]]] + + def __init__( + self, parent: MapperProperty[Any], strategy_key: Tuple[Any, ...] + ): self.parent_property = parent self.is_class_level = False self.parent = self.parent_property.parent @@ -1042,12 +1142,18 @@ class LoaderStrategy: self.strategy_key = strategy_key self.strategy_opts = dict(strategy_key) - def init_class_attribute(self, mapper): + def init_class_attribute(self, mapper: Mapper[Any]) -> None: pass def setup_query( - self, compile_state, query_entity, path, loadopt, adapter, **kwargs - ): + self, + compile_state: ORMCompileState, + query_entity: _MapperEntity, + path: AbstractEntityRegistry, + loadopt: Optional[_LoadElement], + adapter: Optional[ORMAdapter], + **kwargs: Any, + ) -> None: """Establish column and other state for a given QueryContext. This method fulfills the contract specified by MapperProperty.setup(). @@ -1059,15 +1165,15 @@ class LoaderStrategy: def create_row_processor( self, - context, - query_entity, - path, - loadopt, - mapper, - result, - adapter, - populators, - ): + context: ORMCompileState, + query_entity: _MapperEntity, + path: AbstractEntityRegistry, + loadopt: Optional[_LoadElement], + mapper: Mapper[Any], + result: Result, + adapter: Optional[ORMAdapter], + populators: _PopulatorDict, + ) -> None: """Establish row processing functions for a given QueryContext. This method fulfills the contract specified by |
