meerschaum 2.2.6__py3-none-any.whl → 2.3.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.
Files changed (80) hide show
  1. meerschaum/__init__.py +6 -1
  2. meerschaum/__main__.py +9 -9
  3. meerschaum/_internal/arguments/__init__.py +1 -1
  4. meerschaum/_internal/arguments/_parse_arguments.py +72 -6
  5. meerschaum/_internal/arguments/_parser.py +45 -15
  6. meerschaum/_internal/docs/index.py +265 -8
  7. meerschaum/_internal/entry.py +167 -37
  8. meerschaum/_internal/shell/Shell.py +290 -99
  9. meerschaum/_internal/shell/updates.py +175 -0
  10. meerschaum/actions/__init__.py +29 -17
  11. meerschaum/actions/api.py +12 -12
  12. meerschaum/actions/attach.py +113 -0
  13. meerschaum/actions/copy.py +68 -41
  14. meerschaum/actions/delete.py +112 -50
  15. meerschaum/actions/edit.py +3 -3
  16. meerschaum/actions/install.py +40 -32
  17. meerschaum/actions/pause.py +44 -27
  18. meerschaum/actions/register.py +19 -5
  19. meerschaum/actions/restart.py +107 -0
  20. meerschaum/actions/show.py +130 -159
  21. meerschaum/actions/start.py +161 -100
  22. meerschaum/actions/stop.py +78 -42
  23. meerschaum/actions/sync.py +3 -3
  24. meerschaum/actions/upgrade.py +28 -36
  25. meerschaum/api/_events.py +25 -1
  26. meerschaum/api/_oauth2.py +2 -0
  27. meerschaum/api/_websockets.py +2 -2
  28. meerschaum/api/dash/callbacks/jobs.py +36 -44
  29. meerschaum/api/dash/jobs.py +89 -78
  30. meerschaum/api/routes/__init__.py +1 -0
  31. meerschaum/api/routes/_actions.py +148 -17
  32. meerschaum/api/routes/_jobs.py +407 -0
  33. meerschaum/api/routes/_pipes.py +25 -25
  34. meerschaum/config/_default.py +1 -0
  35. meerschaum/config/_formatting.py +1 -0
  36. meerschaum/config/_jobs.py +1 -1
  37. meerschaum/config/_paths.py +11 -0
  38. meerschaum/config/_shell.py +84 -67
  39. meerschaum/config/_version.py +1 -1
  40. meerschaum/config/static/__init__.py +18 -0
  41. meerschaum/connectors/Connector.py +13 -7
  42. meerschaum/connectors/__init__.py +28 -15
  43. meerschaum/connectors/api/APIConnector.py +27 -1
  44. meerschaum/connectors/api/_actions.py +71 -6
  45. meerschaum/connectors/api/_jobs.py +368 -0
  46. meerschaum/connectors/api/_misc.py +1 -1
  47. meerschaum/connectors/api/_pipes.py +85 -84
  48. meerschaum/connectors/api/_request.py +13 -9
  49. meerschaum/connectors/parse.py +27 -15
  50. meerschaum/core/Pipe/_bootstrap.py +16 -8
  51. meerschaum/core/Pipe/_sync.py +3 -0
  52. meerschaum/jobs/_Executor.py +69 -0
  53. meerschaum/jobs/_Job.py +899 -0
  54. meerschaum/jobs/__init__.py +396 -0
  55. meerschaum/jobs/systemd.py +694 -0
  56. meerschaum/plugins/__init__.py +97 -12
  57. meerschaum/utils/daemon/Daemon.py +352 -147
  58. meerschaum/utils/daemon/FileDescriptorInterceptor.py +19 -10
  59. meerschaum/utils/daemon/RotatingFile.py +22 -8
  60. meerschaum/utils/daemon/StdinFile.py +121 -0
  61. meerschaum/utils/daemon/__init__.py +42 -27
  62. meerschaum/utils/daemon/_names.py +15 -13
  63. meerschaum/utils/formatting/__init__.py +83 -37
  64. meerschaum/utils/formatting/_jobs.py +146 -55
  65. meerschaum/utils/formatting/_shell.py +6 -0
  66. meerschaum/utils/misc.py +41 -22
  67. meerschaum/utils/packages/__init__.py +21 -15
  68. meerschaum/utils/packages/_packages.py +9 -6
  69. meerschaum/utils/process.py +9 -9
  70. meerschaum/utils/prompt.py +20 -7
  71. meerschaum/utils/schedule.py +21 -15
  72. meerschaum/utils/venv/__init__.py +2 -2
  73. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/METADATA +22 -25
  74. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/RECORD +80 -70
  75. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/WHEEL +1 -1
  76. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/LICENSE +0 -0
  77. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/NOTICE +0 -0
  78. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/entry_points.txt +0 -0
  79. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/top_level.txt +0 -0
  80. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/zip-safe +0 -0
@@ -8,6 +8,9 @@ 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
+ import shlex
13
+
11
14
  from meerschaum.utils.typing import Union, SuccessTuple, Any, Callable, Optional, List, Dict
12
15
  from meerschaum.utils.packages import attempt_import
13
16
  from meerschaum.config import __doc__, __version__ as version, get_config
@@ -28,7 +31,17 @@ prompt_toolkit = attempt_import('prompt_toolkit', lazy=False, warn=False, instal
28
31
  from meerschaum._internal.shell.ValidAutoSuggest import ValidAutoSuggest
29
32
  from meerschaum._internal.shell.ShellCompleter import ShellCompleter
30
33
  _clear_screen = get_config('shell', 'clear_screen', patch=True)
31
- from meerschaum.utils.misc import string_width
34
+ from meerschaum.utils.misc import string_width, remove_ansi
35
+ from meerschaum.utils.warnings import warn
36
+ from meerschaum.jobs import get_executor_keys_from_context
37
+ from meerschaum.config.static import STATIC_CONFIG
38
+ from meerschaum._internal.arguments._parse_arguments import (
39
+ split_chained_sysargs,
40
+ split_pipeline_sysargs,
41
+ parse_arguments,
42
+ parse_line,
43
+ parse_dict_to_sysargs,
44
+ )
32
45
 
33
46
  patch = True
34
47
  ### remove default cmd2 commands
@@ -56,12 +69,16 @@ hidden_commands = {
56
69
  'ipy',
57
70
  }
58
71
  reserved_completers = {
59
- 'instance', 'repo'
72
+ 'instance', 'repo', 'executor',
60
73
  }
61
74
 
62
75
  ### To handle dynamic reloading, store shell attributes externally.
63
76
  ### This is because the shell object address gets lost upon reloads.
64
77
  shell_attrs = {}
78
+ AND_KEY: str = STATIC_CONFIG['system']['arguments']['and_key']
79
+ ESCAPED_AND_KEY: str = STATIC_CONFIG['system']['arguments']['escaped_and_key']
80
+ PIPELINE_KEY: str = STATIC_CONFIG['system']['arguments']['pipeline_key']
81
+ ESCAPED_PIPELINE_KEY: str = STATIC_CONFIG['system']['arguments']['escaped_pipeline_key']
65
82
 
66
83
  def _insert_shell_actions(
67
84
  _shell: Optional['Shell'] = None,
@@ -95,8 +112,8 @@ def _insert_shell_actions(
95
112
  setattr(_shell_class, 'complete_' + a, completer)
96
113
 
97
114
  def _completer_wrapper(
98
- target: Callable[[Any], List[str]]
99
- ) -> Callable[['meerschaum._internal.shell.Shell', str, str, int, int], Any]:
115
+ target: Callable[[Any], List[str]]
116
+ ) -> Callable[['meerschaum._internal.shell.Shell', str, str, int, int], Any]:
100
117
  """
101
118
  Wrapper for `complete_` functions so they can instead use Meerschaum arguments.
102
119
  """
@@ -110,7 +127,6 @@ def _completer_wrapper(
110
127
  if _check_keys is not None:
111
128
  return _check_keys
112
129
 
113
- from meerschaum._internal.arguments._parse_arguments import parse_line
114
130
  args = parse_line(line)
115
131
  if target.__name__ != 'default_action_completer':
116
132
  if len(args['action']) > 0:
@@ -125,13 +141,13 @@ def _completer_wrapper(
125
141
 
126
142
 
127
143
  def default_action_completer(
128
- text: Optional[str] = None,
129
- line: Optional[str] = None,
130
- begin_index: Optional[int] = None,
131
- end_index: Optional[int] = None,
132
- action: Optional[List[str]] = None,
133
- **kw: Any
134
- ) -> List[str]:
144
+ text: Optional[str] = None,
145
+ line: Optional[str] = None,
146
+ begin_index: Optional[int] = None,
147
+ end_index: Optional[int] = None,
148
+ action: Optional[List[str]] = None,
149
+ **kw: Any
150
+ ) -> List[str]:
135
151
  """
136
152
  Search for subactions by default. This may be overridden by each action.
137
153
  """
@@ -148,7 +164,6 @@ def default_action_completer(
148
164
 
149
165
  def _check_complete_keys(line: str) -> Optional[List[str]]:
150
166
  from meerschaum._internal.arguments._parser import parser, get_arguments_triggers
151
- from meerschaum._internal.arguments._parse_arguments import parse_line
152
167
 
153
168
  ### TODO Add all triggers
154
169
  trigger_args = {
@@ -164,7 +179,6 @@ def _check_complete_keys(line: str) -> Optional[List[str]]:
164
179
 
165
180
  ### TODO Find out arg possibilities
166
181
  possibilities = []
167
- # last_word = line.split(' ')[-1]
168
182
  last_word = line.rstrip(' ').split(' ')[-1]
169
183
 
170
184
  if last_word.startswith('-'):
@@ -206,13 +220,11 @@ def get_shell_intro(with_color: bool = True) -> str:
206
220
  """
207
221
  from meerschaum.utils.formatting import CHARSET, ANSI, colored
208
222
  intro = get_config('shell', CHARSET, 'intro', patch=patch)
209
- intro += '\n' + ''.join(
210
- [' '
211
- for i in range(
212
- string_width(intro) - len('v' + version)
213
- )
214
- ]
215
- ) + 'v' + version
223
+ intro += (
224
+ '\n'
225
+ + (' ' * (string_width(intro) - len('v' + version)))
226
+ + f'v{version}'
227
+ )
216
228
 
217
229
  if not with_color or not ANSI:
218
230
  return intro
@@ -225,10 +237,10 @@ def get_shell_intro(with_color: bool = True) -> str:
225
237
 
226
238
  class Shell(cmd.Cmd):
227
239
  def __init__(
228
- self,
229
- actions: Optional[Dict[str, Any]] = None,
230
- sysargs: Optional[List[str]] = None
231
- ):
240
+ self,
241
+ actions: Optional[Dict[str, Any]] = None,
242
+ sysargs: Optional[List[str]] = None
243
+ ):
232
244
  """
233
245
  Customize the CLI from configuration
234
246
  """
@@ -249,11 +261,11 @@ class Shell(cmd.Cmd):
249
261
 
250
262
  from meerschaum.config._paths import SHELL_HISTORY_PATH
251
263
  shell_attrs['session'] = prompt_toolkit_shortcuts.PromptSession(
252
- history = prompt_toolkit_history.FileHistory(str(SHELL_HISTORY_PATH)),
253
- auto_suggest = ValidAutoSuggest(),
254
- completer = ShellCompleter(),
255
- complete_while_typing = True,
256
- reserve_space_for_menu = False,
264
+ history=prompt_toolkit_history.FileHistory(SHELL_HISTORY_PATH.as_posix()),
265
+ auto_suggest=ValidAutoSuggest(),
266
+ completer=ShellCompleter(),
267
+ complete_while_typing=True,
268
+ reserve_space_for_menu=False,
257
269
  )
258
270
 
259
271
  try: ### try cmd2 arguments first
@@ -283,6 +295,7 @@ class Shell(cmd.Cmd):
283
295
  shell_attrs['_sysargs'] = sysargs
284
296
  shell_attrs['_actions']['instance'] = self.do_instance
285
297
  shell_attrs['_actions']['repo'] = self.do_repo
298
+ shell_attrs['_actions']['executor'] = self.do_executor
286
299
  shell_attrs['_actions']['debug'] = self.do_debug
287
300
  shell_attrs['_update_bottom_toolbar'] = True
288
301
  shell_attrs['_old_bottom_toolbar'] = ''
@@ -297,19 +310,25 @@ class Shell(cmd.Cmd):
297
310
  except Exception as e:
298
311
  pass
299
312
 
313
+ ### Finally, spawn the version update thread.
314
+ from meerschaum._internal.shell.updates import run_version_check_thread
315
+ self._update_thread = run_version_check_thread(debug=shell_attrs.get('debug', False))
316
+
317
+
300
318
  def load_config(self, instance: Optional[str] = None):
301
319
  """
302
320
  Set attributes from the shell configuration.
303
321
  """
304
322
  from meerschaum.utils.misc import remove_ansi
305
323
  from meerschaum.utils.formatting import CHARSET, ANSI, colored
306
-
324
+ from meerschaum._internal.shell.updates import get_update_message
325
+
307
326
  if shell_attrs.get('intro', None) != '':
308
327
  self.intro = (
309
328
  get_shell_intro(with_color=False)
310
329
  if shell_attrs.get('intro', None) != ''
311
330
  else ""
312
- )
331
+ ) + get_update_message()
313
332
 
314
333
  shell_attrs['intro'] = self.intro
315
334
  shell_attrs['_prompt'] = get_config('shell', CHARSET, 'prompt', patch=patch)
@@ -333,6 +352,9 @@ class Shell(cmd.Cmd):
333
352
  shell_attrs['instance_keys'] = remove_ansi(str(instance))
334
353
  if shell_attrs.get('repo_keys', None) is None:
335
354
  shell_attrs['repo_keys'] = get_config('meerschaum', 'default_repository', patch=patch)
355
+ if shell_attrs.get('executor_keys', None) is None:
356
+ shell_attrs['executor_keys'] = get_executor_keys_from_context()
357
+
336
358
  ### this will be updated later in update_prompt ONLY IF {username} is in the prompt
337
359
  shell_attrs['username'] = ''
338
360
 
@@ -358,14 +380,19 @@ class Shell(cmd.Cmd):
358
380
  def insert_actions(self):
359
381
  from meerschaum.actions import actions
360
382
 
361
- def update_prompt(self, instance: Optional[str] = None, username: Optional[str] = None):
383
+ def update_prompt(
384
+ self,
385
+ instance: Optional[str] = None,
386
+ username: Optional[str] = None,
387
+ executor_keys: Optional[str] = None,
388
+ ):
362
389
  from meerschaum.utils.formatting import ANSI, colored
363
390
  from meerschaum._internal.entry import _shell, get_shell
364
391
 
365
392
  cmd.__builtins__['input'] = input_with_sigint(
366
393
  _old_input,
367
394
  shell_attrs['session'],
368
- shell = self,
395
+ shell=self,
369
396
  )
370
397
  prompt = shell_attrs['_prompt']
371
398
  mask = prompt
@@ -391,7 +418,8 @@ class Shell(cmd.Cmd):
391
418
  from meerschaum.connectors.sql import SQLConnector
392
419
  try:
393
420
  conn_attrs = parse_instance_keys(
394
- remove_ansi(shell_attrs['instance_keys']), construct=False
421
+ remove_ansi(shell_attrs['instance_keys']),
422
+ construct=False,
395
423
  )
396
424
  if 'username' not in conn_attrs:
397
425
  if 'uri' in conn_attrs:
@@ -405,12 +433,27 @@ class Shell(cmd.Cmd):
405
433
  if username is None:
406
434
  username = '(no username)'
407
435
  shell_attrs['username'] = (
408
- username if not ANSI else
409
- colored(username, **get_config('shell', 'ansi', 'username', 'rich'))
436
+ username
437
+ if not ANSI
438
+ else colored(username, **get_config('shell', 'ansi', 'username', 'rich'))
410
439
  )
411
440
  prompt = prompt.replace('{username}', shell_attrs['username'])
412
441
  mask = mask.replace('{username}', ''.join(['\0' for c in '{username}']))
413
442
 
443
+ if '{executor_keys}' in shell_attrs['_prompt']:
444
+ if executor_keys is None:
445
+ executor_keys = shell_attrs.get('executor_keys', None) or 'local'
446
+ shell_attrs['executor_keys'] = (
447
+ executor_keys
448
+ if not ANSI
449
+ else colored(
450
+ remove_ansi(executor_keys),
451
+ **get_config('shell', 'ansi', 'executor', 'rich')
452
+ )
453
+ )
454
+ prompt = prompt.replace('{executor_keys}', shell_attrs['executor_keys'])
455
+ mask = mask.replace('{executor_keys}', ''.join(['\0' for c in '{executor_keys}']))
456
+
414
457
  remainder_prompt = list(shell_attrs['_prompt'])
415
458
  for i, c in enumerate(mask):
416
459
  if c != '\0':
@@ -418,10 +461,13 @@ class Shell(cmd.Cmd):
418
461
  if ANSI:
419
462
  _c = colored(_c, **get_config('shell', 'ansi', 'prompt', 'rich'))
420
463
  remainder_prompt[i] = _c
464
+
421
465
  self.prompt = ''.join(remainder_prompt).replace(
422
466
  '{username}', shell_attrs['username']
423
467
  ).replace(
424
468
  '{instance}', shell_attrs['instance']
469
+ ).replace(
470
+ '{executor_keys}', shell_attrs['executor_keys']
425
471
  )
426
472
  shell_attrs['prompt'] = self.prompt
427
473
  ### flush stdout
@@ -444,6 +490,9 @@ class Shell(cmd.Cmd):
444
490
  ### make a backup of line for later
445
491
  original_line = deepcopy(line)
446
492
 
493
+ ### Escape backslashes to allow for multi-line input.
494
+ line = line.replace('\\\n', ' ')
495
+
447
496
  ### cmd2 support: check if command exists
448
497
  try:
449
498
  command = line.command
@@ -474,72 +523,125 @@ class Shell(cmd.Cmd):
474
523
  return "help " + line[len(help_token):]
475
524
 
476
525
  from meerschaum._internal.arguments import parse_line
477
- args = parse_line(line)
478
- if args.get('help', False):
526
+ try:
527
+ sysargs = shlex.split(line)
528
+ except ValueError as e:
529
+ warn(e, stack=False)
530
+ return ""
531
+
532
+ sysargs, pipeline_args = split_pipeline_sysargs(sysargs)
533
+ chained_sysargs = split_chained_sysargs(sysargs)
534
+ chained_kwargs = [
535
+ parse_arguments(_sysargs)
536
+ for _sysargs in chained_sysargs
537
+ ]
538
+
539
+ if '--help' in sysargs or '-h' in sysargs:
479
540
  from meerschaum._internal.arguments._parser import parse_help
480
- parse_help(args)
541
+ parse_help(sysargs)
481
542
  return ""
482
543
 
544
+ patch_args: Dict[str, Any] = {}
545
+
483
546
  ### NOTE: pass `shell` flag in case actions need to distinguish between
484
547
  ### being run on the command line and being run in the shell
485
- args['shell'] = True
486
- args['line'] = line
548
+ patch_args.update({
549
+ 'shell': True,
550
+ 'line': line,
551
+ })
552
+ patches: List[Dict[str, Any]] = [{} for _ in chained_kwargs]
487
553
 
488
554
  ### if debug is not set on the command line,
489
555
  ### default to shell setting
490
- if not args.get('debug', False):
491
- args['debug'] = shell_attrs['debug']
556
+ for kwargs in chained_kwargs:
557
+ if not kwargs.get('debug', False):
558
+ kwargs['debug'] = shell_attrs['debug']
492
559
 
493
560
  ### Make sure an action was provided.
494
- if not args.get('action', None):
561
+ if (
562
+ not chained_kwargs
563
+ or not chained_kwargs[0].get('action', None)
564
+ ):
495
565
  self.emptyline()
496
566
  return ''
497
567
 
498
568
  ### Strip a leading 'mrsm' if it's provided.
499
- if args['action'][0] == 'mrsm':
500
- args['action'] = args['action'][1:]
501
- if not args['action']:
502
- self.emptyline()
503
- return ''
569
+ for kwargs in chained_kwargs:
570
+ if kwargs['action'][0] == 'mrsm':
571
+ kwargs['action'] = kwargs['action'][1:]
572
+ if not kwargs['action']:
573
+ self.emptyline()
574
+ return ''
504
575
 
505
576
  ### If we don't recognize the action,
506
577
  ### make it a shell action.
507
- from meerschaum.actions import get_main_action_name
508
- main_action_name = get_main_action_name(args['action'])
509
- if main_action_name is None:
510
- if not hasattr(self, 'do_' + args['action'][0]):
511
- args['action'].insert(0, 'sh')
512
- main_action_name = 'sh'
578
+ ### TODO: make this work for chained actions
579
+ def _get_main_action_name(kwargs):
580
+ from meerschaum.actions import get_main_action_name
581
+ main_action_name = get_main_action_name(kwargs['action'])
582
+ if main_action_name is None:
583
+ if not hasattr(self, 'do_' + kwargs['action'][0]):
584
+ kwargs['action'].insert(0, 'sh')
585
+ main_action_name = 'sh'
586
+ else:
587
+ main_action_name = kwargs['action'][0]
588
+ return main_action_name
589
+
590
+ def _add_flag_to_kwargs(kwargs, i, key, shell_key=None):
591
+ shell_key = shell_key or key
592
+ shell_value = remove_ansi(shell_attrs.get(shell_key) or '')
593
+ if key == 'mrsm_instance':
594
+ default_value = get_config('meerschaum', 'instance')
595
+ elif key == 'repository':
596
+ default_value = get_config('meerschaum', 'default_repository')
597
+ elif key == 'executor_keys':
598
+ default_value = get_executor_keys_from_context()
513
599
  else:
514
- main_action_name = args['action'][0]
600
+ default_value = None
601
+
602
+ if key in kwargs or shell_value == default_value:
603
+ return
604
+
605
+ patches[i][key] = shell_value
515
606
 
516
607
  ### if no instance is provided, use current shell default,
517
608
  ### but not for the 'api' command (to avoid recursion)
518
- if 'mrsm_instance' not in args and main_action_name != 'api':
519
- args['mrsm_instance'] = str(shell_attrs['instance_keys'])
609
+ for i, kwargs in enumerate(chained_kwargs):
610
+ main_action_name = _get_main_action_name(kwargs)
611
+ if main_action_name == 'api':
612
+ continue
520
613
 
521
- if 'repository' not in args and main_action_name != 'api':
522
- args['repository'] = str(shell_attrs['repo_keys'])
614
+ _add_flag_to_kwargs(kwargs, i, 'mrsm_instance', shell_key='instance_keys')
615
+ _add_flag_to_kwargs(kwargs, i, 'repository', shell_key='repo_keys')
616
+ _add_flag_to_kwargs(kwargs, i, 'executor_keys')
523
617
 
524
618
  ### parse out empty strings
525
- if args['action'][0].strip("\"'") == '':
619
+ if chained_kwargs[0]['action'][0].strip("\"'") == '':
526
620
  self.emptyline()
527
621
  return ""
528
622
 
529
- ### If the `--daemon` flag is present, prepend 'start job'.
530
- if args.get('daemon', False) and 'stack' not in args['action']:
531
- args['action'] = ['start', 'jobs'] + args['action']
532
- main_action_name = 'start'
533
-
534
- positional_only = (main_action_name not in shell_attrs['_actions'])
623
+ positional_only = (_get_main_action_name(chained_kwargs[0]) not in shell_attrs['_actions'])
535
624
  if positional_only:
536
625
  return original_line
537
626
 
538
- from meerschaum._internal.entry import entry_with_args
627
+ ### Apply patch to all kwargs.
628
+ for i, kwargs in enumerate([_ for _ in chained_kwargs]):
629
+ kwargs.update(patches[i])
630
+
631
+ from meerschaum._internal.entry import entry_with_args, entry
632
+ sysargs_to_execute = []
633
+ for i, kwargs in enumerate(chained_kwargs):
634
+ step_kwargs = {k: v for k, v in kwargs.items() if k != 'line'}
635
+ step_sysargs = parse_dict_to_sysargs(step_kwargs)
636
+ sysargs_to_execute.extend(step_sysargs)
637
+ sysargs_to_execute.append(AND_KEY)
638
+
639
+ sysargs_to_execute = sysargs_to_execute[:-1] + (
640
+ ([':'] + pipeline_args) if pipeline_args else []
641
+ )
539
642
 
540
643
  try:
541
- success_tuple = entry_with_args(_actions=shell_attrs['_actions'], **args)
542
- # success_tuple = entry_with_args(**args)
644
+ success_tuple = entry(sysargs_to_execute, _patch_args=patch_args)
543
645
  except Exception as e:
544
646
  success_tuple = False, str(e)
545
647
 
@@ -547,9 +649,9 @@ class Shell(cmd.Cmd):
547
649
  if isinstance(success_tuple, tuple):
548
650
  print_tuple(
549
651
  success_tuple,
550
- skip_common = (not shell_attrs['debug']),
551
- upper_padding = 1,
552
- lower_padding = 0,
652
+ skip_common=(not shell_attrs['debug']),
653
+ upper_padding=1,
654
+ lower_padding=0,
553
655
  )
554
656
 
555
657
  ### Restore the old working directory.
@@ -565,12 +667,12 @@ class Shell(cmd.Cmd):
565
667
  if stop:
566
668
  return True
567
669
 
568
- def do_pass(self, line):
670
+ def do_pass(self, line, executor_keys=None):
569
671
  """
570
672
  Do nothing.
571
673
  """
572
674
 
573
- def do_debug(self, action: Optional[List[str]] = None, **kw):
675
+ def do_debug(self, action: Optional[List[str]] = None, executor_keys=None, **kw):
574
676
  """
575
677
  Toggle the shell's debug mode.
576
678
  If debug = on, append `--debug` to all commands.
@@ -600,11 +702,12 @@ class Shell(cmd.Cmd):
600
702
  info(f"Debug mode is {'on' if shell_attrs['debug'] else 'off'}.")
601
703
 
602
704
  def do_instance(
603
- self,
604
- action : Optional[List[str]] = None,
605
- debug : bool = False,
606
- **kw : Any
607
- ) -> SuccessTuple:
705
+ self,
706
+ action: Optional[List[str]] = None,
707
+ executor_keys=None,
708
+ debug: bool = False,
709
+ **kw: Any
710
+ ) -> SuccessTuple:
608
711
  """
609
712
  Temporarily set a default Meerschaum instance for the duration of the shell.
610
713
  The default instance is loaded from the Meerschaum configuraton file
@@ -661,22 +764,42 @@ class Shell(cmd.Cmd):
661
764
  return True, "Success"
662
765
 
663
766
 
664
- def complete_instance(self, text: str, line: str, begin_index: int, end_index: int):
767
+ def complete_instance(
768
+ self,
769
+ text: str,
770
+ line: str,
771
+ begin_index: int,
772
+ end_index: int,
773
+ _executor: bool = False,
774
+ _additional_options: Optional[List[str]] = None,
775
+ ):
665
776
  from meerschaum.utils.misc import get_connector_labels
666
777
  from meerschaum._internal.arguments._parse_arguments import parse_line
667
- from meerschaum.connectors import instance_types
778
+ from meerschaum.connectors import instance_types, _load_builtin_custom_connectors
779
+ if _executor:
780
+ _load_builtin_custom_connectors()
781
+ from meerschaum.jobs import executor_types
782
+
783
+ conn_types = instance_types if not _executor else executor_types
784
+
668
785
  args = parse_line(line)
669
786
  action = args['action']
670
787
  _text = action[1] if len(action) > 1 else ""
671
- return get_connector_labels(*instance_types, search_term=_text, ignore_exact_match=True)
788
+ return get_connector_labels(
789
+ *conn_types,
790
+ search_term=_text,
791
+ ignore_exact_match=True,
792
+ _additional_options=_additional_options,
793
+ )
672
794
 
673
795
 
674
796
  def do_repo(
675
- self,
676
- action: Optional[List[str]] = None,
677
- debug: bool = False,
678
- **kw: Any
679
- ) -> SuccessTuple:
797
+ self,
798
+ action: Optional[List[str]] = None,
799
+ executor_keys=None,
800
+ debug: bool = False,
801
+ **kw: Any
802
+ ) -> SuccessTuple:
680
803
  """
681
804
  Temporarily set a default Meerschaum repository for the duration of the shell.
682
805
  The default repository (mrsm.io) is loaded from the Meerschaum configuraton file
@@ -723,9 +846,65 @@ class Shell(cmd.Cmd):
723
846
  return True, "Success"
724
847
 
725
848
  def complete_repo(self, *args) -> List[str]:
726
- return self.complete_instance(*args)
849
+ results = self.complete_instance(*args)
850
+ return [result for result in results if result.startswith('api:')]
851
+
852
+ def do_executor(
853
+ self,
854
+ action: Optional[List[str]] = None,
855
+ executor_keys=None,
856
+ debug: bool = False,
857
+ **kw: Any
858
+ ) -> SuccessTuple:
859
+ """
860
+ Temporarily set a default Meerschaum executor for the duration of the shell.
861
+
862
+ You can change the default repository with `edit config`.
863
+
864
+ Usage:
865
+ executor {API label}
866
+
867
+ Examples:
868
+ ### reset to default executor
869
+ executor
870
+
871
+ ### set the executor to 'api:main'
872
+ executor api:main
873
+
874
+ Note that executors are API instances.
875
+ """
876
+ from meerschaum import get_connector
877
+ from meerschaum.connectors.parse import parse_executor_keys
878
+ from meerschaum.utils.warnings import warn, info
879
+ from meerschaum.jobs import get_executor_keys_from_context
880
+
881
+ if action is None:
882
+ action = []
883
+
884
+ try:
885
+ executor_keys = action[0]
886
+ except (IndexError, AttributeError):
887
+ executor_keys = ''
888
+ if executor_keys == '':
889
+ executor_keys = get_executor_keys_from_context()
890
+
891
+ if executor_keys == 'systemd' and get_executor_keys_from_context() != 'systemd':
892
+ warn(f"Cannot execute via `systemd`, falling back to `local`...", stack=False)
893
+ executor_keys = 'local'
894
+
895
+ conn = parse_executor_keys(executor_keys, debug=debug)
896
+
897
+ shell_attrs['executor_keys'] = str(conn).replace('systemd:main', 'systemd')
727
898
 
728
- def do_help(self, line: str) -> List[str]:
899
+ info(f"Default executor for the current shell: {executor_keys}")
900
+ return True, "Success"
901
+
902
+ def complete_executor(self, *args) -> List[str]:
903
+ from meerschaum.jobs import executor_types
904
+ results = self.complete_instance(*args, _executor=True, _additional_options=['local'])
905
+ return [result for result in results if result.split(':')[0] in executor_types]
906
+
907
+ def do_help(self, line: str, executor_keys=None) -> List[str]:
729
908
  """
730
909
  Show help for Meerschaum actions.
731
910
 
@@ -796,7 +975,7 @@ class Shell(cmd.Cmd):
796
975
  possibilities.append(name.replace('do_', ''))
797
976
  return possibilities
798
977
 
799
- def do_exit(self, params) -> True:
978
+ def do_exit(self, params, executor_keys=None) -> True:
800
979
  """
801
980
  Exit the Meerschaum shell.
802
981
  """
@@ -861,23 +1040,31 @@ def input_with_sigint(_input, session, shell: Optional[Shell] = None):
861
1040
 
862
1041
  instance_colored = (
863
1042
  colored(
864
- shell_attrs['instance_keys'], 'on ' + get_config(
865
- 'shell', 'ansi', 'instance', 'rich', 'style'
866
- )
1043
+ remove_ansi(shell_attrs['instance_keys']),
1044
+ 'on ' + get_config('shell', 'ansi', 'instance', 'rich', 'style')
867
1045
  )
868
1046
  if ANSI
869
1047
  else colored(shell_attrs['instance_keys'], 'on white')
870
1048
  )
871
1049
  repo_colored = (
872
1050
  colored(
873
- shell_attrs['repo_keys'],
1051
+ remove_ansi(shell_attrs['repo_keys']),
874
1052
  'on ' + get_config('shell', 'ansi', 'repo', 'rich', 'style')
875
1053
  )
876
1054
  if ANSI
877
1055
  else colored(shell_attrs['repo_keys'], 'on white')
878
1056
  )
1057
+ executor_colored = (
1058
+ colored(
1059
+ remove_ansi(shell_attrs['executor_keys']),
1060
+ 'on ' + get_config('shell', 'ansi', 'executor', 'rich', 'style')
1061
+ )
1062
+ if ANSI
1063
+ else colored(remove_ansi(shell_attrs['executor_keys']), 'on white')
1064
+ )
1065
+
879
1066
  try:
880
- typ, label = shell_attrs['instance_keys'].split(':')
1067
+ typ, label = shell_attrs['instance_keys'].split(':', maxsplit=1)
881
1068
  connected = typ in connectors and label in connectors[typ]
882
1069
  except Exception as e:
883
1070
  connected = False
@@ -894,8 +1081,12 @@ def input_with_sigint(_input, session, shell: Optional[Shell] = None):
894
1081
  )
895
1082
 
896
1083
  left = (
897
- colored(' Instance: ', 'on white') + instance_colored
898
- + colored(' Repo: ', 'on white') + repo_colored
1084
+ ' '
1085
+ + instance_colored
1086
+ + colored(' | ', 'on white')
1087
+ + executor_colored
1088
+ + colored(' | ', 'on white')
1089
+ + repo_colored
899
1090
  )
900
1091
  right = connection_text
901
1092
  buffer_size = (