]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Ensure TypeDecorator delegates _set_parent_with_dispatch
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 20 Oct 2016 14:24:40 +0000 (10:24 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 20 Oct 2016 18:14:50 +0000 (14:14 -0400)
Ensure TypeDecorator delegates _set_parent_with_dispatch as well as
_set_parent to itself as well as its impl, as the TypeDecorator
class itself may have an active SchemaType implementation as well.

Fixed regression which occurred as a side effect of :ticket:`2919`,
which in the less typical case of a user-defined
:class:`.TypeDecorator` that was also itself an instance of
:class:`.SchemaType` (rather than the implementation being such)
would cause the column attachment events to be skipped for the
type itself.

Change-Id: I0afb498fd91ab7d948e4439e7323a89eafcce0bc
Fixes: #3832
doc/build/changelog/changelog_11.rst
lib/sqlalchemy/sql/base.py
lib/sqlalchemy/sql/type_api.py
test/sql/test_metadata.py

index c8785b7661682926264b2c1682222e179e180089..3c00736c7bcdf733343b3e4f98284de8cbf3a7bc 100644 (file)
 .. changelog::
     :version: 1.1.3
 
+    .. change::
+        :tags: bug, sql
+        :tickets: 3832
+
+        Fixed regression which occurred as a side effect of :ticket:`2919`,
+        which in the less typical case of a user-defined
+        :class:`.TypeDecorator` that was also itself an instance of
+        :class:`.SchemaType` (rather than the implementation being such)
+        would cause the column attachment events to be skipped for the
+        type itself.
+
+
 .. changelog::
     :version: 1.1.2
     :released: October 17, 2016
index cf7dcfd310e2157c4dd76084cd62078e54409fdb..0b036847b720bc897f06a865608d9297840bff87 100644 (file)
@@ -426,8 +426,6 @@ class SchemaEventTarget(object):
     def _set_parent(self, parent):
         """Associate with this SchemaEvent's parent object."""
 
-        raise NotImplementedError()
-
     def _set_parent_with_dispatch(self, parent):
         self.dispatch.before_parent_attach(self, parent)
         self._set_parent(parent)
index 689b4c79bd3ba797442492ddd0339b79fa020738..98ede4e6642e59333a6c7152855f678671352036 100644 (file)
@@ -860,12 +860,16 @@ class TypeDecorator(SchemaEventTarget, TypeEngine):
     def _set_parent(self, column):
         """Support SchemaEentTarget"""
 
+        super(TypeDecorator, self)._set_parent(column)
+
         if isinstance(self.impl, SchemaEventTarget):
             self.impl._set_parent(column)
 
     def _set_parent_with_dispatch(self, parent):
         """Support SchemaEentTarget"""
 
+        super(TypeDecorator, self)._set_parent_with_dispatch(parent)
+
         if isinstance(self.impl, SchemaEventTarget):
             self.impl._set_parent_with_dispatch(parent)
 
index f2df4da063925a15f8b46ea769a49f5d120eff7f..f790c2aa0ce04237763a68f688324f85ee75267d 100644 (file)
@@ -1574,6 +1574,66 @@ class SchemaTypeTest(fixtures.TestBase):
     class MyTypeImpl(MyTypeWImpl):
         pass
 
+    class MyTypeDecAndSchema(TypeDecorator, sqltypes.SchemaType):
+        impl = String()
+
+        evt_targets = ()
+
+        def __init__(self):
+            TypeDecorator.__init__(self)
+            sqltypes.SchemaType.__init__(self)
+
+        def _on_table_create(self, target, bind, **kw):
+            self.evt_targets += (target,)
+
+        def _on_metadata_create(self, target, bind, **kw):
+            self.evt_targets += (target,)
+
+    def test_before_parent_attach_plain(self):
+        typ = self.MyType()
+        self._test_before_parent_attach(typ)
+
+    def test_before_parent_attach_typedec_enclosing_schematype(self):
+        # additional test for [ticket:2919] as part of test for
+        # [ticket:3832]
+
+        class MySchemaType(sqltypes.TypeEngine, sqltypes.SchemaType):
+            pass
+
+        target_typ = MySchemaType()
+
+        class MyType(TypeDecorator):
+            impl = target_typ
+
+        typ = MyType()
+        self._test_before_parent_attach(typ, target_typ)
+
+    def test_before_parent_attach_typedec_of_schematype(self):
+        class MyType(TypeDecorator, sqltypes.SchemaType):
+            impl = String
+
+        typ = MyType()
+        self._test_before_parent_attach(typ)
+
+    def test_before_parent_attach_schematype_of_typedec(self):
+        class MyType(sqltypes.SchemaType, TypeDecorator):
+            impl = String
+
+        typ = MyType()
+        self._test_before_parent_attach(typ)
+
+    def _test_before_parent_attach(self, typ, evt_target=None):
+        canary = mock.Mock()
+
+        if evt_target is None:
+            evt_target = typ
+
+        event.listen(evt_target, "before_parent_attach", canary.go)
+
+        c = Column('q', typ)
+
+        eq_(canary.mock_calls, [mock.call.go(evt_target, c)])
+
     def test_independent_schema(self):
         m = MetaData()
         type_ = self.MyType(schema="q")
@@ -1709,6 +1769,13 @@ class SchemaTypeTest(fixtures.TestBase):
         dialect_impl = typ.dialect_impl(testing.db.dialect)
         eq_(dialect_impl.evt_targets, (m1, ))
 
+    def test_table_dispatch_decorator_schematype(self):
+        m1 = MetaData()
+        typ = self.MyTypeDecAndSchema()
+        t1 = Table('t1', m1, Column('x', typ))
+        m1.dispatch.before_create(t1, testing.db)
+        eq_(typ.evt_targets, (t1, ))
+
     def test_table_dispatch_no_new_impl(self):
         m1 = MetaData()
         typ = self.MyType()