From 150fd8c689fb8fc94f42dbc69e89e40c8ff20f49 Mon Sep 17 00:00:00 2001 From: Polina Date: Thu, 2 Jul 2026 18:26:54 +0000 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=B2=20=C2=AB?= =?UTF-8?q?venv/Lib/site-packages/greenlet/tests=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../site-packages/greenlet/tests/__init__.py | 248 ++++++++++++++++ .../greenlet/tests/_test_extension.c | 273 ++++++++++++++++++ .../tests/_test_extension.cp314-win_amd64.pyd | Bin 0 -> 14336 bytes .../_test_extension_cpp.cp314-win_amd64.pyd | Bin 0 -> 14848 bytes .../greenlet/tests/_test_extension_cpp.cpp | 230 +++++++++++++++ 5 files changed, 751 insertions(+) create mode 100644 venv/Lib/site-packages/greenlet/tests/__init__.py create mode 100644 venv/Lib/site-packages/greenlet/tests/_test_extension.c create mode 100644 venv/Lib/site-packages/greenlet/tests/_test_extension.cp314-win_amd64.pyd create mode 100644 venv/Lib/site-packages/greenlet/tests/_test_extension_cpp.cp314-win_amd64.pyd create mode 100644 venv/Lib/site-packages/greenlet/tests/_test_extension_cpp.cpp diff --git a/venv/Lib/site-packages/greenlet/tests/__init__.py b/venv/Lib/site-packages/greenlet/tests/__init__.py new file mode 100644 index 0000000..88d01ab --- /dev/null +++ b/venv/Lib/site-packages/greenlet/tests/__init__.py @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- +""" +Tests for greenlet. + +""" +import os +import sys +import sysconfig +import unittest + +from gc import collect +from gc import get_objects +from threading import active_count as active_thread_count +from time import sleep +from time import time + +import psutil + +from greenlet import greenlet as RawGreenlet +from greenlet import getcurrent + +from greenlet._greenlet import get_pending_cleanup_count +from greenlet._greenlet import get_total_main_greenlets + +from . import leakcheck + +PY312 = sys.version_info[:2] >= (3, 12) +PY313 = sys.version_info[:2] >= (3, 13) +# XXX: First tested on 3.14a7. Revisit all uses of this on later versions to ensure they +# are still valid. +PY314 = sys.version_info[:2] >= (3, 14) + +WIN = sys.platform.startswith("win") +RUNNING_ON_GITHUB_ACTIONS = os.environ.get('GITHUB_ACTIONS') +RUNNING_ON_TRAVIS = os.environ.get('TRAVIS') or RUNNING_ON_GITHUB_ACTIONS +RUNNING_ON_APPVEYOR = os.environ.get('APPVEYOR') +RUNNING_ON_CI = RUNNING_ON_TRAVIS or RUNNING_ON_APPVEYOR +RUNNING_ON_MANYLINUX = os.environ.get('GREENLET_MANYLINUX') + +# Is the current interpreter free-threaded?) Note that this +# isn't the same as whether the GIL is enabled, this is the build-time +# value. Certain CPython details, like the garbage collector, +# work very differently on potentially-free-threaded builds than +# standard builds. +RUNNING_ON_FREETHREAD_BUILD = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) + +class TestCaseMetaClass(type): + # wrap each test method with + # a) leak checks + def __new__(cls, classname, bases, classDict): + # pylint and pep8 fight over what this should be called (mcs or cls). + # pylint gets it right, but we can't scope disable pep8, so we go with + # its convention. + # pylint: disable=bad-mcs-classmethod-argument + check_totalrefcount = True + + # Python 3: must copy, we mutate the classDict. Interestingly enough, + # it doesn't actually error out, but under 3.6 we wind up wrapping + # and re-wrapping the same items over and over and over. + for key, value in list(classDict.items()): + if key.startswith('test') and callable(value): + classDict.pop(key) + if check_totalrefcount: + value = leakcheck.wrap_refcount(value) + classDict[key] = value + return type.__new__(cls, classname, bases, classDict) + + +class TestCase(unittest.TestCase, metaclass=TestCaseMetaClass): + + cleanup_attempt_sleep_duration = 0.001 + cleanup_max_sleep_seconds = 1 + + def wait_for_pending_cleanups(self, + initial_active_threads=None, + initial_main_greenlets=None): + initial_active_threads = initial_active_threads or self.threads_before_test + initial_main_greenlets = initial_main_greenlets or self.main_greenlets_before_test + sleep_time = self.cleanup_attempt_sleep_duration + # NOTE: This is racy! A Python-level thread object may be dead + # and gone, but the C thread may not yet have fired its + # destructors and added to the queue. There's no particular + # way to know that's about to happen. We try to watch the + # Python threads to make sure they, at least, have gone away. + # Counting the main greenlets, which we can easily do deterministically, + # also helps. + + # Always sleep at least once to let other threads run + sleep(sleep_time) + quit_after = time() + self.cleanup_max_sleep_seconds + # TODO: We could add an API that calls us back when a particular main greenlet is deleted? + # It would have to drop the GIL + while ( + get_pending_cleanup_count() + or active_thread_count() > initial_active_threads + or (not self.expect_greenlet_leak + and get_total_main_greenlets() > initial_main_greenlets)): + sleep(sleep_time) + if time() > quit_after: + print("Time limit exceeded.") + print("Threads: Waiting for only", initial_active_threads, + "-->", active_thread_count()) + print("MGlets : Waiting for only", initial_main_greenlets, + "-->", get_total_main_greenlets()) + break + collect() + + def count_objects(self, kind=list, exact_kind=True): + # pylint:disable=unidiomatic-typecheck + # Collect the garbage. + for _ in range(3): + collect() + if exact_kind: + return sum( + 1 + for x in get_objects() + if type(x) is kind + ) + # instances + return sum( + 1 + for x in get_objects() + if isinstance(x, kind) + ) + + greenlets_before_test = 0 + threads_before_test = 0 + main_greenlets_before_test = 0 + expect_greenlet_leak = False + + def count_greenlets(self): + """ + Find all the greenlets and subclasses tracked by the GC. + """ + return self.count_objects(RawGreenlet, False) + + def setUp(self): + # Ensure the main greenlet exists, otherwise the first test + # gets a false positive leak + super().setUp() + getcurrent() + self.threads_before_test = active_thread_count() + self.main_greenlets_before_test = get_total_main_greenlets() + self.wait_for_pending_cleanups(self.threads_before_test, self.main_greenlets_before_test) + self.greenlets_before_test = self.count_greenlets() + + def tearDown(self): + if getattr(self, 'skipTearDown', False): + return + + self.wait_for_pending_cleanups(self.threads_before_test, self.main_greenlets_before_test) + super().tearDown() + + def get_expected_returncodes_for_aborted_process(self): + import signal + # The child should be aborted in an unusual way. On POSIX + # platforms, this is done with abort() and signal.SIGABRT, + # which is reflected in a negative return value; however, on + # Windows, even though we observe the child print "Fatal + # Python error: Aborted" and in older versions of the C + # runtime "This application has requested the Runtime to + # terminate it in an unusual way," it always has an exit code + # of 3. This is interesting because 3 is the error code for + # ERROR_PATH_NOT_FOUND; BUT: the C runtime abort() function + # also uses this code. + # + # If we link to the static C library on Windows, the error + # code changes to '0xc0000409' (hex(3221226505)), which + # apparently is STATUS_STACK_BUFFER_OVERRUN; but "What this + # means is that nowadays when you get a + # STATUS_STACK_BUFFER_OVERRUN, it doesn’t actually mean that + # there is a stack buffer overrun. It just means that the + # application decided to terminate itself with great haste." + # + # + # On windows, we've also seen '0xc0000005' (hex(3221225477)). + # That's "Access Violation" + # + # See + # https://devblogs.microsoft.com/oldnewthing/20110519-00/?p=10623 + # and + # https://docs.microsoft.com/en-us/previous-versions/k089yyh0(v=vs.140)?redirectedfrom=MSDN + # and + # https://devblogs.microsoft.com/oldnewthing/20190108-00/?p=100655 + expected_exit = ( + -signal.SIGABRT, + # But beginning on Python 3.11, the faulthandler + # that prints the C backtraces sometimes segfaults after + # reporting the exception but before printing the stack. + # This has only been seen on linux/gcc. + -signal.SIGSEGV, + ) if not WIN else ( + 3, + 0xc0000409, + 0xc0000005, + ) + return expected_exit + + def get_process_uss(self): + """ + Return the current process's USS in bytes. + + uss is available on Linux, macOS, Windows. Also known as + "Unique Set Size", this is the memory which is unique to a + process and which would be freed if the process was terminated + right now. + + If this is not supported by ``psutil``, this raises the + :exc:`unittest.SkipTest` exception. + """ + try: + return psutil.Process().memory_full_info().uss + except AttributeError as e: + raise unittest.SkipTest("uss not supported") from e + + def run_script(self, script_name, show_output=True): + import subprocess + script = os.path.join( + os.path.dirname(__file__), + script_name, + ) + + try: + return subprocess.check_output([sys.executable, script], + encoding='utf-8', + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as ex: + if show_output: + print('-----') + print('Failed to run script', script) + print('~~~~~') + print(ex.output) + print('------') + raise + + + def assertScriptRaises(self, script_name, exitcodes=None): + import subprocess + with self.assertRaises(subprocess.CalledProcessError) as exc: + output = self.run_script(script_name, show_output=False) + __traceback_info__ = output + # We're going to fail the assertion if we get here, at least + # preserve the output in the traceback. + + if exitcodes is None: + exitcodes = self.get_expected_returncodes_for_aborted_process() + self.assertIn(exc.exception.returncode, exitcodes) + return exc.exception diff --git a/venv/Lib/site-packages/greenlet/tests/_test_extension.c b/venv/Lib/site-packages/greenlet/tests/_test_extension.c new file mode 100644 index 0000000..ac23d04 --- /dev/null +++ b/venv/Lib/site-packages/greenlet/tests/_test_extension.c @@ -0,0 +1,273 @@ +/* This is a set of functions used by test_extension_interface.py to test the + * Greenlet C API. + */ + +#include "../greenlet.h" + +#ifndef Py_RETURN_NONE +# define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None +#endif + +#define TEST_MODULE_NAME "_test_extension" + +// CAUTION: MSVC is stupidly picky: +// +// "The compiler ignores, without warning, any __declspec keywords +// placed after * or & and in front of the variable identifier in a +// declaration." +// (https://docs.microsoft.com/en-us/cpp/cpp/declspec?view=msvc-160) +// +// So pointer return types must be handled differently (because of the +// trailing *), or you get inscrutable compiler warnings like "error +// C2059: syntax error: ''" +// +// In C23, there is a standard syntax for attributes, and +// GCC defines an attribute to use with this: [[gnu:noinline]]. +// In the future, this is expected to become standard. + +#if defined(__GNUC__) || defined(__clang__) +/* We used to check for GCC 4+ or 3.4+, but those compilers are + laughably out of date. Just assume they support it. */ +# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) +#elif defined(_MSC_VER) +/* We used to check for && (_MSC_VER >= 1300) but that's also out of date. */ +# define UNUSED(x) UNUSED_ ## x +#endif + +static PyObject* +test_switch(PyObject* UNUSED(self), PyObject* greenlet) +{ + PyObject* result = NULL; + + if (greenlet == NULL || !PyGreenlet_Check(greenlet)) { + PyErr_BadArgument(); + return NULL; + } + + result = PyGreenlet_Switch((PyGreenlet*)greenlet, NULL, NULL); + if (result == NULL) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_AssertionError, + "greenlet.switch() failed for some reason."); + } + return NULL; + } + return result; +} + +static PyObject* +test_switch_kwargs(PyObject* UNUSED(self), PyObject* args, PyObject* kwargs) +{ + PyGreenlet* g = NULL; + PyObject* result = NULL; + + if (!PyArg_ParseTuple(args, "O!", &PyGreenlet_Type, &g)) { + return NULL; + } + + if (g == NULL || !PyGreenlet_Check(g)) { + PyErr_BadArgument(); + return NULL; + } + + result = PyGreenlet_Switch(g, NULL, kwargs); + if (result == NULL) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_AssertionError, + "greenlet.switch() failed for some reason."); + } + return NULL; + } + return result; +} + +static PyObject* +test_getcurrent(PyObject* UNUSED(self)) +{ + PyGreenlet* g = PyGreenlet_GetCurrent(); + if (g == NULL || !PyGreenlet_Check(g) || !PyGreenlet_ACTIVE(g)) { + PyErr_SetString(PyExc_AssertionError, + "getcurrent() returned an invalid greenlet"); + Py_XDECREF(g); + return NULL; + } + Py_DECREF(g); + Py_RETURN_NONE; +} + +static PyObject* +test_setparent(PyObject* UNUSED(self), PyObject* arg) +{ + PyGreenlet* current; + PyGreenlet* greenlet = NULL; + PyObject* switch_result = NULL; + + if (arg == NULL || !PyGreenlet_Check(arg)) { + PyErr_BadArgument(); + return NULL; + } + if ((current = PyGreenlet_GetCurrent()) == NULL) { + return NULL; + } + greenlet = (PyGreenlet*)arg; + if (PyGreenlet_SetParent(greenlet, current)) { + Py_DECREF(current); + return NULL; + } + Py_DECREF(current); + + switch_result = PyGreenlet_Switch(greenlet, NULL, NULL); + if (switch_result == NULL) { + return NULL; + } + Py_DECREF(switch_result); + Py_RETURN_NONE; +} + +static PyObject* +test_new_greenlet(PyObject* UNUSED(self), PyObject* callable) +{ + PyObject* result = NULL; + PyGreenlet* greenlet = PyGreenlet_New(callable, NULL); + + if (!greenlet) { + return NULL; + } + + result = PyGreenlet_Switch(greenlet, NULL, NULL); + Py_CLEAR(greenlet); + if (result == NULL) { + return NULL; + } + + return result; +} + +static PyObject* +test_raise_dead_greenlet(PyObject* UNUSED(self)) +{ + PyErr_SetString(PyExc_GreenletExit, "test GreenletExit exception."); + return NULL; +} + +static PyObject* +test_raise_greenlet_error(PyObject* UNUSED(self)) +{ + PyErr_SetString(PyExc_GreenletError, "test greenlet.error exception"); + return NULL; +} + +static PyObject* +test_throw(PyObject* UNUSED(self), PyGreenlet* g) +{ + const char msg[] = "take that sucka!"; + PyObject* msg_obj = Py_BuildValue("s", msg); + PyGreenlet_Throw(g, PyExc_ValueError, msg_obj, NULL); + Py_DECREF(msg_obj); + if (PyErr_Occurred()) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject* +test_throw_exact(PyObject* UNUSED(self), PyObject* args) +{ + PyGreenlet* g = NULL; + PyObject* typ = NULL; + PyObject* val = NULL; + PyObject* tb = NULL; + + if (!PyArg_ParseTuple(args, "OOOO:throw", &g, &typ, &val, &tb)) { + return NULL; + } + + PyGreenlet_Throw(g, typ, val, tb); + if (PyErr_Occurred()) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject* +getcurrent_api(PyObject* UNUSED(self)) +{ + return (PyObject*)PyGreenlet_GetCurrent(); + +} + +static PyMethodDef test_methods[] = { + {"test_switch", + (PyCFunction)test_switch, + METH_O, + "Switch to the provided greenlet sending provided arguments, and \n" + "return the results."}, + {"test_switch_kwargs", + (PyCFunction)test_switch_kwargs, + METH_VARARGS | METH_KEYWORDS, + "Switch to the provided greenlet sending the provided keyword args."}, + {"test_getcurrent", + (PyCFunction)test_getcurrent, + METH_NOARGS, + "Test PyGreenlet_GetCurrent()"}, + {"test_setparent", + (PyCFunction)test_setparent, + METH_O, + "Se the parent of the provided greenlet and switch to it."}, + {"test_new_greenlet", + (PyCFunction)test_new_greenlet, + METH_O, + "Test PyGreenlet_New()"}, + {"test_raise_dead_greenlet", + (PyCFunction)test_raise_dead_greenlet, + METH_NOARGS, + "Just raise greenlet.GreenletExit"}, + {"test_raise_greenlet_error", + (PyCFunction)test_raise_greenlet_error, + METH_NOARGS, + "Just raise greenlet.error"}, + {"test_throw", + (PyCFunction)test_throw, + METH_O, + "Throw a ValueError at the provided greenlet"}, + {"test_throw_exact", + (PyCFunction)test_throw_exact, + METH_VARARGS, + "Throw exactly the arguments given at the provided greenlet"}, + { + "getcurrent_api", + (PyCFunction)getcurrent_api, + METH_NOARGS, + "Direct call to the PyGreenlet_GetCurrent API."}, + {NULL, NULL, 0, NULL} +}; + + +#define INITERROR return NULL + +static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, + TEST_MODULE_NAME, + NULL, + 0, + test_methods, + NULL, + NULL, + NULL, + NULL}; + +PyMODINIT_FUNC +PyInit__test_extension(void) +{ + PyObject* module = NULL; + module = PyModule_Create(&moduledef); + + if (module == NULL) { + return NULL; + } + + PyGreenlet_Import(); +#ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED); +#endif + return module; +} diff --git a/venv/Lib/site-packages/greenlet/tests/_test_extension.cp314-win_amd64.pyd b/venv/Lib/site-packages/greenlet/tests/_test_extension.cp314-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..58aaed7cbb691b6043d6d64d187c60d14635b13e GIT binary patch literal 14336 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/greenlet/tests/_test_extension_cpp.cpp b/venv/Lib/site-packages/greenlet/tests/_test_extension_cpp.cpp new file mode 100644 index 0000000..5f8974b --- /dev/null +++ b/venv/Lib/site-packages/greenlet/tests/_test_extension_cpp.cpp @@ -0,0 +1,230 @@ +/* This is a set of functions used to test C++ exceptions are not + * broken during greenlet switches + */ + +#include "../greenlet.h" +#include "../greenlet_compiler_compat.hpp" +#include +#include + +struct exception_t { + int depth; + exception_t(int depth) : depth(depth) {} +}; + +/* Functions are called via pointers to prevent inlining */ +static void (*p_test_exception_throw_nonstd)(int depth); +static void (*p_test_exception_throw_std)(); +static PyObject* (*p_test_exception_switch_recurse)(int depth, int left); + +static void +test_exception_throw_nonstd(int depth) +{ + throw exception_t(depth); +} + +static void +test_exception_throw_std() +{ + throw std::runtime_error("Thrown from an extension."); +} + +static PyObject* +test_exception_switch_recurse(int depth, int left) +{ + if (left > 0) { + return p_test_exception_switch_recurse(depth, left - 1); + } + + PyObject* result = NULL; + PyGreenlet* self = PyGreenlet_GetCurrent(); + if (self == NULL) + return NULL; + + try { + if (PyGreenlet_Switch(PyGreenlet_GET_PARENT(self), NULL, NULL) == NULL) { + Py_DECREF(self); + return NULL; + } + p_test_exception_throw_nonstd(depth); + PyErr_SetString(PyExc_RuntimeError, + "throwing C++ exception didn't work"); + } + catch (const exception_t& e) { + if (e.depth != depth) + PyErr_SetString(PyExc_AssertionError, "depth mismatch"); + else + result = PyLong_FromLong(depth); + } + catch (...) { + PyErr_SetString(PyExc_RuntimeError, "unexpected C++ exception"); + } + + Py_DECREF(self); + return result; +} + +/* test_exception_switch(int depth) + * - recurses depth times + * - switches to parent inside try/catch block + * - throws an exception that (expected to be caught in the same function) + * - verifies depth matches (exceptions shouldn't be caught in other greenlets) + */ +static PyObject* +test_exception_switch(PyObject* UNUSED(self), PyObject* args) +{ + int depth; + if (!PyArg_ParseTuple(args, "i", &depth)) + return NULL; + return p_test_exception_switch_recurse(depth, depth); +} + + +static PyObject* +py_test_exception_throw_nonstd(PyObject* UNUSED(self), PyObject* args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + p_test_exception_throw_nonstd(0); + PyErr_SetString(PyExc_AssertionError, "unreachable code running after throw"); + return NULL; +} + +static PyObject* +py_test_exception_throw_std(PyObject* UNUSED(self), PyObject* args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + p_test_exception_throw_std(); + PyErr_SetString(PyExc_AssertionError, "unreachable code running after throw"); + return NULL; +} + +static PyObject* +py_test_call(PyObject* UNUSED(self), PyObject* arg) +{ + PyObject* noargs = PyTuple_New(0); + PyObject* ret = PyObject_Call(arg, noargs, nullptr); + Py_DECREF(noargs); + return ret; +} + + + +/* test_exception_switch_and_do_in_g2(g2func) + * - creates new greenlet g2 to run g2func + * - switches to g2 inside try/catch block + * - verifies that no exception has been caught + * + * it is used together with test_exception_throw to verify that unhandled + * exceptions thrown in one greenlet do not propagate to other greenlet nor + * segfault the process. + */ +static PyObject* +test_exception_switch_and_do_in_g2(PyObject* UNUSED(self), PyObject* args) +{ + PyObject* g2func = NULL; + PyObject* result = NULL; + + if (!PyArg_ParseTuple(args, "O", &g2func)) + return NULL; + PyGreenlet* g2 = PyGreenlet_New(g2func, NULL); + if (!g2) { + return NULL; + } + + try { + result = PyGreenlet_Switch(g2, NULL, NULL); + if (!result) { + Py_DECREF(g2); + return NULL; + } + } + catch (const exception_t& e) { + /* if we are here the memory can be already corrupted and the program + * might crash before below py-level exception might become printed. + * -> print something to stderr to make it clear that we had entered + * this catch block. + * See comments in inner_bootstrap() + */ +#if defined(WIN32) || defined(_WIN32) + fprintf(stderr, "C++ exception unexpectedly caught in g1\n"); + PyErr_SetString(PyExc_AssertionError, "C++ exception unexpectedly caught in g1"); + Py_XDECREF(result); + return NULL; +#else + throw; +#endif + } + + Py_XDECREF(result); + Py_RETURN_NONE; +} + +static PyMethodDef test_methods[] = { + {"test_exception_switch", + (PyCFunction)&test_exception_switch, + METH_VARARGS, + "Switches to parent twice, to test exception handling and greenlet " + "switching."}, + {"test_exception_switch_and_do_in_g2", + (PyCFunction)&test_exception_switch_and_do_in_g2, + METH_VARARGS, + "Creates new greenlet g2 to run g2func and switches to it inside try/catch " + "block. Used together with test_exception_throw to verify that unhandled " + "C++ exceptions thrown in a greenlet doe not corrupt memory."}, + {"test_exception_throw_nonstd", + (PyCFunction)&py_test_exception_throw_nonstd, + METH_VARARGS, + "Throws non-standard C++ exception. Calling this function directly should abort the process." + }, + {"test_exception_throw_std", + (PyCFunction)&py_test_exception_throw_std, + METH_VARARGS, + "Throws standard C++ exception. Calling this function directly should abort the process." + }, + {"test_call", + (PyCFunction)&py_test_call, + METH_O, + "Call the given callable. Unlike calling it directly, this creates a " + "new C-level stack frame, which may be helpful in testing." + }, + {NULL, NULL, 0, NULL} +}; + + +static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, + "greenlet.tests._test_extension_cpp", + NULL, + 0, + test_methods, + NULL, + NULL, + NULL, + NULL}; + +PyMODINIT_FUNC +PyInit__test_extension_cpp(void) +{ + PyObject* module = NULL; + + module = PyModule_Create(&moduledef); + + if (module == NULL) { + return NULL; + } + + PyGreenlet_Import(); + if (_PyGreenlet_API == NULL) { + return NULL; + } + + p_test_exception_throw_nonstd = test_exception_throw_nonstd; + p_test_exception_throw_std = test_exception_throw_std; + p_test_exception_switch_recurse = test_exception_switch_recurse; +#ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED); +#endif + + return module; +}