From: Mike Bayer Date: Sun, 23 Oct 2011 17:05:56 +0000 (-0400) Subject: - Added accessor to types called "python_type", X-Git-Tag: rel_0_7_4~75 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a708d0fbce3b7d8a9fb575bab5229a9b6b5f8f3a;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Added accessor to types called "python_type", returns the rudimentary Python type object for a particular TypeEngine instance, if known, else raises NotImplementedError. [ticket:77] --- diff --git a/CHANGES b/CHANGES index 7dea5bae31..52ba8cdc94 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,12 @@ CHANGES - Cls.column.collate("some collation") now works. [ticket:1776] Also in 0.6.9 +- sql + - Added accessor to types called "python_type", + returns the rudimentary Python type object + for a particular TypeEngine instance, if known, + else raises NotImplementedError. [ticket:77] + 0.7.3 ===== - general diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py index 5c9ea99c3b..e58313466a 100644 --- a/lib/sqlalchemy/types.py +++ b/lib/sqlalchemy/types.py @@ -112,6 +112,25 @@ class TypeEngine(AbstractType): """ return None + @property + def python_type(self): + """Return the Python type object expected to be returned + by instances of this type, if known. + + Basically, for those types which enforce a return type, + or are known across the board to do such for all common + DBAPIs (like ``int`` for example), will return that type. + + If a return type is not defined, raises + ``NotImplementedError``. + + Note that any type also accommodates NULL in SQL which + means you can also get back ``None`` from any type + in practice. + + """ + raise NotImplementedError() + def with_variant(self, type_, dialect_name): """Produce a new type object that will utilize the given type when applied to the dialect of the given name. @@ -1069,6 +1088,13 @@ class String(Concatenable, TypeEngine): else: return None + @property + def python_type(self): + if self.convert_unicode: + return unicode + else: + return str + def get_dbapi_type(self, dbapi): return dbapi.STRING @@ -1176,6 +1202,10 @@ class Integer(_DateAffinity, TypeEngine): def get_dbapi_type(self, dbapi): return dbapi.NUMBER + @property + def python_type(self): + return int + @util.memoized_property def _expression_adaptations(self): # TODO: need a dictionary object that will @@ -1311,6 +1341,13 @@ class Numeric(_DateAffinity, TypeEngine): def get_dbapi_type(self, dbapi): return dbapi.NUMBER + @property + def python_type(self): + if self.asdecimal: + return decimal.Decimal + else: + return float + def bind_processor(self, dialect): if dialect.supports_native_decimal: return None @@ -1456,6 +1493,10 @@ class DateTime(_DateAffinity, TypeEngine): def get_dbapi_type(self, dbapi): return dbapi.DATETIME + @property + def python_type(self): + return dt.datetime + @util.memoized_property def _expression_adaptations(self): return { @@ -1477,6 +1518,10 @@ class Date(_DateAffinity,TypeEngine): def get_dbapi_type(self, dbapi): return dbapi.DATETIME + @property + def python_type(self): + return dt.date + @util.memoized_property def _expression_adaptations(self): return { @@ -1513,6 +1558,10 @@ class Time(_DateAffinity,TypeEngine): def get_dbapi_type(self, dbapi): return dbapi.DATETIME + @property + def python_type(self): + return dt.time + @util.memoized_property def _expression_adaptations(self): return { @@ -1533,6 +1582,14 @@ class _Binary(TypeEngine): def __init__(self, length=None): self.length = length + @property + def python_type(self): + #Py3K + #return bytes + # Py2K + return str + # end Py2K + # Python 3 - sqlite3 doesn't need the `Binary` conversion # here, though pg8000 does to indicate "bytea" def bind_processor(self, dialect): @@ -1948,6 +2005,10 @@ class Boolean(TypeEngine, SchemaType): ) table.append_constraint(e) + @property + def python_type(self): + return bool + def bind_processor(self, dialect): if dialect.supports_native_boolean: return None @@ -2014,6 +2075,10 @@ class Interval(_DateAffinity, TypeDecorator): day_precision=self.day_precision, **kw) + @property + def python_type(self): + return dt.timedelta + def bind_processor(self, dialect): impl_processor = self.impl.bind_processor(dialect) epoch = self.epoch diff --git a/test/sql/test_types.py b/test/sql/test_types.py index 77d6bb5063..50cb0ba066 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -130,6 +130,32 @@ class AdaptTest(fixtures.TestBase): getattr(t2, k) == t1.__dict__[k] or \ t1.__dict__[k] is None + def test_python_type(self): + eq_(types.Integer().python_type, int) + eq_(types.Numeric().python_type, decimal.Decimal) + eq_(types.Numeric(asdecimal=False).python_type, float) + # Py3K + #eq_(types.LargeBinary().python_type, bytes) + # Py2K + eq_(types.LargeBinary().python_type, str) + # end Py2K + eq_(types.Float().python_type, float) + eq_(types.Interval().python_type, datetime.timedelta) + eq_(types.Date().python_type, datetime.date) + eq_(types.DateTime().python_type, datetime.datetime) + # Py3K + #eq_(types.String().python_type, unicode) + # Py2K + eq_(types.String().python_type, str) + # end Py2K + eq_(types.Unicode().python_type, unicode) + eq_(types.String(convert_unicode=True).python_type, unicode) + + assert_raises( + NotImplementedError, + lambda: types.TypeEngine().python_type + ) + @testing.uses_deprecated() def test_repr(self): for typ in self._all_types():