Search code examples
pythonncursescpythoncurses

Python Curses init_pair implementation not matching the documentation


I've been experimenting with Curses colors and ran into a bit of an issue.

As the documentation of init_pair states, the first argument (the number of the pair) shall be between 1 and curses.COLOR_PAIRS - 1. Doing print(curses.COLOR, curses.COLOR_PAIR) yields 256 65536, so one would think that calling courses.init_pair(40000, 1, 53) (random example) would work, but I get an error instead:

Traceback (most recent call last):
  File "4-colors.py", line 38, in <module>
    curses.wrapper(main)
  File "/usr/lib/python3.8/curses/__init__.py", line 105, in wrapper
    return func(stdscr, *args, **kwds)
  File "4-colors.py", line 18, in main
    curses.init_pair(40000, 1, 53)
OverflowError: signed short integer is greater than maximum

Sure enough, the implementation of init_color (I hope that I'm looking at the right file) checks whether the color pair number is within bounds of a signed short.

Why? Is there a way to bypass this and use all of the colors of my terminal, not just an arbitrary half?


Full source code of the MWE:

import curses

def main(window):
    curses.start_color()
    curses.use_default_colors()

    curses.init_pair(40000, 1, 53)

curses.wrapper(main)

Solution

  • The problem is that Python's curses binding was not updated to account for the changes made in ncurses 6.1: before that point, capability values (numbers) passed to/from the curses library were limited to a signed 16-bit number. ncurses 6.1 extended that, although to make effective use of things like COLOR_PAIRS, it is necessary to use one of the function extensions introduced in ncurses 6.1

    In the given example, for instance, rather than calling init_pair, whose C prototype is

    int init_pair(short pair, short f, short b);
    

    one would use init_extended_pair:

    int init_extended_pair(int pair, int f, int b);
    

    Of course, it's always been the case that applications are responsible for validating the numbers that they deal with. The example shown illustrates a Python runtime exception (rather than for example limiting the number of color pairs seen by callers).