From cd19748b50127caaca24cc856054853993b10cf6 Mon Sep 17 00:00:00 2001 From: Klaas van Schelven Date: Sat, 8 Jan 2022 11:30:29 +0100 Subject: [PATCH 01/22] Merge pull request #69 from vanschelven/master --- readchar/key.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readchar/key.py b/readchar/key.py index 865427f..378c707 100644 --- a/readchar/key.py +++ b/readchar/key.py @@ -1,6 +1,6 @@ # common -LF = "\x0d" -CR = "\x0a" +LF = "\x0a" +CR = "\x0d" ENTER = "\x0d" BACKSPACE = "\x08" SUPR = "" From 47f145a2d683162c5f81b8775fb449f25ca8ebb3 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Sun, 6 Feb 2022 22:58:40 +0100 Subject: [PATCH 02/22] general cleanup of config and settings files --- .coveragerc | 2 -- .flake8 | 2 -- .pre-commit-config.yaml | 7 ++----- Makefile | 8 ++++---- requirements-test.txt | 1 - setup.cfg | 15 +++++++++++---- test.py | 39 --------------------------------------- 7 files changed, 17 insertions(+), 57 deletions(-) delete mode 100644 .coveragerc delete mode 100644 .flake8 delete mode 100644 test.py diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index e7d592e..0000000 --- a/.coveragerc +++ /dev/null @@ -1,2 +0,0 @@ -[run] -omit = */tests* diff --git a/.flake8 b/.flake8 deleted file mode 100644 index c057b0f..0000000 --- a/.flake8 +++ /dev/null @@ -1,2 +0,0 @@ -[flake8] -max-line-length=119 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 45f221f..5193a08 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,12 @@ --- repos: + - repo: https://github.com/adrienverge/yamllint rev: v1.26.3 hooks: - name: check YAML format id: yamllint + - repo: https://github.com/psf/black rev: 22.1.0 hooks: @@ -23,8 +25,3 @@ repos: hooks: - name: check-format with flake8 id: flake8 - args: - - --show-source - - --statistics - - --count - - --max-complexity=12 diff --git a/Makefile b/Makefile index f5ea67e..482d2ad 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ -all: precommit test +all: test precommit test: - python setup.py test + @pytest -precommit:: - pre-commit run -a +precommit: + @pre-commit run -a publish: @python setup.py bdist_wheel upload diff --git a/requirements-test.txt b/requirements-test.txt index 462fa01..f6320d3 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,4 +1,3 @@ flake8 pytest pytest-cov - diff --git a/setup.cfg b/setup.cfg index c59270b..cea3297 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,14 @@ [tool:pytest] -norecursedirs = .git venv build dist *egg -addopts = -rfEsxwX --cov readchar +testpaths = tests +addopts = -r fEsxwX -s --cov=readchar -[flake8] -exclude = **/.*,venv/**,.eggs/** +[coverage:run] +omit = */tests* +[flake8] +exclude = __pycache__,.eggs,.git,.pytest_cache,venv,*.egg +show-source = true +statistics = true +count = true +max-complexity = 12 +max-line-length = 88 diff --git a/test.py b/test.py deleted file mode 100644 index d78b18f..0000000 --- a/test.py +++ /dev/null @@ -1,39 +0,0 @@ -import readchar.key -import readchar.readchar - -decode_dict = { - readchar.key.ESC: "ESC", - readchar.key.UP: "UP", - readchar.key.DOWN: "DOWN", - readchar.key.LEFT: "LEFT", - readchar.key.RIGHT: "RIGHT", - readchar.key.PAGE_UP: "PAGE_UP", - readchar.key.PAGE_DOWN: "PAGE_DOWN", - readchar.key.HOME: "HOME", - readchar.key.END: "END", - readchar.key.INSERT: "INSERT", - readchar.key.SUPR: "DELETE", - readchar.key.F1: "F1", - readchar.key.F2: "F2", - readchar.key.F3: "F3", - readchar.key.F4: "F4", - readchar.key.F5: "F5", - readchar.key.F6: "F6", - readchar.key.F7: "F7", - readchar.key.F8: "F8", - readchar.key.F9: "F9", - readchar.key.F10: "F10", - readchar.key.F12: "F12", - readchar.key.ALT_A: "ALT_A", -} - -while True: - c = readchar.readkey() - - if c in decode_dict: - print("got {}".format(decode_dict[c])) - else: - print(c) - - if c == "d": - break From 28ab4d634511cb27880474478815ce70f89b5d24 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Wed, 26 Jan 2022 22:43:01 +0100 Subject: [PATCH 03/22] seperate platforms and other improvments --- readchar/__init__.py | 14 ++++- readchar/{key.py => key_linux.py} | 3 + readchar/key_windows.py | 50 ++++++++++++++++ readchar/read_linux.py | 52 +++++++++++++++++ readchar/read_windows.py | 37 ++++++++++++ readchar/readchar.py | 96 ------------------------------- readchar/readchar_linux.py | 18 ------ readchar/readchar_windows.py | 27 --------- 8 files changed, 154 insertions(+), 143 deletions(-) rename readchar/{key.py => key_linux.py} (98%) create mode 100644 readchar/key_windows.py create mode 100644 readchar/read_linux.py create mode 100644 readchar/read_windows.py delete mode 100644 readchar/readchar.py delete mode 100644 readchar/readchar_linux.py delete mode 100644 readchar/readchar_windows.py diff --git a/readchar/__init__.py b/readchar/__init__.py index f4ffd5c..bf1335c 100644 --- a/readchar/__init__.py +++ b/readchar/__init__.py @@ -1,4 +1,14 @@ -from . import key -from .readchar import readchar, readkey +import sys + + +if sys.platform.startswith("linux"): + from .read_linux import readchar, readkey + from . import key_linux as key +elif sys.platform in ("win32", "cygwin"): + from .read_windows import readchar, readkey + from . import key_windows as key +else: + raise NotImplementedError("The platform %s is not supported yet" % sys.platform) + __all__ = [readchar, readkey, key] diff --git a/readchar/key.py b/readchar/key_linux.py similarity index 98% rename from readchar/key.py rename to readchar/key_linux.py index 378c707..e07fbd4 100644 --- a/readchar/key.py +++ b/readchar/key_linux.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + # common LF = "\x0a" CR = "\x0d" @@ -6,6 +8,7 @@ SUPR = "" SPACE = "\x20" ESC = "\x1b" +TAB = "\x09" # CTRL CTRL_A = "\x01" diff --git a/readchar/key_windows.py b/readchar/key_windows.py new file mode 100644 index 0000000..533631b --- /dev/null +++ b/readchar/key_windows.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +# common +LF = "\x0a" +CR = "\x0d" +ENTER = "\x0d" +BACKSPACE = "\x08" +SPACE = "\x20" +ESC = "\x1b" +TAB = "\x09" + + +# Windows uses scan codes for extended characters. This dictionary +# translates the second half of the scan codes of special Keys +# into the corresponding variable used by readchar. +# +# for windows scan codes see: +# https://msdn.microsoft.com/en-us/library/aa299374 +# or +# https://www.freepascal.org/docs-html/rtl/keyboard/kbdscancode.html + +ESC_2 = "\x00\x01" +ENTER_2 = "\x00\x1c" +F1 = "\x00\x3b" +F2 = "\x00\x3c" +F3 = "\x00\x3d" +F4 = "\x00\x3e" +F5 = "\x00\x3f" +F6 = "\x00\x40" +F7 = "\x00\x41" +F8 = "\x00\x42" +F9 = "\x00\x43" +F10 = "\x00\x44" +F11 = "\x00\x85" # only in second source +F12 = "\x00\x86" # only in second source +# don't have table entries for... +# ALT_[A-Z] +# CTRL_ALT_A, # Ctrl-Alt-A, etc. +# CTRL_ALT_SUPR, +# CTRL-F1 +INSERT = "\x00\x52" +SUPR = "\x00\x53" # key.py uses SUPR not DELETE +PAGE_UP = "\x00\x49" +PAGE_DOWN = "\x00\x51" +HOME = "\x00\x47" +END = "\x00\x4f" +UP = "\x00\x48" +DOWN = "\x00\x50" +LEFT = "\x00\x4b" +RIGHT = "\x00\x4d" diff --git a/readchar/read_linux.py b/readchar/read_linux.py new file mode 100644 index 0000000..cb7977a --- /dev/null +++ b/readchar/read_linux.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +import sys +import termios +import tty +import select + + +# idea from: +# https://repolinux.wordpress.com/2012/10/09/non-blocking-read-from-stdin-in-python/ +# Thanks to REPOLINUX +def kbhit(): + return sys.stdin in select.select([sys.stdin], [], [], 0)[0] + + +# Initially taken from: +# http://code.activestate.com/recipes/134892/ +# Thanks to Danny Yoo +def readchar(blocking=False): + if not blocking and not kbhit(): + return None + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + +def readkey(): + """Get a single character on Linux. If an escaped key is pressed, the + following characters are read as well (see key_linux.py).""" + + c1 = readchar(blocking=True) + + if c1 == "\x03": + raise KeyboardInterrupt + + if c1 != "\x1B": + return c1 + + c2 = readchar(blocking=True) + if c2 != "\x5B": + return c1 + c2 + + c3 = readchar(blocking=True) + if c3 != "\x33": + return c1 + c2 + c3 + + c4 = readchar(blocking=True) + return c1 + c2 + c3 + c4 diff --git a/readchar/read_windows.py b/readchar/read_windows.py new file mode 100644 index 0000000..b9aa6b1 --- /dev/null +++ b/readchar/read_windows.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# This file is based on this gist: +# http://code.activestate.com/recipes/134892/ +# So real authors are DannyYoo and company. + +import msvcrt + + +def readchar(blocking=False): + """reads a single character from the input stream. Retruns None if none is avalable. + If blocking=True the function waits for the next character.""" + + if blocking or msvcrt.kbhit(): + # manual byte decoding because some bytes in windows are not utf-8 encodable. + return chr(int.from_bytes(msvcrt.getch(), "big")) + else: + return None + + +def readkey(): + """Get a single character on Windows. If an extended key is pressed, the + Windows scan code is translated into a the unicode sequences readchar + expects (see key_windows.py).""" + + ch = readchar(blocking=True) + + if ch == "\x03": + raise KeyboardInterrupt + + # if it is a normal character: + if ch not in "\x00\xe0": + return ch + + # if it is a scpeal key, read second half: + ch2 = readchar() + + return "\x00" + ch2 diff --git a/readchar/readchar.py b/readchar/readchar.py deleted file mode 100644 index 8aa352a..0000000 --- a/readchar/readchar.py +++ /dev/null @@ -1,96 +0,0 @@ -# -*- coding: utf-8 -*- -# This file is based on this gist: -# http://code.activestate.com/recipes/134892/ -# So real authors are DannyYoo and company. -import sys - -if sys.platform.startswith("linux"): - from .readchar_linux import readchar -elif sys.platform == "darwin": - from .readchar_linux import readchar -elif sys.platform in ("win32", "cygwin"): - import msvcrt - - from . import key - from .readchar_windows import readchar -else: - raise NotImplementedError("The platform %s is not supported yet" % sys.platform) - - -if sys.platform in ("win32", "cygwin"): - # - # Windows uses scan codes for extended characters. The ordinal returned is - # 256 * the scan code. This dictionary translates scan codes to the - # unicode sequences expected by readkey. - # - # for windows scan codes see: - # https://msdn.microsoft.com/en-us/library/aa299374 - # or - # http://www.quadibloc.com/comp/scan.htm - xlate_dict = { - 13: key.ENTER, - 27: key.ESC, - 15104: key.F1, - 15360: key.F2, - 15616: key.F3, - 15872: key.F4, - 16128: key.F5, - 16384: key.F6, - 16640: key.F7, - 16896: key.F8, - 17152: key.F9, - 17408: key.F10, - 22272: key.F11, - 34528: key.F12, - 7680: key.ALT_A, - # don't have table entries for... - # CTRL_ALT_A, # Ctrl-Alt-A, etc. - # CTRL_ALT_SUPR, - # CTRL-F1 - 21216: key.INSERT, - 21472: key.SUPR, # key.py uses SUPR, not DELETE - 18912: key.PAGE_UP, - 20960: key.PAGE_DOWN, - 18400: key.HOME, - 20448: key.END, - 18432: key.UP, # 72 * 256 - 20480: key.DOWN, # 80 * 256 - 19200: key.LEFT, # 75 * 256 - 19712: key.RIGHT, # 77 * 256 - } - - def readkey(getchar_fn=None): - # Get a single character on Windows. if an extended key is pressed, the - # Windows scan code is translated into a the unicode sequences readchar - # expects (see key.py). - while True: - if msvcrt.kbhit(): - ch = msvcrt.getch() - a = ord(ch) - if a == 0 or a == 224: - b = ord(msvcrt.getch()) - x = a + (b * 256) - - try: - return xlate_dict[x] - except KeyError: - return None - return x - else: - return ch.decode() - -else: - - def readkey(getchar_fn=None): - getchar = getchar_fn or readchar - c1 = getchar() - if ord(c1) != 0x1B: - return c1 - c2 = getchar() - if ord(c2) != 0x5B: - return c1 + c2 - c3 = getchar() - if ord(c3) != 0x33: - return c1 + c2 + c3 - c4 = getchar() - return c1 + c2 + c3 + c4 diff --git a/readchar/readchar_linux.py b/readchar/readchar_linux.py deleted file mode 100644 index 6bcb4e2..0000000 --- a/readchar/readchar_linux.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -# Initially taken from: -# http://code.activestate.com/recipes/134892/ -# Thanks to Danny Yoo -import sys -import termios -import tty - - -def readchar(): - fd = sys.stdin.fileno() - old_settings = termios.tcgetattr(fd) - try: - tty.setraw(sys.stdin.fileno()) - ch = sys.stdin.read(1) - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - return ch diff --git a/readchar/readchar_windows.py b/readchar/readchar_windows.py deleted file mode 100644 index 3afb4f8..0000000 --- a/readchar/readchar_windows.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -# Initially taken from: -# http://code.activestate.com/recipes/134892/#c9 -# Thanks to Stephen Chappell -import msvcrt -import sys - -win_encoding = "mbcs" - - -XE0_OR_00 = "\x00\xe0" - - -def readchar(blocking=False): - "Get a single character on Windows." - - while msvcrt.kbhit(): - msvcrt.getch() - ch = msvcrt.getch() - # print('ch={}, type(ch)={}'.format(ch, type(ch))) - # while ch.decode(win_encoding) in unicode('\x00\xe0', win_encoding): - while ch.decode(win_encoding) in XE0_OR_00: - # print('found x00 or xe0') - msvcrt.getch() - ch = msvcrt.getch() - - return ch if sys.version_info.major > 2 else ch.decode(encoding=win_encoding) From 1f251d6d47c7b1ad72add0580f5372bc8b782753 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Wed, 26 Jan 2022 22:50:49 +0100 Subject: [PATCH 04/22] pytest setup for test on multiple platforms and windows tests --- tests/__init__.py | 0 tests/acceptance/__init__.py | 0 tests/linux/conftest.py | 34 ++++++++++++++++++ tests/linux/test_keys.py | 17 +++++++++ tests/linux/test_readchar.py | 28 +++++++++++++++ tests/linux/test_readkey.py | 64 +++++++++++++++++++++++++++++++++ tests/unit/__init__.py | 0 tests/unit/test_key.py | 17 --------- tests/unit/test_readkey.py | 54 ---------------------------- tests/windows/conftest.py | 22 ++++++++++++ tests/windows/test_keys.py | 51 ++++++++++++++++++++++++++ tests/windows/test_readchar.py | 26 ++++++++++++++ tests/windows/test_readkey.py | 66 ++++++++++++++++++++++++++++++++++ 13 files changed, 308 insertions(+), 71 deletions(-) delete mode 100644 tests/__init__.py delete mode 100644 tests/acceptance/__init__.py create mode 100644 tests/linux/conftest.py create mode 100644 tests/linux/test_keys.py create mode 100644 tests/linux/test_readchar.py create mode 100644 tests/linux/test_readkey.py delete mode 100644 tests/unit/__init__.py delete mode 100644 tests/unit/test_key.py delete mode 100644 tests/unit/test_readkey.py create mode 100644 tests/windows/conftest.py create mode 100644 tests/windows/test_keys.py create mode 100644 tests/windows/test_readchar.py create mode 100644 tests/windows/test_readkey.py diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/acceptance/__init__.py b/tests/acceptance/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/linux/conftest.py b/tests/linux/conftest.py new file mode 100644 index 0000000..8f4c2ca --- /dev/null +++ b/tests/linux/conftest.py @@ -0,0 +1,34 @@ +import pytest +import sys +import select + + +# ignore all tests in this folder if not on linux +def pytest_ignore_collect(path, config): + if not sys.platform.startswith("linux"): + return True + + +@pytest.fixture +def patched_stdin(): + class mocked_stdin: + buffer = [] + + def push(self, string): + for c in string: + self.buffer.append(c) + + def read(self, n): + string = "" + for i in range(n): + string += self.buffer.pop(0) + return string + + def mock_select(a, b, c, d): + return [(sys.stdin)] + + mock = mocked_stdin() + with pytest.MonkeyPatch.context() as mp: + mp.setattr(sys.stdin, "read", mock.read) + mp.setattr(select, "select", mock_select) + yield mock diff --git a/tests/linux/test_keys.py b/tests/linux/test_keys.py new file mode 100644 index 0000000..dd3e212 --- /dev/null +++ b/tests/linux/test_keys.py @@ -0,0 +1,17 @@ +from readchar import key + + +def test_character_length_1(): + assert 1 == len(key.CTRL_A) + + +def test_character_length_2(): + assert 2 == len(key.ALT_A) + + +def test_character_length_3(): + assert 3 == len(key.UP) + + +def test_character_length_4(): + assert 4 == len(key.CTRL_ALT_SUPR) diff --git a/tests/linux/test_readchar.py b/tests/linux/test_readchar.py new file mode 100644 index 0000000..5c11345 --- /dev/null +++ b/tests/linux/test_readchar.py @@ -0,0 +1,28 @@ +import pytest +from string import printable +from readchar import readchar, key + + +@pytest.mark.skip(reason="These work localy, but not on GitHub...") +@pytest.mark.parametrize("c", printable) +def test_printableCharacters(patched_stdin, c): + patched_stdin.push(c) + assert c == readchar(blocking=True) + + +@pytest.mark.skip(reason="I have no idea why these dont work...") +@pytest.mark.parametrize( + ["seq", "key"], + [ + ("\x0a", key.LF), + ("\x0a", key.ENTER), + ("\x0d", key.CR), + ("\x08", key.BACKSPACE), + ("\x20", key.SPACE), + ("\x1b", key.ESC), + ("\x09", key.TAB), + ], +) +def test_controlCharacters(seq, key, patched_stdin): + patched_stdin.push(seq) + assert key == readchar() diff --git a/tests/linux/test_readkey.py b/tests/linux/test_readkey.py new file mode 100644 index 0000000..734dae2 --- /dev/null +++ b/tests/linux/test_readkey.py @@ -0,0 +1,64 @@ +import pytest +from readchar import readkey, key + + +@pytest.mark.skip(reason="These work localy, but not on GitHub...") +def test_KeyboardInterrupt(patched_stdin): + patched_stdin.push("\x03") + with pytest.raises(KeyboardInterrupt): + readkey() + + +@pytest.mark.skip(reason="I dont know enough about linux to make these work...") +@pytest.mark.parametrize( + ["seq", "key"], + [ + ("\x1b\x5b\x41", key.UP), + ("\x1b\x5b\x42", key.DOWN), + ("\x1b\x5b\x44", key.LEFT), + ("\x1b\x5b\x43", key.RIGHT), + ], +) +def test_arrowKeys(seq, key, patched_stdin): + patched_stdin.push(seq) + assert key == readkey() + + +@pytest.mark.skip(reason="I dont know enough about linux to make these work...") +@pytest.mark.parametrize( + ["seq", "key"], + [ + ("\x1b\x5b\x32\x7e", key.INSERT), + ("\x1b\x5b\x33\x7e", key.SUPR), + ("\x1b\x5b\x48", key.HOME), + ("\x1b\x5b\x46", key.END), + ("\x1b\x5b\x35\x7e", key.PAGE_UP), + ("\x1b\x5b\x36\x7e", key.PAGE_DOWN), + ], +) +def test_specialKeys(seq, key, patched_stdin): + patched_stdin.push(seq) + assert key == readkey() + + +@pytest.mark.skip(reason="I dont know enough about linux to make these work...") +@pytest.mark.parametrize( + ["seq", "key"], + [ + (key.F1, "\x1b\x4f\x50"), + (key.F2, "\x1b\x4f\x51"), + (key.F3, "\x1b\x4f\x52"), + (key.F4, "\x1b\x4f\x53"), + (key.F5, "\x1b\x4f\x31\x35\x7e"), + (key.F6, "\x1b\x4f\x31\x37\x7e"), + (key.F7, "\x1b\x4f\x31\x38\x7e"), + (key.F8, "\x1b\x4f\x31\x39\x7e"), + (key.F9, "\x1b\x4f\x32\x30\x7e"), + (key.F10, "\x1b\x4f\x32\x31\x7e"), + (key.F11, "\x1b\x4f\x32\x33\x7e"), + (key.F12, "\x1b\x4f\x32\x34\x7e"), + ], +) +def test_functionKeys(seq, key, patched_stdin): + patched_stdin.push(seq) + assert key == readkey() diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/unit/test_key.py b/tests/unit/test_key.py deleted file mode 100644 index ef605f3..0000000 --- a/tests/unit/test_key.py +++ /dev/null @@ -1,17 +0,0 @@ -import unittest - -from readchar import key - - -class KeyTest(unittest.TestCase): - def test_character_length_1(self): - self.assertEqual(1, len(key.CTRL_A)) - - def test_character_length_2(self): - self.assertEqual(2, len(key.ALT_A)) - - def test_character_length_3(self): - self.assertEqual(3, len(key.UP)) - - def test_character_length_4(self): - self.assertEqual(4, len(key.CTRL_ALT_SUPR)) diff --git a/tests/unit/test_readkey.py b/tests/unit/test_readkey.py deleted file mode 100644 index 5f52727..0000000 --- a/tests/unit/test_readkey.py +++ /dev/null @@ -1,54 +0,0 @@ -import unittest - -from readchar import readkey - - -def readchar_fn_factory(stream): - - v = [x for x in stream] - - def inner(): - return v.pop(0) - - return inner - - -class ReadKeyTest(unittest.TestCase): - def test_basic_character(self): - getchar_fn = readchar_fn_factory("a") - - result = readkey(getchar_fn) - - self.assertEqual("a", result) - - def test_string_instead_of_char(self): - char = "a" - getchar_fn = readchar_fn_factory(char + "bcde") - - result = readkey(getchar_fn) - - self.assertEqual(char, result) - - def test_special_combo_character(self): - char = "\x1b\x01" - getchar_fn = readchar_fn_factory(char + "foo") - - result = readkey(getchar_fn) - - self.assertEqual(char, result) - - def test_special_key(self): - char = "\x1b\x5b\x41" - getchar_fn = readchar_fn_factory(char + "foo") - - result = readkey(getchar_fn) - - self.assertEqual(char, result) - - def test_special_key_combo(self): - char = "\x1b\x5b\x33\x5e" - getchar_fn = readchar_fn_factory(char + "foo") - - result = readkey(getchar_fn) - - self.assertEqual(char, result) diff --git a/tests/windows/conftest.py b/tests/windows/conftest.py new file mode 100644 index 0000000..94299d4 --- /dev/null +++ b/tests/windows/conftest.py @@ -0,0 +1,22 @@ +import pytest +import sys + + +if sys.platform in ("win32", "cygwin"): + import msvcrt + + +# ignore all tests in this folder if not on windows +def pytest_ignore_collect(path, config): + if sys.platform not in ("win32", "cygwin"): + return True + + +@pytest.fixture +def patched_stdin(): + class mocked_stdin: + def push(self, string): + for c in string: + msvcrt.ungetch(ord(c).to_bytes(1, "big")) + + return mocked_stdin() diff --git a/tests/windows/test_keys.py b/tests/windows/test_keys.py new file mode 100644 index 0000000..55a8d4a --- /dev/null +++ b/tests/windows/test_keys.py @@ -0,0 +1,51 @@ +import pytest +from readchar import key as keys + + +defaultKeys = ["LF", "CR", "ENTER", "BACKSPACE", "SPACE", "ESC", "TAB"] + + +@pytest.mark.parametrize("key", defaultKeys) +def test_defaultKeysExists(key): + assert key in keys.__dict__ + + +@pytest.mark.parametrize("key", defaultKeys) +def test_defaultKeysLength(key): + assert 1 == len(keys.__dict__[key]) + + +specialKeys = [ + "INSERT", + "SUPR", + "PAGE_UP", + "PAGE_DOWN", + "HOME", + "END", + "UP", + "DOWN", + "LEFT", + "RIGHT", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", +] + + +@pytest.mark.parametrize("key", specialKeys) +def test_specialKeysExists(key): + assert key in keys.__dict__ + + +@pytest.mark.parametrize("key", specialKeys) +def test_specialKeysLength(key): + assert 2 == len(keys.__dict__[key]) diff --git a/tests/windows/test_readchar.py b/tests/windows/test_readchar.py new file mode 100644 index 0000000..ba328a9 --- /dev/null +++ b/tests/windows/test_readchar.py @@ -0,0 +1,26 @@ +import pytest +from string import printable +from readchar import readchar, key + + +@pytest.mark.parametrize("c", printable) +def test_printableCharacters(patched_stdin, c): + patched_stdin.push(c) + assert c == readchar() + + +@pytest.mark.parametrize( + ["seq", "key"], + [ + ("\n", key.LF), + ("\n", key.ENTER), + ("\r", key.CR), + ("\x08", key.BACKSPACE), + ("\x20", key.SPACE), + ("\x1b", key.ESC), + ("\t", key.TAB), + ], +) +def test_controlCharacters(seq, key, patched_stdin): + patched_stdin.push(seq) + assert key == readchar() diff --git a/tests/windows/test_readkey.py b/tests/windows/test_readkey.py new file mode 100644 index 0000000..c65b9e7 --- /dev/null +++ b/tests/windows/test_readkey.py @@ -0,0 +1,66 @@ +import pytest +from readchar import readkey, key + + +def test_KeyboardInterrupt(patched_stdin): + patched_stdin.push("\x03") + with pytest.raises(KeyboardInterrupt): + readkey() + + +# for windows scan codes see: +# https://msdn.microsoft.com/en-us/library/aa299374 +# or +# https://www.freepascal.org/docs-html/rtl/keyboard/kbdscancode.html + + +@pytest.mark.parametrize( + ["seq", "key"], + [ + ("\x00\x48", key.UP), + ("\x00\x50", key.DOWN), + ("\x00\x4b", key.LEFT), + ("\x00\x4d", key.RIGHT), + ], +) +def test_arrowKeys(seq, key, patched_stdin): + patched_stdin.push(seq) + assert key == readkey() + + +@pytest.mark.parametrize( + ["seq", "key"], + [ + ("\x00\x52", key.INSERT), + ("\x00\x53", key.SUPR), + ("\x00\x47", key.HOME), + ("\x00\x4f", key.END), + ("\x00\x49", key.PAGE_UP), + ("\x00\x51", key.PAGE_DOWN), + ], +) +def test_specialKeys(seq, key, patched_stdin): + patched_stdin.push(seq) + assert key == readkey() + + +@pytest.mark.parametrize( + ["seq", "key"], + [ + ("\x00\x3b", key.F1), + ("\x00\x3c", key.F2), + ("\x00\x3d", key.F3), + ("\x00\x3e", key.F4), + ("\x00\x3f", key.F5), + ("\x00\x40", key.F6), + ("\x00\x41", key.F7), + ("\x00\x42", key.F8), + ("\x00\x43", key.F9), + ("\x00\x44", key.F10), + ("\x00\x85", key.F11), + ("\x00\x86", key.F12), + ], +) +def test_functionKeys(seq, key, patched_stdin): + patched_stdin.push(seq) + assert key == readkey() From f399db5119b1ed39a86e0b3463a6adf1c022a82a Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Sun, 6 Feb 2022 22:13:15 +0100 Subject: [PATCH 05/22] windows actually uses LF for ENTER --- readchar/key_linux.py | 2 +- readchar/key_windows.py | 2 +- tests/windows/test_readchar.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/readchar/key_linux.py b/readchar/key_linux.py index e07fbd4..c818a00 100644 --- a/readchar/key_linux.py +++ b/readchar/key_linux.py @@ -3,7 +3,7 @@ # common LF = "\x0a" CR = "\x0d" -ENTER = "\x0d" +ENTER = LF BACKSPACE = "\x08" SUPR = "" SPACE = "\x20" diff --git a/readchar/key_windows.py b/readchar/key_windows.py index 533631b..a9a921d 100644 --- a/readchar/key_windows.py +++ b/readchar/key_windows.py @@ -3,7 +3,7 @@ # common LF = "\x0a" CR = "\x0d" -ENTER = "\x0d" +ENTER = CR BACKSPACE = "\x08" SPACE = "\x20" ESC = "\x1b" diff --git a/tests/windows/test_readchar.py b/tests/windows/test_readchar.py index ba328a9..184c91f 100644 --- a/tests/windows/test_readchar.py +++ b/tests/windows/test_readchar.py @@ -13,7 +13,7 @@ def test_printableCharacters(patched_stdin, c): ["seq", "key"], [ ("\n", key.LF), - ("\n", key.ENTER), + ("\r", key.ENTER), ("\r", key.CR), ("\x08", key.BACKSPACE), ("\x20", key.SPACE), From 493da6558abb5ba5fb00972acb0656543fb43545 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Sun, 6 Feb 2022 22:14:50 +0100 Subject: [PATCH 06/22] github workflow for both platforms --- .github/workflows/run-tests.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 9f6ea51..dec4ddc 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -4,7 +4,7 @@ # For more information see: # https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions -name: Python package +name: Pytests on: push: @@ -17,9 +17,12 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: matrix: + os: + - ubuntu-latest + - windows-latest python-version: - "3.5" - "3.6" @@ -38,6 +41,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install -r requirements-test.txt + python -m pip install -e . - name: Test with pytest run: | pytest From 2e973e0116c22463a59157101fa5e227cd106921 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Fri, 29 Apr 2022 18:10:03 +0200 Subject: [PATCH 07/22] bump precommit-hook versions --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5193a08..dc1f5a0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,7 @@ repos: id: yamllint - repo: https://github.com/psf/black - rev: 22.1.0 + rev: 22.3.0 hooks: - name: re-format with black id: black @@ -21,7 +21,7 @@ repos: id: trailing-whitespace - repo: https://gitlab.com/pycqa/flake8 - rev: 21d3c70d676007470908d39b73f0521d39b3b997 + rev: 4.0.1 hooks: - name: check-format with flake8 id: flake8 From bc94d3067a3f752d1af03986716a7f3ab85bab1a Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Fri, 29 Apr 2022 22:19:34 +0200 Subject: [PATCH 08/22] removed python 3.5 and pypy support --- .github/workflows/run-tests.yml | 1 - README.rst | 2 +- setup.py | 6 +----- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index dec4ddc..976f18e 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -24,7 +24,6 @@ jobs: - ubuntu-latest - windows-latest python-version: - - "3.5" - "3.6" - "3.7" - "3.8" diff --git a/README.rst b/README.rst index 611cdac..de8db25 100644 --- a/README.rst +++ b/README.rst @@ -29,7 +29,7 @@ Installation pip install readchar -The :code:`readchar` library works with python 2.7, 3.4, 3.5, 3.6 and Pypy. +The :code:`readchar` library works with python 3.6, 3.7, 3.8 and 3.10. Usage ----- diff --git a/setup.py b/setup.py index c5b46b4..2ca40eb 100644 --- a/setup.py +++ b/setup.py @@ -53,15 +53,11 @@ def run_tests(self): "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", + "Programming Language :: Python :: 3.10", "Topic :: Software Development", "Topic :: Software Development :: User Interfaces", ], From 1cb646220da39cc695a4817e6178a41fe65c0188 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Fri, 29 Apr 2022 21:58:01 +0200 Subject: [PATCH 09/22] simplyfied .yamellinter.yaml --- .yamllint.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.yamllint.yaml b/.yamllint.yaml index e9147c4..da62e9a 100644 --- a/.yamllint.yaml +++ b/.yamllint.yaml @@ -1,8 +1,9 @@ extends: default +ignore: | + .github/workflows/* + venv/* + rules: - truthy: - ignore: | - .github/workflows/* indentation: spaces: 2 From 77e3d11dd8391719d02df325d251a995ea042403 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Fri, 29 Apr 2022 21:58:25 +0200 Subject: [PATCH 10/22] add end-of-line-fixer to pre-commit --- .pre-commit-config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dc1f5a0..74a7905 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,6 +19,8 @@ repos: hooks: - name: remove whitespaces id: trailing-whitespace + - name: add newline to end of files + id: end-of-file-fixer - repo: https://gitlab.com/pycqa/flake8 rev: 4.0.1 From 38e0e8f2a692f37df305a24c16d7c3ea88db088f Mon Sep 17 00:00:00 2001 From: c0d3d3v Date: Fri, 29 Apr 2022 22:53:18 +0200 Subject: [PATCH 11/22] fix linux tests and fix for linux readkey to read all keys --- readchar/read_linux.py | 10 +++++++--- tests/linux/conftest.py | 24 ++++++++++++++++++++---- tests/linux/test_readchar.py | 2 -- tests/linux/test_readkey.py | 4 ---- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/readchar/read_linux.py b/readchar/read_linux.py index cb7977a..8104a40 100644 --- a/readchar/read_linux.py +++ b/readchar/read_linux.py @@ -41,12 +41,16 @@ def readkey(): return c1 c2 = readchar(blocking=True) - if c2 != "\x5B": + if c2 not in "\x4F\x5B": return c1 + c2 c3 = readchar(blocking=True) - if c3 != "\x33": + if c3 not in "\x31\x32\x33\x35\x36": return c1 + c2 + c3 c4 = readchar(blocking=True) - return c1 + c2 + c3 + c4 + if c2 != "\x4F" or c4 not in "\x30\x31\x33\x34\x35\x37\x38\x39": + return c1 + c2 + c3 + c4 + + c5 = readchar(blocking=True) + return c1 + c2 + c3 + c4 + c5 diff --git a/tests/linux/conftest.py b/tests/linux/conftest.py index 8f4c2ca..1547f32 100644 --- a/tests/linux/conftest.py +++ b/tests/linux/conftest.py @@ -1,6 +1,10 @@ import pytest import sys -import select + +if sys.platform.startswith("linux"): + import termios + import tty + import readchar.read_linux as read_linux # ignore all tests in this folder if not on linux @@ -24,11 +28,23 @@ def read(self, n): string += self.buffer.pop(0) return string - def mock_select(a, b, c, d): - return [(sys.stdin)] + def mock_tcgetattr(fd): + return None + + def mock_tcsetattr(fd, TCSADRAIN, old_settings): + return None + + def mock_setraw(fd): + return None + + def mock_kbhit(): + return True mock = mocked_stdin() with pytest.MonkeyPatch.context() as mp: mp.setattr(sys.stdin, "read", mock.read) - mp.setattr(select, "select", mock_select) + mp.setattr(termios, "tcgetattr", mock_tcgetattr) + mp.setattr(termios, "tcsetattr", mock_tcsetattr) + mp.setattr(tty, "setraw", mock_setraw) + mp.setattr(read_linux, "kbhit", mock_kbhit) yield mock diff --git a/tests/linux/test_readchar.py b/tests/linux/test_readchar.py index 5c11345..9149880 100644 --- a/tests/linux/test_readchar.py +++ b/tests/linux/test_readchar.py @@ -3,14 +3,12 @@ from readchar import readchar, key -@pytest.mark.skip(reason="These work localy, but not on GitHub...") @pytest.mark.parametrize("c", printable) def test_printableCharacters(patched_stdin, c): patched_stdin.push(c) assert c == readchar(blocking=True) -@pytest.mark.skip(reason="I have no idea why these dont work...") @pytest.mark.parametrize( ["seq", "key"], [ diff --git a/tests/linux/test_readkey.py b/tests/linux/test_readkey.py index 734dae2..d902579 100644 --- a/tests/linux/test_readkey.py +++ b/tests/linux/test_readkey.py @@ -2,14 +2,12 @@ from readchar import readkey, key -@pytest.mark.skip(reason="These work localy, but not on GitHub...") def test_KeyboardInterrupt(patched_stdin): patched_stdin.push("\x03") with pytest.raises(KeyboardInterrupt): readkey() -@pytest.mark.skip(reason="I dont know enough about linux to make these work...") @pytest.mark.parametrize( ["seq", "key"], [ @@ -24,7 +22,6 @@ def test_arrowKeys(seq, key, patched_stdin): assert key == readkey() -@pytest.mark.skip(reason="I dont know enough about linux to make these work...") @pytest.mark.parametrize( ["seq", "key"], [ @@ -41,7 +38,6 @@ def test_specialKeys(seq, key, patched_stdin): assert key == readkey() -@pytest.mark.skip(reason="I dont know enough about linux to make these work...") @pytest.mark.parametrize( ["seq", "key"], [ From 07cca72dfa72f4955f39db23d88ca8083d815a95 Mon Sep 17 00:00:00 2001 From: c0d3d3v Date: Fri, 29 Apr 2022 22:03:55 +0200 Subject: [PATCH 12/22] fix __all__ --- readchar/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readchar/__init__.py b/readchar/__init__.py index bf1335c..746858b 100644 --- a/readchar/__init__.py +++ b/readchar/__init__.py @@ -11,4 +11,4 @@ raise NotImplementedError("The platform %s is not supported yet" % sys.platform) -__all__ = [readchar, readkey, key] +__all__ = ["readchar", "readkey", "key"] From 47e22bde847cee1433f88feb143bab9a6701e007 Mon Sep 17 00:00:00 2001 From: c0d3d3v Date: Fri, 29 Apr 2022 22:04:14 +0200 Subject: [PATCH 13/22] Fix BACKSPACE Keycode on Linux --- readchar/key_linux.py | 3 +-- tests/linux/test_readchar.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/readchar/key_linux.py b/readchar/key_linux.py index c818a00..a4c1c85 100644 --- a/readchar/key_linux.py +++ b/readchar/key_linux.py @@ -4,8 +4,7 @@ LF = "\x0a" CR = "\x0d" ENTER = LF -BACKSPACE = "\x08" -SUPR = "" +BACKSPACE = "\x7F" SPACE = "\x20" ESC = "\x1b" TAB = "\x09" diff --git a/tests/linux/test_readchar.py b/tests/linux/test_readchar.py index 9149880..75cf5cd 100644 --- a/tests/linux/test_readchar.py +++ b/tests/linux/test_readchar.py @@ -15,7 +15,7 @@ def test_printableCharacters(patched_stdin, c): ("\x0a", key.LF), ("\x0a", key.ENTER), ("\x0d", key.CR), - ("\x08", key.BACKSPACE), + ("\x7F", key.BACKSPACE), ("\x20", key.SPACE), ("\x1b", key.ESC), ("\x09", key.TAB), From 5ff7289da180396b2c9e56d764243b93f1f36c86 Mon Sep 17 00:00:00 2001 From: c0d3d3v Date: Fri, 29 Apr 2022 22:04:26 +0200 Subject: [PATCH 14/22] Reorder Windows Keycodes and add CTRL_A to CTRL_Z to windows Keycodes --- readchar/key_windows.py | 45 +++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/readchar/key_windows.py b/readchar/key_windows.py index a9a921d..3f28cff 100644 --- a/readchar/key_windows.py +++ b/readchar/key_windows.py @@ -9,6 +9,33 @@ ESC = "\x1b" TAB = "\x09" +# CTRL +CTRL_A = "\x01" +CTRL_B = "\x02" +CTRL_C = "\x03" +CTRL_D = "\x04" +CTRL_E = "\x05" +CTRL_F = "\x06" +CTRL_G = "\x07" +CTRL_H = "\x08" +CTRL_I = "\t" +CTRL_J = "\n" +CTRL_K = "\x0b" +CTRL_L = "\x0c" +CTRL_M = "\r" +CTRL_N = "\x0e" +CTRL_O = "\x0f" +CTRL_P = "\x10" +CTRL_Q = "\x11" +CTRL_R = "\x12" +CTRL_S = "\x13" +CTRL_T = "\x14" +CTRL_U = "\x15" +CTRL_V = "\x16" +CTRL_W = "\x17" +CTRL_X = "\x18" +CTRL_Y = "\x19" +CTRL_Z = "\x1a" # Windows uses scan codes for extended characters. This dictionary # translates the second half of the scan codes of special Keys @@ -19,6 +46,13 @@ # or # https://www.freepascal.org/docs-html/rtl/keyboard/kbdscancode.html +# cursors +UP = "\x00\x48" +DOWN = "\x00\x50" +LEFT = "\x00\x4b" +RIGHT = "\x00\x4d" + +# other ESC_2 = "\x00\x01" ENTER_2 = "\x00\x1c" F1 = "\x00\x3b" @@ -33,18 +67,17 @@ F10 = "\x00\x44" F11 = "\x00\x85" # only in second source F12 = "\x00\x86" # only in second source + # don't have table entries for... # ALT_[A-Z] # CTRL_ALT_A, # Ctrl-Alt-A, etc. # CTRL_ALT_SUPR, # CTRL-F1 -INSERT = "\x00\x52" -SUPR = "\x00\x53" # key.py uses SUPR not DELETE + PAGE_UP = "\x00\x49" PAGE_DOWN = "\x00\x51" HOME = "\x00\x47" END = "\x00\x4f" -UP = "\x00\x48" -DOWN = "\x00\x50" -LEFT = "\x00\x4b" -RIGHT = "\x00\x4d" + +INSERT = "\x00\x52" +SUPR = "\x00\x53" From 19a330a126733d1184f2e3c28e90d83a3a9caa58 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Fri, 29 Apr 2022 23:38:22 +0200 Subject: [PATCH 15/22] added DELETE in addition to SUPR --- readchar/key_linux.py | 2 ++ readchar/key_windows.py | 1 + 2 files changed, 3 insertions(+) diff --git a/readchar/key_linux.py b/readchar/key_linux.py index a4c1c85..04228a8 100644 --- a/readchar/key_linux.py +++ b/readchar/key_linux.py @@ -50,6 +50,7 @@ RIGHT = "\x1b\x5b\x43" CTRL_ALT_SUPR = "\x1b\x5b\x33\x5e" +CTRL_ALT_DELETE = CTRL_ALT_SUPR # other F1 = "\x1b\x4f\x50" @@ -72,6 +73,7 @@ INSERT = "\x1b\x5b\x32\x7e" SUPR = "\x1b\x5b\x33\x7e" +DELETE = SUPR ESCAPE_SEQUENCES = ( diff --git a/readchar/key_windows.py b/readchar/key_windows.py index 3f28cff..bd30e3d 100644 --- a/readchar/key_windows.py +++ b/readchar/key_windows.py @@ -81,3 +81,4 @@ INSERT = "\x00\x52" SUPR = "\x00\x53" +DELETE = SUPR From b76043cb96ed86b131f2ee7077550adf6f29916a Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Sun, 1 May 2022 13:40:41 +0200 Subject: [PATCH 16/22] replace redundant codes with reference to existing vars --- readchar/key_linux.py | 6 +++--- readchar/key_windows.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/readchar/key_linux.py b/readchar/key_linux.py index 04228a8..ff8992e 100644 --- a/readchar/key_linux.py +++ b/readchar/key_linux.py @@ -18,11 +18,11 @@ CTRL_F = "\x06" CTRL_G = "\x07" CTRL_H = "\x08" -CTRL_I = "\t" -CTRL_J = "\n" +CTRL_I = TAB +CTRL_J = LF CTRL_K = "\x0b" CTRL_L = "\x0c" -CTRL_M = "\r" +CTRL_M = CR CTRL_N = "\x0e" CTRL_O = "\x0f" CTRL_P = "\x10" diff --git a/readchar/key_windows.py b/readchar/key_windows.py index bd30e3d..4e1cf0e 100644 --- a/readchar/key_windows.py +++ b/readchar/key_windows.py @@ -18,11 +18,11 @@ CTRL_F = "\x06" CTRL_G = "\x07" CTRL_H = "\x08" -CTRL_I = "\t" -CTRL_J = "\n" +CTRL_I = TAB +CTRL_J = LF CTRL_K = "\x0b" CTRL_L = "\x0c" -CTRL_M = "\r" +CTRL_M = CR CTRL_N = "\x0e" CTRL_O = "\x0f" CTRL_P = "\x10" From 507686a9c0c14271e39e2f398d5a773250eb583d Mon Sep 17 00:00:00 2001 From: c0d3d3v Date: Wed, 4 May 2022 15:30:01 +0200 Subject: [PATCH 17/22] fix function keys f5 to f12 --- readchar/key_linux.py | 16 ++++++++-------- readchar/read_linux.py | 2 +- tests/linux/test_readkey.py | 16 ++++++++-------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/readchar/key_linux.py b/readchar/key_linux.py index ff8992e..989f314 100644 --- a/readchar/key_linux.py +++ b/readchar/key_linux.py @@ -57,14 +57,14 @@ F2 = "\x1b\x4f\x51" F3 = "\x1b\x4f\x52" F4 = "\x1b\x4f\x53" -F5 = "\x1b\x4f\x31\x35\x7e" -F6 = "\x1b\x4f\x31\x37\x7e" -F7 = "\x1b\x4f\x31\x38\x7e" -F8 = "\x1b\x4f\x31\x39\x7e" -F9 = "\x1b\x4f\x32\x30\x7e" -F10 = "\x1b\x4f\x32\x31\x7e" -F11 = "\x1b\x4f\x32\x33\x7e" -F12 = "\x1b\x4f\x32\x34\x7e" +F5 = "\x1b\x5b\x31\x35\x7e" +F6 = "\x1b\x5b\x31\x37\x7e" +F7 = "\x1b\x5b\x31\x38\x7e" +F8 = "\x1b\x5b\x31\x39\x7e" +F9 = "\x1b\x5b\x32\x30\x7e" +F10 = "\x1b\x5b\x32\x31\x7e" +F11 = "\x1b\x5b\x32\x33\x7e" +F12 = "\x1b\x5b\x32\x34\x7e" PAGE_UP = "\x1b\x5b\x35\x7e" PAGE_DOWN = "\x1b\x5b\x36\x7e" diff --git a/readchar/read_linux.py b/readchar/read_linux.py index 8104a40..fa6624b 100644 --- a/readchar/read_linux.py +++ b/readchar/read_linux.py @@ -49,7 +49,7 @@ def readkey(): return c1 + c2 + c3 c4 = readchar(blocking=True) - if c2 != "\x4F" or c4 not in "\x30\x31\x33\x34\x35\x37\x38\x39": + if c4 not in "\x30\x31\x33\x34\x35\x37\x38\x39": return c1 + c2 + c3 + c4 c5 = readchar(blocking=True) diff --git a/tests/linux/test_readkey.py b/tests/linux/test_readkey.py index d902579..203f467 100644 --- a/tests/linux/test_readkey.py +++ b/tests/linux/test_readkey.py @@ -45,14 +45,14 @@ def test_specialKeys(seq, key, patched_stdin): (key.F2, "\x1b\x4f\x51"), (key.F3, "\x1b\x4f\x52"), (key.F4, "\x1b\x4f\x53"), - (key.F5, "\x1b\x4f\x31\x35\x7e"), - (key.F6, "\x1b\x4f\x31\x37\x7e"), - (key.F7, "\x1b\x4f\x31\x38\x7e"), - (key.F8, "\x1b\x4f\x31\x39\x7e"), - (key.F9, "\x1b\x4f\x32\x30\x7e"), - (key.F10, "\x1b\x4f\x32\x31\x7e"), - (key.F11, "\x1b\x4f\x32\x33\x7e"), - (key.F12, "\x1b\x4f\x32\x34\x7e"), + (key.F5, "\x1b\x5b\x31\x35\x7e"), + (key.F6, "\x1b\x5b\x31\x37\x7e"), + (key.F7, "\x1b\x5b\x31\x38\x7e"), + (key.F8, "\x1b\x5b\x31\x39\x7e"), + (key.F9, "\x1b\x5b\x32\x30\x7e"), + (key.F10, "\x1b\x5b\x32\x31\x7e"), + (key.F11, "\x1b\x5b\x32\x33\x7e"), + (key.F12, "\x1b\x5b\x32\x34\x7e"), ], ) def test_functionKeys(seq, key, patched_stdin): From ccd4838b720c141e59f7bea523135bb43f148a96 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Tue, 3 May 2022 13:36:33 +0200 Subject: [PATCH 18/22] only import platfrom from sys, not full package --- readchar/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/readchar/__init__.py b/readchar/__init__.py index 746858b..5a55364 100644 --- a/readchar/__init__.py +++ b/readchar/__init__.py @@ -1,14 +1,14 @@ -import sys +from sys import platform -if sys.platform.startswith("linux"): +if platform.startswith("linux"): from .read_linux import readchar, readkey from . import key_linux as key -elif sys.platform in ("win32", "cygwin"): +elif platform in ("win32", "cygwin"): from .read_windows import readchar, readkey from . import key_windows as key else: - raise NotImplementedError("The platform %s is not supported yet" % sys.platform) + raise NotImplementedError(f"The platform {platform} is not supported yet") __all__ = ["readchar", "readkey", "key"] From 95ea9d830ffedb90e30a62479a3dc4940b0c97d0 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Tue, 3 May 2022 13:37:18 +0200 Subject: [PATCH 19/22] enable mac support --- .github/workflows/run-tests.yml | 1 + readchar/__init__.py | 2 +- tests/linux/conftest.py | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 976f18e..01233b9 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -23,6 +23,7 @@ jobs: os: - ubuntu-latest - windows-latest + - macos-latest python-version: - "3.6" - "3.7" diff --git a/readchar/__init__.py b/readchar/__init__.py index 5a55364..edbb350 100644 --- a/readchar/__init__.py +++ b/readchar/__init__.py @@ -1,7 +1,7 @@ from sys import platform -if platform.startswith("linux"): +if platform.startswith("linux") or platform in ("darwin"): from .read_linux import readchar, readkey from . import key_linux as key elif platform in ("win32", "cygwin"): diff --git a/tests/linux/conftest.py b/tests/linux/conftest.py index 1547f32..54fa5df 100644 --- a/tests/linux/conftest.py +++ b/tests/linux/conftest.py @@ -1,7 +1,7 @@ import pytest import sys -if sys.platform.startswith("linux"): +if sys.platform.startswith("linux") or sys.platform in ("darwin"): import termios import tty import readchar.read_linux as read_linux @@ -9,7 +9,7 @@ # ignore all tests in this folder if not on linux def pytest_ignore_collect(path, config): - if not sys.platform.startswith("linux"): + if not (sys.platform.startswith("linux") or sys.platform in ("darwin")): return True From a3073385800c1200b9ba4507e89d3b940b7cd17e Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Thu, 5 May 2022 23:07:45 +0200 Subject: [PATCH 20/22] romoved unused var ESCAPE_SEQUENCES from linux_key --- readchar/key_linux.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/readchar/key_linux.py b/readchar/key_linux.py index 989f314..26f5b0c 100644 --- a/readchar/key_linux.py +++ b/readchar/key_linux.py @@ -74,29 +74,3 @@ INSERT = "\x1b\x5b\x32\x7e" SUPR = "\x1b\x5b\x33\x7e" DELETE = SUPR - - -ESCAPE_SEQUENCES = ( - ESC, - ESC + "\x5b", - ESC + "\x5b" + "\x31", - ESC + "\x5b" + "\x32", - ESC + "\x5b" + "\x33", - ESC + "\x5b" + "\x35", - ESC + "\x5b" + "\x36", - ESC + "\x5b" + "\x31" + "\x35", - ESC + "\x5b" + "\x31" + "\x36", - ESC + "\x5b" + "\x31" + "\x37", - ESC + "\x5b" + "\x31" + "\x38", - ESC + "\x5b" + "\x31" + "\x39", - ESC + "\x5b" + "\x32" + "\x30", - ESC + "\x5b" + "\x32" + "\x31", - ESC + "\x5b" + "\x32" + "\x32", - ESC + "\x5b" + "\x32" + "\x33", - ESC + "\x5b" + "\x32" + "\x34", - ESC + "\x4f", - ESC + ESC, - ESC + ESC + "\x5b", - ESC + ESC + "\x5b" + "\x32", - ESC + ESC + "\x5b" + "\x33", -) From 9fbd90676e527cea720ed7b253f2ccbb68858156 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Thu, 5 May 2022 23:43:42 +0200 Subject: [PATCH 21/22] added testscript --- tests/manual-test.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/manual-test.py diff --git a/tests/manual-test.py b/tests/manual-test.py new file mode 100644 index 0000000..45eaaf9 --- /dev/null +++ b/tests/manual-test.py @@ -0,0 +1,17 @@ +from readchar import key, readkey + + +# construct an inverted code -> key-name mapping +# we need to revese the items so that aliases won't overrive the original name later on +known_keys = {v: k for k, v in reversed(vars(key).items()) if not k.startswith("__")} + + +while True: + data = readkey() + + if data in known_keys: + print(f"got {known_keys[data]}", end="") + else: + print(data, end="") + + print(f' - \\x{"".join([f"{ord(c):02x}" for c in data])}') From 367bb569957f2a426589912a7adba6de9afe3857 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Mon, 9 May 2022 14:10:21 +0200 Subject: [PATCH 22/22] replace redundant CTRL_H code with reference to BACKSPACE --- readchar/key_windows.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readchar/key_windows.py b/readchar/key_windows.py index 4e1cf0e..22d7e0f 100644 --- a/readchar/key_windows.py +++ b/readchar/key_windows.py @@ -17,7 +17,7 @@ CTRL_E = "\x05" CTRL_F = "\x06" CTRL_G = "\x07" -CTRL_H = "\x08" +CTRL_H = BACKSPACE CTRL_I = TAB CTRL_J = LF CTRL_K = "\x0b"