- move to 0.5.0 as we are making some slight naming changes

- add a generalized approach for renamed kw args, accepting the old
ones using a specialized decorator
- change "tablename" to "table_name" for create_index, drop_index,
drop_constraint
- change "name" to "new_column_name" for alter_column
#104
This commit is contained in:
Mike Bayer
2013-02-11 14:37:59 -05:00
parent cc6a13c4f4
commit 8cdd16e401
7 changed files with 164 additions and 55 deletions

View File

@@ -1,6 +1,6 @@
from os import path
__version__ = '0.4.3'
__version__ = '0.5.0'
package_dir = path.abspath(path.dirname(__file__))

View File

@@ -1,7 +1,7 @@
from alembic import util
from alembic.ddl import impl
from sqlalchemy.types import NULLTYPE, Integer
from sqlalchemy import schema as sa_schema, sql
from sqlalchemy import schema as sa_schema
from contextlib import contextmanager
import alembic
@@ -162,10 +162,11 @@ class Operations(object):
schema=schema
)
@util._with_legacy_names([('name', 'new_column_name')])
def alter_column(self, table_name, column_name,
nullable=None,
server_default=False,
name=None,
new_column_name=None,
type_=None,
autoincrement=None,
existing_type=None,
@@ -210,8 +211,14 @@ class Operations(object):
or :class:`~sqlalchemy.schema.DefaultClause` to indicate
an alteration to the column's default value.
Set to ``None`` to have the default removed.
:param name: Optional; specify a string name here to
:param new_column_name: Optional; specify a string name here to
indicate the new name within a column rename operation.
.. versionchanged:: 0.5.0
The ``name`` parameter is now named ``new_column_name``.
The old name will continue to function for backwards
compatibility.
:param ``type_``: Optional; a :class:`~sqlalchemy.types.TypeEngine`
type object to specify a change to the column's type.
For SQLAlchemy types that also indicate a constraint (i.e.
@@ -267,7 +274,7 @@ class Operations(object):
self.impl.alter_column(table_name, column_name,
nullable=nullable,
server_default=server_default,
name=name,
name=new_column_name,
type_=type_,
schema=schema,
autoincrement=autoincrement,
@@ -592,7 +599,7 @@ class Operations(object):
self._table(name, **kw)
)
def create_index(self, name, tablename, columns, schema=None, **kw):
def create_index(self, name, table_name, columns, schema=None, **kw):
"""Issue a "create index" instruction using the current
migration context.
@@ -602,7 +609,13 @@ class Operations(object):
op.create_index('ik_test', 't1', ['foo', 'bar'])
:param name: name of the index.
:param tablename: name of the owning table.
:param table_name: name of the owning table.
.. versionchanged:: 0.5.0
The ``tablename`` parameter is now named ``table_name``.
As this is a positional argument, the old name is no
longer present.
:param columns: a list of string column names in the
table.
:param schema: Optional schema name to operate within.
@@ -612,21 +625,27 @@ class Operations(object):
"""
self.impl.create_index(
self._index(name, tablename, columns, schema=schema, **kw)
self._index(name, table_name, columns, schema=schema, **kw)
)
def drop_index(self, name, tablename=None, schema=None):
@util._with_legacy_names([('tablename', 'table_name')])
def drop_index(self, name, table_name=None, schema=None):
"""Issue a "drop index" instruction using the current
migration context.
e.g.::
drop_index("accounts")
:param name: name of the index.
:param tablename: name of the owning table. Some
:param table_name: name of the owning table. Some
backends such as Microsoft SQL Server require this.
.. versionchanged:: 0.5.0
The ``tablename`` parameter is now named ``table_name``.
The old name will continue to function for backwards
compatibility.
:param schema: Optional schema name to operate within.
.. versionadded:: 0.4.0
@@ -635,43 +654,45 @@ class Operations(object):
# need a dummy column name here since SQLAlchemy
# 0.7.6 and further raises on Index with no columns
self.impl.drop_index(
self._index(name, tablename, ['x'], schema=schema)
self._index(name, table_name, ['x'], schema=schema)
)
def drop_constraint(self, name, tablename, type_=None, schema=None, type=None):
@util._with_legacy_names([("type", "type_")])
def drop_constraint(self, name, table_name, type_=None, schema=None):
"""Drop a constraint of the given name, typically via DROP CONSTRAINT.
:param name: name of the constraint.
:param tablename: tablename.
:param table_name: table name.
.. versionchanged:: 0.5.0
The ``tablename`` parameter is now named ``table_name``.
As this is a positional argument, the old name is no
longer present.
:param ``type_``: optional, required on MySQL. can be
'foreignkey', 'primary', 'unique', or 'check'.
.. versionadded:: 0.4.2 the parameter is now named ``type_``.
.. versionchanged:: 0.5.0
The ``type`` parameter is now named ``type_``. The old name
``type`` will remain for backwards compatibility.
.. versionadded:: 0.3.6 'primary' qualfier to enable
dropping of MySQL primary key constraints.
:param type: deprecated, use ``type_``.
.. versionchanged:: 0.4.2
:param schema: Optional schema name to operate within.
.. versionadded:: 0.4.0
"""
if type and type_ is None:
type_ = type
t = self._table(tablename, schema=schema)
t = self._table(table_name, schema=schema)
types = {
'foreignkey':lambda name:sa_schema.ForeignKeyConstraint(
'foreignkey': lambda name: sa_schema.ForeignKeyConstraint(
[], [], name=name),
'primary':sa_schema.PrimaryKeyConstraint,
'unique':sa_schema.UniqueConstraint,
'check':lambda name:sa_schema.CheckConstraint("", name=name),
None:sa_schema.Constraint
'primary': sa_schema.PrimaryKeyConstraint,
'unique': sa_schema.UniqueConstraint,
'check': lambda name: sa_schema.CheckConstraint("", name=name),
None: sa_schema.Constraint
}
try:
const = types[type_]

View File

@@ -5,14 +5,12 @@ import sys
import os
import textwrap
from sqlalchemy.engine import url
from sqlalchemy import util as sqla_util
import imp
import warnings
import re
import inspect
import time
import random
import uuid
from sqlalchemy.util import format_argspec_plus, update_wrapper
class CommandError(Exception):
pass
@@ -251,3 +249,44 @@ class immutabledict(dict):
def __repr__(self):
return "immutabledict(%s)" % dict.__repr__(self)
def _with_legacy_names(translations):
def decorate(fn):
spec = inspect.getargspec(fn)
metadata = dict(target='target', fn='fn')
metadata.update(format_argspec_plus(spec, grouped=False))
has_keywords = bool(spec[2])
if not has_keywords:
metadata['args'] += ", **kw"
metadata['apply_kw'] += ", **kw"
def go(*arg, **kw):
names = set(kw).difference(spec[0])
for oldname, newname in translations:
if oldname in kw:
kw[newname] = kw.pop(oldname)
names.discard(oldname)
warnings.warn(
"Argument '%s' is now named '%s' for function '%s'" %
(oldname, newname, fn.__name__))
if not has_keywords and names:
raise TypeError("Unknown arguments: %s" % ", ".join(names))
return fn(*arg, **kw)
code = 'lambda %(args)s: %(target)s(%(apply_kw)s)' % (
metadata)
decorated = eval(code, {"target": go})
decorated.func_defaults = getattr(fn, 'im_func', fn).func_defaults
return update_wrapper(decorated, fn)
return decorate

View File

@@ -4,15 +4,31 @@ Changelog
==========
.. changelog::
:version: 0.4.3
:version: 0.5.0
.. change::
:tags: bug, documentation
:tags: bug
:tickets: 104
The name of the "type" parameter on :func:`.drop_constraint`
is now officially named ``type_`` for consistency.
``type`` will remain in at least the near future for
backwards compatibility.
A host of argument name changes within migration
operations for consistency. Keyword arguments
will continue to work on the old name for backwards compatibility,
however required positional arguments will not:
:meth:`.Operations.alter_column` - ``name`` -> ``new_column_name`` - old
name will work for backwards compatibility.
:meth:`.Operations.create_index` - ``tablename`` -> ``table_name`` -
argument is positional.
:meth:`.Operations.drop_index` - ``tablename`` -> ``table_name`` - old
name will work for backwards compatibility.
:meth:`.Operations.drop_constraint` - ``tablename`` -> ``table_name`` -
argument is positional.
:meth:`.Operations.drop_constraint` - ``type`` -> ``type_`` - old
name will work for backwards compatibility
.. changelog::
:version: 0.4.2

View File

@@ -53,7 +53,7 @@ class OpTest(TestCase):
def test_alter_column_rename_mssql(self):
context = op_fixture('mssql')
op.alter_column("t", "c", name="x")
op.alter_column("t", "c", new_column_name="x")
context.assert_(
"EXEC sp_rename 't.c', 'x', 'COLUMN'"
)
@@ -154,7 +154,8 @@ class OpTest(TestCase):
def test_alter_do_everything(self):
context = op_fixture('mssql')
op.alter_column("t", "c", name="c2", nullable=True, type_=Integer, server_default="5")
op.alter_column("t", "c", new_column_name="c2", nullable=True,
type_=Integer, server_default="5")
context.assert_(
'ALTER TABLE t ALTER COLUMN c INTEGER NULL',
"ALTER TABLE t ADD DEFAULT '5' FOR c",

View File

@@ -3,25 +3,25 @@ from alembic import op, util
from sqlalchemy import Integer, Column, ForeignKey, \
UniqueConstraint, Table, MetaData, String,\
func
from sqlalchemy.sql import table
def test_rename_column():
context = op_fixture('mysql')
op.alter_column('t1', 'c1', name="c2", existing_type=Integer)
op.alter_column('t1', 'c1', new_column_name="c2", existing_type=Integer)
context.assert_(
'ALTER TABLE t1 CHANGE c1 c2 INTEGER NULL'
)
def test_rename_column_serv_default():
context = op_fixture('mysql')
op.alter_column('t1', 'c1', name="c2", existing_type=Integer, existing_server_default="q")
op.alter_column('t1', 'c1', new_column_name="c2", existing_type=Integer,
existing_server_default="q")
context.assert_(
"ALTER TABLE t1 CHANGE c1 c2 INTEGER NULL DEFAULT 'q'"
)
def test_rename_column_serv_compiled_default():
context = op_fixture('mysql')
op.alter_column('t1', 'c1', name="c2", existing_type=Integer,
op.alter_column('t1', 'c1', new_column_name="c2", existing_type=Integer,
existing_server_default=func.utc_thing(func.current_timestamp()))
# this is not a valid MySQL default but the point is to just
# test SQL expression rendering
@@ -31,7 +31,7 @@ def test_rename_column_serv_compiled_default():
def test_rename_column_autoincrement():
context = op_fixture('mysql')
op.alter_column('t1', 'c1', name="c2", existing_type=Integer,
op.alter_column('t1', 'c1', new_column_name="c2", existing_type=Integer,
existing_autoincrement=True)
context.assert_(
'ALTER TABLE t1 CHANGE c1 c2 INTEGER NULL AUTO_INCREMENT'
@@ -39,7 +39,7 @@ def test_rename_column_autoincrement():
def test_col_add_autoincrement():
context = op_fixture('mysql')
op.alter_column('t1', 'c1', name="c2", existing_type=Integer,
op.alter_column('t1', 'c1', new_column_name="c2", existing_type=Integer,
autoincrement=True)
context.assert_(
'ALTER TABLE t1 CHANGE c1 c2 INTEGER NULL AUTO_INCREMENT'
@@ -47,7 +47,7 @@ def test_col_add_autoincrement():
def test_col_remove_autoincrement():
context = op_fixture('mysql')
op.alter_column('t1', 'c1', name="c2", existing_type=Integer,
op.alter_column('t1', 'c1', new_column_name="c2", existing_type=Integer,
existing_autoincrement=True,
autoincrement=False)
context.assert_(
@@ -86,7 +86,7 @@ def test_drop_fk():
def test_drop_constraint_primary():
context = op_fixture('mysql')
op.drop_constraint('primary', 't1',type='primary')
op.drop_constraint('primary', 't1', type_='primary')
context.assert_(
"ALTER TABLE t1 DROP PRIMARY KEY "
)

View File

@@ -3,9 +3,8 @@
from tests import op_fixture, assert_raises_message
from alembic import op
from sqlalchemy import Integer, Column, ForeignKey, \
UniqueConstraint, Table, MetaData, String,\
Boolean
from sqlalchemy.sql import table, column, func
Table, String, Boolean
from sqlalchemy.sql import column, func
def test_rename_table():
@@ -169,14 +168,14 @@ def test_alter_column_schema_not_nullable():
def test_alter_column_rename():
context = op_fixture()
op.alter_column("t", "c", name="x")
op.alter_column("t", "c", new_column_name="x")
context.assert_(
"ALTER TABLE t RENAME c TO x"
)
def test_alter_column_schema_rename():
context = op_fixture()
op.alter_column("t", "c", name="x", schema='foo')
op.alter_column("t", "c", new_column_name="x", schema='foo')
context.assert_(
"ALTER TABLE foo.t RENAME c TO x"
)
@@ -550,13 +549,13 @@ def test_inline_literal():
)
op.execute(
account.update().\
where(account.c.name==op.inline_literal('account 1')).\
values({'name':op.inline_literal('account 2')})
where(account.c.name == op.inline_literal('account 1')).\
values({'name': op.inline_literal('account 2')})
)
op.execute(
account.update().\
where(account.c.id==op.inline_literal(1)).\
values({'id':op.inline_literal(2)})
where(account.c.id == op.inline_literal(1)).\
values({'id': op.inline_literal(2)})
)
context.assert_(
"UPDATE account SET name='account 2' WHERE account.name = 'account 1'",
@@ -574,3 +573,36 @@ def test_cant_op():
"Try placing this code inside a callable.",
op.inline_literal, "asdf"
)
def test_naming_changes():
context = op_fixture()
op.alter_column("t", "c", name="x")
context.assert_("ALTER TABLE t RENAME c TO x")
context = op_fixture()
op.alter_column("t", "c", new_column_name="x")
context.assert_("ALTER TABLE t RENAME c TO x")
context = op_fixture('mssql')
op.drop_index('ik_test', tablename='t1')
context.assert_("DROP INDEX [t1].ik_test")
context = op_fixture('mssql')
op.drop_index('ik_test', table_name='t1')
context.assert_("DROP INDEX [t1].ik_test")
context = op_fixture('mysql')
op.drop_constraint("f1", "t1", type="foreignkey")
context.assert_("ALTER TABLE t1 DROP FOREIGN KEY f1")
context = op_fixture('mysql')
op.drop_constraint("f1", "t1", type_="foreignkey")
context.assert_("ALTER TABLE t1 DROP FOREIGN KEY f1")
assert_raises_message(
TypeError,
"Unknown arguments: badarg2, badarg1",
op.alter_column, "t", "c", badarg1="x", badarg2="y"
)