if hasattr(autogen_context.migration_context, 'impl'):
impl_rt = autogen_context.migration_context.impl.render_type(
type_, autogen_context)
+ else:
+ impl_rt = None
mod = type(type_).__module__
imports = autogen_context.imports
else:
return "%s.%r" % (dname, type_)
elif mod.startswith("sqlalchemy."):
- prefix = _sqlalchemy_autogenerate_prefix(autogen_context)
- return "%s%r" % (prefix, type_)
+ if '_render_%s_type' % type_.__visit_name__ in globals():
+ fn = globals()['_render_%s_type' % type_.__visit_name__]
+ return fn(type_, autogen_context)
+ else:
+ prefix = _sqlalchemy_autogenerate_prefix(autogen_context)
+ return "%s%r" % (prefix, type_)
else:
prefix = _user_autogenerate_prefix(autogen_context, type_)
return "%s%r" % (prefix, type_)
+def _render_ARRAY_type(type_, autogen_context):
+ return _render_type_w_subtype(
+ type_, autogen_context, 'item_type', r'(.+?\()'
+ )
+
+
+def _render_type_w_subtype(type_, autogen_context, attrname, regexp):
+ outer_repr = repr(type_)
+ inner_type = getattr(type_, attrname, None)
+ if inner_type is None:
+ return False
+
+ inner_repr = repr(inner_type)
+
+ inner_repr = re.sub(r'([\(\)])', r'\\\1', inner_repr)
+ sub_type = _repr_type(getattr(type_, attrname), autogen_context)
+ outer_type = re.sub(
+ regexp + inner_repr,
+ r"\1%s" % sub_type, outer_repr)
+
+ mod = type(type_).__module__
+ if mod.startswith("sqlalchemy.dialects"):
+ dname = re.match(r"sqlalchemy\.dialects\.(\w+)", mod).group(1)
+ return "%s.%s" % (dname, outer_type)
+ elif mod.startswith("sqlalchemy"):
+ prefix = _sqlalchemy_autogenerate_prefix(autogen_context)
+ return "%s%s" % (prefix, outer_type)
+ else:
+ return None
+
_constraint_renderers = util.Dispatcher()
from ..operations import ops
from ..util import sqla_compat
from ..operations import schemaobj
-from ..autogenerate import render
import logging
metadata_indexes.discard(idx)
def render_type(self, type_, autogen_context):
+ mod = type(type_).__module__
+ if not mod.startswith("sqlalchemy.dialects.postgresql"):
+ return False
+
if hasattr(self, '_render_%s_type' % type_.__visit_name__):
meth = getattr(self, '_render_%s_type' % type_.__visit_name__)
return meth(type_, autogen_context)
return False
- def _render_type_w_subtype(self, type_, autogen_context, attrname, regexp):
- outer_repr = repr(type_)
- inner_type = getattr(type_, attrname, None)
- if inner_type is None:
- return False
-
- inner_repr = repr(inner_type)
-
- inner_repr = re.sub(r'([\(\)])', r'\\\1', inner_repr)
- sub_type = render._repr_type(getattr(type_, attrname), autogen_context)
- outer_type = re.sub(
- regexp + inner_repr,
- r"\1%s" % sub_type, outer_repr)
- return "%s.%s" % ("postgresql", outer_type)
-
def _render_ARRAY_type(self, type_, autogen_context):
- return self._render_type_w_subtype(
+ return render._render_type_w_subtype(
type_, autogen_context, 'item_type', r'(.+?\()'
)
def _render_JSON_type(self, type_, autogen_context):
- return self._render_type_w_subtype(
+ return render._render_type_w_subtype(
type_, autogen_context, 'astext_type', r'(.+?\(.*astext_type=)'
)
def _render_JSONB_type(self, type_, autogen_context):
- return self._render_type_w_subtype(
+ return render._render_type_w_subtype(
type_, autogen_context, 'astext_type', r'(.+?\(.*astext_type=)'
)
--- /dev/null
+.. change::
+ :tags: bug, autogenerate
+ :tickets: 442
+
+ Fixed bug expanding upon the fix for
+ :ticket:`85` which adds the correct module import to the
+ "inner" type for an ``ARRAY`` type, the fix now accommodates for the
+ generic ``sqlalchemy.types.ARRAY`` type added in SQLAlchemy 1.1,
+ rendering the inner type correctly regardless of whether or not the
+ Postgresql dialect is present.
\ No newline at end of file
from sqlalchemy import MetaData, Column, Table, String, \
Numeric, CHAR, ForeignKey, DATETIME, Integer, BigInteger, \
CheckConstraint, Unicode, Enum, cast,\
- UniqueConstraint, Boolean, ForeignKeyConstraint,\
+ DateTime, UniqueConstraint, Boolean, ForeignKeyConstraint,\
PrimaryKeyConstraint, Index, func, text, DefaultClause
from sqlalchemy.types import TIMESTAMP
+from sqlalchemy import types
from sqlalchemy.types import UserDefinedType
from sqlalchemy.engine.default import DefaultDialect
from sqlalchemy.sql import and_, column, literal_column, false, table
"sa.Integer()"
)
+ @config.requirements.sqlalchemy_110
+ def test_generic_array_type(self):
+
+ eq_ignore_whitespace(
+ autogenerate.render._repr_type(
+ types.ARRAY(Integer), self.autogen_context),
+ "sa.ARRAY(sa.Integer())"
+ )
+
+ eq_ignore_whitespace(
+ autogenerate.render._repr_type(
+ types.ARRAY(DateTime(timezone=True)), self.autogen_context),
+ "sa.ARRAY(sa.DateTime(timezone=True))"
+ )
+
+ @config.requirements.sqlalchemy_110
+ def test_render_array_no_context(self):
+ uo = ops.UpgradeOps(ops=[
+ ops.CreateTableOp(
+ "sometable",
+ [Column('x', types.ARRAY(Integer))]
+ )
+ ])
+
+ eq_(
+ autogenerate.render_python_code(uo),
+ "# ### commands auto generated by Alembic - please adjust! ###\n"
+ " op.create_table('sometable',\n"
+ " sa.Column('x', sa.ARRAY(sa.Integer()), nullable=True)\n"
+ " )\n"
+ " # ### end Alembic commands ###"
+ )
+
def test_repr_custom_type_w_sqla_prefix(self):
self.autogen_context.opts['user_module_prefix'] = None
String, Interval, Sequence, Numeric, BigInteger, Float, Numeric
from sqlalchemy.dialects.postgresql import ARRAY, UUID, BYTEA
from sqlalchemy.engine.reflection import Inspector
+from sqlalchemy import types
from alembic.operations import Operations
from sqlalchemy.sql import table, column
from alembic.autogenerate.compare import \
)
@config.requirements.sqlalchemy_09
- def test_array_type(self):
+ def test_postgresql_array_type(self):
eq_ignore_whitespace(
autogenerate.render._repr_type(
assert 'from sqlalchemy.dialects import postgresql' in \
self.autogen_context.imports
+ @config.requirements.sqlalchemy_110
+ def test_generic_array_type(self):
+
+ eq_ignore_whitespace(
+ autogenerate.render._repr_type(
+ types.ARRAY(Integer), self.autogen_context),
+ "sa.ARRAY(sa.Integer())"
+ )
+
+ eq_ignore_whitespace(
+ autogenerate.render._repr_type(
+ types.ARRAY(DateTime(timezone=True)), self.autogen_context),
+ "sa.ARRAY(sa.DateTime(timezone=True))"
+ )
+
+ assert 'from sqlalchemy.dialects import postgresql' not in \
+ self.autogen_context.imports
+
+ eq_ignore_whitespace(
+ autogenerate.render._repr_type(
+ types.ARRAY(BYTEA, as_tuple=True, dimensions=2),
+ self.autogen_context),
+ "sa.ARRAY(postgresql.BYTEA(), as_tuple=True, dimensions=2)"
+ )
+
+ assert 'from sqlalchemy.dialects import postgresql' in \
+ self.autogen_context.imports
+
@config.requirements.sqlalchemy_09
def test_array_type_user_defined_inner(self):
def repr_type(typestring, object_, autogen_context):