Gentoo Archives: gentoo-commits

From: "Michał Górny" <mgorny@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] repo/gentoo:master commit in: dev-python/wrapt/files/, dev-python/wrapt/
Date: Sat, 30 May 2020 07:40:35
Message-Id: 1590824417.40dcb32287d8bdaca28bd0597e2a66d85e3d3ac1.mgorny@gentoo
1 commit: 40dcb32287d8bdaca28bd0597e2a66d85e3d3ac1
2 Author: Michał Górny <mgorny <AT> gentoo <DOT> org>
3 AuthorDate: Fri May 29 14:09:26 2020 +0000
4 Commit: Michał Górny <mgorny <AT> gentoo <DOT> org>
5 CommitDate: Sat May 30 07:40:17 2020 +0000
6 URL: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=40dcb322
7
8 dev-python/wrapt: Port to py39
9
10 Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>
11
12 dev-python/wrapt/files/wrapt-1.12.1-py39.patch | 182 +++++++++++++++++++++++++
13 dev-python/wrapt/wrapt-1.12.1.ebuild | 6 +-
14 2 files changed, 187 insertions(+), 1 deletion(-)
15
16 diff --git a/dev-python/wrapt/files/wrapt-1.12.1-py39.patch b/dev-python/wrapt/files/wrapt-1.12.1-py39.patch
17 new file mode 100644
18 index 00000000000..c3e85e09b66
19 --- /dev/null
20 +++ b/dev-python/wrapt/files/wrapt-1.12.1-py39.patch
21 @@ -0,0 +1,182 @@
22 +From 33708e76578c173333d1879a4a21baddf8fcdb6a Mon Sep 17 00:00:00 2001
23 +From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= <mgorny@g.o>
24 +Date: Fri, 29 May 2020 16:06:07 +0200
25 +Subject: [PATCH] Update for fixed outer @classmethod behavior in Python 3.9
26 +
27 +Fixes #160
28 +---
29 + docs/decorators.rst | 18 ++++++-------
30 + tests/test_outer_classmethod.py | 45 +++++++++++++++++++++------------
31 + tests/test_synchronized_lock.py | 22 ++++++++--------
32 + 3 files changed, 49 insertions(+), 36 deletions(-)
33 +
34 +diff --git a/docs/decorators.rst b/docs/decorators.rst
35 +index b8200d6..94201de 100644
36 +--- a/docs/decorators.rst
37 ++++ b/docs/decorators.rst
38 +@@ -641,15 +641,15 @@ When calling the wrapped function in the decorator wrapper function, the
39 + instance is already bound to ``wrapped`` and will be passed automatically
40 + as the first argument to the original wrapped function.
41 +
42 +-Note that due to a bug in Python ``classmethod.__get__()``, whereby it does
43 +-not apply the descriptor protocol to the function wrapped by ``@classmethod``,
44 +-the above only applies where the decorator wraps the ``@classmethod``
45 +-decorator. If the decorator is placed inside of the ``@classmethod``
46 +-decorator, then ``instance`` will be ``None`` and the decorator wrapper
47 +-function will see the call as being the same as a normal function. As a
48 +-result, always place any decorator outside of the ``@classmethod``
49 +-decorator. Hopefully this issue in Python can be addressed in a future
50 +-Python version.
51 ++Note that due to a bug in Python prior to 3.9 ``classmethod.__get__()``,
52 ++whereby it does not apply the descriptor protocol to the function
53 ++wrapped by ``@classmethod``, the above only applies where the decorator
54 ++wraps the ``@classmethod`` decorator. If the decorator is placed inside
55 ++of the ``@classmethod`` decorator, then ``instance`` will be ``None``
56 ++and the decorator wrapper function will see the call as being the same
57 ++as a normal function. As a result, always place any decorator outside of
58 ++the ``@classmethod`` decorator if you need to support earlier Python
59 ++versions.
60 +
61 + Decorating Static Methods
62 + -------------------------
63 +diff --git a/tests/test_outer_classmethod.py b/tests/test_outer_classmethod.py
64 +index 6b4af4f..9c2fcb8 100644
65 +--- a/tests/test_outer_classmethod.py
66 ++++ b/tests/test_outer_classmethod.py
67 +@@ -3,6 +3,7 @@ from __future__ import print_function
68 + import unittest
69 + import inspect
70 + import imp
71 ++import sys
72 +
73 + import wrapt
74 +
75 +@@ -121,20 +122,26 @@ class TestNamingOuterClassMethod(unittest.TestCase):
76 + class TestCallingOuterClassMethod(unittest.TestCase):
77 +
78 + def test_class_call_function(self):
79 +- # Test calling classmethod. The instance and class passed to the
80 +- # wrapper will both be None because our decorator is surrounded
81 +- # by the classmethod decorator. The classmethod decorator
82 +- # doesn't bind the method and treats it like a normal function,
83 +- # explicitly passing the class as the first argument with the
84 +- # actual arguments following that.
85 ++ # Test calling classmethod. In Python 3.9, the class will be
86 ++ # passed as instance. In older versions of Python, the instance
87 ++ # and class passed to the wrapper will both be None because our
88 ++ # decorator is surrounded by the classmethod decorator.
89 ++ # The classmethod decorator doesn't bind the method and treats
90 ++ # it like a normal function, explicitly passing the class
91 ++ # as the first argument with the actual arguments following
92 ++ # that.
93 +
94 + _args = (1, 2)
95 + _kwargs = {'one': 1, 'two': 2}
96 +
97 + @wrapt.decorator
98 + def _decorator(wrapped, instance, args, kwargs):
99 +- self.assertEqual(instance, None)
100 +- self.assertEqual(args, (Class,)+_args)
101 ++ if sys.hexversion >= 0x03090000:
102 ++ self.assertEqual(instance, Class)
103 ++ self.assertEqual(args, _args)
104 ++ else:
105 ++ self.assertEqual(instance, None)
106 ++ self.assertEqual(args, (Class,)+_args)
107 + self.assertEqual(kwargs, _kwargs)
108 + self.assertEqual(wrapped.__module__, _function.__module__)
109 + self.assertEqual(wrapped.__name__, _function.__name__)
110 +@@ -155,20 +162,26 @@ class TestCallingOuterClassMethod(unittest.TestCase):
111 + self.assertEqual(result, (_args, _kwargs))
112 +
113 + def test_instance_call_function(self):
114 +- # Test calling classmethod via class instance. The instance
115 +- # and class passed to the wrapper will both be None because our
116 +- # decorator is surrounded by the classmethod decorator. The
117 +- # classmethod decorator doesn't bind the method and treats it
118 +- # like a normal function, explicitly passing the class as the
119 +- # first argument with the actual arguments following that.
120 ++ # Test calling classmethod via class instance. In Python 3.9,
121 ++ # the class will be passed as instance. In older versions
122 ++ # of Python, the instance and class passed to the wrapper will
123 ++ # both be None because our decorator is surrounded
124 ++ # by the classmethod decorator. The classmethod decorator
125 ++ # doesn't bind the method and treats it like a normal function,
126 ++ # explicitly passing the class as the first argument with
127 ++ # the actual arguments following that.
128 +
129 + _args = (1, 2)
130 + _kwargs = {'one': 1, 'two': 2}
131 +
132 + @wrapt.decorator
133 + def _decorator(wrapped, instance, args, kwargs):
134 +- self.assertEqual(instance, None)
135 +- self.assertEqual(args, (Class,)+_args)
136 ++ if sys.hexversion >= 0x03090000:
137 ++ self.assertEqual(instance, Class)
138 ++ self.assertEqual(args, _args)
139 ++ else:
140 ++ self.assertEqual(instance, None)
141 ++ self.assertEqual(args, (Class,)+_args)
142 + self.assertEqual(kwargs, _kwargs)
143 + self.assertEqual(wrapped.__module__, _function.__module__)
144 + self.assertEqual(wrapped.__name__, _function.__name__)
145 +diff --git a/tests/test_synchronized_lock.py b/tests/test_synchronized_lock.py
146 +index 6e7eb12..b8f60f3 100644
147 +--- a/tests/test_synchronized_lock.py
148 ++++ b/tests/test_synchronized_lock.py
149 +@@ -1,5 +1,6 @@
150 + from __future__ import print_function
151 +
152 ++import sys
153 + import unittest
154 +
155 + import wrapt
156 +@@ -157,34 +158,33 @@ class TestSynchronized(unittest.TestCase):
157 + self.assertEqual(_lock3, _lock2)
158 +
159 + def test_synchronized_outer_classmethod(self):
160 +- # XXX If all was good, this would be detected as a class
161 ++ # Bug in Python < 3.9:
162 ++ # If all was good, this would be detected as a class
163 + # method call, but the classmethod decorator doesn't bind
164 + # the wrapped function to the class before calling and
165 + # just calls it direct, explicitly passing the class as
166 +- # first argument. This screws things up. Would be nice if
167 +- # Python were fixed, but that isn't likely to happen.
168 ++ # first argument. This screws things up.
169 +
170 +- #_lock0 = getattr(C4, '_synchronized_lock', None)
171 +- _lock0 = getattr(C4.function2, '_synchronized_lock', None)
172 ++ lock_target = (C4 if sys.hexversion >= 0x03090000
173 ++ else C4.function2)
174 ++
175 ++ _lock0 = getattr(lock_target, '_synchronized_lock', None)
176 + self.assertEqual(_lock0, None)
177 +
178 + c4.function2()
179 +
180 +- #_lock1 = getattr(C4, '_synchronized_lock', None)
181 +- _lock1 = getattr(C4.function2, '_synchronized_lock', None)
182 ++ _lock1 = getattr(lock_target, '_synchronized_lock', None)
183 + self.assertNotEqual(_lock1, None)
184 +
185 + C4.function2()
186 +
187 +- #_lock2 = getattr(C4, '_synchronized_lock', None)
188 +- _lock2 = getattr(C4.function2, '_synchronized_lock', None)
189 ++ _lock2 = getattr(lock_target, '_synchronized_lock', None)
190 + self.assertNotEqual(_lock2, None)
191 + self.assertEqual(_lock2, _lock1)
192 +
193 + C4.function2()
194 +
195 +- #_lock3 = getattr(C4, '_synchronized_lock', None)
196 +- _lock3 = getattr(C4.function2, '_synchronized_lock', None)
197 ++ _lock3 = getattr(lock_target, '_synchronized_lock', None)
198 + self.assertNotEqual(_lock3, None)
199 + self.assertEqual(_lock3, _lock2)
200 +
201 +--
202 +2.26.2
203 +
204
205 diff --git a/dev-python/wrapt/wrapt-1.12.1.ebuild b/dev-python/wrapt/wrapt-1.12.1.ebuild
206 index 6634d9007d7..24fccc88aaf 100644
207 --- a/dev-python/wrapt/wrapt-1.12.1.ebuild
208 +++ b/dev-python/wrapt/wrapt-1.12.1.ebuild
209 @@ -4,7 +4,7 @@
210 EAPI=7
211
212 DISTUTILS_USE_SETUPTOOLS=no
213 -PYTHON_COMPAT=( python3_{6,7,8} pypy3 )
214 +PYTHON_COMPAT=( python3_{6,7,8,9} pypy3 )
215
216 inherit distutils-r1
217
218 @@ -19,6 +19,10 @@ KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~ia64 ~ppc ~ppc64 ~s390 ~sparc ~x86 ~amd64-l
219 distutils_enable_tests pytest
220 distutils_enable_sphinx docs dev-python/sphinx_rtd_theme
221
222 +PATCHES=(
223 + "${FILESDIR}"/${P}-py39.patch
224 +)
225 +
226 python_compile() {
227 local WRAPT_EXTENSIONS=true