meerschaum 2.2.5.dev3__py3-none-any.whl → 2.2.7__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 +4 -1
- meerschaum/__main__.py +10 -5
- meerschaum/_internal/arguments/_parser.py +13 -2
- meerschaum/_internal/docs/index.py +523 -26
- meerschaum/_internal/entry.py +13 -13
- meerschaum/_internal/shell/Shell.py +26 -22
- meerschaum/_internal/shell/updates.py +175 -0
- meerschaum/_internal/term/__init__.py +2 -2
- meerschaum/actions/bootstrap.py +13 -14
- meerschaum/actions/python.py +11 -8
- meerschaum/actions/register.py +149 -37
- meerschaum/actions/show.py +79 -71
- meerschaum/actions/stop.py +11 -11
- meerschaum/actions/sync.py +3 -3
- meerschaum/actions/upgrade.py +28 -36
- meerschaum/api/dash/callbacks/login.py +21 -13
- meerschaum/api/dash/pages/login.py +2 -2
- meerschaum/api/routes/_login.py +5 -5
- meerschaum/api/routes/_pipes.py +20 -20
- meerschaum/config/__init__.py +8 -1
- meerschaum/config/_formatting.py +1 -0
- meerschaum/config/_paths.py +24 -2
- meerschaum/config/_shell.py +78 -66
- meerschaum/config/_version.py +1 -1
- meerschaum/config/paths.py +21 -2
- meerschaum/config/static/__init__.py +2 -0
- meerschaum/connectors/Connector.py +7 -2
- meerschaum/connectors/__init__.py +7 -5
- meerschaum/connectors/api/APIConnector.py +7 -2
- meerschaum/connectors/api/_actions.py +23 -31
- meerschaum/connectors/api/_misc.py +1 -1
- meerschaum/connectors/api/_request.py +13 -9
- meerschaum/connectors/api/_uri.py +5 -5
- meerschaum/core/Pipe/__init__.py +7 -3
- meerschaum/core/Pipe/_data.py +23 -15
- meerschaum/core/Pipe/_deduplicate.py +1 -1
- meerschaum/core/Pipe/_dtypes.py +5 -0
- meerschaum/core/Pipe/_fetch.py +18 -16
- meerschaum/core/Pipe/_sync.py +23 -15
- meerschaum/plugins/_Plugin.py +6 -6
- meerschaum/plugins/__init__.py +1 -1
- meerschaum/utils/daemon/Daemon.py +88 -129
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +14 -5
- meerschaum/utils/daemon/RotatingFile.py +23 -17
- meerschaum/utils/daemon/__init__.py +28 -21
- meerschaum/utils/dataframe.py +12 -4
- meerschaum/utils/debug.py +9 -15
- meerschaum/utils/formatting/__init__.py +92 -46
- meerschaum/utils/formatting/_jobs.py +47 -9
- meerschaum/utils/misc.py +117 -11
- meerschaum/utils/packages/__init__.py +28 -16
- meerschaum/utils/prompt.py +5 -0
- meerschaum/utils/schedule.py +21 -15
- meerschaum/utils/typing.py +1 -0
- meerschaum/utils/venv/__init__.py +5 -1
- meerschaum/utils/warnings.py +8 -1
- meerschaum/utils/yaml.py +2 -2
- {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/METADATA +1 -1
- {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/RECORD +65 -64
- {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/WHEEL +1 -1
- {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/LICENSE +0 -0
- {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/NOTICE +0 -0
- {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/top_level.txt +0 -0
- {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/zip-safe +0 -0
@@ -32,14 +32,14 @@ class RotatingFile(io.IOBase):
|
|
32
32
|
SEEK_BACK_ATTEMPTS: int = 5
|
33
33
|
|
34
34
|
def __init__(
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
35
|
+
self,
|
36
|
+
file_path: pathlib.Path,
|
37
|
+
num_files_to_keep: Optional[int] = None,
|
38
|
+
max_file_size: Optional[int] = None,
|
39
|
+
redirect_streams: bool = False,
|
40
|
+
write_timestamps: bool = False,
|
41
|
+
timestamp_format: str = '%Y-%m-%d %H:%M',
|
42
|
+
):
|
43
43
|
"""
|
44
44
|
Create a file-like object which manages other files.
|
45
45
|
|
@@ -79,11 +79,7 @@ class RotatingFile(io.IOBase):
|
|
79
79
|
self.redirect_streams = redirect_streams
|
80
80
|
self.write_timestamps = write_timestamps
|
81
81
|
self.timestamp_format = timestamp_format
|
82
|
-
self.subfile_regex_pattern = re.compile(
|
83
|
-
r'^'
|
84
|
-
+ self.file_path.name
|
85
|
-
+ r'(?:\.\d+)?$'
|
86
|
-
)
|
82
|
+
self.subfile_regex_pattern = re.compile(r'(.*)\.log(?:\.\d+)?$')
|
87
83
|
|
88
84
|
### When subfiles are opened, map from their index to the file objects.
|
89
85
|
self.subfile_objects = {}
|
@@ -173,7 +169,7 @@ class RotatingFile(io.IOBase):
|
|
173
169
|
latest_index = (
|
174
170
|
self.get_index_from_subfile_name(existing_subfile_paths[-1].name)
|
175
171
|
if existing_subfile_paths
|
176
|
-
else
|
172
|
+
else 0
|
177
173
|
)
|
178
174
|
return latest_index
|
179
175
|
|
@@ -222,9 +218,12 @@ class RotatingFile(io.IOBase):
|
|
222
218
|
[
|
223
219
|
(file_name, self.get_index_from_subfile_name(file_name))
|
224
220
|
for file_name in os.listdir(self.file_path.parent)
|
225
|
-
if
|
221
|
+
if (
|
222
|
+
file_name.startswith(self.file_path.name)
|
223
|
+
and re.match(self.subfile_regex_pattern, file_name)
|
224
|
+
)
|
226
225
|
],
|
227
|
-
key
|
226
|
+
key=lambda x: x[1],
|
228
227
|
)
|
229
228
|
return [
|
230
229
|
(self.file_path.parent / file_name)
|
@@ -372,6 +371,9 @@ class RotatingFile(io.IOBase):
|
|
372
371
|
self._current_file_obj.write(data)
|
373
372
|
if suffix_str:
|
374
373
|
self._current_file_obj.write(suffix_str)
|
374
|
+
except BrokenPipeError:
|
375
|
+
warn("BrokenPipeError encountered. The daemon may have been terminated.")
|
376
|
+
return
|
375
377
|
except Exception as e:
|
376
378
|
warn(f"Failed to write to subfile:\n{traceback.format_exc()}")
|
377
379
|
self.flush()
|
@@ -582,10 +584,14 @@ class RotatingFile(io.IOBase):
|
|
582
584
|
if self.redirect_streams:
|
583
585
|
try:
|
584
586
|
sys.stdout.flush()
|
587
|
+
except BrokenPipeError:
|
588
|
+
pass
|
585
589
|
except Exception as e:
|
586
590
|
warn(f"Failed to flush STDOUT:\n{traceback.format_exc()}")
|
587
591
|
try:
|
588
592
|
sys.stderr.flush()
|
593
|
+
except BrokenPipeError:
|
594
|
+
pass
|
589
595
|
except Exception as e:
|
590
596
|
warn(f"Failed to flush STDERR:\n{traceback.format_exc()}")
|
591
597
|
|
@@ -597,7 +603,6 @@ class RotatingFile(io.IOBase):
|
|
597
603
|
if not self.write_timestamps:
|
598
604
|
return
|
599
605
|
|
600
|
-
threads = self.__dict__.get('_interceptor_threads', [])
|
601
606
|
self._stdout_interceptor = FileDescriptorInterceptor(
|
602
607
|
sys.stdout.fileno(),
|
603
608
|
self.get_timestamp_prefix_str,
|
@@ -640,6 +645,7 @@ class RotatingFile(io.IOBase):
|
|
640
645
|
"""
|
641
646
|
if not self.write_timestamps:
|
642
647
|
return
|
648
|
+
|
643
649
|
interceptors = self.__dict__.get('_interceptors', [])
|
644
650
|
interceptor_threads = self.__dict__.get('_interceptor_threads', [])
|
645
651
|
|
@@ -67,19 +67,17 @@ def daemon_entry(sysargs: Optional[List[str]] = None) -> SuccessTuple:
|
|
67
67
|
if daemon.status == 'running':
|
68
68
|
return True, f"Daemon '{daemon}' is already running."
|
69
69
|
return daemon.run(
|
70
|
-
debug
|
71
|
-
allow_dirty_run
|
70
|
+
debug=debug,
|
71
|
+
allow_dirty_run=True,
|
72
72
|
)
|
73
73
|
|
74
74
|
success_tuple = run_daemon(
|
75
75
|
entry,
|
76
76
|
filtered_sysargs,
|
77
|
-
daemon_id
|
78
|
-
label
|
79
|
-
keep_daemon_output
|
77
|
+
daemon_id=_args.get('name', None) if _args else None,
|
78
|
+
label=label,
|
79
|
+
keep_daemon_output=('--rm' not in sysargs),
|
80
80
|
)
|
81
|
-
if not isinstance(success_tuple, tuple):
|
82
|
-
success_tuple = False, str(success_tuple)
|
83
81
|
return success_tuple
|
84
82
|
|
85
83
|
|
@@ -109,25 +107,25 @@ def daemon_action(**kw) -> SuccessTuple:
|
|
109
107
|
|
110
108
|
|
111
109
|
def run_daemon(
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
110
|
+
func: Callable[[Any], Any],
|
111
|
+
*args,
|
112
|
+
daemon_id: Optional[str] = None,
|
113
|
+
keep_daemon_output: bool = True,
|
114
|
+
allow_dirty_run: bool = False,
|
115
|
+
label: Optional[str] = None,
|
116
|
+
**kw
|
117
|
+
) -> Any:
|
120
118
|
"""Execute a function as a daemon."""
|
121
119
|
daemon = Daemon(
|
122
120
|
func,
|
123
|
-
daemon_id
|
124
|
-
target_args
|
125
|
-
target_kw
|
126
|
-
label
|
121
|
+
daemon_id=daemon_id,
|
122
|
+
target_args=[arg for arg in args],
|
123
|
+
target_kw=kw,
|
124
|
+
label=label,
|
127
125
|
)
|
128
126
|
return daemon.run(
|
129
|
-
keep_daemon_output
|
130
|
-
allow_dirty_run
|
127
|
+
keep_daemon_output=keep_daemon_output,
|
128
|
+
allow_dirty_run=allow_dirty_run,
|
131
129
|
)
|
132
130
|
|
133
131
|
|
@@ -268,3 +266,12 @@ def get_filtered_daemons(
|
|
268
266
|
pass
|
269
267
|
daemons.append(d)
|
270
268
|
return daemons
|
269
|
+
|
270
|
+
|
271
|
+
def running_in_daemon() -> bool:
|
272
|
+
"""
|
273
|
+
Return whether the current thread is running in a Daemon context.
|
274
|
+
"""
|
275
|
+
from meerschaum.config.static import STATIC_CONFIG
|
276
|
+
daemon_env_var = STATIC_CONFIG['environment']['daemon_id']
|
277
|
+
return daemon_env_var in os.environ
|
meerschaum/utils/dataframe.py
CHANGED
@@ -8,13 +8,21 @@ Utility functions for working with DataFrames.
|
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
10
|
from datetime import datetime
|
11
|
+
|
12
|
+
import meerschaum as mrsm
|
11
13
|
from meerschaum.utils.typing import (
|
12
14
|
Optional, Dict, Any, List, Hashable, Generator,
|
13
|
-
Iterator, Iterable, Union,
|
15
|
+
Iterator, Iterable, Union, TYPE_CHECKING,
|
14
16
|
)
|
15
17
|
|
18
|
+
if TYPE_CHECKING:
|
19
|
+
pd, dask = mrsm.attempt_import('pandas', 'dask')
|
20
|
+
|
16
21
|
|
17
|
-
def add_missing_cols_to_df(
|
22
|
+
def add_missing_cols_to_df(
|
23
|
+
df: 'pd.DataFrame',
|
24
|
+
dtypes: Dict[str, Any],
|
25
|
+
) -> 'pd.DataFrame':
|
18
26
|
"""
|
19
27
|
Add columns from the dtypes dictionary as null columns to a new DataFrame.
|
20
28
|
|
@@ -723,7 +731,7 @@ def get_datetime_bound_from_df(
|
|
723
731
|
df: Union['pd.DataFrame', dict, list],
|
724
732
|
datetime_column: str,
|
725
733
|
minimum: bool = True,
|
726
|
-
) -> Union[int,
|
734
|
+
) -> Union[int, datetime, None]:
|
727
735
|
"""
|
728
736
|
Return the minimum or maximum datetime (or integer) from a DataFrame.
|
729
737
|
|
@@ -818,7 +826,7 @@ def chunksize_to_npartitions(chunksize: Optional[int]) -> int:
|
|
818
826
|
|
819
827
|
|
820
828
|
def df_from_literal(
|
821
|
-
pipe: Optional[
|
829
|
+
pipe: Optional[mrsm.Pipe] = None,
|
822
830
|
literal: str = None,
|
823
831
|
debug: bool = False
|
824
832
|
) -> 'pd.DataFrame':
|
meerschaum/utils/debug.py
CHANGED
@@ -93,22 +93,16 @@ def _checkpoint(
|
|
93
93
|
) -> None:
|
94
94
|
"""If the `_progress` and `_task` objects are provided, increment the task by one step.
|
95
95
|
If `_total` is provided, update the total instead.
|
96
|
-
|
97
|
-
Parameters
|
98
|
-
----------
|
99
|
-
_progress: Optional['rich.progress.Progress'] :
|
100
|
-
(Default value = None)
|
101
|
-
_task: Optional[int] :
|
102
|
-
(Default value = None)
|
103
|
-
_total: Optional[int] :
|
104
|
-
(Default value = None)
|
105
|
-
**kw :
|
106
|
-
|
107
|
-
|
108
|
-
Returns
|
109
|
-
-------
|
110
|
-
|
111
96
|
"""
|
112
97
|
if _progress is not None and _task is not None:
|
113
98
|
_kw = {'total': _total} if _total is not None else {'advance': 1}
|
114
99
|
_progress.update(_task, **_kw)
|
100
|
+
|
101
|
+
|
102
|
+
def trace(browser: bool = True):
|
103
|
+
"""
|
104
|
+
Open a web-based debugger to trace the execution of the program.
|
105
|
+
"""
|
106
|
+
from meerschaum.utils.packages import attempt_import
|
107
|
+
heartrate = attempt_import('heartrate')
|
108
|
+
heartrate.trace(files=heartrate.files.all, browser=browser)
|
@@ -10,6 +10,7 @@ from __future__ import annotations
|
|
10
10
|
import platform
|
11
11
|
import os
|
12
12
|
import sys
|
13
|
+
import meerschaum as mrsm
|
13
14
|
from meerschaum.utils.typing import Optional, Union, Any, Dict
|
14
15
|
from meerschaum.utils.formatting._shell import make_header
|
15
16
|
from meerschaum.utils.formatting._pprint import pprint
|
@@ -33,6 +34,7 @@ __all__ = sorted([
|
|
33
34
|
'colored',
|
34
35
|
'translate_rich_to_termcolor',
|
35
36
|
'get_console',
|
37
|
+
'format_success_tuple',
|
36
38
|
'print_tuple',
|
37
39
|
'print_options',
|
38
40
|
'fill_ansi',
|
@@ -222,16 +224,17 @@ def get_console():
|
|
222
224
|
|
223
225
|
|
224
226
|
def print_tuple(
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
227
|
+
tup: mrsm.SuccessTuple,
|
228
|
+
skip_common: bool = True,
|
229
|
+
common_only: bool = False,
|
230
|
+
upper_padding: int = 0,
|
231
|
+
lower_padding: int = 0,
|
232
|
+
left_padding: int = 1,
|
233
|
+
calm: bool = False,
|
234
|
+
_progress: Optional['rich.progress.Progress'] = None,
|
235
|
+
) -> None:
|
233
236
|
"""
|
234
|
-
|
237
|
+
Format `meerschaum.utils.typing.SuccessTuple`.
|
235
238
|
|
236
239
|
Parameters
|
237
240
|
----------
|
@@ -247,6 +250,59 @@ def print_tuple(
|
|
247
250
|
lower_padding: int, default 0
|
248
251
|
How many newlines to append to the message.
|
249
252
|
|
253
|
+
left_padding: int, default 1
|
254
|
+
How mant spaces to preprend to the message.
|
255
|
+
|
256
|
+
calm: bool, default False
|
257
|
+
If `True`, use the default emoji and color scheme.
|
258
|
+
|
259
|
+
"""
|
260
|
+
from meerschaum.config.static import STATIC_CONFIG
|
261
|
+
do_print = True
|
262
|
+
|
263
|
+
omit_messages = STATIC_CONFIG['system']['success']['ignore']
|
264
|
+
|
265
|
+
if common_only:
|
266
|
+
skip_common = False
|
267
|
+
do_print = tup[1] in omit_messages
|
268
|
+
|
269
|
+
if skip_common:
|
270
|
+
do_print = tup[1] not in omit_messages
|
271
|
+
|
272
|
+
if not do_print:
|
273
|
+
return
|
274
|
+
|
275
|
+
print(format_success_tuple(
|
276
|
+
tup,
|
277
|
+
upper_padding=upper_padding,
|
278
|
+
lower_padding=lower_padding,
|
279
|
+
calm=calm,
|
280
|
+
_progress=_progress,
|
281
|
+
))
|
282
|
+
|
283
|
+
|
284
|
+
def format_success_tuple(
|
285
|
+
tup: mrsm.SuccessTuple,
|
286
|
+
upper_padding: int = 0,
|
287
|
+
lower_padding: int = 0,
|
288
|
+
left_padding: int = 1,
|
289
|
+
calm: bool = False,
|
290
|
+
_progress: Optional['rich.progress.Progress'] = None,
|
291
|
+
) -> str:
|
292
|
+
"""
|
293
|
+
Format `meerschaum.utils.typing.SuccessTuple`.
|
294
|
+
|
295
|
+
Parameters
|
296
|
+
----------
|
297
|
+
upper_padding: int, default 0
|
298
|
+
How many newlines to prepend to the message.
|
299
|
+
|
300
|
+
lower_padding: int, default 0
|
301
|
+
How many newlines to append to the message.
|
302
|
+
|
303
|
+
left_padding: int, default 1
|
304
|
+
How mant spaces to preprend to the message.
|
305
|
+
|
250
306
|
calm: bool, default False
|
251
307
|
If `True`, use the default emoji and color scheme.
|
252
308
|
"""
|
@@ -261,46 +317,35 @@ def print_tuple(
|
|
261
317
|
if calm:
|
262
318
|
status += '_calm'
|
263
319
|
|
264
|
-
|
320
|
+
ANSI, CHARSET = __getattr__('ANSI'), __getattr__('CHARSET')
|
321
|
+
from meerschaum.config import get_config
|
322
|
+
status_config = get_config('formatting', status, patch=True)
|
265
323
|
|
266
|
-
|
324
|
+
msg = (' ' * left_padding) + status_config[CHARSET]['icon'] + ' ' + str(tup[1])
|
325
|
+
lines = msg.split('\n')
|
326
|
+
lines = [lines[0]] + [
|
327
|
+
((' ' + line if not line.startswith(' ') else line))
|
328
|
+
for line in lines[1:]
|
329
|
+
]
|
330
|
+
if ANSI:
|
331
|
+
lines[0] = fill_ansi(highlight_pipes(lines[0]), **status_config['ansi']['rich'])
|
267
332
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
if skip_common:
|
273
|
-
do_print = tup[1] not in omit_messages
|
274
|
-
|
275
|
-
if do_print:
|
276
|
-
ANSI, CHARSET = __getattr__('ANSI'), __getattr__('CHARSET')
|
277
|
-
from meerschaum.config import get_config
|
278
|
-
status_config = get_config('formatting', status, patch=True)
|
279
|
-
|
280
|
-
msg = ' ' + status_config[CHARSET]['icon'] + ' ' + str(tup[1])
|
281
|
-
lines = msg.split('\n')
|
282
|
-
lines = [lines[0]] + [
|
283
|
-
((' ' + line if not line.startswith(' ') else line))
|
284
|
-
for line in lines[1:]
|
285
|
-
]
|
286
|
-
if ANSI:
|
287
|
-
lines[0] = fill_ansi(highlight_pipes(lines[0]), **status_config['ansi']['rich'])
|
288
|
-
msg = '\n'.join(lines)
|
289
|
-
msg = ('\n' * upper_padding) + msg + ('\n' * lower_padding)
|
290
|
-
print(msg)
|
333
|
+
msg = '\n'.join(lines)
|
334
|
+
msg = ('\n' * upper_padding) + msg + ('\n' * lower_padding)
|
335
|
+
return msg
|
291
336
|
|
292
337
|
|
293
338
|
def print_options(
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
339
|
+
options: Optional[Dict[str, Any]] = None,
|
340
|
+
nopretty: bool = False,
|
341
|
+
no_rich: bool = False,
|
342
|
+
name: str = 'options',
|
343
|
+
header: Optional[str] = None,
|
344
|
+
num_cols: Optional[int] = None,
|
345
|
+
adjust_cols: bool = True,
|
346
|
+
sort_options: bool = False,
|
347
|
+
**kw
|
348
|
+
) -> None:
|
304
349
|
"""
|
305
350
|
Print items in an iterable as a fancy table.
|
306
351
|
|
@@ -342,7 +387,7 @@ def print_options(
|
|
342
387
|
_options.append(str(o))
|
343
388
|
if sort_options:
|
344
389
|
_options = sorted(_options)
|
345
|
-
_header = f"
|
390
|
+
_header = f"\nAvailable {name}" if header is None else header
|
346
391
|
|
347
392
|
if num_cols is None:
|
348
393
|
num_cols = 8
|
@@ -388,7 +433,7 @@ def print_options(
|
|
388
433
|
|
389
434
|
if _header is not None:
|
390
435
|
table = Table(
|
391
|
-
title =
|
436
|
+
title = _header,
|
392
437
|
box = box.SIMPLE,
|
393
438
|
show_header = False,
|
394
439
|
show_footer = False,
|
@@ -467,6 +512,7 @@ def fill_ansi(string: str, style: str = '') -> str:
|
|
467
512
|
|
468
513
|
return rich_text_to_str(msg)
|
469
514
|
|
515
|
+
|
470
516
|
def __getattr__(name: str) -> str:
|
471
517
|
"""
|
472
518
|
Lazily load module-level variables.
|
@@ -7,7 +7,8 @@ Print jobs information.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
-
|
10
|
+
import meerschaum as mrsm
|
11
|
+
from meerschaum.utils.typing import List, Optional, Any, is_success_tuple
|
11
12
|
from meerschaum.utils.daemon import (
|
12
13
|
Daemon,
|
13
14
|
get_daemons,
|
@@ -17,9 +18,9 @@ from meerschaum.utils.daemon import (
|
|
17
18
|
)
|
18
19
|
|
19
20
|
def pprint_jobs(
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
daemons: List[Daemon],
|
22
|
+
nopretty: bool = False,
|
23
|
+
):
|
23
24
|
"""Pretty-print a list of Daemons."""
|
24
25
|
from meerschaum.utils.formatting import make_header
|
25
26
|
|
@@ -48,10 +49,12 @@ def pprint_jobs(
|
|
48
49
|
pprint_job(d, nopretty=nopretty)
|
49
50
|
|
50
51
|
def _pretty_print():
|
51
|
-
from meerschaum.utils.formatting import get_console, UNICODE, ANSI
|
52
|
+
from meerschaum.utils.formatting import get_console, UNICODE, ANSI, format_success_tuple
|
52
53
|
from meerschaum.utils.packages import import_rich, attempt_import
|
53
54
|
rich = import_rich()
|
54
|
-
rich_table, rich_text, rich_box = attempt_import(
|
55
|
+
rich_table, rich_text, rich_box, rich_json, rich_panel, rich_console = attempt_import(
|
56
|
+
'rich.table', 'rich.text', 'rich.box', 'rich.json', 'rich.panel', 'rich.console',
|
57
|
+
)
|
55
58
|
table = rich_table.Table(
|
56
59
|
title = rich_text.Text('Jobs'),
|
57
60
|
box = (rich_box.ROUNDED if UNICODE else rich_box.ASCII),
|
@@ -62,13 +65,42 @@ def pprint_jobs(
|
|
62
65
|
table.add_column("Command")
|
63
66
|
table.add_column("Status")
|
64
67
|
|
68
|
+
def get_success_text(d):
|
69
|
+
success_tuple = d.properties.get('result', None)
|
70
|
+
if isinstance(success_tuple, list):
|
71
|
+
success_tuple = tuple(success_tuple)
|
72
|
+
if not is_success_tuple(success_tuple):
|
73
|
+
return rich_text.Text('')
|
74
|
+
|
75
|
+
success = success_tuple[0]
|
76
|
+
msg = success_tuple[1]
|
77
|
+
lines = msg.split('\n')
|
78
|
+
msg = '\n'.join(line.lstrip().rstrip() for line in lines)
|
79
|
+
success_tuple = success, msg
|
80
|
+
success_tuple_str = (
|
81
|
+
format_success_tuple(success_tuple, left_padding=1)
|
82
|
+
if success_tuple is not None
|
83
|
+
else None
|
84
|
+
)
|
85
|
+
success_tuple_text = (
|
86
|
+
rich_text.Text.from_ansi(success_tuple_str)
|
87
|
+
) if success_tuple_str is not None else None
|
88
|
+
|
89
|
+
if success_tuple_text is None:
|
90
|
+
return rich_text.Text('')
|
91
|
+
|
92
|
+
return rich_text.Text('\n') + success_tuple_text
|
93
|
+
|
94
|
+
|
65
95
|
for d in running_daemons:
|
66
96
|
if d.hidden:
|
67
97
|
continue
|
68
98
|
table.add_row(
|
69
99
|
d.daemon_id,
|
70
100
|
d.label,
|
71
|
-
|
101
|
+
rich_console.Group(
|
102
|
+
rich_text.Text(d.status, style=('green' if ANSI else '')),
|
103
|
+
),
|
72
104
|
)
|
73
105
|
|
74
106
|
for d in paused_daemons:
|
@@ -77,16 +109,22 @@ def pprint_jobs(
|
|
77
109
|
table.add_row(
|
78
110
|
d.daemon_id,
|
79
111
|
d.label,
|
80
|
-
|
112
|
+
rich_console.Group(
|
113
|
+
rich_text.Text(d.status, style=('yellow' if ANSI else '')),
|
114
|
+
),
|
81
115
|
)
|
82
116
|
|
83
117
|
for d in stopped_daemons:
|
84
118
|
if d.hidden:
|
85
119
|
continue
|
120
|
+
|
86
121
|
table.add_row(
|
87
122
|
d.daemon_id,
|
88
123
|
d.label,
|
89
|
-
|
124
|
+
rich_console.Group(
|
125
|
+
rich_text.Text(d.status, style=('red' if ANSI else '')),
|
126
|
+
get_success_text(d)
|
127
|
+
),
|
90
128
|
)
|
91
129
|
get_console().print(table)
|
92
130
|
|