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]

Reply via email to