diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-07-14 12:42:19 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-07-14 12:42:19 -0400 |
commit | 6f8d0f2e2d119a092bd4c46c42eca2d4737e0d23 (patch) | |
tree | 82d5c475d2a239a92583ded424083bb8ea9bdfa6 | |
parent | a294f8cc3f2e5fc2cad048bc4ce27c57554e2688 (diff) | |
download | alembic-6f8d0f2e2d119a092bd4c46c42eca2d4737e0d23.tar.gz |
- move the "legacy names" system into where we create the module proxy.
This is so that we can do a total open ended "*args, **kw" style translation
for the vast majority of use cases that are using alembic.op, without impacting
docstrings for the Operations class.
There is a risk here of impacting an application that is using Operations
directly instantitaed while using old names. We may still have to accommodate
that somehow.
-rw-r--r-- | alembic/operations/base.py | 2 | ||||
-rw-r--r-- | alembic/operations/ops.py | 3 | ||||
-rw-r--r-- | alembic/util/langhelpers.py | 92 | ||||
-rw-r--r-- | tests/test_op.py | 13 |
4 files changed, 62 insertions, 48 deletions
diff --git a/alembic/operations/base.py b/alembic/operations/base.py index 18710fc..346e03b 100644 --- a/alembic/operations/base.py +++ b/alembic/operations/base.py @@ -130,6 +130,8 @@ class Operations(util.ModuleClsProxy): "the :class:`.%s` class, via the :meth:`.%s.%s` method." % ( cls.__name__, cls.__name__, name ) + if hasattr(fn, '_legacy_translations'): + lcl[name]._legacy_translations = fn._legacy_translations return op_cls return register diff --git a/alembic/operations/ops.py b/alembic/operations/ops.py index 16cccb6..73ae55f 100644 --- a/alembic/operations/ops.py +++ b/alembic/operations/ops.py @@ -257,7 +257,8 @@ class CreateUniqueConstraintOp(AddConstraintOp): @classmethod @util._with_legacy_names([ ('name', 'constraint_name'), - ('source', 'table_name') + ('source', 'table_name'), + ('local_cols', 'columns'), ]) def create_unique_constraint( cls, operations, constraint_name, table_name, columns, diff --git a/alembic/util/langhelpers.py b/alembic/util/langhelpers.py index 904848c..21d2bfb 100644 --- a/alembic/util/langhelpers.py +++ b/alembic/util/langhelpers.py @@ -101,9 +101,44 @@ class ModuleClsProxy(with_metaclass(_ModuleClsMeta)): )) globals_['_name_error'] = _name_error + translations = getattr(fn, "_legacy_translations", []) + if translations: + outer_args = inner_args = "*args, **kw" + translate_str = "args, kw = _translate(%r, %r, args, kw)" % ( + tuple(spec), + translations + ) + + def translate(spec, translations, args, kw): + return_kw = {} + return_args = [] + + for oldname, newname in translations: + if oldname in kw: + return_kw[newname] = kw.pop(oldname) + return_kw.update(kw) + + args = list(args) + if spec[3]: + pos_only = spec[0][:-len(spec[3])] + else: + pos_only = spec[0] + for arg in pos_only: + if arg not in return_kw: + return_args.append(args.pop(0)) + return_args.extend(args) + + return return_args, return_kw + globals_['_translate'] = translate + else: + outer_args = args[1:-1] + inner_args = apply_kw[1:-1] + translate_str = "" + func_text = textwrap.dedent("""\ def %(name)s(%(args)s): %(doc)r + %(translate)s try: p = _proxy except NameError: @@ -112,8 +147,9 @@ class ModuleClsProxy(with_metaclass(_ModuleClsMeta)): e """ % { 'name': name, - 'args': args[1:-1], - 'apply_kw': apply_kw[1:-1], + 'translate': translate_str, + 'args': outer_args, + 'apply_kw': inner_args, 'doc': fn.__doc__, }) lcl = {} @@ -121,6 +157,14 @@ class ModuleClsProxy(with_metaclass(_ModuleClsMeta)): return lcl[name] +def _with_legacy_names(translations): + def decorate(fn): + fn._legacy_translations = translations + return fn + + return decorate + + def asbool(value): return value is not None and \ value.lower() == 'true' @@ -201,50 +245,6 @@ class immutabledict(dict): return "immutabledict(%s)" % dict.__repr__(self) -def _with_legacy_names(translations): - def decorate(fn): - - spec = inspect_getfullargspec(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.__defaults__ = getattr(fn, '__func__', fn).__defaults__ - update_wrapper(decorated, fn) - if hasattr(decorated, '__wrapped__'): - # update_wrapper in py3k applies __wrapped__, which causes - # inspect.getargspec() to ignore the extra arguments on our - # wrapper as of Python 3.4. We need this for the - # "module class proxy" thing though, so just del the __wrapped__ - # for now. See #175 as well as bugs.python.org/issue17482 - del decorated.__wrapped__ - return decorated - - return decorate - - class Dispatcher(object): def __init__(self): self._registry = {} diff --git a/tests/test_op.py b/tests/test_op.py index 9c14e49..610c948 100644 --- a/tests/test_op.py +++ b/tests/test_op.py @@ -596,9 +596,20 @@ class OpTest(TestBase): "ALTER TABLE t1 ADD CONSTRAINT uk_test UNIQUE (foo, bar)" ) + def test_add_unique_constraint_legacy_kwarg(self): + context = op_fixture() + op.create_unique_constraint( + name='uk_test', + source='t1', + local_cols=['foo', 'bar']) + context.assert_( + "ALTER TABLE t1 ADD CONSTRAINT uk_test UNIQUE (foo, bar)" + ) + def test_add_unique_constraint_schema(self): context = op_fixture() - op.create_unique_constraint('uk_test', 't1', ['foo', 'bar'], schema='foo') + op.create_unique_constraint( + 'uk_test', 't1', ['foo', 'bar'], schema='foo') context.assert_( "ALTER TABLE foo.t1 ADD CONSTRAINT uk_test UNIQUE (foo, bar)" ) |