meerschaum 3.0.0rc4__py3-none-any.whl → 3.0.0rc8__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/_internal/arguments/_parser.py +14 -2
- 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 +1 -2
- meerschaum/_internal/entry.py +44 -8
- meerschaum/_internal/shell/Shell.py +115 -24
- meerschaum/_internal/shell/__init__.py +4 -1
- meerschaum/_internal/static.py +4 -1
- 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 +39 -11
- meerschaum/actions/attach.py +1 -0
- meerschaum/actions/delete.py +4 -2
- meerschaum/actions/edit.py +27 -8
- meerschaum/actions/login.py +8 -8
- meerschaum/actions/register.py +13 -7
- meerschaum/actions/reload.py +22 -5
- meerschaum/actions/restart.py +14 -0
- meerschaum/actions/show.py +69 -4
- meerschaum/actions/start.py +135 -14
- meerschaum/actions/stop.py +36 -3
- meerschaum/actions/sync.py +6 -1
- meerschaum/api/__init__.py +35 -13
- meerschaum/api/_events.py +2 -2
- meerschaum/api/_oauth2.py +47 -4
- meerschaum/api/dash/callbacks/dashboard.py +29 -0
- meerschaum/api/dash/callbacks/jobs.py +3 -2
- meerschaum/api/dash/callbacks/login.py +10 -1
- meerschaum/api/dash/callbacks/register.py +9 -2
- meerschaum/api/dash/pages/login.py +2 -2
- meerschaum/api/dash/pipes.py +72 -36
- meerschaum/api/dash/webterm.py +14 -6
- meerschaum/api/models/_pipes.py +7 -1
- 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 +1 -0
- meerschaum/api/routes/_jobs.py +23 -11
- meerschaum/api/routes/_login.py +73 -5
- meerschaum/api/routes/_pipes.py +6 -4
- meerschaum/api/routes/_webterm.py +3 -3
- meerschaum/config/__init__.py +60 -13
- meerschaum/config/_default.py +89 -61
- meerschaum/config/_edit.py +10 -8
- meerschaum/config/_formatting.py +2 -0
- meerschaum/config/_patch.py +4 -2
- meerschaum/config/_paths.py +127 -12
- meerschaum/config/_read_config.py +32 -12
- meerschaum/config/_version.py +1 -1
- meerschaum/config/environment.py +262 -0
- meerschaum/config/stack/__init__.py +7 -5
- meerschaum/connectors/_Connector.py +1 -2
- meerschaum/connectors/__init__.py +37 -2
- meerschaum/connectors/api/_APIConnector.py +1 -1
- meerschaum/connectors/api/_jobs.py +11 -0
- meerschaum/connectors/api/_pipes.py +7 -1
- meerschaum/connectors/instance/_plugins.py +9 -1
- meerschaum/connectors/instance/_tokens.py +20 -3
- meerschaum/connectors/instance/_users.py +8 -1
- meerschaum/connectors/parse.py +1 -1
- meerschaum/connectors/sql/_create_engine.py +3 -0
- meerschaum/connectors/sql/_pipes.py +93 -79
- meerschaum/connectors/sql/_users.py +8 -1
- meerschaum/connectors/valkey/_ValkeyConnector.py +3 -3
- meerschaum/connectors/valkey/_pipes.py +7 -5
- meerschaum/core/Pipe/__init__.py +45 -71
- meerschaum/core/Pipe/_attributes.py +66 -90
- meerschaum/core/Pipe/_cache.py +555 -0
- meerschaum/core/Pipe/_clear.py +0 -11
- meerschaum/core/Pipe/_data.py +0 -50
- 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 +1 -1
- meerschaum/core/Pipe/_index.py +8 -14
- meerschaum/core/Pipe/_sync.py +12 -18
- meerschaum/core/Plugin/_Plugin.py +7 -1
- meerschaum/core/Token/_Token.py +1 -1
- meerschaum/core/User/_User.py +1 -2
- meerschaum/jobs/_Executor.py +88 -4
- meerschaum/jobs/_Job.py +146 -36
- meerschaum/jobs/systemd.py +7 -2
- meerschaum/plugins/__init__.py +277 -81
- meerschaum/utils/daemon/Daemon.py +197 -42
- 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 +18 -5
- meerschaum/utils/daemon/_names.py +6 -3
- meerschaum/utils/debug.py +34 -4
- meerschaum/utils/dtypes/__init__.py +5 -1
- meerschaum/utils/formatting/__init__.py +4 -1
- meerschaum/utils/formatting/_jobs.py +1 -1
- meerschaum/utils/formatting/_pipes.py +47 -46
- meerschaum/utils/formatting/_shell.py +33 -9
- meerschaum/utils/misc.py +22 -38
- meerschaum/utils/packages/__init__.py +15 -13
- meerschaum/utils/packages/_packages.py +1 -0
- meerschaum/utils/pipes.py +33 -5
- meerschaum/utils/process.py +1 -1
- meerschaum/utils/prompt.py +172 -143
- meerschaum/utils/sql.py +12 -2
- meerschaum/utils/threading.py +42 -0
- meerschaum/utils/venv/__init__.py +2 -0
- meerschaum/utils/warnings.py +19 -13
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/METADATA +3 -1
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/RECORD +116 -110
- meerschaum/config/_environment.py +0 -145
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/WHEEL +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/entry_points.txt +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/licenses/NOTICE +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/top_level.txt +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/zip-safe +0 -0
@@ -0,0 +1,435 @@
|
|
1
|
+
#! /usr/bin/env python3
|
2
|
+
# vim:fenc=utf-8
|
3
|
+
|
4
|
+
"""
|
5
|
+
Define utilities for managing the workers jobs.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import os
|
9
|
+
import pathlib
|
10
|
+
import json
|
11
|
+
import asyncio
|
12
|
+
import time
|
13
|
+
from typing import List, Dict, Any, Union, TextIO
|
14
|
+
|
15
|
+
import meerschaum as mrsm
|
16
|
+
from meerschaum.utils.warnings import warn
|
17
|
+
from meerschaum.jobs import Job
|
18
|
+
from meerschaum.utils.threading import Thread
|
19
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
20
|
+
|
21
|
+
STOP_TOKEN: str = STATIC_CONFIG['jobs']['stop_token']
|
22
|
+
|
23
|
+
|
24
|
+
def get_worker_input_file_path(ix: int) -> pathlib.Path:
|
25
|
+
"""
|
26
|
+
Return the file path to the worker's input named pipe file.
|
27
|
+
"""
|
28
|
+
from meerschaum.config.paths import CLI_RESOURCES_PATH
|
29
|
+
return CLI_RESOURCES_PATH / f"worker-{ix}.input"
|
30
|
+
|
31
|
+
|
32
|
+
def get_worker_output_file_path(ix: int) -> pathlib.Path:
|
33
|
+
"""
|
34
|
+
Return the file path to the worker's output `StdinFile`.
|
35
|
+
"""
|
36
|
+
from meerschaum.config.paths import CLI_RESOURCES_PATH
|
37
|
+
return CLI_RESOURCES_PATH / f"worker-{ix}.output"
|
38
|
+
|
39
|
+
|
40
|
+
def get_worker_stop_path(ix: int) -> pathlib.Path:
|
41
|
+
"""
|
42
|
+
Return the file path to the worker's stop file.
|
43
|
+
"""
|
44
|
+
from meerschaum.config.paths import CLI_RESOURCES_PATH
|
45
|
+
return CLI_RESOURCES_PATH / f"worker-{ix}.stop"
|
46
|
+
|
47
|
+
|
48
|
+
class ActionWorker:
|
49
|
+
"""
|
50
|
+
The process loop which will accept commands to be run.
|
51
|
+
"""
|
52
|
+
|
53
|
+
def __init__(self, ix: int, refresh_seconds: Union[int, float, None] = None):
|
54
|
+
self.ix = ix
|
55
|
+
self.refresh_seconds = (
|
56
|
+
refresh_seconds
|
57
|
+
if refresh_seconds is not None
|
58
|
+
else mrsm.get_config('system', 'cli', 'refresh_seconds')
|
59
|
+
)
|
60
|
+
self.refresh_logs_stop_event = asyncio.Event()
|
61
|
+
|
62
|
+
@property
|
63
|
+
def input_file_path(self) -> pathlib.Path:
|
64
|
+
"""
|
65
|
+
Return the path to the input file.
|
66
|
+
"""
|
67
|
+
return get_worker_input_file_path(self.ix)
|
68
|
+
|
69
|
+
@property
|
70
|
+
def output_file_path(self) -> pathlib.Path:
|
71
|
+
"""
|
72
|
+
Return the path to the output file.
|
73
|
+
"""
|
74
|
+
return get_worker_output_file_path(self.ix)
|
75
|
+
|
76
|
+
@property
|
77
|
+
def lock_path(self) -> pathlib.Path:
|
78
|
+
"""
|
79
|
+
Return the lock path for this CLI worker.
|
80
|
+
"""
|
81
|
+
if '_lock_path' in self.__dict__:
|
82
|
+
return self._lock_path
|
83
|
+
|
84
|
+
from meerschaum._internal.cli.daemons import get_cli_lock_path
|
85
|
+
self._lock_path = get_cli_lock_path(self.ix)
|
86
|
+
return self._lock_path
|
87
|
+
|
88
|
+
@property
|
89
|
+
def job(self) -> Job:
|
90
|
+
"""
|
91
|
+
Return the job associated with this worker.
|
92
|
+
"""
|
93
|
+
from meerschaum.config.paths import CLI_LOGS_RESOURCES_PATH, CLI_RESOURCES_PATH
|
94
|
+
log_path = CLI_LOGS_RESOURCES_PATH / f'cli.{self.ix}.worker.log'
|
95
|
+
|
96
|
+
return Job(
|
97
|
+
f'.cli.{self.ix}.worker',
|
98
|
+
sysargs=['start', 'worker', str(self.ix)],
|
99
|
+
executor_keys='local',
|
100
|
+
delete_after_completion=False,
|
101
|
+
_properties={
|
102
|
+
'logs': {
|
103
|
+
'path': log_path.as_posix(),
|
104
|
+
'write_timestamps': False,
|
105
|
+
'refresh_files_seconds': 31557600,
|
106
|
+
'max_file_size': 10_000_000,
|
107
|
+
'num_files_to_keep': 1,
|
108
|
+
'redirect_streams': True,
|
109
|
+
'lines_to_show': 0,
|
110
|
+
},
|
111
|
+
'cwd': CLI_RESOURCES_PATH.as_posix(),
|
112
|
+
},
|
113
|
+
)
|
114
|
+
|
115
|
+
@property
|
116
|
+
def stop_event(self) -> asyncio.Event:
|
117
|
+
"""
|
118
|
+
Return the stop event to set to exit the logs monitoring.
|
119
|
+
"""
|
120
|
+
if '_stop_event' in self.__dict__:
|
121
|
+
return self._stop_event
|
122
|
+
|
123
|
+
self._stop_event = asyncio.Event()
|
124
|
+
return self._stop_event
|
125
|
+
|
126
|
+
@property
|
127
|
+
def stop_path(self) -> pathlib.Path:
|
128
|
+
"""
|
129
|
+
Return the path to the worker's stop file.
|
130
|
+
"""
|
131
|
+
if '_stop_path' in self.__dict__:
|
132
|
+
return self._stop_path
|
133
|
+
|
134
|
+
self._stop_path = get_worker_stop_path(self.ix)
|
135
|
+
return self._stop_path
|
136
|
+
|
137
|
+
def check_stop_status(self) -> None:
|
138
|
+
"""
|
139
|
+
Check for the stop file, and if it exists, set the stop event.
|
140
|
+
"""
|
141
|
+
if self.stop_path.exists():
|
142
|
+
self.stop_event.set()
|
143
|
+
|
144
|
+
def set_stop_status(self) -> None:
|
145
|
+
"""
|
146
|
+
Create the stop file and set the stop event.
|
147
|
+
"""
|
148
|
+
self.stop_path.touch()
|
149
|
+
self.stop_event.set()
|
150
|
+
|
151
|
+
def clear_stop_status(self) -> None:
|
152
|
+
"""
|
153
|
+
Remove the stop file and clear the stop event.
|
154
|
+
"""
|
155
|
+
try:
|
156
|
+
self.stop_event.clear()
|
157
|
+
if self.stop_path.exists():
|
158
|
+
self.stop_path.unlink()
|
159
|
+
except Exception as e:
|
160
|
+
warn(f"Failed to clear stop status:\n{e}")
|
161
|
+
|
162
|
+
def set_lock(self) -> None:
|
163
|
+
"""
|
164
|
+
Create the lock file.
|
165
|
+
"""
|
166
|
+
self.lock_path.touch()
|
167
|
+
|
168
|
+
def release_lock(self) -> None:
|
169
|
+
"""
|
170
|
+
Delete the lock file.
|
171
|
+
"""
|
172
|
+
try:
|
173
|
+
if self.lock_path.exists():
|
174
|
+
self.lock_path.unlink()
|
175
|
+
except Exception as e:
|
176
|
+
warn(f"Failed to release lock for {self}:\n{e}")
|
177
|
+
|
178
|
+
def send_signal(self, signalnum):
|
179
|
+
"""
|
180
|
+
Send a signal to the running job.
|
181
|
+
"""
|
182
|
+
pid = self.job.pid
|
183
|
+
if pid is None:
|
184
|
+
raise EnvironmentError("Job is not running.")
|
185
|
+
|
186
|
+
os.kill(pid, signalnum)
|
187
|
+
|
188
|
+
def _read_data(
|
189
|
+
self,
|
190
|
+
file_to_read: TextIO,
|
191
|
+
) -> Dict[str, Any]:
|
192
|
+
"""
|
193
|
+
Common logic for reading data from a pipe.
|
194
|
+
"""
|
195
|
+
try:
|
196
|
+
data_str = file_to_read.readline()
|
197
|
+
except Exception as e:
|
198
|
+
warn(f"Could not read data:\n{e}")
|
199
|
+
return {}
|
200
|
+
|
201
|
+
if not data_str:
|
202
|
+
return {}
|
203
|
+
|
204
|
+
try:
|
205
|
+
data = json.loads(data_str)
|
206
|
+
except Exception as e:
|
207
|
+
return {'error': str(e)}
|
208
|
+
|
209
|
+
return data
|
210
|
+
|
211
|
+
@staticmethod
|
212
|
+
def _write_data(
|
213
|
+
file_to_write: TextIO,
|
214
|
+
data: Dict[str, Any],
|
215
|
+
) -> None:
|
216
|
+
"""
|
217
|
+
Write a data dictionary to a pipe file.
|
218
|
+
"""
|
219
|
+
try:
|
220
|
+
file_to_write.write(json.dumps(data, separators=(',', ':')) + '\n')
|
221
|
+
except Exception as e:
|
222
|
+
warn(f"Failed to write data:\n{e}")
|
223
|
+
|
224
|
+
def read_input_data(self) -> Dict[str, Any]:
|
225
|
+
"""
|
226
|
+
Read input data from the input pipe file.
|
227
|
+
This method is called from within the worker's daemon context.
|
228
|
+
"""
|
229
|
+
with open(self.input_file_path, 'r') as f:
|
230
|
+
return self._read_data(f)
|
231
|
+
|
232
|
+
def write_output_data(self, output_data: Dict[str, Any]) -> None:
|
233
|
+
"""
|
234
|
+
Write the output data dictionary to the output pipe file.
|
235
|
+
This method is called from within the worker's daemon context.
|
236
|
+
"""
|
237
|
+
with open(self.output_file_path, 'w') as f:
|
238
|
+
self._write_data(f, output_data)
|
239
|
+
|
240
|
+
def write_input_data(self, input_data: Dict[str, Any]) -> None:
|
241
|
+
"""
|
242
|
+
Write the input data dictionary to the input pipe file.
|
243
|
+
This method is called from the client entry context.
|
244
|
+
"""
|
245
|
+
with open(self.input_file_path, 'w') as f:
|
246
|
+
self._write_data(f, input_data)
|
247
|
+
|
248
|
+
def read_output_data(self) -> Dict[str, Any]:
|
249
|
+
"""
|
250
|
+
Read output data from the output pipe file.
|
251
|
+
This method is called from the client entry context.
|
252
|
+
"""
|
253
|
+
with open(self.output_file_path, 'r') as f:
|
254
|
+
return self._read_data(f)
|
255
|
+
|
256
|
+
def increment_log(self) -> None:
|
257
|
+
"""
|
258
|
+
Increment the rotating log to clear output for the next action.
|
259
|
+
"""
|
260
|
+
try:
|
261
|
+
self.job.daemon.rotating_log.increment_subfiles()
|
262
|
+
if self.job.daemon.log_offset_path.exists():
|
263
|
+
self.job.daemon.log_offset_path.unlink()
|
264
|
+
except Exception as e:
|
265
|
+
warn(f"Failed to refresh log files:\n{e}")
|
266
|
+
|
267
|
+
def is_ready(self) -> bool:
|
268
|
+
"""
|
269
|
+
Return whether the CLI worker is ready to accept input.
|
270
|
+
"""
|
271
|
+
return (
|
272
|
+
not self.lock_path.exists()
|
273
|
+
and self.input_file_path.exists()
|
274
|
+
and not self.stop_path.exists()
|
275
|
+
)
|
276
|
+
|
277
|
+
def create_fifos(self) -> mrsm.SuccessTuple:
|
278
|
+
"""
|
279
|
+
Create the named pipes (FIFO files) for input and output.
|
280
|
+
"""
|
281
|
+
paths = [self.input_file_path, self.output_file_path]
|
282
|
+
for path in paths:
|
283
|
+
try:
|
284
|
+
os.mkfifo(path)
|
285
|
+
except FileExistsError:
|
286
|
+
pass
|
287
|
+
except Exception as e:
|
288
|
+
return False, f"Failed to create FIFO file for {self}:\n{e}"
|
289
|
+
|
290
|
+
return True, "Success"
|
291
|
+
|
292
|
+
def run(self) -> mrsm.SuccessTuple:
|
293
|
+
"""
|
294
|
+
Run the worker's process loop.
|
295
|
+
"""
|
296
|
+
from meerschaum._internal.entry import entry
|
297
|
+
from meerschaum.config import replace_config
|
298
|
+
from meerschaum.config.environment import replace_env
|
299
|
+
|
300
|
+
self.create_fifos()
|
301
|
+
|
302
|
+
while True:
|
303
|
+
self.release_lock()
|
304
|
+
self.clear_stop_status()
|
305
|
+
|
306
|
+
input_data = self.read_input_data()
|
307
|
+
self.set_lock()
|
308
|
+
|
309
|
+
if 'error' in input_data:
|
310
|
+
warn(input_data['error'])
|
311
|
+
continue
|
312
|
+
|
313
|
+
if input_data.get('increment', False):
|
314
|
+
self.increment_log()
|
315
|
+
continue
|
316
|
+
|
317
|
+
sysargs = input_data.get('sysargs', None)
|
318
|
+
session_id = input_data.get('session_id', None)
|
319
|
+
action_id = input_data.get('action_id', None)
|
320
|
+
patch_args = input_data.get('patch_args', None)
|
321
|
+
env = input_data.get('env', {})
|
322
|
+
config = input_data.get('config', {})
|
323
|
+
self.write_output_data({
|
324
|
+
'state': 'accepted',
|
325
|
+
'session_id': session_id,
|
326
|
+
'action_id': action_id,
|
327
|
+
})
|
328
|
+
|
329
|
+
with replace_config(config):
|
330
|
+
with replace_env(env):
|
331
|
+
action_success, action_msg = entry(
|
332
|
+
sysargs,
|
333
|
+
_use_cli_daemon=False,
|
334
|
+
_patch_args=patch_args,
|
335
|
+
)
|
336
|
+
print(STOP_TOKEN, flush=True, end='\n')
|
337
|
+
|
338
|
+
self.write_output_data({
|
339
|
+
'state': 'completed',
|
340
|
+
'session_id': session_id,
|
341
|
+
'action_id': action_id,
|
342
|
+
'success': action_success,
|
343
|
+
'message': action_msg,
|
344
|
+
})
|
345
|
+
self.set_stop_status()
|
346
|
+
|
347
|
+
return True, "Success"
|
348
|
+
|
349
|
+
def monitor_callback(self, data: str):
|
350
|
+
print(data, flush=True, end='')
|
351
|
+
self.check_stop_status()
|
352
|
+
|
353
|
+
def cleanup(self, debug: bool = False) -> mrsm.SuccessTuple:
|
354
|
+
"""
|
355
|
+
Delete the worker's job and any existing files.
|
356
|
+
"""
|
357
|
+
delete_success, delete_msg = self.job.delete(debug=debug)
|
358
|
+
if not delete_success:
|
359
|
+
return delete_success, delete_msg
|
360
|
+
|
361
|
+
paths = [
|
362
|
+
get_worker_stop_path(self.ix),
|
363
|
+
get_worker_input_file_path(self.ix),
|
364
|
+
get_worker_output_file_path(self.ix),
|
365
|
+
]
|
366
|
+
|
367
|
+
try:
|
368
|
+
for path in paths:
|
369
|
+
if path.exists():
|
370
|
+
path.unlink()
|
371
|
+
except Exception as e:
|
372
|
+
return False, f"Failed to clean up worker files:\n{e}"
|
373
|
+
|
374
|
+
return True, "Success"
|
375
|
+
|
376
|
+
def touch_cli_logs_loop(self):
|
377
|
+
"""
|
378
|
+
Touch the CLI daemon's logs to refresh the logs monitoring.
|
379
|
+
"""
|
380
|
+
while not self.refresh_logs_stop_event.is_set():
|
381
|
+
self.job.daemon.rotating_log.touch()
|
382
|
+
time.sleep(self.refresh_seconds)
|
383
|
+
|
384
|
+
def start_cli_logs_refresh_thread(self):
|
385
|
+
"""
|
386
|
+
Spin up a daemon thread to refresh the CLI's logs.
|
387
|
+
"""
|
388
|
+
self._logs_refresh_thread = Thread(
|
389
|
+
target=self.touch_cli_logs_loop,
|
390
|
+
daemon=True,
|
391
|
+
)
|
392
|
+
self._logs_refresh_thread.start()
|
393
|
+
|
394
|
+
def stop_cli_logs_refresh_thread(self):
|
395
|
+
"""
|
396
|
+
Stop the logs refresh thread.
|
397
|
+
"""
|
398
|
+
self.refresh_logs_stop_event.set()
|
399
|
+
thread = self.__dict__.pop('_logs_refresh_thread', None)
|
400
|
+
if thread is None:
|
401
|
+
return
|
402
|
+
|
403
|
+
thread.join()
|
404
|
+
|
405
|
+
def __repr__(self) -> str:
|
406
|
+
return self.__str__()
|
407
|
+
|
408
|
+
def __str__(self) -> str:
|
409
|
+
return f'ActionWorker({self.ix})'
|
410
|
+
|
411
|
+
|
412
|
+
def get_existing_cli_worker_indices() -> List[int]:
|
413
|
+
"""
|
414
|
+
Get a list of the existing CLI workers' indices.
|
415
|
+
"""
|
416
|
+
from meerschaum.config.paths import CLI_RESOURCES_PATH
|
417
|
+
from meerschaum.utils.misc import is_int
|
418
|
+
|
419
|
+
return sorted(list({
|
420
|
+
int(worker_ix)
|
421
|
+
for filename in os.listdir(CLI_RESOURCES_PATH)
|
422
|
+
if (
|
423
|
+
filename.startswith('worker-')
|
424
|
+
and is_int(
|
425
|
+
(worker_ix := filename.split('.', maxsplit=1)[0].replace('worker-', ''))
|
426
|
+
)
|
427
|
+
)
|
428
|
+
}))
|
429
|
+
|
430
|
+
|
431
|
+
def get_existing_cli_workers() -> List[ActionWorker]:
|
432
|
+
"""
|
433
|
+
Get a list of the existing CLI workers.
|
434
|
+
"""
|
435
|
+
return [ActionWorker(ix) for ix in get_existing_cli_worker_indices()]
|
@@ -578,12 +578,12 @@ def init_dash(dash_app):
|
|
578
578
|
<li><code>meerschaum.utils.dtypes.deserialize_base64()</code></li>
|
579
579
|
<li><code>meerschaum.utils.dtypes.deserialize_bytes_string()</code></li>
|
580
580
|
<li><code>meerschaum.utils.dtypes.deserialize_geometry()</code></li>
|
581
|
-
<li><code>meerschaum.utils.dtypes.dtype_is_special()</code></li>
|
582
581
|
<li><code>meerschaum.utils.dtypes.encode_bytes_for_bytea()</code></li>
|
583
582
|
<li><code>meerschaum.utils.dtypes.geometry_is_wkt()</code></li>
|
584
583
|
<li><code>meerschaum.utils.dtypes.get_current_timestamp()</code></li>
|
585
584
|
<li><code>meerschaum.utils.dtypes.get_geometry_type_srid()</code></li>
|
586
585
|
<li><code>meerschaum.utils.dtypes.is_dtype_numeric()</code></li>
|
586
|
+
<li><code>meerschaum.utils.dtypes.is_dtype_special()</code></li>
|
587
587
|
<li><code>meerschaum.utils.dtypes.json_serialize_value()</code></li>
|
588
588
|
<li><code>meerschaum.utils.dtypes.none_if_null()</code></li>
|
589
589
|
<li><code>meerschaum.utils.dtypes.project_geometry()</code></li>
|
@@ -597,7 +597,6 @@ def init_dash(dash_app):
|
|
597
597
|
<li><code>meerschaum.utils.dtypes.to_pandas_dtype()</code></li>
|
598
598
|
<li><code>meerschaum.utils.dtypes.value_is_null()</code></li>
|
599
599
|
<li><code>meerschaum.utils.dtypes.get_current_timestamp()</code></li>
|
600
|
-
<li><code>meerschaum.utils.dtypes.dtype_is_special()</code></li>
|
601
600
|
<li><code>meerschaum.utils.dtypes.get_next_precision_unit()</code></li>
|
602
601
|
<li><code>meerschaum.utils.dtypes.round_time()</code></li>
|
603
602
|
</ul>
|
meerschaum/_internal/entry.py
CHANGED
@@ -12,9 +12,12 @@ from __future__ import annotations
|
|
12
12
|
import os
|
13
13
|
import sys
|
14
14
|
import pathlib
|
15
|
+
import shlex
|
16
|
+
import json
|
17
|
+
import time
|
15
18
|
|
16
19
|
import meerschaum as mrsm
|
17
|
-
from meerschaum.utils.typing import SuccessTuple, List, Optional, Dict, Callable, Any
|
20
|
+
from meerschaum.utils.typing import SuccessTuple, List, Optional, Dict, Callable, Any, Union
|
18
21
|
from meerschaum._internal.static import STATIC_CONFIG as _STATIC_CONFIG
|
19
22
|
|
20
23
|
_systemd_result_path = None
|
@@ -46,7 +49,44 @@ if (_STATIC_CONFIG['environment']['systemd_log_path']) in os.environ:
|
|
46
49
|
|
47
50
|
|
48
51
|
def entry(
|
49
|
-
sysargs:
|
52
|
+
sysargs: Union[List[str], str, None] = None,
|
53
|
+
_patch_args: Optional[Dict[str, Any]] = None,
|
54
|
+
_use_cli_daemon: bool = True,
|
55
|
+
_session_id: Optional[str] = None,
|
56
|
+
) -> SuccessTuple:
|
57
|
+
"""
|
58
|
+
Parse arguments and launch a Meerschaum action.
|
59
|
+
|
60
|
+
Returns
|
61
|
+
-------
|
62
|
+
A `SuccessTuple` indicating success.
|
63
|
+
"""
|
64
|
+
start = time.perf_counter()
|
65
|
+
sysargs_list = shlex.split(sysargs) if isinstance(sysargs, str) else sysargs
|
66
|
+
if (
|
67
|
+
not _use_cli_daemon
|
68
|
+
or (not sysargs or (sysargs[0] and sysargs[0].startswith('-')))
|
69
|
+
or '--no-daemon' in sysargs_list
|
70
|
+
or '--daemon' in sysargs_list
|
71
|
+
or '-d' in sysargs_list
|
72
|
+
or not mrsm.get_config('system', 'experimental', 'cli_daemon')
|
73
|
+
):
|
74
|
+
success, msg = entry_without_daemon(sysargs, _patch_args=_patch_args)
|
75
|
+
end = time.perf_counter()
|
76
|
+
if '--debug' in sysargs_list:
|
77
|
+
print(f"Duration without daemon: {round(end - start, 3)}")
|
78
|
+
return success, msg
|
79
|
+
|
80
|
+
from meerschaum._internal.cli.entry import entry_with_daemon
|
81
|
+
success, msg = entry_with_daemon(sysargs, _patch_args=_patch_args)
|
82
|
+
end = time.perf_counter()
|
83
|
+
if '--debug' in sysargs_list:
|
84
|
+
print(f"Duration with daemon: {round(end - start, 3)}")
|
85
|
+
return success, msg
|
86
|
+
|
87
|
+
|
88
|
+
def entry_without_daemon(
|
89
|
+
sysargs: Union[List[str], str, None] = None,
|
50
90
|
_patch_args: Optional[Dict[str, Any]] = None,
|
51
91
|
) -> SuccessTuple:
|
52
92
|
"""
|
@@ -56,8 +96,6 @@ def entry(
|
|
56
96
|
-------
|
57
97
|
A `SuccessTuple` indicating success.
|
58
98
|
"""
|
59
|
-
import shlex
|
60
|
-
import json
|
61
99
|
from meerschaum.utils.formatting import make_header
|
62
100
|
from meerschaum._internal.arguments import (
|
63
101
|
parse_arguments,
|
@@ -174,7 +212,6 @@ def entry(
|
|
174
212
|
).rstrip() if len(chained_sysargs) > 1 else results[0][1]
|
175
213
|
|
176
214
|
if _systemd_result_path:
|
177
|
-
import json
|
178
215
|
from meerschaum.utils.warnings import warn
|
179
216
|
import meerschaum as mrsm
|
180
217
|
|
@@ -217,7 +254,7 @@ def entry_with_args(
|
|
217
254
|
if and_key in (sysargs := kw.get('sysargs', [])):
|
218
255
|
if '-d' in sysargs or '--daemon' in sysargs:
|
219
256
|
sysargs = [(arg if arg != and_key else escaped_and_key) for arg in sysargs]
|
220
|
-
return entry(sysargs, _patch_args=_patch_args)
|
257
|
+
return entry(sysargs, _patch_args=_patch_args, _use_cli_daemon=False)
|
221
258
|
|
222
259
|
if kw.get('trace', None):
|
223
260
|
from meerschaum.utils.misc import debug_trace
|
@@ -287,7 +324,6 @@ def entry_with_args(
|
|
287
324
|
if kw.get('schedule', None) and not _skip_schedule:
|
288
325
|
from meerschaum.utils.schedule import schedule_function
|
289
326
|
from meerschaum.utils.misc import interval_str
|
290
|
-
import time
|
291
327
|
from datetime import timedelta
|
292
328
|
start_time = time.perf_counter()
|
293
329
|
schedule_function(do_action, **kw)
|
@@ -321,7 +357,7 @@ def _do_action_wrapper(
|
|
321
357
|
try:
|
322
358
|
result = action_function(**filter_keywords(action_function, **kw))
|
323
359
|
except Exception as e:
|
324
|
-
if kw.get('debug',
|
360
|
+
if kw.get('debug', True):
|
325
361
|
import traceback
|
326
362
|
traceback.print_exception(type(e), e, e.__traceback__)
|
327
363
|
result = False, (
|