meerschaum 2.3.0.dev1__py3-none-any.whl → 2.3.0.dev3__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 +2 -0
- meerschaum/_internal/arguments/_parse_arguments.py +10 -3
- meerschaum/actions/__init__.py +8 -6
- meerschaum/actions/attach.py +19 -6
- meerschaum/actions/start.py +1 -1
- meerschaum/api/routes/_jobs.py +49 -18
- meerschaum/config/_shell.py +1 -1
- meerschaum/config/_version.py +1 -1
- meerschaum/config/static/__init__.py +3 -1
- meerschaum/connectors/api/_jobs.py +80 -10
- meerschaum/utils/jobs/_Job.py +48 -22
- {meerschaum-2.3.0.dev1.dist-info → meerschaum-2.3.0.dev3.dist-info}/METADATA +1 -1
- {meerschaum-2.3.0.dev1.dist-info → meerschaum-2.3.0.dev3.dist-info}/RECORD +19 -19
- {meerschaum-2.3.0.dev1.dist-info → meerschaum-2.3.0.dev3.dist-info}/LICENSE +0 -0
- {meerschaum-2.3.0.dev1.dist-info → meerschaum-2.3.0.dev3.dist-info}/NOTICE +0 -0
- {meerschaum-2.3.0.dev1.dist-info → meerschaum-2.3.0.dev3.dist-info}/WHEEL +0 -0
- {meerschaum-2.3.0.dev1.dist-info → meerschaum-2.3.0.dev3.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.3.0.dev1.dist-info → meerschaum-2.3.0.dev3.dist-info}/top_level.txt +0 -0
- {meerschaum-2.3.0.dev1.dist-info → meerschaum-2.3.0.dev3.dist-info}/zip-safe +0 -0
meerschaum/__init__.py
CHANGED
@@ -30,6 +30,7 @@ from meerschaum.utils import get_pipes
|
|
30
30
|
from meerschaum.utils.formatting import pprint
|
31
31
|
from meerschaum._internal.docs import index as __doc__
|
32
32
|
from meerschaum.config import __version__, get_config
|
33
|
+
from meerschaum._internal.entry import entry
|
33
34
|
from meerschaum.__main__ import _close_pools
|
34
35
|
|
35
36
|
atexit.register(_close_pools)
|
@@ -55,4 +56,5 @@ __all__ = (
|
|
55
56
|
"SuccessTuple",
|
56
57
|
"Connector",
|
57
58
|
"make_connector",
|
59
|
+
"entry",
|
58
60
|
)
|
@@ -288,9 +288,6 @@ def remove_leading_action(
|
|
288
288
|
for a in action:
|
289
289
|
_action.append(a.replace('_', UNDERSCORE_STANDIN))
|
290
290
|
|
291
|
-
### e.g. 'show_pipes_baz'
|
292
|
-
action_str = '_'.join(_action)
|
293
|
-
|
294
291
|
### e.g. 'show_pipes'
|
295
292
|
action_name = action_function.__name__.lstrip('_')
|
296
293
|
|
@@ -300,6 +297,16 @@ def remove_leading_action(
|
|
300
297
|
### Strip away any leading prefices.
|
301
298
|
action_name = action_name[main_action_index:]
|
302
299
|
|
300
|
+
subaction_parts = action_name.replace(main_action_name, '').lstrip('_').split('_')
|
301
|
+
subaction_name = subaction_parts[0] if subaction_parts else None
|
302
|
+
|
303
|
+
### e.g. 'pipe' -> 'pipes'
|
304
|
+
if subaction_name and subaction_name.endswith('s') and not action[1].endswith('s'):
|
305
|
+
_action[1] += 's'
|
306
|
+
|
307
|
+
### e.g. 'show_pipes_baz'
|
308
|
+
action_str = '_'.join(_action)
|
309
|
+
|
303
310
|
if not action_str.replace(UNDERSCORE_STANDIN, '_').startswith(action_name):
|
304
311
|
warn(f"Unable to parse '{action_str}' for action '{action_name}'.")
|
305
312
|
return action
|
meerschaum/actions/__init__.py
CHANGED
@@ -12,9 +12,9 @@ from meerschaum.utils.packages import get_modules_from_package
|
|
12
12
|
_custom_actions = []
|
13
13
|
|
14
14
|
def get_subactions(
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
action: Union[str, List[str]],
|
16
|
+
_actions: Optional[Dict[str, Callable[[Any], Any]]] = None,
|
17
|
+
) -> Dict[str, Callable[[Any], Any]]:
|
18
18
|
"""
|
19
19
|
Return a dictionary of an action's sub-action functions.
|
20
20
|
|
@@ -52,9 +52,9 @@ def get_subactions(
|
|
52
52
|
|
53
53
|
|
54
54
|
def get_action(
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
action: Union[str, List[str]],
|
56
|
+
_actions: Optional[Dict[str, Callable[[Any], Any]]] = None,
|
57
|
+
) -> Union[Callable[[Any], Any], None]:
|
58
58
|
"""
|
59
59
|
Return a function corresponding to the given action list.
|
60
60
|
This may be a custom action with an underscore, in which case, allow for underscores.
|
@@ -92,6 +92,8 @@ def get_action(
|
|
92
92
|
if action[0] in _actions:
|
93
93
|
subactions = get_subactions([action[0]], _actions=_actions)
|
94
94
|
if action[1] not in subactions:
|
95
|
+
if (action[1] + 's') in subactions:
|
96
|
+
return subactions[action[1] + 's']
|
95
97
|
return _actions[action[0]]
|
96
98
|
return subactions[action[1]]
|
97
99
|
|
meerschaum/actions/attach.py
CHANGED
@@ -11,6 +11,7 @@ from meerschaum.utils.typing import Optional, List, Any, SuccessTuple
|
|
11
11
|
|
12
12
|
def attach(
|
13
13
|
action: Optional[List[str]] = None,
|
14
|
+
executor_keys: Optional[str] = None,
|
14
15
|
**kwargs: Any
|
15
16
|
) -> SuccessTuple:
|
16
17
|
"""
|
@@ -21,7 +22,7 @@ def attach(
|
|
21
22
|
'jobs': _attach_jobs,
|
22
23
|
'logs': _attach_logs,
|
23
24
|
}
|
24
|
-
return choose_subaction(action, attach_options, **kwargs)
|
25
|
+
return choose_subaction(action, attach_options, executor_keys=executor_keys, **kwargs)
|
25
26
|
|
26
27
|
|
27
28
|
def _complete_attach(
|
@@ -64,6 +65,7 @@ def _attach_jobs(
|
|
64
65
|
"""
|
65
66
|
Attach to a job, and prompt the user when blocking on input.
|
66
67
|
"""
|
68
|
+
action = action or []
|
67
69
|
if not action and not name:
|
68
70
|
return False, "Provide the name of the job to attach to."
|
69
71
|
|
@@ -72,12 +74,23 @@ def _attach_jobs(
|
|
72
74
|
if not job.exists():
|
73
75
|
return False, f"Job '{job.name}' does not exist."
|
74
76
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
77
|
+
success, message = True, "Success"
|
78
|
+
|
79
|
+
def _capture_result(result: SuccessTuple):
|
80
|
+
nonlocal success, message
|
81
|
+
success, message = result
|
82
|
+
|
83
|
+
try:
|
84
|
+
job.monitor_logs(
|
85
|
+
stop_callback_function=_capture_result,
|
86
|
+
accept_input=True,
|
87
|
+
stop_on_exit=True,
|
88
|
+
strip_timestamps=True,
|
89
|
+
)
|
90
|
+
except KeyboardInterrupt:
|
91
|
+
pass
|
79
92
|
|
80
|
-
return
|
93
|
+
return success, message
|
81
94
|
|
82
95
|
|
83
96
|
def _attach_logs(*args, **kwargs) -> SuccessTuple:
|
meerschaum/actions/start.py
CHANGED
@@ -232,7 +232,7 @@ def _start_jobs(
|
|
232
232
|
stop_success_tuple = actions['stop'](
|
233
233
|
action=['jobs'] + [_name for _name in _filtered_running_jobs],
|
234
234
|
force=True,
|
235
|
-
|
235
|
+
executor_keys=executor_keys,
|
236
236
|
debug=debug,
|
237
237
|
)
|
238
238
|
if not stop_success_tuple[0]:
|
meerschaum/api/routes/_jobs.py
CHANGED
@@ -34,6 +34,7 @@ from meerschaum.api import (
|
|
34
34
|
from meerschaum.config.static import STATIC_CONFIG
|
35
35
|
|
36
36
|
JOBS_STDIN_MESSAGE: str = STATIC_CONFIG['api']['jobs']['stdin_message']
|
37
|
+
JOBS_STOP_MESSAGE: str = STATIC_CONFIG['api']['jobs']['stop_message']
|
37
38
|
|
38
39
|
|
39
40
|
@app.get(endpoints['jobs'], tags=['Jobs'])
|
@@ -245,13 +246,10 @@ def get_is_blocking_on_stdin(
|
|
245
246
|
|
246
247
|
_job_clients = defaultdict(lambda: [])
|
247
248
|
_job_stop_events = defaultdict(lambda: asyncio.Event())
|
248
|
-
async def notify_clients(name: str, content: str):
|
249
|
+
async def notify_clients(name: str, websocket: WebSocket, content: str):
|
249
250
|
"""
|
250
251
|
Write the given content to all connected clients.
|
251
252
|
"""
|
252
|
-
if not _job_clients[name]:
|
253
|
-
_job_stop_events[name].set()
|
254
|
-
|
255
253
|
async def _notify_client(client):
|
256
254
|
try:
|
257
255
|
await client.send_text(content)
|
@@ -261,20 +259,14 @@ async def notify_clients(name: str, content: str):
|
|
261
259
|
except Exception:
|
262
260
|
pass
|
263
261
|
|
264
|
-
|
265
|
-
asyncio.create_task(_notify_client(client))
|
266
|
-
for client in _job_clients[name]
|
267
|
-
]
|
268
|
-
await asyncio.wait(notify_tasks)
|
262
|
+
await _notify_client(websocket)
|
269
263
|
|
270
264
|
|
271
|
-
async def get_input_from_clients(name):
|
265
|
+
async def get_input_from_clients(name: str, websocket: WebSocket) -> str:
|
272
266
|
"""
|
273
267
|
When a job is blocking on input, return input from the first client which provides it.
|
274
268
|
"""
|
275
|
-
print('GET INPUT FROM CLIENTS')
|
276
269
|
if not _job_clients[name]:
|
277
|
-
print('NO CLIENTS')
|
278
270
|
return ''
|
279
271
|
|
280
272
|
async def _read_client(client):
|
@@ -299,6 +291,23 @@ async def get_input_from_clients(name):
|
|
299
291
|
return task.result()
|
300
292
|
|
301
293
|
|
294
|
+
async def send_stop_message(name: str, client: WebSocket, result: SuccessTuple):
|
295
|
+
"""
|
296
|
+
Send a stop message to clients when the job stops.
|
297
|
+
"""
|
298
|
+
try:
|
299
|
+
await client.send_text(JOBS_STOP_MESSAGE)
|
300
|
+
await client.send_json(result)
|
301
|
+
except WebSocketDisconnect:
|
302
|
+
_job_stop_events[name].set()
|
303
|
+
if client in _job_clients[name]:
|
304
|
+
_job_clients[name].remove(client)
|
305
|
+
except RuntimeError:
|
306
|
+
pass
|
307
|
+
except Exception:
|
308
|
+
warn(traceback.format_exc())
|
309
|
+
|
310
|
+
|
302
311
|
@app.websocket(endpoints['logs'] + '/{name}/ws')
|
303
312
|
async def logs_websocket(name: str, websocket: WebSocket):
|
304
313
|
"""
|
@@ -309,11 +318,32 @@ async def logs_websocket(name: str, websocket: WebSocket):
|
|
309
318
|
_job_clients[name].append(websocket)
|
310
319
|
|
311
320
|
async def monitor_logs():
|
312
|
-
|
313
|
-
partial(
|
314
|
-
|
315
|
-
|
316
|
-
|
321
|
+
try:
|
322
|
+
callback_function = partial(
|
323
|
+
notify_clients,
|
324
|
+
name,
|
325
|
+
websocket,
|
326
|
+
)
|
327
|
+
input_callback_function = partial(
|
328
|
+
get_input_from_clients,
|
329
|
+
name,
|
330
|
+
websocket,
|
331
|
+
)
|
332
|
+
stop_callback_function = partial(
|
333
|
+
send_stop_message,
|
334
|
+
name,
|
335
|
+
websocket,
|
336
|
+
)
|
337
|
+
await job.monitor_logs_async(
|
338
|
+
callback_function=callback_function,
|
339
|
+
input_callback_function=input_callback_function,
|
340
|
+
stop_callback_function=stop_callback_function,
|
341
|
+
stop_event=_job_stop_events[name],
|
342
|
+
stop_on_exit=True,
|
343
|
+
accept_input=True,
|
344
|
+
)
|
345
|
+
except Exception:
|
346
|
+
warn(traceback.format_exc())
|
317
347
|
|
318
348
|
try:
|
319
349
|
token = await websocket.receive_text()
|
@@ -329,7 +359,8 @@ async def logs_websocket(name: str, websocket: WebSocket):
|
|
329
359
|
await websocket.send_text("Invalid credentials.")
|
330
360
|
await websocket.close()
|
331
361
|
except WebSocketDisconnect:
|
332
|
-
|
362
|
+
_job_stop_events[name].set()
|
363
|
+
monitor_task.cancel()
|
333
364
|
except asyncio.CancelledError:
|
334
365
|
pass
|
335
366
|
except Exception:
|
meerschaum/config/_shell.py
CHANGED
meerschaum/config/_version.py
CHANGED
@@ -43,7 +43,9 @@ STATIC_CONFIG: Dict[str, Any] = {
|
|
43
43
|
'webterm_job_name': '_webterm',
|
44
44
|
'default_timeout': 600,
|
45
45
|
'jobs': {
|
46
|
-
'stdin_message': 'MRSM_STDIN'
|
46
|
+
'stdin_message': 'MRSM_STDIN',
|
47
|
+
'stop_message': 'MRSM_STOP',
|
48
|
+
'metadata_cache_seconds': 5,
|
47
49
|
},
|
48
50
|
},
|
49
51
|
'sql': {
|
@@ -7,17 +7,21 @@ Manage jobs via the Meerschaum API.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
import asyncio
|
10
|
+
import time
|
11
|
+
import json
|
10
12
|
from datetime import datetime
|
11
13
|
|
12
14
|
import meerschaum as mrsm
|
13
15
|
from meerschaum.utils.typing import Dict, Any, SuccessTuple, List, Union, Callable
|
14
16
|
from meerschaum.utils.jobs import Job
|
15
17
|
from meerschaum.config.static import STATIC_CONFIG
|
16
|
-
from meerschaum.utils.warnings import warn
|
18
|
+
from meerschaum.utils.warnings import warn, dprint
|
17
19
|
|
18
20
|
JOBS_ENDPOINT: str = STATIC_CONFIG['api']['endpoints']['jobs']
|
19
21
|
LOGS_ENDPOINT: str = STATIC_CONFIG['api']['endpoints']['logs']
|
20
22
|
JOBS_STDIN_MESSAGE: str = STATIC_CONFIG['api']['jobs']['stdin_message']
|
23
|
+
JOBS_STOP_MESSAGE: str = STATIC_CONFIG['api']['jobs']['stop_message']
|
24
|
+
JOB_METADATA_CACHE_SECONDS: int = STATIC_CONFIG['api']['jobs']['metadata_cache_seconds']
|
21
25
|
|
22
26
|
|
23
27
|
def get_jobs(self, debug: bool = False) -> Dict[str, Job]:
|
@@ -59,6 +63,20 @@ def get_job_metadata(self, name: str, debug: bool = False) -> Dict[str, Any]:
|
|
59
63
|
"""
|
60
64
|
Return the metadata for a single job.
|
61
65
|
"""
|
66
|
+
now = time.perf_counter()
|
67
|
+
_job_metadata_cache = self.__dict__.get('_job_metadata_cache', None)
|
68
|
+
_job_metadata_timestamp = (
|
69
|
+
_job_metadata_cache.get(name, {}).get('timestamp', None)
|
70
|
+
) if _job_metadata_cache is not None else None
|
71
|
+
|
72
|
+
if (
|
73
|
+
_job_metadata_timestamp is not None
|
74
|
+
and (now - _job_metadata_timestamp) < JOB_METADATA_CACHE_SECONDS
|
75
|
+
):
|
76
|
+
if debug:
|
77
|
+
dprint(f"Returning cached metadata for job '{name}'.")
|
78
|
+
return _job_metadata_cache[name]['metadata']
|
79
|
+
|
62
80
|
response = self.get(JOBS_ENDPOINT + f"/{name}", debug=debug)
|
63
81
|
if not response:
|
64
82
|
if debug:
|
@@ -70,7 +88,15 @@ def get_job_metadata(self, name: str, debug: bool = False) -> Dict[str, Any]:
|
|
70
88
|
warn(f"Failed to get metadata for job '{name}':\n{msg}")
|
71
89
|
return {}
|
72
90
|
|
73
|
-
|
91
|
+
metadata = response.json()
|
92
|
+
if _job_metadata_cache is None:
|
93
|
+
self._job_metadata_cache = {}
|
94
|
+
|
95
|
+
self._job_metadata_cache[name] = {
|
96
|
+
'timestamp': now,
|
97
|
+
'metadata': metadata,
|
98
|
+
}
|
99
|
+
return metadata
|
74
100
|
|
75
101
|
|
76
102
|
def get_job_properties(self, name: str, debug: bool = False) -> Dict[str, Any]:
|
@@ -191,17 +217,55 @@ async def monitor_logs_async(
|
|
191
217
|
name: str,
|
192
218
|
callback_function: Callable[[Any], Any],
|
193
219
|
input_callback_function: Callable[[], str],
|
220
|
+
stop_callback_function: Callable[[SuccessTuple], str],
|
221
|
+
stop_on_exit: bool = False,
|
222
|
+
strip_timestamps: bool = False,
|
194
223
|
accept_input: bool = True,
|
195
224
|
debug: bool = False,
|
196
225
|
):
|
197
226
|
"""
|
198
227
|
Monitor a job's log files and await a callback with the changes.
|
199
228
|
"""
|
229
|
+
from meerschaum.utils.jobs import StopMonitoringLogs
|
230
|
+
from meerschaum.utils.formatting._jobs import strip_timestamp_from_line
|
231
|
+
|
200
232
|
websockets, websockets_exceptions = mrsm.attempt_import('websockets', 'websockets.exceptions')
|
201
233
|
protocol = 'ws' if self.URI.startswith('http://') else 'wss'
|
202
234
|
port = self.port if 'port' in self.__dict__ else ''
|
203
235
|
uri = f"{protocol}://{self.host}:{port}{LOGS_ENDPOINT}/{name}/ws"
|
204
236
|
|
237
|
+
async def _stdin_callback(client):
|
238
|
+
if input_callback_function is None:
|
239
|
+
return
|
240
|
+
|
241
|
+
if asyncio.iscoroutinefunction(input_callback_function):
|
242
|
+
data = await input_callback_function()
|
243
|
+
else:
|
244
|
+
data = input_callback_function()
|
245
|
+
|
246
|
+
await client.send(data)
|
247
|
+
|
248
|
+
async def _stop_callback(client):
|
249
|
+
try:
|
250
|
+
result = tuple(json.loads(await client.recv()))
|
251
|
+
except Exception as e:
|
252
|
+
warn(traceback.format_exc())
|
253
|
+
result = False, str(e)
|
254
|
+
|
255
|
+
if stop_callback_function is not None:
|
256
|
+
if asyncio.iscoroutinefunction(stop_callback_function):
|
257
|
+
await stop_callback_function(result)
|
258
|
+
else:
|
259
|
+
stop_callback_function(result)
|
260
|
+
|
261
|
+
if stop_on_exit:
|
262
|
+
raise StopMonitoringLogs
|
263
|
+
|
264
|
+
message_callbacks = {
|
265
|
+
JOBS_STDIN_MESSAGE: _stdin_callback,
|
266
|
+
JOBS_STOP_MESSAGE: _stop_callback,
|
267
|
+
}
|
268
|
+
|
205
269
|
async with websockets.connect(uri) as websocket:
|
206
270
|
try:
|
207
271
|
await websocket.send(self.token or 'no-login')
|
@@ -211,28 +275,31 @@ async def monitor_logs_async(
|
|
211
275
|
while True:
|
212
276
|
try:
|
213
277
|
response = await websocket.recv()
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
else:
|
218
|
-
data = input_callback_function()
|
219
|
-
|
220
|
-
await websocket.send(data)
|
278
|
+
callback = message_callbacks.get(response, None)
|
279
|
+
if callback is not None:
|
280
|
+
await callback(websocket)
|
221
281
|
continue
|
222
282
|
|
283
|
+
if strip_timestamps:
|
284
|
+
response = strip_timestamp_from_line(response)
|
285
|
+
|
223
286
|
if asyncio.iscoroutinefunction(callback_function):
|
224
287
|
await callback_function(response)
|
225
288
|
else:
|
226
289
|
callback_function(response)
|
227
|
-
except KeyboardInterrupt:
|
290
|
+
except (KeyboardInterrupt, StopMonitoringLogs):
|
228
291
|
await websocket.close()
|
229
292
|
break
|
230
293
|
|
294
|
+
|
231
295
|
def monitor_logs(
|
232
296
|
self,
|
233
297
|
name: str,
|
234
298
|
callback_function: Callable[[Any], Any],
|
235
299
|
input_callback_function: Callable[[None], str],
|
300
|
+
stop_callback_function: Callable[[None], str],
|
301
|
+
stop_on_exit: bool = False,
|
302
|
+
strip_timestamps: bool = False,
|
236
303
|
accept_input: bool = True,
|
237
304
|
debug: bool = False,
|
238
305
|
):
|
@@ -244,6 +311,9 @@ def monitor_logs(
|
|
244
311
|
name,
|
245
312
|
callback_function,
|
246
313
|
input_callback_function=input_callback_function,
|
314
|
+
stop_callback_function=stop_callback_function,
|
315
|
+
stop_on_exit=stop_on_exit,
|
316
|
+
strip_timestamps=strip_timestamps,
|
247
317
|
accept_input=accept_input,
|
248
318
|
debug=debug
|
249
319
|
)
|
meerschaum/utils/jobs/_Job.py
CHANGED
@@ -218,7 +218,8 @@ class Job:
|
|
218
218
|
self,
|
219
219
|
callback_function: Callable[[str], None] = partial(print, end=''),
|
220
220
|
input_callback_function: Optional[Callable[[], str]] = None,
|
221
|
-
|
221
|
+
stop_callback_function: Optional[Callable[[SuccessTuple], None]] = None,
|
222
|
+
stop_event: Optional[asyncio.Event] = None,
|
222
223
|
stop_on_exit: bool = False,
|
223
224
|
strip_timestamps: bool = False,
|
224
225
|
accept_input: bool = True,
|
@@ -237,6 +238,10 @@ class Job:
|
|
237
238
|
If provided, execute this callback when the daemon is blocking on stdin.
|
238
239
|
Defaults to `sys.stdin.readline()`.
|
239
240
|
|
241
|
+
stop_callback_function: Optional[Callable[[SuccessTuple]], str], default None
|
242
|
+
If provided, execute this callback when the daemon stops.
|
243
|
+
The job's SuccessTuple will be passed to the callback.
|
244
|
+
|
240
245
|
stop_event: Optional[asyncio.Event], default None
|
241
246
|
If provided, stop monitoring when this event is set.
|
242
247
|
You may instead raise `meerschaum.utils.jobs.StopMonitoringLogs`
|
@@ -251,13 +256,29 @@ class Job:
|
|
251
256
|
accept_input: bool, default True
|
252
257
|
If `True`, accept input when the daemon blocks on stdin.
|
253
258
|
"""
|
259
|
+
def default_input_callback_function():
|
260
|
+
return sys.stdin.readline()
|
261
|
+
|
262
|
+
if input_callback_function is None:
|
263
|
+
input_callback_function = default_input_callback_function
|
264
|
+
|
254
265
|
if self.executor is not None:
|
255
|
-
self.executor.monitor_logs(
|
266
|
+
self.executor.monitor_logs(
|
267
|
+
self.name,
|
268
|
+
callback_function,
|
269
|
+
input_callback_function=input_callback_function,
|
270
|
+
stop_callback_function=stop_callback_function,
|
271
|
+
stop_on_exit=stop_on_exit,
|
272
|
+
accept_input=accept_input,
|
273
|
+
strip_timestamps=strip_timestamps,
|
274
|
+
debug=debug,
|
275
|
+
)
|
256
276
|
return
|
257
277
|
|
258
278
|
monitor_logs_coroutine = self.monitor_logs_async(
|
259
279
|
callback_function=callback_function,
|
260
280
|
input_callback_function=input_callback_function,
|
281
|
+
stop_callback_function=stop_callback_function,
|
261
282
|
stop_event=stop_event,
|
262
283
|
stop_on_exit=stop_on_exit,
|
263
284
|
strip_timestamps=strip_timestamps,
|
@@ -270,6 +291,7 @@ class Job:
|
|
270
291
|
self,
|
271
292
|
callback_function: Callable[[str], None] = partial(print, end='', flush=True),
|
272
293
|
input_callback_function: Optional[Callable[[], str]] = None,
|
294
|
+
stop_callback_function: Optional[Callable[[SuccessTuple], None]] = None,
|
273
295
|
stop_event: Optional[asyncio.Event] = None,
|
274
296
|
stop_on_exit: bool = False,
|
275
297
|
strip_timestamps: bool = False,
|
@@ -289,6 +311,10 @@ class Job:
|
|
289
311
|
If provided, execute this callback when the daemon is blocking on stdin.
|
290
312
|
Defaults to `sys.stdin.readline()`.
|
291
313
|
|
314
|
+
stop_callback_function: Optional[Callable[[SuccessTuple]], str], default None
|
315
|
+
If provided, execute this callback when the daemon stops.
|
316
|
+
The job's SuccessTuple will be passed to the callback.
|
317
|
+
|
292
318
|
stop_event: Optional[asyncio.Event], default None
|
293
319
|
If provided, stop monitoring when this event is set.
|
294
320
|
You may instead raise `meerschaum.utils.jobs.StopMonitoringLogs`
|
@@ -314,6 +340,8 @@ class Job:
|
|
314
340
|
self.name,
|
315
341
|
callback_function,
|
316
342
|
input_callback_function=input_callback_function,
|
343
|
+
stop_callback_function=stop_callback_function,
|
344
|
+
stop_on_exit=stop_on_exit,
|
317
345
|
accept_input=accept_input,
|
318
346
|
debug=debug,
|
319
347
|
)
|
@@ -323,7 +351,7 @@ class Job:
|
|
323
351
|
|
324
352
|
events = {
|
325
353
|
'user': stop_event,
|
326
|
-
'stopped':
|
354
|
+
'stopped': asyncio.Event(),
|
327
355
|
}
|
328
356
|
combined_event = asyncio.Event()
|
329
357
|
emitted_text = False
|
@@ -340,7 +368,19 @@ class Job:
|
|
340
368
|
await asyncio.sleep(sleep_time)
|
341
369
|
sleep_time = round(sleep_time * 1.1, 2)
|
342
370
|
continue
|
343
|
-
|
371
|
+
|
372
|
+
if stop_callback_function is not None:
|
373
|
+
try:
|
374
|
+
if asyncio.iscoroutinefunction(stop_callback_function):
|
375
|
+
await stop_callback_function(self.result)
|
376
|
+
else:
|
377
|
+
stop_callback_function(self.result)
|
378
|
+
except Exception:
|
379
|
+
warn(traceback.format_exc())
|
380
|
+
|
381
|
+
if stop_on_exit:
|
382
|
+
events['stopped'].set()
|
383
|
+
|
344
384
|
break
|
345
385
|
await asyncio.sleep(0.1)
|
346
386
|
|
@@ -360,7 +400,7 @@ class Job:
|
|
360
400
|
|
361
401
|
try:
|
362
402
|
print('', end='', flush=True)
|
363
|
-
if asyncio.iscoroutinefunction(
|
403
|
+
if asyncio.iscoroutinefunction(input_callback_function):
|
364
404
|
data = await input_callback_function()
|
365
405
|
else:
|
366
406
|
data = input_callback_function()
|
@@ -444,23 +484,8 @@ class Job:
|
|
444
484
|
if latest_subfile_path != file_path:
|
445
485
|
continue
|
446
486
|
|
447
|
-
|
448
|
-
|
449
|
-
if strip_timestamps:
|
450
|
-
line = strip_timestamp_from_line(line)
|
451
|
-
try:
|
452
|
-
if asyncio.iscoroutinefunction(callback_function):
|
453
|
-
await callback_function(line)
|
454
|
-
else:
|
455
|
-
callback_function(line)
|
456
|
-
emitted_text = True
|
457
|
-
except RuntimeError:
|
458
|
-
return
|
459
|
-
except StopMonitoringLogs:
|
460
|
-
return
|
461
|
-
except Exception:
|
462
|
-
warn(f"Error in logs callback:\n{traceback.format_exc()}")
|
463
|
-
return
|
487
|
+
await emit_latest_lines()
|
488
|
+
await emit_latest_lines()
|
464
489
|
|
465
490
|
await emit_latest_lines()
|
466
491
|
|
@@ -477,6 +502,7 @@ class Job:
|
|
477
502
|
"""
|
478
503
|
Write to a job's daemon's `stdin`.
|
479
504
|
"""
|
505
|
+
### TODO implement remote method?
|
480
506
|
if self.executor is not None:
|
481
507
|
pass
|
482
508
|
|
@@ -1,9 +1,9 @@
|
|
1
|
-
meerschaum/__init__.py,sha256=
|
1
|
+
meerschaum/__init__.py,sha256=XhFLXXw1EmoFxiTMDXuNYLb6Bpzg3-V3CmvjSHDLQPk,1694
|
2
2
|
meerschaum/__main__.py,sha256=-xIXokhJ5vV784zDBF4engE3J1nj3LDHlLIRYMQ5nXY,2883
|
3
3
|
meerschaum/_internal/__init__.py,sha256=ilC7utfKtin7GAvuN34fKyUQYfPyqH0Mm3MJF5iyEf4,169
|
4
4
|
meerschaum/_internal/entry.py,sha256=4zHxcNFffxNUVVwYCjWMupG9CFeY78rKKsRfbmCPRVc,6228
|
5
5
|
meerschaum/_internal/arguments/__init__.py,sha256=HFciFQgo2ZOT19Mo6CpLhPYlpLYh2sNn1C9Lo7NMADc,519
|
6
|
-
meerschaum/_internal/arguments/_parse_arguments.py,sha256=
|
6
|
+
meerschaum/_internal/arguments/_parse_arguments.py,sha256=EJfxtQJcoc0DBTTUHLTbC1cwe_K_WAiNOXMLEXGQ7vE,10708
|
7
7
|
meerschaum/_internal/arguments/_parser.py,sha256=vRqs7NuDSC0EDkmaDLwyTi4XDjAQOhon1lIEZjjtiEQ,14842
|
8
8
|
meerschaum/_internal/docs/__init__.py,sha256=ZQYHWo6n0kfLLkyG36YXqTYvv2Pc7it5HZHMylT6cBA,126
|
9
9
|
meerschaum/_internal/docs/index.py,sha256=Qovl1fI-KqCz7KtxCIWklqUGUcJ8Sbx4UKAEarrUn_A,18056
|
@@ -21,9 +21,9 @@ meerschaum/_internal/shell/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeu
|
|
21
21
|
meerschaum/_internal/term/TermPageHandler.py,sha256=Rt5S47Pr_3HLJc8xIXpZUczYE_Dw2qT8qwf1jZFtUHQ,520
|
22
22
|
meerschaum/_internal/term/__init__.py,sha256=eXjfRzpnASWomB4xpY2AfzC_CLenkInNnVWSLZEexlg,1638
|
23
23
|
meerschaum/_internal/term/tools.py,sha256=dXVAimKD-Yv2fg2WOTr0YGBY7XDKjQqw-RizcS65YVI,727
|
24
|
-
meerschaum/actions/__init__.py,sha256=
|
24
|
+
meerschaum/actions/__init__.py,sha256=A-xwztWrq9xL_FbiBcoNB9-WKfwzYlFerh4XFl-Ao5c,11515
|
25
25
|
meerschaum/actions/api.py,sha256=8wjREHNRkl8WBgtz1KfWmAVjeFR_J2j7ghXjSyYUW3g,12603
|
26
|
-
meerschaum/actions/attach.py,sha256=
|
26
|
+
meerschaum/actions/attach.py,sha256=oH0OLCMe0ki00VM7l6fPuDzRRbRSYKlnT2NJ-w0jJ-M,2837
|
27
27
|
meerschaum/actions/bootstrap.py,sha256=9D3cBHzgZbZyWy-Y7iQgk9bpTbKEhumFKbIIThZgPXI,14058
|
28
28
|
meerschaum/actions/clear.py,sha256=OoFZE0bK5m8s3GLNZcixuVT0DMj1izXVxGCATcmUGbI,4851
|
29
29
|
meerschaum/actions/copy.py,sha256=8g3ANXfVdvuyaoXcZjgTg3BxHTOhHGrzVDOOsTBrpSU,6213
|
@@ -43,7 +43,7 @@ meerschaum/actions/sh.py,sha256=fLfTJaacKu4sjLTRqEzzYlT2WbbdZBEczsKb6F-qAek,2026
|
|
43
43
|
meerschaum/actions/show.py,sha256=kPKKKvfM_orc9wqkIDU3Nq9Hy-vOYnu3Nne7XuyigGg,28165
|
44
44
|
meerschaum/actions/sql.py,sha256=wYofwk1vGO96U2ncigGEfMtYMZeprz2FR1PRRZhkAPI,4311
|
45
45
|
meerschaum/actions/stack.py,sha256=7ODAxzmCx8i9AHxvkbr5ZtzUNPpY-iqlSVo4rZHMOw4,5900
|
46
|
-
meerschaum/actions/start.py,sha256=
|
46
|
+
meerschaum/actions/start.py,sha256=CHOQRkhUEDW65Vlr6wlzUZOkHhAOX0g5-YIwl1vRod8,18105
|
47
47
|
meerschaum/actions/stop.py,sha256=HM_oCMlQWARXa3kh60SdxvUHWTJV42R9B8VBUmXCE9U,4472
|
48
48
|
meerschaum/actions/sync.py,sha256=AkH-1O5bkUC-UElQGr0lRhrX-z18ZY2nBPSy9EsW1Kc,17506
|
49
49
|
meerschaum/actions/tag.py,sha256=SJf5qFW0ccLXjqlTdkK_0MCcrCMdg6xhYrhKdco0hdA,3053
|
@@ -117,7 +117,7 @@ meerschaum/api/routes/__init__.py,sha256=jbkeFNl51Tg8aT5gWe560ZLZLojFJsLMe5IENRj
|
|
117
117
|
meerschaum/api/routes/_actions.py,sha256=uZCf6HdSxixEJ2S2YJhzU8zx1E5mmKXPsVHN8xJW-z4,4400
|
118
118
|
meerschaum/api/routes/_connectors.py,sha256=NNbcn5xWhKqw2PqueSEaqRaZ95hFGDKazG5lE7gsssc,1849
|
119
119
|
meerschaum/api/routes/_index.py,sha256=QI6CBo6pI2Zi0a6fJHDjZfiLa9f4okb0BGe3A_JD0kM,578
|
120
|
-
meerschaum/api/routes/_jobs.py,sha256=
|
120
|
+
meerschaum/api/routes/_jobs.py,sha256=rh20XU6JTE0OUpHSmzO8G6eqETW10QSnWvKNW07nyvs,9545
|
121
121
|
meerschaum/api/routes/_login.py,sha256=psPKmFkXgYVX83NepqwIhaLsQ5uWgOc4F2QZtPGxY1A,2482
|
122
122
|
meerschaum/api/routes/_misc.py,sha256=05--9ZVFeaCgZrHER2kA3SYdK4TyfkEXOCjLvPbum-w,2469
|
123
123
|
meerschaum/api/routes/_pipes.py,sha256=1gBuE4E-QvIK_kmbmiw7uLcXjnIobFI1t4tb2skpp6E,21592
|
@@ -137,9 +137,9 @@ meerschaum/config/_patch.py,sha256=21N30q1ANmWMDQ-2RUjpMx7KafWfPQ3lKx9rrMqg1s4,1
|
|
137
137
|
meerschaum/config/_paths.py,sha256=Tfet05CY4OsIwumZO98h-L1Jy8_DH_OQ-0M1UBp83tk,9365
|
138
138
|
meerschaum/config/_preprocess.py,sha256=-AEA8m_--KivZwTQ1sWN6LTn5sio_fUr2XZ51BO6wLs,1220
|
139
139
|
meerschaum/config/_read_config.py,sha256=WFZKIXZMDe_ca0ES7ivgM_mnwShvFxLdoeisT_X5-h0,14720
|
140
|
-
meerschaum/config/_shell.py,sha256=
|
140
|
+
meerschaum/config/_shell.py,sha256=46_m49Txc5q1rGfCgO49ca48BODx45DQJi8D0zz1R18,4245
|
141
141
|
meerschaum/config/_sync.py,sha256=oK2ZujO2T1he08BXCFyiniBUevNGWSQKXLcS_jRv_7Y,4155
|
142
|
-
meerschaum/config/_version.py,sha256=
|
142
|
+
meerschaum/config/_version.py,sha256=RWnwyq5Sie04gEBTdVX8F09ia8uQfTEBjVp7fIsT_1c,76
|
143
143
|
meerschaum/config/paths.py,sha256=JjibeGN3YAdSNceRwsd42aNmeUrIgM6ndzC8qZAmNI0,621
|
144
144
|
meerschaum/config/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
145
145
|
meerschaum/config/stack/__init__.py,sha256=Yt7GNzC_hz7iUDZ4gVho_lugJO2DnXgnMtsMG_ReoRg,9114
|
@@ -147,7 +147,7 @@ meerschaum/config/stack/grafana/__init__.py,sha256=LNXQw2FvHKrD68RDhqDmi2wJjAHaK
|
|
147
147
|
meerschaum/config/stack/mosquitto/__init__.py,sha256=-OwOjq8KiBoSH_pmgCAAF3Dp3CRD4KgAEdimZSadROs,186
|
148
148
|
meerschaum/config/stack/mosquitto/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
149
149
|
meerschaum/config/stack/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
150
|
-
meerschaum/config/static/__init__.py,sha256=
|
150
|
+
meerschaum/config/static/__init__.py,sha256=ckRqGYD6xnIzWqSyPeKGVPgNCnNAIOvUGRlQLYnBpc0,4818
|
151
151
|
meerschaum/connectors/Connector.py,sha256=sa95S2upqaMpwZoVm3SwDovUoMM5JiWD6qg6QT4jxdA,6476
|
152
152
|
meerschaum/connectors/__init__.py,sha256=jo6X4AdhB2BXKIsoqHOqWtNqMaqLa_EqwopPNVE4vkc,12147
|
153
153
|
meerschaum/connectors/parse.py,sha256=F2RLb6U7lW0BGDxSHcXdCDf5g4RibKFjX8jOAcfYvFU,4451
|
@@ -156,7 +156,7 @@ meerschaum/connectors/api/APIConnector.py,sha256=CRPrlarB3QccY2sm7D1J-tZTfLcvP3B
|
|
156
156
|
meerschaum/connectors/api/__init__.py,sha256=JwKrGtuE5aOd2VnsRwudFBYyBf5IxczOwPVdNvCUgSQ,205
|
157
157
|
meerschaum/connectors/api/_actions.py,sha256=KS2Ib5_yHuN0Fw86wyg0bXORV2xY4m3DQrBtcqIIjnY,2343
|
158
158
|
meerschaum/connectors/api/_fetch.py,sha256=Khq9AFr1nk8Dsmcedb77aWhAuHw0JGgVeahDG95Q5MQ,2072
|
159
|
-
meerschaum/connectors/api/_jobs.py,sha256
|
159
|
+
meerschaum/connectors/api/_jobs.py,sha256=-8m-MRfpuldLmo4_ZSaJx-F5ok1kdYMq4c_QkubMaUc,9848
|
160
160
|
meerschaum/connectors/api/_login.py,sha256=5GsD-B214vr5EYfM3XrTUs1sTFApxZA-9dNxq8oNSyg,2050
|
161
161
|
meerschaum/connectors/api/_misc.py,sha256=OZRZBYOokKIEjmQaR8jUYgu6ZRn9VzXBChzR8CfDv_w,1092
|
162
162
|
meerschaum/connectors/api/_pipes.py,sha256=wf-_jqFBiTNw2s2msp0Ye6IyTl5JWiyC9aLBI0PQuPs,20962
|
@@ -231,18 +231,18 @@ meerschaum/utils/formatting/_jobs.py,sha256=m3dVGRS3zRNAbu7zWPdpEuou6GEOe7hHWrTz
|
|
231
231
|
meerschaum/utils/formatting/_pipes.py,sha256=wy0iWJFsFl3X2VloaiA_gp9Yx9w6tD3FQZvAQAqef4A,19492
|
232
232
|
meerschaum/utils/formatting/_pprint.py,sha256=tgrT3FyGyu5CWJYysqK3kX1xdZYorlbOk9fcU_vt9Qg,3096
|
233
233
|
meerschaum/utils/formatting/_shell.py,sha256=jsjf_GMXBpGNBbpT7skTUZrHZl5HooFzffRDc4hz5Tc,3786
|
234
|
-
meerschaum/utils/jobs/_Job.py,sha256=
|
234
|
+
meerschaum/utils/jobs/_Job.py,sha256=1glG_n8r6CuC2Al3NVYogwFMKjSxiM4GCT9o6WDtkGg,23322
|
235
235
|
meerschaum/utils/jobs/__init__.py,sha256=NfO43tpGgPBikoFV98w2cFag0aQd7Z7FOpb5NCJZ0lg,6088
|
236
236
|
meerschaum/utils/packages/__init__.py,sha256=zBy3cUy60Eht90PhgWzL_VjTjJSFpt5sA-DvrLch47A,63179
|
237
237
|
meerschaum/utils/packages/_packages.py,sha256=pWSVUEQ0XXEmnRbVUOH-c17O0VGdxuGAixuE2-XJP0I,7976
|
238
238
|
meerschaum/utils/packages/lazy_loader.py,sha256=VHnph3VozH29R4JnSSBfwtA5WKZYZQFT_GeQSShCnuc,2540
|
239
239
|
meerschaum/utils/venv/_Venv.py,sha256=sBnlmxHdAh2bx8btfVoD79-H9-cYsv5lP02IIXkyECs,3553
|
240
240
|
meerschaum/utils/venv/__init__.py,sha256=7TjSHKbUAACqteNFkqp3CS8vxm9lMo8sJJ7q_oaCMd0,24365
|
241
|
-
meerschaum-2.3.0.
|
242
|
-
meerschaum-2.3.0.
|
243
|
-
meerschaum-2.3.0.
|
244
|
-
meerschaum-2.3.0.
|
245
|
-
meerschaum-2.3.0.
|
246
|
-
meerschaum-2.3.0.
|
247
|
-
meerschaum-2.3.0.
|
248
|
-
meerschaum-2.3.0.
|
241
|
+
meerschaum-2.3.0.dev3.dist-info/LICENSE,sha256=jG2zQEdRNt88EgHUWPpXVWmOrOduUQRx7MnYV9YIPaw,11359
|
242
|
+
meerschaum-2.3.0.dev3.dist-info/METADATA,sha256=EqHz-2EqQQmaoX6ptL-7G8tJHNbfH1CzICUberopzDc,24188
|
243
|
+
meerschaum-2.3.0.dev3.dist-info/NOTICE,sha256=OTA9Fcthjf5BRvWDDIcBC_xfLpeDV-RPZh3M-HQBRtQ,114
|
244
|
+
meerschaum-2.3.0.dev3.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
|
245
|
+
meerschaum-2.3.0.dev3.dist-info/entry_points.txt,sha256=5YBVzibw-0rNA_1VjB16z5GABsOGf-CDhW4yqH8C7Gc,88
|
246
|
+
meerschaum-2.3.0.dev3.dist-info/top_level.txt,sha256=bNoSiDj0El6buocix-FRoAtJOeq1qOF5rRm2u9i7Q6A,11
|
247
|
+
meerschaum-2.3.0.dev3.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
248
|
+
meerschaum-2.3.0.dev3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|