gh-155863: Add the WACS_* constants to the curses module (GH-155870)

serhiy-storchaka <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/8e01c9082971c848ee3a2d3946e03919a46a268d
commit: 8e01c9082971c848ee3a2d3946e03919a46a268d
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-21T09:41:01+03:00
summary:

gh-155863: Add the WACS_* constants to the curses module (GH-155870)

They are the counterparts of the ACS_* line-drawing codes as complexchar
cells, added to the module by initscr() and newterm() like the ACS_* ones.
Besides the codes that mirror the ACS_* ones, ncurses provides the
double-line and thick-line families, which have no ACS_* counterpart.

files:
A Misc/NEWS.d/next/Library/2026-08-15-17-40-12.gh-issue-155863.Kw3Vqp.rst
M Doc/library/curses.rst
M Doc/whatsnew/3.16.rst
M Lib/curses/__init__.py
M Lib/test/test_curses.py
M Modules/_cursesmodule.c

diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst
index 04cf2e17d538fcc..e5face72d1e5c9b 100644
--- a/Doc/library/curses.rst
+++ b/Doc/library/curses.rst
@@ -1562,25 +1562,28 @@ Borders and lines
       that parameter.  Keyword parameters can *not* be used.  The defaults are listed
       in this table:
 
-   +-----------+---------------------+-----------------------+
-   | Parameter | Description         | Default value         |
-   +===========+=====================+=======================+
-   | *ls*      | Left side           | :const:`ACS_VLINE`    |
-   +-----------+---------------------+-----------------------+
-   | *rs*      | Right side          | :const:`ACS_VLINE`    |
-   +-----------+---------------------+-----------------------+
-   | *ts*      | Top                 | :const:`ACS_HLINE`    |
-   +-----------+---------------------+-----------------------+
-   | *bs*      | Bottom              | :const:`ACS_HLINE`    |
-   +-----------+---------------------+-----------------------+
-   | *tl*      | Upper-left corner   | :const:`ACS_ULCORNER` |
-   +-----------+---------------------+-----------------------+
-   | *tr*      | Upper-right corner  | :const:`ACS_URCORNER` |
-   +-----------+---------------------+-----------------------+
-   | *bl*      | Bottom-left corner  | :const:`ACS_LLCORNER` |
-   +-----------+---------------------+-----------------------+
-   | *br*      | Bottom-right corner | :const:`ACS_LRCORNER` |
-   +-----------+---------------------+-----------------------+
+   +-----------+---------------------+-----------------------+------------------------+
+   | Parameter | Description         | Default value         | Wide default value     |
+   +===========+=====================+=======================+========================+
+   | *ls*      | Left side           | :const:`ACS_VLINE`    | :const:`WACS_VLINE`    |
+   +-----------+---------------------+-----------------------+------------------------+
+   | *rs*      | Right side          | :const:`ACS_VLINE`    | :const:`WACS_VLINE`    |
+   +-----------+---------------------+-----------------------+------------------------+
+   | *ts*      | Top                 | :const:`ACS_HLINE`    | :const:`WACS_HLINE`    |
+   +-----------+---------------------+-----------------------+------------------------+
+   | *bs*      | Bottom              | :const:`ACS_HLINE`    | :const:`WACS_HLINE`    |
+   +-----------+---------------------+-----------------------+------------------------+
+   | *tl*      | Upper-left corner   | :const:`ACS_ULCORNER` | :const:`WACS_ULCORNER` |
+   +-----------+---------------------+-----------------------+------------------------+
+   | *tr*      | Upper-right corner  | :const:`ACS_URCORNER` | :const:`WACS_URCORNER` |
+   +-----------+---------------------+-----------------------+------------------------+
+   | *bl*      | Bottom-left corner  | :const:`ACS_LLCORNER` | :const:`WACS_LLCORNER` |
+   +-----------+---------------------+-----------------------+------------------------+
+   | *br*      | Bottom-right corner | :const:`ACS_LRCORNER` | :const:`WACS_LRCORNER` |
+   +-----------+---------------------+-----------------------+------------------------+
+
+   The wide default value is used when the border is drawn from string
+   characters or :class:`complexchar` cells.
 
    .. versionchanged:: next
       Wide and combining characters, and :class:`complexchar` cells, are now
@@ -2255,62 +2258,58 @@ Attributes
 Some constants are available to specify character cell attributes.
 The exact constants available are system dependent.
 
-+------------------------+-------------------------------+
-| Attribute              | Meaning                       |
-+========================+===============================+
-| .. data:: A_ALTCHARSET | Alternate character set mode  |
-+------------------------+-------------------------------+
-| .. data:: A_BLINK      | Blink mode                    |
-+------------------------+-------------------------------+
-| .. data:: A_BOLD       | Bold mode                     |
-+------------------------+-------------------------------+
-| .. data:: A_DIM        | Dim mode                      |
-+------------------------+-------------------------------+
-| .. data:: A_INVIS      | Invisible or blank mode       |
-+------------------------+-------------------------------+
-| .. data:: A_ITALIC     | Italic mode                   |
-+------------------------+-------------------------------+
-| .. data:: A_NORMAL     | Normal attribute              |
-+------------------------+-------------------------------+
-| .. data:: A_PROTECT    | Protected mode                |
-+------------------------+-------------------------------+
-| .. data:: A_REVERSE    | Reverse background and        |
-|                        | foreground colors             |
-+------------------------+-------------------------------+
-| .. data:: A_STANDOUT   | Standout mode                 |
-+------------------------+-------------------------------+
-| .. data:: A_UNDERLINE  | Underline mode                |
-+------------------------+-------------------------------+
-| .. data:: A_HORIZONTAL | Horizontal highlight          |
-+------------------------+-------------------------------+
-| .. data:: A_LEFT       | Left highlight                |
-+------------------------+-------------------------------+
-| .. data:: A_LOW        | Low highlight                 |
-+------------------------+-------------------------------+
-| .. data:: A_RIGHT      | Right highlight               |
-+------------------------+-------------------------------+
-| .. data:: A_TOP        | Top highlight                 |
-+------------------------+-------------------------------+
-| .. data:: A_VERTICAL   | Vertical highlight            |
-+------------------------+-------------------------------+
+.. _curses-wa-constants:
+
++------------------------+-------------------------+------------------------------------------+
+| Attribute              | Wide attribute          | Meaning                                  |
++========================+=========================+==========================================+
+| .. data:: A_ALTCHARSET | .. data:: WA_ALTCHARSET | Alternate character set mode             |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_BLINK      | .. data:: WA_BLINK      | Blink mode                               |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_BOLD       | .. data:: WA_BOLD       | Bold mode                                |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_DIM        | .. data:: WA_DIM        | Dim mode                                 |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_INVIS      | .. data:: WA_INVIS      | Invisible or blank mode                  |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_ITALIC     | .. data:: WA_ITALIC     | Italic mode                              |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_NORMAL     | .. data:: WA_NORMAL     | Normal attribute                         |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_PROTECT    | .. data:: WA_PROTECT    | Protected mode                           |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_REVERSE    | .. data:: WA_REVERSE    | Reverse background and foreground colors |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_STANDOUT   | .. data:: WA_STANDOUT   | Standout mode                            |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_UNDERLINE  | .. data:: WA_UNDERLINE  | Underline mode                           |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_HORIZONTAL | .. data:: WA_HORIZONTAL | Horizontal highlight                     |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_LEFT       | .. data:: WA_LEFT       | Left highlight                           |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_LOW        | .. data:: WA_LOW        | Low highlight                            |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_RIGHT      | .. data:: WA_RIGHT      | Right highlight                          |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_TOP        | .. data:: WA_TOP        | Top highlight                            |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: A_VERTICAL   | .. data:: WA_VERTICAL   | Vertical highlight                       |
++------------------------+-------------------------+------------------------------------------+
 
 .. versionadded:: 3.7
    ``A_ITALIC`` was added.
 
-.. _curses-wa-constants:
-
 The :meth:`~window.attr_get`, :meth:`~window.attr_set`, :meth:`~window.attr_on`
-and :meth:`~window.attr_off` methods use a parallel set of ``WA_*`` constants.
-These have the same meaning as the corresponding ``A_*`` attributes above
-(``WA_BOLD`` like :const:`A_BOLD`, and so on), but belong to the ``attr_t`` type
-rather than being packed into a character.  In ncurses the two sets share the
-same values, but other curses implementations may give them different ones, so
-use the ``WA_*`` constants with the ``attr_*`` methods.  The available names are
-``WA_ATTRIBUTES``, ``WA_NORMAL``, ``WA_STANDOUT``, ``WA_UNDERLINE``,
-``WA_REVERSE``, ``WA_BLINK``, ``WA_DIM``, ``WA_BOLD``, ``WA_ALTCHARSET``,
-``WA_INVIS``, ``WA_PROTECT``, ``WA_HORIZONTAL``, ``WA_LEFT``, ``WA_LOW``,
-``WA_RIGHT``, ``WA_TOP``, ``WA_VERTICAL`` and ``WA_ITALIC`` (each available only
-where the platform defines it).
+and :meth:`~window.attr_off` methods use the parallel set of ``WA_*`` constants
+listed above.
+Each has the same meaning as the corresponding ``A_*`` attribute
+(:const:`WA_BOLD` like :const:`A_BOLD`, and so on), but belongs to the
+``attr_t`` type rather than being packed into a character.
+In ncurses the two sets share the same values, but other curses implementations
+may give them different ones, so use the ``WA_*`` constants with the ``attr_*``
+methods.
 
 .. versionadded:: next
    The ``WA_*`` constants were added.
@@ -2318,18 +2317,15 @@ where the platform defines it).
 Several constants are available to extract corresponding attributes returned
 by some methods.
 
-+-------------------------+-------------------------------+
-| Bit-mask                | Meaning                       |
-+=========================+===============================+
-|  .. data:: A_ATTRIBUTES | Bit-mask to extract           |
-|                         | attributes                    |
-+-------------------------+-------------------------------+
-|  .. data:: A_CHARTEXT   | Bit-mask to extract a         |
-|                         | character                     |
-+-------------------------+-------------------------------+
-|  .. data:: A_COLOR      | Bit-mask to extract           |
-|                         | color-pair field information  |
-+-------------------------+-------------------------------+
++-------------------------+--------------------------+--------------------------------------------------+
+| Bit-mask                | Wide bit-mask            | Meaning                                          |
++=========================+==========================+==================================================+
+| .. data:: A_ATTRIBUTES  | .. data:: WA_ATTRIBUTES  | Bit-mask to extract attributes                   |
++-------------------------+--------------------------+--------------------------------------------------+
+| .. data:: A_CHARTEXT    |                          | Bit-mask to extract a character                  |
++-------------------------+--------------------------+--------------------------------------------------+
+| .. data:: A_COLOR       |                          | Bit-mask to extract color-pair field information |
++-------------------------+--------------------------+--------------------------------------------------+
 
 Keys
 ~~~~
@@ -2564,99 +2560,165 @@ inherited from the VT100 terminal, and will generally be  available on software
 emulations such as X terminals.  When there is no graphic available, curses
 falls back on a crude printable ASCII approximation.
 
+Every character has two names.
+The ``ACS_*`` code is an integer character, restricted to the 8-bit
+alternate character set of the terminal.
+The ``WACS_*`` code is the same character as a :class:`complexchar` cell,
+which is not restricted to the alternate character set.
+
 .. note::
 
    These are available only after :func:`initscr` has  been called.
+   The ``WACS_*`` codes are only available if Python is built with
+   wide character support.
 
-+------------------------+------------------------------------------+
-| ACS code               | Meaning                                  |
-+========================+==========================================+
-| .. data:: ACS_BBSS     | alternate name for upper-right corner    |
-+------------------------+------------------------------------------+
-| .. data:: ACS_BLOCK    | solid square block                       |
-+------------------------+------------------------------------------+
-| .. data:: ACS_BOARD    | board of squares                         |
-+------------------------+------------------------------------------+
-| .. data:: ACS_BSBS     | alternate name for horizontal line       |
-+------------------------+------------------------------------------+
-| .. data:: ACS_BSSB     | alternate name for upper-left corner     |
-+------------------------+------------------------------------------+
-| .. data:: ACS_BSSS     | alternate name for top tee               |
-+------------------------+------------------------------------------+
-| .. data:: ACS_BTEE     | bottom tee                               |
-+------------------------+------------------------------------------+
-| .. data:: ACS_BULLET   | bullet                                   |
-+------------------------+------------------------------------------+
-| .. data:: ACS_CKBOARD  | checker board (stipple)                  |
-+------------------------+------------------------------------------+
-| .. data:: ACS_DARROW   | arrow pointing down                      |
-+------------------------+------------------------------------------+
-| .. data:: ACS_DEGREE   | degree symbol                            |
-+------------------------+------------------------------------------+
-| .. data:: ACS_DIAMOND  | diamond                                  |
-+------------------------+------------------------------------------+
-| .. data:: ACS_GEQUAL   | greater-than-or-equal-to                 |
-+------------------------+------------------------------------------+
-| .. data:: ACS_HLINE    | horizontal line                          |
-+------------------------+------------------------------------------+
-| .. data:: ACS_LANTERN  | lantern symbol                           |
-+------------------------+------------------------------------------+
-| .. data:: ACS_LARROW   | left arrow                               |
-+------------------------+------------------------------------------+
-| .. data:: ACS_LEQUAL   | less-than-or-equal-to                    |
-+------------------------+------------------------------------------+
-| .. data:: ACS_LLCORNER | lower-left corner                        |
-+------------------------+------------------------------------------+
-| .. data:: ACS_LRCORNER | lower-right corner                       |
-+------------------------+------------------------------------------+
-| .. data:: ACS_LTEE     | left tee                                 |
-+------------------------+------------------------------------------+
-| .. data:: ACS_NEQUAL   | not-equal sign                           |
-+------------------------+------------------------------------------+
-| .. data:: ACS_PI       | letter pi                                |
-+------------------------+------------------------------------------+
-| .. data:: ACS_PLMINUS  | plus-or-minus sign                       |
-+------------------------+------------------------------------------+
-| .. data:: ACS_PLUS     | big plus sign                            |
-+------------------------+------------------------------------------+
-| .. data:: ACS_RARROW   | right arrow                              |
-+------------------------+------------------------------------------+
-| .. data:: ACS_RTEE     | right tee                                |
-+------------------------+------------------------------------------+
-| .. data:: ACS_S1       | scan line 1                              |
-+------------------------+------------------------------------------+
-| .. data:: ACS_S3       | scan line 3                              |
-+------------------------+------------------------------------------+
-| .. data:: ACS_S7       | scan line 7                              |
-+------------------------+------------------------------------------+
-| .. data:: ACS_S9       | scan line 9                              |
-+------------------------+------------------------------------------+
-| .. data:: ACS_SBBS     | alternate name for lower-right corner    |
-+------------------------+------------------------------------------+
-| .. data:: ACS_SBSB     | alternate name for vertical line         |
-+------------------------+------------------------------------------+
-| .. data:: ACS_SBSS     | alternate name for right tee             |
-+------------------------+------------------------------------------+
-| .. data:: ACS_SSBB     | alternate name for lower-left corner     |
-+------------------------+------------------------------------------+
-| .. data:: ACS_SSBS     | alternate name for bottom tee            |
-+------------------------+------------------------------------------+
-| .. data:: ACS_SSSB     | alternate name for left tee              |
-+------------------------+------------------------------------------+
-| .. data:: ACS_SSSS     | alternate name for crossover or big plus |
-+------------------------+------------------------------------------+
-| .. data:: ACS_STERLING | pound sterling                           |
-+------------------------+------------------------------------------+
-| .. data:: ACS_TTEE     | top tee                                  |
-+------------------------+------------------------------------------+
-| .. data:: ACS_UARROW   | up arrow                                 |
-+------------------------+------------------------------------------+
-| .. data:: ACS_ULCORNER | upper-left corner                        |
-+------------------------+------------------------------------------+
-| .. data:: ACS_URCORNER | upper-right corner                       |
-+------------------------+------------------------------------------+
-| .. data:: ACS_VLINE    | vertical line                            |
-+------------------------+------------------------------------------+
+.. versionadded:: next
+   The ``WACS_*`` codes.
+
++------------------------+-------------------------+------------------------------------------+
+| ACS code               | WACS code               | Meaning                                  |
++========================+=========================+==========================================+
+| .. data:: ACS_BBSS     | .. data:: WACS_BBSS     | alternate name for upper-right corner    |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_BLOCK    | .. data:: WACS_BLOCK    | solid square block                       |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_BOARD    | .. data:: WACS_BOARD    | board of squares                         |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_BSBS     | .. data:: WACS_BSBS     | alternate name for horizontal line       |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_BSSB     | .. data:: WACS_BSSB     | alternate name for upper-left corner     |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_BSSS     | .. data:: WACS_BSSS     | alternate name for top tee               |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_BTEE     | .. data:: WACS_BTEE     | bottom tee                               |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_BULLET   | .. data:: WACS_BULLET   | bullet                                   |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_CKBOARD  | .. data:: WACS_CKBOARD  | checker board (stipple)                  |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_DARROW   | .. data:: WACS_DARROW   | arrow pointing down                      |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_DEGREE   | .. data:: WACS_DEGREE   | degree symbol                            |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_DIAMOND  | .. data:: WACS_DIAMOND  | diamond                                  |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_GEQUAL   | .. data:: WACS_GEQUAL   | greater-than-or-equal-to                 |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_HLINE    | .. data:: WACS_HLINE    | horizontal line                          |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_LANTERN  | .. data:: WACS_LANTERN  | lantern symbol                           |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_LARROW   | .. data:: WACS_LARROW   | left arrow                               |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_LEQUAL   | .. data:: WACS_LEQUAL   | less-than-or-equal-to                    |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_LLCORNER | .. data:: WACS_LLCORNER | lower-left corner                        |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_LRCORNER | .. data:: WACS_LRCORNER | lower-right corner                       |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_LTEE     | .. data:: WACS_LTEE     | left tee                                 |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_NEQUAL   | .. data:: WACS_NEQUAL   | not-equal sign                           |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_PI       | .. data:: WACS_PI       | letter pi                                |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_PLMINUS  | .. data:: WACS_PLMINUS  | plus-or-minus sign                       |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_PLUS     | .. data:: WACS_PLUS     | big plus sign                            |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_RARROW   | .. data:: WACS_RARROW   | right arrow                              |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_RTEE     | .. data:: WACS_RTEE     | right tee                                |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_S1       | .. data:: WACS_S1       | scan line 1                              |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_S3       | .. data:: WACS_S3       | scan line 3                              |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_S7       | .. data:: WACS_S7       | scan line 7                              |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_S9       | .. data:: WACS_S9       | scan line 9                              |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_SBBS     | .. data:: WACS_SBBS     | alternate name for lower-right corner    |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_SBSB     | .. data:: WACS_SBSB     | alternate name for vertical line         |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_SBSS     | .. data:: WACS_SBSS     | alternate name for right tee             |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_SSBB     | .. data:: WACS_SSBB     | alternate name for lower-left corner     |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_SSBS     | .. data:: WACS_SSBS     | alternate name for bottom tee            |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_SSSB     | .. data:: WACS_SSSB     | alternate name for left tee              |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_SSSS     | .. data:: WACS_SSSS     | alternate name for crossover or big plus |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_STERLING | .. data:: WACS_STERLING | pound sterling                           |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_TTEE     | .. data:: WACS_TTEE     | top tee                                  |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_UARROW   | .. data:: WACS_UARROW   | up arrow                                 |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_ULCORNER | .. data:: WACS_ULCORNER | upper-left corner                        |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_URCORNER | .. data:: WACS_URCORNER | upper-right corner                       |
++------------------------+-------------------------+------------------------------------------+
+| .. data:: ACS_VLINE    | .. data:: WACS_VLINE    | vertical line                            |
++------------------------+-------------------------+------------------------------------------+
+
+The following table lists the double-line and thick-line characters.
+They have no ``ACS_*`` counterpart, and are not provided by every implementation.
+As in the table above, the alternate name spells out the four sides of the
+character, clockwise from the top:
+``B`` for a blank side, ``S`` for a single line, ``D`` for a double line and
+``T`` for a thick line.
+
++---------------------------+---------------------+--------------------------------+
+| WACS code                 | Alternate name      | Meaning                        |
++===========================+=====================+================================+
+| .. data:: WACS_D_BTEE     | .. data:: WACS_DDBD | double-line bottom tee         |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_D_HLINE    | .. data:: WACS_BDBD | double-line horizontal line    |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_D_LLCORNER | .. data:: WACS_DDBB | double-line lower-left corner  |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_D_LRCORNER | .. data:: WACS_DBBD | double-line lower-right corner |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_D_LTEE     | .. data:: WACS_DDDB | double-line left tee           |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_D_PLUS     | .. data:: WACS_DDDD | double-line big plus sign      |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_D_RTEE     | .. data:: WACS_DBDD | double-line right tee          |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_D_TTEE     | .. data:: WACS_BDDD | double-line top tee            |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_D_ULCORNER | .. data:: WACS_BDDB | double-line upper-left corner  |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_D_URCORNER | .. data:: WACS_BBDD | double-line upper-right corner |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_D_VLINE    | .. data:: WACS_DBDB | double-line vertical line      |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_T_BTEE     | .. data:: WACS_TTBT | thick-line bottom tee          |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_T_HLINE    | .. data:: WACS_BTBT | thick-line horizontal line     |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_T_LLCORNER | .. data:: WACS_TTBB | thick-line lower-left corner   |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_T_LRCORNER | .. data:: WACS_TBBT | thick-line lower-right corner  |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_T_LTEE     | .. data:: WACS_TTTB | thick-line left tee            |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_T_PLUS     | .. data:: WACS_TTTT | thick-line big plus sign       |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_T_RTEE     | .. data:: WACS_TBTT | thick-line right tee           |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_T_TTEE     | .. data:: WACS_BTTT | thick-line top tee             |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_T_ULCORNER | .. data:: WACS_BTTB | thick-line upper-left corner   |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_T_URCORNER | .. data:: WACS_BBTT | thick-line upper-right corner  |
++---------------------------+---------------------+--------------------------------+
+| .. data:: WACS_T_VLINE    | .. data:: WACS_TBTB | thick-line vertical line       |
++---------------------------+---------------------+--------------------------------+
 
 Mouse buttons
 ~~~~~~~~~~~~~
diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst
index 063755e1eadcb53..a1a8415482b97aa 100644
--- a/Doc/whatsnew/3.16.rst
+++ b/Doc/whatsnew/3.16.rst
@@ -216,6 +216,11 @@ curses
   counterpart of :func:`curses.termattrs`.
   (Contributed by Serhiy Storchaka in :gh:`152332`.)
 
+* Add the ``WACS_*`` constants to the :mod:`curses` module, the counterparts of
+  the :ref:`ACS_* <curses-acs-codes>` line-drawing codes as
+  :class:`curses.complexchar` cells.
+  (Contributed by Serhiy Storchaka in :gh:`155863`.)
+
 * Add the :mod:`curses` functions :func:`curses.alloc_pair`,
   :func:`curses.find_pair`, :func:`curses.free_pair` and
   :func:`curses.reset_color_pairs` for dynamic color-pair management,
diff --git a/Lib/curses/__init__.py b/Lib/curses/__init__.py
index e150c7f932385eb..1e372ecdc831396 100644
--- a/Lib/curses/__init__.py
+++ b/Lib/curses/__init__.py
@@ -14,8 +14,8 @@
 import os as _os
 import sys as _sys
 
-# Some constants, most notably the ACS_* ones, are only added to the C
-# _curses module's dictionary after initscr() is called.  (Some
+# Some constants, most notably the ACS_* and WACS_* ones, are only added
+# to the C _curses module's dictionary after initscr() is called.  (Some
 # versions of SGI's curses don't define values for those constants
 # until initscr() has been called.)  This wrapper function calls the
 # underlying C initscr(), and then copies the constants from the
@@ -30,13 +30,13 @@ def initscr():
               fd=_sys.__stdout__.fileno())
     stdscr = _curses.initscr()
     for key, value in _curses.__dict__.items():
-        if key.startswith('ACS_') or key in ('LINES', 'COLS'):
+        if key.startswith(('ACS_', 'WACS_')) or key in ('LINES', 'COLS'):
             setattr(curses, key, value)
     return stdscr
 
-# newterm() is wrapped for the same reason as initscr(): the ACS_* constants
-# and LINES/COLS only become available once a terminal is initialized, and are
-# then copied to the curses package's dictionary.
+# newterm() is wrapped for the same reason as initscr(): the ACS_* and WACS_*
+# constants and LINES/COLS only become available once a terminal is
+# initialized, and are then copied to the curses package's dictionary.
 
 try:
     newterm
@@ -47,7 +47,7 @@ def newterm(type=None, fd=None, infd=None, /):
         import _curses, curses
         screen = _curses.newterm(type, fd, infd)
         for key, value in _curses.__dict__.items():
-            if key.startswith('ACS_') or key in ('LINES', 'COLS'):
+            if key.startswith(('ACS_', 'WACS_')) or key in ('LINES', 'COLS'):
                 setattr(curses, key, value)
         return screen
 
diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index 6518dadebae83ff..6a7a8e320438f85 100644
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -57,6 +57,23 @@ def wrapped(self, *args, **kwargs):
     return wrapped
 
 
+# The WACS_* double-line and thick-line character cells, without the common
+# prefix, paired with the alternate name spelling out their four sides
+# (blank, double or thick, clockwise from the top).
+WACS_LINE_ALIASES = [
+    ('D_ULCORNER', 'BDDB'), ('D_LLCORNER', 'DDBB'),
+    ('D_URCORNER', 'BBDD'), ('D_LRCORNER', 'DBBD'),
+    ('D_LTEE', 'DDDB'), ('D_RTEE', 'DBDD'),
+    ('D_BTEE', 'DDBD'), ('D_TTEE', 'BDDD'),
+    ('D_HLINE', 'BDBD'), ('D_VLINE', 'DBDB'), ('D_PLUS', 'DDDD'),
+    ('T_ULCORNER', 'BTTB'), ('T_LLCORNER', 'TTBB'),
+    ('T_URCORNER', 'BBTT'), ('T_LRCORNER', 'TBBT'),
+    ('T_LTEE', 'TTTB'), ('T_RTEE', 'TBTT'),
+    ('T_BTEE', 'TTBT'), ('T_TTEE', 'BTTT'),
+    ('T_HLINE', 'BTBT'), ('T_VLINE', 'TBTB'), ('T_PLUS', 'TTTT'),
+]
+
+
 def requires_colors(test):
     @functools.wraps(test)
     def wrapped(self, *args, **kwargs):
@@ -470,6 +487,72 @@ def test_wide_characters(self):
         # border() and box() cannot mix integer and wide-string characters.
         self.assertRaises(TypeError, stdscr.box, vline, ord('-'))
 
+    @requires_wide_build
+    def test_wacs_constants(self):
+        # Every ACS_* code has a WACS_* character cell counterpart, plus the
+        # double-line and thick-line codes, which have no ACS_* counterpart.
+        acs = {name.removeprefix('ACS_')
+               for name in dir(curses) if name.startswith('ACS_')}
+        wacs = {name.removeprefix('WACS_')
+                for name in dir(curses) if name.startswith('WACS_')}
+        extra = {name for pair in WACS_LINE_ALIASES for name in pair}
+        self.assertEqual(wacs - extra, acs)
+        for name in sorted(wacs):
+            with self.subTest(name=name):
+                self.assertIsInstance(getattr(curses, 'WACS_' + name),
+                                      curses.complexchar)
+        # The alternate names refer to the same cells.
+        self.assertEqual(curses.WACS_BSSB, curses.WACS_ULCORNER)
+        self.assertEqual(curses.WACS_BSBS, curses.WACS_HLINE)
+        self.assertEqual(curses.WACS_SBSB, curses.WACS_VLINE)
+        self.assertEqual(curses.WACS_SSSS, curses.WACS_PLUS)
+
+    @requires_wide_build
+    def test_wacs_line_constants(self):
+        # The double-line and thick-line codes are optional, but a supporting
+        # implementation provides the whole family under both names.
+        present = [name for name, alias in WACS_LINE_ALIASES
+                   if hasattr(curses, 'WACS_' + name)]
+        if not present:
+            self.skipTest('requires double-line and thick-line characters')
+        self.assertEqual(len(present), len(WACS_LINE_ALIASES))
+        for name, alias in WACS_LINE_ALIASES:
+            with self.subTest(name=name):
+                cell = getattr(curses, 'WACS_' + name)
+                self.assertIsInstance(cell, curses.complexchar)
+                self.assertEqual(getattr(curses, 'WACS_' + alias), cell)
+        # They are distinct from the single-line characters.
+        self.assertNotEqual(curses.WACS_D_HLINE, curses.WACS_HLINE)
+        self.assertNotEqual(curses.WACS_T_HLINE, curses.WACS_HLINE)
+        self.assertNotEqual(curses.WACS_D_HLINE, curses.WACS_T_HLINE)
+        stdscr = self.stdscr
+        stdscr.border(curses.WACS_D_VLINE, curses.WACS_D_VLINE,
+                      curses.WACS_D_HLINE, curses.WACS_D_HLINE,
+                      curses.WACS_D_ULCORNER, curses.WACS_D_URCORNER,
+                      curses.WACS_D_LLCORNER, curses.WACS_D_LRCORNER)
+        self.assertEqual(stdscr.in_wch(0, 0), curses.WACS_D_ULCORNER)
+        self.assertEqual(stdscr.in_wch(0, 1), curses.WACS_D_HLINE)
+
+    @requires_wide_build
+    def test_wacs_in_cell_methods(self):
+        # A WACS_* cell can be used wherever a character cell is accepted.
+        stdscr = self.stdscr
+        stdscr.addch(0, 0, curses.WACS_ULCORNER)
+        self.assertEqual(stdscr.in_wch(0, 0), curses.WACS_ULCORNER)
+        stdscr.insch(1, 0, curses.WACS_DIAMOND)
+        self.assertEqual(stdscr.in_wch(1, 0), curses.WACS_DIAMOND)
+        stdscr.hline(2, 0, curses.WACS_HLINE, 5)
+        self.assertEqual(stdscr.in_wch(2, 4), curses.WACS_HLINE)
+        stdscr.vline(3, 0, curses.WACS_VLINE, 3)
+        self.assertEqual(stdscr.in_wch(5, 0), curses.WACS_VLINE)
+        stdscr.border(curses.WACS_VLINE, curses.WACS_VLINE,
+                      curses.WACS_HLINE, curses.WACS_HLINE,
+                      curses.WACS_ULCORNER, curses.WACS_URCORNER,
+                      curses.WACS_LLCORNER, curses.WACS_LRCORNER)
+        self.assertEqual(stdscr.in_wch(0, 0), curses.WACS_ULCORNER)
+        stdscr.box(curses.WACS_VLINE, curses.WACS_HLINE)
+        self.assertEqual(stdscr.in_wch(0, 1), curses.WACS_HLINE)
+
     def test_complexchar_in_cell_methods(self):
         # Every single-character-cell method also accepts a complexchar, whose
         # attributes and color pair come from the cell itself.
diff --git a/Misc/NEWS.d/next/Library/2026-08-15-17-40-12.gh-issue-155863.Kw3Vqp.rst b/Misc/NEWS.d/next/Library/2026-08-15-17-40-12.gh-issue-155863.Kw3Vqp.rst
new file mode 100644
index 000000000000000..1266fbba5cc2325
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-15-17-40-12.gh-issue-155863.Kw3Vqp.rst
@@ -0,0 +1,4 @@
+Add the ``WACS_*`` constants to the :mod:`curses` module.  They are the
+counterparts of the ``ACS_*`` line-drawing codes as :class:`curses.complexchar`
+cells, and are added, like the latter, by :func:`curses.initscr` and
+:func:`curses.newterm`.
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index 866ed4bafe6b9cf..ea399e25213aef2 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -6612,6 +6612,179 @@ curses_init_dict(PyObject *module)
     SetDictInt("ACS_STERLING",      (ACS_STERLING));
 #endif
 
+#ifdef HAVE_NCURSESW
+    /* The same graphic symbols as character cells, for the methods that take
+       a complexchar.  Unlike the ACS_* codes, these are not restricted to the
+       8-bit alternate character set. */
+    cursesmodule_state *state = get_cursesmodule_state(module);
+#define SetDictWACS(NAME, VALUE)                                    \
+    do {                                                            \
+        PyObject *value = PyCursesComplexChar_New(state, (VALUE));  \
+        if (value == NULL) {                                        \
+            return -1;                                              \
+        }                                                           \
+        int rc = PyDict_SetItemString(module_dict, (NAME), value);  \
+        Py_DECREF(value);                                           \
+        if (rc < 0) {                                               \
+            return -1;                                              \
+        }                                                           \
+    } while (0)
+
+    SetDictWACS("WACS_ULCORNER",    WACS_ULCORNER);
+    SetDictWACS("WACS_LLCORNER",    WACS_LLCORNER);
+    SetDictWACS("WACS_URCORNER",    WACS_URCORNER);
+    SetDictWACS("WACS_LRCORNER",    WACS_LRCORNER);
+    SetDictWACS("WACS_LTEE",        WACS_LTEE);
+    SetDictWACS("WACS_RTEE",        WACS_RTEE);
+    SetDictWACS("WACS_BTEE",        WACS_BTEE);
+    SetDictWACS("WACS_TTEE",        WACS_TTEE);
+    SetDictWACS("WACS_HLINE",       WACS_HLINE);
+    SetDictWACS("WACS_VLINE",       WACS_VLINE);
+    SetDictWACS("WACS_PLUS",        WACS_PLUS);
+    SetDictWACS("WACS_S1",          WACS_S1);
+    SetDictWACS("WACS_S9",          WACS_S9);
+    SetDictWACS("WACS_DIAMOND",     WACS_DIAMOND);
+    SetDictWACS("WACS_CKBOARD",     WACS_CKBOARD);
+    SetDictWACS("WACS_DEGREE",      WACS_DEGREE);
+    SetDictWACS("WACS_PLMINUS",     WACS_PLMINUS);
+    SetDictWACS("WACS_BULLET",      WACS_BULLET);
+    SetDictWACS("WACS_LARROW",      WACS_LARROW);
+    SetDictWACS("WACS_RARROW",      WACS_RARROW);
+    SetDictWACS("WACS_DARROW",      WACS_DARROW);
+    SetDictWACS("WACS_UARROW",      WACS_UARROW);
+    SetDictWACS("WACS_BOARD",       WACS_BOARD);
+    SetDictWACS("WACS_LANTERN",     WACS_LANTERN);
+    SetDictWACS("WACS_BLOCK",       WACS_BLOCK);
+
+    SetDictWACS("WACS_BSSB",        WACS_ULCORNER);
+    SetDictWACS("WACS_SSBB",        WACS_LLCORNER);
+    SetDictWACS("WACS_BBSS",        WACS_URCORNER);
+    SetDictWACS("WACS_SBBS",        WACS_LRCORNER);
+    SetDictWACS("WACS_SBSS",        WACS_RTEE);
+    SetDictWACS("WACS_SSSB",        WACS_LTEE);
+    SetDictWACS("WACS_SSBS",        WACS_BTEE);
+    SetDictWACS("WACS_BSSS",        WACS_TTEE);
+    SetDictWACS("WACS_BSBS",        WACS_HLINE);
+    SetDictWACS("WACS_SBSB",        WACS_VLINE);
+    SetDictWACS("WACS_SSSS",        WACS_PLUS);
+
+    /* The following are never available with strict SYSV curses */
+#ifdef WACS_S3
+    SetDictWACS("WACS_S3",          WACS_S3);
+#endif
+#ifdef WACS_S7
+    SetDictWACS("WACS_S7",          WACS_S7);
+#endif
+#ifdef WACS_LEQUAL
+    SetDictWACS("WACS_LEQUAL",      WACS_LEQUAL);
+#endif
+#ifdef WACS_GEQUAL
+    SetDictWACS("WACS_GEQUAL",      WACS_GEQUAL);
+#endif
+#ifdef WACS_PI
+    SetDictWACS("WACS_PI",          WACS_PI);
+#endif
+#ifdef WACS_NEQUAL
+    SetDictWACS("WACS_NEQUAL",      WACS_NEQUAL);
+#endif
+#ifdef WACS_STERLING
+    SetDictWACS("WACS_STERLING",    WACS_STERLING);
+#endif
+
+    /* Double-line and thick-line symbols have no ACS_* counterpart, and are
+       only provided by some implementations. */
+#ifdef WACS_D_ULCORNER
+    SetDictWACS("WACS_D_ULCORNER",  WACS_D_ULCORNER);
+    SetDictWACS("WACS_BDDB",        WACS_D_ULCORNER);
+#endif
+#ifdef WACS_D_LLCORNER
+    SetDictWACS("WACS_D_LLCORNER",  WACS_D_LLCORNER);
+    SetDictWACS("WACS_DDBB",        WACS_D_LLCORNER);
+#endif
+#ifdef WACS_D_URCORNER
+    SetDictWACS("WACS_D_URCORNER",  WACS_D_URCORNER);
+    SetDictWACS("WACS_BBDD",        WACS_D_URCORNER);
+#endif
+#ifdef WACS_D_LRCORNER
+    SetDictWACS("WACS_D_LRCORNER",  WACS_D_LRCORNER);
+    SetDictWACS("WACS_DBBD",        WACS_D_LRCORNER);
+#endif
+#ifdef WACS_D_LTEE
+    SetDictWACS("WACS_D_LTEE",      WACS_D_LTEE);
+    SetDictWACS("WACS_DDDB",        WACS_D_LTEE);
+#endif
+#ifdef WACS_D_RTEE
+    SetDictWACS("WACS_D_RTEE",      WACS_D_RTEE);
+    SetDictWACS("WACS_DBDD",        WACS_D_RTEE);
+#endif
+#ifdef WACS_D_BTEE
+    SetDictWACS("WACS_D_BTEE",      WACS_D_BTEE);
+    SetDictWACS("WACS_DDBD",        WACS_D_BTEE);
+#endif
+#ifdef WACS_D_TTEE
+    SetDictWACS("WACS_D_TTEE",      WACS_D_TTEE);
+    SetDictWACS("WACS_BDDD",        WACS_D_TTEE);
+#endif
+#ifdef WACS_D_HLINE
+    SetDictWACS("WACS_D_HLINE",     WACS_D_HLINE);
+    SetDictWACS("WACS_BDBD",        WACS_D_HLINE);
+#endif
+#ifdef WACS_D_VLINE
+    SetDictWACS("WACS_D_VLINE",     WACS_D_VLINE);
+    SetDictWACS("WACS_DBDB",        WACS_D_VLINE);
+#endif
+#ifdef WACS_D_PLUS
+    SetDictWACS("WACS_D_PLUS",      WACS_D_PLUS);
+    SetDictWACS("WACS_DDDD",        WACS_D_PLUS);
+#endif
+
+#ifdef WACS_T_ULCORNER
+    SetDictWACS("WACS_T_ULCORNER",  WACS_T_ULCORNER);
+    SetDictWACS("WACS_BTTB",        WACS_T_ULCORNER);
+#endif
+#ifdef WACS_T_LLCORNER
+    SetDictWACS("WACS_T_LLCORNER",  WACS_T_LLCORNER);
+    SetDictWACS("WACS_TTBB",        WACS_T_LLCORNER);
+#endif
+#ifdef WACS_T_URCORNER
+    SetDictWACS("WACS_T_URCORNER",  WACS_T_URCORNER);
+    SetDictWACS("WACS_BBTT",        WACS_T_URCORNER);
+#endif
+#ifdef WACS_T_LRCORNER
+    SetDictWACS("WACS_T_LRCORNER",  WACS_T_LRCORNER);
+    SetDictWACS("WACS_TBBT",        WACS_T_LRCORNER);
+#endif
+#ifdef WACS_T_LTEE
+    SetDictWACS("WACS_T_LTEE",      WACS_T_LTEE);
+    SetDictWACS("WACS_TTTB",        WACS_T_LTEE);
+#endif
+#ifdef WACS_T_RTEE
+    SetDictWACS("WACS_T_RTEE",      WACS_T_RTEE);
+    SetDictWACS("WACS_TBTT",        WACS_T_RTEE);
+#endif
+#ifdef WACS_T_BTEE
+    SetDictWACS("WACS_T_BTEE",      WACS_T_BTEE);
+    SetDictWACS("WACS_TTBT",        WACS_T_BTEE);
+#endif
+#ifdef WACS_T_TTEE
+    SetDictWACS("WACS_T_TTEE",      WACS_T_TTEE);
+    SetDictWACS("WACS_BTTT",        WACS_T_TTEE);
+#endif
+#ifdef WACS_T_HLINE
+    SetDictWACS("WACS_T_HLINE",     WACS_T_HLINE);
+    SetDictWACS("WACS_BTBT",        WACS_T_HLINE);
+#endif
+#ifdef WACS_T_VLINE
+    SetDictWACS("WACS_T_VLINE",     WACS_T_VLINE);
+    SetDictWACS("WACS_TBTB",        WACS_T_VLINE);
+#endif
+#ifdef WACS_T_PLUS
+    SetDictWACS("WACS_T_PLUS",      WACS_T_PLUS);
+    SetDictWACS("WACS_TTTT",        WACS_T_PLUS);
+#endif
+#undef SetDictWACS
+#endif /* HAVE_NCURSESW */
+
     SetDictInt("LINES", LINES);
     SetDictInt("COLS", COLS);
 #undef SetDictInt

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.