main commit
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-10-16 16:30:25 +09:00
parent 91c7e04474
commit 537e7b363f
1146 changed files with 45926 additions and 77196 deletions

View File

@@ -1,5 +1,5 @@
# ext/mutable.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,7 +21,6 @@ JSON strings before being persisted::
from sqlalchemy.types import TypeDecorator, VARCHAR
import json
class JSONEncodedDict(TypeDecorator):
"Represents an immutable structure as a json-encoded string."
@@ -49,7 +48,6 @@ the :class:`.Mutable` mixin to a plain Python dictionary::
from sqlalchemy.ext.mutable import Mutable
class MutableDict(Mutable, dict):
@classmethod
def coerce(cls, key, value):
@@ -103,11 +101,9 @@ attribute. Such as, with classical table metadata::
from sqlalchemy import Table, Column, Integer
my_data = Table(
"my_data",
metadata,
Column("id", Integer, primary_key=True),
Column("data", MutableDict.as_mutable(JSONEncodedDict)),
my_data = Table('my_data', metadata,
Column('id', Integer, primary_key=True),
Column('data', MutableDict.as_mutable(JSONEncodedDict))
)
Above, :meth:`~.Mutable.as_mutable` returns an instance of ``JSONEncodedDict``
@@ -119,17 +115,13 @@ mapping against the ``my_data`` table::
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
class Base(DeclarativeBase):
pass
class MyDataClass(Base):
__tablename__ = "my_data"
__tablename__ = 'my_data'
id: Mapped[int] = mapped_column(primary_key=True)
data: Mapped[dict[str, str]] = mapped_column(
MutableDict.as_mutable(JSONEncodedDict)
)
data: Mapped[dict[str, str]] = mapped_column(MutableDict.as_mutable(JSONEncodedDict))
The ``MyDataClass.data`` member will now be notified of in place changes
to its value.
@@ -140,11 +132,11 @@ will flag the attribute as "dirty" on the parent object::
>>> from sqlalchemy.orm import Session
>>> sess = Session(some_engine)
>>> m1 = MyDataClass(data={"value1": "foo"})
>>> m1 = MyDataClass(data={'value1':'foo'})
>>> sess.add(m1)
>>> sess.commit()
>>> m1.data["value1"] = "bar"
>>> m1.data['value1'] = 'bar'
>>> assert m1 in sess.dirty
True
@@ -161,16 +153,15 @@ the need to declare it individually::
MutableDict.associate_with(JSONEncodedDict)
class Base(DeclarativeBase):
pass
class MyDataClass(Base):
__tablename__ = "my_data"
__tablename__ = 'my_data'
id: Mapped[int] = mapped_column(primary_key=True)
data: Mapped[dict[str, str]] = mapped_column(JSONEncodedDict)
Supporting Pickling
--------------------
@@ -189,7 +180,7 @@ stream::
class MyMutableType(Mutable):
def __getstate__(self):
d = self.__dict__.copy()
d.pop("_parents", None)
d.pop('_parents', None)
return d
With our dictionary example, we need to return the contents of the dict itself
@@ -222,18 +213,13 @@ from within the mutable extension::
from sqlalchemy.orm import mapped_column
from sqlalchemy import event
class Base(DeclarativeBase):
pass
class MyDataClass(Base):
__tablename__ = "my_data"
__tablename__ = 'my_data'
id: Mapped[int] = mapped_column(primary_key=True)
data: Mapped[dict[str, str]] = mapped_column(
MutableDict.as_mutable(JSONEncodedDict)
)
data: Mapped[dict[str, str]] = mapped_column(MutableDict.as_mutable(JSONEncodedDict))
@event.listens_for(MyDataClass.data, "modified")
def modified_json(instance, initiator):
@@ -261,7 +247,6 @@ class introduced in :ref:`mapper_composite` to include
import dataclasses
from sqlalchemy.ext.mutable import MutableComposite
@dataclasses.dataclass
class Point(MutableComposite):
x: int
@@ -276,6 +261,7 @@ class introduced in :ref:`mapper_composite` to include
# alert all parents to the change
self.changed()
The :class:`.MutableComposite` class makes use of class mapping events to
automatically establish listeners for any usage of :func:`_orm.composite` that
specifies our ``Point`` type. Below, when ``Point`` is mapped to the ``Vertex``
@@ -285,7 +271,6 @@ objects to each of the ``Vertex.start`` and ``Vertex.end`` attributes::
from sqlalchemy.orm import DeclarativeBase, Mapped
from sqlalchemy.orm import composite, mapped_column
class Base(DeclarativeBase):
pass
@@ -295,12 +280,8 @@ objects to each of the ``Vertex.start`` and ``Vertex.end`` attributes::
id: Mapped[int] = mapped_column(primary_key=True)
start: Mapped[Point] = composite(
mapped_column("x1"), mapped_column("y1")
)
end: Mapped[Point] = composite(
mapped_column("x2"), mapped_column("y2")
)
start: Mapped[Point] = composite(mapped_column("x1"), mapped_column("y1"))
end: Mapped[Point] = composite(mapped_column("x2"), mapped_column("y2"))
def __repr__(self):
return f"Vertex(start={self.start}, end={self.end})"
@@ -397,7 +378,6 @@ from weakref import WeakKeyDictionary
from .. import event
from .. import inspect
from .. import types
from .. import util
from ..orm import Mapper
from ..orm._typing import _ExternalEntityType
from ..orm._typing import _O
@@ -410,7 +390,6 @@ from ..orm.context import QueryContext
from ..orm.decl_api import DeclarativeAttributeIntercept
from ..orm.state import InstanceState
from ..orm.unitofwork import UOWTransaction
from ..sql._typing import _TypeEngineArgument
from ..sql.base import SchemaEventTarget
from ..sql.schema import Column
from ..sql.type_api import TypeEngine
@@ -524,7 +503,6 @@ class MutableBase:
if val is not None:
if coerce:
val = cls.coerce(key, val)
assert val is not None
state.dict[key] = val
val._parents[state] = key
@@ -659,7 +637,7 @@ class Mutable(MutableBase):
event.listen(Mapper, "mapper_configured", listen_for_type)
@classmethod
def as_mutable(cls, sqltype: _TypeEngineArgument[_T]) -> TypeEngine[_T]:
def as_mutable(cls, sqltype: TypeEngine[_T]) -> TypeEngine[_T]:
"""Associate a SQL type with this mutable Python type.
This establishes listeners that will detect ORM mappings against
@@ -668,11 +646,9 @@ class Mutable(MutableBase):
The type is returned, unconditionally as an instance, so that
:meth:`.as_mutable` can be used inline::
Table(
"mytable",
metadata,
Column("id", Integer, primary_key=True),
Column("data", MyMutableType.as_mutable(PickleType)),
Table('mytable', metadata,
Column('id', Integer, primary_key=True),
Column('data', MyMutableType.as_mutable(PickleType))
)
Note that the returned type is always an instance, even if a class
@@ -823,12 +799,15 @@ class MutableDict(Mutable, Dict[_KT, _VT]):
@overload
def setdefault(
self: MutableDict[_KT, Optional[_T]], key: _KT, value: None = None
) -> Optional[_T]: ...
) -> Optional[_T]:
...
@overload
def setdefault(self, key: _KT, value: _VT) -> _VT: ...
def setdefault(self, key: _KT, value: _VT) -> _VT:
...
def setdefault(self, key: _KT, value: object = None) -> object: ...
def setdefault(self, key: _KT, value: object = None) -> object:
...
else:
@@ -849,14 +828,17 @@ class MutableDict(Mutable, Dict[_KT, _VT]):
if TYPE_CHECKING:
@overload
def pop(self, __key: _KT) -> _VT: ...
def pop(self, __key: _KT) -> _VT:
...
@overload
def pop(self, __key: _KT, __default: _VT | _T) -> _VT | _T: ...
def pop(self, __key: _KT, __default: _VT | _T) -> _VT | _T:
...
def pop(
self, __key: _KT, __default: _VT | _T | None = None
) -> _VT | _T: ...
) -> _VT | _T:
...
else:
@@ -927,10 +909,10 @@ class MutableList(Mutable, List[_T]):
self[:] = state
def is_scalar(self, value: _T | Iterable[_T]) -> TypeGuard[_T]:
return not util.is_non_string_iterable(value)
return not isinstance(value, Iterable)
def is_iterable(self, value: _T | Iterable[_T]) -> TypeGuard[Iterable[_T]]:
return util.is_non_string_iterable(value)
return isinstance(value, Iterable)
def __setitem__(
self, index: SupportsIndex | slice, value: _T | Iterable[_T]