xulbux 1.6.5__py3-none-any.whl → 1.6.6__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 xulbux might be problematic. Click here for more details.
- xulbux/__init__.py +1 -1
- xulbux/_cli_.py +9 -9
- xulbux/_consts_.py +65 -52
- xulbux/xx_code.py +37 -42
- xulbux/xx_color.py +27 -55
- xulbux/xx_console.py +154 -106
- xulbux/xx_data.py +176 -125
- xulbux/xx_env_path.py +1 -5
- xulbux/xx_format_codes.py +26 -37
- xulbux/xx_json.py +2 -5
- xulbux/xx_path.py +5 -5
- xulbux/xx_regex.py +18 -20
- xulbux/xx_string.py +23 -76
- xulbux/xx_system.py +5 -13
- {xulbux-1.6.5.dist-info → xulbux-1.6.6.dist-info}/METADATA +3 -1
- xulbux-1.6.6.dist-info/RECORD +21 -0
- xulbux-1.6.5.dist-info/RECORD +0 -21
- {xulbux-1.6.5.dist-info → xulbux-1.6.6.dist-info}/LICENSE +0 -0
- {xulbux-1.6.5.dist-info → xulbux-1.6.6.dist-info}/WHEEL +0 -0
- {xulbux-1.6.5.dist-info → xulbux-1.6.6.dist-info}/entry_points.txt +0 -0
- {xulbux-1.6.5.dist-info → xulbux-1.6.6.dist-info}/top_level.txt +0 -0
xulbux/xx_console.py
CHANGED
|
@@ -1,31 +1,18 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Functions for logging and other small actions within the console
|
|
3
|
-
|
|
4
|
-
- `Console.user()`
|
|
5
|
-
- `Console.is_admin()`
|
|
6
|
-
- `Console.pause_exit()`
|
|
7
|
-
- `Console.cls()`
|
|
8
|
-
- `Console.log()`
|
|
9
|
-
- `Console.debug()`
|
|
10
|
-
- `Console.info()`
|
|
11
|
-
- `Console.done()`
|
|
12
|
-
- `Console.warn()`
|
|
13
|
-
- `Console.fail()`
|
|
14
|
-
- `Console.exit()`
|
|
15
|
-
- `Console.confirm()`
|
|
16
|
-
- `Console.restricted_input()`
|
|
17
|
-
- `Console.pwd_input()`\n
|
|
18
|
-
------------------------------------------------------------------------------------------------------
|
|
2
|
+
Functions for logging and other small actions within the console.\n
|
|
3
|
+
----------------------------------------------------------------------------------------------------------
|
|
19
4
|
You can also use special formatting codes directly inside the log message to change their appearance.
|
|
20
|
-
For more detailed information about formatting codes, see the the `xx_format_codes`
|
|
5
|
+
For more detailed information about formatting codes, see the the `xx_format_codes` module documentation.
|
|
21
6
|
"""
|
|
22
7
|
|
|
23
|
-
from ._consts_ import
|
|
24
|
-
from .xx_format_codes import FormatCodes
|
|
8
|
+
from ._consts_ import COLOR, CHARS
|
|
9
|
+
from .xx_format_codes import FormatCodes, _COMPILED
|
|
25
10
|
from .xx_string import String
|
|
26
|
-
from .xx_color import
|
|
11
|
+
from .xx_color import Color, rgba, hexa
|
|
27
12
|
|
|
28
|
-
from
|
|
13
|
+
from prompt_toolkit.key_binding.key_bindings import KeyBindings
|
|
14
|
+
from typing import Optional
|
|
15
|
+
import prompt_toolkit as _prompt_toolkit
|
|
29
16
|
import pyperclip as _pyperclip
|
|
30
17
|
import keyboard as _keyboard
|
|
31
18
|
import getpass as _getpass
|
|
@@ -35,8 +22,33 @@ import sys as _sys
|
|
|
35
22
|
import os as _os
|
|
36
23
|
|
|
37
24
|
|
|
25
|
+
# YAPF: disable
|
|
26
|
+
class _ConsoleWidth:
|
|
27
|
+
def __get__(self, obj, owner=None):
|
|
28
|
+
return _os.get_terminal_size().columns
|
|
29
|
+
|
|
30
|
+
class _ConsoleHeight:
|
|
31
|
+
def __get__(self, obj, owner=None):
|
|
32
|
+
return _os.get_terminal_size().lines
|
|
33
|
+
|
|
34
|
+
class _ConsoleSize:
|
|
35
|
+
def __get__(self, obj, owner=None):
|
|
36
|
+
size = _os.get_terminal_size()
|
|
37
|
+
return (size.columns, size.lines)
|
|
38
|
+
|
|
39
|
+
class _ConsoleUser:
|
|
40
|
+
def __get__(self, obj, owner=None):
|
|
41
|
+
return _os.getenv("USER") or _os.getenv("USERNAME") or _getpass.getuser()
|
|
42
|
+
# YAPF: enable
|
|
43
|
+
|
|
44
|
+
|
|
38
45
|
class Console:
|
|
39
46
|
|
|
47
|
+
w: int = _ConsoleWidth()
|
|
48
|
+
h: int = _ConsoleHeight()
|
|
49
|
+
wh: tuple[int, int] = _ConsoleSize()
|
|
50
|
+
usr: str = _ConsoleUser()
|
|
51
|
+
|
|
40
52
|
@staticmethod
|
|
41
53
|
def get_args(find_args: dict) -> dict[str, dict[str, any]]:
|
|
42
54
|
args = _sys.argv[1:]
|
|
@@ -54,18 +66,6 @@ class Console:
|
|
|
54
66
|
results[arg_key] = {"exists": exists, "value": value}
|
|
55
67
|
return results
|
|
56
68
|
|
|
57
|
-
def w() -> int:
|
|
58
|
-
return getattr(_shutil.get_terminal_size(), "columns", 80)
|
|
59
|
-
|
|
60
|
-
def h() -> int:
|
|
61
|
-
return getattr(_shutil.get_terminal_size(), "lines", 24)
|
|
62
|
-
|
|
63
|
-
def wh() -> tuple[int, int]:
|
|
64
|
-
return Console.w(), Console.h()
|
|
65
|
-
|
|
66
|
-
def user() -> str:
|
|
67
|
-
return _os.getenv("USER") or _os.getenv("USERNAME") or _getpass.getuser()
|
|
68
|
-
|
|
69
69
|
@staticmethod
|
|
70
70
|
def pause_exit(
|
|
71
71
|
pause: bool = False,
|
|
@@ -84,6 +84,7 @@ class Console:
|
|
|
84
84
|
if exit:
|
|
85
85
|
_sys.exit(exit_code)
|
|
86
86
|
|
|
87
|
+
@staticmethod
|
|
87
88
|
def cls() -> None:
|
|
88
89
|
"""Will clear the console in addition to completely resetting the ANSI formats."""
|
|
89
90
|
if _shutil.which("cls"):
|
|
@@ -94,33 +95,45 @@ class Console:
|
|
|
94
95
|
|
|
95
96
|
@staticmethod
|
|
96
97
|
def log(
|
|
97
|
-
title: str,
|
|
98
|
+
title: Optional[str] = None,
|
|
98
99
|
prompt: object = "",
|
|
100
|
+
format_linebreaks: bool = True,
|
|
99
101
|
start: str = "",
|
|
100
102
|
end: str = "\n",
|
|
101
103
|
title_bg_color: hexa | rgba = None,
|
|
102
104
|
default_color: hexa | rgba = None,
|
|
105
|
+
_console_tabsize: int = 8,
|
|
103
106
|
) -> None:
|
|
104
107
|
"""Will print a formatted log message:
|
|
105
108
|
- `title` -⠀the title of the log message (e.g. `DEBUG`, `WARN`, `FAIL`, etc.)
|
|
106
109
|
- `prompt` -⠀the log message
|
|
110
|
+
- `format_linebreaks` -⠀whether to format (indent after) the line breaks or not
|
|
107
111
|
- `start` -⠀something to print before the log is printed
|
|
108
112
|
- `end` -⠀something to print after the log is printed (e.g. `\\n`)
|
|
109
113
|
- `title_bg_color` -⠀the background color of the `title`
|
|
110
114
|
- `default_color` -⠀the default text color of the `prompt`\n
|
|
111
|
-
|
|
112
|
-
The log message
|
|
113
|
-
information about formatting codes, see `xx_format_codes`
|
|
115
|
+
-----------------------------------------------------------------------------------
|
|
116
|
+
The log message can be formatted with special formatting codes. For more detailed
|
|
117
|
+
information about formatting codes, see `xx_format_codes` module documentation."""
|
|
118
|
+
title = "" if title is None else title.strip().upper()
|
|
119
|
+
title_len, tab_len = len(title) + 4, _console_tabsize - ((len(title) + 4) % _console_tabsize)
|
|
114
120
|
title_color = "_color" if not title_bg_color else Color.text_color_for_on_bg(title_bg_color)
|
|
115
|
-
if
|
|
121
|
+
if format_linebreaks:
|
|
122
|
+
prompt_lst = (String.split_count(l, Console.w - (title_len + tab_len)) for l in str(prompt).splitlines())
|
|
123
|
+
prompt_lst = (item for lst in prompt_lst for item in (lst if isinstance(lst, list) else [lst]))
|
|
124
|
+
prompt = f"\n{' ' * title_len}\t".join(prompt_lst)
|
|
125
|
+
else:
|
|
126
|
+
prompt = str(prompt)
|
|
127
|
+
if title == "":
|
|
116
128
|
FormatCodes.print(
|
|
117
|
-
f'{start}
|
|
129
|
+
f'{start} {f"[{default_color}]" if default_color else ""}{str(prompt)}[_]',
|
|
118
130
|
default_color=default_color,
|
|
119
131
|
end=end,
|
|
120
132
|
)
|
|
121
133
|
else:
|
|
122
134
|
FormatCodes.print(
|
|
123
|
-
f'{start} {f"[{
|
|
135
|
+
f'{start} [bold][{title_color}]{f"[BG:{title_bg_color}]" if title_bg_color else ""} {title} [_]'
|
|
136
|
+
+ f'\t{f"[{default_color}]" if default_color else ""}{prompt}[_]',
|
|
124
137
|
default_color=default_color,
|
|
125
138
|
end=end,
|
|
126
139
|
)
|
|
@@ -129,94 +142,101 @@ class Console:
|
|
|
129
142
|
def debug(
|
|
130
143
|
prompt: object = "Point in program reached.",
|
|
131
144
|
active: bool = True,
|
|
145
|
+
format_linebreaks: bool = True,
|
|
132
146
|
start: str = "",
|
|
133
147
|
end: str = "\n",
|
|
134
|
-
title_bg_color: hexa | rgba =
|
|
135
|
-
default_color: hexa | rgba =
|
|
148
|
+
title_bg_color: hexa | rgba = COLOR.yellow,
|
|
149
|
+
default_color: hexa | rgba = COLOR.text,
|
|
136
150
|
pause: bool = False,
|
|
137
151
|
exit: bool = False,
|
|
138
152
|
) -> None:
|
|
139
153
|
"""A preset for `log()`: `DEBUG` log message with the options to pause
|
|
140
|
-
at the message and exit the program after the message was printed.
|
|
154
|
+
at the message and exit the program after the message was printed.
|
|
155
|
+
If `active` is false, no debug message will be printed."""
|
|
141
156
|
if active:
|
|
142
|
-
Console.log("DEBUG", prompt, start, end, title_bg_color, default_color)
|
|
157
|
+
Console.log("DEBUG", prompt, format_linebreaks, start, end, title_bg_color, default_color)
|
|
143
158
|
Console.pause_exit(pause, exit)
|
|
144
159
|
|
|
145
160
|
@staticmethod
|
|
146
161
|
def info(
|
|
147
162
|
prompt: object = "Program running.",
|
|
163
|
+
format_linebreaks: bool = True,
|
|
148
164
|
start: str = "",
|
|
149
165
|
end: str = "\n",
|
|
150
|
-
title_bg_color: hexa | rgba =
|
|
151
|
-
default_color: hexa | rgba =
|
|
166
|
+
title_bg_color: hexa | rgba = COLOR.blue,
|
|
167
|
+
default_color: hexa | rgba = COLOR.text,
|
|
152
168
|
pause: bool = False,
|
|
153
169
|
exit: bool = False,
|
|
154
170
|
) -> None:
|
|
155
171
|
"""A preset for `log()`: `INFO` log message with the options to pause
|
|
156
172
|
at the message and exit the program after the message was printed."""
|
|
157
|
-
Console.log("INFO", prompt, start, end, title_bg_color, default_color)
|
|
173
|
+
Console.log("INFO", prompt, format_linebreaks, start, end, title_bg_color, default_color)
|
|
158
174
|
Console.pause_exit(pause, exit)
|
|
159
175
|
|
|
160
176
|
@staticmethod
|
|
161
177
|
def done(
|
|
162
178
|
prompt: object = "Program finished.",
|
|
179
|
+
format_linebreaks: bool = True,
|
|
163
180
|
start: str = "",
|
|
164
181
|
end: str = "\n",
|
|
165
|
-
title_bg_color: hexa | rgba =
|
|
166
|
-
default_color: hexa | rgba =
|
|
182
|
+
title_bg_color: hexa | rgba = COLOR.teal,
|
|
183
|
+
default_color: hexa | rgba = COLOR.text,
|
|
167
184
|
pause: bool = False,
|
|
168
185
|
exit: bool = False,
|
|
169
186
|
) -> None:
|
|
170
187
|
"""A preset for `log()`: `DONE` log message with the options to pause
|
|
171
188
|
at the message and exit the program after the message was printed."""
|
|
172
|
-
Console.log("DONE", prompt, start, end, title_bg_color, default_color)
|
|
189
|
+
Console.log("DONE", prompt, format_linebreaks, start, end, title_bg_color, default_color)
|
|
173
190
|
Console.pause_exit(pause, exit)
|
|
174
191
|
|
|
175
192
|
@staticmethod
|
|
176
193
|
def warn(
|
|
177
194
|
prompt: object = "Important message.",
|
|
195
|
+
format_linebreaks: bool = True,
|
|
178
196
|
start: str = "",
|
|
179
197
|
end: str = "\n",
|
|
180
|
-
title_bg_color: hexa | rgba =
|
|
181
|
-
default_color: hexa | rgba =
|
|
198
|
+
title_bg_color: hexa | rgba = COLOR.orange,
|
|
199
|
+
default_color: hexa | rgba = COLOR.text,
|
|
182
200
|
pause: bool = False,
|
|
183
201
|
exit: bool = False,
|
|
184
202
|
) -> None:
|
|
185
203
|
"""A preset for `log()`: `WARN` log message with the options to pause
|
|
186
204
|
at the message and exit the program after the message was printed."""
|
|
187
|
-
Console.log("WARN", prompt, start, end, title_bg_color, default_color)
|
|
205
|
+
Console.log("WARN", prompt, format_linebreaks, start, end, title_bg_color, default_color)
|
|
188
206
|
Console.pause_exit(pause, exit)
|
|
189
207
|
|
|
190
208
|
@staticmethod
|
|
191
209
|
def fail(
|
|
192
210
|
prompt: object = "Program error.",
|
|
211
|
+
format_linebreaks: bool = True,
|
|
193
212
|
start: str = "",
|
|
194
213
|
end: str = "\n",
|
|
195
|
-
title_bg_color: hexa | rgba =
|
|
196
|
-
default_color: hexa | rgba =
|
|
214
|
+
title_bg_color: hexa | rgba = COLOR.red,
|
|
215
|
+
default_color: hexa | rgba = COLOR.text,
|
|
197
216
|
pause: bool = False,
|
|
198
217
|
exit: bool = True,
|
|
199
218
|
reset_ansi=True,
|
|
200
219
|
) -> None:
|
|
201
220
|
"""A preset for `log()`: `FAIL` log message with the options to pause
|
|
202
221
|
at the message and exit the program after the message was printed."""
|
|
203
|
-
Console.log("FAIL", prompt, start, end, title_bg_color, default_color)
|
|
222
|
+
Console.log("FAIL", prompt, format_linebreaks, start, end, title_bg_color, default_color)
|
|
204
223
|
Console.pause_exit(pause, exit, reset_ansi=reset_ansi)
|
|
205
224
|
|
|
206
225
|
@staticmethod
|
|
207
226
|
def exit(
|
|
208
227
|
prompt: object = "Program ended.",
|
|
228
|
+
format_linebreaks: bool = True,
|
|
209
229
|
start: str = "",
|
|
210
230
|
end: str = "\n",
|
|
211
|
-
title_bg_color: hexa | rgba =
|
|
212
|
-
default_color: hexa | rgba =
|
|
231
|
+
title_bg_color: hexa | rgba = COLOR.magenta,
|
|
232
|
+
default_color: hexa | rgba = COLOR.text,
|
|
213
233
|
pause: bool = False,
|
|
214
234
|
exit: bool = True,
|
|
215
235
|
reset_ansi=True,
|
|
216
236
|
) -> None:
|
|
217
237
|
"""A preset for `log()`: `EXIT` log message with the options to pause
|
|
218
238
|
at the message and exit the program after the message was printed."""
|
|
219
|
-
Console.log("EXIT", prompt, start, end, title_bg_color, default_color)
|
|
239
|
+
Console.log("EXIT", prompt, format_linebreaks, start, end, title_bg_color, default_color)
|
|
220
240
|
Console.pause_exit(pause, exit, reset_ansi=reset_ansi)
|
|
221
241
|
|
|
222
242
|
@staticmethod
|
|
@@ -226,28 +246,33 @@ class Console:
|
|
|
226
246
|
end: str = "\n",
|
|
227
247
|
box_bg_color: str | hexa | rgba = "green",
|
|
228
248
|
default_color: hexa | rgba = "#000",
|
|
229
|
-
|
|
249
|
+
w_padding: int = 2,
|
|
250
|
+
w_full: bool = False,
|
|
230
251
|
) -> None:
|
|
231
252
|
"""Will print a box, containing a formatted log message:
|
|
232
253
|
- `*values` -⠀the box content (each value is on a new line)
|
|
233
254
|
- `start` -⠀something to print before the log box is printed
|
|
234
255
|
- `end` -⠀something to print after the log box is printed (e.g. `\\n`)
|
|
235
256
|
- `box_bg_color` -⠀the box's background color
|
|
236
|
-
- `default_color` -⠀the default text color of the `*values
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
257
|
+
- `default_color` -⠀the default text color of the `*values`
|
|
258
|
+
- `w_padding` -⠀the horizontal padding (in chars) to the box content
|
|
259
|
+
- `w_full` -⠀whether to make the box be the full console width or not\n
|
|
260
|
+
-----------------------------------------------------------------------------------
|
|
261
|
+
The box content can be formatted with special formatting codes. For more detailed
|
|
262
|
+
information about formatting codes, see `xx_format_codes` module documentation."""
|
|
263
|
+
lines = [line.strip() for val in values for line in val.splitlines()]
|
|
241
264
|
unfmt_lines = [FormatCodes.remove_formatting(line) for line in lines]
|
|
242
265
|
max_line_len = max(len(line) for line in unfmt_lines)
|
|
266
|
+
pad_w_full = (Console.w - (max_line_len + (2 * w_padding))) if w_full else 0
|
|
243
267
|
lines = [
|
|
244
|
-
f"[bg:{box_bg_color}]{' ' *
|
|
245
|
-
for line, unfmt in zip(lines, unfmt_lines)
|
|
268
|
+
f"[bg:{box_bg_color}]{' ' * w_padding}{line}" + " " *
|
|
269
|
+
((w_padding + max_line_len - len(unfmt)) + pad_w_full) + "[_bg]" for line, unfmt in zip(lines, unfmt_lines)
|
|
246
270
|
]
|
|
271
|
+
pady = " " * (Console.w if w_full else max_line_len + (2 * w_padding))
|
|
247
272
|
FormatCodes.print(
|
|
248
|
-
f"{start}[bg:{box_bg_color}]{
|
|
249
|
-
+ "\n".join(lines)
|
|
250
|
-
+ f"\n[bg:{box_bg_color}]{
|
|
273
|
+
f"{start}[bg:{box_bg_color}]{pady}[_bg]\n"
|
|
274
|
+
+ _COMPILED["formatting"].sub(lambda m: f"{m.group(0)}[bg:{box_bg_color}]", "\n".join(lines))
|
|
275
|
+
+ f"\n[bg:{box_bg_color}]{pady}[_bg]",
|
|
251
276
|
default_color=default_color,
|
|
252
277
|
sep="\n",
|
|
253
278
|
end=end,
|
|
@@ -258,13 +283,13 @@ class Console:
|
|
|
258
283
|
prompt: object = "Do you want to continue?",
|
|
259
284
|
start="",
|
|
260
285
|
end="\n",
|
|
261
|
-
default_color: hexa | rgba =
|
|
286
|
+
default_color: hexa | rgba = COLOR.cyan,
|
|
262
287
|
default_is_yes: bool = True,
|
|
263
288
|
) -> bool:
|
|
264
289
|
"""Ask a yes/no question.\n
|
|
265
|
-
|
|
266
|
-
The
|
|
267
|
-
information about formatting codes, see the `xx_format_codes`
|
|
290
|
+
---------------------------------------------------------------------------------------
|
|
291
|
+
The prompt can be formatted with special formatting codes. For more detailed
|
|
292
|
+
information about formatting codes, see the `xx_format_codes` module documentation."""
|
|
268
293
|
confirmed = input(
|
|
269
294
|
FormatCodes.to_ansi(
|
|
270
295
|
f'{start} {str(prompt)} [_|dim](({"Y" if default_is_yes else "y"}/{"n" if default_is_yes else "N"}): )',
|
|
@@ -280,21 +305,21 @@ class Console:
|
|
|
280
305
|
prompt: object = "",
|
|
281
306
|
start="",
|
|
282
307
|
end="\n",
|
|
283
|
-
default_color: hexa | rgba =
|
|
308
|
+
default_color: hexa | rgba = COLOR.cyan,
|
|
284
309
|
allowed_chars: str = CHARS.all,
|
|
285
310
|
min_len: int = None,
|
|
286
311
|
max_len: int = None,
|
|
287
312
|
mask_char: str = None,
|
|
288
313
|
reset_ansi: bool = True,
|
|
289
|
-
) -> str
|
|
314
|
+
) -> Optional[str]:
|
|
290
315
|
"""Acts like a standard Python `input()` with the advantage, that you can specify:
|
|
291
316
|
- what text characters the user is allowed to type and
|
|
292
317
|
- the minimum and/or maximum length of the users input
|
|
293
318
|
- optional mask character (hide user input, e.g. for passwords)
|
|
294
319
|
- reset the ANSI formatting codes after the user continues\n
|
|
295
|
-
|
|
320
|
+
---------------------------------------------------------------------------------------
|
|
296
321
|
The input can be formatted with special formatting codes. For more detailed
|
|
297
|
-
information about formatting codes, see the `xx_format_codes`
|
|
322
|
+
information about formatting codes, see the `xx_format_codes` module documentation."""
|
|
298
323
|
FormatCodes.print(start + prompt, default_color=default_color, end="")
|
|
299
324
|
result = ""
|
|
300
325
|
select_all = False
|
|
@@ -303,22 +328,17 @@ class Console:
|
|
|
303
328
|
|
|
304
329
|
def update_display(console_width: int) -> None:
|
|
305
330
|
nonlocal select_all, last_line_count, last_console_width
|
|
306
|
-
lines = String.split_count(
|
|
307
|
-
str(prompt) + (mask_char * len(result) if mask_char else result),
|
|
308
|
-
console_width,
|
|
309
|
-
)
|
|
331
|
+
lines = String.split_count(str(prompt) + (mask_char * len(result) if mask_char else result), console_width)
|
|
310
332
|
line_count = len(lines)
|
|
311
333
|
if (line_count > 1 or line_count < last_line_count) and not last_line_count == 1:
|
|
312
334
|
if last_console_width > console_width:
|
|
313
335
|
line_count *= 2
|
|
314
|
-
for _ in range(
|
|
315
|
-
|
|
316
|
-
if line_count < last_line_count and not line_count > last_line_count
|
|
317
|
-
else (line_count - 2 if line_count > last_line_count else line_count - 1)
|
|
318
|
-
):
|
|
336
|
+
for _ in range(line_count if line_count < last_line_count and not line_count > last_line_count else (
|
|
337
|
+
line_count - 2 if line_count > last_line_count else line_count - 1)):
|
|
319
338
|
_sys.stdout.write("\033[2K\r\033[A")
|
|
320
339
|
prompt_len = len(str(prompt)) if prompt else 0
|
|
321
|
-
prompt_str
|
|
340
|
+
prompt_str = lines[0][:prompt_len]
|
|
341
|
+
input_str = (
|
|
322
342
|
lines[0][prompt_len:] if len(lines) == 1 else "\n".join([lines[0][prompt_len:]] + lines[1:])
|
|
323
343
|
) # SEPARATE THE PROMPT AND THE INPUT
|
|
324
344
|
_sys.stdout.write(
|
|
@@ -338,7 +358,7 @@ class Console:
|
|
|
338
358
|
result, select_all = "", False
|
|
339
359
|
elif result and event.name == "backspace":
|
|
340
360
|
result = result[:-1]
|
|
341
|
-
update_display(Console.w
|
|
361
|
+
update_display(Console.w)
|
|
342
362
|
|
|
343
363
|
def handle_paste():
|
|
344
364
|
nonlocal result, select_all
|
|
@@ -347,25 +367,18 @@ class Console:
|
|
|
347
367
|
filtered_text = "".join(char for char in _pyperclip.paste() if allowed_chars == CHARS.all or char in allowed_chars)
|
|
348
368
|
if max_len is None or len(result) + len(filtered_text) <= max_len:
|
|
349
369
|
result += filtered_text
|
|
350
|
-
update_display(Console.w
|
|
370
|
+
update_display(Console.w)
|
|
351
371
|
|
|
352
372
|
def handle_select_all():
|
|
353
373
|
nonlocal select_all
|
|
354
374
|
select_all = True
|
|
355
|
-
update_display(Console.w
|
|
356
|
-
|
|
357
|
-
def handle_copy():
|
|
358
|
-
nonlocal select_all
|
|
359
|
-
with suppress(KeyboardInterrupt):
|
|
360
|
-
select_all = False
|
|
361
|
-
update_display(Console.w())
|
|
362
|
-
_pyperclip.copy(result)
|
|
375
|
+
update_display(Console.w)
|
|
363
376
|
|
|
364
377
|
def handle_character_input():
|
|
365
378
|
nonlocal result
|
|
366
379
|
if (allowed_chars == CHARS.all or event.name in allowed_chars) and (max_len is None or len(result) < max_len):
|
|
367
380
|
result += event.name
|
|
368
|
-
update_display(Console.w
|
|
381
|
+
update_display(Console.w)
|
|
369
382
|
|
|
370
383
|
while True:
|
|
371
384
|
event = _keyboard.read_event()
|
|
@@ -378,8 +391,8 @@ class Console:
|
|
|
378
391
|
handle_paste()
|
|
379
392
|
elif event.name == "a" and _keyboard.is_pressed("ctrl"):
|
|
380
393
|
handle_select_all()
|
|
381
|
-
elif event.name == "c" and _keyboard.is_pressed("ctrl")
|
|
382
|
-
|
|
394
|
+
elif event.name == "c" and _keyboard.is_pressed("ctrl"):
|
|
395
|
+
raise KeyboardInterrupt
|
|
383
396
|
elif event.name == "esc":
|
|
384
397
|
return None
|
|
385
398
|
elif event.name == "space":
|
|
@@ -388,19 +401,54 @@ class Console:
|
|
|
388
401
|
handle_character_input()
|
|
389
402
|
else:
|
|
390
403
|
select_all = False
|
|
391
|
-
update_display(Console.w
|
|
404
|
+
update_display(Console.w)
|
|
392
405
|
|
|
393
406
|
@staticmethod
|
|
394
407
|
def pwd_input(
|
|
395
408
|
prompt: object = "Password: ",
|
|
396
409
|
start="",
|
|
397
410
|
end="\n",
|
|
398
|
-
default_color: hexa | rgba =
|
|
411
|
+
default_color: hexa | rgba = COLOR.cyan,
|
|
399
412
|
allowed_chars: str = CHARS.standard_ascii,
|
|
400
413
|
min_len: int = None,
|
|
401
414
|
max_len: int = None,
|
|
402
|
-
|
|
415
|
+
reset_ansi: bool = True,
|
|
403
416
|
) -> str:
|
|
404
417
|
"""Password input (preset for `Console.restricted_input()`)
|
|
405
418
|
that always masks the entered characters with asterisks."""
|
|
406
|
-
return Console.restricted_input(prompt, start, end, default_color, allowed_chars, min_len, max_len, "*",
|
|
419
|
+
return Console.restricted_input(prompt, start, end, default_color, allowed_chars, min_len, max_len, "*", reset_ansi)
|
|
420
|
+
|
|
421
|
+
@staticmethod
|
|
422
|
+
def multiline_input(
|
|
423
|
+
prompt: object = "",
|
|
424
|
+
start="",
|
|
425
|
+
end="\n",
|
|
426
|
+
default_color: hexa | rgba = COLOR.cyan,
|
|
427
|
+
show_keybindings=True,
|
|
428
|
+
input_prefix=" ⤷ ",
|
|
429
|
+
reset_ansi=True,
|
|
430
|
+
) -> str:
|
|
431
|
+
"""An input where users can input (and paste) text over multiple lines.\n
|
|
432
|
+
-----------------------------------------------------------------------------------
|
|
433
|
+
- `prompt` -⠀the input prompt
|
|
434
|
+
- `start` -⠀something to print before the input
|
|
435
|
+
- `end` -⠀something to print after the input (e.g. `\\n`)
|
|
436
|
+
- `default_color` -⠀the default text color of the `prompt`
|
|
437
|
+
- `show_keybindings` -⠀whether to show the special keybindings or not
|
|
438
|
+
- `input_prefix` -⠀the prefix of the input line
|
|
439
|
+
- `reset_ansi` -⠀whether to reset the ANSI codes after the input or not
|
|
440
|
+
-----------------------------------------------------------------------------------
|
|
441
|
+
The input prompt can be formatted with special formatting codes. For more detailed
|
|
442
|
+
information about formatting codes, see `xx_format_codes` module documentation."""
|
|
443
|
+
kb = KeyBindings()
|
|
444
|
+
|
|
445
|
+
@kb.add("c-d", eager=True) # CTRL+D
|
|
446
|
+
def _(event):
|
|
447
|
+
event.app.exit(result=event.app.current_buffer.document.text)
|
|
448
|
+
|
|
449
|
+
FormatCodes.print(start + prompt, default_color=default_color)
|
|
450
|
+
if show_keybindings:
|
|
451
|
+
FormatCodes.print("[dim][[b](CTRL+D)[dim] : end of input][_dim]")
|
|
452
|
+
input_string = _prompt_toolkit.prompt(input_prefix, multiline=True, wrap_lines=True, key_bindings=kb)
|
|
453
|
+
FormatCodes.print("[_]" if reset_ansi else "", end=end[1:] if end.startswith("\n") else end)
|
|
454
|
+
return input_string
|