From 5c30fd675d568276c27572943d6c90bc2e7f789b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Nov 2025 16:18:52 +0100 Subject: [PATCH 1/7] document curses C API --- Doc/c-api/concrete.rst | 1 + Doc/c-api/curses.rst | 135 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 Doc/c-api/curses.rst diff --git a/Doc/c-api/concrete.rst b/Doc/c-api/concrete.rst index 880f7b15ce68e8..3107a75e028cf9 100644 --- a/Doc/c-api/concrete.rst +++ b/Doc/c-api/concrete.rst @@ -115,5 +115,6 @@ Other Objects gen.rst coro.rst contextvars.rst + curses.rst datetime.rst typehints.rst diff --git a/Doc/c-api/curses.rst b/Doc/c-api/curses.rst new file mode 100644 index 00000000000000..121e9cdd65f54f --- /dev/null +++ b/Doc/c-api/curses.rst @@ -0,0 +1,135 @@ +.. highlight:: c + +Curses C API +------------ + +:mod:`curses` exposes a small C interface for extension modules. +Consumers must include the header file :file:`py_curses.h` (which is not +included by default by :file:`Python.h`) and :c:func:`import_curses` must +be invoked, usually as part of the module initialisation function, to populate +:c:var:`PyCurses_API`. + +.. c:macro:: import_curses() + + Import the curses C API. The macro does not need a semi-colon to be called. + + On success, populate the :c:var:`PyCurses_API` pointer. + + On failure, set :c:var:`PyCurses_API` to NULL and set an exception. + The caller must check if an error occurred via :c:func:`PyErr_Occurred`: + + .. code-block:: + + import_curses(); // semi-colon is optional but recommended + if (PyErr_Occurred()) { /* cleanup */ } + + +.. c:var:: void **PyCurses_API + + Dynamically allocated object containing the curses C API. + This variable is only available once :c:macro:`import_curses` succeeded. + + ``PyCurses_API[0]`` corresponds to :c:data:`PyCursesWindow_Type`. + + ``PyCurses_API[1]``, ``PyCurses_API[2]``, and ``PyCurses_API[3]`` + are pointers to predicate functions of type ``int (*)(void)``. + + When called, these predicates return whether :func:`curses.setupterm`, + :func:`curses.initscr`, and :func:`curses.start_color` have been called + respectively. + + See also the convenience macros :c:macro:`PyCursesSetupTermCalled`, + :c:macro:`PyCursesInitialised`, and :c:macro:`PyCursesInitialisedColor`. + + +.. c:data:: PyTypeObject PyCursesWindow_Type + + The :ref:`heap type ` corresponding to :class:`curses.window`. + + +.. c:macro:: PyCursesWindow_Check(op) + + Return *1* if *op* is an :class:`curses.window` instance, *0* otherwise. + + The macro expansion is equivalent to: + + .. code-block:: + + Py_IS_TYPE((op), &PyCursesWindow_Type) + + +The following macros are convenience macros expanding into C statements. +In particular, they can only be used as ``macro;`` or ``macro``, but not +``macro()`` or ``macro();``. + +.. c:macro:: PyCursesSetupTermCalled + + Macro checking if :func:`curses.setupterm` has been called. + + The macro expansion is roughly equivalent to: + + .. code-block:: + + { + typedef int (*predicate_t)(void); + predicate_t was_setupterm_called = (predicate_t)PyCurses_API[1]; + if (!was_setupterm_called()) { + return NULL; + } + } + + +.. c:macro:: PyCursesInitialised + + Macro checking if :func:`curses.initscr` has been called. + + The macro expansion is roughly equivalent to: + + .. code-block:: + + { + typedef int (*predicate_t)(void); + predicate_t was_initscr_called = (predicate_t)PyCurses_API[2]; + if (!was_initscr_called()) { + return NULL; + } + } + + +.. c:macro:: PyCursesInitialisedColor + + Macro checking if :func:`curses.start_color` has been called. + + The macro expansion is roughly equivalent to: + + .. code-block:: + + { + typedef int (*predicate_t)(void); + predicate_t was_start_color_called = (predicate_t)PyCurses_API[3]; + if (!was_start_color_called()) { + return NULL; + } + } + + +Internal data +------------- + +The following objects are exposed by the C API but should be considered +internal-only. + + +.. c:macro:: PyCurses_API_pointers + + The number of accessible fields in :c:var:`PyCurses_API`. + + Internal usage only. + + +.. c:macro:: PyCurses_CAPSULE_NAME + + Name of the curses capsule to pass to :c:func:`PyCapsule_Import`. + + Internal usage only. Use :c:macro:`import_curses` instead. + From 7c3b8f3b9d7172145df2844f7db2444e1501d837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Nov 2025 16:39:19 +0100 Subject: [PATCH 2/7] fixup --- Doc/c-api/curses.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/curses.rst b/Doc/c-api/curses.rst index 121e9cdd65f54f..732e840fa3b69d 100644 --- a/Doc/c-api/curses.rst +++ b/Doc/c-api/curses.rst @@ -42,7 +42,7 @@ be invoked, usually as part of the module initialisation function, to populate :c:macro:`PyCursesInitialised`, and :c:macro:`PyCursesInitialisedColor`. -.. c:data:: PyTypeObject PyCursesWindow_Type +.. c:var:: PyTypeObject PyCursesWindow_Type The :ref:`heap type ` corresponding to :class:`curses.window`. From 054bfa14a9a6718c26ea66fff1109c409a455393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Nov 2025 18:27:56 +0100 Subject: [PATCH 3/7] . --- Doc/c-api/curses.rst | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Doc/c-api/curses.rst b/Doc/c-api/curses.rst index 732e840fa3b69d..328b5a3dc75d36 100644 --- a/Doc/c-api/curses.rst +++ b/Doc/c-api/curses.rst @@ -47,16 +47,10 @@ be invoked, usually as part of the module initialisation function, to populate The :ref:`heap type ` corresponding to :class:`curses.window`. -.. c:macro:: PyCursesWindow_Check(op) +.. c:func:: int PyCursesWindow_Check(PyObject *op) Return *1* if *op* is an :class:`curses.window` instance, *0* otherwise. - The macro expansion is equivalent to: - - .. code-block:: - - Py_IS_TYPE((op), &PyCursesWindow_Type) - The following macros are convenience macros expanding into C statements. In particular, they can only be used as ``macro;`` or ``macro``, but not From 11663426a82c599b08594011ae4c3d6a6df6662c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Nov 2025 18:28:45 +0100 Subject: [PATCH 4/7] new section for extension modules --- Doc/c-api/concrete.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Doc/c-api/concrete.rst b/Doc/c-api/concrete.rst index 3107a75e028cf9..a5c5a53236c9a4 100644 --- a/Doc/c-api/concrete.rst +++ b/Doc/c-api/concrete.rst @@ -115,6 +115,13 @@ Other Objects gen.rst coro.rst contextvars.rst + typehints.rst + + +C API for extension modules +=========================== + +.. toctree:: + curses.rst datetime.rst - typehints.rst From bf55f97cd18f69503a26a03c6f17ca665e5bf073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Nov 2025 18:41:20 +0100 Subject: [PATCH 5/7] fixup --- Doc/c-api/curses.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/curses.rst b/Doc/c-api/curses.rst index 328b5a3dc75d36..e706a021a6ac68 100644 --- a/Doc/c-api/curses.rst +++ b/Doc/c-api/curses.rst @@ -47,9 +47,9 @@ be invoked, usually as part of the module initialisation function, to populate The :ref:`heap type ` corresponding to :class:`curses.window`. -.. c:func:: int PyCursesWindow_Check(PyObject *op) +.. c:function:: int PyCursesWindow_Check(PyObject *op) - Return *1* if *op* is an :class:`curses.window` instance, *0* otherwise. + Return *1* if *op* is a :class:`curses.window` instance, *0* otherwise. The following macros are convenience macros expanding into C statements. From a35cd11eb200cec5759407a5e4fe8c6e60b09807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 9 Nov 2025 15:03:54 +0100 Subject: [PATCH 6/7] address review --- Doc/c-api/curses.rst | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Doc/c-api/curses.rst b/Doc/c-api/curses.rst index e706a021a6ac68..c23e4ad9f9e643 100644 --- a/Doc/c-api/curses.rst +++ b/Doc/c-api/curses.rst @@ -9,6 +9,11 @@ included by default by :file:`Python.h`) and :c:func:`import_curses` must be invoked, usually as part of the module initialisation function, to populate :c:var:`PyCurses_API`. +.. warning:: + + Neither the C API nor the pure Python :mod:`curses` module are compatible + with subinterpreters. + .. c:macro:: import_curses() Import the curses C API. The macro does not need a semi-colon to be called. @@ -41,6 +46,17 @@ be invoked, usually as part of the module initialisation function, to populate See also the convenience macros :c:macro:`PyCursesSetupTermCalled`, :c:macro:`PyCursesInitialised`, and :c:macro:`PyCursesInitialisedColor`. + .. note:: + + The number of entries in this structure is subject to changes. + Consider using :c:macro:`PyCurses_API_pointers` to check if + new fields are available or not. + + +.. c:macro:: PyCurses_API_pointers + + The number of accessible fields (``4``) in :c:var:`PyCurses_API`. + .. c:var:: PyTypeObject PyCursesWindow_Type @@ -113,14 +129,6 @@ Internal data The following objects are exposed by the C API but should be considered internal-only. - -.. c:macro:: PyCurses_API_pointers - - The number of accessible fields in :c:var:`PyCurses_API`. - - Internal usage only. - - .. c:macro:: PyCurses_CAPSULE_NAME Name of the curses capsule to pass to :c:func:`PyCapsule_Import`. From 55465c9d99eb5823e418f342a1c50204044d7d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 9 Nov 2025 16:37:03 +0100 Subject: [PATCH 7/7] address review --- Doc/c-api/curses.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/curses.rst b/Doc/c-api/curses.rst index c23e4ad9f9e643..5a1697c43cc969 100644 --- a/Doc/c-api/curses.rst +++ b/Doc/c-api/curses.rst @@ -32,7 +32,7 @@ be invoked, usually as part of the module initialisation function, to populate .. c:var:: void **PyCurses_API Dynamically allocated object containing the curses C API. - This variable is only available once :c:macro:`import_curses` succeeded. + This variable is only available once :c:macro:`import_curses` succeeds. ``PyCurses_API[0]`` corresponds to :c:data:`PyCursesWindow_Type`. @@ -56,6 +56,7 @@ be invoked, usually as part of the module initialisation function, to populate .. c:macro:: PyCurses_API_pointers The number of accessible fields (``4``) in :c:var:`PyCurses_API`. + This number is incremented whenever new fields are added. .. c:var:: PyTypeObject PyCursesWindow_Type @@ -65,7 +66,7 @@ be invoked, usually as part of the module initialisation function, to populate .. c:function:: int PyCursesWindow_Check(PyObject *op) - Return *1* if *op* is a :class:`curses.window` instance, *0* otherwise. + Return true if *op* is a :class:`curses.window` instance, false otherwise. The following macros are convenience macros expanding into C statements.