diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a7b99ad7..ee03260a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.9, "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: [3.9, "3.10", "3.11", "3.12", "3.13", "3.14", "3.14t", "3.15", "3.15t"] # Recall the macOS builds upload built wheels so all supported versions # need to run on mac. os: [ubuntu-latest, macos-latest] diff --git a/docs/development.rst b/docs/development.rst index 651924ab..2fdd5fdd 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -11,6 +11,17 @@ Github The primary development location for greenlet is GitHub: https://github.com/python-greenlet/greenlet/ +Compilation database +==================== + +To get ``compile_commands.json`` (used by ``clang-tidy``, for example), +install ``bear``, then run: + +.. code-block:: shell + + $ bear -- python setup.py + + Releases ======== diff --git a/src/greenlet/tests/_test_extension.c b/src/greenlet/tests/_test_extension.c index 05e81c03..3a32b668 100644 --- a/src/greenlet/tests/_test_extension.c +++ b/src/greenlet/tests/_test_extension.c @@ -10,8 +10,36 @@ #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 GREENLET_NOINLINE(name) __attribute__((noinline)) name +# define GREENLET_NOINLINE_P(rtype, name) rtype __attribute__((noinline)) name +# 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 GREENLET_NOINLINE(name) __declspec(noinline) name +# define GREENLET_NOINLINE_P(rtype, name) __declspec(noinline) rtype name +# define UNUSED(x) UNUSED_ ## x +#endif + static PyObject* -test_switch(PyObject* self, PyObject* greenlet) +test_switch(PyObject* UNUSED(self), PyObject* greenlet) { PyObject* result = NULL; @@ -33,7 +61,7 @@ test_switch(PyObject* self, PyObject* greenlet) } static PyObject* -test_switch_kwargs(PyObject* self, PyObject* args, PyObject* kwargs) +test_switch_kwargs(PyObject* UNUSED(self), PyObject* args, PyObject* kwargs) { PyGreenlet* g = NULL; PyObject* result = NULL; @@ -58,7 +86,7 @@ test_switch_kwargs(PyObject* self, PyObject* args, PyObject* kwargs) } static PyObject* -test_getcurrent(PyObject* self) +test_getcurrent(PyObject* UNUSED(self)) { PyGreenlet* g = PyGreenlet_GetCurrent(); if (g == NULL || !PyGreenlet_Check(g) || !PyGreenlet_ACTIVE(g)) { @@ -72,7 +100,7 @@ test_getcurrent(PyObject* self) } static PyObject* -test_setparent(PyObject* self, PyObject* arg) +test_setparent(PyObject* UNUSED(self), PyObject* arg) { PyGreenlet* current; PyGreenlet* greenlet = NULL; @@ -97,7 +125,7 @@ test_setparent(PyObject* self, PyObject* arg) } static PyObject* -test_new_greenlet(PyObject* self, PyObject* callable) +test_new_greenlet(PyObject* UNUSED(self), PyObject* callable) { PyObject* result = NULL; PyGreenlet* greenlet = PyGreenlet_New(callable, NULL); @@ -117,21 +145,21 @@ test_new_greenlet(PyObject* self, PyObject* callable) } static PyObject* -test_raise_dead_greenlet(PyObject* self) +test_raise_dead_greenlet(PyObject* UNUSED(self)) { PyErr_SetString(PyExc_GreenletExit, "test GreenletExit exception."); return NULL; } static PyObject* -test_raise_greenlet_error(PyObject* self) +test_raise_greenlet_error(PyObject* UNUSED(self)) { PyErr_SetString(PyExc_GreenletError, "test greenlet.error exception"); return NULL; } static PyObject* -test_throw(PyObject* self, PyGreenlet* g) +test_throw(PyObject* UNUSED(self), PyGreenlet* g) { const char msg[] = "take that sucka!"; PyObject* msg_obj = Py_BuildValue("s", msg); @@ -144,7 +172,7 @@ test_throw(PyObject* self, PyGreenlet* g) } static PyObject* -test_throw_exact(PyObject* self, PyObject* args) +test_throw_exact(PyObject* UNUSED(self), PyObject* args) { PyGreenlet* g = NULL; PyObject* typ = NULL; diff --git a/src/greenlet/tests/_test_extension_cpp.cpp b/src/greenlet/tests/_test_extension_cpp.cpp index 5cbe6a76..f4df2bf6 100644 --- a/src/greenlet/tests/_test_extension_cpp.cpp +++ b/src/greenlet/tests/_test_extension_cpp.cpp @@ -81,7 +81,7 @@ test_exception_switch(PyObject* UNUSED(self), PyObject* args) static PyObject* -py_test_exception_throw_nonstd(PyObject* self, PyObject* args) +py_test_exception_throw_nonstd(PyObject* UNUSED(self), PyObject* args) { if (!PyArg_ParseTuple(args, "")) return NULL; @@ -91,7 +91,7 @@ py_test_exception_throw_nonstd(PyObject* self, PyObject* args) } static PyObject* -py_test_exception_throw_std(PyObject* self, PyObject* args) +py_test_exception_throw_std(PyObject* UNUSED(self), PyObject* args) { if (!PyArg_ParseTuple(args, "")) return NULL; @@ -101,7 +101,7 @@ py_test_exception_throw_std(PyObject* self, PyObject* args) } static PyObject* -py_test_call(PyObject* self, PyObject* arg) +py_test_call(PyObject* UNUSED(self), PyObject* arg) { PyObject* noargs = PyTuple_New(0); PyObject* ret = PyObject_Call(arg, noargs, nullptr); @@ -121,7 +121,7 @@ py_test_call(PyObject* self, PyObject* arg) * segfault the process. */ static PyObject* -test_exception_switch_and_do_in_g2(PyObject* self, PyObject* args) +test_exception_switch_and_do_in_g2(PyObject* UNUSED(self), PyObject* args) { PyObject* g2func = NULL; PyObject* result = NULL;