lol
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||
# mypy: ignore-errors
|
||||
|
||||
|
||||
"""Dynamic collection API.
|
||||
@@ -23,13 +22,19 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
from typing import Iterable
|
||||
from typing import Iterator
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import Type
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TypeVar
|
||||
from typing import Union
|
||||
|
||||
from . import attributes
|
||||
from . import exc as orm_exc
|
||||
from . import relationships
|
||||
from . import util as orm_util
|
||||
from .base import PassiveFlag
|
||||
from .query import Query
|
||||
from .session import object_session
|
||||
from .writeonly import AbstractCollectionWriter
|
||||
@@ -39,15 +44,28 @@ from .writeonly import WriteOnlyLoader
|
||||
from .. import util
|
||||
from ..engine import result
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .session import Session
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import QueryableAttribute
|
||||
from .mapper import Mapper
|
||||
from .relationships import _RelationshipOrderByArg
|
||||
from .session import Session
|
||||
from .state import InstanceState
|
||||
from .util import AliasedClass
|
||||
from ..event import _Dispatch
|
||||
from ..sql.elements import ColumnElement
|
||||
|
||||
_T = TypeVar("_T", bound=Any)
|
||||
|
||||
|
||||
class DynamicCollectionHistory(WriteOnlyHistory):
|
||||
def __init__(self, attr, state, passive, apply_to=None):
|
||||
class DynamicCollectionHistory(WriteOnlyHistory[_T]):
|
||||
def __init__(
|
||||
self,
|
||||
attr: DynamicAttributeImpl,
|
||||
state: InstanceState[_T],
|
||||
passive: PassiveFlag,
|
||||
apply_to: Optional[DynamicCollectionHistory[_T]] = None,
|
||||
) -> None:
|
||||
if apply_to:
|
||||
coll = AppenderQuery(attr, state).autoflush(False)
|
||||
self.unchanged_items = util.OrderedIdentitySet(coll)
|
||||
@@ -63,21 +81,21 @@ class DynamicCollectionHistory(WriteOnlyHistory):
|
||||
|
||||
class DynamicAttributeImpl(WriteOnlyAttributeImpl):
|
||||
_supports_dynamic_iteration = True
|
||||
collection_history_cls = DynamicCollectionHistory
|
||||
collection_history_cls = DynamicCollectionHistory[Any]
|
||||
query_class: Type[AppenderMixin[Any]] # type: ignore[assignment]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
class_,
|
||||
key,
|
||||
typecallable,
|
||||
dispatch,
|
||||
target_mapper,
|
||||
order_by,
|
||||
query_class=None,
|
||||
**kw,
|
||||
):
|
||||
class_: Union[Type[Any], AliasedClass[Any]],
|
||||
key: str,
|
||||
dispatch: _Dispatch[QueryableAttribute[Any]],
|
||||
target_mapper: Mapper[_T],
|
||||
order_by: _RelationshipOrderByArg,
|
||||
query_class: Optional[Type[AppenderMixin[_T]]] = None,
|
||||
**kw: Any,
|
||||
) -> None:
|
||||
attributes.AttributeImpl.__init__(
|
||||
self, class_, key, typecallable, dispatch, **kw
|
||||
self, class_, key, None, dispatch, **kw
|
||||
)
|
||||
self.target_mapper = target_mapper
|
||||
if order_by:
|
||||
@@ -102,21 +120,23 @@ class AppenderMixin(AbstractCollectionWriter[_T]):
|
||||
|
||||
"""
|
||||
|
||||
query_class = None
|
||||
query_class: Optional[Type[Query[_T]]] = None
|
||||
_order_by_clauses: Tuple[ColumnElement[Any], ...]
|
||||
|
||||
def __init__(self, attr, state):
|
||||
Query.__init__(self, attr.target_mapper, None)
|
||||
def __init__(
|
||||
self, attr: DynamicAttributeImpl, state: InstanceState[_T]
|
||||
) -> None:
|
||||
Query.__init__(
|
||||
self, # type: ignore[arg-type]
|
||||
attr.target_mapper,
|
||||
None,
|
||||
)
|
||||
super().__init__(attr, state)
|
||||
|
||||
@property
|
||||
def session(self) -> Session:
|
||||
def session(self) -> Optional[Session]:
|
||||
sess = object_session(self.instance)
|
||||
if (
|
||||
sess is not None
|
||||
and self.autoflush
|
||||
and sess.autoflush
|
||||
and self.instance in sess
|
||||
):
|
||||
if sess is not None and sess.autoflush and self.instance in sess:
|
||||
sess.flush()
|
||||
if not orm_util.has_identity(self.instance):
|
||||
return None
|
||||
@@ -127,7 +147,7 @@ class AppenderMixin(AbstractCollectionWriter[_T]):
|
||||
def session(self, session: Session) -> None:
|
||||
self.sess = session
|
||||
|
||||
def _iter(self):
|
||||
def _iter(self) -> Union[result.ScalarResult[_T], result.Result[_T]]:
|
||||
sess = self.session
|
||||
if sess is None:
|
||||
state = attributes.instance_state(self.instance)
|
||||
@@ -141,9 +161,9 @@ class AppenderMixin(AbstractCollectionWriter[_T]):
|
||||
|
||||
return result.IteratorResult(
|
||||
result.SimpleResultMetaData([self.attr.class_.__name__]),
|
||||
self.attr._get_collection_history(
|
||||
self.attr._get_collection_history( # type: ignore[arg-type]
|
||||
attributes.instance_state(self.instance),
|
||||
attributes.PASSIVE_NO_INITIALIZE,
|
||||
PassiveFlag.PASSIVE_NO_INITIALIZE,
|
||||
).added_items,
|
||||
_source_supports_scalars=True,
|
||||
).scalars()
|
||||
@@ -155,15 +175,15 @@ class AppenderMixin(AbstractCollectionWriter[_T]):
|
||||
def __iter__(self) -> Iterator[_T]:
|
||||
...
|
||||
|
||||
def __getitem__(self, index: Any) -> _T:
|
||||
def __getitem__(self, index: Any) -> Union[_T, List[_T]]:
|
||||
sess = self.session
|
||||
if sess is None:
|
||||
return self.attr._get_collection_history(
|
||||
attributes.instance_state(self.instance),
|
||||
attributes.PASSIVE_NO_INITIALIZE,
|
||||
PassiveFlag.PASSIVE_NO_INITIALIZE,
|
||||
).indexed(index)
|
||||
else:
|
||||
return self._generate(sess).__getitem__(index)
|
||||
return self._generate(sess).__getitem__(index) # type: ignore[no-any-return] # noqa: E501
|
||||
|
||||
def count(self) -> int:
|
||||
sess = self.session
|
||||
@@ -171,13 +191,16 @@ class AppenderMixin(AbstractCollectionWriter[_T]):
|
||||
return len(
|
||||
self.attr._get_collection_history(
|
||||
attributes.instance_state(self.instance),
|
||||
attributes.PASSIVE_NO_INITIALIZE,
|
||||
PassiveFlag.PASSIVE_NO_INITIALIZE,
|
||||
).added_items
|
||||
)
|
||||
else:
|
||||
return self._generate(sess).count()
|
||||
|
||||
def _generate(self, sess=None):
|
||||
def _generate(
|
||||
self,
|
||||
sess: Optional[Session] = None,
|
||||
) -> Query[_T]:
|
||||
# note we're returning an entirely new Query class instance
|
||||
# here without any assignment capabilities; the class of this
|
||||
# query is determined by the session.
|
||||
@@ -259,7 +282,7 @@ class AppenderMixin(AbstractCollectionWriter[_T]):
|
||||
self._remove_impl(item)
|
||||
|
||||
|
||||
class AppenderQuery(AppenderMixin[_T], Query[_T]):
|
||||
class AppenderQuery(AppenderMixin[_T], Query[_T]): # type: ignore[misc]
|
||||
"""A dynamic query that supports basic collection storage operations.
|
||||
|
||||
Methods on :class:`.AppenderQuery` include all methods of
|
||||
@@ -270,7 +293,7 @@ class AppenderQuery(AppenderMixin[_T], Query[_T]):
|
||||
"""
|
||||
|
||||
|
||||
def mixin_user_query(cls):
|
||||
def mixin_user_query(cls: Any) -> type[AppenderMixin[Any]]:
|
||||
"""Return a new class with AppenderQuery functionality layered over."""
|
||||
name = "Appender" + cls.__name__
|
||||
return type(name, (AppenderMixin, cls), {"query_class": cls})
|
||||
|
||||
Reference in New Issue
Block a user