meerschaum 2.2.6__py3-none-any.whl → 2.3.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 +6 -1
- meerschaum/__main__.py +9 -9
- meerschaum/_internal/arguments/__init__.py +1 -1
- meerschaum/_internal/arguments/_parse_arguments.py +72 -6
- meerschaum/_internal/arguments/_parser.py +45 -15
- meerschaum/_internal/docs/index.py +265 -8
- meerschaum/_internal/entry.py +167 -37
- meerschaum/_internal/shell/Shell.py +290 -99
- meerschaum/_internal/shell/updates.py +175 -0
- meerschaum/actions/__init__.py +29 -17
- meerschaum/actions/api.py +12 -12
- meerschaum/actions/attach.py +113 -0
- meerschaum/actions/copy.py +68 -41
- meerschaum/actions/delete.py +112 -50
- meerschaum/actions/edit.py +3 -3
- meerschaum/actions/install.py +40 -32
- meerschaum/actions/pause.py +44 -27
- meerschaum/actions/register.py +19 -5
- meerschaum/actions/restart.py +107 -0
- meerschaum/actions/show.py +130 -159
- meerschaum/actions/start.py +161 -100
- meerschaum/actions/stop.py +78 -42
- meerschaum/actions/sync.py +3 -3
- meerschaum/actions/upgrade.py +28 -36
- meerschaum/api/_events.py +25 -1
- meerschaum/api/_oauth2.py +2 -0
- meerschaum/api/_websockets.py +2 -2
- meerschaum/api/dash/callbacks/jobs.py +36 -44
- meerschaum/api/dash/jobs.py +89 -78
- meerschaum/api/routes/__init__.py +1 -0
- meerschaum/api/routes/_actions.py +148 -17
- meerschaum/api/routes/_jobs.py +407 -0
- meerschaum/api/routes/_pipes.py +25 -25
- meerschaum/config/_default.py +1 -0
- meerschaum/config/_formatting.py +1 -0
- meerschaum/config/_jobs.py +1 -1
- meerschaum/config/_paths.py +11 -0
- meerschaum/config/_shell.py +84 -67
- meerschaum/config/_version.py +1 -1
- meerschaum/config/static/__init__.py +18 -0
- meerschaum/connectors/Connector.py +13 -7
- meerschaum/connectors/__init__.py +28 -15
- meerschaum/connectors/api/APIConnector.py +27 -1
- meerschaum/connectors/api/_actions.py +71 -6
- meerschaum/connectors/api/_jobs.py +368 -0
- meerschaum/connectors/api/_misc.py +1 -1
- meerschaum/connectors/api/_pipes.py +85 -84
- meerschaum/connectors/api/_request.py +13 -9
- meerschaum/connectors/parse.py +27 -15
- meerschaum/core/Pipe/_bootstrap.py +16 -8
- meerschaum/core/Pipe/_sync.py +3 -0
- meerschaum/jobs/_Executor.py +69 -0
- meerschaum/jobs/_Job.py +899 -0
- meerschaum/jobs/__init__.py +396 -0
- meerschaum/jobs/systemd.py +694 -0
- meerschaum/plugins/__init__.py +97 -12
- meerschaum/utils/daemon/Daemon.py +352 -147
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +19 -10
- meerschaum/utils/daemon/RotatingFile.py +22 -8
- meerschaum/utils/daemon/StdinFile.py +121 -0
- meerschaum/utils/daemon/__init__.py +42 -27
- meerschaum/utils/daemon/_names.py +15 -13
- meerschaum/utils/formatting/__init__.py +83 -37
- meerschaum/utils/formatting/_jobs.py +146 -55
- meerschaum/utils/formatting/_shell.py +6 -0
- meerschaum/utils/misc.py +41 -22
- meerschaum/utils/packages/__init__.py +21 -15
- meerschaum/utils/packages/_packages.py +9 -6
- meerschaum/utils/process.py +9 -9
- meerschaum/utils/prompt.py +20 -7
- meerschaum/utils/schedule.py +21 -15
- meerschaum/utils/venv/__init__.py +2 -2
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/METADATA +22 -25
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/RECORD +80 -70
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/WHEEL +1 -1
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/LICENSE +0 -0
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/NOTICE +0 -0
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/top_level.txt +0 -0
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/zip-safe +0 -0
@@ -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
|
@@ -27,12 +28,14 @@ _attrs = {
|
|
27
28
|
'ANSI': None,
|
28
29
|
'UNICODE': None,
|
29
30
|
'CHARSET': None,
|
31
|
+
'RESET': '\033[0m',
|
30
32
|
}
|
31
33
|
__all__ = sorted([
|
32
|
-
'ANSI', 'CHARSET', 'UNICODE',
|
34
|
+
'ANSI', 'CHARSET', 'UNICODE', 'RESET',
|
33
35
|
'colored',
|
34
36
|
'translate_rich_to_termcolor',
|
35
37
|
'get_console',
|
38
|
+
'format_success_tuple',
|
36
39
|
'print_tuple',
|
37
40
|
'print_options',
|
38
41
|
'fill_ansi',
|
@@ -222,16 +225,17 @@ def get_console():
|
|
222
225
|
|
223
226
|
|
224
227
|
def print_tuple(
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
228
|
+
tup: mrsm.SuccessTuple,
|
229
|
+
skip_common: bool = True,
|
230
|
+
common_only: bool = False,
|
231
|
+
upper_padding: int = 0,
|
232
|
+
lower_padding: int = 0,
|
233
|
+
left_padding: int = 1,
|
234
|
+
calm: bool = False,
|
235
|
+
_progress: Optional['rich.progress.Progress'] = None,
|
236
|
+
) -> None:
|
233
237
|
"""
|
234
|
-
|
238
|
+
Format `meerschaum.utils.typing.SuccessTuple`.
|
235
239
|
|
236
240
|
Parameters
|
237
241
|
----------
|
@@ -247,24 +251,18 @@ def print_tuple(
|
|
247
251
|
lower_padding: int, default 0
|
248
252
|
How many newlines to append to the message.
|
249
253
|
|
254
|
+
left_padding: int, default 1
|
255
|
+
How mant spaces to preprend to the message.
|
256
|
+
|
250
257
|
calm: bool, default False
|
251
258
|
If `True`, use the default emoji and color scheme.
|
259
|
+
|
252
260
|
"""
|
253
261
|
from meerschaum.config.static import STATIC_CONFIG
|
254
|
-
|
255
|
-
try:
|
256
|
-
status = 'success' if tup[0] else 'failure'
|
257
|
-
except TypeError:
|
258
|
-
status = 'failure'
|
259
|
-
tup = None, None
|
260
|
-
|
261
|
-
if calm:
|
262
|
-
status += '_calm'
|
262
|
+
do_print = True
|
263
263
|
|
264
264
|
omit_messages = STATIC_CONFIG['system']['success']['ignore']
|
265
265
|
|
266
|
-
do_print = True
|
267
|
-
|
268
266
|
if common_only:
|
269
267
|
skip_common = False
|
270
268
|
do_print = tup[1] in omit_messages
|
@@ -272,22 +270,70 @@ def print_tuple(
|
|
272
270
|
if skip_common:
|
273
271
|
do_print = tup[1] not in omit_messages
|
274
272
|
|
275
|
-
if do_print:
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
273
|
+
if not do_print:
|
274
|
+
return
|
275
|
+
|
276
|
+
print(format_success_tuple(
|
277
|
+
tup,
|
278
|
+
upper_padding=upper_padding,
|
279
|
+
lower_padding=lower_padding,
|
280
|
+
calm=calm,
|
281
|
+
_progress=_progress,
|
282
|
+
))
|
283
|
+
|
284
|
+
|
285
|
+
def format_success_tuple(
|
286
|
+
tup: mrsm.SuccessTuple,
|
287
|
+
upper_padding: int = 0,
|
288
|
+
lower_padding: int = 0,
|
289
|
+
left_padding: int = 1,
|
290
|
+
calm: bool = False,
|
291
|
+
_progress: Optional['rich.progress.Progress'] = None,
|
292
|
+
) -> str:
|
293
|
+
"""
|
294
|
+
Format `meerschaum.utils.typing.SuccessTuple`.
|
295
|
+
|
296
|
+
Parameters
|
297
|
+
----------
|
298
|
+
upper_padding: int, default 0
|
299
|
+
How many newlines to prepend to the message.
|
300
|
+
|
301
|
+
lower_padding: int, default 0
|
302
|
+
How many newlines to append to the message.
|
303
|
+
|
304
|
+
left_padding: int, default 1
|
305
|
+
How mant spaces to preprend to the message.
|
306
|
+
|
307
|
+
calm: bool, default False
|
308
|
+
If `True`, use the default emoji and color scheme.
|
309
|
+
"""
|
310
|
+
from meerschaum.config.static import STATIC_CONFIG
|
311
|
+
_init()
|
312
|
+
try:
|
313
|
+
status = 'success' if tup[0] else 'failure'
|
314
|
+
except TypeError:
|
315
|
+
status = 'failure'
|
316
|
+
tup = None, None
|
317
|
+
|
318
|
+
if calm:
|
319
|
+
status += '_calm'
|
320
|
+
|
321
|
+
ANSI, CHARSET = __getattr__('ANSI'), __getattr__('CHARSET')
|
322
|
+
from meerschaum.config import get_config
|
323
|
+
status_config = get_config('formatting', status, patch=True)
|
324
|
+
|
325
|
+
msg = (' ' * left_padding) + status_config[CHARSET]['icon'] + ' ' + str(tup[1])
|
326
|
+
lines = msg.split('\n')
|
327
|
+
lines = [lines[0]] + [
|
328
|
+
((' ' + line if not line.startswith(' ') else line))
|
329
|
+
for line in lines[1:]
|
330
|
+
]
|
331
|
+
if ANSI:
|
332
|
+
lines[0] = fill_ansi(highlight_pipes(lines[0]), **status_config['ansi']['rich'])
|
333
|
+
|
334
|
+
msg = '\n'.join(lines)
|
335
|
+
msg = ('\n' * upper_padding) + msg + ('\n' * lower_padding)
|
336
|
+
return msg
|
291
337
|
|
292
338
|
|
293
339
|
def print_options(
|
@@ -7,86 +7,160 @@ Print jobs information.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
-
|
11
|
-
from
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
|
11
|
+
from datetime import datetime, timezone
|
12
|
+
|
13
|
+
import meerschaum as mrsm
|
14
|
+
from meerschaum.utils.typing import List, Optional, Any, is_success_tuple, Dict
|
15
|
+
from meerschaum.jobs import (
|
16
|
+
Job,
|
17
|
+
get_jobs,
|
18
|
+
get_running_jobs,
|
19
|
+
get_stopped_jobs,
|
20
|
+
get_paused_jobs,
|
21
|
+
get_executor_keys_from_context,
|
17
22
|
)
|
23
|
+
from meerschaum.config import get_config
|
24
|
+
|
18
25
|
|
19
26
|
def pprint_jobs(
|
20
|
-
|
21
|
-
|
22
|
-
|
27
|
+
jobs: Dict[str, Job],
|
28
|
+
nopretty: bool = False,
|
29
|
+
):
|
23
30
|
"""Pretty-print a list of Daemons."""
|
24
31
|
from meerschaum.utils.formatting import make_header
|
32
|
+
from meerschaum.utils.misc import items_str
|
25
33
|
|
26
|
-
|
27
|
-
|
28
|
-
|
34
|
+
running_jobs = get_running_jobs(jobs=jobs)
|
35
|
+
paused_jobs = get_paused_jobs(jobs=jobs)
|
36
|
+
stopped_jobs = get_stopped_jobs(jobs=jobs)
|
37
|
+
executor_keys_list = list(set(
|
38
|
+
[
|
39
|
+
job.executor_keys.replace('systemd:main', 'systemd')
|
40
|
+
for job in jobs.values()
|
41
|
+
]
|
42
|
+
)) if jobs else [get_executor_keys_from_context()]
|
29
43
|
|
30
44
|
def _nopretty_print():
|
31
45
|
from meerschaum.utils.misc import print_options
|
32
|
-
if
|
46
|
+
if running_jobs:
|
33
47
|
if not nopretty:
|
34
48
|
print('\n' + make_header('Running jobs'))
|
35
|
-
for
|
36
|
-
pprint_job(
|
49
|
+
for name, job in running_jobs.items():
|
50
|
+
pprint_job(job, nopretty=nopretty)
|
37
51
|
|
38
|
-
if
|
52
|
+
if paused_jobs:
|
39
53
|
if not nopretty:
|
40
54
|
print('\n' + make_header('Paused jobs'))
|
41
|
-
for
|
42
|
-
pprint_job(
|
55
|
+
for name, job in paused_jobs.items():
|
56
|
+
pprint_job(job, nopretty=nopretty)
|
43
57
|
|
44
|
-
if
|
58
|
+
if stopped_jobs:
|
45
59
|
if not nopretty:
|
46
60
|
print('\n' + make_header('Stopped jobs'))
|
47
|
-
for
|
48
|
-
pprint_job(
|
61
|
+
for name, job in stopped_jobs.items():
|
62
|
+
pprint_job(job, nopretty=nopretty)
|
49
63
|
|
50
64
|
def _pretty_print():
|
51
|
-
from meerschaum.utils.formatting import get_console, UNICODE, ANSI
|
65
|
+
from meerschaum.utils.formatting import get_console, UNICODE, ANSI, format_success_tuple
|
52
66
|
from meerschaum.utils.packages import import_rich, attempt_import
|
53
67
|
rich = import_rich()
|
54
|
-
rich_table, rich_text, rich_box = attempt_import(
|
68
|
+
rich_table, rich_text, rich_box, rich_json, rich_panel, rich_console = attempt_import(
|
69
|
+
'rich.table', 'rich.text', 'rich.box', 'rich.json', 'rich.panel', 'rich.console',
|
70
|
+
)
|
55
71
|
table = rich_table.Table(
|
56
|
-
title
|
57
|
-
|
58
|
-
|
59
|
-
|
72
|
+
title=rich_text.Text(
|
73
|
+
f"\nJobs on Executor"
|
74
|
+
+ ('s' if len(executor_keys_list) != 1 else '')
|
75
|
+
+ f" {items_str(executor_keys_list)}"
|
76
|
+
),
|
77
|
+
box=(rich_box.ROUNDED if UNICODE else rich_box.ASCII),
|
78
|
+
show_lines=True,
|
79
|
+
show_header=ANSI,
|
60
80
|
)
|
61
81
|
table.add_column("Name", justify='right', style=('magenta' if ANSI else ''))
|
82
|
+
table.add_column(
|
83
|
+
"Executor",
|
84
|
+
style=(get_config('shell', 'ansi', 'executor', 'rich', 'style') if ANSI else ''),
|
85
|
+
)
|
62
86
|
table.add_column("Command")
|
63
87
|
table.add_column("Status")
|
64
88
|
|
65
|
-
|
66
|
-
|
67
|
-
|
89
|
+
def get_success_text(job):
|
90
|
+
success_tuple = job.result
|
91
|
+
if not is_success_tuple(success_tuple):
|
92
|
+
return rich_text.Text('')
|
93
|
+
|
94
|
+
success = success_tuple[0]
|
95
|
+
msg = success_tuple[1]
|
96
|
+
lines = msg.split('\n')
|
97
|
+
msg = '\n'.join(line.lstrip().rstrip() for line in lines)
|
98
|
+
success_tuple = success, msg
|
99
|
+
success_tuple_str = (
|
100
|
+
format_success_tuple(success_tuple, left_padding=1)
|
101
|
+
if success_tuple is not None
|
102
|
+
else None
|
103
|
+
)
|
104
|
+
success_tuple_text = (
|
105
|
+
rich_text.Text.from_ansi(success_tuple_str)
|
106
|
+
) if success_tuple_str is not None else None
|
107
|
+
|
108
|
+
if success_tuple_text is None:
|
109
|
+
return rich_text.Text('')
|
110
|
+
|
111
|
+
return rich_text.Text('\n') + success_tuple_text
|
112
|
+
|
113
|
+
|
114
|
+
for name, job in running_jobs.items():
|
115
|
+
status_group = [
|
116
|
+
(
|
117
|
+
rich_text.Text(job.status, style=('green' if ANSI else ''))
|
118
|
+
if not job.is_blocking_on_stdin()
|
119
|
+
else rich_text.Text('waiting for input', style=('yellow' if ANSI else ''))
|
120
|
+
),
|
121
|
+
] + ([rich_text.Text(f"PID: {pid}")] if (pid := job.pid) else [])
|
122
|
+
|
123
|
+
if job.restart:
|
124
|
+
status_group.append(rich_text.Text('(restarts)'))
|
125
|
+
|
68
126
|
table.add_row(
|
69
|
-
|
70
|
-
|
71
|
-
|
127
|
+
job.name,
|
128
|
+
job.executor_keys.replace('systemd:main', 'systemd'),
|
129
|
+
job.label,
|
130
|
+
rich_console.Group(*status_group),
|
72
131
|
)
|
73
132
|
|
74
|
-
for
|
75
|
-
|
76
|
-
|
133
|
+
for name, job in paused_jobs.items():
|
134
|
+
status_group = [
|
135
|
+
rich_text.Text(job.status, style=('yellow' if ANSI else '')),
|
136
|
+
]
|
137
|
+
if job.restart:
|
138
|
+
status_group.append(rich_text.Text('(restarts)'))
|
139
|
+
|
77
140
|
table.add_row(
|
78
|
-
|
79
|
-
|
80
|
-
|
141
|
+
job.name,
|
142
|
+
job.executor_keys.replace('systemd:main', 'systemd'),
|
143
|
+
job.label,
|
144
|
+
rich_console.Group(*status_group),
|
81
145
|
)
|
82
146
|
|
83
|
-
for
|
84
|
-
|
85
|
-
|
147
|
+
for name, job in stopped_jobs.items():
|
148
|
+
status_group = [
|
149
|
+
rich_text.Text(job.status, style=('red' if ANSI else '')),
|
150
|
+
]
|
151
|
+
if job.restart:
|
152
|
+
if job.stop_time is None:
|
153
|
+
status_group.append(rich_text.Text('(restarts)'))
|
154
|
+
else:
|
155
|
+
status_group.append(rich_text.Text('(start to resume restarts)'))
|
156
|
+
|
157
|
+
status_group.append(get_success_text(job))
|
158
|
+
|
86
159
|
table.add_row(
|
87
|
-
|
88
|
-
|
89
|
-
|
160
|
+
job.name,
|
161
|
+
job.executor_keys.replace('systemd:main', 'systemd'),
|
162
|
+
job.label,
|
163
|
+
rich_console.Group(*status_group),
|
90
164
|
)
|
91
165
|
get_console().print(table)
|
92
166
|
|
@@ -95,15 +169,32 @@ def pprint_jobs(
|
|
95
169
|
|
96
170
|
|
97
171
|
def pprint_job(
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
"""Pretty-print a single
|
102
|
-
if daemon.hidden:
|
103
|
-
return
|
172
|
+
job: Job,
|
173
|
+
nopretty: bool = False,
|
174
|
+
):
|
175
|
+
"""Pretty-print a single `Job`."""
|
104
176
|
from meerschaum.utils.warnings import info
|
105
177
|
if not nopretty:
|
106
|
-
info(f"Command for job '{
|
107
|
-
print('\n' +
|
178
|
+
info(f"Command for job '{job.name}':")
|
179
|
+
print('\n' + job.label + '\n')
|
108
180
|
else:
|
109
|
-
print(
|
181
|
+
print(job.name)
|
182
|
+
|
183
|
+
|
184
|
+
def strip_timestamp_from_line(line: str) -> str:
|
185
|
+
"""
|
186
|
+
Remove the leading timestamp from a job's line (if present).
|
187
|
+
"""
|
188
|
+
now = datetime.now(timezone.utc)
|
189
|
+
timestamp_format = get_config('jobs', 'logs', 'timestamps', 'format')
|
190
|
+
now_str = now.strftime(timestamp_format)
|
191
|
+
|
192
|
+
date_prefix_str = line[:len(now_str)]
|
193
|
+
try:
|
194
|
+
line_timestamp = datetime.strptime(date_prefix_str, timestamp_format)
|
195
|
+
except Exception:
|
196
|
+
line_timestamp = None
|
197
|
+
if line_timestamp:
|
198
|
+
line = line[(len(now_str) + 3):]
|
199
|
+
|
200
|
+
return line
|
@@ -47,9 +47,15 @@ def clear_screen(debug: bool = False) -> bool:
|
|
47
47
|
from meerschaum.utils.formatting import ANSI, get_console
|
48
48
|
from meerschaum.utils.debug import dprint
|
49
49
|
from meerschaum.config import get_config
|
50
|
+
from meerschaum.utils.daemon import running_in_daemon
|
50
51
|
global _tried_clear_command
|
52
|
+
|
53
|
+
if running_in_daemon():
|
54
|
+
return True
|
55
|
+
|
51
56
|
if not get_config('shell', 'clear_screen'):
|
52
57
|
return True
|
58
|
+
|
53
59
|
print("", end="", flush=True)
|
54
60
|
if debug:
|
55
61
|
dprint("Skipping screen clear.")
|
meerschaum/utils/misc.py
CHANGED
@@ -875,7 +875,7 @@ def dict_from_od(od: collections.OrderedDict) -> Dict[Any, Any]:
|
|
875
875
|
_d[k] = dict_from_od(v)
|
876
876
|
return _d
|
877
877
|
|
878
|
-
def remove_ansi(s
|
878
|
+
def remove_ansi(s: str) -> str:
|
879
879
|
"""
|
880
880
|
Remove ANSI escape characters from a string.
|
881
881
|
|
@@ -899,10 +899,11 @@ def remove_ansi(s : str) -> str:
|
|
899
899
|
|
900
900
|
|
901
901
|
def get_connector_labels(
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
902
|
+
*types: str,
|
903
|
+
search_term: str = '',
|
904
|
+
ignore_exact_match = True,
|
905
|
+
_additional_options: Optional[List[str]] = None,
|
906
|
+
) -> List[str]:
|
906
907
|
"""
|
907
908
|
Read connector labels from the configuration dictionary.
|
908
909
|
|
@@ -941,12 +942,16 @@ def get_connector_labels(
|
|
941
942
|
continue
|
942
943
|
conns += [ f'{t}:{label}' for label in connectors.get(t, {}) if label != 'default' ]
|
943
944
|
|
945
|
+
if _additional_options:
|
946
|
+
conns += _additional_options
|
947
|
+
|
944
948
|
possibilities = [
|
945
|
-
c
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
949
|
+
c
|
950
|
+
for c in conns
|
951
|
+
if c.startswith(search_term)
|
952
|
+
and c != (
|
953
|
+
search_term if ignore_exact_match else ''
|
954
|
+
)
|
950
955
|
]
|
951
956
|
return sorted(possibilities)
|
952
957
|
|
@@ -1063,7 +1068,7 @@ def async_wrap(func):
|
|
1063
1068
|
loop = asyncio.get_event_loop()
|
1064
1069
|
pfunc = partial(func, *args, **kwargs)
|
1065
1070
|
return await loop.run_in_executor(executor, pfunc)
|
1066
|
-
return run
|
1071
|
+
return run
|
1067
1072
|
|
1068
1073
|
|
1069
1074
|
def debug_trace(browser: bool = True):
|
@@ -1077,17 +1082,17 @@ def debug_trace(browser: bool = True):
|
|
1077
1082
|
|
1078
1083
|
|
1079
1084
|
def items_str(
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1085
|
+
items: List[Any],
|
1086
|
+
quotes: bool = True,
|
1087
|
+
quote_str: str = "'",
|
1088
|
+
commas: bool = True,
|
1089
|
+
comma_str: str = ',',
|
1090
|
+
and_: bool = True,
|
1091
|
+
and_str: str = 'and',
|
1092
|
+
oxford_comma: bool = True,
|
1093
|
+
spaces: bool = True,
|
1094
|
+
space_str = ' ',
|
1095
|
+
) -> str:
|
1091
1096
|
"""
|
1092
1097
|
Return a formatted string if list items separated by commas.
|
1093
1098
|
|
@@ -1218,6 +1223,19 @@ def is_bcp_available() -> bool:
|
|
1218
1223
|
return has_bcp
|
1219
1224
|
|
1220
1225
|
|
1226
|
+
def is_systemd_available() -> bool:
|
1227
|
+
"""Check if running on systemd."""
|
1228
|
+
import subprocess
|
1229
|
+
try:
|
1230
|
+
has_systemctl = subprocess.call(
|
1231
|
+
['systemctl', '-h'],
|
1232
|
+
stdout=subprocess.DEVNULL,
|
1233
|
+
stderr=subprocess.STDOUT,
|
1234
|
+
) == 0
|
1235
|
+
except Exception:
|
1236
|
+
has_systemctl = False
|
1237
|
+
return has_systemctl
|
1238
|
+
|
1221
1239
|
def get_last_n_lines(file_name: str, N: int):
|
1222
1240
|
"""
|
1223
1241
|
https://thispointer.com/python-get-last-n-lines-of-a-text-file-like-tail-command/
|
@@ -1522,6 +1540,7 @@ def safely_extract_tar(tarf: 'file', output_dir: Union[str, 'pathlib.Path']) ->
|
|
1522
1540
|
|
1523
1541
|
return safe_extract(tarf, output_dir)
|
1524
1542
|
|
1543
|
+
|
1525
1544
|
##################
|
1526
1545
|
# Legacy imports #
|
1527
1546
|
##################
|
@@ -816,6 +816,7 @@ def pip_install(
|
|
816
816
|
"""
|
817
817
|
from meerschaum.config._paths import VIRTENV_RESOURCES_PATH
|
818
818
|
from meerschaum.config import get_config
|
819
|
+
from meerschaum.config.static import STATIC_CONFIG
|
819
820
|
from meerschaum.utils.warnings import warn
|
820
821
|
from meerschaum.utils.misc import is_android
|
821
822
|
if args is None:
|
@@ -827,6 +828,11 @@ def pip_install(
|
|
827
828
|
if check_wheel:
|
828
829
|
have_wheel = venv_contains_package('wheel', venv=venv, debug=debug)
|
829
830
|
|
831
|
+
daemon_env_var = STATIC_CONFIG['environment']['daemon_id']
|
832
|
+
inside_daemon = daemon_env_var in os.environ
|
833
|
+
if inside_daemon:
|
834
|
+
silent = True
|
835
|
+
|
830
836
|
_args = list(args)
|
831
837
|
have_pip = venv_contains_package('pip', venv=None, debug=debug)
|
832
838
|
try:
|
@@ -844,16 +850,16 @@ def pip_install(
|
|
844
850
|
if have_pip and not have_uv_pip and _install_uv_pip and not is_android():
|
845
851
|
if not pip_install(
|
846
852
|
'uv',
|
847
|
-
venv
|
848
|
-
debug
|
849
|
-
_install_uv_pip
|
850
|
-
check_update
|
851
|
-
check_pypi
|
852
|
-
check_wheel
|
853
|
-
):
|
853
|
+
venv=None,
|
854
|
+
debug=debug,
|
855
|
+
_install_uv_pip=False,
|
856
|
+
check_update=False,
|
857
|
+
check_pypi=False,
|
858
|
+
check_wheel=False,
|
859
|
+
) and not silent:
|
854
860
|
warn(
|
855
861
|
f"Failed to install `uv` for virtual environment '{venv}'.",
|
856
|
-
color
|
862
|
+
color=False,
|
857
863
|
)
|
858
864
|
|
859
865
|
use_uv_pip = (
|
@@ -909,13 +915,13 @@ def pip_install(
|
|
909
915
|
check_wheel = False,
|
910
916
|
debug = debug,
|
911
917
|
_install_uv_pip = False,
|
912
|
-
):
|
918
|
+
) and not silent:
|
913
919
|
warn(
|
914
920
|
(
|
915
921
|
"Failed to install `setuptools`, `wheel`, and `uv` for virtual "
|
916
922
|
+ f"environment '{venv}'."
|
917
923
|
),
|
918
|
-
color
|
924
|
+
color=False,
|
919
925
|
)
|
920
926
|
|
921
927
|
if requirements_file_path is not None:
|
@@ -975,7 +981,7 @@ def pip_install(
|
|
975
981
|
if not completely_uninstall_package(
|
976
982
|
_install_no_version,
|
977
983
|
venv=venv, debug=debug,
|
978
|
-
):
|
984
|
+
) and not silent:
|
979
985
|
warn(
|
980
986
|
f"Failed to clean up package '{_install_no_version}'.",
|
981
987
|
)
|
@@ -989,9 +995,9 @@ def pip_install(
|
|
989
995
|
rc = run_python_package(
|
990
996
|
('pip' if not use_uv_pip else 'uv'),
|
991
997
|
_args + _packages,
|
992
|
-
venv
|
993
|
-
env
|
994
|
-
debug
|
998
|
+
venv=None,
|
999
|
+
env=_get_pip_os_env(color=color),
|
1000
|
+
debug=debug,
|
995
1001
|
)
|
996
1002
|
if debug:
|
997
1003
|
print(f"{rc=}")
|
@@ -1003,7 +1009,7 @@ def pip_install(
|
|
1003
1009
|
)
|
1004
1010
|
if not silent:
|
1005
1011
|
print(msg)
|
1006
|
-
if debug:
|
1012
|
+
if debug and not silent:
|
1007
1013
|
print('pip ' + ('un' if _uninstall else '') + 'install returned:', success)
|
1008
1014
|
return success
|
1009
1015
|
|
@@ -47,18 +47,20 @@ packages: Dict[str, Dict[str, str]] = {
|
|
47
47
|
'packaging' : 'packaging>=21.3.0',
|
48
48
|
'prompt_toolkit' : 'prompt-toolkit>=3.0.39',
|
49
49
|
'more_itertools' : 'more-itertools>=8.7.0',
|
50
|
-
'
|
51
|
-
'fasteners' : 'fasteners>=0.18.0',
|
52
|
-
'psutil' : 'psutil>=5.8.0',
|
53
|
-
'watchfiles' : 'watchfiles>=0.21.0',
|
54
|
-
'dill' : 'dill>=0.3.3',
|
50
|
+
'fasteners' : 'fasteners>=0.19.0',
|
55
51
|
'virtualenv' : 'virtualenv>=20.1.0',
|
56
52
|
'apscheduler' : 'APScheduler>=4.0.0a5',
|
57
53
|
'uv' : 'uv>=0.2.11',
|
58
54
|
},
|
55
|
+
'jobs': {
|
56
|
+
'dill' : 'dill>=0.3.3',
|
57
|
+
'daemon' : 'python-daemon>=0.2.3',
|
58
|
+
'watchfiles' : 'watchfiles>=0.21.0',
|
59
|
+
'psutil' : 'psutil>=5.8.0',
|
60
|
+
},
|
59
61
|
'drivers': {
|
60
62
|
'cryptography' : 'cryptography>=38.0.1',
|
61
|
-
'psycopg' : 'psycopg[binary]>=3.1
|
63
|
+
'psycopg' : 'psycopg[binary]>=3.2.1',
|
62
64
|
'pymysql' : 'PyMySQL>=0.9.0',
|
63
65
|
'aiomysql' : 'aiomysql>=0.0.21',
|
64
66
|
'sqlalchemy_cockroachdb' : 'sqlalchemy-cockroachdb>=2.0.0',
|
@@ -156,6 +158,7 @@ packages['api'] = {
|
|
156
158
|
packages['api'].update(packages['sql'])
|
157
159
|
packages['api'].update(packages['formatting'])
|
158
160
|
packages['api'].update(packages['dash'])
|
161
|
+
packages['api'].update(packages['jobs'])
|
159
162
|
|
160
163
|
all_packages = {}
|
161
164
|
for group, import_names in packages.items():
|