Skip to content
Closed
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
12 changes: 12 additions & 0 deletions Doc/library/curses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,18 @@ the following methods and attributes:
Return the character at the given position in the window. The bottom 8 bits are
the character proper, and upper bits are the attributes.

.. note::

``inch`` only works for ASCII characters. Use :meth:`in_wch` instead
for unicode support.
Comment on lines +1020 to +1023

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.

Suggested change
.. note::
``inch`` only works for ASCII characters. Use :meth:`in_wch` instead
for unicode support.
.. note::
This method only works for ASCII characters. Use :meth:`in_wch`
instead to retrieve the Unicode character at a given position.


.. method:: window.in_wch([x, y])
Comment on lines +1024 to +1025

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.

Suggested change
.. method:: window.in_wch([x, y])
.. method:: window.in_wch([x, y])

We separate methods by 2 blank lines (just check that my suggestion is correct before committing it).

Comment on lines +1024 to +1025

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.

Suggested change
.. method:: window.in_wch([x, y])
.. method:: window.in_wch([y, x])


Return the wide character at the given position in the window. The return
value is a 3-tuple ``(character, attributes, color_pair)``.
Comment on lines +1027 to +1028

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.

Suggested change
Return the wide character at the given position in the window. The return
value is a 3-tuple ``(character, attributes, color_pair)``.
Return the wide character at the given position in the window as
a triplet ``(character, attributes, color_pair)``.


.. versionadded:: 3.14


.. method:: window.insch(ch[, attr])
window.insch(y, x, ch[, attr])
Expand Down
33 changes: 33 additions & 0 deletions Lib/test/test_curses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,39 @@ def test_issue6243(self):
curses.ungetch(1025)
self.stdscr.getkey()

def test_in_wch(self):
stdscr = self.stdscr

if not hasattr(stdscr, 'in_wch'):
raise unittest.SkipTest('requires curses.window.in_wch')
Comment on lines +1113 to +1114

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.

Do you need this kind of check or can you use @requires_curses_window_meth('in_wch')?


if curses.has_colors():
curses.init_pair(1, curses.COLOR_RED, 0)
expected_pair = 1
color_attr = curses.color_pair(expected_pair)
else:
expected_pair = 0
color_attr = 0

def _norm(ch, attr, color):
# for some reason, curses returns some color bits here?
return ch, attr & ~curses.A_COLOR, color

for ch in ('a', '\xe9', '\u20ac', '\U0001f643'):

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.

I would use a subTest to locate failures if needed.

expected = (ch, curses.A_BOLD, expected_pair)

stdscr.insstr(0, 0, ch, color_attr | curses.A_BOLD)
self.assertEqual(_norm(*stdscr.in_wch()), expected)

stdscr.addstr(0, 0, ch, color_attr | curses.A_BOLD)
self.assertEqual(_norm(*stdscr.in_wch(0, 0)), expected)
Comment on lines +1131 to +1135

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.

Can we use something that is not at (0, 0) as well to check the ordering (y, x)?


# in_wch can return multiple characters in the case of zero width
# curses at max returns 5 characters in a cchar_t
stdscr.addstr(0, 0, 'a' + '\u200b' * 10)
expected = ('a\u200b\u200b\u200b\u200b', 0, 0)
self.assertEqual(stdscr.in_wch(0, 0), expected)

@requires_curses_func('unget_wch')
@unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8),
"unget_wch is broken in ncurses 5.7 and earlier")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :func:`curses.window.in_wch` function - by Anthony Sottile.

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.

Suggested change
Add :func:`curses.window.in_wch` function - by Anthony Sottile.
Add :func:`curses.window.in_wch` function to get the Unicode character
at a given position. Patch by Anthony Sottile.

49 changes: 49 additions & 0 deletions Modules/_cursesmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,54 @@ _curses_window_inch_impl(PyCursesWindowObject *self, int group_right_1,
return rtn;
}

#ifdef HAVE_NCURSESW
/*[clinic input]
_curses.window.in_wch

[
y: int
Starting Y-coordinate.
x: int
Starting X-coordinate.
]
Comment on lines +1744 to +1748

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.

Suggested change
y: int
Starting Y-coordinate.
x: int
Starting X-coordinate.
]
y: int
Y-coordinate.
x: int
X-coordinate.
]

Let's use the same wording as for inch.

/

Retrieve the wide character at the gven position in the window.

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.

Suggested change
Retrieve the wide character at the gven position in the window.
Retrieve the Unicode character at the given position in the window.

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.

Ah, wait, should we say wide character or Unicode character here (it's a wchar_t but we use it to indicate "Unicode" range so...)


The return value is a 3-tuple of (character, attributes, color_pair).
[clinic start generated code]*/

static PyObject *
_curses_window_in_wch_impl(PyCursesWindowObject *self, int group_right_1,
int y, int x)
/*[clinic end generated code: output=846ca8a82f2ecab4 input=5c20d96b592b0e0b]*/
{
int ret;

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.

Ah maybe I should have said it in the other review, but I think we use rtn in general and not ret.


cchar_t wcval;

wchar_t wstr[CCHARW_MAX + 1];
attr_t attr;
short color_pair;

if (group_right_1) {
ret = mvwin_wch(self->win, y, x, &wcval);
} else {
ret = win_wch(self->win, &wcval);
}
if (PyCursesCheckERR(ret, "in_wch") == NULL) {
return NULL;
}
Comment on lines +1769 to +1776

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.

Suggested change
if (group_right_1) {
ret = mvwin_wch(self->win, y, x, &wcval);
} else {
ret = win_wch(self->win, &wcval);
}
if (PyCursesCheckERR(ret, "in_wch") == NULL) {
return NULL;
}
if (group_right_1) {
ret = mvwin_wch(self->win, y, x, &wcval);
if (ret == ERR) {
PyErr_SetString(PyCursesError, "mvwin_wch() returned ERR");
return NULL;
}
}
else {
ret = win_wch(self->win, &wcval);
if (ret == ERR) {
PyErr_SetString(PyCursesError, "win_wch() returned ERR");
return NULL;
}
}


ret = getcchar(&wcval, wstr, &attr, &color_pair, NULL);
if (PyCursesCheckERR(ret, "getcchar") == NULL) {
return NULL;
}
Comment on lines +1779 to +1781

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.

Suggested change
if (PyCursesCheckERR(ret, "getcchar") == NULL) {
return NULL;
}
if (ret == ERR) {
PyErr_SetString(PyCursesError, "getcchar() returned ERR");
return NULL;
}


return Py_BuildValue("ukh", wstr, attr, color_pair);
}
#endif

/*[-clinic input]
_curses.window.instr

Expand Down Expand Up @@ -2530,6 +2578,7 @@ static PyMethodDef PyCursesWindow_Methods[] = {
{"immedok", (PyCFunction)PyCursesWindow_immedok, METH_VARARGS},
#endif
_CURSES_WINDOW_INCH_METHODDEF
_CURSES_WINDOW_IN_WCH_METHODDEF
_CURSES_WINDOW_INSCH_METHODDEF
{"insdelln", (PyCFunction)PyCursesWindow_winsdelln, METH_VARARGS},
{"insertln", (PyCFunction)PyCursesWindow_winsertln, METH_NOARGS},
Expand Down
55 changes: 54 additions & 1 deletion Modules/clinic/_cursesmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.