This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# orm/collections.py
|
||||
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
|
||||
# Copyright (C) 2005-2023 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
@@ -21,8 +21,6 @@ provided. One is a bundle of generic decorators that map function arguments
|
||||
and return values to events::
|
||||
|
||||
from sqlalchemy.orm.collections import collection
|
||||
|
||||
|
||||
class MyClass:
|
||||
# ...
|
||||
|
||||
@@ -34,6 +32,7 @@ and return values to events::
|
||||
def pop(self):
|
||||
return self.data.pop()
|
||||
|
||||
|
||||
The second approach is a bundle of targeted decorators that wrap appropriate
|
||||
append and remove notifiers around the mutation methods present in the
|
||||
standard Python ``list``, ``set`` and ``dict`` interfaces. These could be
|
||||
@@ -74,11 +73,10 @@ generally not needed. Odds are, the extension method will delegate to a
|
||||
method that's already instrumented. For example::
|
||||
|
||||
class QueueIsh(list):
|
||||
def push(self, item):
|
||||
self.append(item)
|
||||
|
||||
def shift(self):
|
||||
return self.pop(0)
|
||||
def push(self, item):
|
||||
self.append(item)
|
||||
def shift(self):
|
||||
return self.pop(0)
|
||||
|
||||
There's no need to decorate these methods. ``append`` and ``pop`` are already
|
||||
instrumented as part of the ``list`` interface. Decorating them would fire
|
||||
@@ -150,12 +148,10 @@ __all__ = [
|
||||
"keyfunc_mapping",
|
||||
"column_keyed_dict",
|
||||
"attribute_keyed_dict",
|
||||
"KeyFuncDict",
|
||||
# old names in < 2.0
|
||||
"mapped_collection",
|
||||
"column_mapped_collection",
|
||||
"attribute_mapped_collection",
|
||||
"column_keyed_dict",
|
||||
"attribute_keyed_dict",
|
||||
"MappedCollection",
|
||||
"KeyFuncDict",
|
||||
]
|
||||
|
||||
__instrumentation_mutex = threading.Lock()
|
||||
@@ -171,7 +167,8 @@ _FN = TypeVar("_FN", bound="Callable[..., Any]")
|
||||
|
||||
|
||||
class _CollectionConverterProtocol(Protocol):
|
||||
def __call__(self, collection: _COL) -> _COL: ...
|
||||
def __call__(self, collection: _COL) -> _COL:
|
||||
...
|
||||
|
||||
|
||||
class _AdaptedCollectionProtocol(Protocol):
|
||||
@@ -197,10 +194,9 @@ class collection:
|
||||
The recipe decorators all require parens, even those that take no
|
||||
arguments::
|
||||
|
||||
@collection.adds("entity")
|
||||
@collection.adds('entity')
|
||||
def insert(self, position, entity): ...
|
||||
|
||||
|
||||
@collection.removes_return()
|
||||
def popitem(self): ...
|
||||
|
||||
@@ -220,13 +216,11 @@ class collection:
|
||||
@collection.appender
|
||||
def add(self, append): ...
|
||||
|
||||
|
||||
# or, equivalently
|
||||
@collection.appender
|
||||
@collection.adds(1)
|
||||
def add(self, append): ...
|
||||
|
||||
|
||||
# for mapping type, an 'append' may kick out a previous value
|
||||
# that occupies that slot. consider d['a'] = 'foo'- any previous
|
||||
# value in d['a'] is discarded.
|
||||
@@ -266,11 +260,10 @@ class collection:
|
||||
@collection.remover
|
||||
def zap(self, entity): ...
|
||||
|
||||
|
||||
# or, equivalently
|
||||
@collection.remover
|
||||
@collection.removes_return()
|
||||
def zap(self): ...
|
||||
def zap(self, ): ...
|
||||
|
||||
If the value to remove is not present in the collection, you may
|
||||
raise an exception or return None to ignore the error.
|
||||
@@ -359,7 +352,7 @@ class collection:
|
||||
return fn
|
||||
|
||||
@staticmethod
|
||||
def adds(arg: int) -> Callable[[_FN], _FN]:
|
||||
def adds(arg):
|
||||
"""Mark the method as adding an entity to the collection.
|
||||
|
||||
Adds "add to collection" handling to the method. The decorator
|
||||
@@ -370,8 +363,7 @@ class collection:
|
||||
@collection.adds(1)
|
||||
def push(self, item): ...
|
||||
|
||||
|
||||
@collection.adds("entity")
|
||||
@collection.adds('entity')
|
||||
def do_stuff(self, thing, entity=None): ...
|
||||
|
||||
"""
|
||||
@@ -556,9 +548,9 @@ class CollectionAdapter:
|
||||
self.empty
|
||||
), "This collection adapter is not in the 'empty' state"
|
||||
self.empty = False
|
||||
self.owner_state.dict[self._key] = (
|
||||
self.owner_state._empty_collections.pop(self._key)
|
||||
)
|
||||
self.owner_state.dict[
|
||||
self._key
|
||||
] = self.owner_state._empty_collections.pop(self._key)
|
||||
|
||||
def _refuse_empty(self) -> NoReturn:
|
||||
raise sa_exc.InvalidRequestError(
|
||||
@@ -1562,14 +1554,14 @@ class InstrumentedDict(Dict[_KT, _VT]):
|
||||
"""An instrumented version of the built-in dict."""
|
||||
|
||||
|
||||
__canned_instrumentation: util.immutabledict[Any, _CollectionFactoryType] = (
|
||||
util.immutabledict(
|
||||
{
|
||||
list: InstrumentedList,
|
||||
set: InstrumentedSet,
|
||||
dict: InstrumentedDict,
|
||||
}
|
||||
)
|
||||
__canned_instrumentation: util.immutabledict[
|
||||
Any, _CollectionFactoryType
|
||||
] = util.immutabledict(
|
||||
{
|
||||
list: InstrumentedList,
|
||||
set: InstrumentedSet,
|
||||
dict: InstrumentedDict,
|
||||
}
|
||||
)
|
||||
|
||||
__interfaces: util.immutabledict[
|
||||
|
||||
Reference in New Issue
Block a user