diff --git a/Doc/library/curses.ascii.rst b/Doc/library/curses.ascii.rst index f3661d9bc5b..db3c8274069 100644 --- a/Doc/library/curses.ascii.rst +++ b/Doc/library/curses.ascii.rst @@ -115,12 +115,12 @@ C library: .. function:: isblank(c) - Checks for an ASCII whitespace character. + Checks for an ASCII whitespace character; space or horizontal tab. .. function:: iscntrl(c) - Checks for an ASCII control character (in the range 0x00 to 0x1f). + Checks for an ASCII control character (in the range 0x00 to 0x1f or 0x7f). .. function:: isdigit(c) diff --git a/Lib/curses/ascii.py b/Lib/curses/ascii.py index 800fd8b4b71..6a466e00786 100644 --- a/Lib/curses/ascii.py +++ b/Lib/curses/ascii.py @@ -54,13 +54,13 @@ def _ctoi(c): def isalnum(c): return isalpha(c) or isdigit(c) def isalpha(c): return isupper(c) or islower(c) def isascii(c): return _ctoi(c) <= 127 # ? -def isblank(c): return _ctoi(c) in (8,32) -def iscntrl(c): return _ctoi(c) <= 31 +def isblank(c): return _ctoi(c) in (9, 32) +def iscntrl(c): return _ctoi(c) <= 31 or _ctoi(c) == 127 def isdigit(c): return _ctoi(c) >= 48 and _ctoi(c) <= 57 def isgraph(c): return _ctoi(c) >= 33 and _ctoi(c) <= 126 def islower(c): return _ctoi(c) >= 97 and _ctoi(c) <= 122 def isprint(c): return _ctoi(c) >= 32 and _ctoi(c) <= 126 -def ispunct(c): return _ctoi(c) != 32 and not isalnum(c) +def ispunct(c): return isgraph(c) and not isalnum(c) def isspace(c): return _ctoi(c) in (9, 10, 11, 12, 13, 32) def isupper(c): return _ctoi(c) >= 65 and _ctoi(c) <= 90 def isxdigit(c): return isdigit(c) or \ diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index 8411cdb0f25..897d738f57d 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -10,6 +10,7 @@ # import os +import string import sys import tempfile import unittest @@ -399,6 +400,55 @@ class MiscTests(unittest.TestCase): class TestAscii(unittest.TestCase): + def test_controlnames(self): + for name in curses.ascii.controlnames: + self.assertTrue(hasattr(curses.ascii, name), name) + + def test_ctypes(self): + def check(func, expected): + with self.subTest(ch=c, func=func): + self.assertEqual(func(i), expected) + self.assertEqual(func(c), expected) + + for i in range(256): + c = chr(i) + b = bytes([i]) + check(curses.ascii.isalnum, b.isalnum()) + check(curses.ascii.isalpha, b.isalpha()) + check(curses.ascii.isdigit, b.isdigit()) + check(curses.ascii.islower, b.islower()) + check(curses.ascii.isspace, b.isspace()) + check(curses.ascii.isupper, b.isupper()) + + check(curses.ascii.isascii, i < 128) + check(curses.ascii.ismeta, i >= 128) + check(curses.ascii.isctrl, i < 32) + check(curses.ascii.iscntrl, i < 32 or i == 127) + check(curses.ascii.isblank, c in ' \t') + check(curses.ascii.isgraph, 32 < i <= 126) + check(curses.ascii.isprint, 32 <= i <= 126) + check(curses.ascii.ispunct, c in string.punctuation) + check(curses.ascii.isxdigit, c in string.hexdigits) + + def test_ascii(self): + ascii = curses.ascii.ascii + self.assertEqual(ascii('\xc1'), 'A') + self.assertEqual(ascii('A'), 'A') + self.assertEqual(ascii(ord('\xc1')), ord('A')) + + def test_ctrl(self): + ctrl = curses.ascii.ctrl + self.assertEqual(ctrl('J'), '\n') + self.assertEqual(ctrl('\n'), '\n') + self.assertEqual(ctrl('@'), '\0') + self.assertEqual(ctrl(ord('J')), ord('\n')) + + def test_alt(self): + alt = curses.ascii.alt + self.assertEqual(alt('\n'), '\x8a') + self.assertEqual(alt('A'), '\xc1') + self.assertEqual(alt(ord('A')), 0xc1) + def test_unctrl(self): unctrl = curses.ascii.unctrl self.assertEqual(unctrl('a'), 'a') @@ -408,9 +458,13 @@ class TestAscii(unittest.TestCase): self.assertEqual(unctrl('\x7f'), '^?') self.assertEqual(unctrl('\n'), '^J') self.assertEqual(unctrl('\0'), '^@') + self.assertEqual(unctrl(ord('A')), 'A') + self.assertEqual(unctrl(ord('\n')), '^J') # Meta-bit characters self.assertEqual(unctrl('\x8a'), '!^J') self.assertEqual(unctrl('\xc1'), '!A') + self.assertEqual(unctrl(ord('\x8a')), '!^J') + self.assertEqual(unctrl(ord('\xc1')), '!A') if __name__ == '__main__': diff --git a/Misc/NEWS b/Misc/NEWS index 3c6d3aed716..49304ba38e3 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,8 @@ Core and Builtins Library ------- +- Issue #27079: Fixed curses.ascii functions isblank(), iscntrl() and ispunct(). + - Issue #26754: Some functions (compile() etc) accepted a filename argument encoded as an iterable of integers. Now only strings and byte-like objects are accepted.