This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: curses.addch('a', curses.color_pair(1)) ignores the color information
Type: Stage: resolved
Components: Library (Lib) Versions: Python 3.9, Python 3.8, Python 3.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: mgedmin, miss-islington, vstinner
Priority: normal Keywords: patch

Created on 2019-08-01 13:45 by mgedmin, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 15071 merged vstinner, 2019-08-01 15:12
PR 15272 merged miss-islington, 2019-08-14 10:31
PR 15273 merged vstinner, 2019-08-14 10:41
Messages (8)
msg348855 - (view) Author: Marius Gedminas (mgedmin) * Date: 2019-08-01 13:45
curses.addch() ignores color information if I pass it a string of length one.  Color works fine if I pass it a byte string or an int.  Here's a reproducer:

### start of example ###
import curses                                                                                                                               
                                                                                                                                            
                                                                                                                                            
def main(stdscr):                                                                                                                           
    curses.start_color()                                                                                                                    
    curses.use_default_colors()                                                                                                             
    curses.init_pair(1, curses.COLOR_RED, -1)                                                                                               
    curses.init_pair(2, curses.COLOR_GREEN, -1)                                                                                             
    curses.curs_set(0)                                                                                                                      
                                                                                                                                            
    stdscr.addch("a", curses.color_pair(1))                                                                                                 
    stdscr.addch("b", curses.color_pair(2) | curses.A_BOLD)                                                                                 
    stdscr.addch(b"c", curses.color_pair(1))                                                                                                
    stdscr.addch(b"d", curses.color_pair(2) | curses.A_BOLD)                                                                                
    stdscr.addch(ord("e"), curses.color_pair(1))                                                                                            
    stdscr.addch(ord("f"), curses.color_pair(2) | curses.A_BOLD)                                                                            
    stdscr.refresh()                                                                                                                        
                                                                                                                                            
    stdscr.getch()                                                                                                                          
                                                                                                                                            
                                                                                                                                            
curses.wrapper(main)                                                                                                                        
### end of example ###

On Python 2.7 this prints 'abcdef' in alternating red and green.  On Python 3.5 through 3.8 this prints 'ab' in white and the rest in red/green.

Note that only color pair information is lost -- the bold attribute is correctly set on the 'b'.
msg348862 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2019-08-01 15:13
stdscr.addch(str, color_pair) is implemented with:

   setcchar(&wcval, wstr, attr, 0, NULL);
   rtn = wadd_wch(self->win, &wcval);

whereas stdscr.addch(bytes, color_pair) is implemented with:

   rtn = waddch(self->win, cch | (attr_t) attr);

The 4th argument of setcchar() is "short color_pair": Python always pass 0. It seems to be your bug.

Attached PR 15071 fix this bug.

Note: Python 3.5 and 3.6 don't accept bugfixes anymore, only security fixes.
msg348863 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2019-08-01 15:18
I'm able to reproduce the issue on Fedora 30: Python 3.7.4 with ncurses-libs-6.1-10.20180923.fc30.x86_64.

vstinner@apu$ cat /etc/fedora-release 
Fedora release 30 (Thirty)
vstinner@apu$ python3 -VV
Python 3.7.4 (default, Jul  9 2019, 16:32:37) 
[GCC 9.1.1 20190503 (Red Hat 9.1.1-1)]
vstinner@apu$ python3 -c 'import _curses; print(_curses.__file__)'
/usr/lib64/python3.7/lib-dynload/_curses.cpython-37m-x86_64-linux-gnu.so
vstinner@apu$ ldd $(python3 -c 'import _curses; print(_curses.__file__)')
	linux-vdso.so.1 (0x00007ffe6f1b4000)
	libncursesw.so.6 => /lib64/libncursesw.so.6 (0x00007f1acf456000)
	libtinfo.so.6 => /lib64/libtinfo.so.6 (0x00007f1acf427000)
	libpython3.7m.so.1.0 => /lib64/libpython3.7m.so.1.0 (0x00007f1acf0de000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f1acef18000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007f1acef12000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f1aceef1000)
	libutil.so.1 => /lib64/libutil.so.1 (0x00007f1aceeea000)
	libm.so.6 => /lib64/libm.so.6 (0x00007f1aceda4000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f1acf4dc000)
vstinner@apu$ rpm -qf /lib64/libncursesw.so.6
ncurses-libs-6.1-10.20180923.fc30.x86_64
msg349672 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2019-08-14 10:31
New changeset 077af8c2c93dd71086e2c5e5ff1e634b6da8f214 by Victor Stinner in branch 'master':
bpo-37738: Fix curses addch(str, color_pair) (GH-15071)
https://github.com/python/cpython/commit/077af8c2c93dd71086e2c5e5ff1e634b6da8f214
msg349673 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2019-08-14 10:40
On my Fedora 30 with libncursesw, A_COLOR = 0xff00.

After my change, _curses uses:

    static inline short
    attr_to_color_pair(int attr)
    {
        return (short)((attr & A_COLOR) >> 8);
    }

    ...
    setcchar(&wcval, wstr, attr, attr_to_color_pair(attr), NULL);
    ...

If someone gets troubles with attr passed "directly" as the 3rd argument of setcchar(), we can try to pass (attr & ~A_COLOR) instead. On my Linux, it would mean: only pass the low 8 bits of attr.

But since it "just" works on my Linux, I prefer to only make minimum changes to fix this issue on Linux.
msg349674 - (view) Author: miss-islington (miss-islington) Date: 2019-08-14 10:49
New changeset 984226962bc35254551d92771b5c8fb074507903 by Miss Islington (bot) in branch '3.8':
bpo-37738: Fix curses addch(str, color_pair) (GH-15071)
https://github.com/python/cpython/commit/984226962bc35254551d92771b5c8fb074507903
msg349676 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2019-08-14 11:00
New changeset 7eef81ee766c8df23e522b4e46a930cc1d360ad7 by Victor Stinner in branch '3.7':
bpo-37738: Fix curses addch(str, color_pair) (GH-15071) (GH-15273)
https://github.com/python/cpython/commit/7eef81ee766c8df23e522b4e46a930cc1d360ad7
msg349677 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2019-08-14 11:01
I fixed the bug in 3.7, 3.8 and master (future 3.9) branches. Thanks Marius Gedminas for the bug report. In the meanwhile, you have to pass bytes strings to addch() :-(
History
Date User Action Args
2022-04-11 14:59:18adminsetgithub: 81919
2019-08-14 11:01:28vstinnersetstatus: open -> closed
resolution: fixed
messages: + msg349677

stage: patch review -> resolved
2019-08-14 11:00:30vstinnersetmessages: + msg349676
2019-08-14 10:49:16miss-islingtonsetnosy: + miss-islington
messages: + msg349674
2019-08-14 10:41:55vstinnersetpull_requests: + pull_request14995
2019-08-14 10:40:24vstinnersetmessages: + msg349673
2019-08-14 10:31:57vstinnersetmessages: + msg349672
2019-08-14 10:31:54miss-islingtonsetpull_requests: + pull_request14994
2019-08-01 16:03:23vstinnersetversions: + Python 3.9, - Python 3.5, Python 3.6
2019-08-01 15:18:31vstinnersetmessages: + msg348863
2019-08-01 15:13:21vstinnersetnosy: + vstinner
messages: + msg348862
2019-08-01 15:12:29vstinnersetkeywords: + patch
stage: patch review
pull_requests: + pull_request14818
2019-08-01 13:45:15mgedmincreate