codekingpro/portable-devtools
114k
1/* This is a set of functions used by test_extension_interface.py to test the
2 * Greenlet C API.
3 */
4
5#include "../greenlet.h"
6
7#ifndef Py_RETURN_NONE
8# define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
9#endif
10
11#define TEST_MODULE_NAME "_test_extension"
12
13// CAUTION: MSVC is stupidly picky:
14//
15// "The compiler ignores, without warning, any __declspec keywords
16// placed after * or & and in front of the variable identifier in a
17// declaration."
18// (https://docs.microsoft.com/en-us/cpp/cpp/declspec?view=msvc-160)
19//
20// So pointer return types must be handled differently (because of the
21// trailing *), or you get inscrutable compiler warnings like "error
22// C2059: syntax error: ''"
23//
24// In C23, there is a standard syntax for attributes, and
25// GCC defines an attribute to use with this: [[gnu:noinline]].
26// In the future, this is expected to become standard.
27
28#if defined(__GNUC__) || defined(__clang__)
29/* We used to check for GCC 4+ or 3.4+, but those compilers are
30 laughably out of date. Just assume they support it. */
31# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
32#elif defined(_MSC_VER)
33/* We used to check for && (_MSC_VER >= 1300) but that's also out of date. */
34# define UNUSED(x) UNUSED_ ## x
35#endif
36
37static PyObject*
38test_switch(PyObject* UNUSED(self), PyObject* greenlet)
39{
40 PyObject* result = NULL;
41
42 if (greenlet == NULL || !PyGreenlet_Check(greenlet)) {
43 PyErr_BadArgument();
44 return NULL;
45 }
46
47 result = PyGreenlet_Switch((PyGreenlet*)greenlet, NULL, NULL);
48 if (result == NULL) {
49 if (!PyErr_Occurred()) {
50 PyErr_SetString(PyExc_AssertionError,
51 "greenlet.switch() failed for some reason.");
52 }
53 return NULL;
54 }
55 return result;
56}
57
58static PyObject*
59test_switch_kwargs(PyObject* UNUSED(self), PyObject* args, PyObject* kwargs)
60{
61 PyGreenlet* g = NULL;
62 PyObject* result = NULL;
63
64 if (!PyArg_ParseTuple(args, "O!", &PyGreenlet_Type, &g)) {
65 return NULL;
66 }
67
68 if (g == NULL || !PyGreenlet_Check(g)) {
69 PyErr_BadArgument();
70 return NULL;
71 }
72
73 result = PyGreenlet_Switch(g, NULL, kwargs);
74 if (result == NULL) {
75 if (!PyErr_Occurred()) {
76 PyErr_SetString(PyExc_AssertionError,
77 "greenlet.switch() failed for some reason.");
78 }
79 return NULL;
80 }
81 return result;
82}
83
84static PyObject*
85test_getcurrent(PyObject* UNUSED(self))
86{
87 PyGreenlet* g = PyGreenlet_GetCurrent();
88 if (g == NULL || !PyGreenlet_Check(g) || !PyGreenlet_ACTIVE(g)) {
89 PyErr_SetString(PyExc_AssertionError,
90 "getcurrent() returned an invalid greenlet");
91 Py_XDECREF(g);
92 return NULL;
93 }
94 Py_DECREF(g);
95 Py_RETURN_NONE;
96}
97
98static PyObject*
99test_setparent(PyObject* UNUSED(self), PyObject* arg)
100{
101 PyGreenlet* current;
102 PyGreenlet* greenlet = NULL;
103 PyObject* switch_result = NULL;
104
105 if (arg == NULL || !PyGreenlet_Check(arg)) {
106 PyErr_BadArgument();
107 return NULL;
108 }
109 if ((current = PyGreenlet_GetCurrent()) == NULL) {
110 return NULL;
111 }
112 greenlet = (PyGreenlet*)arg;
113 if (PyGreenlet_SetParent(greenlet, current)) {
114 Py_DECREF(current);
115 return NULL;
116 }
117 Py_DECREF(current);
118
119 switch_result = PyGreenlet_Switch(greenlet, NULL, NULL);
120 if (switch_result == NULL) {
121 return NULL;
122 }
123 Py_DECREF(switch_result);
124 Py_RETURN_NONE;
125}
126
127static PyObject*
128test_new_greenlet(PyObject* UNUSED(self), PyObject* callable)
129{
130 PyObject* result = NULL;
131 PyGreenlet* greenlet = PyGreenlet_New(callable, NULL);
132
133 if (!greenlet) {
134 return NULL;
135 }
136
137 result = PyGreenlet_Switch(greenlet, NULL, NULL);
138 Py_CLEAR(greenlet);
139 if (result == NULL) {
140 return NULL;
141 }
142
143 return result;
144}
145
146static PyObject*
147test_raise_dead_greenlet(PyObject* UNUSED(self))
148{
149 PyErr_SetString(PyExc_GreenletExit, "test GreenletExit exception.");
150 return NULL;
151}
152
153static PyObject*
154test_raise_greenlet_error(PyObject* UNUSED(self))
155{
156 PyErr_SetString(PyExc_GreenletError, "test greenlet.error exception");
157 return NULL;
158}
159
160static PyObject*
161test_throw(PyObject* UNUSED(self), PyGreenlet* g)
162{
163 const char msg[] = "take that sucka!";
164 PyObject* msg_obj = Py_BuildValue("s", msg);
165 PyGreenlet_Throw(g, PyExc_ValueError, msg_obj, NULL);
166 Py_DECREF(msg_obj);
167 if (PyErr_Occurred()) {
168 return NULL;
169 }
170 Py_RETURN_NONE;
171}
172
173static PyObject*
174test_throw_exact(PyObject* UNUSED(self), PyObject* args)
175{
176 PyGreenlet* g = NULL;
177 PyObject* typ = NULL;
178 PyObject* val = NULL;
179 PyObject* tb = NULL;
180
181 if (!PyArg_ParseTuple(args, "OOOO:throw", &g, &typ, &val, &tb)) {
182 return NULL;
183 }
184
185 PyGreenlet_Throw(g, typ, val, tb);
186 if (PyErr_Occurred()) {
187 return NULL;
188 }
189 Py_RETURN_NONE;
190}
191
192static PyObject*
193getcurrent_api(PyObject* UNUSED(self))
194{
195 return (PyObject*)PyGreenlet_GetCurrent();
196
197}
198
199static PyMethodDef test_methods[] = {
200 {"test_switch",
201 (PyCFunction)test_switch,
202 METH_O,
203 "Switch to the provided greenlet sending provided arguments, and \n"
204 "return the results."},
205 {"test_switch_kwargs",
206 (PyCFunction)test_switch_kwargs,
207 METH_VARARGS | METH_KEYWORDS,
208 "Switch to the provided greenlet sending the provided keyword args."},
209 {"test_getcurrent",
210 (PyCFunction)test_getcurrent,
211 METH_NOARGS,
212 "Test PyGreenlet_GetCurrent()"},
213 {"test_setparent",
214 (PyCFunction)test_setparent,
215 METH_O,
216 "Se the parent of the provided greenlet and switch to it."},
217 {"test_new_greenlet",
218 (PyCFunction)test_new_greenlet,
219 METH_O,
220 "Test PyGreenlet_New()"},
221 {"test_raise_dead_greenlet",
222 (PyCFunction)test_raise_dead_greenlet,
223 METH_NOARGS,
224 "Just raise greenlet.GreenletExit"},
225 {"test_raise_greenlet_error",
226 (PyCFunction)test_raise_greenlet_error,
227 METH_NOARGS,
228 "Just raise greenlet.error"},
229 {"test_throw",
230 (PyCFunction)test_throw,
231 METH_O,
232 "Throw a ValueError at the provided greenlet"},
233 {"test_throw_exact",
234 (PyCFunction)test_throw_exact,
235 METH_VARARGS,
236 "Throw exactly the arguments given at the provided greenlet"},
237 {
238 "getcurrent_api",
239 (PyCFunction)getcurrent_api,
240 METH_NOARGS,
241 "Direct call to the PyGreenlet_GetCurrent API."},
242 {NULL, NULL, 0, NULL}
243};
244
245
246#define INITERROR return NULL
247
248static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT,
249 TEST_MODULE_NAME,
250 NULL,
251 0,
252 test_methods,
253 NULL,
254 NULL,
255 NULL,
256 NULL};
257
258PyMODINIT_FUNC
259PyInit__test_extension(void)
260{
261 PyObject* module = NULL;
262 module = PyModule_Create(&moduledef);
263
264 if (module == NULL) {
265 return NULL;
266 }
267
268 PyGreenlet_Import();
269#ifdef Py_GIL_DISABLED
270 PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED);
271#endif
272 return module;
273}
274 