from .. import exc
from .. import util
-
# these are back-assigned by sqltypes.
BOOLEANTYPE = None
INTEGERTYPE = None
else:
return self.__class__
+ @util.memoized_property
def _generic_type_affinity(self):
best_camelcase = None
best_uppercase = None
)
and issubclass(t, TypeEngine)
and t is not TypeEngine
+ and t.__name__[0] != "_"
):
if t.__name__.isupper() and not best_uppercase:
best_uppercase = t
return best_camelcase or best_uppercase or NULLTYPE.__class__
- def as_generic(self):
+ def as_generic(self, allow_nulltype=False):
"""
- Return an instance of the generic type corresponding to this type.
+ Return an instance of the generic type corresponding to this type using
+ heuristic rule. The method may be overridden if this heuristic rule is not
+ sufficient.
>>> from sqlalchemy.dialects.mysql import INTEGER
>>> INTEGER(display_width=4).as_generic()
.. versionadded:: 1.4.0b2
"""
+ from sqlalchemy import Enum
- return util.constructor_copy(self, self._generic_type_affinity())
+ if isinstance(self, Enum):
+ if hasattr(self, "enums"):
+ args = self.enums
+ else:
+ raise NotImplementedError(
+ "TypeEngine.as_generic() heuristic "
+ "is undefined for types that inherit Enum but do not have "
+ "an `enums` attribute."
+ )
+ else:
+ args = ()
+
+ if (
+ not allow_nulltype
+ and self._generic_type_affinity == NULLTYPE.__class__
+ ):
+ raise NotImplementedError(
+ "Default TypeEngine.as_generic() "
+ "heuristic method was unsuccessful for {}. A custom "
+ "as_generic() method must be implemented for this "
+ "type class.".format(
+ self.__class__.__module__ + "." + self.__class__.__name__
+ )
+ )
+
+ return util.constructor_copy(self, self._generic_type_affinity, *args)
+
+ @classmethod
+ def _uses_as_generic_heuristic(cls):
+ return cls.as_generic == TypeEngine.as_generic
def dialect_impl(self, dialect):
"""Return a dialect-specific implementation for this
from sqlalchemy import Unicode
from sqlalchemy import util
from sqlalchemy import VARCHAR
+import sqlalchemy.dialects.mysql as mysql
import sqlalchemy.dialects.postgresql as pg
from sqlalchemy.engine import default
from sqlalchemy.schema import AddConstraint
from sqlalchemy.sql import sqltypes
from sqlalchemy.sql import table
from sqlalchemy.sql import visitors
+from sqlalchemy.sql.sqltypes import TypeEngine
from sqlalchemy.testing import assert_raises
from sqlalchemy.testing import assert_raises_message
from sqlalchemy.testing import AssertsCompiledSQL
(DATE(), Date()),
(pg.JSON(), sa.JSON()),
(pg.ARRAY(sa.String), sa.ARRAY(sa.String)),
+ (Enum("a", "b", "c"), Enum("a", "b", "c")),
+ (pg.ENUM("a", "b", "c"), Enum("a", "b", "c")),
+ (mysql.ENUM("a", "b", "c"), Enum("a", "b", "c")),
)
def test_as_generic(self, t1, t2):
assert repr(t1.as_generic()) == repr(t2)
else:
t1 = type_()
- t1.as_generic()
+ try:
+ gentype = t1.as_generic()
+ except NotImplementedError as e:
+ pass
+ else:
+ if t1.__class__._uses_as_generic_heuristic():
+ assert isinstance(t1, gentype.__class__)
+
+ assert isinstance(gentype, TypeEngine)
+
+ @testing.combinations(*[(t,) for t in _all_types(omit_special_types=True)])
+ def test_as_generic_all_types_allow_nulltype(self, type_):
+ if issubclass(type_, ARRAY):
+ t1 = type_(String)
+ else:
+ t1 = type_()
+
+ # The `allow_nulltype` argument may not be available in custom
+ # implementations of as_generic() which override the
+ # TypeEngine.as_generic heuristic.
+ if t1.__class__._uses_as_generic_heuristic():
+ gentype = t1.as_generic(allow_nulltype=True)
+ else:
+ gentype = t1.as_generic()
+
+ if isinstance(gentype, types.NULLTYPE.__class__):
+ return
+
+ if t1.__class__._uses_as_generic_heuristic():
+ assert isinstance(t1, gentype.__class__)
+
+ assert isinstance(gentype, TypeEngine)
class PickleTypesTest(fixtures.TestBase):