bpo-30524: Write unit tests for FASTCALL (#2022) (#2030) · python/cpython@b757745 (original) (raw)
``
1
`+
import datetime
`
1
2
`import unittest
`
``
3
`+
from test.support import cpython_only
`
``
4
`+
try:
`
``
5
`+
import _testcapi
`
``
6
`+
except ImportError:
`
``
7
`+
_testcapi = None
`
2
8
``
3
9
`# The test cases here cover several paths through the function calling
`
4
10
`# code. They depend on the METH_XXX flag that is used to define a C
`
`@@ -122,5 +128,175 @@ def test_oldargs1_2_kw(self):
`
122
128
`self.assertRaises(TypeError, [].count, x=2, y=2)
`
123
129
``
124
130
``
``
131
`+
def pyfunc(arg1, arg2):
`
``
132
`+
return [arg1, arg2]
`
``
133
+
``
134
+
``
135
`+
def pyfunc_noarg():
`
``
136
`+
return "noarg"
`
``
137
+
``
138
+
``
139
`+
class PythonClass:
`
``
140
`+
def method(self, arg1, arg2):
`
``
141
`+
return [arg1, arg2]
`
``
142
+
``
143
`+
def method_noarg(self):
`
``
144
`+
return "noarg"
`
``
145
+
``
146
`+
@classmethod
`
``
147
`+
def class_method(cls):
`
``
148
`+
return "classmethod"
`
``
149
+
``
150
`+
@staticmethod
`
``
151
`+
def static_method():
`
``
152
`+
return "staticmethod"
`
``
153
+
``
154
+
``
155
`+
PYTHON_INSTANCE = PythonClass()
`
``
156
+
``
157
+
``
158
`+
IGNORE_RESULT = object()
`
``
159
+
``
160
+
``
161
`+
@cpython_only
`
``
162
`+
class FastCallTests(unittest.TestCase):
`
``
163
`+
Test calls with positional arguments
`
``
164
`+
CALLS_POSARGS = (
`
``
165
`+
(func, args: tuple, result)
`
``
166
+
``
167
`+
Python function with 2 arguments
`
``
168
`+
(pyfunc, (1, 2), [1, 2]),
`
``
169
+
``
170
`+
Python function without argument
`
``
171
`+
(pyfunc_noarg, (), "noarg"),
`
``
172
+
``
173
`+
Python class methods
`
``
174
`+
(PythonClass.class_method, (), "classmethod"),
`
``
175
`+
(PythonClass.static_method, (), "staticmethod"),
`
``
176
+
``
177
`+
Python instance methods
`
``
178
`+
(PYTHON_INSTANCE.method, (1, 2), [1, 2]),
`
``
179
`+
(PYTHON_INSTANCE.method_noarg, (), "noarg"),
`
``
180
`+
(PYTHON_INSTANCE.class_method, (), "classmethod"),
`
``
181
`+
(PYTHON_INSTANCE.static_method, (), "staticmethod"),
`
``
182
+
``
183
`+
C function: METH_NOARGS
`
``
184
`+
(globals, (), IGNORE_RESULT),
`
``
185
+
``
186
`+
C function: METH_O
`
``
187
`+
(id, ("hello",), IGNORE_RESULT),
`
``
188
+
``
189
`+
C function: METH_VARARGS
`
``
190
`+
(dir, (1,), IGNORE_RESULT),
`
``
191
+
``
192
`+
C function: METH_VARARGS | METH_KEYWORDS
`
``
193
`+
(min, (5, 9), 5),
`
``
194
+
``
195
`+
C function: METH_FASTCALL
`
``
196
`+
(divmod, (1000, 33), (30, 10)),
`
``
197
+
``
198
`+
C type static method: METH_FASTCALL | METH_CLASS
`
``
199
`+
(int.from_bytes, (b'\x01\x00', 'little'), 1),
`
``
200
+
``
201
`+
bpo-30524: Test that calling a C type static method with no argument
`
``
202
`+
doesn't crash (ignore the result): METH_FASTCALL | METH_CLASS
`
``
203
`+
(datetime.datetime.now, (), IGNORE_RESULT),
`
``
204
`+
)
`
``
205
+
``
206
`+
Test calls with positional and keyword arguments
`
``
207
`+
CALLS_KWARGS = (
`
``
208
`+
(func, args: tuple, kwargs: dict, result)
`
``
209
+
``
210
`+
Python function with 2 arguments
`
``
211
`+
(pyfunc, (1,), {'arg2': 2}, [1, 2]),
`
``
212
`+
(pyfunc, (), {'arg1': 1, 'arg2': 2}, [1, 2]),
`
``
213
+
``
214
`+
Python instance methods
`
``
215
`+
(PYTHON_INSTANCE.method, (1,), {'arg2': 2}, [1, 2]),
`
``
216
`+
(PYTHON_INSTANCE.method, (), {'arg1': 1, 'arg2': 2}, [1, 2]),
`
``
217
+
``
218
`+
C function: METH_VARARGS | METH_KEYWORDS
`
``
219
`+
(max, ([],), {'default': 9}, 9),
`
``
220
+
``
221
`+
C type static method: METH_FASTCALL | METH_CLASS
`
``
222
`+
(int.from_bytes, (b'\x01\x00',), {'byteorder': 'little'}, 1),
`
``
223
`+
(int.from_bytes, (), {'bytes': b'\x01\x00', 'byteorder': 'little'}, 1),
`
``
224
`+
)
`
``
225
+
``
226
`+
def check_result(self, result, expected):
`
``
227
`+
if expected is IGNORE_RESULT:
`
``
228
`+
return
`
``
229
`+
self.assertEqual(result, expected)
`
``
230
+
``
231
`+
def test_fastcall(self):
`
``
232
`+
Test _PyObject_FastCall()
`
``
233
+
``
234
`+
for func, args, expected in self.CALLS_POSARGS:
`
``
235
`+
with self.subTest(func=func, args=args):
`
``
236
`+
result = _testcapi.pyobject_fastcall(func, args)
`
``
237
`+
self.check_result(result, expected)
`
``
238
+
``
239
`+
if not args:
`
``
240
`+
args=NULL, nargs=0
`
``
241
`+
result = _testcapi.pyobject_fastcall(func, None)
`
``
242
`+
self.check_result(result, expected)
`
``
243
+
``
244
`+
def test_fastcall_dict(self):
`
``
245
`+
Test _PyObject_FastCallDict()
`
``
246
+
``
247
`+
for func, args, expected in self.CALLS_POSARGS:
`
``
248
`+
with self.subTest(func=func, args=args):
`
``
249
`+
kwargs=NULL
`
``
250
`+
result = _testcapi.pyobject_fastcalldict(func, args, None)
`
``
251
`+
self.check_result(result, expected)
`
``
252
+
``
253
`+
kwargs={}
`
``
254
`+
result = _testcapi.pyobject_fastcalldict(func, args, {})
`
``
255
`+
self.check_result(result, expected)
`
``
256
+
``
257
`+
if not args:
`
``
258
`+
args=NULL, nargs=0, kwargs=NULL
`
``
259
`+
result = _testcapi.pyobject_fastcalldict(func, None, None)
`
``
260
`+
self.check_result(result, expected)
`
``
261
+
``
262
`+
args=NULL, nargs=0, kwargs={}
`
``
263
`+
result = _testcapi.pyobject_fastcalldict(func, None, {})
`
``
264
`+
self.check_result(result, expected)
`
``
265
+
``
266
`+
for func, args, kwargs, expected in self.CALLS_KWARGS:
`
``
267
`+
with self.subTest(func=func, args=args, kwargs=kwargs):
`
``
268
`+
result = _testcapi.pyobject_fastcalldict(func, args, kwargs)
`
``
269
`+
self.check_result(result, expected)
`
``
270
+
``
271
`+
def test_fastcall_keywords(self):
`
``
272
`+
Test _PyObject_FastCallKeywords()
`
``
273
+
``
274
`+
for func, args, expected in self.CALLS_POSARGS:
`
``
275
`+
with self.subTest(func=func, args=args):
`
``
276
`+
kwnames=NULL
`
``
277
`+
result = _testcapi.pyobject_fastcallkeywords(func, args, None)
`
``
278
`+
self.check_result(result, expected)
`
``
279
+
``
280
`+
kwnames=()
`
``
281
`+
result = _testcapi.pyobject_fastcallkeywords(func, args, ())
`
``
282
`+
self.check_result(result, expected)
`
``
283
+
``
284
`+
if not args:
`
``
285
`+
kwnames=NULL
`
``
286
`+
result = _testcapi.pyobject_fastcallkeywords(func, None, None)
`
``
287
`+
self.check_result(result, expected)
`
``
288
+
``
289
`+
kwnames=()
`
``
290
`+
result = _testcapi.pyobject_fastcallkeywords(func, None, ())
`
``
291
`+
self.check_result(result, expected)
`
``
292
+
``
293
`+
for func, args, kwargs, expected in self.CALLS_KWARGS:
`
``
294
`+
with self.subTest(func=func, args=args, kwargs=kwargs):
`
``
295
`+
kwnames = tuple(kwargs.keys())
`
``
296
`+
args = args + tuple(kwargs.values())
`
``
297
`+
result = _testcapi.pyobject_fastcallkeywords(func, args, kwnames)
`
``
298
`+
self.check_result(result, expected)
`
``
299
+
``
300
+
125
301
`if name == "main":
`
126
302
`unittest.main()
`