diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 78ddc627fcf4d7..c99e02f19dc3c0 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -2009,6 +2009,12 @@ New Features (Contributed by Eddie Elizondo in :gh:`84436`.) +* Convert the ``PyCurses_API`` C API (``_curses._C_API`` capsule) from a pointer + array to a structure(``PyCurses_C_API``) in pycore_curses.h. ``PyCursesWindow_Type``, + ``PyCursesSetupTermCalled``, ``PyCursesInitialised``, ``PyCursesInitialisedColor`` + be moved into internal APIs and the public API will be deprecated in 3.14. + (Contributed by Hai Shi in :gh:`87175`). + * :pep:`684`: Add the new :c:func:`Py_NewInterpreterFromConfig` function and :c:type:`PyInterpreterConfig`, which may be used to create sub-interpreters with their own GILs. diff --git a/Include/internal/pycore_curses.h b/Include/internal/pycore_curses.h new file mode 100644 index 00000000000000..1b8bc27740fcff --- /dev/null +++ b/Include/internal/pycore_curses.h @@ -0,0 +1,46 @@ +#ifndef Py_INTERNAL_CURSES_H +#define Py_INTERNAL_CURSES_H + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyTypeObject *window_type; + int (*setup_term_called)(void); + int (*initialized)(void); + int (*initialized_color)(void); +} PyCurses_CAPI; + +#ifdef CURSES_MODULE +/* This section is used when compiling _cursesmodule.c */ + +#else +/* This section is used in modules that use the _cursesmodule API */ + +static PyCurses_CAPI *PyCurses_C_API; + +#undef PyCursesWindow_Type +#undef PyCursesSetupTermCalled +#undef PyCursesInitialised +#undef PyCursesInitialisedColor +#define PyCursesWindow_Type (*PyCurses_C_API->window_type) +#define PyCursesSetupTermCalled {if (!PyCurses_C_API->setup_term_called()) return NULL;} +#define PyCursesInitialised {if (!PyCurses_C_API->initialized()) return NULL;} +#define PyCursesInitialisedColor {if (!PyCurses_C_API->initialized_color()) return NULL;} + +#undef import_curses +#define import_curses() \ + PyCurses_C_API = PyCapsule_Import(PyCurses_CAPSULE_NAME, 1); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_INTERNAL_CURSES_H */ diff --git a/Include/py_curses.h b/Include/py_curses.h index 85a540086c8384..0579050ab06b2f 100644 --- a/Include/py_curses.h +++ b/Include/py_curses.h @@ -117,8 +117,9 @@ typedef struct { #else /* This section is used in modules that use the _cursesmodule API */ -static void **PyCurses_API; +Py_DEPRECATED(3.14) static void **PyCurses_API; +/* Those macros will be deprecated in 3.14 and moved into internal APIs. */ #define PyCursesWindow_Type (*_PyType_CAST(PyCurses_API[0])) #define PyCursesSetupTermCalled {if (! ((int (*)(void))PyCurses_API[1]) () ) return NULL;} #define PyCursesInitialised {if (! ((int (*)(void))PyCurses_API[2]) () ) return NULL;} diff --git a/Makefile.pre.in b/Makefile.pre.in index 3d971ddc8cd132..df656fcfe20336 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1343,6 +1343,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_critical_section.h \ $(srcdir)/Include/internal/pycore_crossinterp.h \ $(srcdir)/Include/internal/pycore_crossinterp_data_registry.h \ + $(srcdir)/Include/internal/pycore_curses.h \ $(srcdir)/Include/internal/pycore_debug_offsets.h \ $(srcdir)/Include/internal/pycore_descrobject.h \ $(srcdir)/Include/internal/pycore_dict.h \ @@ -3459,8 +3460,8 @@ Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h MODULE_DEPS_STATIC=Modules/config.c MODULE_DEPS_SHARED=@MODULE_DEPS_SHARED@ -MODULE__CURSES_DEPS=$(srcdir)/Include/py_curses.h -MODULE__CURSES_PANEL_DEPS=$(srcdir)/Include/py_curses.h +MODULE__CURSES_DEPS=$(srcdir)/Include/py_curses.h $(srcdir)/Include/internal/pycore_curses.h +MODULE__CURSES_PANEL_DEPS=$(srcdir)/Include/py_curses.h $(srcdir)/Include/internal/pycore_curses.h MODULE__DATETIME_DEPS=$(srcdir)/Include/datetime.h MODULE_CMATH_DEPS=$(srcdir)/Modules/_math.h MODULE_MATH_DEPS=$(srcdir)/Modules/_math.h diff --git a/Misc/NEWS.d/next/C API/2023-05-17-00-59-26.gh-issue-87175.VpxNNY.rst b/Misc/NEWS.d/next/C API/2023-05-17-00-59-26.gh-issue-87175.VpxNNY.rst new file mode 100644 index 00000000000000..fef9896c78ff55 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-05-17-00-59-26.gh-issue-87175.VpxNNY.rst @@ -0,0 +1,2 @@ +Convert the ``PyCurses_API`` C API (``_curses._C_API`` capsule) from a pointer array to a structure(``PyCurses_C_API``) in pycore_curses.h. +``PyCursesWindow_Type``, ``PyCursesSetupTermCalled``, ``PyCursesInitialised``, ``PyCursesInitialisedColor`` be moved into internal APIs and the public API will be deprecated in 3.14. diff --git a/Modules/_curses_panel.c b/Modules/_curses_panel.c index 40d407742b1621..20a0796b0a2991 100644 --- a/Modules/_curses_panel.c +++ b/Modules/_curses_panel.c @@ -19,6 +19,7 @@ static const char PyCursesVersion[] = "2.1"; #define CURSES_PANEL_MODULE #include "py_curses.h" +#include "pycore_curses.h" #if defined(HAVE_NCURSESW_PANEL_H) # include diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index b2b410cfe5f5cc..a0dffc59744aab 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -106,6 +106,7 @@ static const char PyCursesVersion[] = "2.2"; #define CURSES_MODULE #include "py_curses.h" +#include "pycore_curses.h" #if defined(HAVE_TERM_H) || defined(__sgi) /* For termname, longname, putp, tigetflag, tigetnum, tigetstr, tparm @@ -9273,15 +9274,16 @@ static void * curses_capi_new(cursesmodule_state *state) { assert(state->window_type != NULL); - void **capi = (void **)PyMem_Calloc(PyCurses_API_pointers, sizeof(void *)); + PyCurses_CAPI *capi = PyMem_Malloc(sizeof(PyCurses_CAPI)); if (capi == NULL) { PyErr_NoMemory(); return NULL; } - capi[0] = (void *)Py_NewRef(state->window_type); - capi[1] = curses_capi_setupterm_called; - capi[2] = curses_capi_initscr_called; - capi[3] = curses_capi_start_color_called; + /* Initialize the C API struct */ + capi->window_type = (PyTypeObject *)Py_NewRef(state->window_type); + capi->setup_term_called = curses_capi_setupterm_called; + capi->initialized = curses_capi_initscr_called; + capi->initialized_color = curses_capi_start_color_called; return (void *)capi; } @@ -9289,10 +9291,11 @@ static void curses_capi_free(void *capi) { assert(capi != NULL); - void **capi_ptr = (void **)capi; - // In free-threaded builds, capi_ptr[0] may have been already cleared - // by curses_capi_capsule_destructor(), hence the use of Py_XDECREF(). - Py_XDECREF(capi_ptr[0]); // decref curses window type + PyCurses_CAPI *capi_ptr = (PyCurses_CAPI *)capi; + // In free-threaded builds, capi_ptr->window_type may have been already + // cleared by curses_capi_capsule_destructor(), hence the use of + // Py_XDECREF(). + Py_XDECREF(capi_ptr->window_type); // decref curses window type PyMem_Free(capi_ptr); } @@ -9308,18 +9311,18 @@ curses_capi_capsule_destructor(PyObject *op) static int curses_capi_capsule_traverse(PyObject *op, visitproc visit, void *arg) { - void **capi_ptr = PyCapsule_GetPointer(op, PyCurses_CAPSULE_NAME); + PyCurses_CAPI *capi_ptr = PyCapsule_GetPointer(op, PyCurses_CAPSULE_NAME); assert(capi_ptr != NULL); - Py_VISIT(capi_ptr[0]); // visit curses window type + Py_VISIT(capi_ptr->window_type); // visit curses window type return 0; } static int curses_capi_capsule_clear(PyObject *op) { - void **capi_ptr = PyCapsule_GetPointer(op, PyCurses_CAPSULE_NAME); + PyCurses_CAPI *capi_ptr = PyCapsule_GetPointer(op, PyCurses_CAPSULE_NAME); assert(capi_ptr != NULL); - Py_CLEAR(capi_ptr[0]); // clear curses window type + Py_CLEAR(capi_ptr->window_type); // clear curses window type return 0; } diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 51d18739d7c93f..adf918e9377f45 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -241,6 +241,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 9abf09b6550ecf..0e889ae018d821 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -636,6 +636,9 @@ Include\internal + + Include\internal + Include\internal diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index db9f74fdbc591f..6f51b92894e199 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -671,6 +671,7 @@ Modules/_xxtestfuzz/fuzzer.c LLVMFuzzerTestOneInput ELEMENTTREE_PARSEWHOLE_INITI # XXX Fix the analyzer. ## forward/extern references +Include/internal/pycore_curses.h - PyCurses_C_API - Include/internal/pycore_importdl.h - _PyImport_DynLoadFiletab - Include/py_curses.h - PyCurses_API - Include/pydecimal.h - _decimal_api -