From: Stefan Tjarks <66305+stj@users.noreply.github.com> Date: Thu, 22 Nov 2018 03:08:56 +0000 (-0500) Subject: Vendor python3 formatargspec, import from collections.abc X-Git-Tag: rel_1_0_5~1^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e3c5fc317ed306890c65b28c7ab75aa806cd2633;p=thirdparty%2Fsqlalchemy%2Falembic.git Vendor python3 formatargspec, import from collections.abc Resolved remaining Python 3 deprecation warnings, covering the use of inspect.formatargspec() with a vendored version copied from the Python standard library, importing collections.abc above Python 3.3 when testing against abstract base classes, fixed one occurrence of log.warn(), as well as a few invalid escape sequences. Add DeprecationWarning to the test suite as an error raise as has been the case within SQLAlchemy for some time now. Fixes: #507 Co-authored-by: Mike Bayer Change-Id: I121121b3d2dd90e6f3c9b16dec2fc80b9699c400 Pull-request: https://bitbucket.org/zzzeek/alembic/pull-requests/85 --- diff --git a/alembic/operations/base.py b/alembic/operations/base.py index ef6ecf8f..1ae95241 100644 --- a/alembic/operations/base.py +++ b/alembic/operations/base.py @@ -5,9 +5,9 @@ from ..util import sqla_compat from . import batch from . import schemaobj from ..util.compat import exec_ +from ..util.compat import inspect_formatargspec from ..util.compat import inspect_getargspec import textwrap -import inspect __all__ = ('Operations', 'BatchOperations') @@ -99,14 +99,14 @@ class Operations(util.ModuleClsProxy): name_args[0:2] = ['self'] - args = inspect.formatargspec(*spec) + args = inspect_formatargspec(*spec) num_defaults = len(spec[3]) if spec[3] else 0 if num_defaults: defaulted_vals = name_args[0 - num_defaults:] else: defaulted_vals = () - apply_kw = inspect.formatargspec( + apply_kw = inspect_formatargspec( name_args, spec[1], spec[2], defaulted_vals, formatvalue=lambda x: '=' + x) diff --git a/alembic/testing/provision.py b/alembic/testing/provision.py index 546ef23c..15d9e1cd 100644 --- a/alembic/testing/provision.py +++ b/alembic/testing/provision.py @@ -256,6 +256,7 @@ def _oracle_create_db(cfg, eng, ident): conn.execute("grant unlimited tablespace to %s_ts1" % ident) conn.execute("grant unlimited tablespace to %s_ts2" % ident) + @_configure_follower.for_db("oracle") def _oracle_configure_follower(config, ident): config.test_schema = "%s_ts1" % ident @@ -268,7 +269,7 @@ def _ora_drop_ignore(conn, dbname): log.info("Reaped db: %s" % dbname) return True except exc.DatabaseError as err: - log.warn("couldn't drop db: %s" % err) + log.warning("couldn't drop db: %s" % err) return False diff --git a/alembic/testing/warnings.py b/alembic/testing/warnings.py index 397938f0..de917785 100644 --- a/alembic/testing/warnings.py +++ b/alembic/testing/warnings.py @@ -17,11 +17,11 @@ import re def setup_filters(): """Set global warning behavior for the test suite.""" - warnings.filterwarnings('ignore', category=sa_exc.SAPendingDeprecationWarning) warnings.filterwarnings('error', category=sa_exc.SADeprecationWarning) warnings.filterwarnings('error', category=sa_exc.SAWarning) + warnings.filterwarnings('error', category=DeprecationWarning) def assert_warnings(fn, warning_msgs, regex=False): diff --git a/alembic/util/compat.py b/alembic/util/compat.py index b3e0d57a..dec2ca8f 100644 --- a/alembic/util/compat.py +++ b/alembic/util/compat.py @@ -7,6 +7,7 @@ if sys.version_info < (2, 7): py27 = sys.version_info >= (2, 7) py2k = sys.version_info.major < 3 py3k = sys.version_info.major >= 3 +py33 = sys.version_info >= (3, 3) py35 = sys.version_info >= (3, 5) py36 = sys.version_info >= (3, 6) @@ -47,6 +48,11 @@ else: range = xrange +if py33: + import collections.abc as collections_abc +else: + import collections as collections_abc + if py3k: import collections ArgSpec = collections.namedtuple( @@ -62,6 +68,63 @@ if py3k: else: from inspect import getargspec as inspect_getargspec # noqa +if py35: + from inspect import formatannotation + + def inspect_formatargspec( + args, varargs=None, varkw=None, defaults=None, + kwonlyargs=(), kwonlydefaults={}, annotations={}, + formatarg=str, + formatvarargs=lambda name: '*' + name, + formatvarkw=lambda name: '**' + name, + formatvalue=lambda value: '=' + repr(value), + formatreturns=lambda text: ' -> ' + text, + formatannotation=formatannotation): + """Copy formatargspec from python 3.7 standard library. + + Python 3 has deprecated formatargspec and requested that Signature + be used instead, however this requires a full reimplementation + of formatargspec() in terms of creating Parameter objects and such. + Instead of introducing all the object-creation overhead and having + to reinvent from scratch, just copy their compatibility routine. + + """ + + def formatargandannotation(arg): + result = formatarg(arg) + if arg in annotations: + result += ': ' + formatannotation(annotations[arg]) + return result + specs = [] + if defaults: + firstdefault = len(args) - len(defaults) + for i, arg in enumerate(args): + spec = formatargandannotation(arg) + if defaults and i >= firstdefault: + spec = spec + formatvalue(defaults[i - firstdefault]) + specs.append(spec) + if varargs is not None: + specs.append(formatvarargs(formatargandannotation(varargs))) + else: + if kwonlyargs: + specs.append('*') + if kwonlyargs: + for kwonlyarg in kwonlyargs: + spec = formatargandannotation(kwonlyarg) + if kwonlydefaults and kwonlyarg in kwonlydefaults: + spec += formatvalue(kwonlydefaults[kwonlyarg]) + specs.append(spec) + if varkw is not None: + specs.append(formatvarkw(formatargandannotation(varkw))) + result = '(' + ', '.join(specs) + ')' + if 'return' in annotations: + result += formatreturns(formatannotation(annotations['return'])) + return result + +else: + from inspect import formatargspec as inspect_formatargspec + + if py3k: from configparser import ConfigParser as SafeConfigParser import configparser diff --git a/alembic/util/langhelpers.py b/alembic/util/langhelpers.py index aa016f04..832332cb 100644 --- a/alembic/util/langhelpers.py +++ b/alembic/util/langhelpers.py @@ -1,12 +1,14 @@ import textwrap import warnings -import inspect import uuid import collections +from .compat import collections_abc + from .compat import callable, exec_, string_types, with_metaclass from .compat import inspect_getargspec +from .compat import inspect_formatargspec class _ModuleClsMeta(type): @@ -75,7 +77,7 @@ class ModuleClsProxy(with_metaclass(_ModuleClsMeta)): spec = inspect_getargspec(fn) if spec[0] and spec[0][0] == 'self': spec[0].pop(0) - args = inspect.formatargspec(*spec) + args = inspect_formatargspec(*spec) num_defaults = 0 if spec[3]: num_defaults += len(spec[3]) @@ -85,7 +87,7 @@ class ModuleClsProxy(with_metaclass(_ModuleClsMeta)): else: defaulted_vals = () - apply_kw = inspect.formatargspec( + apply_kw = inspect_formatargspec( name_args, spec[1], spec[2], defaulted_vals, formatvalue=lambda x: '=' + x) @@ -189,7 +191,7 @@ def to_list(x, default=None): return default elif isinstance(x, string_types): return [x] - elif isinstance(x, collections.Iterable): + elif isinstance(x, collections_abc.Iterable): return list(x) else: return [x] @@ -200,7 +202,7 @@ def to_tuple(x, default=None): return default elif isinstance(x, string_types): return (x, ) - elif isinstance(x, collections.Iterable): + elif isinstance(x, collections_abc.Iterable): return tuple(x) else: return (x, ) diff --git a/alembic/util/messaging.py b/alembic/util/messaging.py index c202e96c..872345b5 100644 --- a/alembic/util/messaging.py +++ b/alembic/util/messaging.py @@ -1,9 +1,10 @@ from .compat import py27, binary_type, string_types +from .compat import collections_abc + import sys from sqlalchemy.engine import url import warnings import textwrap -import collections import logging log = logging.getLogger(__name__) @@ -88,7 +89,7 @@ def format_as_comma(value): return "" elif isinstance(value, string_types): return value - elif isinstance(value, collections.Iterable): + elif isinstance(value, collections_abc.Iterable): return ", ".join(value) else: raise ValueError("Don't know how to comma-format %r" % value) diff --git a/docs/build/unreleased/507.rst b/docs/build/unreleased/507.rst new file mode 100644 index 00000000..7293ba4b --- /dev/null +++ b/docs/build/unreleased/507.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, py3k + :tickets: 507 + + Resolved remaining Python 3 deprecation warnings, covering + the use of inspect.formatargspec() with a vendored version + copied from the Python standard library, importing + collections.abc above Python 3.3 when testing against abstract + base classes, fixed one occurrence of log.warn(), as well as a few + invalid escape sequences. \ No newline at end of file diff --git a/tests/test_op.py b/tests/test_op.py index 63e4a748..f9a6c51d 100644 --- a/tests/test_op.py +++ b/tests/test_op.py @@ -36,7 +36,7 @@ class OpTest(TestBase): op_fixture() assert_raises_message( ValueError, - "String or text\(\) construct expected", + r"String or text\(\) construct expected", op.create_index, 'name', 'tname', [func.foo(column('x'))] ) diff --git a/tests/test_version_traversal.py b/tests/test_version_traversal.py index 08c737f1..f69a9bd2 100644 --- a/tests/test_version_traversal.py +++ b/tests/test_version_traversal.py @@ -183,7 +183,7 @@ class RevisionPathTest(MigrationTest): assert_raises_message( util.CommandError, r"Destination %s is not a valid downgrade " - "target from current head\(s\)" % self.b.revision[0:3], + r"target from current head\(s\)" % self.b.revision[0:3], self.env._downgrade_revs, self.b.revision[0:3], None ) @@ -192,7 +192,7 @@ class RevisionPathTest(MigrationTest): assert_raises_message( util.CommandError, r"Destination %s is not a valid downgrade " - "target from current head\(s\)" % self.c.revision[0:4], + r"target from current head\(s\)" % self.c.revision[0:4], self.env._downgrade_revs, self.c.revision[0:4], self.b.revision )