]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- added util.portable_instancemethod to provide a quick way to make an instancemethod...
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 22 Feb 2010 20:37:44 +0000 (20:37 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 22 Feb 2010 20:37:44 +0000 (20:37 +0000)
- SchemaType and subclasses Boolean, Enum are now serializable,
including their ddl listener and other event callables.
[ticket:1694] [ticket:1698]
- AddConstraint/DropConstraint use the wrapper for _create_rule
- added test coverage for AddConstraint override of _create_rule

CHANGES
lib/sqlalchemy/schema.py
lib/sqlalchemy/types.py
lib/sqlalchemy/util.py
test/sql/test_constraints.py
test/sql/test_types.py

diff --git a/CHANGES b/CHANGES
index 41761c207f3988dd77bf57f521c588d8c218d3b1..986ba6ba4f2f71b4c28297e429a88a8f2b0ea102 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -78,6 +78,10 @@ CHANGES
     encouraged to use those functions whenever they correspond
     to their needs instead of implementing custom ones.
 
+  - SchemaType and subclasses Boolean, Enum are now serializable,
+    including their ddl listener and other event callables.
+    [ticket:1694] [ticket:1698]
+    
   - Added math negation operator support, -x.
   
   - FunctionElement subclasses are now directly executable the
index d51ae3a458a7f5caab58188db50f05613551d8b9..c68d1f1354caeb480bcfb84b41808494688d773e 100644 (file)
@@ -1426,7 +1426,8 @@ class CheckConstraint(Constraint):
     Can be included in the definition of a Table or Column.
     """
 
-    def __init__(self, sqltext, name=None, deferrable=None, initially=None, table=None, _create_rule=None):
+    def __init__(self, sqltext, name=None, deferrable=None, 
+                    initially=None, table=None, _create_rule=None):
         """Construct a CHECK constraint.
 
         sqltext
@@ -2291,6 +2292,16 @@ class _CreateDropBase(DDLElement):
         self.on = on
         self.bind = bind
 
+    def _create_rule_disable(self, compiler):
+        """Allow disable of _create_rule using a callable.
+        
+        Pass to _create_rule using 
+        util.portable_instancemethod(self._create_rule_disable)
+        to retain serializability.
+        
+        """
+        return False
+
 class CreateTable(_CreateDropBase):
     """Represent a CREATE TABLE statement."""
     
@@ -2332,7 +2343,7 @@ class AddConstraint(_CreateDropBase):
 
     def __init__(self, element, *args, **kw):
         super(AddConstraint, self).__init__(element, *args, **kw)
-        element._create_rule = lambda compiler: False
+        element._create_rule = util.portable_instancemethod(self._create_rule_disable)
         
 class DropConstraint(_CreateDropBase):
     """Represent an ALTER TABLE DROP CONSTRAINT statement."""
@@ -2342,7 +2353,7 @@ class DropConstraint(_CreateDropBase):
     def __init__(self, element, cascade=False, **kw):
         self.cascade = cascade
         super(DropConstraint, self).__init__(element, **kw)
-        element._create_rule = lambda compiler: False
+        element._create_rule = util.portable_instancemethod(self._create_rule_disable)
 
 def _bind_or_error(schemaitem, msg=None):
     bind = schemaitem.bind
index a52ffe59c6f381df45d78499ad9cd27365968303..356edecd85ca746bf03d4c36365dc53067b0071f 100644 (file)
@@ -1038,22 +1038,36 @@ class SchemaType(object):
         self.schema = kw.pop('schema', None)
         self.metadata = kw.pop('metadata', None)
         if self.metadata:
-            self.metadata.append_ddl_listener('before-create',
-                                                self._on_metadata_create)
-            self.metadata.append_ddl_listener('after-drop',
-                                                self._on_metadata_drop)
+            self.metadata.append_ddl_listener(
+                                    'before-create',
+                                    util.portable_instancemethod(self._on_metadata_create)
+                                    )
+            self.metadata.append_ddl_listener(
+                                    'after-drop',
+                                    util.portable_instancemethod(self._on_metadata_drop)
+                                    )
             
     def _set_parent(self, column):
-        column._on_table_attach(self._set_table)
+        column._on_table_attach(util.portable_instancemethod(self._set_table))
         
     def _set_table(self, table, column):
-        table.append_ddl_listener('before-create', self._on_table_create)
-        table.append_ddl_listener('after-drop', self._on_table_drop)
+        table.append_ddl_listener(
+                            'before-create', 
+                            util.portable_instancemethod(self._on_table_create)
+                            )
+        table.append_ddl_listener(
+                            'after-drop', 
+                            util.portable_instancemethod(self._on_table_drop)
+                            )
         if self.metadata is None:
-            table.metadata.append_ddl_listener('before-create',
-                                                self._on_metadata_create)
-            table.metadata.append_ddl_listener('after-drop',
-                                                self._on_metadata_drop)
+            table.metadata.append_ddl_listener(
+                            'before-create',
+                            util.portable_instancemethod(self._on_metadata_create)
+                            )
+            table.metadata.append_ddl_listener(
+                            'after-drop',
+                            util.portable_instancemethod(self._on_metadata_drop)
+                            )
     
     @property
     def bind(self):
@@ -1178,19 +1192,20 @@ class Enum(String, SchemaType):
                         assert_unicode=assert_unicode
                         )
         SchemaType.__init__(self, **kw)
+
+    def _should_create_constraint(self, compiler):
+        return not self.native_enum or \
+                    not compiler.dialect.supports_native_enum
     
     def _set_table(self, table, column):
         if self.native_enum:
             SchemaType._set_table(self, table, column)
             
-        def should_create_constraint(compiler):
-            return not self.native_enum or \
-                        not compiler.dialect.supports_native_enum
 
         e = schema.CheckConstraint(
                         column.in_(self.enums),
                         name=self.name,
-                        _create_rule=should_create_constraint
+                        _create_rule=util.portable_instancemethod(self._should_create_constraint)
                     )
         table.append_constraint(e)
         
@@ -1314,18 +1329,18 @@ class Boolean(TypeEngine, SchemaType):
         """
         self.create_constraint = create_constraint
         self.name = name
+    
+    def _should_create_constraint(self, compiler):
+        return not compiler.dialect.supports_native_boolean
         
     def _set_table(self, table, column):
         if not self.create_constraint:
             return
             
-        def should_create_constraint(compiler):
-            return not compiler.dialect.supports_native_boolean
-
         e = schema.CheckConstraint(
                         column.in_([0, 1]),
                         name=self.name,
-                        _create_rule=should_create_constraint
+                        _create_rule=util.portable_instancemethod(self._should_create_constraint)
                     )
         table.append_constraint(e)
     
index 07b8269a917a4f44e55b3ca8269f1172d9a00ac8..873243de166a49beadd6cb13797d8390644ad509 100644 (file)
@@ -453,6 +453,18 @@ def unbound_method_to_callable(func_or_cls):
     else:
         return func_or_cls
 
+class portable_instancemethod(object):
+    """Turn an instancemethod into a (parent, name) pair
+    to produce a serializable callable.
+    
+    """
+    def __init__(self, meth):
+        self.target = meth.im_self
+        self.name = meth.__name__
+
+    def __call__(self, *arg, **kw):
+        return getattr(self.target, self.name)(*arg, **kw)
+        
 def class_hierarchy(cls):
     """Return an unordered sequence of all classes related to cls.
 
index 55dcd3484ea41170560df6d1b18fc17a2ef7c83f..97f5190d4ce6c5c6a4e8c0bb7ebd5c919a25d825 100644 (file)
@@ -316,10 +316,36 @@ class ConstraintCompilationTest(TestBase, AssertsCompiledSQL):
                 Column('b', Integer)
         )
         
-        constraint = CheckConstraint('a < b',name="my_test_constraint", deferrable=True,initially='DEFERRED', table=t)
+        constraint = CheckConstraint('a < b',name="my_test_constraint",
+                                        deferrable=True,initially='DEFERRED', table=t)
+
+        
+        # before we create an AddConstraint,
+        # the CONSTRAINT comes out inline
+        self.assert_compile(
+            schema.CreateTable(t),
+            "CREATE TABLE tbl ("
+            "a INTEGER, "
+            "b INTEGER, "
+            "CONSTRAINT my_test_constraint CHECK (a < b) DEFERRABLE INITIALLY DEFERRED"
+            ")"
+        )
+
         self.assert_compile(
             schema.AddConstraint(constraint),
-            "ALTER TABLE tbl ADD CONSTRAINT my_test_constraint CHECK (a < b) DEFERRABLE INITIALLY DEFERRED"
+            "ALTER TABLE tbl ADD CONSTRAINT my_test_constraint "
+                    "CHECK (a < b) DEFERRABLE INITIALLY DEFERRED"
+        )
+
+        # once we make an AddConstraint,
+        # inline compilation of the CONSTRAINT
+        # is disabled
+        self.assert_compile(
+            schema.CreateTable(t),
+            "CREATE TABLE tbl ("
+            "a INTEGER, "
+            "b INTEGER"
+            ")"
         )
 
         self.assert_compile(
index 77fdcf6e684f6446aff00095d403b66c58edb2fc..cd1430f4cfa767e618e6f5dc5f271d5ff293192f 100644 (file)
@@ -10,6 +10,7 @@ import sqlalchemy.engine.url as url
 from sqlalchemy.databases import *
 from sqlalchemy.test.schema import Table, Column
 from sqlalchemy.test import *
+from sqlalchemy.test.util import picklers
 
 
 class AdaptTest(TestBase):
@@ -77,8 +78,36 @@ class TypeAffinityTest(TestBase):
             (PickleType(), PickleType(), True),
         ]:
             eq_(t1._compare_type_affinity(t2), comp, "%s %s" % (t1, t2))
-        
-    
+
+class PickleMetadataTest(TestBase):
+    def testmeta(self):
+        for loads, dumps in picklers():
+            column_types = [
+                Column('Boo', Boolean()),
+                Column('Str', String()),
+                Column('Tex', Text()),
+                Column('Uni', Unicode()),
+                Column('Int', Integer()),
+                Column('Sma', SmallInteger()),
+                Column('Big', BigInteger()),
+                Column('Num', Numeric()),
+                Column('Flo', Float()),
+                Column('Dat', DateTime()),
+                Column('Dat', Date()),
+                Column('Tim', Time()),
+                Column('Lar', LargeBinary()),
+                Column('Pic', PickleType()),
+                Column('Int', Interval()),
+                Column('Enu', Enum('x','y','z', name="somename")),
+            ]
+            for column_type in column_types:
+                #print column_type
+                meta = MetaData()
+                Table('foo', meta, column_type)
+                ct = loads(dumps(column_type))
+                mt = loads(dumps(meta))
+                
+
 class UserDefinedTest(TestBase):
     """tests user-defined types."""