diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-06-24 16:48:04 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-06-24 16:54:27 -0400 |
commit | 69588e424a8e0fa2b367851219f2ed1f634e8ba2 (patch) | |
tree | 1758b7ccd18214befae5d0d33250e1eb1efc072f | |
parent | 9eaa306423a8876783dc25699530dd2d8d889866 (diff) | |
download | alembic-69588e424a8e0fa2b367851219f2ed1f634e8ba2.tar.gz |
- continue to scale back how much magic we're building into this
for now
-rw-r--r-- | alembic/ddl/base.py | 1 | ||||
-rw-r--r-- | alembic/ddl/mssql.py | 6 | ||||
-rw-r--r-- | alembic/ddl/mysql.py | 7 | ||||
-rw-r--r-- | alembic/operations/__init__.py | 9 | ||||
-rw-r--r-- | alembic/operations/base.py | 61 | ||||
-rw-r--r-- | alembic/operations/ops.py | 78 | ||||
-rw-r--r-- | alembic/operations/schemaobj.py | 2 | ||||
-rw-r--r-- | alembic/operations/toimpl.py | 104 | ||||
-rw-r--r-- | alembic/util/__init__.py | 2 | ||||
-rw-r--r-- | alembic/util/langhelpers.py | 33 | ||||
-rw-r--r-- | tests/test_op.py | 6 |
11 files changed, 137 insertions, 172 deletions
diff --git a/alembic/ddl/base.py b/alembic/ddl/base.py index ed0c6f8..f4a525f 100644 --- a/alembic/ddl/base.py +++ b/alembic/ddl/base.py @@ -3,6 +3,7 @@ import functools from sqlalchemy.ext.compiler import compiles from sqlalchemy.schema import DDLElement, Column from sqlalchemy import Integer +from sqlalchemy import types as sqltypes from .. import util # backwards compat diff --git a/alembic/ddl/mssql.py b/alembic/ddl/mssql.py index f516e9b..f51de33 100644 --- a/alembic/ddl/mssql.py +++ b/alembic/ddl/mssql.py @@ -39,11 +39,10 @@ class MSSQLImpl(DefaultImpl): name=None, type_=None, schema=None, - autoincrement=None, existing_type=None, existing_server_default=None, existing_nullable=None, - existing_autoincrement=None + **kw ): if nullable is not None and existing_type is None: @@ -63,10 +62,9 @@ class MSSQLImpl(DefaultImpl): nullable=nullable, type_=type_, schema=schema, - autoincrement=autoincrement, existing_type=existing_type, existing_nullable=existing_nullable, - existing_autoincrement=existing_autoincrement + **kw ) if server_default is not False: diff --git a/alembic/ddl/mysql.py b/alembic/ddl/mysql.py index fe42145..b1cb324 100644 --- a/alembic/ddl/mysql.py +++ b/alembic/ddl/mysql.py @@ -23,11 +23,12 @@ class MySQLImpl(DefaultImpl): name=None, type_=None, schema=None, - autoincrement=None, existing_type=None, existing_server_default=None, existing_nullable=None, - existing_autoincrement=None + autoincrement=None, + existing_autoincrement=None, + **kw ): if name is not None: self._exec( @@ -284,3 +285,5 @@ def _mysql_drop_constraint(element, compiler, **kw): raise NotImplementedError( "No generic 'DROP CONSTRAINT' in MySQL - " "please specify constraint type") + + diff --git a/alembic/operations/__init__.py b/alembic/operations/__init__.py index 945169c..f01860d 100644 --- a/alembic/operations/__init__.py +++ b/alembic/operations/__init__.py @@ -1,11 +1,2 @@ from .base import Operations, BatchOperations -from .ops import ( - RenameTable, AddColumn, DropColumn, AlterColumn, - ColumnNullable, ColumnDefault, ColumnType, ColumnName) - - -__all__ = ( - 'Operations', 'BatchOperations', 'RenameTable', - 'AddColumn', 'DropColumn', 'AlterColumn', - 'ColumnNullable', 'ColumnDefault', 'ColumnType', 'ColumnName') diff --git a/alembic/operations/base.py b/alembic/operations/base.py index 250d1fe..5162101 100644 --- a/alembic/operations/base.py +++ b/alembic/operations/base.py @@ -7,6 +7,8 @@ from .. import util from ..util import sqla_compat from . import batch from . import schemaobj +from . import ops +from . import toimpl __all__ = ('Operations', 'BatchOperations') @@ -234,18 +236,17 @@ class Operations(object): ) @util._with_legacy_names([('name', 'new_column_name')]) - def alter_column(self, table_name, column_name, - nullable=None, - server_default=False, - new_column_name=None, - type_=None, - autoincrement=None, - existing_type=None, - existing_server_default=False, - existing_nullable=None, - existing_autoincrement=None, - schema=None - ): + def alter_column( + self, table_name, column_name, + nullable=None, + server_default=False, + new_column_name=None, + type_=None, + existing_type=None, + existing_server_default=False, + existing_nullable=None, + schema=None, **kw + ): """Issue an "alter column" instruction using the current migration context. @@ -321,26 +322,30 @@ class Operations(object): :class:`~sqlalchemy.sql.elements.quoted_name` construct. """ - if autoincrement is not None: - # TODO: or we add **kw to AlterColumnOp - cls = mysql.MySQLAlterColumnOp - else: - cls = ops.AlterColumnOp - - op = cls.from_alter_column( - table_name, column_name, - nullable=nullable, - server_default=server_default, - new_column_name=new_column_name, - type_=type_, + + alt = ops.AlterColumnOp( + table_name, column_name, schema=schema, existing_type=existing_type, existing_server_default=existing_server_default, - existing_nullable=existing_nullable, - schema=schema + existing_nullable=existing_nullable ) - # hits a ToImpl object - self.invoke(op) + alt.kw = kw + if new_column_name is not None: + alt.modify_name = new_column_name + if type_ is not None: + alt.modify_type = type_ + if server_default is not False: + alt.modify_server_default = server_default + if nullable is not None: + alt.modify_nullable = nullable + + return self.invoke(alt) + + def invoke(self, operation): + fn = ops.to_impl.dispatch( + operation, self.migration_context.impl.__dialect__) + return fn(self, operation) def f(self, name): """Indicate a string name that has already had a naming convention diff --git a/alembic/operations/ops.py b/alembic/operations/ops.py index 4cc3258..e34ff09 100644 --- a/alembic/operations/ops.py +++ b/alembic/operations/ops.py @@ -1,3 +1,8 @@ +from .. import util + + +to_impl = util.Dispatcher() + class MigrateOperation(object): """base class for migration command and organization objects.""" @@ -131,74 +136,27 @@ class RenameTableOp(AlterTableOp): class AlterColumnOp(AlterTableOp): - def __init__(self, table_name, column_name, schema=None): + def __init__( + self, table_name, column_name, schema=None, + existing_type=None, + existing_server_default=False, + existing_nullable=None + ): super(AlterColumnOp, self).__init__(table_name, schema=schema) self.column_name = column_name + self.existing_type = existing_type + self.existing_server_default = existing_server_default + self.existing_nullable = existing_nullable modify_nullable = None modify_server_default = False modify_name = None modify_type = None + kw = None def dispatch_for(self, handler): return handler.alter_column - @classmethod - def from_alter_column( - cls, table_name, column_name, - nullable=None, - server_default=False, - name=None, - type_=None, - schema=None, - existing_type=None, - existing_server_default=None, - existing_nullable=None): - """Generate an AlterColumn object from a set of 'alter_column' - arguments. - - The 'alter_column' arguments are common throughout Alembic, both - because this is the legacy API for altering columns as well as that - it remains the primary public API from the Operations object. - Internally, we seek to be able to convert between these arguments - and distinct AlterColumnElement operations grouped together. - - """ - - alt = AlterColumnOp( - table_name, column_name, schema=schema, - existing_type=existing_type, - existing_server_default=existing_server_default, - existing_nullable=existing_nullable - ) - - if name is not None: - alt.modify_name = name - if type_ is not None: - alt.modify_type = type_, - if server_default is not False: - alt.modify_server_default = server_default - if nullable is not None: - alt.modify_nullable = nullable - - return alt - - @property - def has_nullable(self): - return self.modify_nullable is not None - - @property - def has_type(self): - return self.modify_type is not None - - @property - def has_server_default(self): - return self.modify_server_default is not False - - @property - def has_name(self): - return self.modify_name is not None - class AddColumnOp(AlterTableOp): @@ -259,9 +217,3 @@ class MigrationScript(OpContainer): """ - -class MigrationDispatch(object): - def handle(self, operation, **kw): - fn = operation.dispatch_for(self) - return fn(operation, **kw) - diff --git a/alembic/operations/schemaobj.py b/alembic/operations/schemaobj.py index 575226a..4e0474e 100644 --- a/alembic/operations/schemaobj.py +++ b/alembic/operations/schemaobj.py @@ -106,7 +106,7 @@ class SchemaObjects(object): ) idx = sa_schema.Index( name, - *[util._textual_index_column(t, n) for n in columns], + *[util.sqla_compat._textual_index_column(t, n) for n in columns], **kw) return idx diff --git a/alembic/operations/toimpl.py b/alembic/operations/toimpl.py index b1a5348..b3688ff 100644 --- a/alembic/operations/toimpl.py +++ b/alembic/operations/toimpl.py @@ -1,50 +1,62 @@ -from .ops import MigrationDispatch +from . import ops +from sqlalchemy import schema as sa_schema -class ToImpl(MigrationDispatch): - """translates from ops objects into commands sent to DefaultImpl.""" - def alter_column(self, operation): - compiler = self.impl.dialect.statement_compiler( - self.impl.dialect, - None +@ops.to_impl.dispatch_for(ops.AlterColumnOp, 'default') +def alter_column(operations, operation): + + compiler = operations.impl.dialect.statement_compiler( + operations.impl.dialect, + None + ) + + existing_type = operation.existing_type + existing_nullable = operation.existing_nullable + existing_server_default = operation.existing_server_default + type_ = operation.modify_type + column_name = operation.column_name + table_name = operation.table_name + schema = operation.schema + server_default = operation.modify_server_default + new_column_name = operation.modify_name + nullable = operation.modify_nullable + + def _count_constraint(constraint): + return not isinstance( + constraint, + sa_schema.PrimaryKeyConstraint) and \ + (not constraint._create_rule or + constraint._create_rule(compiler)) + + if existing_type and type_: + t = operations.schema_obj.table( + table_name, + sa_schema.Column(column_name, existing_type), + schema=schema ) + for constraint in t.constraints: + if _count_constraint(constraint): + operations.impl.drop_constraint(constraint) + + operations.impl.alter_column( + table_name, column_name, + nullable=nullable, + server_default=server_default, + name=new_column_name, + type_=type_, + schema=schema, + existing_type=existing_type, + existing_server_default=existing_server_default, + existing_nullable=existing_nullable, + **operation.kw + ) - def _count_constraint(constraint): - return not isinstance( - constraint, - sa_schema.PrimaryKeyConstraint) and \ - (not constraint._create_rule or - constraint._create_rule(compiler)) - - if existing_type and type_: - t = self.schema_obj.table( - table_name, - sa_schema.Column(column_name, existing_type), - schema=schema - ) - for constraint in t.constraints: - if _count_constraint(constraint): - self.impl.drop_constraint(constraint) - - self.impl.alter_column(table_name, column_name, - nullable=nullable, - server_default=server_default, - name=new_column_name, - type_=type_, - schema=schema, - autoincrement=autoincrement, - existing_type=existing_type, - existing_server_default=existing_server_default, - existing_nullable=existing_nullable, - existing_autoincrement=existing_autoincrement - ) - - if type_: - t = self.schema_obj.table( - table_name, - self.schema_obj.column(column_name, type_), - schema=schema - ) - for constraint in t.constraints: - if _count_constraint(constraint): - self.impl.add_constraint(constraint) + if type_: + t = operations.schema_obj.table( + table_name, + operations.schema_obj.column(column_name, type_), + schema=schema + ) + for constraint in t.constraints: + if _count_constraint(constraint): + operations.impl.add_constraint(constraint) diff --git a/alembic/util/__init__.py b/alembic/util/__init__.py index 6cfc5d3..0fb3b1d 100644 --- a/alembic/util/__init__.py +++ b/alembic/util/__init__.py @@ -1,6 +1,6 @@ from .langhelpers import ( # noqa create_module_class_proxy, asbool, rev_id, to_tuple, memoized_property, - immutabledict, _with_legacy_names) + immutabledict, _with_legacy_names, Dispatcher) from .messaging import ( # noqa write_outstream, status, err, obfuscate_url_pw, warn, msg, format_as_comma) from .pyfiles import ( # noqa diff --git a/alembic/util/langhelpers.py b/alembic/util/langhelpers.py index 5886125..8e2fb90 100644 --- a/alembic/util/langhelpers.py +++ b/alembic/util/langhelpers.py @@ -33,8 +33,16 @@ def create_module_class_proxy(cls, globals_, locals_): for name in attr_names: del globals_[name] + def _add_proxied_method(methname): + if not methname.startswith('_'): + if callable(getattr(cls, methname)): + locals_[methname] = _create_op_proxy(methname) + else: + attr_names.add(methname) + globals_['_install_proxy'] = _install_proxy globals_['_remove_proxy'] = _remove_proxy + globals_['_add_proxied_method'] = _add_proxied_method def _create_op_proxy(name): fn = getattr(cls, name) @@ -86,11 +94,7 @@ def create_module_class_proxy(cls, globals_, locals_): return lcl[name] for methname in dir(cls): - if not methname.startswith('_'): - if callable(getattr(cls, methname)): - locals_[methname] = _create_op_proxy(methname) - else: - attr_names.add(methname) + _add_proxied_method(methname) def asbool(value): @@ -210,14 +214,19 @@ class Dispatcher(object): def __init__(self): self._registry = {} - def dispatch_for(self, fn, target): - assert isinstance(target, type) - assert target not in self._registry - self._registry[target] = fn + def dispatch_for(self, target, qualifier='default'): + def decorate(fn): + assert isinstance(target, type) + assert target not in self._registry + self._registry[(target, qualifier)] = fn + return fn + return decorate - def dispatch(self, obj): + def dispatch(self, obj, qualifier='default'): for spcls in type(obj).__mro__: - if spcls in self._registry: - return self._registry[spcls] + if qualifier != 'default' and (spcls, qualifier) in self._registry: + return self._registry[(spcls, qualifier)] + elif (spcls, 'default') in self._registry: + return self._registry[(spcls, 'default')] else: raise ValueError("no dispatch function for object: %s" % obj) diff --git a/tests/test_op.py b/tests/test_op.py index 5be9aa8..7b61697 100644 --- a/tests/test_op.py +++ b/tests/test_op.py @@ -809,12 +809,6 @@ class OpTest(TestBase): op.drop_constraint("f1", "t1", type_="foreignkey") context.assert_("ALTER TABLE t1 DROP FOREIGN KEY f1") - assert_raises_message( - TypeError, - r"Unknown arguments: badarg\d, badarg\d", - op.alter_column, "t", "c", badarg1="x", badarg2="y" - ) - @config.requirements.fail_before_sqla_084 def test_naming_changes_drop_idx(self): context = op_fixture('mssql') |