Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

compatibility with python 3.11 #205

Merged
merged 2 commits into from
Mar 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- 3.8
- 3.9
- "3.10"
#- 3.11-dev
- 3.11-dev
- pypy-2.7
- pypy-3.6
- pypy-3.7
Expand Down Expand Up @@ -57,7 +57,7 @@ jobs:
- 3.8
- 3.9
- "3.10"
#- 3.11-dev
- 3.11-dev
- pypy-2.7
#- pypy-3.6
- pypy-3.7
Expand Down Expand Up @@ -117,7 +117,7 @@ jobs:
- 3.8
- 3.9
- "3.10"
#- 3.11-dev
- 3.11-dev
- pypy-2.7
- pypy-3.6
- pypy-3.7
Expand Down
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ python =
deps =
coverage
pytest
install_command =
py311,py311-{without,install,disable}-extensions: python -m pip install --no-binary coverage {opts} {packages}
commands =
python -m coverage run --rcfile {toxinidir}/setup.cfg -m pytest -v {posargs} {toxinidir}/tests
setenv =
Expand Down
35 changes: 33 additions & 2 deletions src/wrapt/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,41 @@ def exec_(_code_, _globs_=None, _locs_=None):
del builtins

from functools import partial
from inspect import ismethod, isclass, formatargspec
from collections import namedtuple
from inspect import isclass
from threading import Lock, RLock

try:
from inspect import Parameter, Signature
except ImportError: # < py3.5
from inspect import formatargspec
else:
def formatargspec(args, varargs=None, varkw=None, defaults=None,
kwonlyargs=(), kwonlydefaults={}, annotations={}):
if kwonlydefaults is None:
kwonlydefaults = {}
ndefaults = len(defaults) if defaults else 0
parameters = [
Parameter(
arg,
Parameter.POSITIONAL_OR_KEYWORD,
default=defaults[i] if i >= 0 else Parameter.empty,
annotation=annotations.get(arg, Parameter.empty),
) for i, arg in enumerate(args, ndefaults - len(args))
]
if varargs:
parameters.append(Parameter(varargs, Parameter.VAR_POSITIONAL))
parameters.extend(
Parameter(
kwonlyarg,
Parameter.KEYWORD_ONLY,
default=kwonlydefaults.get(kwonlyarg, Parameter.empty),
annotation=annotations.get(kwonlyarg, Parameter.empty),
) for kwonlyarg in kwonlyargs
)
if varkw:
parameters.append(Parameter(varkw, Parameter.VAR_KEYWORD))
return_annotation = annotations.get('return', Signature.empty)
return str(Signature(parameters, return_annotation=return_annotation))
try:
from inspect import signature
except ImportError:
Expand Down
5 changes: 5 additions & 0 deletions tests/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ def exec_(_code_, _globs_=None, _locs_=None):
exec_("""def reraise(tp, value, tb=None):
raise tp, value, tb
""")

try:
from inspect import getfullargspec
except ImportError:
from inspect import getargspec as getfullargspec
44 changes: 22 additions & 22 deletions tests/test_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import wrapt

from compat import PY2, PY3, exec_
from compat import PY2, exec_, getfullargspec

DECORATORS_CODE = """
import wrapt
Expand Down Expand Up @@ -72,15 +72,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 = getfullargspec(_adapter)
function1d_argspec = getfullargspec(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 = getfullargspec(bound_function1d)
self.assertEqual(function1a_argspec, bound_function1d_argspec)

def test_signature(self):
Expand All @@ -107,7 +107,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 = getfullargspec(_adapter)

@wrapt.decorator(adapter=argspec)
def _wrapper_1(wrapped, instance, args, kwargs):
Expand All @@ -117,9 +117,9 @@ def _wrapper_1(wrapped, instance, args, kwargs):
def _function_1():
pass

self.assertEqual(inspect.getargspec(_function_1), argspec)
self.assertEqual(getfullargspec(_function_1), argspec)

args = inspect.formatargspec(*argspec)
args = '(arg1, arg2, arg3=None, *args, **kwargs)'

@wrapt.decorator(adapter=args)
def _wrapper_2(wrapped, instance, args, kwargs):
Expand All @@ -129,12 +129,12 @@ def _wrapper_2(wrapped, instance, args, kwargs):
def _function_2():
pass

self.assertEqual(inspect.getargspec(_function_2), argspec)
self.assertEqual(getfullargspec(_function_2), argspec)

def test_dynamic_adapter_instancemethod(self):
def _adapter(self, arg1, arg2, arg3=None, *args, **kwargs): pass

argspec = inspect.getargspec(_adapter)
argspec = getfullargspec(_adapter)

@wrapt.decorator(adapter=argspec)
def _wrapper_1(wrapped, instance, args, kwargs):
Expand All @@ -147,10 +147,10 @@ def function(self):

instance1 = Class1()

self.assertEqual(inspect.getargspec(Class1.function), argspec)
self.assertEqual(inspect.getargspec(instance1.function), argspec)
self.assertEqual(getfullargspec(Class1.function), argspec)
self.assertEqual(getfullargspec(instance1.function), argspec)

args = inspect.formatargspec(*argspec)
args = '(self, arg1, arg2, arg3=None, *args, **kwargs)'

@wrapt.decorator(adapter=args)
def _wrapper_2(wrapped, instance, args, kwargs):
Expand All @@ -163,13 +163,13 @@ def function(self):

instance2 = Class2()

self.assertEqual(inspect.getargspec(Class2.function), argspec)
self.assertEqual(inspect.getargspec(instance2.function), argspec)
self.assertEqual(getfullargspec(Class2.function), argspec)
self.assertEqual(getfullargspec(instance2.function), argspec)

def test_dynamic_adapter_classmethod(self):
def _adapter(cls, arg1, arg2, arg3=None, *args, **kwargs): pass

argspec = inspect.getargspec(_adapter)
argspec = getfullargspec(_adapter)

@wrapt.decorator(adapter=argspec)
def _wrapper_1(wrapped, instance, args, kwargs):
Expand All @@ -183,10 +183,10 @@ def function(cls):

instance1 = Class1()

self.assertEqual(inspect.getargspec(Class1.function), argspec)
self.assertEqual(inspect.getargspec(instance1.function), argspec)
self.assertEqual(getfullargspec(Class1.function), argspec)
self.assertEqual(getfullargspec(instance1.function), argspec)

args = inspect.formatargspec(*argspec)
args = '(cls, arg1, arg2, arg3=None, *args, **kwargs)'

@wrapt.decorator(adapter=args)
def _wrapper_2(wrapped, instance, args, kwargs):
Expand All @@ -200,12 +200,12 @@ def function(self):

instance2 = Class2()

self.assertEqual(inspect.getargspec(Class2.function), argspec)
self.assertEqual(inspect.getargspec(instance2.function), argspec)
self.assertEqual(getfullargspec(Class2.function), argspec)
self.assertEqual(getfullargspec(instance2.function), argspec)

def test_adapter_factory(self):
def factory(wrapped):
argspec = inspect.getargspec(wrapped)
argspec = getfullargspec(wrapped)
argspec.args.insert(0, 'arg0')
return argspec

Expand All @@ -217,7 +217,7 @@ def _wrapper_1(wrapped, instance, args, kwargs):
def _function_1(arg1, arg2):
pass

argspec = inspect.getargspec(_function_1)
argspec = getfullargspec(_function_1)

self.assertEqual(argspec.args, ['arg0', 'arg1', 'arg2'])

Expand Down
9 changes: 4 additions & 5 deletions tests/test_adapter_py3.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
import inspect
import unittest
import imp
import collections

from typing import Iterable

import wrapt

from compat import PY2, PY3, exec_
from compat import PY2, exec_

DECORATORS_CODE = """
import wrapt
Expand Down Expand Up @@ -128,7 +127,7 @@ def _adapter2(arg1, arg2, arg3=None, *args, **kwargs) -> int: pass

argspec2 = inspect.getfullargspec(_adapter2)

args = inspect.formatargspec(*argspec2)
args = '(arg1, arg2, arg3=None, *args, **kwargs) -> int'

@wrapt.decorator(adapter=args)
def _wrapper_2(wrapped, instance, args, kwargs):
Expand Down Expand Up @@ -166,7 +165,7 @@ def _adapter2(self, arg1, arg2, arg3=None, *args, **kwargs) -> int: pass

argspec2 = inspect.getfullargspec(_adapter2)

args = inspect.formatargspec(*argspec2)
args = '(self, arg1, arg2, arg3=None, *args, **kwargs) -> int'

@wrapt.decorator(adapter=args)
def _wrapper_2(wrapped, instance, args, kwargs):
Expand Down Expand Up @@ -209,7 +208,7 @@ def _adapter2(cls, arg1, arg2, arg3=None, *args, **kwargs) -> int: pass

argspec2 = inspect.getfullargspec(_adapter2)

args = inspect.formatargspec(*argspec2)
args = '(cls, arg1, arg2, arg3=None, *args, **kwargs) -> int'

@wrapt.decorator(adapter=args)
def _wrapper_2(wrapped, instance, args, kwargs):
Expand Down
35 changes: 35 additions & 0 deletions tests/test_formatargspec_py35.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import unittest
import sys
from inspect import getfullargspec

from wrapt.decorators import formatargspec

class TestFormatargspec35(unittest.TestCase):

def assertFormatEqual(self, func, ref):
formatted = formatargspec(*getfullargspec(func))
self.assertEqual(formatted, ref)

def test_formatargspec(self):
def foo1(): pass
self.assertFormatEqual(foo1, '()')

def foo2(a, b='c'): pass
self.assertFormatEqual(foo2, ("(a, b='c')"))

def foo3(a, b, *args, **kwargs): pass
self.assertFormatEqual(foo3, '(a, b, *args, **kwargs)')

def foo4(a: int, b) -> list: pass
if sys.version_info[:2] < (3, 7):
formatted4 = '(a:int, b) -> list'
else:
formatted4 = '(a: int, b) -> list'
self.assertFormatEqual(foo4, formatted4)

# examples from https://www.python.org/dev/peps/pep-3102/
def sortwords(*wordlist, case_sensitive=False): pass
self.assertFormatEqual(sortwords, '(*wordlist, case_sensitive=False)')

def compare(a, b, *, key=None): pass
self.assertFormatEqual(compare, '(a, b, *, key=None)')
37 changes: 37 additions & 0 deletions tests/test_formatargspec_py38.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import unittest
import sys
from inspect import getfullargspec

from wrapt.decorators import formatargspec

class TestFormatargspec38(unittest.TestCase):

def assertFormatEqual(self, func, ref):
formatted = formatargspec(*getfullargspec(func))
self.assertEqual(formatted, ref)

def test_formatargspec(self):
# exemples from https://www.python.org/dev/peps/pep-0570/
def name1(p1, p2, /, p_or_kw, *, kw): pass
self.assertFormatEqual(name1, '(p1, p2, p_or_kw, *, kw)')

def name2(p1, p2=None, /, p_or_kw=None, *, kw): pass
self.assertFormatEqual(name2, '(p1, p2=None, p_or_kw=None, *, kw)')

def name3(p1, p2=None, /, *, kw): pass
self.assertFormatEqual(name3, '(p1, p2=None, *, kw)')

def name4(p1, p2=None, /): pass
self.assertFormatEqual(name4, '(p1, p2=None)')

def name5(p1, p2, /, p_or_kw): pass
self.assertFormatEqual(name5, '(p1, p2, p_or_kw)')

def name6(p1, p2, /): pass
self.assertFormatEqual(name6, '(p1, p2)')

def name7(p_or_kw, *, kw): pass
self.assertFormatEqual(name7, '(p_or_kw, *, kw)')

def name8(*, kw): pass
self.assertFormatEqual(name8, '(*, kw)')
6 changes: 3 additions & 3 deletions tests/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import wrapt

from compat import PY2, PY3, exec_
from compat import exec_, getfullargspec

DECORATORS_CODE = """
import wrapt
Expand Down Expand Up @@ -57,8 +57,8 @@ def test_doc_string(self):
def test_argspec(self):
# Test preservation of function argument specification.

function1o_argspec = inspect.getargspec(function1o)
function1d_argspec = inspect.getargspec(function1d)
function1o_argspec = getfullargspec(function1o)
function1d_argspec = getfullargspec(function1d)
self.assertEqual(function1o_argspec, function1d_argspec)

def test_getmembers(self):
Expand Down
11 changes: 5 additions & 6 deletions tests/test_inner_classmethod.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from __future__ import print_function

import unittest
import inspect
import imp

import wrapt

from compat import PY2, PY3, exec_
from compat import exec_, getfullargspec

DECORATORS_CODE = """
import wrapt
Expand Down Expand Up @@ -95,15 +94,15 @@ def test_instance_doc_string(self):
def test_class_argspec(self):
# Test preservation of instance method argument specification.

original_argspec = inspect.getargspec(Original.function)
function_argspec = inspect.getargspec(Class.function)
original_argspec = getfullargspec(Original.function)
function_argspec = getfullargspec(Class.function)
self.assertEqual(original_argspec, function_argspec)

def test_instance_argspec(self):
# Test preservation of instance method argument specification.

original_argspec = inspect.getargspec(Original().function)
function_argspec = inspect.getargspec(Class().function)
original_argspec = getfullargspec(Original().function)
function_argspec = getfullargspec(Class().function)
self.assertEqual(original_argspec, function_argspec)

def test_class_isinstance(self):
Expand Down
Loading