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]