meerschaum 2.3.0.dev3__py3-none-any.whl → 2.3.0rc2__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.
Files changed (55) hide show
  1. meerschaum/__init__.py +3 -2
  2. meerschaum/__main__.py +0 -5
  3. meerschaum/_internal/arguments/__init__.py +1 -1
  4. meerschaum/_internal/arguments/_parse_arguments.py +56 -2
  5. meerschaum/_internal/arguments/_parser.py +6 -2
  6. meerschaum/_internal/entry.py +94 -23
  7. meerschaum/_internal/shell/Shell.py +112 -35
  8. meerschaum/actions/attach.py +12 -7
  9. meerschaum/actions/copy.py +68 -41
  10. meerschaum/actions/delete.py +75 -26
  11. meerschaum/actions/edit.py +3 -3
  12. meerschaum/actions/install.py +40 -32
  13. meerschaum/actions/pause.py +44 -27
  14. meerschaum/actions/restart.py +107 -0
  15. meerschaum/actions/show.py +8 -8
  16. meerschaum/actions/start.py +27 -46
  17. meerschaum/actions/stop.py +11 -4
  18. meerschaum/api/_events.py +10 -3
  19. meerschaum/api/dash/jobs.py +69 -70
  20. meerschaum/api/routes/_actions.py +8 -3
  21. meerschaum/api/routes/_jobs.py +37 -19
  22. meerschaum/config/_default.py +1 -1
  23. meerschaum/config/_paths.py +5 -0
  24. meerschaum/config/_version.py +1 -1
  25. meerschaum/config/static/__init__.py +5 -0
  26. meerschaum/connectors/Connector.py +13 -7
  27. meerschaum/connectors/__init__.py +21 -5
  28. meerschaum/connectors/api/APIConnector.py +3 -0
  29. meerschaum/connectors/api/_jobs.py +30 -3
  30. meerschaum/connectors/parse.py +10 -14
  31. meerschaum/core/Pipe/_bootstrap.py +16 -8
  32. meerschaum/jobs/_Executor.py +69 -0
  33. meerschaum/{utils/jobs → jobs}/_Job.py +169 -21
  34. meerschaum/jobs/_LocalExecutor.py +88 -0
  35. meerschaum/jobs/_SystemdExecutor.py +613 -0
  36. meerschaum/jobs/__init__.py +388 -0
  37. meerschaum/plugins/__init__.py +6 -6
  38. meerschaum/utils/daemon/Daemon.py +7 -0
  39. meerschaum/utils/daemon/RotatingFile.py +5 -2
  40. meerschaum/utils/daemon/StdinFile.py +12 -2
  41. meerschaum/utils/daemon/__init__.py +2 -0
  42. meerschaum/utils/formatting/_jobs.py +49 -25
  43. meerschaum/utils/misc.py +23 -5
  44. meerschaum/utils/packages/_packages.py +7 -4
  45. meerschaum/utils/process.py +9 -9
  46. meerschaum/utils/venv/__init__.py +2 -2
  47. {meerschaum-2.3.0.dev3.dist-info → meerschaum-2.3.0rc2.dist-info}/METADATA +14 -17
  48. {meerschaum-2.3.0.dev3.dist-info → meerschaum-2.3.0rc2.dist-info}/RECORD +54 -50
  49. meerschaum/utils/jobs/__init__.py +0 -245
  50. {meerschaum-2.3.0.dev3.dist-info → meerschaum-2.3.0rc2.dist-info}/LICENSE +0 -0
  51. {meerschaum-2.3.0.dev3.dist-info → meerschaum-2.3.0rc2.dist-info}/NOTICE +0 -0
  52. {meerschaum-2.3.0.dev3.dist-info → meerschaum-2.3.0rc2.dist-info}/WHEEL +0 -0
  53. {meerschaum-2.3.0.dev3.dist-info → meerschaum-2.3.0rc2.dist-info}/entry_points.txt +0 -0
  54. {meerschaum-2.3.0.dev3.dist-info → meerschaum-2.3.0rc2.dist-info}/top_level.txt +0 -0
  55. {meerschaum-2.3.0.dev3.dist-info → meerschaum-2.3.0rc2.dist-info}/zip-safe +0 -0
@@ -19,7 +19,12 @@ from functools import partial
19
19
  from fastapi import WebSocket, WebSocketDisconnect
20
20
 
21
21
  from meerschaum.utils.typing import Dict, Any, SuccessTuple, List, Optional, Union
22
- from meerschaum.utils.jobs import get_jobs as _get_jobs, Job, StopMonitoringLogs
22
+ from meerschaum.jobs import (
23
+ get_jobs as _get_jobs,
24
+ Job,
25
+ StopMonitoringLogs,
26
+ get_executor_keys_from_context,
27
+ )
23
28
  from meerschaum.utils.warnings import warn
24
29
 
25
30
  from meerschaum.api import (
@@ -35,6 +40,7 @@ from meerschaum.config.static import STATIC_CONFIG
35
40
 
36
41
  JOBS_STDIN_MESSAGE: str = STATIC_CONFIG['api']['jobs']['stdin_message']
37
42
  JOBS_STOP_MESSAGE: str = STATIC_CONFIG['api']['jobs']['stop_message']
43
+ EXECUTOR_KEYS: str = get_executor_keys_from_context()
38
44
 
39
45
 
40
46
  @app.get(endpoints['jobs'], tags=['Jobs'])
@@ -46,15 +52,21 @@ def get_jobs(
46
52
  """
47
53
  Return metadata about the current jobs.
48
54
  """
49
- jobs = _get_jobs()
55
+ jobs = _get_jobs(executor_keys=EXECUTOR_KEYS, combine_local_and_systemd=False)
50
56
  return {
51
57
  name: {
52
58
  'sysargs': job.sysargs,
53
59
  'result': job.result,
60
+ 'restart': job.restart,
61
+ 'status': job.status,
54
62
  'daemon': {
55
- 'status': job.daemon.status,
56
- 'pid': job.daemon.pid,
57
- 'properties': job.daemon.properties,
63
+ 'status': job.daemon.status if job.executor_keys is None else job.status,
64
+ 'pid': job.pid,
65
+ 'properties': (
66
+ job.daemon.properties
67
+ if job.executor is None
68
+ else job.executor.get_job_properties(name)
69
+ ),
58
70
  },
59
71
  }
60
72
  for name, job in jobs.items()
@@ -71,7 +83,7 @@ def get_job(
71
83
  """
72
84
  Return metadata for a single job.
73
85
  """
74
- job = Job(name)
86
+ job = Job(name, executor_keys=EXECUTOR_KEYS)
75
87
  if not job.exists():
76
88
  raise fastapi.HTTPException(
77
89
  status_code=404,
@@ -81,10 +93,16 @@ def get_job(
81
93
  return {
82
94
  'sysargs': job.sysargs,
83
95
  'result': job.result,
96
+ 'restart': job.restart,
97
+ 'status': job.status,
84
98
  'daemon': {
85
- 'status': job.daemon.status,
86
- 'pid': job.daemon.pid,
87
- 'properties': job.daemon.properties,
99
+ 'status': job.daemon.status if job.executor_keys is None else job.status,
100
+ 'pid': job.pid,
101
+ 'properties': (
102
+ job.daemon.properties
103
+ if job.executor is None
104
+ else job.executor.get_job_properties(job.name)
105
+ ),
88
106
  },
89
107
  }
90
108
 
@@ -100,7 +118,7 @@ def create_job(
100
118
  """
101
119
  Create and start a new job.
102
120
  """
103
- job = Job(name, sysargs)
121
+ job = Job(name, sysargs, executor_keys=EXECUTOR_KEYS)
104
122
  if job.exists():
105
123
  raise fastapi.HTTPException(
106
124
  status_code=409,
@@ -120,7 +138,7 @@ def delete_job(
120
138
  """
121
139
  Delete a job.
122
140
  """
123
- job = Job(name)
141
+ job = Job(name, executor_keys=EXECUTOR_KEYS)
124
142
  return job.delete()
125
143
 
126
144
 
@@ -134,7 +152,7 @@ def get_job_exists(
134
152
  """
135
153
  Return whether a job exists.
136
154
  """
137
- job = Job(name)
155
+ job = Job(name, executor_keys=EXECUTOR_KEYS)
138
156
  return job.exists()
139
157
 
140
158
 
@@ -149,7 +167,7 @@ def get_logs(
149
167
  Return a job's log text.
150
168
  To stream log text, connect to the WebSocket endpoint `/logs/{name}/ws`.
151
169
  """
152
- job = Job(name)
170
+ job = Job(name, executor_keys=EXECUTOR_KEYS)
153
171
  if not job.exists():
154
172
  raise fastapi.HTTPException(
155
173
  status_code=404,
@@ -169,7 +187,7 @@ def start_job(
169
187
  """
170
188
  Start a job if stopped.
171
189
  """
172
- job = Job(name)
190
+ job = Job(name, executor_keys=EXECUTOR_KEYS)
173
191
  if not job.exists():
174
192
  raise fastapi.HTTPException(
175
193
  status_code=404,
@@ -188,7 +206,7 @@ def stop_job(
188
206
  """
189
207
  Stop a job if running.
190
208
  """
191
- job = Job(name)
209
+ job = Job(name, executor_keys=EXECUTOR_KEYS)
192
210
  if not job.exists():
193
211
  raise fastapi.HTTPException(
194
212
  status_code=404,
@@ -207,7 +225,7 @@ def pause_job(
207
225
  """
208
226
  Pause a job if running.
209
227
  """
210
- job = Job(name)
228
+ job = Job(name, executor_keys=EXECUTOR_KEYS)
211
229
  if not job.exists():
212
230
  raise fastapi.HTTPException(
213
231
  status_code=404,
@@ -226,7 +244,7 @@ def get_stop_time(
226
244
  """
227
245
  Get the timestamp when the job was manually stopped.
228
246
  """
229
- job = Job(name)
247
+ job = Job(name, executor_keys=EXECUTOR_KEYS)
230
248
  return job.stop_time
231
249
 
232
250
 
@@ -240,7 +258,7 @@ def get_is_blocking_on_stdin(
240
258
  """
241
259
  Return whether a job is blocking on stdin.
242
260
  """
243
- job = Job(name)
261
+ job = Job(name, executor_keys=EXECUTOR_KEYS)
244
262
  return job.is_blocking_on_stdin()
245
263
 
246
264
 
@@ -314,7 +332,7 @@ async def logs_websocket(name: str, websocket: WebSocket):
314
332
  Stream logs from a job over a websocket.
315
333
  """
316
334
  await websocket.accept()
317
- job = Job(name)
335
+ job = Job(name, executor_keys=EXECUTOR_KEYS)
318
336
  _job_clients[name].append(websocket)
319
337
 
320
338
  async def monitor_logs():
@@ -16,7 +16,7 @@ default_meerschaum_config = {
16
16
  'api_instance': 'MRSM{meerschaum:instance}',
17
17
  'web_instance': 'MRSM{meerschaum:instance}',
18
18
  'default_repository': 'api:mrsm',
19
- 'default_executor': 'local',
19
+ # 'default_executor': 'local',
20
20
  'connectors': {
21
21
  'sql': {
22
22
  'default': {},
@@ -181,6 +181,11 @@ paths = {
181
181
  'LOGS_RESOURCES_PATH' : ('{ROOT_DIR_PATH}', 'logs'),
182
182
  'DAEMON_ERROR_LOG_PATH' : ('{ROOT_DIR_PATH}', 'daemon_errors.log'),
183
183
  'CHECK_JOBS_LOCK_PATH' : ('{INTERNAL_RESOURCES_PATH}', 'check-jobs.lock'),
184
+
185
+ 'SYSTEMD_RESOURCES_PATH' : ('{DOT_CONFIG_DIR_PATH}', 'systemd'),
186
+ 'SYSTEMD_USER_RESOURCES_PATH' : ('{SYSTEMD_RESOURCES_PATH}', 'user'),
187
+ 'SYSTEMD_ROOT_RESOURCES_PATH' : ('{ROOT_DIR_PATH}', 'systemd'),
188
+ 'SYSTEMD_LOGS_RESOURCES_PATH' : ('{SYSTEMD_ROOT_RESOURCES_PATH}', 'logs'),
184
189
  }
185
190
 
186
191
  def set_root(root: Union[Path, str]):
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "2.3.0.dev3"
5
+ __version__ = "2.3.0rc2"
@@ -70,6 +70,9 @@ STATIC_CONFIG: Dict[str, Any] = {
70
70
  'noask': 'MRSM_NOASK',
71
71
  'id': 'MRSM_SERVER_ID',
72
72
  'daemon_id': 'MRSM_DAEMON_ID',
73
+ 'systemd_log_path': 'MRSM_SYSTEMD_LOG_PATH',
74
+ 'systemd_stdin_path': 'MRSM_SYSTEMD_STDIN_PATH',
75
+ 'systemd_result_path': 'MRSM_SYSTEMD_RESULT_PATH',
73
76
  'uri_regex': r'MRSM_([a-zA-Z0-9]*)_(\d*[a-zA-Z][a-zA-Z0-9-_+]*$)',
74
77
  'prefix': 'MRSM_',
75
78
  },
@@ -85,6 +88,8 @@ STATIC_CONFIG: Dict[str, Any] = {
85
88
  ),
86
89
  'underscore_standin': '<UNDERSCORE>', ### Temporary replacement for parsing.
87
90
  'failure_key': '_argparse_exception',
91
+ 'and_key': '+',
92
+ 'escaped_and_key': '++',
88
93
  },
89
94
  'urls': {
90
95
  'get-pip.py': 'https://bootstrap.pypa.io/get-pip.py',
@@ -21,11 +21,11 @@ class Connector(metaclass=abc.ABCMeta):
21
21
  The base connector class to hold connection attributes.
22
22
  """
23
23
  def __init__(
24
- self,
25
- type: Optional[str] = None,
26
- label: Optional[str] = None,
27
- **kw: Any
28
- ):
24
+ self,
25
+ type: Optional[str] = None,
26
+ label: Optional[str] = None,
27
+ **kw: Any
28
+ ):
29
29
  """
30
30
  Set the given keyword arguments as attributes.
31
31
 
@@ -101,7 +101,7 @@ class Connector(metaclass=abc.ABCMeta):
101
101
 
102
102
  ### load user config into self._attributes
103
103
  if self.type in conn_configs and self.label in conn_configs[self.type]:
104
- self._attributes.update(conn_configs[self.type][self.label])
104
+ self._attributes.update(conn_configs[self.type][self.label] or {})
105
105
 
106
106
  ### load system config into self._sys_config
107
107
  ### (deep copy so future Connectors don't inherit changes)
@@ -200,7 +200,13 @@ class Connector(metaclass=abc.ABCMeta):
200
200
  _type = self.__dict__.get('type', None)
201
201
  if _type is None:
202
202
  import re
203
- _type = re.sub(r'connector$', '', self.__class__.__name__.lower())
203
+ is_executor = self.__class__.__name__.lower().endswith('executor')
204
+ suffix_regex = (
205
+ r'connector$'
206
+ if not is_executor
207
+ else r'executor$'
208
+ )
209
+ _type = re.sub(suffix_regex, '', self.__class__.__name__.lower())
204
210
  self.__dict__['type'] = _type
205
211
  return _type
206
212
 
@@ -36,9 +36,9 @@ __all__ = (
36
36
  ### store connectors partitioned by
37
37
  ### type, label for reuse
38
38
  connectors: Dict[str, Dict[str, Connector]] = {
39
- 'api' : {},
40
- 'sql' : {},
41
- 'plugin': {},
39
+ 'api' : {},
40
+ 'sql' : {},
41
+ 'plugin' : {},
42
42
  }
43
43
  instance_types: List[str] = ['sql', 'api']
44
44
  _locks: Dict[str, RLock] = {
@@ -127,10 +127,13 @@ def get_connector(
127
127
  global _loaded_plugin_connectors
128
128
  if isinstance(type, str) and not label and ':' in type:
129
129
  type, label = type.split(':', maxsplit=1)
130
+
130
131
  with _locks['_loaded_plugin_connectors']:
131
132
  if not _loaded_plugin_connectors:
132
133
  load_plugin_connectors()
134
+ _load_builtin_custom_connectors()
133
135
  _loaded_plugin_connectors = True
136
+
134
137
  if type is None and label is None:
135
138
  default_instance_keys = get_config('meerschaum', 'instance', patch=True)
136
139
  ### recursive call to get_connector
@@ -274,7 +277,7 @@ def is_connected(keys: str, **kw) -> bool:
274
277
  return False
275
278
 
276
279
 
277
- def make_connector(cls):
280
+ def make_connector(cls, _is_executor: bool = False):
278
281
  """
279
282
  Register a class as a `Connector`.
280
283
  The `type` will be the lower case of the class name, without the suffix `connector`.
@@ -300,7 +303,12 @@ def make_connector(cls):
300
303
  >>>
301
304
  """
302
305
  import re
303
- typ = re.sub(r'connector$', '', cls.__name__.lower())
306
+ suffix_regex = (
307
+ r'connector$'
308
+ if not _is_executor
309
+ else r'executor$'
310
+ )
311
+ typ = re.sub(suffix_regex, '', cls.__name__.lower())
304
312
  with _locks['types']:
305
313
  types[typ] = cls
306
314
  with _locks['custom_types']:
@@ -363,3 +371,11 @@ def get_connector_plugin(
363
371
  )
364
372
  plugin = mrsm.Plugin(plugin_name)
365
373
  return plugin if plugin.is_installed() else None
374
+
375
+
376
+ def _load_builtin_custom_connectors():
377
+ """
378
+ Import custom connectors decorated with `@make_connector` or `@make_executor`.
379
+ """
380
+ import meerschaum.jobs._SystemdExecutor
381
+ # import meerschaum.jobs._LocalExecutor
@@ -88,6 +88,9 @@ class APIConnector(Connector):
88
88
  monitor_logs,
89
89
  monitor_logs_async,
90
90
  get_job_is_blocking_on_stdin,
91
+ get_job_began,
92
+ get_job_ended,
93
+ get_job_status,
91
94
  )
92
95
 
93
96
  def __init__(
@@ -13,7 +13,7 @@ from datetime import datetime
13
13
 
14
14
  import meerschaum as mrsm
15
15
  from meerschaum.utils.typing import Dict, Any, SuccessTuple, List, Union, Callable
16
- from meerschaum.utils.jobs import Job
16
+ from meerschaum.jobs import Job
17
17
  from meerschaum.config.static import STATIC_CONFIG
18
18
  from meerschaum.utils.warnings import warn, dprint
19
19
 
@@ -98,7 +98,6 @@ def get_job_metadata(self, name: str, debug: bool = False) -> Dict[str, Any]:
98
98
  }
99
99
  return metadata
100
100
 
101
-
102
101
  def get_job_properties(self, name: str, debug: bool = False) -> Dict[str, Any]:
103
102
  """
104
103
  Return the daemon properties for a single job.
@@ -106,6 +105,34 @@ def get_job_properties(self, name: str, debug: bool = False) -> Dict[str, Any]:
106
105
  metadata = self.get_job_metadata(name, debug=debug)
107
106
  return metadata.get('daemon', {}).get('properties', {})
108
107
 
108
+ def get_job_status(self, name: str, debug: bool = False) -> str:
109
+ """
110
+ Return the job's status.
111
+ """
112
+ metadata = self.get_job_metadata(name, debug=debug)
113
+ return metadata.get('status', 'stopped')
114
+
115
+ def get_job_began(self, name: str, debug: bool = False) -> str:
116
+ """
117
+ Return a job's `began` timestamp, if it exists.
118
+ """
119
+ properties = self.get_job_properties(name, debug=debug)
120
+ began_str = properties.get('daemon', {}).get('began', None)
121
+ if began_str is None:
122
+ return None
123
+
124
+ return began_str
125
+
126
+ def get_job_ended(self, name: str, debug: bool = False) -> str:
127
+ """
128
+ Return a job's `ended` timestamp, if it exists.
129
+ """
130
+ properties = self.get_job_properties(name, debug=debug)
131
+ ended_str = properties.get('daemon', {}).get('ended', None)
132
+ if ended_str is None:
133
+ return None
134
+
135
+ return ended_str
109
136
 
110
137
  def get_job_exists(self, name: str, debug: bool = False) -> bool:
111
138
  """
@@ -226,7 +253,7 @@ async def monitor_logs_async(
226
253
  """
227
254
  Monitor a job's log files and await a callback with the changes.
228
255
  """
229
- from meerschaum.utils.jobs import StopMonitoringLogs
256
+ from meerschaum.jobs import StopMonitoringLogs
230
257
  from meerschaum.utils.formatting._jobs import strip_timestamp_from_line
231
258
 
232
259
  websockets, websockets_exceptions = mrsm.attempt_import('websockets', 'websockets.exceptions')
@@ -87,19 +87,17 @@ def parse_connector_keys(
87
87
 
88
88
 
89
89
  def parse_instance_keys(
90
- keys: Optional[str],
91
- construct: bool = True,
92
- as_tuple: bool = False,
93
- **kw
94
- ):
90
+ keys: Optional[str],
91
+ construct: bool = True,
92
+ as_tuple: bool = False,
93
+ **kw
94
+ ):
95
95
  """
96
96
  Parse the Meerschaum instance value into a Connector object.
97
97
  """
98
98
  from meerschaum.utils.warnings import warn
99
99
  from meerschaum.config import get_config
100
100
 
101
- ### TODO Check for valid types? Not sure how to do that if construct = False.
102
-
103
101
  if keys is None:
104
102
  keys = get_config('meerschaum', 'instance')
105
103
  keys = str(keys)
@@ -120,25 +118,23 @@ def parse_repo_keys(keys: Optional[str] = None, **kw):
120
118
 
121
119
 
122
120
  def parse_executor_keys(keys: Optional[str] = None, **kw):
123
- """Parse the executor keys into an APIConnector or None."""
124
- from meerschaum.config import get_config
121
+ """Parse the executor keys into an APIConnector or string."""
122
+ from meerschaum.jobs import get_executor_keys_from_context
125
123
  if keys is None:
126
- keys = get_config('meerschaum', 'default_executor')
124
+ keys = get_executor_keys_from_context()
127
125
 
128
126
  if keys is None or keys == 'local':
129
127
  return 'local'
130
128
 
131
129
  keys = str(keys)
132
- if ':' not in keys:
133
- keys = 'api:' + keys
134
-
135
130
  return parse_connector_keys(keys, **kw)
136
131
 
137
132
 
138
133
  def is_valid_connector_keys(
139
134
  keys: str
140
135
  ) -> bool:
141
- """Verify a connector_keys string references a valid connector.
136
+ """
137
+ Verify a connector_keys string references a valid connector.
142
138
  """
143
139
  try:
144
140
  success = parse_connector_keys(keys, construct=False) is not None
@@ -88,21 +88,29 @@ def bootstrap(
88
88
 
89
89
  try:
90
90
  if yes_no(
91
- f"Would you like to edit the definition for {self}?", yes=yes, noask=noask
91
+ f"Would you like to edit the definition for {self}?",
92
+ yes=yes,
93
+ noask=noask,
94
+ default='n',
92
95
  ):
93
96
  edit_tuple = self.edit_definition(debug=debug)
94
97
  if not edit_tuple[0]:
95
98
  return edit_tuple
96
99
 
97
- if yes_no(f"Would you like to try syncing {self} now?", yes=yes, noask=noask):
100
+ if yes_no(
101
+ f"Would you like to try syncing {self} now?",
102
+ yes=yes,
103
+ noask=noask,
104
+ default='n',
105
+ ):
98
106
  sync_tuple = actions['sync'](
99
107
  ['pipes'],
100
- connector_keys = [self.connector_keys],
101
- metric_keys = [self.metric_key],
102
- location_keys = [self.location_key],
103
- mrsm_instance = str(self.instance_connector),
104
- debug = debug,
105
- shell = shell,
108
+ connector_keys=[self.connector_keys],
109
+ metric_keys=[self.metric_key],
110
+ location_keys=[self.location_key],
111
+ mrsm_instance=str(self.instance_connector),
112
+ debug=debug,
113
+ shell=shell,
106
114
  )
107
115
  if not sync_tuple[0]:
108
116
  return sync_tuple
@@ -0,0 +1,69 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Define the base class for a Job executor.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from abc import abstractmethod
11
+
12
+ from meerschaum.connectors import Connector
13
+ from meerschaum.utils.typing import List, Dict, SuccessTuple, TYPE_CHECKING
14
+
15
+ if TYPE_CHECKING:
16
+ from meerschaum.jobs import Job
17
+
18
+ class Executor(Connector):
19
+ """
20
+ Define the methods for managing jobs.
21
+ """
22
+
23
+ @abstractmethod
24
+ def get_job_exists(self, name: str, debug: bool = False) -> bool:
25
+ """
26
+ Return whether a job exists.
27
+ """
28
+
29
+ @abstractmethod
30
+ def get_jobs(self) -> Dict[str, Job]:
31
+ """
32
+ Return a dictionary of names -> Jobs.
33
+ """
34
+
35
+ @abstractmethod
36
+ def create_job(self, name: str, sysargs: List[str], debug: bool = False) -> SuccessTuple:
37
+ """
38
+ Create a new job.
39
+ """
40
+
41
+ @abstractmethod
42
+ def start_job(self, name: str, debug: bool = False) -> SuccessTuple:
43
+ """
44
+ Start a job.
45
+ """
46
+
47
+ @abstractmethod
48
+ def stop_job(self, name: str, debug: bool = False) -> SuccessTuple:
49
+ """
50
+ Stop a job.
51
+ """
52
+
53
+ @abstractmethod
54
+ def pause_job(self, name: str, debug: bool = False) -> SuccessTuple:
55
+ """
56
+ Pause a job.
57
+ """
58
+
59
+ @abstractmethod
60
+ def delete_job(self, name: str, debug: bool = False) -> SuccessTuple:
61
+ """
62
+ Delete a job.
63
+ """
64
+
65
+ @abstractmethod
66
+ def get_logs(self, name: str, debug: bool = False) -> str:
67
+ """
68
+ Return a job's log output.
69
+ """