]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
overhaul to types system, decoupled base type and engine-specific type into a compose...
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 9 Mar 2006 18:30:27 +0000 (18:30 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 9 Mar 2006 18:30:27 +0000 (18:30 +0000)
lib/sqlalchemy/types.py
test/testtypes.py

index fcd416398d1693cd751cba0e7581dccf2bd3065e..f8b2b490f3629d7394bbf1cbca4d5e2dc9785445 100644 (file)
@@ -14,52 +14,48 @@ __all__ = [ 'TypeEngine', 'TypeDecorator', 'NullTypeEngine',
 import sqlalchemy.util as util
 
 class TypeEngine(object):
+    basetypes = []
+    def __init__(self, *args, **kwargs):
+        pass
+    def _get_impl(self):
+        if hasattr(self, '_impl'):
+            return self._impl
+        else:
+            return NULLTYPE
+    def _set_impl(self, impl):
+        self._impl = impl
+    impl = property(_get_impl, _set_impl)
     def get_col_spec(self):
-        raise NotImplementedError()
+        return self.impl.get_col_spec()
     def convert_bind_param(self, value, engine):
-        raise NotImplementedError()
+        return self.impl.convert_bind_param(value, engine)
     def convert_result_value(self, value, engine):
-        raise NotImplementedError()
-    def adapt(self, typeobj):
-        """given a class that is a subclass of this TypeEngine's class, produces a new
-        instance of that class with an equivalent state to this TypeEngine.  The given
-        class is a database-specific subclass which is obtained via a lookup dictionary,
-        mapped against the class returned by the class_to_adapt() method."""
-        return typeobj()
+        return self.impl.convert_result_value(value, engine)
+    def set_impl(self, impltype):
+        self.impl = impltype(**self.get_constructor_args())
+    def get_constructor_args(self):
+        return {}
     def adapt_args(self):
-        """Returns an instance of this TypeEngine instance's class, adapted according
-        to the constructor arguments of this TypeEngine.  Default return value is 
-        just this object instance."""
         return self
-    def class_to_adapt(self):
-        """returns the class that should be sent to the adapt() method.  This class
-        will be used to lookup an approprate database-specific subclass."""
-        return self.__class__
-#    def __repr__(self):
- #       return util.generic_repr(self)
-        
+            
 def adapt_type(typeobj, colspecs):
-    """given a generic type from this package, and a dictionary of 
-    "conversion" specs from a DB-specific package, adapts the type
-    to a correctly-configured type instance from the DB-specific package."""
-    if type(typeobj) is type:
+    if isinstance(typeobj, type):
         typeobj = typeobj()
-    # if the type is not a base type, i.e. not from our module, or its Null, 
-    # we return the type as is
-    if (typeobj.__module__ != 'sqlalchemy.types' or typeobj.__class__ is NullTypeEngine) and not isinstance(typeobj, TypeDecorator):
-        return typeobj
-    typeobj = typeobj.adapt_args()
-    t = typeobj.class_to_adapt()
-    for t in t.__mro__[0:-1]:
+    t2 = typeobj.adapt_args()
+    for t in t2.__class__.__mro__[0:-1]:
         try:
-            return typeobj.adapt(colspecs[t])
-        except KeyError, e:
+            impltype = colspecs[t]
+            break
+        except KeyError:
             pass
-    return typeobj.adapt(typeobj.__class__)
+    else:
+        # couldnt adapt...raise exception ?
+        return typeobj
+    typeobj.set_impl(impltype)
+    typeobj.impl.impl = NULLTYPE
+    return typeobj
     
 class NullTypeEngine(TypeEngine):
-    def __init__(self, *args, **kwargs):
-        pass
     def get_col_spec(self):
         raise NotImplementedError()
     def convert_bind_param(self, value, engine):
@@ -68,32 +64,15 @@ class NullTypeEngine(TypeEngine):
         return value
 
 class TypeDecorator(object):
-    def get_col_spec(self):
-        return self.extended.get_col_spec()
-    def adapt(self, typeobj):
-        if self.extended is self:
-            t = self.__class__.__mro__[2]
-            self.extended = t.adapt(self, typeobj)
-        else:
-            self.extended = self.extended.adapt(typeobj)
-        return self
-    def adapt_args(self):
-        t = self.__class__.__mro__[2]
-        self.extended = t.adapt_args(self)
-        return self
-    def class_to_adapt(self):
-        return self.extended.__class__
+    """TypeDecorator is deprecated"""
+    pass
     
-class String(NullTypeEngine):
+    
+class String(TypeEngine):
     def __init__(self, length = None):
         self.length = length
-    def adapt(self, typeobj):
-        return typeobj(self.length)
-    def adapt_args(self):
-        if self.length is None:
-            return TEXT()
-        else:
-            return self
+    def get_constructor_args(self):
+        return {'length':self.length}
     def convert_bind_param(self, value, engine):
         if not engine.convert_unicode or value is None or not isinstance(value, unicode):
             return value
@@ -104,10 +83,13 @@ class String(NullTypeEngine):
             return value
         else:
             return value.decode('utf-8')
-
-class Unicode(TypeDecorator,String):
-    def __init__(self, length=None):
-        String.__init__(self, length)
+    def adapt_args(self):
+        if self.length is None:
+            return TEXT()
+        else:
+            return self
+            
+class Unicode(String):
     def convert_bind_param(self, value, engine):
          if isinstance(value, unicode):
               return value.encode('utf-8')
@@ -119,49 +101,48 @@ class Unicode(TypeDecorator,String):
          else:
              return value
               
-class Integer(NullTypeEngine):
+class Integer(TypeEngine):
     """integer datatype"""
-    # TODO: do string bind params need int(value) performed before sending ?  
-    # seems to be not needed with SQLite, Postgres
     pass
-
-class Smallinteger(Integer):
+    
+class SmallInteger(Integer):
     """ smallint datatype """
     pass
-
-class Numeric(NullTypeEngine):
+Smallinteger = SmallInteger
+  
+class Numeric(TypeEngine):
     def __init__(self, precision = 10, length = 2):
         self.precision = precision
         self.length = length
-    def adapt(self, typeobj):
-        return typeobj(self.precision, self.length)
+    def get_constructor_args(self):
+        return {'precision':self.precision, 'length':self.length}
 
-class Float(NullTypeEngine):
+class Float(TypeEngine):
     def __init__(self, precision = 10):
         self.precision = precision
-    def adapt(self, typeobj):
-        return typeobj(self.precision)
+    def get_constructor_args(self):
+        return {'precision':self.precision}
 
-class DateTime(NullTypeEngine):
+class DateTime(TypeEngine):
     pass
 
-class Date(NullTypeEngine):
+class Date(TypeEngine):
     pass
 
-class Time(NullTypeEngine):
+class Time(TypeEngine):
     pass
 
-class Binary(NullTypeEngine):
+class Binary(TypeEngine):
     def __init__(self, length=None):
         self.length = length
     def convert_bind_param(self, value, engine):
         return engine.dbapi().Binary(value)
     def convert_result_value(self, value, engine):
         return value
-    def adapt(self, typeobj):
-        return typeobj(self.length)
+    def get_constructor_args(self):
+        return {'length':self.length}
 
-class Boolean(NullTypeEngine):
+class Boolean(TypeEngine):
     pass
 
 class FLOAT(Float):pass
index 4bee6a3949f3270583f17446dc55c5438be2feac..d44c439bdb90b96098aad1873e60da5b64e4e050 100644 (file)
@@ -17,25 +17,31 @@ class MyType(types.TypeEngine):
     def adapt_args(self):
         return self
 
-class MyDecoratedType(types.TypeDecorator, types.String):
+class MyDecoratedType(types.String):
     def convert_bind_param(self, value, engine):
         return "BIND_IN"+ value
     def convert_result_value(self, value, engine):
         return value + "BIND_OUT"
 
+class MyUnicodeType(types.Unicode):
+    def convert_bind_param(self, value, engine):
+        return "UNI_BIND_IN"+ value
+    def convert_result_value(self, value, engine):
+        return value + "UNI_BIND_OUT"
+
 class OverrideTest(PersistTest):
     """tests user-defined types, including a full type as well as a TypeDecorator"""
 
     def testprocessing(self):
 
         global users
-        users.insert().execute(user_id = 2, goofy = 'jack', goofy2='jack', goofy3='jack')
-        users.insert().execute(user_id = 3, goofy = 'lala', goofy2='lala', goofy3='lala')
-        users.insert().execute(user_id = 4, goofy = 'fred', goofy2='fred', goofy3='fred')
+        users.insert().execute(user_id = 2, goofy = 'jack', goofy2='jack', goofy3='jack', goofy4='jack')
+        users.insert().execute(user_id = 3, goofy = 'lala', goofy2='lala', goofy3='lala', goofy4='lala')
+        users.insert().execute(user_id = 4, goofy = 'fred', goofy2='fred', goofy3='fred', goofy4='fred')
         
         l = users.select().execute().fetchall()
         print repr(l)
-        self.assert_(l == [(2, 'BIND_INjackBIND_OUT', 'BIND_INjackBIND_OUT', 'BIND_INjackBIND_OUT'), (3, 'BIND_INlalaBIND_OUT', 'BIND_INlalaBIND_OUT', 'BIND_INlalaBIND_OUT'), (4, 'BIND_INfredBIND_OUT', 'BIND_INfredBIND_OUT', 'BIND_INfredBIND_OUT')])
+        self.assert_(l == [(2, 'BIND_INjackBIND_OUT', 'BIND_INjackBIND_OUT', 'BIND_INjackBIND_OUT', u'UNI_BIND_INjackUNI_BIND_OUT'), (3, 'BIND_INlalaBIND_OUT', 'BIND_INlalaBIND_OUT', 'BIND_INlalaBIND_OUT', u'UNI_BIND_INlalaUNI_BIND_OUT'), (4, 'BIND_INfredBIND_OUT', 'BIND_INfredBIND_OUT', 'BIND_INfredBIND_OUT', u'UNI_BIND_INfredUNI_BIND_OUT')])
 
     def setUpAll(self):
         global users
@@ -49,6 +55,9 @@ class OverrideTest(PersistTest):
             
             # decorated type without an argument, it will adapt_args to TEXT
             Column('goofy3', MyDecoratedType, nullable = False),
+
+            Column('goofy4', MyUnicodeType, nullable = False),
+
         )
         
         users.create()