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()

`