Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Include/py_curses.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@
# define PDC_NCMOUSE
#endif

/* On Solaris/illumos, the SVr4 <curses.h> does "typedef char bool;", which
clashes with C's bool from <stdbool.h>. Define _BOOL to suppress it, and
include <stdbool.h> for the bool the header then needs. ncurses ignores
_BOOL. */
#if defined(__sun) && !defined(_BOOL)
# include <stdbool.h>
# define _BOOL
#endif

#if defined(HAVE_NCURSESW_NCURSES_H)
# include <ncursesw/ncurses.h>
#elif defined(HAVE_NCURSESW_CURSES_H)
Expand Down
38 changes: 26 additions & 12 deletions Lib/test/test_curses.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,15 @@ def wrapped(self, *args, **kwargs):
term = os.environ.get('TERM')
SHORT_MAX = 0x7fff

# ncurses before 6.5 can crash on repeated newterm(). Fall back to initscr()
# and skip the tests that need several screens.
# ncurses before 6.5, and the native curses of NetBSD and illumos/Solaris,
# crash on repeated newterm()/delscreen(); fall back to initscr() and skip the
# multi-screen tests. The native ones are keyed off the platform so a fixed
# version can be excluded later.
_ncurses_version = getattr(curses, 'ncurses_version', None)
BROKEN_NEWTERM = _ncurses_version is not None and _ncurses_version < (6, 5)
if _ncurses_version is not None:
BROKEN_NEWTERM = _ncurses_version < (6, 5)
else:
BROKEN_NEWTERM = sys.platform.startswith(('netbsd', 'sunos'))
USE_NEWTERM = hasattr(curses, 'newterm') and not BROKEN_NEWTERM

# Older macOS reports a variation selector as a spacing character (wcwidth()
Expand Down Expand Up @@ -1143,8 +1148,17 @@ def test_attributes(self):
win.standout()
win.standend()

# attron()/attroff()/attrset() reject a bad attribute.
self.assertRaises(OverflowError, win.attron, 1 << 64)
self.assertRaises(OverflowError, win.attroff, -1)
self.assertRaises(OverflowError, win.attrset, 1 << 64)
self.assertRaises(TypeError, win.attron, 'x')

@requires_curses_window_meth('attr_set')
def test_attr(self):
# The attr_*() family works on attr_t attributes paired with a color
# pair, unlike the chtype-based attron()/attroff()/attrset().
win = curses.newwin(5, 15, 5, 2)
win.attr_set(curses.A_BOLD | curses.A_UNDERLINE)
attrs, pair = win.attr_get()
self.assertTrue(attrs & curses.A_BOLD)
Expand All @@ -1170,13 +1184,9 @@ def test_attributes(self):
self.assertRaises(OverflowError, win.attr_set, -1)
self.assertRaises(OverflowError, win.attr_on, -1)
self.assertRaises(OverflowError, win.attr_set, 1 << 64)
# attron()/attroff()/attrset() reject a bad attribute too.
self.assertRaises(OverflowError, win.attron, 1 << 64)
self.assertRaises(OverflowError, win.attroff, -1)
self.assertRaises(OverflowError, win.attrset, 1 << 64)
self.assertRaises(TypeError, win.attron, 'x')

@requires_colors
@requires_curses_window_meth('attr_set')
def test_attr_color_pair(self):
win = curses.newwin(5, 15, 5, 2)
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
Expand Down Expand Up @@ -1386,8 +1396,10 @@ def test_scr_dump(self):
stdscr.refresh()
self.assertIsNone(curses.scr_restore(dump))
# scr_init() and scr_set() also accept a dump file and return None.
# scr_set() is not available on every curses (e.g. old SVr4).
self.assertIsNone(curses.scr_init(dump))
self.assertIsNone(curses.scr_set(dump))
if hasattr(curses, 'scr_set'):
self.assertIsNone(curses.scr_set(dump))
# A bytes (path-like) filename is accepted too.
curses.scr_dump(os.fsencode(dump))
# Restoring from a missing file is an error.
Expand Down Expand Up @@ -1849,9 +1861,11 @@ def test_escdelay(self):
def test_tabsize(self):
tabsize = curses.get_tabsize()
self.assertIsInstance(tabsize, int)
curses.set_tabsize(4)
self.assertEqual(curses.get_tabsize(), 4)
curses.set_tabsize(tabsize)
# set_tabsize() is not available on every curses (e.g. old SVr4).
if hasattr(curses, 'set_tabsize'):
curses.set_tabsize(4)
self.assertEqual(curses.get_tabsize(), 4)
curses.set_tabsize(tabsize)

@requires_curses_func('getsyx')
def test_getsyx(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The :mod:`curses` and :mod:`curses.panel` modules can now be built against a
curses library that lacks the X/Open ``attr_t`` and soft-label attribute
functions, ``scr_set()`` or ``resizeterm()`` -- such as the native SVr4 curses
of illumos and Solaris. These functions are probed and used only when present.
27 changes: 23 additions & 4 deletions Modules/_cursesmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2433,6 +2433,7 @@ _curses_window_attrset_impl(PyCursesWindowObject *self, attr_t attr)
return curses_window_check_err(self, rtn, "wattrset", "attrset");
}

#ifdef HAVE_CURSES_WATTR_GET
/*[clinic input]
_curses.window.attr_get

Expand All @@ -2458,7 +2459,9 @@ _curses_window_attr_get_impl(PyCursesWindowObject *self)
}
return Py_BuildValue("(ki)", (unsigned long)attrs, (int)pair);
}
#endif /* HAVE_CURSES_WATTR_GET */

#ifdef HAVE_CURSES_WATTR_SET
/*[clinic input]
_curses.window.attr_set

Expand All @@ -2482,7 +2485,9 @@ _curses_window_attr_set_impl(PyCursesWindowObject *self, attr_t attr,
#endif
return curses_window_check_err(self, rtn, "wattr_set", "attr_set");
}
#endif /* HAVE_CURSES_WATTR_SET */

#ifdef HAVE_CURSES_WATTR_ON
/*[clinic input]
_curses.window.attr_on

Expand All @@ -2499,7 +2504,9 @@ _curses_window_attr_on_impl(PyCursesWindowObject *self, attr_t attr)
int rtn = wattr_on(self->win, attr, NULL);
return curses_window_check_err(self, rtn, "wattr_on", "attr_on");
}
#endif /* HAVE_CURSES_WATTR_ON */

#ifdef HAVE_CURSES_WATTR_OFF
/*[clinic input]
_curses.window.attr_off

Expand All @@ -2516,7 +2523,9 @@ _curses_window_attr_off_impl(PyCursesWindowObject *self, attr_t attr)
int rtn = wattr_off(self->win, attr, NULL);
return curses_window_check_err(self, rtn, "wattr_off", "attr_off");
}
#endif /* HAVE_CURSES_WATTR_OFF */

#ifdef HAVE_CURSES_WCOLOR_SET
/*[clinic input]
_curses.window.color_set

Expand All @@ -2538,6 +2547,7 @@ _curses_window_color_set_impl(PyCursesWindowObject *self, int pair)
#endif
return curses_window_check_err(self, rtn, "wcolor_set", "color_set");
}
#endif /* HAVE_CURSES_WCOLOR_SET */

/*[clinic input]
_curses.window.getattrs
Expand Down Expand Up @@ -6065,6 +6075,7 @@ _curses_scr_init(PyObject *module, PyObject *filename)
/*[clinic end generated code: output=2e861d381d710419 input=81c45e4702124ef6]*/
ScreenDumpFunctionBody(scr_init)

#ifdef HAVE_CURSES_SCR_SET
/*[clinic input]
_curses.scr_set

Expand All @@ -6081,6 +6092,7 @@ static PyObject *
_curses_scr_set(PyObject *module, PyObject *filename)
/*[clinic end generated code: output=6056fdec12c5935f input=d248c20543cc289b]*/
ScreenDumpFunctionBody(scr_set)
#endif /* HAVE_CURSES_SCR_SET */
#endif /* HAVE_CURSES_SCR_DUMP */

/*[clinic input]
Expand Down Expand Up @@ -7444,9 +7456,10 @@ _curses_qiflush_impl(PyObject *module, int flag)
Py_RETURN_NONE;
}

#if defined(HAVE_CURSES_RESIZETERM) || defined(HAVE_CURSES_RESIZE_TERM)
/* Internal helper used for updating curses.LINES, curses.COLS, _curses.LINES
* and _curses.COLS. Returns 1 on success and 0 on failure. */
* and _curses.COLS. Returns 1 on success and 0 on failure. Used
* unconditionally (e.g. by set_term()), so it must not be gated on resizeterm().
*/
static int
update_lines_cols(PyObject *private_module)
{
Expand Down Expand Up @@ -7515,8 +7528,6 @@ _curses_update_lines_cols_impl(PyObject *module)
Py_RETURN_NONE;
}

#endif

/*[clinic input]
_curses.raw

Expand Down Expand Up @@ -8353,6 +8364,7 @@ _curses_slk_attr_impl(PyObject *module)
}
#endif

#ifdef HAVE_CURSES_SLK_ATTR_ON
/*[clinic input]
_curses.slk_attr_on

Expand All @@ -8370,7 +8382,9 @@ _curses_slk_attr_on_impl(PyObject *module, attr_t attr)
return curses_check_err(module, slk_attr_on(attr, NULL),
"slk_attr_on", NULL);
}
#endif /* HAVE_CURSES_SLK_ATTR_ON */

#ifdef HAVE_CURSES_SLK_ATTR_OFF
/*[clinic input]
_curses.slk_attr_off

Expand All @@ -8388,7 +8402,9 @@ _curses_slk_attr_off_impl(PyObject *module, attr_t attr)
return curses_check_err(module, slk_attr_off(attr, NULL),
"slk_attr_off", NULL);
}
#endif /* HAVE_CURSES_SLK_ATTR_OFF */

#ifdef HAVE_CURSES_SLK_ATTR_SET
/*[clinic input]
_curses.slk_attr_set

Expand All @@ -8412,7 +8428,9 @@ _curses_slk_attr_set_impl(PyObject *module, attr_t attr, int pair)
#endif
return curses_check_err(module, rtn, "slk_attr_set", NULL);
}
#endif /* HAVE_CURSES_SLK_ATTR_SET */

#ifdef HAVE_CURSES_SLK_COLOR
/*[clinic input]
_curses.slk_color

Expand All @@ -8429,6 +8447,7 @@ _curses_slk_color_impl(PyObject *module, int pair)
PyCursesStatefulInitialised(module);
return curses_check_err(module, slk_color((short)pair), "slk_color", NULL);
}
#endif /* HAVE_CURSES_SLK_COLOR */

#ifdef HAVE_CURSES_USE_ENV
/*[clinic input]
Expand Down
Loading
Loading