From 880993f269694603a7df74671e94ad6fe74c43ad Mon Sep 17 00:00:00 2001 From: grayjk Date: Fri, 7 Jan 2022 10:29:51 -0800 Subject: [PATCH 1/2] Use inspect.Signature instead of inspect.formatargspec Closes #196 --- src/wrapt/decorators.py | 25 ++++++++--- tests/test_adapter.py | 96 ++++++++++++++++++++++------------------- 2 files changed, 72 insertions(+), 49 deletions(-) diff --git a/src/wrapt/decorators.py b/src/wrapt/decorators.py index 389a764b..95c187e0 100644 --- a/src/wrapt/decorators.py +++ b/src/wrapt/decorators.py @@ -31,7 +31,7 @@ def exec_(_code_, _globs_=None, _locs_=None): del builtins from functools import partial -from inspect import ismethod, isclass, formatargspec +from inspect import ismethod, isclass, Signature, Parameter from collections import namedtuple from threading import Lock, RLock @@ -166,6 +166,23 @@ def __call__(self, wrapped): adapter_factory = DelegatedAdapterFactory +def _formatargspec(spec): + params = [] + for arg in spec.args: + params.append(Parameter(arg, Parameter.POSITIONAL_OR_KEYWORD)) + if spec.defaults: + for i, d in enumerate(reversed(spec.defaults)): + idx = len(params) - 1 - i + params[idx] = params[idx].replace(default=d) + if spec.varargs: + params.append(Parameter(spec.varargs, Parameter.VAR_POSITIONAL)) + if hasattr(spec, 'keywords') and spec.keywords: + params.append(Parameter(spec.keywords, Parameter.VAR_KEYWORD)) + if hasattr(spec, 'varkw') and spec.varkw: + params.append(Parameter(spec.varkw, Parameter.VAR_KEYWORD)) + + return str(Signature(params)) + # Decorator for creating other decorators. This decorator and the # wrappers which they use are designed to properly preserve any name # attributes, function signatures etc, in addition to the wrappers @@ -211,8 +228,7 @@ def _build(wrapped, wrapper, enabled=None, adapter=None): # Check if the signature argument specification has # annotations. If it does then we need to remember - # it but also drop it when attempting to manufacture - # a standin adapter function. This is necessary else + # it. This is necessary else # it will try and look up any types referenced in # the annotations in the empty namespace we use, # which will fail. @@ -222,8 +238,7 @@ def _build(wrapped, wrapper, enabled=None, adapter=None): if not isinstance(adapter, string_types): if len(adapter) == 7: annotations = adapter[-1] - adapter = adapter[:-1] - adapter = formatargspec(*adapter) + adapter = _formatargspec(adapter) exec_('def adapter{}: pass'.format(adapter), ns, ns) adapter = ns['adapter'] diff --git a/tests/test_adapter.py b/tests/test_adapter.py index 14ba6604..c8606f73 100644 --- a/tests/test_adapter.py +++ b/tests/test_adapter.py @@ -3,6 +3,11 @@ import unittest import inspect import imp +import sys +try: + from inspect import getargspec as getspec +except ImportError: # python 3.11 and above + from inspect import getfullargspec as getspec import wrapt @@ -72,15 +77,15 @@ def test_argspec(self): def _adapter(arg1, arg2, arg3=None, *args, **kwargs): pass - function1a_argspec = inspect.getargspec(_adapter) - function1d_argspec = inspect.getargspec(function1d) + function1a_argspec = getspec(_adapter) + function1d_argspec = getspec(function1d) self.assertEqual(function1a_argspec, function1d_argspec) # Now bind the function to an instance. The argspec should # still match. bound_function1d = function1d.__get__(object(), object) - bound_function1d_argspec = inspect.getargspec(bound_function1d) + bound_function1d_argspec = getspec(bound_function1d) self.assertEqual(function1a_argspec, bound_function1d_argspec) def test_signature(self): @@ -107,7 +112,7 @@ class TestDynamicAdapter(unittest.TestCase): def test_dynamic_adapter_function(self): def _adapter(arg1, arg2, arg3=None, *args, **kwargs): pass - argspec = inspect.getargspec(_adapter) + argspec = getspec(_adapter) @wrapt.decorator(adapter=argspec) def _wrapper_1(wrapped, instance, args, kwargs): @@ -117,24 +122,25 @@ def _wrapper_1(wrapped, instance, args, kwargs): def _function_1(): pass - self.assertEqual(inspect.getargspec(_function_1), argspec) + self.assertEqual(getspec(_function_1), argspec) - args = inspect.formatargspec(*argspec) + if sys.version_info < (3, 11): + args = inspect.formatargspec(*argspec) - @wrapt.decorator(adapter=args) - def _wrapper_2(wrapped, instance, args, kwargs): - return wrapped(*args, **kwargs) + @wrapt.decorator(adapter=args) + def _wrapper_2(wrapped, instance, args, kwargs): + return wrapped(*args, **kwargs) - @_wrapper_2 - def _function_2(): - pass + @_wrapper_2 + def _function_2(): + pass - self.assertEqual(inspect.getargspec(_function_2), argspec) + self.assertEqual(getspec(_function_2), argspec) def test_dynamic_adapter_instancemethod(self): def _adapter(self, arg1, arg2, arg3=None, *args, **kwargs): pass - argspec = inspect.getargspec(_adapter) + argspec = getspec(_adapter) @wrapt.decorator(adapter=argspec) def _wrapper_1(wrapped, instance, args, kwargs): @@ -147,29 +153,30 @@ def function(self): instance1 = Class1() - self.assertEqual(inspect.getargspec(Class1.function), argspec) - self.assertEqual(inspect.getargspec(instance1.function), argspec) + self.assertEqual(getspec(Class1.function), argspec) + self.assertEqual(getspec(instance1.function), argspec) - args = inspect.formatargspec(*argspec) + if sys.version_info < (3, 11): + args = inspect.formatargspec(*argspec) - @wrapt.decorator(adapter=args) - def _wrapper_2(wrapped, instance, args, kwargs): - return wrapped(*args, **kwargs) + @wrapt.decorator(adapter=args) + def _wrapper_2(wrapped, instance, args, kwargs): + return wrapped(*args, **kwargs) - class Class2(object): - @_wrapper_2 - def function(self): - pass + class Class2(object): + @_wrapper_2 + def function(self): + pass - instance2 = Class2() + instance2 = Class2() - self.assertEqual(inspect.getargspec(Class2.function), argspec) - self.assertEqual(inspect.getargspec(instance2.function), argspec) + self.assertEqual(getspec(Class2.function), argspec) + self.assertEqual(getspec(instance2.function), argspec) def test_dynamic_adapter_classmethod(self): def _adapter(cls, arg1, arg2, arg3=None, *args, **kwargs): pass - argspec = inspect.getargspec(_adapter) + argspec = getspec(_adapter) @wrapt.decorator(adapter=argspec) def _wrapper_1(wrapped, instance, args, kwargs): @@ -183,29 +190,30 @@ def function(cls): instance1 = Class1() - self.assertEqual(inspect.getargspec(Class1.function), argspec) - self.assertEqual(inspect.getargspec(instance1.function), argspec) + self.assertEqual(getspec(Class1.function), argspec) + self.assertEqual(getspec(instance1.function), argspec) - args = inspect.formatargspec(*argspec) + if sys.version_info < (3, 11): + args = inspect.formatargspec(*argspec) - @wrapt.decorator(adapter=args) - def _wrapper_2(wrapped, instance, args, kwargs): - return wrapped(*args, **kwargs) + @wrapt.decorator(adapter=args) + def _wrapper_2(wrapped, instance, args, kwargs): + return wrapped(*args, **kwargs) - class Class2(object): - @_wrapper_2 - @classmethod - def function(self): - pass + class Class2(object): + @_wrapper_2 + @classmethod + def function(self): + pass - instance2 = Class2() + instance2 = Class2() - self.assertEqual(inspect.getargspec(Class2.function), argspec) - self.assertEqual(inspect.getargspec(instance2.function), argspec) + self.assertEqual(getspec(Class2.function), argspec) + self.assertEqual(getspec(instance2.function), argspec) def test_adapter_factory(self): def factory(wrapped): - argspec = inspect.getargspec(wrapped) + argspec = getspec(wrapped) argspec.args.insert(0, 'arg0') return argspec @@ -217,7 +225,7 @@ def _wrapper_1(wrapped, instance, args, kwargs): def _function_1(arg1, arg2): pass - argspec = inspect.getargspec(_function_1) + argspec = getspec(_function_1) self.assertEqual(argspec.args, ['arg0', 'arg1', 'arg2']) From 858ae8cd77eecf8229463db0baaa00c5abf6cc14 Mon Sep 17 00:00:00 2001 From: grayjk Date: Mon, 10 Jan 2022 11:25:59 -0800 Subject: [PATCH 2/2] python2 compat --- src/wrapt/decorators.py | 42 ++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/wrapt/decorators.py b/src/wrapt/decorators.py index 95c187e0..86268cac 100644 --- a/src/wrapt/decorators.py +++ b/src/wrapt/decorators.py @@ -31,14 +31,14 @@ def exec_(_code_, _globs_=None, _locs_=None): del builtins from functools import partial -from inspect import ismethod, isclass, Signature, Parameter +from inspect import ismethod, isclass from collections import namedtuple from threading import Lock, RLock try: - from inspect import signature + from inspect import signature, Signature, Parameter except ImportError: - pass + from inspect import formatargspec from .wrappers import (FunctionWrapper, BoundFunctionWrapper, ObjectProxy, CallableObjectProxy) @@ -166,22 +166,26 @@ def __call__(self, wrapped): adapter_factory = DelegatedAdapterFactory -def _formatargspec(spec): - params = [] - for arg in spec.args: - params.append(Parameter(arg, Parameter.POSITIONAL_OR_KEYWORD)) - if spec.defaults: - for i, d in enumerate(reversed(spec.defaults)): - idx = len(params) - 1 - i - params[idx] = params[idx].replace(default=d) - if spec.varargs: - params.append(Parameter(spec.varargs, Parameter.VAR_POSITIONAL)) - if hasattr(spec, 'keywords') and spec.keywords: - params.append(Parameter(spec.keywords, Parameter.VAR_KEYWORD)) - if hasattr(spec, 'varkw') and spec.varkw: - params.append(Parameter(spec.varkw, Parameter.VAR_KEYWORD)) - - return str(Signature(params)) +if PY2: + def _formatargspec(spec): + return formatargspec(*spec) +else: + def _formatargspec(spec): + params = [] + for arg in spec.args: + params.append(Parameter(arg, Parameter.POSITIONAL_OR_KEYWORD)) + if spec.defaults: + for i, d in enumerate(reversed(spec.defaults)): + idx = len(params) - 1 - i + params[idx] = params[idx].replace(default=d) + if spec.varargs: + params.append(Parameter(spec.varargs, Parameter.VAR_POSITIONAL)) + if hasattr(spec, 'keywords') and spec.keywords: + params.append(Parameter(spec.keywords, Parameter.VAR_KEYWORD)) + if hasattr(spec, 'varkw') and spec.varkw: + params.append(Parameter(spec.varkw, Parameter.VAR_KEYWORD)) + + return str(Signature(params)) # Decorator for creating other decorators. This decorator and the # wrappers which they use are designed to properly preserve any name