From 039c684331c387718339f1a735c0cb22aa00faa9 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 7 May 2024 18:03:51 -0400 Subject: [PATCH] setup JoinedDispatch to support pickling Fixed issue where a :class:`.MetaData` collection would not be serializable, if an :class:`.Enum` or :class:`.Boolean` datatype were present which had been adapted. This specific scenario in turn could occur when using the :class:`.Enum` or :class:`.Boolean` within ORM Annotated Declarative form where type objects frequently get copied. Fixes: #11365 Change-Id: Iaaa64baad79c41075d37cf53dade744d79e600a3 (cherry picked from commit 93cfb49572ac56bc320a09b82285bf8ef8cdff57) --- doc/build/changelog/unreleased_20/11365.rst | 9 +++++++++ lib/sqlalchemy/event/base.py | 7 +++++++ test/sql/test_types.py | 9 ++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 doc/build/changelog/unreleased_20/11365.rst diff --git a/doc/build/changelog/unreleased_20/11365.rst b/doc/build/changelog/unreleased_20/11365.rst new file mode 100644 index 0000000000..d2b353e912 --- /dev/null +++ b/doc/build/changelog/unreleased_20/11365.rst @@ -0,0 +1,9 @@ +.. change:: + :tags: bug, orm + :tickets: 11365 + + Fixed issue where a :class:`.MetaData` collection would not be + serializable, if an :class:`.Enum` or :class:`.Boolean` datatype were + present which had been adapted. This specific scenario in turn could occur + when using the :class:`.Enum` or :class:`.Boolean` within ORM Annotated + Declarative form where type objects frequently get copied. diff --git a/lib/sqlalchemy/event/base.py b/lib/sqlalchemy/event/base.py index 1f52e2eb79..434886316f 100644 --- a/lib/sqlalchemy/event/base.py +++ b/lib/sqlalchemy/event/base.py @@ -198,6 +198,10 @@ class _Dispatch(_DispatchCommon[_ET]): {"__slots__": self._event_names}, ) self.__class__._joined_dispatch_cls = cls + + # establish pickle capability by adding it to this module + globals()[cls.__name__] = cls + return self._joined_dispatch_cls(self, other) def __reduce__(self) -> Union[str, Tuple[Any, ...]]: @@ -398,6 +402,9 @@ class _JoinedDispatcher(_DispatchCommon[_ET]): self.parent = parent self._instance_cls = self.local._instance_cls + def __reduce__(self) -> Any: + return (self.__class__, (self.local, self.parent)) + def __getattr__(self, name: str) -> _JoinedListener[_ET]: # Assign _JoinedListeners as attributes on demand # to reduce startup time for new dispatch objects. diff --git a/test/sql/test_types.py b/test/sql/test_types.py index 0127004438..5214ebac53 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -507,15 +507,22 @@ class PickleTypesTest(fixtures.TestBase): ("Big", BigInteger()), ("Num", Numeric()), ("Flo", Float()), + ("Enu", Enum("one", "two", "three")), ("Dat", DateTime()), ("Dat", Date()), ("Tim", Time()), ("Lar", LargeBinary()), ("Pic", PickleType()), ("Int", Interval()), + argnames="name,type_", id_="ar", ) - def test_pickle_types(self, name, type_): + @testing.variation("use_adapt", [True, False]) + def test_pickle_types(self, name, type_, use_adapt): + + if use_adapt: + type_ = type_.copy() + column_type = Column(name, type_) meta = MetaData() Table("foo", meta, column_type) -- 2.47.2