jenkins-bot has submitted this change and it was merged.
Change subject: [FIX] Implement getargspec in tools ......................................................................
[FIX] Implement getargspec in tools
With version 3 inspect.getargspec has been deprecated and with version 3.6 it is supposed to be removed. This implements getargspec in tools which just calls the implementation before Python 3.3. In Python 3.3 it calls the replacement, inspect.signature, and constructs a result simulating getargspec from it.
It removes the special case for handling deprecation warnings in Python 3.5 introduced in 773e72ee.
Bug: T106209 Change-Id: Ib68f09b3e47169279d9725ae0ddfab71009b2e1c --- M pywikibot/data/api.py M pywikibot/tools/__init__.py M tests/tools_tests.py M tests/utils.py 4 files changed, 117 insertions(+), 10 deletions(-)
Approvals: John Vandenberg: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/data/api.py b/pywikibot/data/api.py index e5f6873..53e6132 100644 --- a/pywikibot/data/api.py +++ b/pywikibot/data/api.py @@ -29,7 +29,9 @@
import pywikibot from pywikibot import config, login -from pywikibot.tools import MediaWikiVersion, deprecated, itergroup, ip, PY2 +from pywikibot.tools import ( + MediaWikiVersion, deprecated, itergroup, ip, PY2, getargspec, +) from pywikibot.tools.formatter import color_format from pywikibot.exceptions import ( Server504Error, Server414Error, FatalServerError, NoUsername, Error @@ -1515,7 +1517,7 @@ for super_cls in inspect.getmro(cls): if not super_cls.__name__.endswith('Request'): break - args |= set(inspect.getargspec(super_cls.__init__)[0]) + args |= set(getargspec(super_cls.__init__)[0]) else: raise ValueError('Request was not a super class of ' '{0!r}'.format(cls)) diff --git a/pywikibot/tools/__init__.py b/pywikibot/tools/__init__.py index 61b353d..4655d91 100644 --- a/pywikibot/tools/__init__.py +++ b/pywikibot/tools/__init__.py @@ -38,6 +38,38 @@
from pywikibot.logging import debug
+ +if PYTHON_VERSION < (3, 5): + # although deprecated in 3 completely no message was emitted until 3.5 + ArgSpec = inspect.ArgSpec + getargspec = inspect.getargspec +else: + ArgSpec = collections.namedtuple('ArgSpec', ['args', 'varargs', 'keywords', + 'defaults']) + + def getargspec(func): + """Python 3 implementation using inspect.signature.""" + sig = inspect.signature(func) + args = [] + defaults = [] + varargs = None + kwargs = None + for p in sig.parameters.values(): + if p.kind == inspect.Parameter.VAR_POSITIONAL: + varargs = p.name + elif p.kind == inspect.Parameter.VAR_KEYWORD: + kwargs = p.name + else: + args += [p.name] + if p.default != inspect.Parameter.empty: + defaults += [p.default] + if defaults: + defaults = tuple(defaults) + else: + defaults = None + return ArgSpec(args, varargs, kwargs, defaults) + + _logger = 'tools'
@@ -1349,7 +1381,7 @@ """ name = obj.__full_name__ depth = get_wrapper_depth(wrapper) + 1 - args, varargs, kwargs, _ = inspect.getargspec(wrapper.__wrapped__) + args, varargs, kwargs, _ = getargspec(wrapper.__wrapped__) if varargs is not None and kwargs is not None: raise ValueError('{0} may not have * or ** args.'.format( name)) diff --git a/tests/tools_tests.py b/tests/tools_tests.py index 9b2445a..919f28c 100644 --- a/tests/tools_tests.py +++ b/tests/tools_tests.py @@ -11,15 +11,17 @@
import collections import decimal +import inspect import os.path import subprocess import tempfile +import warnings
from pywikibot import tools
from tests import join_xml_data_path -from tests.aspects import unittest, DeprecationTestCase, TestCase -from tests.utils import expected_failure_if +from tests.aspects import unittest, DeprecationTestCase, TestCase, MetaTestCaseClass +from tests.utils import expected_failure_if, add_metaclass
class ContextManagerWrapperTestCase(TestCase): @@ -487,6 +489,82 @@ self.assertRaises(StopIteration, next, deduper)
+class MetaTestArgSpec(MetaTestCaseClass): + + """Metaclass to create dynamically the tests. Set the net flag to false.""" + + def __new__(cls, name, bases, dct): + """Create a new test case class.""" + def create_test(method): + def test_method(self): + # all expect at least self and param + expected = method(1, 2) + returned = self.getargspec(method) + self.assertEqual(returned, expected) + self.assertIsInstance(returned, self.expected_class) + self.assertNoDeprecation() + return test_method + + for name, tested_method in list(dct.items()): + if name.startswith('_method_test_'): + suffix = name[len('_method_test_'):] + test_name = 'test_method_' + suffix + dct[test_name] = create_test(tested_method) + dct[test_name].__doc__ = 'Test getargspec on {0}'.format(suffix) + + dct['net'] = False + return super(MetaTestArgSpec, cls).__new__(cls, name, bases, dct) + + +@add_metaclass +class TestArgSpec(DeprecationTestCase): + + """Test getargspec and ArgSpec from tools.""" + + __metaclass__ = MetaTestArgSpec + + expected_class = tools.ArgSpec + + def _method_test_args(self, param): + """Test method with two positional arguments.""" + return (['self', 'param'], None, None, None) + + def _method_test_kwargs(self, param=42): + """Test method with one positional and one keyword argument.""" + return (['self', 'param'], None, None, (42,)) + + def _method_test_varargs(self, param, *var): + """Test method with two positional arguments and var args.""" + return (['self', 'param'], 'var', None, None) + + def _method_test_varkwargs(self, param, **var): + """Test method with two positional arguments and var kwargs.""" + return (['self', 'param'], None, 'var', None) + + def _method_test_vars(self, param, *args, **kwargs): + """Test method with two positional arguments and both var args.""" + return (['self', 'param'], 'args', 'kwargs', None) + + def getargspec(self, method): + """Call tested getargspec function.""" + return tools.getargspec(method) + + +@unittest.skipIf(tools.PYTHON_VERSION >= (3, 6), 'removed in Python 3.6') +class TestPythonArgSpec(TestArgSpec): + + """Test the same tests using Python's implementation.""" + + expected_class = inspect.ArgSpec + + def getargspec(self, method): + """Call inspect's getargspec function.""" + with warnings.catch_warnings(): + if tools.PYTHON_VERSION >= (3, 5): + warnings.simplefilter('ignore', DeprecationWarning) + return inspect.getargspec(method) + + if __name__ == '__main__': try: unittest.main() diff --git a/tests/utils.py b/tests/utils.py index 69f2fae..533f17c 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -230,11 +230,6 @@ else: skip_lines -= 1
- # Avoid failures because getargspec hasn't been removed yet: T106209 - if PYTHON_VERSION >= (3, 5, 0): - if str(entry.message) == ('inspect.getargspec() is deprecated, ' - 'use inspect.signature() instead'): - return # Avoid failures because cryptography is mentioning Python 2.6 # is outdated if PYTHON_VERSION < (2, 7):
pywikibot-commits@lists.wikimedia.org