archrelease: copy trunk to extra-x86_64
[arch-packages.git] / python-wrapt / trunk / py39.patch
blob29617ae1bc62346aeae4f77b5e90310c1f7f733b
1 From 33708e76578c173333d1879a4a21baddf8fcdb6a Mon Sep 17 00:00:00 2001
2 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= <mgorny@gentoo.org>
3 Date: Fri, 29 May 2020 16:06:07 +0200
4 Subject: [PATCH] Update for fixed outer @classmethod behavior in Python 3.9
6 Fixes #160
7 ---
8 docs/decorators.rst | 18 ++++++-------
9 tests/test_outer_classmethod.py | 45 +++++++++++++++++++++------------
10 tests/test_synchronized_lock.py | 22 ++++++++--------
11 3 files changed, 49 insertions(+), 36 deletions(-)
13 diff --git a/docs/decorators.rst b/docs/decorators.rst
14 index b8200d6..94201de 100644
15 --- a/docs/decorators.rst
16 +++ b/docs/decorators.rst
17 @@ -641,15 +641,15 @@ When calling the wrapped function in the decorator wrapper function, the
18 instance is already bound to ``wrapped`` and will be passed automatically
19 as the first argument to the original wrapped function.
21 -Note that due to a bug in Python ``classmethod.__get__()``, whereby it does
22 -not apply the descriptor protocol to the function wrapped by ``@classmethod``,
23 -the above only applies where the decorator wraps the ``@classmethod``
24 -decorator. If the decorator is placed inside of the ``@classmethod``
25 -decorator, then ``instance`` will be ``None`` and the decorator wrapper
26 -function will see the call as being the same as a normal function. As a
27 -result, always place any decorator outside of the ``@classmethod``
28 -decorator. Hopefully this issue in Python can be addressed in a future
29 -Python version.
30 +Note that due to a bug in Python prior to 3.9 ``classmethod.__get__()``,
31 +whereby it does not apply the descriptor protocol to the function
32 +wrapped by ``@classmethod``, the above only applies where the decorator
33 +wraps the ``@classmethod`` decorator. If the decorator is placed inside
34 +of the ``@classmethod`` decorator, then ``instance`` will be ``None``
35 +and the decorator wrapper function will see the call as being the same
36 +as a normal function. As a result, always place any decorator outside of
37 +the ``@classmethod`` decorator if you need to support earlier Python
38 +versions.
40 Decorating Static Methods
41 -------------------------
42 diff --git a/tests/test_outer_classmethod.py b/tests/test_outer_classmethod.py
43 index 6b4af4f..9c2fcb8 100644
44 --- a/tests/test_outer_classmethod.py
45 +++ b/tests/test_outer_classmethod.py
46 @@ -3,6 +3,7 @@
47 import unittest
48 import inspect
49 import imp
50 +import sys
52 import wrapt
54 @@ -121,20 +122,26 @@ def test_instance_isinstance(self):
55 class TestCallingOuterClassMethod(unittest.TestCase):
57 def test_class_call_function(self):
58 - # Test calling classmethod. The instance and class passed to the
59 - # wrapper will both be None because our decorator is surrounded
60 - # by the classmethod decorator. The classmethod decorator
61 - # doesn't bind the method and treats it like a normal function,
62 - # explicitly passing the class as the first argument with the
63 - # actual arguments following that.
64 + # Test calling classmethod. In Python 3.9, the class will be
65 + # passed as instance. In older versions of Python, the instance
66 + # and class passed to the wrapper will both be None because our
67 + # decorator is surrounded by the classmethod decorator.
68 + # The classmethod decorator doesn't bind the method and treats
69 + # it like a normal function, explicitly passing the class
70 + # as the first argument with the actual arguments following
71 + # that.
73 _args = (1, 2)
74 _kwargs = {'one': 1, 'two': 2}
76 @wrapt.decorator
77 def _decorator(wrapped, instance, args, kwargs):
78 - self.assertEqual(instance, None)
79 - self.assertEqual(args, (Class,)+_args)
80 + if sys.hexversion >= 0x03090000:
81 + self.assertEqual(instance, Class)
82 + self.assertEqual(args, _args)
83 + else:
84 + self.assertEqual(instance, None)
85 + self.assertEqual(args, (Class,)+_args)
86 self.assertEqual(kwargs, _kwargs)
87 self.assertEqual(wrapped.__module__, _function.__module__)
88 self.assertEqual(wrapped.__name__, _function.__name__)
89 @@ -155,20 +162,26 @@ def _function(cls, *args, **kwargs):
90 self.assertEqual(result, (_args, _kwargs))
92 def test_instance_call_function(self):
93 - # Test calling classmethod via class instance. The instance
94 - # and class passed to the wrapper will both be None because our
95 - # decorator is surrounded by the classmethod decorator. The
96 - # classmethod decorator doesn't bind the method and treats it
97 - # like a normal function, explicitly passing the class as the
98 - # first argument with the actual arguments following that.
99 + # Test calling classmethod via class instance. In Python 3.9,
100 + # the class will be passed as instance. In older versions
101 + # of Python, the instance and class passed to the wrapper will
102 + # both be None because our decorator is surrounded
103 + # by the classmethod decorator. The classmethod decorator
104 + # doesn't bind the method and treats it like a normal function,
105 + # explicitly passing the class as the first argument with
106 + # the actual arguments following that.
108 _args = (1, 2)
109 _kwargs = {'one': 1, 'two': 2}
111 @wrapt.decorator
112 def _decorator(wrapped, instance, args, kwargs):
113 - self.assertEqual(instance, None)
114 - self.assertEqual(args, (Class,)+_args)
115 + if sys.hexversion >= 0x03090000:
116 + self.assertEqual(instance, Class)
117 + self.assertEqual(args, _args)
118 + else:
119 + self.assertEqual(instance, None)
120 + self.assertEqual(args, (Class,)+_args)
121 self.assertEqual(kwargs, _kwargs)
122 self.assertEqual(wrapped.__module__, _function.__module__)
123 self.assertEqual(wrapped.__name__, _function.__name__)
124 diff --git a/tests/test_synchronized_lock.py b/tests/test_synchronized_lock.py
125 index 6e7eb12..b8f60f3 100644
126 --- a/tests/test_synchronized_lock.py
127 +++ b/tests/test_synchronized_lock.py
128 @@ -1,5 +1,6 @@
129 from __future__ import print_function
131 +import sys
132 import unittest
134 import wrapt
135 @@ -157,34 +158,33 @@ def test_synchronized_inner_classmethod(self):
136 self.assertEqual(_lock3, _lock2)
138 def test_synchronized_outer_classmethod(self):
139 - # XXX If all was good, this would be detected as a class
140 + # Bug in Python < 3.9:
141 + # If all was good, this would be detected as a class
142 # method call, but the classmethod decorator doesn't bind
143 # the wrapped function to the class before calling and
144 # just calls it direct, explicitly passing the class as
145 - # first argument. This screws things up. Would be nice if
146 - # Python were fixed, but that isn't likely to happen.
147 + # first argument. This screws things up.
149 - #_lock0 = getattr(C4, '_synchronized_lock', None)
150 - _lock0 = getattr(C4.function2, '_synchronized_lock', None)
151 + lock_target = (C4 if sys.hexversion >= 0x03090000
152 + else C4.function2)
154 + _lock0 = getattr(lock_target, '_synchronized_lock', None)
155 self.assertEqual(_lock0, None)
157 c4.function2()
159 - #_lock1 = getattr(C4, '_synchronized_lock', None)
160 - _lock1 = getattr(C4.function2, '_synchronized_lock', None)
161 + _lock1 = getattr(lock_target, '_synchronized_lock', None)
162 self.assertNotEqual(_lock1, None)
164 C4.function2()
166 - #_lock2 = getattr(C4, '_synchronized_lock', None)
167 - _lock2 = getattr(C4.function2, '_synchronized_lock', None)
168 + _lock2 = getattr(lock_target, '_synchronized_lock', None)
169 self.assertNotEqual(_lock2, None)
170 self.assertEqual(_lock2, _lock1)
172 C4.function2()
174 - #_lock3 = getattr(C4, '_synchronized_lock', None)
175 - _lock3 = getattr(C4.function2, '_synchronized_lock', None)
176 + _lock3 = getattr(lock_target, '_synchronized_lock', None)
177 self.assertNotEqual(_lock3, None)
178 self.assertEqual(_lock3, _lock2)