hspylib-clitt 0.9.32__py3-none-any.whl → 0.9.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hspylib-clitt might be problematic. Click here for more details.
- clitt/.version +1 -1
- clitt/__init__.py +2 -2
- clitt/addons/__init__.py +2 -2
- clitt/addons/appman/__init__.py +2 -2
- clitt/addons/appman/templates/__init__.py +2 -2
- clitt/addons/setman/__init__.py +2 -2
- clitt/addons/widman/__init__.py +2 -2
- clitt/addons/widman/widgets/__init__.py +2 -2
- clitt/core/__init__.py +2 -2
- clitt/core/icons/__init__.py +2 -2
- clitt/core/icons/emojis/__init__.py +2 -2
- clitt/core/icons/font_awesome/__init__.py +2 -2
- clitt/core/tui/__init__.py +2 -2
- clitt/core/tui/mchoose/__init__.py +2 -2
- clitt/core/tui/mdashboard/__init__.py +2 -2
- clitt/core/tui/menu/__init__.py +2 -2
- clitt/core/tui/minput/__init__.py +3 -2
- clitt/core/tui/minput/field_builder.py +112 -0
- clitt/core/tui/minput/form_builder.py +19 -98
- clitt/core/tui/minput/form_field.py +101 -22
- clitt/core/tui/minput/input_validator.py +29 -40
- clitt/core/tui/minput/menu_input.py +56 -63
- clitt/core/tui/minput/minput_utils.py +33 -28
- clitt/core/tui/mselect/__init__.py +2 -2
- clitt/core/tui/table/__init__.py +2 -2
- {hspylib_clitt-0.9.32.dist-info → hspylib_clitt-0.9.34.dist-info}/METADATA +2 -2
- {hspylib_clitt-0.9.32.dist-info → hspylib_clitt-0.9.34.dist-info}/RECORD +29 -28
- {hspylib_clitt-0.9.32.dist-info → hspylib_clitt-0.9.34.dist-info}/WHEEL +0 -0
- {hspylib_clitt-0.9.32.dist-info → hspylib_clitt-0.9.34.dist-info}/top_level.txt +0 -0
|
@@ -24,69 +24,62 @@ class InputValidator(Validator):
|
|
|
24
24
|
|
|
25
25
|
class PatternType(Enumeration):
|
|
26
26
|
# fmt: off
|
|
27
|
-
CUSTOM = r'
|
|
28
|
-
ANYTHING = r'.
|
|
29
|
-
LETTERS = r'
|
|
30
|
-
WORDS = r'
|
|
31
|
-
NUMBERS = r'
|
|
32
|
-
|
|
27
|
+
CUSTOM = r'.' # It will be set later
|
|
28
|
+
ANYTHING = r'.'
|
|
29
|
+
LETTERS = r'[a-zA-Z]'
|
|
30
|
+
WORDS = r'[a-zA-Z0-9 _]'
|
|
31
|
+
NUMBERS = r'[0-9\.\,]'
|
|
32
|
+
BINARY = r'[01]'
|
|
33
|
+
MASKED = r'.'
|
|
33
34
|
# fmt: on
|
|
34
35
|
|
|
35
36
|
@classmethod
|
|
36
|
-
def custom(cls, pattern: str
|
|
37
|
+
def custom(cls, pattern: str) -> "InputValidator":
|
|
37
38
|
"""Return a custom validator that allows customize the input rules.
|
|
38
39
|
:param pattern: the custom validator pattern.
|
|
39
|
-
:param min_length: the minimum length required.
|
|
40
|
-
:param max_length: the maximum length allowed.
|
|
41
40
|
"""
|
|
42
41
|
pattern_type = cls.PatternType.CUSTOM
|
|
43
|
-
validator = InputValidator(
|
|
42
|
+
validator = InputValidator(pattern_type=pattern_type)
|
|
44
43
|
validator.pattern = pattern
|
|
45
44
|
return validator
|
|
46
45
|
|
|
47
46
|
@classmethod
|
|
48
|
-
def letters(cls
|
|
47
|
+
def letters(cls) -> "InputValidator":
|
|
49
48
|
"""Return a validator that allows only letters.
|
|
50
|
-
:param min_length: the minimum length required.
|
|
51
|
-
:param max_length: the maximum length allowed.
|
|
52
49
|
"""
|
|
53
|
-
return InputValidator(
|
|
50
|
+
return InputValidator(cls.PatternType.LETTERS)
|
|
54
51
|
|
|
55
52
|
@classmethod
|
|
56
|
-
def numbers(cls
|
|
53
|
+
def numbers(cls) -> "InputValidator":
|
|
57
54
|
"""Return a validator that allows only numbers.
|
|
58
|
-
:param min_length: the minimum length required.
|
|
59
|
-
:param max_length: the maximum length allowed.
|
|
60
55
|
"""
|
|
61
|
-
return InputValidator(
|
|
56
|
+
return InputValidator(cls.PatternType.NUMBERS)
|
|
62
57
|
|
|
63
58
|
@classmethod
|
|
64
|
-
def words(cls
|
|
59
|
+
def words(cls) -> "InputValidator":
|
|
65
60
|
"""Return a validator that allows only words (space, numbers or letters).
|
|
66
|
-
:param min_length: the minimum length required.
|
|
67
|
-
:param max_length: the maximum length allowed.
|
|
68
61
|
"""
|
|
69
|
-
return InputValidator(
|
|
62
|
+
return InputValidator(cls.PatternType.WORDS)
|
|
70
63
|
|
|
71
64
|
@classmethod
|
|
72
|
-
def anything(cls
|
|
65
|
+
def anything(cls) -> "InputValidator":
|
|
73
66
|
"""Return a validator that allows any input value.
|
|
74
|
-
:param min_length: the minimum length required.
|
|
75
|
-
:param max_length: the maximum length allowed.
|
|
76
67
|
"""
|
|
77
|
-
return InputValidator(
|
|
68
|
+
return InputValidator(cls.PatternType.ANYTHING)
|
|
78
69
|
|
|
79
70
|
@classmethod
|
|
80
|
-
def
|
|
81
|
-
"""Return a validator that allows
|
|
82
|
-
:param min_length: the minimum length required.
|
|
83
|
-
:param max_length: the maximum length allowed.
|
|
71
|
+
def binary(cls) -> "InputValidator":
|
|
72
|
+
"""Return a validator that allows only zero or ones.
|
|
84
73
|
"""
|
|
85
|
-
return InputValidator(
|
|
74
|
+
return InputValidator(cls.PatternType.BINARY)
|
|
86
75
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
76
|
+
@classmethod
|
|
77
|
+
def masked(cls) -> "InputValidator":
|
|
78
|
+
"""Return a validator that allows masked inputs.
|
|
79
|
+
"""
|
|
80
|
+
return InputValidator(cls.PatternType.MASKED)
|
|
81
|
+
|
|
82
|
+
def __init__(self, pattern_type: PatternType = PatternType.ANYTHING):
|
|
90
83
|
self._pattern_type = pattern_type
|
|
91
84
|
self._pattern = pattern_type.value
|
|
92
85
|
|
|
@@ -105,16 +98,12 @@ class InputValidator(Validator):
|
|
|
105
98
|
|
|
106
99
|
def validate(self, value: str) -> bool:
|
|
107
100
|
"""Validate the value against the validator pattern."""
|
|
108
|
-
unmasked_value = re.split("[|,;]", value)[0]
|
|
101
|
+
unmasked_value = re.split("[|,;]", value)[0] if value else ""
|
|
109
102
|
return bool(re.match(self.pattern, unmasked_value))
|
|
110
103
|
|
|
111
104
|
@property
|
|
112
105
|
def pattern(self) -> str:
|
|
113
|
-
return (
|
|
114
|
-
str(self._pattern)
|
|
115
|
-
.replace("%min%", str(self._min_length or 1))
|
|
116
|
-
.replace("%max%", str(self._max_length or 30))
|
|
117
|
-
)
|
|
106
|
+
return str(self._pattern)
|
|
118
107
|
|
|
119
108
|
@pattern.setter
|
|
120
109
|
def pattern(self, pattern: str) -> None:
|
|
@@ -13,9 +13,11 @@
|
|
|
13
13
|
Copyright 2023, HsPyLib team
|
|
14
14
|
"""
|
|
15
15
|
import time
|
|
16
|
+
from functools import reduce
|
|
16
17
|
from typing import List
|
|
17
18
|
|
|
18
19
|
import pyperclip
|
|
20
|
+
from hspylib.core.exception.exceptions import InvalidStateError
|
|
19
21
|
from hspylib.core.namespace import Namespace
|
|
20
22
|
from hspylib.modules.cli.keyboard import Keyboard
|
|
21
23
|
from hspylib.modules.cli.vt100.vt_utils import get_cursor_position, set_enable_echo, set_show_cursor
|
|
@@ -71,17 +73,16 @@ class MenuInput(TUIComponent):
|
|
|
71
73
|
self.writeln(f"{self.prefs.title_color.placeholder}{self.title}%EOL%%NC%")
|
|
72
74
|
|
|
73
75
|
for idx, field in enumerate(self.fields):
|
|
74
|
-
|
|
76
|
+
field_length = field.length
|
|
75
77
|
if self.tab_index == idx:
|
|
76
78
|
mi_print(
|
|
77
79
|
self.screen, f" {field.label}", self.prefs.sel_bg_color.placeholder, self.max_label_length + 2)
|
|
78
80
|
self.cur_field = field
|
|
79
81
|
else:
|
|
80
82
|
mi_print(self.screen, f" {field.label}", field_len=self.max_label_length + 2)
|
|
81
|
-
|
|
82
|
-
self._buffer_positions(idx, field_size)
|
|
83
|
+
self._buffer_positions(idx, field_length)
|
|
83
84
|
self._render_field(field)
|
|
84
|
-
self._render_details(field,
|
|
85
|
+
self._render_details(field, field_length)
|
|
85
86
|
|
|
86
87
|
self.draw_navbar(self.navbar())
|
|
87
88
|
self._re_render = False
|
|
@@ -114,7 +115,7 @@ class MenuInput(TUIComponent):
|
|
|
114
115
|
case Keyboard.VK_CTRL_P:
|
|
115
116
|
self._handle_ctrl_p()
|
|
116
117
|
case Keyboard.VK_ENTER:
|
|
117
|
-
self._handle_enter()
|
|
118
|
+
return self._handle_enter()
|
|
118
119
|
case _ as key if key.isalnum() or key.ispunct() or key == Keyboard.VK_SPACE:
|
|
119
120
|
if not self.cur_field.can_write():
|
|
120
121
|
self._display_error("This field is read only !")
|
|
@@ -125,19 +126,16 @@ class MenuInput(TUIComponent):
|
|
|
125
126
|
|
|
126
127
|
return keypress
|
|
127
128
|
|
|
128
|
-
def _handle_enter(self) ->
|
|
129
|
+
def _handle_enter(self) -> Optional[Keyboard]:
|
|
129
130
|
"""Handle 'enter' press. Validate & Save form and exit."""
|
|
130
|
-
|
|
131
|
-
# Fixme: Commented because at this point we need to validate the entire form since inputs are already valid
|
|
132
|
-
# TODO: Find a way to validate the form itself, maybe add a form validator.
|
|
133
|
-
# invalid = next((field for field in self.fields if not field.validate_input(field.value)), None)
|
|
134
|
-
invalid = None
|
|
131
|
+
invalid = next((field for field in self.fields if not field.validate_field()), None)
|
|
135
132
|
if invalid:
|
|
136
133
|
idx = self.fields.index(invalid)
|
|
137
134
|
pos = self.positions[idx]
|
|
138
135
|
self.cur_row = pos[0]
|
|
139
136
|
self.tab_index = idx
|
|
140
137
|
self._display_error("Form field is not valid: " + str(invalid))
|
|
138
|
+
return None
|
|
141
139
|
else:
|
|
142
140
|
for idx, field in enumerate(self.fields):
|
|
143
141
|
match field.itype:
|
|
@@ -148,6 +146,7 @@ class MenuInput(TUIComponent):
|
|
|
148
146
|
case InputType.SELECT:
|
|
149
147
|
_, field.value = get_selected(field.value)
|
|
150
148
|
self._done = True
|
|
149
|
+
return Keyboard.VK_ENTER
|
|
151
150
|
|
|
152
151
|
def _handle_input(self, keypress: Keyboard) -> None:
|
|
153
152
|
"""Handle a form input.
|
|
@@ -157,24 +156,28 @@ class MenuInput(TUIComponent):
|
|
|
157
156
|
match self.cur_field.itype:
|
|
158
157
|
case InputType.CHECKBOX:
|
|
159
158
|
if keypress == Keyboard.VK_SPACE:
|
|
160
|
-
self.cur_field.value =
|
|
159
|
+
self.cur_field.value = not self.cur_field.value
|
|
161
160
|
case InputType.SELECT:
|
|
162
161
|
if keypress == Keyboard.VK_SPACE:
|
|
163
162
|
if self.cur_field.value:
|
|
164
|
-
self.cur_field.value = toggle_selected(str(self.cur_field.value))
|
|
163
|
+
self.cur_field.value = toggle_selected(str(self.cur_field.value or ''))
|
|
165
164
|
case InputType.MASKED:
|
|
166
|
-
value, mask = unpack_masked(str(self.cur_field.value))
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
self._display_error(f"{str(err)}")
|
|
165
|
+
value, mask = unpack_masked(str(self.cur_field.value or ''))
|
|
166
|
+
try:
|
|
167
|
+
self.cur_field.value = append_masked(value, mask, keypress.value)
|
|
168
|
+
except InvalidInputError as err:
|
|
169
|
+
self._display_error(str(err))
|
|
172
170
|
case _:
|
|
173
|
-
if len(str(self.cur_field.value)) < self.cur_field.max_length:
|
|
171
|
+
if len(str(self.cur_field.value or '')) < self.cur_field.max_length:
|
|
174
172
|
if self.cur_field.validate_input(keypress.value):
|
|
175
|
-
self.cur_field.value =
|
|
173
|
+
self.cur_field.value = (
|
|
174
|
+
str(self.cur_field.value) if self.cur_field.value else ''
|
|
175
|
+
) + str(keypress.value)
|
|
176
176
|
else:
|
|
177
|
-
self._display_error(
|
|
177
|
+
self._display_error(
|
|
178
|
+
f"Input '{keypress.value}' is invalid. "
|
|
179
|
+
f"This field takes only {self.cur_field.input_validator}!"
|
|
180
|
+
)
|
|
178
181
|
|
|
179
182
|
def _handle_backspace(self) -> None:
|
|
180
183
|
"""Handle 'backspace' press. Delete previous input."""
|
|
@@ -200,48 +203,33 @@ class MenuInput(TUIComponent):
|
|
|
200
203
|
"""Set the cursor at the right position according to the ATB index."""
|
|
201
204
|
pos = self.positions[self.tab_index]
|
|
202
205
|
self.screen.cursor.move_to(
|
|
203
|
-
pos[0], pos[1] + self.
|
|
204
|
-
|
|
205
|
-
def _get_field_len(self) -> int:
|
|
206
|
-
"""Get the field real length, depending on the field type."""
|
|
207
|
-
value = self.cur_field.value
|
|
208
|
-
match self.cur_field.itype:
|
|
209
|
-
case InputType.CHECKBOX:
|
|
210
|
-
value = '1'
|
|
211
|
-
case InputType.SELECT:
|
|
212
|
-
_, value = get_selected(str(self.cur_field.value))
|
|
213
|
-
case InputType.MASKED:
|
|
214
|
-
value, mask = unpack_masked(str(self.cur_field.value))
|
|
215
|
-
idx = len(value)
|
|
216
|
-
while idx < len(mask) and mask[idx] not in MASK_SYMBOLS:
|
|
217
|
-
idx += 1
|
|
218
|
-
return idx
|
|
219
|
-
|
|
220
|
-
return len(str(value))
|
|
206
|
+
pos[0], pos[1] + self.cur_field.length)
|
|
221
207
|
|
|
222
208
|
def _render_field(self, field: FormField) -> None:
|
|
223
209
|
"""Render the specified form field.
|
|
224
210
|
:param field: the form field to render.
|
|
225
211
|
"""
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
212
|
+
match field.itype:
|
|
213
|
+
case InputType.TEXT:
|
|
214
|
+
mi_print(self.screen, str(field.value or ''), field_len=self.max_value_length)
|
|
215
|
+
case InputType.PASSWORD:
|
|
216
|
+
mi_print(self.screen, "*" * field.length, field_len=self.max_value_length)
|
|
217
|
+
case InputType.CHECKBOX:
|
|
218
|
+
mi_print(
|
|
219
|
+
self.screen, " ",
|
|
220
|
+
FormIcons.CHECK_SQUARE.unicode if field.value else FormIcons.UNCHECK_SQUARE.unicode,
|
|
221
|
+
field_len=self.max_value_length - 1
|
|
222
|
+
)
|
|
223
|
+
case InputType.SELECT:
|
|
224
|
+
if field.value:
|
|
225
|
+
mat = re.search(rf".*({VALUE_SEPARATORS})?<(.+)>({VALUE_SEPARATORS})?.*", str(field.value))
|
|
226
|
+
sel_value = mat.group(2) if mat else re.split(VALUE_SEPARATORS, str(field.value))[0]
|
|
227
|
+
mi_print(self.screen, f"{sel_value}", field_len=self.max_value_length)
|
|
228
|
+
case InputType.MASKED:
|
|
229
|
+
value, mask = unpack_masked(str(field.value))
|
|
230
|
+
mi_print(self.screen, over_masked(value, mask), field_len=self.max_value_length)
|
|
231
|
+
case _:
|
|
232
|
+
raise InvalidStateError(f"Invalid form field type: {field.itype}")
|
|
245
233
|
|
|
246
234
|
def _render_details(self, field: FormField, field_details: int) -> None:
|
|
247
235
|
"""Render details about total/remaining field characters.
|
|
@@ -255,8 +243,12 @@ class MenuInput(TUIComponent):
|
|
|
255
243
|
count = len(re.split(VALUE_SEPARATORS, field.value))
|
|
256
244
|
self.writeln(fmt.format(field.icon, idx + 1 if idx >= 0 else 1, count))
|
|
257
245
|
elif field.itype == InputType.MASKED:
|
|
258
|
-
value,
|
|
259
|
-
self.writeln(fmt.format(
|
|
246
|
+
value, mask = unpack_masked(str(field.value or ''))
|
|
247
|
+
self.writeln(fmt.format(
|
|
248
|
+
field.icon,
|
|
249
|
+
reduce(lambda n, m: n + m, list(map(lambda s: mask[:len(value)].count(s), MASK_SYMBOLS))),
|
|
250
|
+
reduce(lambda n, m: n + m, list(map(lambda s: mask.count(s), MASK_SYMBOLS)))
|
|
251
|
+
))
|
|
260
252
|
else:
|
|
261
253
|
self.writeln(fmt.format(field.icon, field_details, field.max_length))
|
|
262
254
|
|
|
@@ -282,9 +274,10 @@ class MenuInput(TUIComponent):
|
|
|
282
274
|
self.cursor.move_to(self.cur_row, err_pos)
|
|
283
275
|
mi_print_err(self.screen, f"{FormIcons.ERROR_CIRCLE} {err_msg}")
|
|
284
276
|
# This calculation gives a good delay amount based on the size of the message.
|
|
285
|
-
time.sleep(max(1.
|
|
277
|
+
time.sleep(max(1.8, int(len(err_msg) / 24)))
|
|
286
278
|
set_enable_echo()
|
|
287
|
-
set_show_cursor()
|
|
288
279
|
# Erase the message after the timeout
|
|
289
280
|
self.cursor.move_to(self.cur_row, err_pos)
|
|
290
281
|
self.cursor.erase(TUIScreen.CursorDirection.RIGHT)
|
|
282
|
+
set_show_cursor()
|
|
283
|
+
self._re_render = True
|
|
@@ -16,13 +16,14 @@ import re
|
|
|
16
16
|
from typing import Any, Optional, Tuple
|
|
17
17
|
|
|
18
18
|
from hspylib.core.exception.exceptions import InvalidInputError
|
|
19
|
-
from hspylib.core.preconditions import check_argument
|
|
20
19
|
|
|
21
20
|
from clitt.core.tui.tui_screen import TUIScreen
|
|
22
21
|
|
|
23
22
|
MASK_SYMBOLS = ["#", "@", "%", "*"]
|
|
24
23
|
|
|
25
|
-
VALUE_SEPARATORS = "[|,;]"
|
|
24
|
+
VALUE_SEPARATORS = r"[|,;]"
|
|
25
|
+
|
|
26
|
+
VALUE_SELECTORS = r"[<>]"
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
def detail_len(field: Any) -> int:
|
|
@@ -57,7 +58,6 @@ def mi_print_err(screen: TUIScreen, text: str) -> None:
|
|
|
57
58
|
"""Special menu input print error message.
|
|
58
59
|
:param screen: the component's screen.
|
|
59
60
|
:param text: the text to be printed.
|
|
60
|
-
:param end: string appended after the last value, default an empty string.
|
|
61
61
|
"""
|
|
62
62
|
screen.cursor.write(f"%RED%{text}%NC%")
|
|
63
63
|
|
|
@@ -74,7 +74,7 @@ def toggle_selected(tokenized_values: str) -> str:
|
|
|
74
74
|
else:
|
|
75
75
|
values[0] = f"<{values[0]}>"
|
|
76
76
|
return "|".join(values)
|
|
77
|
-
unselected = list(map(lambda x:
|
|
77
|
+
unselected = list(map(lambda x: re.sub(VALUE_SELECTORS, '', x), values))
|
|
78
78
|
# fmt: off
|
|
79
79
|
return '|'.join([
|
|
80
80
|
f'<{val}>'
|
|
@@ -91,7 +91,7 @@ def get_selected(tokenized_values: str) -> Optional[Tuple[int, str]]:
|
|
|
91
91
|
values = re.split(VALUE_SEPARATORS, tokenized_values)
|
|
92
92
|
# fmt: off
|
|
93
93
|
sel_item = next((
|
|
94
|
-
|
|
94
|
+
re.sub(VALUE_SELECTORS, '', val)
|
|
95
95
|
for val in values if val.startswith('<') and val.endswith('>')
|
|
96
96
|
), values[0])
|
|
97
97
|
# fmt: on
|
|
@@ -110,8 +110,7 @@ def unpack_masked(value: str) -> Tuple[str, str]:
|
|
|
110
110
|
"""
|
|
111
111
|
if value:
|
|
112
112
|
parts = re.split(VALUE_SEPARATORS, value)
|
|
113
|
-
|
|
114
|
-
return parts[0], parts[1]
|
|
113
|
+
return parts[0], parts[1] if len(parts) == 2 else ''
|
|
115
114
|
|
|
116
115
|
return '', ''
|
|
117
116
|
|
|
@@ -126,43 +125,49 @@ def append_masked(
|
|
|
126
125
|
:param keypress_value: the value to append (it can be a part of the mask itself).
|
|
127
126
|
"""
|
|
128
127
|
idx = len(value)
|
|
129
|
-
if keypress_value == mask[idx]:
|
|
130
|
-
return f"{value}{keypress_value}|{mask}"
|
|
131
128
|
masked_value = value
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
raise InvalidInputError(f"Value {keypress_value} is not a valid value against mask: {mask}")
|
|
129
|
+
if idx < len(mask):
|
|
130
|
+
if keypress_value == mask[idx]:
|
|
131
|
+
return f"{value}{keypress_value}|{mask}"
|
|
132
|
+
while idx < len(mask) and mask[idx] not in MASK_SYMBOLS:
|
|
133
|
+
masked_value += mask[idx]
|
|
134
|
+
idx += 1
|
|
135
|
+
if mask and idx < len(mask):
|
|
136
|
+
mask_re = mask_regex(mask, idx)
|
|
137
|
+
if re.search(mask_re, keypress_value):
|
|
138
|
+
masked_value += keypress_value
|
|
139
|
+
else:
|
|
140
|
+
raise InvalidInputError(f"Value '{keypress_value}' is invalid. Expecting: {mask_re}")
|
|
145
141
|
|
|
146
142
|
return f"{masked_value}|{mask}"
|
|
147
143
|
|
|
148
144
|
|
|
149
145
|
def over_masked(
|
|
150
146
|
value: str,
|
|
151
|
-
mask: str
|
|
152
|
-
placeholder: str = "_") -> str:
|
|
147
|
+
mask: str) -> str:
|
|
153
148
|
"""
|
|
154
149
|
Return the value to be printed by a 'masked' input field. A placeholder value will be used for unfilled values.
|
|
155
150
|
:param value: the masked field current value.
|
|
156
151
|
:param mask: the masked field's mask.
|
|
157
|
-
:param placeholder: the placeholder character. It's going to be printed if current value is None.
|
|
158
152
|
"""
|
|
159
153
|
if value:
|
|
160
154
|
over_masked_val = ""
|
|
161
155
|
for idx, element in enumerate(mask):
|
|
162
|
-
if element in MASK_SYMBOLS:
|
|
163
|
-
over_masked_val += value[idx] if idx < len(value) else
|
|
156
|
+
if element in MASK_SYMBOLS:
|
|
157
|
+
over_masked_val += value[idx] if idx < len(value) else mask[idx]
|
|
164
158
|
else:
|
|
165
159
|
over_masked_val += element
|
|
166
160
|
return over_masked_val
|
|
167
161
|
|
|
168
|
-
return
|
|
162
|
+
return mask
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def mask_regex(
|
|
166
|
+
mask: str,
|
|
167
|
+
idx: int) -> str:
|
|
168
|
+
"""Return a regex matching the index of the mask."""
|
|
169
|
+
return mask[idx] \
|
|
170
|
+
.replace("#", "[0-9]") \
|
|
171
|
+
.replace("@", "[a-zA-Z]") \
|
|
172
|
+
.replace("%", "[a-zA-Z0-9]") \
|
|
173
|
+
.replace("*", ".")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# _*_ coding: utf-8 _*_
|
|
2
2
|
#
|
|
3
|
-
# hspylib-clitt v0.9.
|
|
3
|
+
# hspylib-clitt v0.9.34
|
|
4
4
|
#
|
|
5
5
|
# Package: main.clitt.core.tui.mselect
|
|
6
6
|
"""Package initialization."""
|
|
@@ -9,4 +9,4 @@ __all__ = [
|
|
|
9
9
|
'menu_select',
|
|
10
10
|
'mselect'
|
|
11
11
|
]
|
|
12
|
-
__version__ = '0.9.
|
|
12
|
+
__version__ = '0.9.34'
|
clitt/core/tui/table/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# _*_ coding: utf-8 _*_
|
|
2
2
|
#
|
|
3
|
-
# hspylib-clitt v0.9.
|
|
3
|
+
# hspylib-clitt v0.9.34
|
|
4
4
|
#
|
|
5
5
|
# Package: main.clitt.core.tui.table
|
|
6
6
|
"""Package initialization."""
|
|
@@ -8,4 +8,4 @@
|
|
|
8
8
|
__all__ = [
|
|
9
9
|
'table_renderer'
|
|
10
10
|
]
|
|
11
|
-
__version__ = '0.9.
|
|
11
|
+
__version__ = '0.9.34'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hspylib-clitt
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.34
|
|
4
4
|
Summary: HsPyLib - CLI Terminal Tools
|
|
5
5
|
Home-page: https://github.com/yorevs/hspylib
|
|
6
6
|
Author: Hugo Saporetti Junior
|
|
@@ -31,7 +31,7 @@ Requires-Dist: hspylib
|
|
|
31
31
|
## Create professional CLI applications
|
|
32
32
|
|
|
33
33
|
[](LICENSE.md)
|
|
34
|
-
[](CHANGELOG.md#unreleased)
|
|
35
35
|
[](https://pypi.org/project/hspylib-cfman)
|
|
36
36
|
[](https://github.com/yorevs/hspylib)
|
|
37
37
|
[](https://gitter.im/hspylib/community)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
clitt/.version,sha256=
|
|
1
|
+
clitt/.version,sha256=GiSux3LKPB7ZJg2Z6q_kl-eS_lJ4cPlTKGImSFNQN4U,7
|
|
2
2
|
clitt/__classpath__.py,sha256=j7k_kckSu-7r0RB630enzF3JH_1z2J7RByxp2ZZNdmg,717
|
|
3
|
-
clitt/__init__.py,sha256=
|
|
3
|
+
clitt/__init__.py,sha256=QEvjCmII4t28WjOdWVBsqDthnsqgF4g4KnguSDc40Hs,164
|
|
4
4
|
clitt/__main__.py,sha256=leYCCcxf0CrO9zBiI4MVRL-s02rt25Ib6uROSYlNca4,6973
|
|
5
5
|
clitt/welcome.txt,sha256=x4oQU6m_9g75x4d7tLriPkFR6I_4MLhlHRzqwBIC-lA,452
|
|
6
|
-
clitt/addons/__init__.py,sha256=
|
|
7
|
-
clitt/addons/appman/__init__.py,sha256=
|
|
6
|
+
clitt/addons/__init__.py,sha256=eWlL32sZ8cfQ2m1kP24W8NfzC0fxmrvAuuMrmrzZyLg,186
|
|
7
|
+
clitt/addons/appman/__init__.py,sha256=4gIHR-LpwYezD8JPidvgN9Waxs8Al56-Mr29Iz8lPKQ,202
|
|
8
8
|
clitt/addons/appman/appman.py,sha256=-fwijYySTWoyoAwzWFycIdScBkStH7VLFr-4bFuGXw8,13150
|
|
9
9
|
clitt/addons/appman/appman_enums.py,sha256=FMfSIBptbCDBxWsqUmF_xIsRBFNITqqxeXr2iOIdcLM,873
|
|
10
|
-
clitt/addons/appman/templates/__init__.py,sha256=
|
|
10
|
+
clitt/addons/appman/templates/__init__.py,sha256=V7r3LmKDVG1u_-qyYpq53ZMYm55cekkSOT_BlraZeHg,169
|
|
11
11
|
clitt/addons/appman/templates/build.gradle.tpl,sha256=VQKs4r41xPr7P14PtQPt3alYCmNuXaGraaXbKB_FKkM,1817
|
|
12
12
|
clitt/addons/appman/templates/classpath.py.tpl,sha256=R60uLvVxeJ7nJCXCvav_9LAnDe5VOfJqWO-T1QAlwAc,397
|
|
13
13
|
clitt/addons/appman/templates/dependencies.hspd.tpl,sha256=bbVNU6XBm5QT28lvH7Bp_4nlXvoD-LwX5kGQ-CPhOEA,764
|
|
@@ -20,28 +20,28 @@ clitt/addons/appman/templates/run.sh.tpl,sha256=2xpuHjZP9-7j14dAA2CqPm4G2xtnuenf
|
|
|
20
20
|
clitt/addons/appman/templates/test_main.py.tpl,sha256=x_xuKy2O2b1bs6tuzOUqF5shnx-2-EfFRc7sBV4Fndc,1239
|
|
21
21
|
clitt/addons/appman/templates/usage.txt.tpl,sha256=R0e48HCeD7PDbWnX6HGIjz6gr_hBdp-zK6V42U1Aexw,217
|
|
22
22
|
clitt/addons/appman/templates/widget.py.tpl,sha256=JmlO5rh2ETCF5ldiB9MStfjB4oB4JLI8UUnNP5SAAcM,1668
|
|
23
|
-
clitt/addons/setman/__init__.py,sha256=
|
|
23
|
+
clitt/addons/setman/__init__.py,sha256=pGI-w38LyS17ZdFDn_BkCCpcfN5rp200a5C5dVzsVus,270
|
|
24
24
|
clitt/addons/setman/setman.py,sha256=70CC1dnuNxeumo7fESQfIViIeKBr-ee2ST6JcL1N_Ow,8129
|
|
25
25
|
clitt/addons/setman/setman_config.py,sha256=X39iFkd4HLqMgQ0dpbtngASe05HrtCdxIbKwgQWImIs,1012
|
|
26
26
|
clitt/addons/setman/setman_entry.py,sha256=eZqOgNzRK6mtFc_eoyQw8Y-sq8z87QK95RpoYpcqtLo,3394
|
|
27
27
|
clitt/addons/setman/setman_enums.py,sha256=fZ0uXTzBh7fx1xUqV02BkFmVKSUUIJbr2HmVLbcrhqk,1281
|
|
28
28
|
clitt/addons/setman/setman_repository.py,sha256=yxL9raNA3FVcvWdFtbIQMrKYmrjgPbu4TcqViJGE3lY,2547
|
|
29
29
|
clitt/addons/setman/setman_service.py,sha256=1Yw27DFmlaiCedaGjg1TLxDyOfNFjlrbfE91uChzLL8,2244
|
|
30
|
-
clitt/addons/widman/__init__.py,sha256=
|
|
30
|
+
clitt/addons/widman/__init__.py,sha256=kS21YpLV4Jpn-L3q1pu1rfoyKkCUaJLprq_mpOsbOWc,213
|
|
31
31
|
clitt/addons/widman/widget.py,sha256=mIEB3Fs4ypaUMhh85meg2vMhKJVjW8Tyxkp_XqHIHtA,1920
|
|
32
32
|
clitt/addons/widman/widget_entry.py,sha256=8Shj4_esh6KN69usTrCcUsfGVas81Xz0QabsY_zV9o0,1350
|
|
33
33
|
clitt/addons/widman/widman.py,sha256=kfv3CcDSjUZglurPXa4Bv1jYELtEkbixOjpVL8yRf3M,5260
|
|
34
|
-
clitt/addons/widman/widgets/__init__.py,sha256=
|
|
34
|
+
clitt/addons/widman/widgets/__init__.py,sha256=EYww5RDRZaLGDuB_pOSGkwfIjnaceX0HQdonKtEmga4,244
|
|
35
35
|
clitt/addons/widman/widgets/widget_free.py,sha256=5imXWD4F984KqwcWuI-ZuBaF1gtNha0TAmmaiIZ-MVg,3997
|
|
36
36
|
clitt/addons/widman/widgets/widget_punch.py,sha256=YQ27qC15gyt1jn-R4oNumVLVbMzbs3s7I0yLPqpR8xY,10535
|
|
37
37
|
clitt/addons/widman/widgets/widget_send_msg.py,sha256=MMBOGTBc6c3jgLgZBzbQIiQsSs5jl-8ohqy7evMSNQU,11056
|
|
38
38
|
clitt/addons/widman/widgets/widget_time_calc.py,sha256=JQOHRf5OFDL73Y-GJEWtdN5_s2vKR-9mX0yoisxNQRg,5100
|
|
39
|
-
clitt/core/__init__.py,sha256=
|
|
40
|
-
clitt/core/icons/__init__.py,sha256=
|
|
41
|
-
clitt/core/icons/emojis/__init__.py,sha256=
|
|
39
|
+
clitt/core/__init__.py,sha256=twL6xekgpsy_Vmp5Wl5VvugMlr_vBbo0Tcl9x_rJV4M,167
|
|
40
|
+
clitt/core/icons/__init__.py,sha256=wklMn68feTajNTbQodPrfU56oGGYGUftp2sw756kT1s,183
|
|
41
|
+
clitt/core/icons/emojis/__init__.py,sha256=NPdTQoJn7sYMXGqWkBr_mpuwZGKON3alp2C-DBP_xQg,190
|
|
42
42
|
clitt/core/icons/emojis/emojis.py,sha256=LbwpjJonRlNVPa6eMEDTreQz1fmFfFK4dOk6X-4XHrI,1109
|
|
43
43
|
clitt/core/icons/emojis/face_smiling.py,sha256=qho6t7W3za6-H9bW51_0RoYKah_f0_DCJ0EiCiwvQwI,1269
|
|
44
|
-
clitt/core/icons/font_awesome/__init__.py,sha256=
|
|
44
|
+
clitt/core/icons/font_awesome/__init__.py,sha256=NPp7cJgEkla2IK3Bzd0V5Z77D-PzFp19DBJE4agLrO0,288
|
|
45
45
|
clitt/core/icons/font_awesome/app_icons.py,sha256=cMPlXVWOn5YnLmlk20nwhFgA16Ug2jFiHZPYlwwH724,1662
|
|
46
46
|
clitt/core/icons/font_awesome/awesome.py,sha256=oEOi0DWj0yvJbmg18fXE5hgSrMmCckTYC0cbgw1cyQQ,1831
|
|
47
47
|
clitt/core/icons/font_awesome/control_icons.py,sha256=OuAnvXRIusxDRVnPHxltTn4V6tF_uc2gfoqoQWB7Bks,1075
|
|
@@ -49,41 +49,42 @@ clitt/core/icons/font_awesome/dashboard_icons.py,sha256=MGXLJ-I_xXalOaLeWHkaZe5Z
|
|
|
49
49
|
clitt/core/icons/font_awesome/form_icons.py,sha256=mDF_0nFJ9VYO2ZoXWSTETm7qEd03dcIgeCUpFnH0sLI,2314
|
|
50
50
|
clitt/core/icons/font_awesome/nav_icons.py,sha256=A3GqJuwD-RKybY67THimI1oqcNlDSEaA_Hdy8LSzfqo,1105
|
|
51
51
|
clitt/core/icons/font_awesome/widget_icons.py,sha256=Oho4jPezyMSw-tKWSjpdK3dv-FK2GC_62liNeThhhAs,1024
|
|
52
|
-
clitt/core/tui/__init__.py,sha256=
|
|
52
|
+
clitt/core/tui/__init__.py,sha256=u5pdP4mRJuBtwBIeM3UGVUZgcIYzxFNzBe_35lJnuDw,311
|
|
53
53
|
clitt/core/tui/tui_application.py,sha256=kBYc_IUrVrZ6v7MZ6A8QJ9hONDpXwqO9TqJa_XBsM8E,1611
|
|
54
54
|
clitt/core/tui/tui_component.py,sha256=3GXfU7AO9oi7VnM8MAClieZUyTU9s9vkrv0Pwce_0hM,4733
|
|
55
55
|
clitt/core/tui/tui_preferences.py,sha256=C3cXqwBykE8Sw3Brn-s4nBxr5gDgSslS7bD1g67rtBw,4731
|
|
56
56
|
clitt/core/tui/tui_screen.py,sha256=I043SGE7d2OQZDwjLsuvyUO7ucwYHmtyfx54FL52ejE,8374
|
|
57
|
-
clitt/core/tui/mchoose/__init__.py,sha256=
|
|
57
|
+
clitt/core/tui/mchoose/__init__.py,sha256=6dXoUnpOlyYyfoJRIXmJ508vCnRJu49zSHaozD8F4nM,189
|
|
58
58
|
clitt/core/tui/mchoose/mchoose.py,sha256=SsflRDJUAB2opK1BeZXBba6zFYuqe1prflaePX21Xfk,1197
|
|
59
59
|
clitt/core/tui/mchoose/menu_choose.py,sha256=kvEvuADZDk_lRqC7jH_cWtJGiNZjdh_765x27cu8ySo,7016
|
|
60
|
-
clitt/core/tui/mdashboard/__init__.py,sha256=
|
|
60
|
+
clitt/core/tui/mdashboard/__init__.py,sha256=irSTrEeG6efAutKKGlKeRFHWWD0ubDZDR4JuBW7gLVU,243
|
|
61
61
|
clitt/core/tui/mdashboard/dashboard_builder.py,sha256=wWcT1UyQnOrvalV920XtmBUg5lRVbhA8hMrmxRcLrtY,1658
|
|
62
62
|
clitt/core/tui/mdashboard/dashboard_item.py,sha256=4MK6bxROWo1ZK9FlMoSm38z0qe5HoxqH0W5tt3iUT5M,886
|
|
63
63
|
clitt/core/tui/mdashboard/mdashboard.py,sha256=sUOAsrHxCIz3gjJAJOcm6ZD3Xh1uU_I5qx-6pOiJ6KU,903
|
|
64
64
|
clitt/core/tui/mdashboard/menu_dashboard.py,sha256=UyzbdhSeWV_NylryVJcjcHqW5vwFScUcaTpdCXllvXs,5547
|
|
65
|
-
clitt/core/tui/menu/__init__.py,sha256=
|
|
65
|
+
clitt/core/tui/menu/__init__.py,sha256=6X1nGM4mWttgTDKgVcJJqQXNgbRiaXvf0eOSAotPttE,272
|
|
66
66
|
clitt/core/tui/menu/tui_menu.py,sha256=wWgHr0D_jzj3l6XPwWoIfy1kCXn-zYwZn2ctqB9lTrk,3599
|
|
67
67
|
clitt/core/tui/menu/tui_menu_action.py,sha256=KqhG_YkEMWF37uoI7Q_bCr3jHLIeTbDr_-uROuhAvB0,1590
|
|
68
68
|
clitt/core/tui/menu/tui_menu_factory.py,sha256=rL33KX5yD2ppvVRAEFIAme5ka7GrBQecVr63aUf2hf0,4459
|
|
69
69
|
clitt/core/tui/menu/tui_menu_item.py,sha256=NZjlYl6nVwB0wo0fJ4OFnIo98eowLwLWHGMnSD-yTto,7482
|
|
70
70
|
clitt/core/tui/menu/tui_menu_ui.py,sha256=GK9suH6eeG4rqV_TahZ9PDDIHYpu7yETHWjzKsh_Xdo,3271
|
|
71
71
|
clitt/core/tui/menu/tui_menu_view.py,sha256=oJwdmwfbMoJ2mh_mc-IQZ4qERuz5v4hZtsHjaW34fvM,1943
|
|
72
|
-
clitt/core/tui/minput/__init__.py,sha256=
|
|
72
|
+
clitt/core/tui/minput/__init__.py,sha256=TCQd9t46b_aQqLleIKPg2bHA6457iNpR2q_3yfKzLi4,318
|
|
73
73
|
clitt/core/tui/minput/access_type.py,sha256=Dss6okfXHCoUI3_OOu2nKsgpy2NeStIwhdDvTR2z-2w,636
|
|
74
|
-
clitt/core/tui/minput/
|
|
75
|
-
clitt/core/tui/minput/
|
|
74
|
+
clitt/core/tui/minput/field_builder.py,sha256=lw-JguTb8glG7NIiyoCb6UigzlOgk7k81l5x0uyWuhI,4311
|
|
75
|
+
clitt/core/tui/minput/form_builder.py,sha256=HjwJVtrizUNgpNWA7NkpzM0ZRweFUJBHvuB5L33LD2k,2866
|
|
76
|
+
clitt/core/tui/minput/form_field.py,sha256=3xYglKDAQj-YFyRGvUWgl9fiF4jJjA7tN4sgYfg6vzQ,5776
|
|
76
77
|
clitt/core/tui/minput/input_type.py,sha256=O6-jk_QSjjRGeHsGVzIDZSCLg739f2xtnVV_ElcHYpA,722
|
|
77
|
-
clitt/core/tui/minput/input_validator.py,sha256=
|
|
78
|
-
clitt/core/tui/minput/menu_input.py,sha256=
|
|
78
|
+
clitt/core/tui/minput/input_validator.py,sha256=z6CNtWFx90wDeYgLilIdPeZC23aub804fCBd_95wKAA,3438
|
|
79
|
+
clitt/core/tui/minput/menu_input.py,sha256=usLRCnjheA7dfoinEZIflsRiwIo0yV6XHrsGqmgCL-w,12535
|
|
79
80
|
clitt/core/tui/minput/minput.py,sha256=no3J-eIhlalvvEDmfjMLX2Gi197PKqSY28tur6oePBk,1545
|
|
80
|
-
clitt/core/tui/minput/minput_utils.py,sha256=
|
|
81
|
-
clitt/core/tui/mselect/__init__.py,sha256=
|
|
81
|
+
clitt/core/tui/minput/minput_utils.py,sha256=raA0KE7NaFesR4W1MNeevaDEep0O0XBRYguDtsf2Pbo,5374
|
|
82
|
+
clitt/core/tui/mselect/__init__.py,sha256=ZmwlV0J1fKnTnxbQ__Mkud2C_pK2LFtd_Fuh-1arW8g,189
|
|
82
83
|
clitt/core/tui/mselect/menu_select.py,sha256=059o7lN81dAO5su7iO2tzIjRpmoerRekGXzFlWNGw5w,6176
|
|
83
84
|
clitt/core/tui/mselect/mselect.py,sha256=2vuIHjbjBQq0XtIUKxX5ku7hPv3iLBwgEX4Sa82cOc8,1059
|
|
84
|
-
clitt/core/tui/table/__init__.py,sha256=
|
|
85
|
+
clitt/core/tui/table/__init__.py,sha256=PqbtbZNr2NdkmlY1iK_uGE9p-zYrFPqQZ9kfeY0yPiY,176
|
|
85
86
|
clitt/core/tui/table/table_renderer.py,sha256=qOkcmG7LWYzI8-qTkAVTNogSIr-S7lND41c7hvV3suw,8270
|
|
86
|
-
hspylib_clitt-0.9.
|
|
87
|
-
hspylib_clitt-0.9.
|
|
88
|
-
hspylib_clitt-0.9.
|
|
89
|
-
hspylib_clitt-0.9.
|
|
87
|
+
hspylib_clitt-0.9.34.dist-info/METADATA,sha256=D7b2w0lR9ZIBGUT5ZW3PWOmWzkiPdWp8aP1Ushaaf-s,1728
|
|
88
|
+
hspylib_clitt-0.9.34.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
89
|
+
hspylib_clitt-0.9.34.dist-info/top_level.txt,sha256=vrnpkFU7dZ-vH7x5xdcY1vJSuboTqPBUcc3jD6SJkAo,6
|
|
90
|
+
hspylib_clitt-0.9.34.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|