this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "Python.h"
3#include "gtest/gtest.h"
4
5#include "capi-fixture.h"
6#include "capi-testing.h"
7
8namespace py {
9namespace testing {
10
11using PyCFunctionExtensionApiTest = ExtensionApi;
12
13PyObject* getPyCFunctionDunderModule(PyObject* function) {
14 PyObject* real_function = function;
15 // Work around PyRo behavior.
16 if (PyMethod_Check(function)) {
17 real_function = PyMethod_Function(function);
18 }
19 return PyObject_GetAttrString(real_function, "__module__");
20}
21
22TEST_F(PyCFunctionExtensionApiTest, CheckWithNonFunctionReturnsFalse) {
23 EXPECT_FALSE(PyCFunction_Check(Py_True));
24}
25
26TEST_F(PyCFunctionExtensionApiTest, CheckWithNonExtensionFunctionReturnsFalse) {
27 PyRun_SimpleString("def func(): pass");
28 PyObjectPtr func(mainModuleGet("func"));
29 EXPECT_FALSE(PyCFunction_Check(func));
30}
31
32TEST_F(PyCFunctionExtensionApiTest, CheckWithBoundMethodReturnsFalse) {
33 PyRun_SimpleString(R"(
34class C:
35 def foo(self):
36 pass
37method = C.foo
38)");
39 PyObjectPtr method(mainModuleGet("method"));
40 EXPECT_FALSE(PyCFunction_Check(method));
41}
42
43TEST_F(PyCFunctionExtensionApiTest,
44 GetFunctionWithNonCFunctionRaisesBadInternalCall) {
45 PyObjectPtr value(PyLong_FromLong(42));
46 EXPECT_EQ(PyCFunction_GetFunction(value), nullptr);
47 EXPECT_NE(PyErr_Occurred(), nullptr);
48 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
49}
50
51TEST_F(PyCFunctionExtensionApiTest,
52 GetSelfWithNonCFunctionRaisesBadInternalCall) {
53 PyObjectPtr value(PyLong_FromLong(42));
54 EXPECT_EQ(PyCFunction_GetSelf(value), nullptr);
55 EXPECT_NE(PyErr_Occurred(), nullptr);
56 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
57}
58
59TEST_F(PyCFunctionExtensionApiTest, GET_SELFReturnsSelf) {
60 PyObjectPtr self_value(PyUnicode_FromString("baz"));
61 binaryfunc meth = [](PyObject* self, PyObject* arg) {
62 EXPECT_EQ(arg, nullptr);
63 Py_INCREF(self);
64 return self;
65 };
66 static PyMethodDef func_def = {"foo", meth, METH_NOARGS};
67 PyObjectPtr func(PyCFunction_New(&func_def, self_value));
68 ASSERT_NE(func, nullptr);
69 PyObjectPtr result(_PyObject_CallNoArg(func));
70 EXPECT_EQ(result, self_value);
71 ASSERT_EQ(PyErr_Occurred(), nullptr);
72
73 EXPECT_TRUE(PyCFunction_Check(func));
74 EXPECT_EQ(PyCFunction_GET_SELF(func.get()), self_value.get());
75}
76
77TEST_F(PyCFunctionExtensionApiTest, NewReturnsCallable) {
78 PyObjectPtr self_value(PyUnicode_FromString("baz"));
79 binaryfunc meth = [](PyObject* self, PyObject* arg) {
80 EXPECT_EQ(arg, nullptr);
81 Py_INCREF(self);
82 return self;
83 };
84 static PyMethodDef func_def = {"foo", meth, METH_NOARGS};
85 PyObjectPtr func(PyCFunction_New(&func_def, self_value));
86 ASSERT_NE(func, nullptr);
87 PyObjectPtr result(_PyObject_CallNoArg(func));
88 EXPECT_EQ(result, self_value);
89 ASSERT_EQ(PyErr_Occurred(), nullptr);
90 PyObjectPtr dunder_module(getPyCFunctionDunderModule(func));
91 EXPECT_EQ(dunder_module, Py_None);
92
93 EXPECT_TRUE(PyCFunction_Check(func));
94 EXPECT_EQ(PyCFunction_GetFunction(func), meth);
95 EXPECT_EQ(PyCFunction_GetSelf(func), self_value.get());
96}
97
98TEST_F(PyCFunctionExtensionApiTest, NewExWithModuleReturnsCallable) {
99 PyObjectPtr self_value(PyUnicode_FromString("foo"));
100 PyObjectPtr module_name(PyUnicode_FromString("bar"));
101 binaryfunc meth = [](PyObject* self, PyObject* arg) {
102 EXPECT_EQ(arg, nullptr);
103 Py_INCREF(self);
104 return self;
105 };
106 static PyMethodDef foo_func = {"foo", meth, METH_NOARGS};
107 PyObjectPtr func(PyCFunction_NewEx(&foo_func, self_value, module_name));
108 ASSERT_NE(func, nullptr);
109 PyObjectPtr noargs(PyTuple_New(0));
110 PyObjectPtr result(PyObject_Call(func, noargs, nullptr));
111 EXPECT_EQ(result, self_value);
112 ASSERT_EQ(PyErr_Occurred(), nullptr);
113 PyObjectPtr dunder_module(getPyCFunctionDunderModule(func));
114 EXPECT_EQ(dunder_module, module_name);
115
116 EXPECT_TRUE(PyCFunction_Check(func));
117 EXPECT_EQ(PyCFunction_GetFunction(func), meth);
118 EXPECT_EQ(PyCFunction_GetSelf(func), self_value.get());
119}
120
121TEST_F(PyCFunctionExtensionApiTest, NewExWithNullSelfReturnsCallable) {
122 binaryfunc meth = [](PyObject* self, PyObject* arg) {
123 EXPECT_EQ(self, nullptr);
124 EXPECT_EQ(arg, nullptr);
125 Py_INCREF(Py_None);
126 return Py_None;
127 };
128 static PyMethodDef foo_func = {"foo", meth, METH_NOARGS};
129 PyObjectPtr func(
130 PyCFunction_NewEx(&foo_func, /*self=*/nullptr, /*module=*/nullptr));
131 ASSERT_NE(func, nullptr);
132 PyObjectPtr noargs(PyTuple_New(0));
133 PyObjectPtr result(PyObject_Call(func, noargs, nullptr));
134 EXPECT_EQ(result, Py_None);
135 ASSERT_EQ(PyErr_Occurred(), nullptr);
136 PyObjectPtr dunder_module(getPyCFunctionDunderModule(func));
137 EXPECT_EQ(dunder_module, Py_None);
138
139 EXPECT_TRUE(PyCFunction_Check(func));
140 EXPECT_EQ(PyCFunction_GetFunction(func), meth);
141 EXPECT_EQ(PyCFunction_GetSelf(func), nullptr);
142 EXPECT_EQ(PyErr_Occurred(), nullptr);
143}
144
145TEST_F(PyCFunctionExtensionApiTest, NewExResultDoesNotBindSelfInClass) {
146 PyRun_SimpleString(R"(
147class C:
148 pass
149instance = C()
150)");
151 PyObjectPtr self_value(PyUnicode_FromString("foo"));
152 binaryfunc meth = [](PyObject* self, PyObject* arg) {
153 EXPECT_EQ(arg, nullptr);
154 Py_INCREF(self);
155 return self;
156 };
157 static PyMethodDef foo_func = {"foo", meth, METH_NOARGS};
158 PyObjectPtr func(
159 PyCFunction_NewEx(&foo_func, self_value, /*module=*/nullptr));
160 ASSERT_NE(func, nullptr);
161 PyObjectPtr c(mainModuleGet("C"));
162 PyObjectPtr instance(mainModuleGet("instance"));
163 PyObject_SetAttrString(c, "foo", func);
164 PyObjectPtr result(PyObject_CallMethod(instance, "foo", ""));
165 EXPECT_NE(result, c);
166 EXPECT_EQ(result, self_value);
167}
168
169TEST_F(PyCFunctionExtensionApiTest, NewExWithMethNoArgsCallsFunction) {
170 PyCFunction foo_func = [](PyObject* self, PyObject* args) {
171 EXPECT_TRUE(isUnicodeEqualsCStr(self, "self"));
172 EXPECT_EQ(args, nullptr);
173 return PyLong_FromLong(-7);
174 };
175 PyMethodDef def = {
176 "foo", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(foo_func)),
177 METH_NOARGS};
178 PyObjectPtr self(PyUnicode_FromString("self"));
179 PyObjectPtr func(PyCFunction_NewEx(&def, self, nullptr));
180
181 PyObjectPtr result(_PyObject_CallNoArg(func));
182 ASSERT_TRUE(isLongEqualsLong(result, -7));
183 EXPECT_EQ(PyErr_Occurred(), nullptr);
184}
185
186TEST_F(PyCFunctionExtensionApiTest, NewExWithMethOCallsFunction) {
187 PyCFunction foo_func = [](PyObject* self, PyObject* arg) {
188 EXPECT_TRUE(isUnicodeEqualsCStr(self, "self"));
189 EXPECT_TRUE(isLongEqualsLong(arg, 42));
190 return PyLong_FromLong(1);
191 };
192 PyMethodDef def = {"foo", foo_func, METH_O};
193 PyObjectPtr self(PyUnicode_FromString("self"));
194 PyObjectPtr func(PyCFunction_NewEx(&def, self, nullptr));
195 PyObjectPtr arg(PyLong_FromLong(42));
196
197 PyObject* args[1] = {arg.get()};
198 PyObjectPtr result(_PyObject_FastCallDict(func, args, 1, nullptr));
199 ASSERT_TRUE(isLongEqualsLong(result, 1));
200 EXPECT_EQ(PyErr_Occurred(), nullptr);
201}
202
203TEST_F(PyCFunctionExtensionApiTest, NewExWithMethVarArgsCallsFunction) {
204 PyCFunction foo_func = [](PyObject* self, PyObject* args) {
205 EXPECT_TRUE(isUnicodeEqualsCStr(self, "self"));
206 EXPECT_TRUE(PyTuple_Check(args));
207 EXPECT_EQ(PyTuple_Size(args), 2);
208 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(args, 0), -14));
209 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(args, 1), 15));
210 return PyLong_FromLong(22);
211 };
212 PyMethodDef def = {"foo", foo_func, METH_VARARGS};
213 PyObjectPtr self(PyUnicode_FromString("self"));
214 PyObjectPtr func(PyCFunction_NewEx(&def, self, nullptr));
215 PyObjectPtr arg0(PyLong_FromLong(-14));
216 PyObjectPtr arg1(PyLong_FromLong(15));
217 PyObjectPtr args(PyTuple_Pack(2, arg0.get(), arg1.get()));
218
219 PyObjectPtr result(PyObject_Call(func, args, nullptr));
220 ASSERT_TRUE(isLongEqualsLong(result, 22));
221 EXPECT_EQ(PyErr_Occurred(), nullptr);
222}
223
224TEST_F(PyCFunctionExtensionApiTest, NewExWithVarArgsAndKeywordsCallsFunction) {
225 PyCFunctionWithKeywords foo_func = [](PyObject* self, PyObject* args,
226 PyObject* kwargs) -> PyObject* {
227 EXPECT_TRUE(isUnicodeEqualsCStr(self, "self"));
228 EXPECT_TRUE(PyTuple_Check(args));
229 EXPECT_EQ(PyTuple_Size(args), 2);
230 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(args, 0), -111));
231 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(args, 1), 222));
232 EXPECT_TRUE(PyDict_Check(kwargs));
233 EXPECT_EQ(PyDict_Size(kwargs), 1);
234 EXPECT_TRUE(isLongEqualsLong(PyDict_GetItemString(kwargs, "keyword"), 333));
235 return PyLong_FromLong(876);
236 };
237 PyMethodDef def = {
238 "foo", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(foo_func)),
239 METH_VARARGS | METH_KEYWORDS};
240 PyObjectPtr self(PyUnicode_FromString("self"));
241 PyObjectPtr func(PyCFunction_NewEx(&def, self, nullptr));
242
243 PyObjectPtr arg0(PyLong_FromLong(-111));
244 PyObjectPtr arg1(PyLong_FromLong(222));
245 PyObjectPtr args(PyTuple_Pack(2, arg0.get(), arg1.get()));
246 PyObjectPtr kwargs(PyDict_New());
247 PyObjectPtr value(PyLong_FromLong(333));
248 PyDict_SetItemString(kwargs, "keyword", value);
249 PyObjectPtr result(PyObject_Call(func, args, kwargs));
250 ASSERT_TRUE(isLongEqualsLong(result, 876));
251 EXPECT_EQ(PyErr_Occurred(), nullptr);
252}
253
254TEST_F(PyCFunctionExtensionApiTest, NewExWithMethFastCallCallsFunction) {
255 _PyCFunctionFast foo_func = [](PyObject* self, PyObject* const* args,
256 Py_ssize_t num_args) -> PyObject* {
257 EXPECT_TRUE(isUnicodeEqualsCStr(self, "self"));
258 EXPECT_EQ(num_args, 3);
259 EXPECT_TRUE(isLongEqualsLong(args[0], 17));
260 EXPECT_TRUE(isLongEqualsLong(args[1], -8));
261 EXPECT_TRUE(isLongEqualsLong(args[2], 99));
262 return PyLong_FromLong(4444);
263 };
264 PyMethodDef def = {
265 "foo", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(foo_func)),
266 METH_FASTCALL};
267 PyObjectPtr self(PyUnicode_FromString("self"));
268 PyObjectPtr func(PyCFunction_NewEx(&def, self, nullptr));
269
270 PyObjectPtr arg0(PyLong_FromLong(17));
271 PyObjectPtr arg1(PyLong_FromLong(-8));
272 PyObjectPtr arg2(PyLong_FromLong(99));
273 PyObjectPtr args(PyTuple_Pack(3, arg0.get(), arg1.get(), arg2.get()));
274 PyObjectPtr result(PyObject_Call(func, args, nullptr));
275 ASSERT_TRUE(isLongEqualsLong(result, 4444));
276 EXPECT_EQ(PyErr_Occurred(), nullptr);
277}
278
279TEST_F(PyCFunctionExtensionApiTest,
280 NewExWithMethFastCallAndKeywordsCallsFunction) {
281 _PyCFunctionFastWithKeywords foo_func =
282 [](PyObject* self, PyObject* const* args, Py_ssize_t num_args,
283 PyObject* kwnames) -> PyObject* {
284 EXPECT_TRUE(isUnicodeEqualsCStr(self, "self"));
285 EXPECT_EQ(num_args, 1);
286 EXPECT_TRUE(isLongEqualsLong(args[0], 42));
287 EXPECT_TRUE(isLongEqualsLong(args[1], 30));
288 EXPECT_TRUE(PyTuple_Check(kwnames));
289 EXPECT_EQ(PyTuple_Size(kwnames), 1);
290 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(kwnames, 0), "keyword"));
291 return PyLong_FromLong(333);
292 };
293 PyMethodDef def = {
294 "foo", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(foo_func)),
295 METH_FASTCALL | METH_KEYWORDS};
296 PyObjectPtr self(PyUnicode_FromString("self"));
297 PyObjectPtr func(PyCFunction_NewEx(&def, self, nullptr));
298
299 PyObjectPtr arg(PyLong_FromLong(42));
300 PyObjectPtr args(PyTuple_Pack(1, arg.get()));
301 PyObjectPtr kwargs(PyDict_New());
302 PyObjectPtr value(PyLong_FromLong(30));
303 PyDict_SetItemString(kwargs, "keyword", value);
304 PyObjectPtr result(PyObject_Call(func, args, kwargs));
305 ASSERT_TRUE(isLongEqualsLong(result, 333));
306 EXPECT_EQ(PyErr_Occurred(), nullptr);
307}
308
309} // namespace testing
310} // namespace py