Skip to content

gh-154840: Keep the color pair out of curses complexchar.attr - #154841

Open
fedonman wants to merge 3 commits into
python:mainfrom
fedonman:curses-complexchar-attr-color
Open

gh-154840: Keep the color pair out of curses complexchar.attr#154841
fedonman wants to merge 3 commits into
python:mainfrom
fedonman:curses-complexchar-attr-color

Conversation

@fedonman

@fedonman fedonman commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

complexchar.attr carried the packed color pair on a wide build and saturated at 255,
although the docs say the pair is stored separately and is not limited to a color_pair()
value. Mask A_COLOR in the wide branch of curses_cell_attr_pair(), which its narrow
branch already does. repr() reads through the same function and is fixed with it.

The test fails without the change. New in 3.16, so no NEWS entry.

On a wide build getcchar() reports the packed color pair in the attributes,
so attr carried it and saturated at 255, although the documentation says the
pair is stored separately and is not limited to a color_pair() value. Mask
A_COLOR out, as the narrow branch of the same function already does. repr()
reads through the same function and is fixed with it.
Comment thread Lib/test/test_curses.py Outdated
self.assertEqual(curses.complexchar('A', 0, 1).pair, 1)
# attr never carries the color pair, not even a pair that does not fit
# in a color_pair() value.
self.assertEqual(curses.complexchar('A', 0, 1).attr, 0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestCurses.test_complexchar is probably a better place for these new tests.

Comment thread Modules/_cursesmodule.c Outdated
PyErr_SetString(state->error, "getcchar() returned ERR");
return -1;
}
*attr &= ~(attr_t)A_COLOR;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would curses_getcchar() be a better place for the mask?

getcchar() is the one place that reads the pair out of a cell, so the mask
belongs there. inch() no longer needs its own. The complexchar checks move
to test_complexchar.
@fedonman

fedonman commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

@serhiy-storchaka Both done, thanks.

Moving the mask into curses_getcchar() turned out better than I expected. inch() already had the same mask written out by hand, so that one is gone now. And curses_cell_equal() and curses_cchar_hash() call curses_getcchar() directly, so they were comparing the pair twice, once through the opts slot and once through the A_COLOR bits. Now it is counted once.

The complexchar checks moved to test_complexchar. I left the in_wch assertion where it is, since that one is about reading a colored cell back from the screen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants