diff --git a/praw/decorator_helpers.py b/praw/decorator_helpers.py index 413cbfb02..375dca8ff 100644 --- a/praw/decorator_helpers.py +++ b/praw/decorator_helpers.py @@ -1,4 +1,5 @@ """Internal helper functions used by praw.decorators.""" +import inspect from requests.compat import urljoin import six import sys @@ -23,3 +24,15 @@ def _is_mod_of_all(user, subreddit): mod_subs = user.get_cached_moderated_reddits() subs = six.text_type(subreddit).lower().split('+') return all(sub in mod_subs for sub in subs) + + +def _make_func_args(function): + if six.PY3 and not hasattr(sys, 'pypy_version_info'): + # CPython3 uses inspect.signature(), not inspect.getargspec() + # see #551 and #541 for more info + func_items = inspect.signature(function).parameters.items() + func_args = [name for name, param in func_items + if param.kind == param.POSITIONAL_OR_KEYWORD] + else: + func_args = inspect.getargspec(function).args + return func_args diff --git a/praw/decorators.py b/praw/decorators.py index 41d366f71..d28e4ce8e 100644 --- a/praw/decorators.py +++ b/praw/decorators.py @@ -24,11 +24,14 @@ from __future__ import print_function, unicode_literals import decorator -import inspect import six import sys from functools import wraps -from praw.decorator_helpers import _get_captcha, _is_mod_of_all +from praw.decorator_helpers import ( + _get_captcha, + _is_mod_of_all, + _make_func_args +) from praw import errors from warnings import simplefilter, warn @@ -48,7 +51,7 @@ def alias_function(function, class_name): """ @wraps(function) def wrapped(self, *args, **kwargs): - func_args = inspect.getargspec(function).args + func_args = _make_func_args(function) if 'subreddit' in func_args and func_args.index('subreddit') != 1: # Only happens for search kwargs['subreddit'] = self @@ -164,9 +167,9 @@ def require_captcha(function, *args, **kwargs): # *args currently contains a None where the captcha answer # needs to go. If we put the captcha in the **kwargs, # we get a TypeError for having two values of the same param. - func_args = inspect.getargspec(function) - if 'captcha' in func_args.args: - captcha_index = func_args.args.index('captcha') + func_args = _make_func_args(function) + if 'captcha' in func_args: + captcha_index = func_args.index('captcha') args = list(args) args[captcha_index] = captcha_answer else: diff --git a/tests/test_decorators.py b/tests/test_decorators.py index fdc299318..1b62736b6 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -1,6 +1,7 @@ from __future__ import print_function, unicode_literals import unittest +from praw.decorator_helpers import _make_func_args from praw.decorators import restrict_access @@ -8,3 +9,15 @@ class DecoratorTest(unittest.TestCase): def test_require_access_failure(self): self.assertRaises(TypeError, restrict_access, scope=None, oauth_only=True) + + def test_make_func_args(self): + def foo(arg1, arg2, arg3): + pass + + def bar(arg1, arg2, arg3, *args, **kwargs): + pass + + arglist = ['arg1', 'arg2', 'arg3'] + + self.assertEqual(_make_func_args(foo), arglist) + self.assertEqual(_make_func_args(bar), arglist)