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
meerschaum/__init__.py CHANGED
@@ -24,7 +24,7 @@ from meerschaum.utils.packages import attempt_import
24
24
  from meerschaum.core.Pipe import Pipe
25
25
  from meerschaum.plugins import Plugin
26
26
  from meerschaum.utils.venv import Venv
27
- from meerschaum.utils.jobs import Job
27
+ from meerschaum.jobs import Job, Executor, make_executor
28
28
  from meerschaum.connectors import get_connector, Connector, make_connector
29
29
  from meerschaum.utils import get_pipes
30
30
  from meerschaum.utils.formatting import pprint
@@ -45,7 +45,8 @@ __all__ = (
45
45
  "Venv",
46
46
  "Plugin",
47
47
  "Job",
48
- "Daemon",
48
+ "Executor",
49
+ "make_executor",
49
50
  "pprint",
50
51
  "attempt_import",
51
52
  "actions",
meerschaum/__main__.py CHANGED
@@ -45,11 +45,6 @@ def main(sysargs: Optional[List[str]] = None) -> None:
45
45
  parse_version(sysargs)
46
46
  return _exit(old_cwd=old_cwd)
47
47
 
48
- if ('-d' in sysargs or '--daemon' in sysargs) and ('stack' not in sysargs):
49
- from meerschaum.utils.daemon import daemon_entry
50
- _print_tuple(daemon_entry(sysargs), upper_padding=1)
51
- return _exit(old_cwd=old_cwd)
52
-
53
48
  from meerschaum._internal.entry import entry, get_shell
54
49
 
55
50
  ### Try to launch a shell if --shell is provided.
@@ -8,7 +8,7 @@ This package includes argument parsing utilities.
8
8
 
9
9
  from meerschaum._internal.arguments._parse_arguments import (
10
10
  parse_arguments, parse_line, remove_leading_action,
11
- parse_dict_to_sysargs,
11
+ parse_dict_to_sysargs, split_chained_sysargs,
12
12
  )
13
13
  from meerschaum._internal.arguments._parser import parser
14
14
  from meerschaum.plugins import add_plugin_argument
@@ -19,6 +19,44 @@ _locks = {
19
19
  _loaded_plugins_args: bool = False
20
20
 
21
21
 
22
+ def split_chained_sysargs(sysargs: List[str]) -> List[List[str]]:
23
+ """
24
+ Split a `sysargs` list containing "and" keys (`&&`)
25
+ into a list of individual `sysargs`.
26
+ """
27
+ from meerschaum.config.static import STATIC_CONFIG
28
+ and_key = STATIC_CONFIG['system']['arguments']['and_key']
29
+
30
+ if not sysargs or and_key not in sysargs:
31
+ return [sysargs]
32
+
33
+ ### Coalesce and consecutive joiners into one.
34
+ coalesce_args = []
35
+ previous_arg = None
36
+ for arg in [_arg for _arg in sysargs]:
37
+ if arg == and_key and previous_arg == and_key:
38
+ continue
39
+ coalesce_args.append(arg)
40
+ previous_arg = arg
41
+
42
+ ### Remove any joiners from the ends.
43
+ if coalesce_args[0] == and_key:
44
+ coalesce_args = coalesce_args[1:]
45
+ if coalesce_args[-1] == and_key:
46
+ coalesce_args = coalesce_args[:-1]
47
+
48
+ chained_sysargs = []
49
+ current_sysargs = []
50
+ for arg in coalesce_args:
51
+ if arg != and_key:
52
+ current_sysargs.append(arg)
53
+ else:
54
+ chained_sysargs.append(current_sysargs)
55
+ current_sysargs = []
56
+ chained_sysargs.append(current_sysargs)
57
+ return chained_sysargs
58
+
59
+
22
60
  def parse_arguments(sysargs: List[str]) -> Dict[str, Any]:
23
61
  """
24
62
  Parse a list of arguments into standard Meerschaum arguments.
@@ -206,9 +244,24 @@ def parse_dict_to_sysargs(
206
244
  args_dict: Dict[str, Any]
207
245
  ) -> List[str]:
208
246
  """Revert an arguments dictionary back to a command line list."""
247
+ import shlex
209
248
  from meerschaum._internal.arguments._parser import get_arguments_triggers
210
- sysargs = []
211
- sysargs += args_dict.get('action', [])
249
+ from meerschaum.config.static import STATIC_CONFIG
250
+ from meerschaum.utils.warnings import warn
251
+
252
+ and_key = STATIC_CONFIG['system']['arguments']['and_key']
253
+ if (line := args_dict.get('line', None)):
254
+ return shlex.split(line)
255
+
256
+ if (_sysargs := args_dict.get('sysargs', None)):
257
+ return _sysargs
258
+
259
+ action = args_dict.get('action', None)
260
+ if action and and_key in action:
261
+ warn(f"Cannot determine flags from chained actions:\n{args_dict}")
262
+
263
+ sysargs: List[str] = []
264
+ sysargs.extend(action or [])
212
265
  allow_none_args = {'location_keys'}
213
266
 
214
267
  triggers = get_arguments_triggers()
@@ -216,6 +269,7 @@ def parse_dict_to_sysargs(
216
269
  for a, t in triggers.items():
217
270
  if a == 'action' or a not in args_dict:
218
271
  continue
272
+
219
273
  ### Add boolean flags
220
274
  if isinstance(args_dict[a], bool):
221
275
  if args_dict[a] is True:
@@ -66,7 +66,7 @@ def parse_executor_keys(executor_keys_str: str) -> Union[str, None]:
66
66
  """
67
67
  Ensure that only API keys are provided for executor_keys.
68
68
  """
69
- if executor_keys_str == 'local':
69
+ if executor_keys_str in ('local', 'systemd'):
70
70
  return executor_keys_str
71
71
 
72
72
  if executor_keys_str.lower() == 'none':
@@ -214,10 +214,14 @@ groups['jobs'].add_argument(
214
214
  '--restart', action='store_true',
215
215
  help=("Restart a job if not stopped manually."),
216
216
  )
217
+ groups['jobs'].add_argument(
218
+ '--systemd', action='store_true',
219
+ help=("Create a job via systemd. Shorthand for `-e systemd`."),
220
+ )
217
221
  groups['jobs'].add_argument(
218
222
  '--executor-keys', '--executor', '-e', type=parse_executor_keys,
219
223
  help=(
220
- "Remotely execute jobs on an API instance."
224
+ "Execute jobs on an API instance or via systemd."
221
225
  ),
222
226
  )
223
227
  groups['jobs'].add_argument(
@@ -8,7 +8,29 @@ The entry point for launching Meerschaum actions.
8
8
  """
9
9
 
10
10
  from __future__ import annotations
11
+
12
+ import os
13
+ import sys
11
14
  from meerschaum.utils.typing import SuccessTuple, List, Optional, Dict, Callable, Any
15
+ from meerschaum.config.static import STATIC_CONFIG as _STATIC_CONFIG
16
+
17
+ _systemd_result_path = None
18
+ if (_STATIC_CONFIG['environment']['systemd_log_path']) in os.environ:
19
+ from meerschaum.utils.daemon import RotatingFile as _RotatingFile, StdinFile as _StdinFile
20
+ from meerschaum.config import get_config as _get_config
21
+
22
+ _systemd_result_path = os.environ[_STATIC_CONFIG['environment']['systemd_result_path']]
23
+ _systemd_log_path = os.environ[_STATIC_CONFIG['environment']['systemd_log_path']]
24
+ _systemd_log = _RotatingFile(
25
+ _systemd_log_path,
26
+ write_timestamps=True,
27
+ timestamp_format=_get_config('jobs', 'logs', 'timestamps', 'format'),
28
+ )
29
+ sys.stdout = _systemd_log
30
+ sys.stderr = _systemd_log
31
+ _systemd_stdin_path = os.environ.get(_STATIC_CONFIG['environment']['systemd_stdin_path'], None)
32
+ if _systemd_stdin_path:
33
+ sys.stdin = _StdinFile(_systemd_stdin_path)
12
34
 
13
35
  def entry(sysargs: Optional[List[str]] = None) -> SuccessTuple:
14
36
  """
@@ -18,31 +40,68 @@ def entry(sysargs: Optional[List[str]] = None) -> SuccessTuple:
18
40
  -------
19
41
  A `SuccessTuple` indicating success.
20
42
  """
21
- from meerschaum._internal.arguments import parse_arguments
43
+ import shlex
44
+ from meerschaum.utils.formatting import make_header
45
+ from meerschaum._internal.arguments import parse_arguments, split_chained_sysargs
22
46
  from meerschaum.config.static import STATIC_CONFIG
23
47
  if sysargs is None:
24
48
  sysargs = []
25
49
  if not isinstance(sysargs, list):
26
- import shlex
27
50
  sysargs = shlex.split(sysargs)
28
- args = parse_arguments(sysargs)
29
- argparse_exception = args.get(
30
- STATIC_CONFIG['system']['arguments']['failure_key'],
31
- None,
51
+
52
+ has_daemon = '-d' in sysargs or '--daemon' in sysargs
53
+ has_start_job = sysargs[:2] == ['start', 'job']
54
+ chained_sysargs = (
55
+ [sysargs]
56
+ if has_daemon or has_start_job
57
+ else split_chained_sysargs(sysargs)
32
58
  )
33
- if argparse_exception is not None:
34
- args_text = args.get('text', '')
35
- if not args_text.startswith('show arguments'):
36
- return (
37
- False,
59
+ results: List[SuccessTuple] = []
60
+
61
+ for _sysargs in chained_sysargs:
62
+ args = parse_arguments(_sysargs)
63
+ argparse_exception = args.get(
64
+ STATIC_CONFIG['system']['arguments']['failure_key'],
65
+ None,
66
+ )
67
+ if argparse_exception is not None:
68
+ args_text = args.get('text', '')
69
+ if not args_text.startswith('show arguments'):
70
+ return (
71
+ False,
72
+ (
73
+ "Invalid arguments:"
74
+ + (f"\n{args_text}" if args_text else '')
75
+ + f"\n {argparse_exception}"
76
+ )
77
+ )
78
+
79
+ entry_success, entry_msg = entry_with_args(**args)
80
+ if not entry_success:
81
+ return entry_success, entry_msg
82
+
83
+ results.append((entry_success, entry_msg))
84
+
85
+ success = all(_success for _success, _ in results)
86
+ msg = (
87
+ results[0][1]
88
+ if len(results) == 1
89
+ else 'Successfully completed steps:\n\n' + '\n'.join(
90
+ [
38
91
  (
39
- "Invalid arguments:"
40
- + (f"\n{args_text}" if args_text else '')
41
- + f"\n {argparse_exception}"
92
+ make_header(shlex.join(_sysargs))
93
+ + '\n ' + _msg + '\n'
42
94
  )
43
- )
95
+ for i, ((_, _msg), _sysargs) in enumerate(zip(results, chained_sysargs))
96
+ ]
97
+ )
98
+ )
99
+ if _systemd_result_path:
100
+ import json
101
+ with open(_systemd_result_path, 'w+', encoding='utf-8') as f:
102
+ json.dump((success, msg), f)
44
103
 
45
- return entry_with_args(**args)
104
+ return success, msg
46
105
 
47
106
 
48
107
  def entry_with_args(
@@ -57,7 +116,16 @@ def entry_with_args(
57
116
  import inspect
58
117
  from meerschaum.actions import get_action, get_main_action_name
59
118
  from meerschaum._internal.arguments import remove_leading_action
60
- from meerschaum.utils.venv import Venv, active_venvs, deactivate_venv
119
+ from meerschaum.utils.venv import active_venvs, deactivate_venv
120
+ from meerschaum.config.static import STATIC_CONFIG
121
+
122
+ and_key = STATIC_CONFIG['system']['arguments']['and_key']
123
+ escaped_and_key = STATIC_CONFIG['system']['arguments']['escaped_and_key']
124
+ if and_key in (sysargs := kw.get('sysargs', [])):
125
+ if '-d' in sysargs or '--daemon' in sysargs:
126
+ sysargs = [(arg if arg != and_key else escaped_and_key) for arg in sysargs]
127
+ return entry(sysargs)
128
+
61
129
  if kw.get('trace', None):
62
130
  from meerschaum.utils.misc import debug_trace
63
131
  debug_trace()
@@ -87,6 +155,10 @@ def entry_with_args(
87
155
  kw['action'].insert(1, api_label)
88
156
  skip_schedule = True
89
157
 
158
+ ### If the `--daemon` flag is present, prepend 'start job'.
159
+ if kw.get('daemon', False) and kw['action'][0] != 'stack':
160
+ kw['action'] = ['start', 'jobs'] + kw['action']
161
+
90
162
  action_function = get_action(kw['action'], _actions=_actions)
91
163
 
92
164
  ### If action does not exist, execute in a subshell.
@@ -117,7 +189,6 @@ def entry_with_args(
117
189
  **kw
118
190
  )
119
191
 
120
-
121
192
  if kw.get('schedule', None) and not skip_schedule:
122
193
  from meerschaum.utils.schedule import schedule_function
123
194
  from meerschaum.utils.misc import interval_str
@@ -160,16 +231,16 @@ def _do_action_wrapper(action_function, plugin_name, **kw):
160
231
  )
161
232
  )
162
233
  except KeyboardInterrupt:
163
- result = False, f"Cancelled action `{action_name}`."
234
+ result = False, f"Cancelled action `{action_name.lstrip()}`."
164
235
  return result
165
236
 
166
237
  _shells = []
167
238
  _shell = None
168
239
  def get_shell(
169
- sysargs: Optional[List[str]] = None,
170
- reload: bool = False,
171
- debug: bool = False
172
- ):
240
+ sysargs: Optional[List[str]] = None,
241
+ reload: bool = False,
242
+ debug: bool = False
243
+ ):
173
244
  """Initialize and return the Meerschaum shell object."""
174
245
  global _shell
175
246
  from meerschaum.utils.debug import dprint
@@ -8,6 +8,8 @@ This module is the entry point for the interactive shell.
8
8
  from __future__ import annotations
9
9
  import os
10
10
  from copy import deepcopy
11
+ from itertools import chain
12
+
11
13
  from meerschaum.utils.typing import Union, SuccessTuple, Any, Callable, Optional, List, Dict
12
14
  from meerschaum.utils.packages import attempt_import
13
15
  from meerschaum.config import __doc__, __version__ as version, get_config
@@ -29,6 +31,13 @@ from meerschaum._internal.shell.ValidAutoSuggest import ValidAutoSuggest
29
31
  from meerschaum._internal.shell.ShellCompleter import ShellCompleter
30
32
  _clear_screen = get_config('shell', 'clear_screen', patch=True)
31
33
  from meerschaum.utils.misc import string_width, remove_ansi
34
+ from meerschaum.jobs import get_executor_keys_from_context
35
+ from meerschaum.config.static import STATIC_CONFIG
36
+ from meerschaum._internal.arguments._parse_arguments import (
37
+ split_chained_sysargs,
38
+ parse_arguments,
39
+ parse_line,
40
+ )
32
41
 
33
42
  patch = True
34
43
  ### remove default cmd2 commands
@@ -62,6 +71,8 @@ reserved_completers = {
62
71
  ### To handle dynamic reloading, store shell attributes externally.
63
72
  ### This is because the shell object address gets lost upon reloads.
64
73
  shell_attrs = {}
74
+ AND_KEY: str = STATIC_CONFIG['system']['arguments']['and_key']
75
+ ESCAPED_AND_KEY: str = STATIC_CONFIG['system']['arguments']['escaped_and_key']
65
76
 
66
77
  def _insert_shell_actions(
67
78
  _shell: Optional['Shell'] = None,
@@ -110,7 +121,6 @@ def _completer_wrapper(
110
121
  if _check_keys is not None:
111
122
  return _check_keys
112
123
 
113
- from meerschaum._internal.arguments._parse_arguments import parse_line
114
124
  args = parse_line(line)
115
125
  if target.__name__ != 'default_action_completer':
116
126
  if len(args['action']) > 0:
@@ -148,7 +158,6 @@ def default_action_completer(
148
158
 
149
159
  def _check_complete_keys(line: str) -> Optional[List[str]]:
150
160
  from meerschaum._internal.arguments._parser import parser, get_arguments_triggers
151
- from meerschaum._internal.arguments._parse_arguments import parse_line
152
161
 
153
162
  ### TODO Add all triggers
154
163
  trigger_args = {
@@ -339,10 +348,7 @@ class Shell(cmd.Cmd):
339
348
  if shell_attrs.get('repo_keys', None) is None:
340
349
  shell_attrs['repo_keys'] = get_config('meerschaum', 'default_repository', patch=patch)
341
350
  if shell_attrs.get('executor_keys', None) is None:
342
- shell_attrs['executor_keys'] = get_config(
343
- 'meerschaum', 'default_executor',
344
- patch=patch,
345
- ) or 'local'
351
+ shell_attrs['executor_keys'] = get_executor_keys_from_context()
346
352
 
347
353
  ### this will be updated later in update_prompt ONLY IF {username} is in the prompt
348
354
  shell_attrs['username'] = ''
@@ -548,27 +554,74 @@ class Shell(cmd.Cmd):
548
554
  else:
549
555
  main_action_name = args['action'][0]
550
556
 
557
+ def _add_flag_to_sysargs(
558
+ chained_sysargs, key, value, flag, shell_key=None,
559
+ ):
560
+ shell_key = shell_key or key
561
+ shell_value = remove_ansi(shell_attrs.get(shell_key) or '')
562
+ if key == 'mrsm_instance':
563
+ default_value = get_config('meerschaum', 'instance')
564
+ elif key == 'repository':
565
+ default_value = get_config('meerschaum', 'default_repository')
566
+ elif key == 'executor_keys':
567
+ default_value = get_executor_keys_from_context()
568
+ else:
569
+ default_value = None
570
+
571
+ if shell_value == default_value:
572
+ return
573
+
574
+ for sysargs in chained_sysargs:
575
+ kwargs = parse_arguments(sysargs)
576
+ if key in kwargs:
577
+ continue
578
+ sysargs.extend([flag, value])
579
+
580
+ args['sysargs'] = list(
581
+ chain.from_iterable(
582
+ sub_sysargs + [AND_KEY]
583
+ for sub_sysargs in chained_sysargs
584
+ )
585
+ )[:-1]
586
+
587
+
551
588
  ### if no instance is provided, use current shell default,
552
589
  ### but not for the 'api' command (to avoid recursion)
553
- if 'mrsm_instance' not in args and main_action_name != 'api':
554
- args['mrsm_instance'] = str(shell_attrs['instance_keys'])
590
+ if main_action_name != 'api':
591
+ chained_sysargs = split_chained_sysargs(args['sysargs'])
592
+ chained_filtered_sysargs = split_chained_sysargs(args['filtered_sysargs'])
593
+ mrsm_instance = shell_attrs['instance_keys']
594
+ args['mrsm_instance'] = mrsm_instance
595
+ _add_flag_to_sysargs(
596
+ chained_sysargs, 'mrsm_instance', mrsm_instance, '-i', 'instance_keys',
597
+ )
598
+ _add_flag_to_sysargs(
599
+ chained_filtered_sysargs, 'mrsm_instance', mrsm_instance, '-i', 'instance_keys',
600
+ )
555
601
 
556
- if 'repository' not in args and main_action_name != 'api':
557
- args['repository'] = str(shell_attrs['repo_keys'])
602
+ repo_keys = str(shell_attrs['repo_keys'])
603
+ args['repository'] = repo_keys
604
+ _add_flag_to_sysargs(
605
+ chained_sysargs, 'repository', repo_keys, '-r', 'repo_keys',
606
+ )
607
+ _add_flag_to_sysargs(
608
+ chained_filtered_sysargs, 'repository', repo_keys, '-r', 'repo_keys',
609
+ )
558
610
 
559
- if 'executor_keys' not in args:
560
- args['executor_keys'] = remove_ansi(str(shell_attrs['executor_keys']))
611
+ executor_keys = remove_ansi(str(shell_attrs['executor_keys']))
612
+ args['executor_keys'] = remove_ansi(executor_keys)
613
+ _add_flag_to_sysargs(
614
+ chained_sysargs, 'executor_keys', executor_keys, '-e',
615
+ )
616
+ _add_flag_to_sysargs(
617
+ chained_filtered_sysargs, 'executor_keys', executor_keys, '-e',
618
+ )
561
619
 
562
620
  ### parse out empty strings
563
621
  if args['action'][0].strip("\"'") == '':
564
622
  self.emptyline()
565
623
  return ""
566
624
 
567
- ### If the `--daemon` flag is present, prepend 'start job'.
568
- if args.get('daemon', False) and 'stack' not in args['action']:
569
- args['action'] = ['start', 'jobs'] + args['action']
570
- main_action_name = 'start'
571
-
572
625
  positional_only = (main_action_name not in shell_attrs['_actions'])
573
626
  if positional_only:
574
627
  return original_line
@@ -577,7 +630,6 @@ class Shell(cmd.Cmd):
577
630
 
578
631
  try:
579
632
  success_tuple = entry_with_args(_actions=shell_attrs['_actions'], **args)
580
- # success_tuple = entry_with_args(**args)
581
633
  except Exception as e:
582
634
  success_tuple = False, str(e)
583
635
 
@@ -585,9 +637,9 @@ class Shell(cmd.Cmd):
585
637
  if isinstance(success_tuple, tuple):
586
638
  print_tuple(
587
639
  success_tuple,
588
- skip_common = (not shell_attrs['debug']),
589
- upper_padding = 1,
590
- lower_padding = 0,
640
+ skip_common=(not shell_attrs['debug']),
641
+ upper_padding=1,
642
+ lower_padding=0,
591
643
  )
592
644
 
593
645
  ### Restore the old working directory.
@@ -638,12 +690,12 @@ class Shell(cmd.Cmd):
638
690
  info(f"Debug mode is {'on' if shell_attrs['debug'] else 'off'}.")
639
691
 
640
692
  def do_instance(
641
- self,
642
- action: Optional[List[str]] = None,
643
- executor_keys=None,
644
- debug: bool = False,
645
- **kw: Any
646
- ) -> SuccessTuple:
693
+ self,
694
+ action: Optional[List[str]] = None,
695
+ executor_keys=None,
696
+ debug: bool = False,
697
+ **kw: Any
698
+ ) -> SuccessTuple:
647
699
  """
648
700
  Temporarily set a default Meerschaum instance for the duration of the shell.
649
701
  The default instance is loaded from the Meerschaum configuraton file
@@ -700,14 +752,33 @@ class Shell(cmd.Cmd):
700
752
  return True, "Success"
701
753
 
702
754
 
703
- def complete_instance(self, text: str, line: str, begin_index: int, end_index: int):
755
+ def complete_instance(
756
+ self,
757
+ text: str,
758
+ line: str,
759
+ begin_index: int,
760
+ end_index: int,
761
+ _executor: bool = False,
762
+ _additional_options: Optional[List[str]] = None,
763
+ ):
704
764
  from meerschaum.utils.misc import get_connector_labels
705
765
  from meerschaum._internal.arguments._parse_arguments import parse_line
706
- from meerschaum.connectors import instance_types
766
+ from meerschaum.connectors import instance_types, _load_builtin_custom_connectors
767
+ if _executor:
768
+ _load_builtin_custom_connectors()
769
+ from meerschaum.jobs import executor_types
770
+
771
+ conn_types = instance_types if not _executor else executor_types
772
+
707
773
  args = parse_line(line)
708
774
  action = args['action']
709
775
  _text = action[1] if len(action) > 1 else ""
710
- return get_connector_labels(*instance_types, search_term=_text, ignore_exact_match=True)
776
+ return get_connector_labels(
777
+ *conn_types,
778
+ search_term=_text,
779
+ ignore_exact_match=True,
780
+ _additional_options=_additional_options,
781
+ )
711
782
 
712
783
 
713
784
  def do_repo(
@@ -793,6 +864,7 @@ class Shell(cmd.Cmd):
793
864
  from meerschaum import get_connector
794
865
  from meerschaum.connectors.parse import parse_executor_keys
795
866
  from meerschaum.utils.warnings import warn, info
867
+ from meerschaum.jobs import get_executor_keys_from_context
796
868
 
797
869
  if action is None:
798
870
  action = []
@@ -802,18 +874,23 @@ class Shell(cmd.Cmd):
802
874
  except (IndexError, AttributeError):
803
875
  executor_keys = ''
804
876
  if executor_keys == '':
805
- executor_keys = get_config('meerschaum', 'default_executor') or 'local'
877
+ executor_keys = get_executor_keys_from_context()
878
+
879
+ if executor_keys == 'systemd' and get_executor_keys_from_context() != 'systemd':
880
+ warn(f"Cannot execute via `systemd`, falling back to `local`...", stack=False)
881
+ executor_keys = 'local'
806
882
 
807
883
  conn = parse_executor_keys(executor_keys, debug=debug)
808
884
 
809
- shell_attrs['executor_keys'] = str(conn)
885
+ shell_attrs['executor_keys'] = str(conn).replace('systemd:main', 'systemd')
810
886
 
811
887
  info(f"Default executor for the current shell: {executor_keys}")
812
888
  return True, "Success"
813
889
 
814
890
  def complete_executor(self, *args) -> List[str]:
815
- results = self.complete_instance(*args)
816
- return ['local'] + [result for result in results if result.startswith('api:')]
891
+ from meerschaum.jobs import executor_types
892
+ results = self.complete_instance(*args, _executor=True, _additional_options=['local'])
893
+ return [result for result in results if result.split(':')[0] in executor_types]
817
894
 
818
895
  def do_help(self, line: str, executor_keys=None) -> List[str]:
819
896
  """
@@ -975,7 +1052,7 @@ def input_with_sigint(_input, session, shell: Optional[Shell] = None):
975
1052
  )
976
1053
 
977
1054
  try:
978
- typ, label = shell_attrs['instance_keys'].split(':')
1055
+ typ, label = shell_attrs['instance_keys'].split(':', maxsplit=1)
979
1056
  connected = typ in connectors and label in connectors[typ]
980
1057
  except Exception as e:
981
1058
  connected = False
@@ -15,7 +15,7 @@ def attach(
15
15
  **kwargs: Any
16
16
  ) -> SuccessTuple:
17
17
  """
18
- Attach to a job.
18
+ Attach to a job, and prompt the user when blocking on input.
19
19
  """
20
20
  from meerschaum.actions import choose_subaction
21
21
  attach_options = {
@@ -32,16 +32,16 @@ def _complete_attach(
32
32
  """
33
33
  Override the default Meerschaum `complete_` function.
34
34
  """
35
- from meerschaum.actions.start import _complete_start_jobs
35
+ from meerschaum.actions.delete import _complete_delete_jobs
36
36
 
37
37
  if action is None:
38
38
  action = []
39
39
 
40
40
  options = {
41
- 'job': _complete_start_jobs,
42
- 'jobs': _complete_start_jobs,
43
- 'log': _complete_start_jobs,
44
- 'logs': _complete_start_jobs,
41
+ 'job': _complete_delete_jobs,
42
+ 'jobs': _complete_delete_jobs,
43
+ 'log': _complete_delete_jobs,
44
+ 'logs': _complete_delete_jobs,
45
45
  }
46
46
 
47
47
  if (
@@ -71,8 +71,13 @@ def _attach_jobs(
71
71
 
72
72
  name = name or action[0]
73
73
  job = mrsm.Job(name, executor_keys=executor_keys)
74
+ other_executor_keys = 'systemd' if executor_keys in (None, 'local') else 'local'
74
75
  if not job.exists():
75
- return False, f"Job '{job.name}' does not exist."
76
+ other_job = mrsm.Job(name, executor_keys=other_executor_keys)
77
+ if not other_job.exists():
78
+ return False, f"Job '{job.name}' does not exist."
79
+
80
+ job = other_job
76
81
 
77
82
  success, message = True, "Success"
78
83