interact.py 3.23 KB
Newer Older
Nianchen Deng's avatar
sync    
Nianchen Deng committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import readline
import sys
import tty
import termios
import select
from typing import List


def make_completer(vocabulary):
    def custom_complete(text, state):
        #print('custom_complete: ', text, state)
        # None is returned for the end of the completion session.
        results = [x for x in vocabulary if x.startswith(text)] + [None]
        # A space is added to the completion since the Python readline doesn't
        # do this on its own. When a word is fully completed we want to mimic
        # the default readline library behavior of adding a space after it.
        return results[state] + " "
    return custom_complete


def check_input_in_list(value, list, err_msg):
    if value not in list:
        raise ValueError('Wrong input. (%s)' % err_msg)
    return value


def check_input_not_empty(value):
    if not value:
        raise ValueError('Wrong input. (Cannot be empty)')
    return value


def input_to_int(*, min=None, max=None):
    def action(s):
        try:
            s = int(s)
        except ValueError:
            raise ValueError('Wrong input. (Must be an integer)')
        if min != None and s < min:
            raise ValueError('Wrong input. (Must be larger than or equal to %d)' % min)
        if max != None and s > max:
            raise ValueError('Wrong input. (Must be less than or equal to %d)' % max)
        return s
    return action


def input_ex(prompt, *actions, default=None):
    prompt_default = '(Default: %s) ' % default if default != None else ''
    while True:
        s = input(prompt + ' ' + prompt_default).strip()
        try:
            if default == None:
                s = check_input_not_empty(s)
            if not s:
                s = default
            else:
                for action in actions:
                    s = action(s)
            break
        except ValueError as err:
            print(err.strerror)
    return s


Nianchen Deng's avatar
sync    
Nianchen Deng committed
65
def input_enum(prompt, complete_list: list[str], *, err_msg: str, default=None):
Nianchen Deng's avatar
sync    
Nianchen Deng committed
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
    readline.set_completer(make_completer(complete_list))
    prompt_default = '(Default: %s) ' % default if default != None else ''
    while True:
        s = input(prompt + ' ' + prompt_default).strip()
        try:
            if default == None:
                s = check_input_not_empty(s)
            s = check_input_in_list(
                s, complete_list, err_msg) if s else default
            break
        except ValueError as err:
            print(err)
    readline.set_completer()  # Clear completer
    return s


def readchar():
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    try:
        tty.setraw(sys.stdin.fileno())
        if sys.stdin.readable:
            ch = sys.stdin.read(1)
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    select.select()
    return ch


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
    c3 = getchar()
    return chr(0x10 + ord(c3) - 65)


if 'libedit' in readline.__doc__:  # mac OS/X 是走这个分支
    print('x')
    readline.parse_and_bind("bind ^I rl_complete")
else:
    readline.parse_and_bind("tab: complete")
readline.set_completer_delims(' ')