meerschaum 2.9.4__py3-none-any.whl → 3.0.0__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.
- meerschaum/__init__.py +5 -2
- meerschaum/_internal/__init__.py +1 -0
- meerschaum/_internal/arguments/_parse_arguments.py +4 -4
- meerschaum/_internal/arguments/_parser.py +33 -4
- meerschaum/_internal/cli/__init__.py +6 -0
- meerschaum/_internal/cli/daemons.py +103 -0
- meerschaum/_internal/cli/entry.py +220 -0
- meerschaum/_internal/cli/workers.py +435 -0
- meerschaum/_internal/docs/index.py +48 -2
- meerschaum/_internal/entry.py +50 -14
- meerschaum/_internal/shell/Shell.py +121 -29
- meerschaum/_internal/shell/__init__.py +4 -1
- meerschaum/_internal/static.py +359 -0
- meerschaum/_internal/term/TermPageHandler.py +1 -2
- meerschaum/_internal/term/__init__.py +40 -6
- meerschaum/_internal/term/tools.py +33 -8
- meerschaum/actions/__init__.py +6 -4
- meerschaum/actions/api.py +53 -13
- meerschaum/actions/attach.py +1 -0
- meerschaum/actions/bootstrap.py +8 -8
- meerschaum/actions/delete.py +4 -2
- meerschaum/actions/edit.py +171 -25
- meerschaum/actions/login.py +8 -8
- meerschaum/actions/register.py +143 -6
- meerschaum/actions/reload.py +22 -5
- meerschaum/actions/restart.py +14 -0
- meerschaum/actions/show.py +184 -31
- meerschaum/actions/start.py +166 -17
- meerschaum/actions/stop.py +38 -2
- meerschaum/actions/sync.py +7 -2
- meerschaum/actions/tag.py +9 -8
- meerschaum/actions/verify.py +5 -8
- meerschaum/api/__init__.py +45 -15
- meerschaum/api/_events.py +46 -4
- meerschaum/api/_oauth2.py +162 -9
- meerschaum/api/_tokens.py +102 -0
- meerschaum/api/dash/__init__.py +0 -3
- meerschaum/api/dash/callbacks/__init__.py +1 -0
- meerschaum/api/dash/callbacks/custom.py +4 -3
- meerschaum/api/dash/callbacks/dashboard.py +228 -117
- meerschaum/api/dash/callbacks/jobs.py +14 -7
- meerschaum/api/dash/callbacks/login.py +10 -1
- meerschaum/api/dash/callbacks/pipes.py +194 -14
- meerschaum/api/dash/callbacks/plugins.py +0 -1
- meerschaum/api/dash/callbacks/register.py +10 -3
- meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
- meerschaum/api/dash/callbacks/tokens.py +389 -0
- meerschaum/api/dash/components.py +36 -15
- meerschaum/api/dash/jobs.py +1 -1
- meerschaum/api/dash/keys.py +35 -93
- meerschaum/api/dash/pages/__init__.py +2 -1
- meerschaum/api/dash/pages/dashboard.py +1 -20
- meerschaum/api/dash/pages/{job.py → jobs.py} +10 -7
- meerschaum/api/dash/pages/login.py +2 -2
- meerschaum/api/dash/pages/pipes.py +16 -5
- meerschaum/api/dash/pages/settings/password_reset.py +1 -1
- meerschaum/api/dash/pages/tokens.py +53 -0
- meerschaum/api/dash/pipes.py +438 -88
- meerschaum/api/dash/sessions.py +12 -0
- meerschaum/api/dash/tokens.py +603 -0
- meerschaum/api/dash/websockets.py +1 -1
- meerschaum/api/dash/webterm.py +18 -6
- meerschaum/api/models/__init__.py +23 -3
- meerschaum/api/models/_actions.py +22 -0
- meerschaum/api/models/_pipes.py +91 -7
- meerschaum/api/models/_tokens.py +81 -0
- meerschaum/api/resources/static/css/dash.css +16 -0
- meerschaum/api/resources/static/js/terminado.js +3 -0
- meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
- meerschaum/api/resources/templates/termpage.html +13 -0
- meerschaum/api/routes/__init__.py +1 -0
- meerschaum/api/routes/_actions.py +3 -4
- meerschaum/api/routes/_connectors.py +3 -7
- meerschaum/api/routes/_jobs.py +26 -35
- meerschaum/api/routes/_login.py +120 -15
- meerschaum/api/routes/_misc.py +5 -10
- meerschaum/api/routes/_pipes.py +178 -143
- meerschaum/api/routes/_plugins.py +38 -28
- meerschaum/api/routes/_tokens.py +236 -0
- meerschaum/api/routes/_users.py +47 -35
- meerschaum/api/routes/_version.py +3 -3
- meerschaum/api/routes/_webterm.py +3 -3
- meerschaum/config/__init__.py +100 -30
- meerschaum/config/_default.py +132 -64
- meerschaum/config/_edit.py +38 -32
- meerschaum/config/_formatting.py +2 -0
- meerschaum/config/_patch.py +10 -8
- meerschaum/config/_paths.py +133 -13
- meerschaum/config/_read_config.py +87 -36
- meerschaum/config/_sync.py +6 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/environment.py +262 -0
- meerschaum/config/stack/__init__.py +37 -15
- meerschaum/config/static.py +18 -0
- meerschaum/connectors/_Connector.py +11 -6
- meerschaum/connectors/__init__.py +41 -22
- meerschaum/connectors/api/_APIConnector.py +34 -6
- meerschaum/connectors/api/_actions.py +2 -2
- meerschaum/connectors/api/_jobs.py +12 -1
- meerschaum/connectors/api/_login.py +33 -7
- meerschaum/connectors/api/_misc.py +2 -2
- meerschaum/connectors/api/_pipes.py +23 -32
- meerschaum/connectors/api/_plugins.py +2 -2
- meerschaum/connectors/api/_request.py +1 -1
- meerschaum/connectors/api/_tokens.py +146 -0
- meerschaum/connectors/api/_users.py +70 -58
- meerschaum/connectors/instance/_InstanceConnector.py +83 -0
- meerschaum/connectors/instance/__init__.py +10 -0
- meerschaum/connectors/instance/_pipes.py +442 -0
- meerschaum/connectors/instance/_plugins.py +159 -0
- meerschaum/connectors/instance/_tokens.py +317 -0
- meerschaum/connectors/instance/_users.py +188 -0
- meerschaum/connectors/parse.py +5 -2
- meerschaum/connectors/sql/_SQLConnector.py +22 -5
- meerschaum/connectors/sql/_cli.py +12 -11
- meerschaum/connectors/sql/_create_engine.py +12 -168
- meerschaum/connectors/sql/_fetch.py +2 -18
- meerschaum/connectors/sql/_pipes.py +295 -278
- meerschaum/connectors/sql/_plugins.py +29 -0
- meerschaum/connectors/sql/_sql.py +47 -22
- meerschaum/connectors/sql/_users.py +36 -2
- meerschaum/connectors/sql/tables/__init__.py +254 -122
- meerschaum/connectors/valkey/_ValkeyConnector.py +5 -7
- meerschaum/connectors/valkey/_pipes.py +60 -31
- meerschaum/connectors/valkey/_plugins.py +2 -26
- meerschaum/core/Pipe/__init__.py +115 -85
- meerschaum/core/Pipe/_attributes.py +425 -124
- meerschaum/core/Pipe/_bootstrap.py +54 -24
- meerschaum/core/Pipe/_cache.py +555 -0
- meerschaum/core/Pipe/_clear.py +0 -11
- meerschaum/core/Pipe/_data.py +96 -68
- meerschaum/core/Pipe/_deduplicate.py +0 -13
- meerschaum/core/Pipe/_delete.py +12 -21
- meerschaum/core/Pipe/_drop.py +11 -23
- meerschaum/core/Pipe/_dtypes.py +49 -19
- meerschaum/core/Pipe/_edit.py +14 -4
- meerschaum/core/Pipe/_fetch.py +1 -1
- meerschaum/core/Pipe/_index.py +8 -14
- meerschaum/core/Pipe/_show.py +5 -5
- meerschaum/core/Pipe/_sync.py +123 -204
- meerschaum/core/Pipe/_verify.py +4 -4
- meerschaum/{plugins → core/Plugin}/_Plugin.py +16 -12
- meerschaum/core/Plugin/__init__.py +1 -1
- meerschaum/core/Token/_Token.py +220 -0
- meerschaum/core/Token/__init__.py +12 -0
- meerschaum/core/User/_User.py +35 -10
- meerschaum/core/User/__init__.py +9 -1
- meerschaum/core/__init__.py +1 -0
- meerschaum/jobs/_Executor.py +88 -4
- meerschaum/jobs/_Job.py +149 -38
- meerschaum/jobs/__init__.py +3 -2
- meerschaum/jobs/systemd.py +8 -3
- meerschaum/models/__init__.py +35 -0
- meerschaum/models/pipes.py +247 -0
- meerschaum/models/tokens.py +38 -0
- meerschaum/models/users.py +26 -0
- meerschaum/plugins/__init__.py +301 -88
- meerschaum/plugins/bootstrap.py +510 -4
- meerschaum/utils/_get_pipes.py +97 -30
- meerschaum/utils/daemon/Daemon.py +199 -43
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
- meerschaum/utils/daemon/RotatingFile.py +63 -36
- meerschaum/utils/daemon/StdinFile.py +53 -13
- meerschaum/utils/daemon/__init__.py +47 -6
- meerschaum/utils/daemon/_names.py +6 -3
- meerschaum/utils/dataframe.py +480 -82
- meerschaum/utils/debug.py +49 -19
- meerschaum/utils/dtypes/__init__.py +478 -37
- meerschaum/utils/dtypes/sql.py +369 -29
- meerschaum/utils/formatting/__init__.py +5 -2
- meerschaum/utils/formatting/_jobs.py +1 -1
- meerschaum/utils/formatting/_pipes.py +52 -50
- meerschaum/utils/formatting/_pprint.py +1 -0
- meerschaum/utils/formatting/_shell.py +44 -18
- meerschaum/utils/misc.py +268 -186
- meerschaum/utils/packages/__init__.py +25 -40
- meerschaum/utils/packages/_packages.py +42 -34
- meerschaum/utils/pipes.py +213 -0
- meerschaum/utils/process.py +2 -2
- meerschaum/utils/prompt.py +175 -144
- meerschaum/utils/schedule.py +2 -1
- meerschaum/utils/sql.py +135 -49
- meerschaum/utils/threading.py +42 -0
- meerschaum/utils/typing.py +1 -4
- meerschaum/utils/venv/_Venv.py +2 -2
- meerschaum/utils/venv/__init__.py +7 -7
- meerschaum/utils/warnings.py +19 -13
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/METADATA +94 -96
- meerschaum-3.0.0.dist-info/RECORD +289 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/WHEEL +1 -1
- meerschaum-3.0.0.dist-info/licenses/NOTICE +2 -0
- meerschaum/api/models/_interfaces.py +0 -15
- meerschaum/api/models/_locations.py +0 -15
- meerschaum/api/models/_metrics.py +0 -15
- meerschaum/config/_environment.py +0 -145
- meerschaum/config/static/__init__.py +0 -186
- meerschaum-2.9.4.dist-info/RECORD +0 -263
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/zip-safe +0 -0
meerschaum/utils/prompt.py
CHANGED
@@ -14,7 +14,8 @@ from meerschaum.utils.typing import Any, Union, Optional, Tuple, List
|
|
14
14
|
|
15
15
|
|
16
16
|
def prompt(
|
17
|
-
question: str,
|
17
|
+
question: str = '',
|
18
|
+
*,
|
18
19
|
icon: bool = True,
|
19
20
|
default: Union[str, Tuple[str, str], None] = None,
|
20
21
|
default_editable: Optional[str] = None,
|
@@ -22,6 +23,7 @@ def prompt(
|
|
22
23
|
is_password: bool = False,
|
23
24
|
wrap_lines: bool = True,
|
24
25
|
noask: bool = False,
|
26
|
+
silent: bool = False,
|
25
27
|
**kw: Any
|
26
28
|
) -> str:
|
27
29
|
"""
|
@@ -59,6 +61,9 @@ def prompt(
|
|
59
61
|
noask: bool, default False
|
60
62
|
If `True`, only print the question and return the default answer.
|
61
63
|
|
64
|
+
silent: bool, default False
|
65
|
+
If `True` do not print anything to the screen, but still block for input.
|
66
|
+
|
62
67
|
Returns
|
63
68
|
-------
|
64
69
|
A `str` of the input provided by the user.
|
@@ -67,11 +72,25 @@ def prompt(
|
|
67
72
|
from meerschaum.utils.packages import attempt_import
|
68
73
|
from meerschaum.utils.formatting import ANSI, CHARSET, highlight_pipes, fill_ansi
|
69
74
|
from meerschaum.config import get_config
|
70
|
-
from meerschaum.utils.misc import filter_keywords
|
71
|
-
from meerschaum.utils.daemon import running_in_daemon
|
75
|
+
from meerschaum.utils.misc import filter_keywords, remove_ansi
|
76
|
+
from meerschaum.utils.daemon import running_in_daemon, get_current_daemon
|
77
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
78
|
+
|
79
|
+
original_kwargs = {
|
80
|
+
'question': question,
|
81
|
+
'icon': icon,
|
82
|
+
'default': default,
|
83
|
+
'default_editable': default_editable,
|
84
|
+
'detect_password': detect_password,
|
85
|
+
'is_password': is_password,
|
86
|
+
'wrap_lines': wrap_lines,
|
87
|
+
'noask': noask,
|
88
|
+
'silent': silent,
|
89
|
+
**kw
|
90
|
+
}
|
91
|
+
|
72
92
|
noask = check_noask(noask)
|
73
|
-
|
74
|
-
prompt_toolkit = attempt_import('prompt_toolkit')
|
93
|
+
prompt_toolkit = attempt_import('prompt_toolkit')
|
75
94
|
question_config = get_config('formatting', 'question', patch=True)
|
76
95
|
|
77
96
|
### if a default is provided, append it to the question.
|
@@ -103,27 +122,50 @@ def prompt(
|
|
103
122
|
question += first_line
|
104
123
|
if len(other_lines) > 0:
|
105
124
|
question += '\n' + other_lines
|
106
|
-
|
125
|
+
|
126
|
+
if not remove_ansi(question).endswith(' '):
|
127
|
+
question += ' '
|
128
|
+
|
129
|
+
prompt_kwargs = {
|
130
|
+
'message': prompt_toolkit.formatted_text.ANSI(question) if not silent else '',
|
131
|
+
'wrap_lines': wrap_lines,
|
132
|
+
'default': default_editable or '',
|
133
|
+
**filter_keywords(prompt_toolkit.prompt, **kw)
|
134
|
+
}
|
135
|
+
|
136
|
+
printed_question = False
|
107
137
|
|
108
138
|
if not running_in_daemon():
|
109
|
-
answer = (
|
110
|
-
|
111
|
-
prompt_toolkit.formatted_text.ANSI(question),
|
112
|
-
wrap_lines=wrap_lines,
|
113
|
-
default=default_editable or '',
|
114
|
-
**filter_keywords(prompt_toolkit.prompt, **kw)
|
115
|
-
) if not noask else ''
|
116
|
-
)
|
139
|
+
answer = prompt_toolkit.prompt(**prompt_kwargs) if not noask else ''
|
140
|
+
printed_question = True
|
117
141
|
else:
|
118
|
-
|
142
|
+
import json
|
143
|
+
daemon = get_current_daemon()
|
144
|
+
print(STATIC_CONFIG['jobs']['flush_token'], end='', flush=True)
|
145
|
+
wrote_file = False
|
146
|
+
try:
|
147
|
+
with open(daemon.prompt_kwargs_file_path, 'w+', encoding='utf-8') as f:
|
148
|
+
json.dump(original_kwargs, f, separators=(',', ':'))
|
149
|
+
wrote_file = True
|
150
|
+
except Exception:
|
151
|
+
pass
|
152
|
+
|
153
|
+
if not silent and not wrote_file:
|
154
|
+
print(question, end='', flush=True)
|
155
|
+
print(STATIC_CONFIG['jobs']['flush_token'], end='', flush=True)
|
156
|
+
printed_question = True
|
157
|
+
|
119
158
|
try:
|
120
159
|
answer = input() if not noask else ''
|
121
160
|
except EOFError:
|
122
161
|
answer = ''
|
123
|
-
|
162
|
+
|
163
|
+
if noask and not silent and not printed_question:
|
124
164
|
print(question)
|
165
|
+
|
125
166
|
if answer == '' and default is not None:
|
126
167
|
return default_answer
|
168
|
+
|
127
169
|
return answer
|
128
170
|
|
129
171
|
|
@@ -207,7 +249,7 @@ def yes_no(
|
|
207
249
|
|
208
250
|
def choose(
|
209
251
|
question: str,
|
210
|
-
choices: List[
|
252
|
+
choices: Union[List[str], List[Tuple[str, str]]],
|
211
253
|
default: Union[str, List[str], None] = None,
|
212
254
|
numeric: bool = True,
|
213
255
|
multiple: bool = False,
|
@@ -270,8 +312,9 @@ def choose(
|
|
270
312
|
noask = check_noask(noask)
|
271
313
|
|
272
314
|
### Handle empty choices.
|
273
|
-
if
|
274
|
-
|
315
|
+
if not choices:
|
316
|
+
if warn:
|
317
|
+
_warn(f"No available choices. Returning default value '{default}'.", stacklevel=3)
|
275
318
|
return default
|
276
319
|
|
277
320
|
### If the default case is to include multiple answers, allow for multiple inputs.
|
@@ -279,18 +322,25 @@ def choose(
|
|
279
322
|
multiple = True
|
280
323
|
|
281
324
|
choices_indices = {}
|
282
|
-
for i, c in enumerate(choices):
|
325
|
+
for i, c in enumerate(choices, start=1):
|
283
326
|
if isinstance(c, tuple):
|
284
327
|
i, c = c
|
285
328
|
choices_indices[i] = c
|
286
329
|
|
330
|
+
choices_values_indices = {v: k for k, v in choices_indices.items()}
|
331
|
+
ordered_keys = list(choices_indices.keys())
|
332
|
+
numeric_map = {str(i): key for i, key in enumerate(ordered_keys, 1)}
|
333
|
+
|
287
334
|
def _enforce_default(d):
|
288
|
-
if d is
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
335
|
+
if d is None:
|
336
|
+
return True
|
337
|
+
if d not in choices_values_indices and d not in choices_indices:
|
338
|
+
if warn:
|
339
|
+
_warn(
|
340
|
+
f"Default choice '{d}' is not contained in the choices. "
|
341
|
+
+ "Setting numeric = False.",
|
342
|
+
stacklevel=3
|
343
|
+
)
|
294
344
|
return False
|
295
345
|
return True
|
296
346
|
|
@@ -300,161 +350,141 @@ def choose(
|
|
300
350
|
numeric = False
|
301
351
|
break
|
302
352
|
|
303
|
-
|
304
|
-
|
353
|
+
_choices = (
|
354
|
+
[str(k) for k in choices_indices] if numeric
|
355
|
+
else list(choices_indices.values())
|
356
|
+
)
|
305
357
|
if multiple:
|
306
358
|
question += f"\n Enter your choices, separated by '{delimiter}'.\n"
|
307
359
|
|
308
360
|
altered_choices = {}
|
309
|
-
|
310
|
-
|
311
|
-
delim_replacement = '_' if delimiter != '_' else '-'
|
312
|
-
can_strip_start_spaces, can_strip_end_spaces = True, True
|
313
|
-
for i, c in choices_indices.items():
|
314
|
-
if isinstance(c, tuple):
|
315
|
-
key, c = c
|
316
|
-
if can_strip_start_spaces and c.startswith(' '):
|
317
|
-
can_strip_start_spaces = False
|
318
|
-
if can_strip_end_spaces and c.endswith(' '):
|
319
|
-
can_strip_end_spaces = False
|
320
|
-
|
321
|
-
if multiple:
|
322
|
-
### Check if the defaults have the delimiter.
|
323
|
-
for i, d in enumerate(default if isinstance(default, list) else [default]):
|
324
|
-
if d is None or delimiter not in d:
|
325
|
-
continue
|
326
|
-
new_d = d.replace(delimiter, delim_replacement)
|
327
|
-
altered_choices[new_d] = d
|
328
|
-
altered_default_indices[i] = new_d
|
329
|
-
for i, new_d in altered_default_indices.items():
|
330
|
-
if not isinstance(default, list):
|
331
|
-
default = new_d
|
332
|
-
break
|
333
|
-
default[i] = new_d
|
334
|
-
|
361
|
+
if multiple and not numeric:
|
362
|
+
delim_replacement = '_' if delimiter != '_' else '-'
|
335
363
|
### Check if the choices have the delimiter.
|
336
364
|
for i, c in choices_indices.items():
|
337
|
-
if delimiter in c
|
365
|
+
if delimiter not in c:
|
366
|
+
continue
|
367
|
+
if warn:
|
338
368
|
_warn(
|
339
369
|
f"The delimiter '{delimiter}' is contained within choice '{c}'.\n"
|
340
370
|
+ f"Replacing the string '{delimiter}' with '{delim_replacement}' in "
|
341
371
|
+ "the choice for correctly parsing input (will be replaced upon returning the prompt).",
|
342
|
-
stacklevel
|
372
|
+
stacklevel=3,
|
343
373
|
)
|
344
|
-
|
345
|
-
|
346
|
-
altered_indices[i] = new_c
|
347
|
-
for i, new_c in altered_indices.items():
|
374
|
+
new_c = c.replace(delimiter, delim_replacement)
|
375
|
+
altered_choices[new_c] = c
|
348
376
|
choices_indices[i] = new_c
|
349
|
-
default = delimiter.join(default) if isinstance(default, list) else default
|
350
377
|
|
351
378
|
question_options = []
|
379
|
+
default_tuple = None
|
352
380
|
if numeric:
|
353
|
-
|
354
|
-
_default = ''
|
381
|
+
_default_prompt_str = ''
|
355
382
|
if default is not None:
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
# question += '\n'
|
383
|
+
default_list = default if isinstance(default, list) else [default]
|
384
|
+
if multiple and isinstance(default, str):
|
385
|
+
default_list = default.split(delimiter)
|
386
|
+
|
387
|
+
_default_indices = []
|
388
|
+
for d in default_list:
|
389
|
+
key = None
|
390
|
+
if d in choices_values_indices: # is a value
|
391
|
+
key = choices_values_indices[d]
|
392
|
+
elif d in choices_indices: # is an index
|
393
|
+
key = d
|
394
|
+
|
395
|
+
if key in ordered_keys:
|
396
|
+
_default_indices.append(str(ordered_keys.index(key) + 1))
|
397
|
+
|
398
|
+
_default_prompt_str = delimiter.join(_default_indices)
|
399
|
+
|
374
400
|
choices_digits = len(str(len(choices)))
|
375
|
-
for
|
401
|
+
for choice_ix, c in enumerate(choices_indices.values(), start=1):
|
376
402
|
question_options.append(
|
377
|
-
f" {
|
378
|
-
+ (" " * (choices_digits - len(str(
|
403
|
+
f" {choice_ix}. "
|
404
|
+
+ (" " * (choices_digits - len(str(choice_ix))))
|
379
405
|
+ f"{c}\n"
|
380
406
|
)
|
381
|
-
default_tuple = (
|
407
|
+
default_tuple = (_default_prompt_str, default) if default is not None else None
|
382
408
|
else:
|
383
409
|
default_tuple = default
|
384
|
-
# question += '\n'
|
385
410
|
for c in choices_indices.values():
|
386
|
-
question_options.append(f"{c}\n")
|
411
|
+
question_options.append(f" • {c}\n")
|
387
412
|
|
388
413
|
if 'completer' not in kw:
|
389
|
-
WordCompleter = attempt_import('prompt_toolkit.completion').WordCompleter
|
390
|
-
kw['completer'] = WordCompleter(
|
414
|
+
WordCompleter = attempt_import('prompt_toolkit.completion', lazy=False).WordCompleter
|
415
|
+
kw['completer'] = WordCompleter(
|
416
|
+
[str(v) for v in choices_indices.values()] + [str(i) for i in choices_indices],
|
417
|
+
sentence=True,
|
418
|
+
)
|
391
419
|
|
392
|
-
|
393
|
-
while not
|
420
|
+
answers = []
|
421
|
+
while not answers:
|
394
422
|
print_options(question_options, header='')
|
395
423
|
answer = prompt(
|
396
424
|
question,
|
397
|
-
icon
|
398
|
-
default
|
399
|
-
noask
|
425
|
+
icon=icon,
|
426
|
+
default=default_tuple,
|
427
|
+
noask=noask,
|
400
428
|
**kw
|
401
429
|
)
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
_answers =
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
430
|
+
if not answer and default is not None:
|
431
|
+
answer = default if isinstance(default, str) else delimiter.join(default)
|
432
|
+
|
433
|
+
if not answer:
|
434
|
+
if warn:
|
435
|
+
_warn("Please pick a valid choice.", stack=False)
|
436
|
+
continue
|
437
|
+
|
438
|
+
_answers = [answer] if not multiple else [a.strip() for a in answer.split(delimiter)]
|
439
|
+
_answers = [a for a in _answers if a]
|
440
|
+
|
441
|
+
if numeric:
|
442
|
+
_raw_answers = list(_answers)
|
443
|
+
_answers = []
|
444
|
+
for _a in _raw_answers:
|
445
|
+
if _a in choices_values_indices:
|
446
|
+
_answers.append(str(choices_values_indices[_a]))
|
447
|
+
elif _a in numeric_map:
|
448
|
+
_answers.append(str(numeric_map[_a]))
|
449
|
+
else:
|
450
|
+
_answers.append(_a)
|
451
|
+
|
452
|
+
_processed_answers = [altered_choices.get(a, a) for a in _answers]
|
453
|
+
|
454
|
+
valid_answers = []
|
455
|
+
for a in _processed_answers:
|
456
|
+
if a in _choices:
|
457
|
+
valid_answers.append(a)
|
458
|
+
|
459
|
+
if len(valid_answers) != len(_processed_answers):
|
460
|
+
if warn:
|
461
|
+
_warn("Please pick a valid choice.", stack=False)
|
462
|
+
continue
|
463
|
+
answers = valid_answers
|
464
|
+
|
465
|
+
def get_key(key_str):
|
466
|
+
try:
|
467
|
+
return int(key_str)
|
468
|
+
except (ValueError, TypeError):
|
469
|
+
return key_str
|
432
470
|
|
433
471
|
if not multiple:
|
472
|
+
answer = answers[0]
|
434
473
|
if not numeric:
|
435
|
-
return answer
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
return _answer[0]
|
440
|
-
return _answer
|
441
|
-
except Exception:
|
442
|
-
_warn(f"Could not cast answer '{answer}' to an integer.", stacklevel=3)
|
474
|
+
return choices_values_indices.get(answer, answer) if as_indices else answer
|
475
|
+
|
476
|
+
key = get_key(answer)
|
477
|
+
return key if as_indices else choices_indices[key]
|
443
478
|
|
444
479
|
if not numeric:
|
445
|
-
return answers
|
480
|
+
return [choices_values_indices.get(a, a) for a in answers] if as_indices else answers
|
446
481
|
|
447
|
-
|
482
|
+
final_answers = []
|
448
483
|
for a in answers:
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
_answer_to_return = _answer_to_return[0]
|
454
|
-
_answers.append(_answer_to_return)
|
455
|
-
except Exception:
|
456
|
-
_warn(f"Could not cast answer '{a}' to an integer.", stacklevel=3)
|
457
|
-
return _answers
|
484
|
+
key = get_key(a)
|
485
|
+
final_answers.append(key if as_indices else choices_indices[key])
|
486
|
+
return final_answers
|
487
|
+
|
458
488
|
|
459
489
|
|
460
490
|
def get_password(
|
@@ -564,10 +594,11 @@ def check_noask(noask: bool = False) -> bool:
|
|
564
594
|
"""
|
565
595
|
Flip `noask` to `True` if `MRSM_NOASK` is set.
|
566
596
|
"""
|
567
|
-
from meerschaum.
|
597
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
568
598
|
NOASK = STATIC_CONFIG['environment']['noask']
|
569
599
|
if noask:
|
570
600
|
return True
|
601
|
+
|
571
602
|
return (
|
572
603
|
os.environ.get(NOASK, 'false').lower()
|
573
604
|
in ('1', 'true')
|
meerschaum/utils/schedule.py
CHANGED
@@ -14,6 +14,7 @@ import meerschaum as mrsm
|
|
14
14
|
from meerschaum.utils.typing import Callable, Any, Optional, List, Dict
|
15
15
|
from meerschaum.utils.warnings import warn, error
|
16
16
|
|
17
|
+
|
17
18
|
STARTING_KEYWORD: str = 'starting'
|
18
19
|
INTERVAL_UNITS: List[str] = ['months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'years']
|
19
20
|
FREQUENCY_ALIASES: Dict[str, str] = {
|
@@ -292,7 +293,7 @@ def parse_start_time(schedule: str, now: Optional[datetime] = None) -> datetime:
|
|
292
293
|
>>> parse_start_time('hourly starting 00:30')
|
293
294
|
datetime.datetime(2024, 5, 13, 0, 30, tzinfo=datetime.timezone.utc)
|
294
295
|
"""
|
295
|
-
from meerschaum.utils.
|
296
|
+
from meerschaum.utils.dtypes import round_time
|
296
297
|
dateutil_parser = mrsm.attempt_import('dateutil.parser')
|
297
298
|
starting_parts = schedule.split(STARTING_KEYWORD)
|
298
299
|
starting_str = ('now' if len(starting_parts) == 1 else starting_parts[-1]).strip()
|