meerschaum 2.9.5__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 +198 -118
- 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 +382 -95
- 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/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 +46 -21
- 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 +479 -81
- meerschaum/utils/debug.py +49 -19
- meerschaum/utils/dtypes/__init__.py +476 -34
- 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 +134 -47
- 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.5.dist-info → meerschaum-3.0.0.dist-info}/METADATA +94 -96
- meerschaum-3.0.0.dist-info/RECORD +289 -0
- {meerschaum-2.9.5.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.5.dist-info/RECORD +0 -263
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.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, PACKAGE_ROOT_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': PACKAGE_ROOT_PATH.parent.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()]
|
@@ -98,7 +98,7 @@ Decorate your connector classes with `meerschaum.make_connector()` to designate
|
|
98
98
|
from datetime import datetime, timezone
|
99
99
|
from random import randint
|
100
100
|
import meerschaum as mrsm
|
101
|
-
from meerschaum.utils.
|
101
|
+
from meerschaum.utils.dtypes import round_time
|
102
102
|
|
103
103
|
@mrsm.make_connector
|
104
104
|
class FooConnector(mrsm.Connector):
|
@@ -535,14 +535,25 @@ def init_dash(dash_app):
|
|
535
535
|
<p></p>
|
536
536
|
<ul>
|
537
537
|
<li><code>meerschaum.utils.dataframe.add_missing_cols_to_df()</code></li>
|
538
|
+
<li><code>meerschaum.utils.dataframe.chunksize_to_npartitions()</code></li>
|
539
|
+
<li><code>meerschaum.utils.dataframe.df_from_literal()</code></li>
|
538
540
|
<li><code>meerschaum.utils.dataframe.df_is_chunk_generator()</code></li>
|
539
541
|
<li><code>meerschaum.utils.dataframe.enforce_dtypes()</code></li>
|
540
542
|
<li><code>meerschaum.utils.dataframe.filter_unseen_df()</code></li>
|
543
|
+
<li><code>meerschaum.utils.dataframe.get_bool_cols()</code></li>
|
544
|
+
<li><code>meerschaum.utils.dataframe.get_bytes_cols()</code></li>
|
541
545
|
<li><code>meerschaum.utils.dataframe.get_datetime_bound_from_df()</code></li>
|
546
|
+
<li><code>meerschaum.utils.dataframe.get_datetime_cols()</code></li>
|
547
|
+
<li><code>meerschaum.utils.dataframe.get_datetime_cols_types()</code></li>
|
542
548
|
<li><code>meerschaum.utils.dataframe.get_first_valid_dask_partition()</code></li>
|
549
|
+
<li><code>meerschaum.utils.dataframe.get_geometry_cols()</code></li>
|
550
|
+
<li><code>meerschaum.utils.dataframe.get_geometry_cols_types()</code></li>
|
543
551
|
<li><code>meerschaum.utils.dataframe.get_json_cols()</code></li>
|
544
552
|
<li><code>meerschaum.utils.dataframe.get_numeric_cols()</code></li>
|
553
|
+
<li><code>meerschaum.utils.dataframe.get_special_cols()</code></li>
|
545
554
|
<li><code>meerschaum.utils.dataframe.get_unhashable_cols()</code></li>
|
555
|
+
<li><code>meerschaum.utils.dataframe.get_unique_index_values()</code></li>
|
556
|
+
<li><code>meerschaum.utils.dataframe.get_uuid_cols()</code></li>
|
546
557
|
<li><code>meerschaum.utils.dataframe.parse_df_datetimes()</code></li>
|
547
558
|
<li><code>meerschaum.utils.dataframe.query_df()</code></li>
|
548
559
|
<li><code>meerschaum.utils.dataframe.to_json()</code></li>
|
@@ -559,14 +570,50 @@ def init_dash(dash_app):
|
|
559
570
|
<p></p>
|
560
571
|
<ul>
|
561
572
|
<li><code>meerschaum.utils.dtypes.are_dtypes_equal()</code></li>
|
573
|
+
<li><code>meerschaum.utils.dtypes.attempt_cast_to_bytes()</code></li>
|
574
|
+
<li><code>meerschaum.utils.dtypes.attempt_cast_to_geometry()</code></li>
|
562
575
|
<li><code>meerschaum.utils.dtypes.attempt_cast_to_numeric()</code></li>
|
576
|
+
<li><code>meerschaum.utils.dtypes.attempt_cast_to_uuid()</code></li>
|
577
|
+
<li><code>meerschaum.utils.dtypes.coerce_timezone()</code></li>
|
578
|
+
<li><code>meerschaum.utils.dtypes.deserialize_base64()</code></li>
|
579
|
+
<li><code>meerschaum.utils.dtypes.deserialize_bytes_string()</code></li>
|
580
|
+
<li><code>meerschaum.utils.dtypes.deserialize_geometry()</code></li>
|
581
|
+
<li><code>meerschaum.utils.dtypes.encode_bytes_for_bytea()</code></li>
|
582
|
+
<li><code>meerschaum.utils.dtypes.geometry_is_wkt()</code></li>
|
583
|
+
<li><code>meerschaum.utils.dtypes.get_current_timestamp()</code></li>
|
584
|
+
<li><code>meerschaum.utils.dtypes.get_geometry_type_srid()</code></li>
|
563
585
|
<li><code>meerschaum.utils.dtypes.is_dtype_numeric()</code></li>
|
586
|
+
<li><code>meerschaum.utils.dtypes.is_dtype_special()</code></li>
|
587
|
+
<li><code>meerschaum.utils.dtypes.json_serialize_value()</code></li>
|
564
588
|
<li><code>meerschaum.utils.dtypes.none_if_null()</code></li>
|
589
|
+
<li><code>meerschaum.utils.dtypes.project_geometry()</code></li>
|
565
590
|
<li><code>meerschaum.utils.dtypes.quantize_decimal()</code></li>
|
591
|
+
<li><code>meerschaum.utils.dtypes.serialize_bytes()</code></li>
|
592
|
+
<li><code>meerschaum.utils.dtypes.serialize_datetime()</code></li>
|
593
|
+
<li><code>meerschaum.utils.dtypes.serialize_date()</code></li>
|
594
|
+
<li><code>meerschaum.utils.dtypes.serialize_decimal()</code></li>
|
595
|
+
<li><code>meerschaum.utils.dtypes.serialize_geometry()</code></li>
|
596
|
+
<li><code>meerschaum.utils.dtypes.to_datetime()</code></li>
|
566
597
|
<li><code>meerschaum.utils.dtypes.to_pandas_dtype()</code></li>
|
567
598
|
<li><code>meerschaum.utils.dtypes.value_is_null()</code></li>
|
599
|
+
<li><code>meerschaum.utils.dtypes.get_current_timestamp()</code></li>
|
600
|
+
<li><code>meerschaum.utils.dtypes.get_next_precision_unit()</code></li>
|
601
|
+
<li><code>meerschaum.utils.dtypes.round_time()</code></li>
|
602
|
+
</ul>
|
603
|
+
</details>
|
604
|
+
</ul>
|
605
|
+
|
606
|
+
<ul>
|
607
|
+
<details>
|
608
|
+
<summary>
|
609
|
+
<code>meerschaum.utils.dtypes.sql</code><br>
|
610
|
+
Work with SQL data types.<br>
|
611
|
+
</summary>
|
612
|
+
<p></p>
|
613
|
+
<ul>
|
568
614
|
<li><code>meerschaum.utils.dtypes.sql.get_pd_type_from_db_type()</code></li>
|
569
615
|
<li><code>meerschaum.utils.dtypes.sql.get_db_type_from_pd_type()</code></li>
|
616
|
+
<li><code>meerschaum.utils.dtypes.sql.get_numeric_precision_scale()</code></li>
|
570
617
|
</ul>
|
571
618
|
</details>
|
572
619
|
</ul>
|
@@ -605,7 +652,6 @@ def init_dash(dash_app):
|
|
605
652
|
<p></p>
|
606
653
|
<ul>
|
607
654
|
<li><code>meerschaum.utils.misc.items_str()</code></li>
|
608
|
-
<li><code>meerschaum.utils.misc.round_time()</code></li>
|
609
655
|
<li><code>meerschaum.utils.misc.is_int()</code></li>
|
610
656
|
<li><code>meerschaum.utils.misc.interval_str()</code></li>
|
611
657
|
<li><code>meerschaum.utils.misc.filter_keywords()</code></li>
|
meerschaum/_internal/entry.py
CHANGED
@@ -12,10 +12,13 @@ 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
|
18
|
-
from meerschaum.
|
20
|
+
from meerschaum.utils.typing import SuccessTuple, List, Optional, Dict, Callable, Any, Union
|
21
|
+
from meerschaum._internal.static import STATIC_CONFIG as _STATIC_CONFIG
|
19
22
|
|
20
23
|
_systemd_result_path = None
|
21
24
|
if (_STATIC_CONFIG['environment']['systemd_log_path']) in os.environ:
|
@@ -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,
|
@@ -66,7 +104,7 @@ def entry(
|
|
66
104
|
sysargs_has_api_executor_keys,
|
67
105
|
get_pipeline_sysargs,
|
68
106
|
)
|
69
|
-
from meerschaum.
|
107
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
70
108
|
if sysargs is None:
|
71
109
|
sysargs = []
|
72
110
|
if not isinstance(sysargs, list):
|
@@ -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
|
|
@@ -206,7 +243,7 @@ def entry_with_args(
|
|
206
243
|
from meerschaum.actions import get_action
|
207
244
|
from meerschaum._internal.arguments import remove_leading_action
|
208
245
|
from meerschaum.utils.venv import active_venvs, deactivate_venv
|
209
|
-
from meerschaum.
|
246
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
210
247
|
from meerschaum.utils.typing import is_success_tuple
|
211
248
|
|
212
249
|
if _patch_args:
|
@@ -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,13 +357,13 @@ 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, (
|
328
|
-
f"Failed to execute `{action_name}` "
|
329
|
-
+ "with
|
330
|
-
f
|
364
|
+
f"Failed to execute `{action_name.strip()}` "
|
365
|
+
+ f"with `{type(e).__name__}`"
|
366
|
+
+ (f':\n\n{e}' if str(e) else '.')
|
331
367
|
+ (
|
332
368
|
"\n\nRun again with '--debug' to see a full stacktrace."
|
333
369
|
if not kw.get('debug', False) else ''
|