xulbux 1.6.4__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 +2 -2
- xulbux/_cli_.py +9 -9
- xulbux/_consts_.py +71 -58
- xulbux/xx_code.py +37 -42
- xulbux/xx_color.py +49 -79
- xulbux/xx_console.py +197 -109
- xulbux/xx_data.py +177 -126
- xulbux/xx_env_path.py +1 -5
- xulbux/xx_file.py +1 -1
- xulbux/xx_format_codes.py +40 -38
- xulbux/xx_json.py +2 -5
- xulbux/xx_path.py +5 -5
- xulbux/xx_regex.py +18 -20
- xulbux/xx_string.py +28 -82
- xulbux/xx_system.py +5 -13
- {xulbux-1.6.4.dist-info → xulbux-1.6.6.dist-info}/METADATA +19 -17
- xulbux-1.6.6.dist-info/RECORD +21 -0
- xulbux-1.6.4.dist-info/RECORD +0 -21
- {xulbux-1.6.4.dist-info → xulbux-1.6.6.dist-info}/LICENSE +0 -0
- {xulbux-1.6.4.dist-info → xulbux-1.6.6.dist-info}/WHEEL +0 -0
- {xulbux-1.6.4.dist-info → xulbux-1.6.6.dist-info}/entry_points.txt +0 -0
- {xulbux-1.6.4.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
|
-
- `end` -⠀something to print after the log is printed (e.g. `\\n
|
|
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,108 +142,154 @@ class Console:
|
|
|
129
142
|
def debug(
|
|
130
143
|
prompt: object = "Point in program reached.",
|
|
131
144
|
active: bool = True,
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
145
|
+
format_linebreaks: bool = True,
|
|
146
|
+
start: str = "",
|
|
147
|
+
end: str = "\n",
|
|
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.",
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
163
|
+
format_linebreaks: bool = True,
|
|
164
|
+
start: str = "",
|
|
165
|
+
end: str = "\n",
|
|
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.",
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
179
|
+
format_linebreaks: bool = True,
|
|
180
|
+
start: str = "",
|
|
181
|
+
end: str = "\n",
|
|
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.",
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
195
|
+
format_linebreaks: bool = True,
|
|
196
|
+
start: str = "",
|
|
197
|
+
end: str = "\n",
|
|
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.",
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
211
|
+
format_linebreaks: bool = True,
|
|
212
|
+
start: str = "",
|
|
213
|
+
end: str = "\n",
|
|
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.",
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
228
|
+
format_linebreaks: bool = True,
|
|
229
|
+
start: str = "",
|
|
230
|
+
end: str = "\n",
|
|
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
|
|
|
242
|
+
@staticmethod
|
|
243
|
+
def log_box(
|
|
244
|
+
*values: object,
|
|
245
|
+
start: str = "",
|
|
246
|
+
end: str = "\n",
|
|
247
|
+
box_bg_color: str | hexa | rgba = "green",
|
|
248
|
+
default_color: hexa | rgba = "#000",
|
|
249
|
+
w_padding: int = 2,
|
|
250
|
+
w_full: bool = False,
|
|
251
|
+
) -> None:
|
|
252
|
+
"""Will print a box, containing a formatted log message:
|
|
253
|
+
- `*values` -⠀the box content (each value is on a new line)
|
|
254
|
+
- `start` -⠀something to print before the log box is printed
|
|
255
|
+
- `end` -⠀something to print after the log box is printed (e.g. `\\n`)
|
|
256
|
+
- `box_bg_color` -⠀the box's background color
|
|
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()]
|
|
264
|
+
unfmt_lines = [FormatCodes.remove_formatting(line) for line in lines]
|
|
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
|
|
267
|
+
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)
|
|
270
|
+
]
|
|
271
|
+
pady = " " * (Console.w if w_full else max_line_len + (2 * w_padding))
|
|
272
|
+
FormatCodes.print(
|
|
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]",
|
|
276
|
+
default_color=default_color,
|
|
277
|
+
sep="\n",
|
|
278
|
+
end=end,
|
|
279
|
+
)
|
|
280
|
+
|
|
222
281
|
@staticmethod
|
|
223
282
|
def confirm(
|
|
224
283
|
prompt: object = "Do you want to continue?",
|
|
225
|
-
start="
|
|
284
|
+
start="",
|
|
226
285
|
end="\n",
|
|
227
|
-
default_color: hexa | rgba =
|
|
286
|
+
default_color: hexa | rgba = COLOR.cyan,
|
|
228
287
|
default_is_yes: bool = True,
|
|
229
288
|
) -> bool:
|
|
230
289
|
"""Ask a yes/no question.\n
|
|
231
|
-
|
|
232
|
-
The
|
|
233
|
-
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."""
|
|
234
293
|
confirmed = input(
|
|
235
294
|
FormatCodes.to_ansi(
|
|
236
295
|
f'{start} {str(prompt)} [_|dim](({"Y" if default_is_yes else "y"}/{"n" if default_is_yes else "N"}): )',
|
|
@@ -244,21 +303,24 @@ class Console:
|
|
|
244
303
|
@staticmethod
|
|
245
304
|
def restricted_input(
|
|
246
305
|
prompt: object = "",
|
|
306
|
+
start="",
|
|
307
|
+
end="\n",
|
|
308
|
+
default_color: hexa | rgba = COLOR.cyan,
|
|
247
309
|
allowed_chars: str = CHARS.all,
|
|
248
310
|
min_len: int = None,
|
|
249
311
|
max_len: int = None,
|
|
250
312
|
mask_char: str = None,
|
|
251
313
|
reset_ansi: bool = True,
|
|
252
|
-
) -> str
|
|
314
|
+
) -> Optional[str]:
|
|
253
315
|
"""Acts like a standard Python `input()` with the advantage, that you can specify:
|
|
254
316
|
- what text characters the user is allowed to type and
|
|
255
317
|
- the minimum and/or maximum length of the users input
|
|
256
318
|
- optional mask character (hide user input, e.g. for passwords)
|
|
257
319
|
- reset the ANSI formatting codes after the user continues\n
|
|
258
|
-
|
|
320
|
+
---------------------------------------------------------------------------------------
|
|
259
321
|
The input can be formatted with special formatting codes. For more detailed
|
|
260
|
-
information about formatting codes, see the `xx_format_codes`
|
|
261
|
-
FormatCodes.print(prompt, end=""
|
|
322
|
+
information about formatting codes, see the `xx_format_codes` module documentation."""
|
|
323
|
+
FormatCodes.print(start + prompt, default_color=default_color, end="")
|
|
262
324
|
result = ""
|
|
263
325
|
select_all = False
|
|
264
326
|
last_line_count = 1
|
|
@@ -266,22 +328,17 @@ class Console:
|
|
|
266
328
|
|
|
267
329
|
def update_display(console_width: int) -> None:
|
|
268
330
|
nonlocal select_all, last_line_count, last_console_width
|
|
269
|
-
lines = String.split_count(
|
|
270
|
-
str(prompt) + (mask_char * len(result) if mask_char else result),
|
|
271
|
-
console_width,
|
|
272
|
-
)
|
|
331
|
+
lines = String.split_count(str(prompt) + (mask_char * len(result) if mask_char else result), console_width)
|
|
273
332
|
line_count = len(lines)
|
|
274
333
|
if (line_count > 1 or line_count < last_line_count) and not last_line_count == 1:
|
|
275
334
|
if last_console_width > console_width:
|
|
276
335
|
line_count *= 2
|
|
277
|
-
for _ in range(
|
|
278
|
-
|
|
279
|
-
if line_count < last_line_count and not line_count > last_line_count
|
|
280
|
-
else (line_count - 2 if line_count > last_line_count else line_count - 1)
|
|
281
|
-
):
|
|
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)):
|
|
282
338
|
_sys.stdout.write("\033[2K\r\033[A")
|
|
283
339
|
prompt_len = len(str(prompt)) if prompt else 0
|
|
284
|
-
prompt_str
|
|
340
|
+
prompt_str = lines[0][:prompt_len]
|
|
341
|
+
input_str = (
|
|
285
342
|
lines[0][prompt_len:] if len(lines) == 1 else "\n".join([lines[0][prompt_len:]] + lines[1:])
|
|
286
343
|
) # SEPARATE THE PROMPT AND THE INPUT
|
|
287
344
|
_sys.stdout.write(
|
|
@@ -292,7 +349,7 @@ class Console:
|
|
|
292
349
|
def handle_enter():
|
|
293
350
|
if min_len is not None and len(result) < min_len:
|
|
294
351
|
return False
|
|
295
|
-
FormatCodes.print("[_]" if reset_ansi else
|
|
352
|
+
FormatCodes.print(f"[_]{end}" if reset_ansi else end, default_color=default_color)
|
|
296
353
|
return True
|
|
297
354
|
|
|
298
355
|
def handle_backspace_delete():
|
|
@@ -301,7 +358,7 @@ class Console:
|
|
|
301
358
|
result, select_all = "", False
|
|
302
359
|
elif result and event.name == "backspace":
|
|
303
360
|
result = result[:-1]
|
|
304
|
-
update_display(Console.w
|
|
361
|
+
update_display(Console.w)
|
|
305
362
|
|
|
306
363
|
def handle_paste():
|
|
307
364
|
nonlocal result, select_all
|
|
@@ -310,25 +367,18 @@ class Console:
|
|
|
310
367
|
filtered_text = "".join(char for char in _pyperclip.paste() if allowed_chars == CHARS.all or char in allowed_chars)
|
|
311
368
|
if max_len is None or len(result) + len(filtered_text) <= max_len:
|
|
312
369
|
result += filtered_text
|
|
313
|
-
update_display(Console.w
|
|
370
|
+
update_display(Console.w)
|
|
314
371
|
|
|
315
372
|
def handle_select_all():
|
|
316
373
|
nonlocal select_all
|
|
317
374
|
select_all = True
|
|
318
|
-
update_display(Console.w
|
|
319
|
-
|
|
320
|
-
def handle_copy():
|
|
321
|
-
nonlocal select_all
|
|
322
|
-
with suppress(KeyboardInterrupt):
|
|
323
|
-
select_all = False
|
|
324
|
-
update_display(Console.w())
|
|
325
|
-
_pyperclip.copy(result)
|
|
375
|
+
update_display(Console.w)
|
|
326
376
|
|
|
327
377
|
def handle_character_input():
|
|
328
378
|
nonlocal result
|
|
329
379
|
if (allowed_chars == CHARS.all or event.name in allowed_chars) and (max_len is None or len(result) < max_len):
|
|
330
380
|
result += event.name
|
|
331
|
-
update_display(Console.w
|
|
381
|
+
update_display(Console.w)
|
|
332
382
|
|
|
333
383
|
while True:
|
|
334
384
|
event = _keyboard.read_event()
|
|
@@ -341,8 +391,8 @@ class Console:
|
|
|
341
391
|
handle_paste()
|
|
342
392
|
elif event.name == "a" and _keyboard.is_pressed("ctrl"):
|
|
343
393
|
handle_select_all()
|
|
344
|
-
elif event.name == "c" and _keyboard.is_pressed("ctrl")
|
|
345
|
-
|
|
394
|
+
elif event.name == "c" and _keyboard.is_pressed("ctrl"):
|
|
395
|
+
raise KeyboardInterrupt
|
|
346
396
|
elif event.name == "esc":
|
|
347
397
|
return None
|
|
348
398
|
elif event.name == "space":
|
|
@@ -351,16 +401,54 @@ class Console:
|
|
|
351
401
|
handle_character_input()
|
|
352
402
|
else:
|
|
353
403
|
select_all = False
|
|
354
|
-
update_display(Console.w
|
|
404
|
+
update_display(Console.w)
|
|
355
405
|
|
|
356
406
|
@staticmethod
|
|
357
407
|
def pwd_input(
|
|
358
408
|
prompt: object = "Password: ",
|
|
409
|
+
start="",
|
|
410
|
+
end="\n",
|
|
411
|
+
default_color: hexa | rgba = COLOR.cyan,
|
|
359
412
|
allowed_chars: str = CHARS.standard_ascii,
|
|
360
413
|
min_len: int = None,
|
|
361
414
|
max_len: int = None,
|
|
362
|
-
|
|
415
|
+
reset_ansi: bool = True,
|
|
363
416
|
) -> str:
|
|
364
417
|
"""Password input (preset for `Console.restricted_input()`)
|
|
365
418
|
that always masks the entered characters with asterisks."""
|
|
366
|
-
return Console.restricted_input(prompt, 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
|