meerschaum 2.2.7__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.
Files changed (52) hide show
  1. meerschaum/__init__.py +6 -1
  2. meerschaum/_internal/arguments/_parse_arguments.py +10 -3
  3. meerschaum/_internal/arguments/_parser.py +44 -15
  4. meerschaum/_internal/entry.py +22 -1
  5. meerschaum/_internal/shell/Shell.py +129 -31
  6. meerschaum/actions/__init__.py +8 -6
  7. meerschaum/actions/api.py +12 -12
  8. meerschaum/actions/attach.py +108 -0
  9. meerschaum/actions/delete.py +35 -26
  10. meerschaum/actions/show.py +119 -148
  11. meerschaum/actions/start.py +85 -75
  12. meerschaum/actions/stop.py +68 -39
  13. meerschaum/api/_events.py +18 -1
  14. meerschaum/api/_oauth2.py +2 -0
  15. meerschaum/api/_websockets.py +2 -2
  16. meerschaum/api/dash/jobs.py +5 -2
  17. meerschaum/api/routes/__init__.py +1 -0
  18. meerschaum/api/routes/_actions.py +122 -44
  19. meerschaum/api/routes/_jobs.py +371 -0
  20. meerschaum/api/routes/_pipes.py +5 -5
  21. meerschaum/config/_default.py +1 -0
  22. meerschaum/config/_paths.py +1 -0
  23. meerschaum/config/_shell.py +8 -3
  24. meerschaum/config/_version.py +1 -1
  25. meerschaum/config/static/__init__.py +10 -0
  26. meerschaum/connectors/__init__.py +9 -11
  27. meerschaum/connectors/api/APIConnector.py +18 -1
  28. meerschaum/connectors/api/_actions.py +60 -71
  29. meerschaum/connectors/api/_jobs.py +330 -0
  30. meerschaum/connectors/parse.py +23 -7
  31. meerschaum/plugins/__init__.py +89 -5
  32. meerschaum/utils/daemon/Daemon.py +255 -30
  33. meerschaum/utils/daemon/FileDescriptorInterceptor.py +5 -5
  34. meerschaum/utils/daemon/RotatingFile.py +10 -6
  35. meerschaum/utils/daemon/StdinFile.py +110 -0
  36. meerschaum/utils/daemon/__init__.py +13 -7
  37. meerschaum/utils/formatting/__init__.py +2 -1
  38. meerschaum/utils/formatting/_jobs.py +83 -54
  39. meerschaum/utils/formatting/_shell.py +6 -0
  40. meerschaum/utils/jobs/_Job.py +710 -0
  41. meerschaum/utils/jobs/__init__.py +245 -0
  42. meerschaum/utils/misc.py +18 -17
  43. meerschaum/utils/packages/_packages.py +2 -2
  44. meerschaum/utils/prompt.py +16 -8
  45. {meerschaum-2.2.7.dist-info → meerschaum-2.3.0.dev3.dist-info}/METADATA +9 -9
  46. {meerschaum-2.2.7.dist-info → meerschaum-2.3.0.dev3.dist-info}/RECORD +52 -46
  47. {meerschaum-2.2.7.dist-info → meerschaum-2.3.0.dev3.dist-info}/WHEEL +1 -1
  48. {meerschaum-2.2.7.dist-info → meerschaum-2.3.0.dev3.dist-info}/LICENSE +0 -0
  49. {meerschaum-2.2.7.dist-info → meerschaum-2.3.0.dev3.dist-info}/NOTICE +0 -0
  50. {meerschaum-2.2.7.dist-info → meerschaum-2.3.0.dev3.dist-info}/entry_points.txt +0 -0
  51. {meerschaum-2.2.7.dist-info → meerschaum-2.3.0.dev3.dist-info}/top_level.txt +0 -0
  52. {meerschaum-2.2.7.dist-info → meerschaum-2.3.0.dev3.dist-info}/zip-safe +0 -0
meerschaum/__init__.py CHANGED
@@ -20,15 +20,17 @@ limitations under the License.
20
20
 
21
21
  import atexit
22
22
  from meerschaum.utils.typing import SuccessTuple
23
+ from meerschaum.utils.packages import attempt_import
23
24
  from meerschaum.core.Pipe import Pipe
24
25
  from meerschaum.plugins import Plugin
25
26
  from meerschaum.utils.venv import Venv
27
+ from meerschaum.utils.jobs import Job
26
28
  from meerschaum.connectors import get_connector, Connector, make_connector
27
29
  from meerschaum.utils import get_pipes
28
30
  from meerschaum.utils.formatting import pprint
29
31
  from meerschaum._internal.docs import index as __doc__
30
32
  from meerschaum.config import __version__, get_config
31
- from meerschaum.utils.packages import attempt_import
33
+ from meerschaum._internal.entry import entry
32
34
  from meerschaum.__main__ import _close_pools
33
35
 
34
36
  atexit.register(_close_pools)
@@ -42,6 +44,8 @@ __all__ = (
42
44
  "Plugin",
43
45
  "Venv",
44
46
  "Plugin",
47
+ "Job",
48
+ "Daemon",
45
49
  "pprint",
46
50
  "attempt_import",
47
51
  "actions",
@@ -52,4 +56,5 @@ __all__ = (
52
56
  "SuccessTuple",
53
57
  "Connector",
54
58
  "make_connector",
59
+ "entry",
55
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
@@ -62,7 +62,24 @@ def parse_datetime(dt_str: str) -> Union[datetime, int, str]:
62
62
  return dt
63
63
 
64
64
 
65
- def parse_help(sysargs : Union[List[str], Dict[str, Any]]) -> None:
65
+ def parse_executor_keys(executor_keys_str: str) -> Union[str, None]:
66
+ """
67
+ Ensure that only API keys are provided for executor_keys.
68
+ """
69
+ if executor_keys_str == 'local':
70
+ return executor_keys_str
71
+
72
+ if executor_keys_str.lower() == 'none':
73
+ return 'local'
74
+
75
+ if not executor_keys_str.startswith('api:'):
76
+ from meerschaum.utils.warnings import error
77
+ error(f"Invalid exectutor keys '{executor_keys_str}'.", stack=False)
78
+
79
+ return executor_keys_str
80
+
81
+
82
+ def parse_help(sysargs: Union[List[str], Dict[str, Any]]) -> None:
66
83
  """Parse the `--help` flag to determine which help message to print."""
67
84
  from meerschaum._internal.arguments._parse_arguments import parse_arguments, parse_line
68
85
  from meerschaum.actions import actions, get_subactions
@@ -135,6 +152,7 @@ _seen_plugin_args = {}
135
152
 
136
153
  groups = {}
137
154
  groups['actions'] = parser.add_argument_group(title='Actions options')
155
+ groups['jobs'] = parser.add_argument_group(title='Jobs options')
138
156
  groups['pipes'] = parser.add_argument_group(title='Pipes options')
139
157
  groups['sync'] = parser.add_argument_group(title='Sync options')
140
158
  groups['api'] = parser.add_argument_group(title='API options')
@@ -166,18 +184,25 @@ groups['actions'].add_argument(
166
184
  help="Automatically choose the defaults answers to questions. Does not result in data loss.",
167
185
  )
168
186
  groups['actions'].add_argument(
169
- '-d', '--daemon', action='store_true',
170
- help = "Run an action as a background daemon."
187
+ '-A', '--sub-args', nargs=argparse.REMAINDER,
188
+ help = (
189
+ "Provide a list of arguments for subprocesses. " +
190
+ "You can also type sub-arguments in [] instead." +
191
+ " E.g. `stack -A='--version'`, `ls [-lh]`, `echo -A these are sub-arguments`"
192
+ )
171
193
  )
172
- groups['actions'].add_argument(
173
- '--rm', action='store_true', help="Delete a job once it has finished executing."
194
+
195
+ ### Jobs options
196
+ groups['jobs'].add_argument(
197
+ '-d', '--daemon', action='store_true',
198
+ help = "Run an action as a background daemon to create a job."
174
199
  )
175
- groups['actions'].add_argument(
200
+ groups['jobs'].add_argument(
176
201
  '--name', '--job-name', type=parse_name, help=(
177
202
  "Assign a name to a job. If no name is provided, a random name will be assigned."
178
203
  ),
179
204
  )
180
- groups['actions'].add_argument(
205
+ groups['jobs'].add_argument(
181
206
  '-s', '--schedule', '--cron', type=str,
182
207
  help = (
183
208
  "Continue executing the action according to a schedule (e.g. 'every 1 seconds'). \n"
@@ -185,15 +210,19 @@ groups['actions'].add_argument(
185
210
  + "https://red-engine.readthedocs.io/en/stable/condition_syntax/index.html"
186
211
  )
187
212
  )
188
- groups['actions'].add_argument(
189
- '-A', '--sub-args', nargs=argparse.REMAINDER,
190
- help = (
191
- "Provide a list of arguments for subprocesses. " +
192
- "You can also type sub-arguments in [] instead." +
193
- " E.g. `stack -A='--version'`, `ls [-lh]`, `echo -A these are sub-arguments`"
194
- )
213
+ groups['jobs'].add_argument(
214
+ '--restart', action='store_true',
215
+ help=("Restart a job if not stopped manually."),
216
+ )
217
+ groups['jobs'].add_argument(
218
+ '--executor-keys', '--executor', '-e', type=parse_executor_keys,
219
+ help=(
220
+ "Remotely execute jobs on an API instance."
221
+ ),
222
+ )
223
+ groups['jobs'].add_argument(
224
+ '--rm', action='store_true', help="Delete a job once it has finished executing."
195
225
  )
196
-
197
226
  ### Pipes options
198
227
  groups['pipes'].add_argument(
199
228
  '-c', '-C', '--connector-keys', nargs='+',
@@ -54,6 +54,7 @@ def entry_with_args(
54
54
  """
55
55
  import sys
56
56
  import functools
57
+ import inspect
57
58
  from meerschaum.actions import get_action, get_main_action_name
58
59
  from meerschaum._internal.arguments import remove_leading_action
59
60
  from meerschaum.utils.venv import Venv, active_venvs, deactivate_venv
@@ -67,6 +68,25 @@ def entry_with_args(
67
68
  ):
68
69
  return get_shell().cmdloop()
69
70
 
71
+ skip_schedule = False
72
+
73
+ executor_keys = kw.get('executor_keys', None)
74
+ if executor_keys is None:
75
+ executor_keys = 'local'
76
+
77
+ if executor_keys.startswith('api:'):
78
+ intended_action_function = get_action(kw['action'], _actions=_actions)
79
+ function_accepts_executor_keys = (
80
+ 'executor_keys' in inspect.signature(intended_action_function).parameters
81
+ if intended_action_function is not None
82
+ else False
83
+ )
84
+ if not function_accepts_executor_keys:
85
+ api_label = executor_keys.split(':')[-1]
86
+ kw['action'].insert(0, 'api')
87
+ kw['action'].insert(1, api_label)
88
+ skip_schedule = True
89
+
70
90
  action_function = get_action(kw['action'], _actions=_actions)
71
91
 
72
92
  ### If action does not exist, execute in a subshell.
@@ -81,7 +101,6 @@ def entry_with_args(
81
101
  ) else None
82
102
  )
83
103
 
84
- skip_schedule = False
85
104
  if (
86
105
  kw['action']
87
106
  and kw['action'][0] == 'start'
@@ -97,6 +116,8 @@ def entry_with_args(
97
116
  plugin_name,
98
117
  **kw
99
118
  )
119
+
120
+
100
121
  if kw.get('schedule', None) and not skip_schedule:
101
122
  from meerschaum.utils.schedule import schedule_function
102
123
  from meerschaum.utils.misc import interval_str
@@ -28,7 +28,7 @@ prompt_toolkit = attempt_import('prompt_toolkit', lazy=False, warn=False, instal
28
28
  from meerschaum._internal.shell.ValidAutoSuggest import ValidAutoSuggest
29
29
  from meerschaum._internal.shell.ShellCompleter import ShellCompleter
30
30
  _clear_screen = get_config('shell', 'clear_screen', patch=True)
31
- from meerschaum.utils.misc import string_width
31
+ from meerschaum.utils.misc import string_width, remove_ansi
32
32
 
33
33
  patch = True
34
34
  ### remove default cmd2 commands
@@ -56,7 +56,7 @@ hidden_commands = {
56
56
  'ipy',
57
57
  }
58
58
  reserved_completers = {
59
- 'instance', 'repo'
59
+ 'instance', 'repo', 'executor',
60
60
  }
61
61
 
62
62
  ### To handle dynamic reloading, store shell attributes externally.
@@ -247,11 +247,11 @@ class Shell(cmd.Cmd):
247
247
 
248
248
  from meerschaum.config._paths import SHELL_HISTORY_PATH
249
249
  shell_attrs['session'] = prompt_toolkit_shortcuts.PromptSession(
250
- history = prompt_toolkit_history.FileHistory(str(SHELL_HISTORY_PATH)),
251
- auto_suggest = ValidAutoSuggest(),
252
- completer = ShellCompleter(),
253
- complete_while_typing = True,
254
- reserve_space_for_menu = False,
250
+ history=prompt_toolkit_history.FileHistory(SHELL_HISTORY_PATH.as_posix()),
251
+ auto_suggest=ValidAutoSuggest(),
252
+ completer=ShellCompleter(),
253
+ complete_while_typing=True,
254
+ reserve_space_for_menu=False,
255
255
  )
256
256
 
257
257
  try: ### try cmd2 arguments first
@@ -281,6 +281,7 @@ class Shell(cmd.Cmd):
281
281
  shell_attrs['_sysargs'] = sysargs
282
282
  shell_attrs['_actions']['instance'] = self.do_instance
283
283
  shell_attrs['_actions']['repo'] = self.do_repo
284
+ shell_attrs['_actions']['executor'] = self.do_executor
284
285
  shell_attrs['_actions']['debug'] = self.do_debug
285
286
  shell_attrs['_update_bottom_toolbar'] = True
286
287
  shell_attrs['_old_bottom_toolbar'] = ''
@@ -337,6 +338,12 @@ class Shell(cmd.Cmd):
337
338
  shell_attrs['instance_keys'] = remove_ansi(str(instance))
338
339
  if shell_attrs.get('repo_keys', None) is None:
339
340
  shell_attrs['repo_keys'] = get_config('meerschaum', 'default_repository', patch=patch)
341
+ 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'
346
+
340
347
  ### this will be updated later in update_prompt ONLY IF {username} is in the prompt
341
348
  shell_attrs['username'] = ''
342
349
 
@@ -362,14 +369,19 @@ class Shell(cmd.Cmd):
362
369
  def insert_actions(self):
363
370
  from meerschaum.actions import actions
364
371
 
365
- def update_prompt(self, instance: Optional[str] = None, username: Optional[str] = None):
372
+ def update_prompt(
373
+ self,
374
+ instance: Optional[str] = None,
375
+ username: Optional[str] = None,
376
+ executor_keys: Optional[str] = None,
377
+ ):
366
378
  from meerschaum.utils.formatting import ANSI, colored
367
379
  from meerschaum._internal.entry import _shell, get_shell
368
380
 
369
381
  cmd.__builtins__['input'] = input_with_sigint(
370
382
  _old_input,
371
383
  shell_attrs['session'],
372
- shell = self,
384
+ shell=self,
373
385
  )
374
386
  prompt = shell_attrs['_prompt']
375
387
  mask = prompt
@@ -395,7 +407,8 @@ class Shell(cmd.Cmd):
395
407
  from meerschaum.connectors.sql import SQLConnector
396
408
  try:
397
409
  conn_attrs = parse_instance_keys(
398
- remove_ansi(shell_attrs['instance_keys']), construct=False
410
+ remove_ansi(shell_attrs['instance_keys']),
411
+ construct=False,
399
412
  )
400
413
  if 'username' not in conn_attrs:
401
414
  if 'uri' in conn_attrs:
@@ -409,12 +422,27 @@ class Shell(cmd.Cmd):
409
422
  if username is None:
410
423
  username = '(no username)'
411
424
  shell_attrs['username'] = (
412
- username if not ANSI else
413
- colored(username, **get_config('shell', 'ansi', 'username', 'rich'))
425
+ username
426
+ if not ANSI
427
+ else colored(username, **get_config('shell', 'ansi', 'username', 'rich'))
414
428
  )
415
429
  prompt = prompt.replace('{username}', shell_attrs['username'])
416
430
  mask = mask.replace('{username}', ''.join(['\0' for c in '{username}']))
417
431
 
432
+ if '{executor_keys}' in shell_attrs['_prompt']:
433
+ if executor_keys is None:
434
+ executor_keys = shell_attrs.get('executor_keys', None) or 'local'
435
+ shell_attrs['executor_keys'] = (
436
+ executor_keys
437
+ if not ANSI
438
+ else colored(
439
+ remove_ansi(executor_keys),
440
+ **get_config('shell', 'ansi', 'executor', 'rich')
441
+ )
442
+ )
443
+ prompt = prompt.replace('{executor_keys}', shell_attrs['executor_keys'])
444
+ mask = mask.replace('{executor_keys}', ''.join(['\0' for c in '{executor_keys}']))
445
+
418
446
  remainder_prompt = list(shell_attrs['_prompt'])
419
447
  for i, c in enumerate(mask):
420
448
  if c != '\0':
@@ -422,10 +450,13 @@ class Shell(cmd.Cmd):
422
450
  if ANSI:
423
451
  _c = colored(_c, **get_config('shell', 'ansi', 'prompt', 'rich'))
424
452
  remainder_prompt[i] = _c
453
+
425
454
  self.prompt = ''.join(remainder_prompt).replace(
426
455
  '{username}', shell_attrs['username']
427
456
  ).replace(
428
457
  '{instance}', shell_attrs['instance']
458
+ ).replace(
459
+ '{executor_keys}', shell_attrs['executor_keys']
429
460
  )
430
461
  shell_attrs['prompt'] = self.prompt
431
462
  ### flush stdout
@@ -525,6 +556,9 @@ class Shell(cmd.Cmd):
525
556
  if 'repository' not in args and main_action_name != 'api':
526
557
  args['repository'] = str(shell_attrs['repo_keys'])
527
558
 
559
+ if 'executor_keys' not in args:
560
+ args['executor_keys'] = remove_ansi(str(shell_attrs['executor_keys']))
561
+
528
562
  ### parse out empty strings
529
563
  if args['action'][0].strip("\"'") == '':
530
564
  self.emptyline()
@@ -569,12 +603,12 @@ class Shell(cmd.Cmd):
569
603
  if stop:
570
604
  return True
571
605
 
572
- def do_pass(self, line):
606
+ def do_pass(self, line, executor_keys=None):
573
607
  """
574
608
  Do nothing.
575
609
  """
576
610
 
577
- def do_debug(self, action: Optional[List[str]] = None, **kw):
611
+ def do_debug(self, action: Optional[List[str]] = None, executor_keys=None, **kw):
578
612
  """
579
613
  Toggle the shell's debug mode.
580
614
  If debug = on, append `--debug` to all commands.
@@ -605,9 +639,10 @@ class Shell(cmd.Cmd):
605
639
 
606
640
  def do_instance(
607
641
  self,
608
- action : Optional[List[str]] = None,
609
- debug : bool = False,
610
- **kw : Any
642
+ action: Optional[List[str]] = None,
643
+ executor_keys=None,
644
+ debug: bool = False,
645
+ **kw: Any
611
646
  ) -> SuccessTuple:
612
647
  """
613
648
  Temporarily set a default Meerschaum instance for the duration of the shell.
@@ -676,11 +711,12 @@ class Shell(cmd.Cmd):
676
711
 
677
712
 
678
713
  def do_repo(
679
- self,
680
- action: Optional[List[str]] = None,
681
- debug: bool = False,
682
- **kw: Any
683
- ) -> SuccessTuple:
714
+ self,
715
+ action: Optional[List[str]] = None,
716
+ executor_keys=None,
717
+ debug: bool = False,
718
+ **kw: Any
719
+ ) -> SuccessTuple:
684
720
  """
685
721
  Temporarily set a default Meerschaum repository for the duration of the shell.
686
722
  The default repository (mrsm.io) is loaded from the Meerschaum configuraton file
@@ -727,9 +763,59 @@ class Shell(cmd.Cmd):
727
763
  return True, "Success"
728
764
 
729
765
  def complete_repo(self, *args) -> List[str]:
730
- return self.complete_instance(*args)
766
+ results = self.complete_instance(*args)
767
+ return [result for result in results if result.startswith('api:')]
768
+
769
+ def do_executor(
770
+ self,
771
+ action: Optional[List[str]] = None,
772
+ executor_keys=None,
773
+ debug: bool = False,
774
+ **kw: Any
775
+ ) -> SuccessTuple:
776
+ """
777
+ Temporarily set a default Meerschaum executor for the duration of the shell.
778
+
779
+ You can change the default repository with `edit config`.
780
+
781
+ Usage:
782
+ executor {API label}
783
+
784
+ Examples:
785
+ ### reset to default executor
786
+ executor
787
+
788
+ ### set the executor to 'api:main'
789
+ executor api:main
790
+
791
+ Note that executors are API instances.
792
+ """
793
+ from meerschaum import get_connector
794
+ from meerschaum.connectors.parse import parse_executor_keys
795
+ from meerschaum.utils.warnings import warn, info
796
+
797
+ if action is None:
798
+ action = []
731
799
 
732
- def do_help(self, line: str) -> List[str]:
800
+ try:
801
+ executor_keys = action[0]
802
+ except (IndexError, AttributeError):
803
+ executor_keys = ''
804
+ if executor_keys == '':
805
+ executor_keys = get_config('meerschaum', 'default_executor') or 'local'
806
+
807
+ conn = parse_executor_keys(executor_keys, debug=debug)
808
+
809
+ shell_attrs['executor_keys'] = str(conn)
810
+
811
+ info(f"Default executor for the current shell: {executor_keys}")
812
+ return True, "Success"
813
+
814
+ 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:')]
817
+
818
+ def do_help(self, line: str, executor_keys=None) -> List[str]:
733
819
  """
734
820
  Show help for Meerschaum actions.
735
821
 
@@ -800,7 +886,7 @@ class Shell(cmd.Cmd):
800
886
  possibilities.append(name.replace('do_', ''))
801
887
  return possibilities
802
888
 
803
- def do_exit(self, params) -> True:
889
+ def do_exit(self, params, executor_keys=None) -> True:
804
890
  """
805
891
  Exit the Meerschaum shell.
806
892
  """
@@ -865,21 +951,29 @@ def input_with_sigint(_input, session, shell: Optional[Shell] = None):
865
951
 
866
952
  instance_colored = (
867
953
  colored(
868
- shell_attrs['instance_keys'], 'on ' + get_config(
869
- 'shell', 'ansi', 'instance', 'rich', 'style'
870
- )
954
+ remove_ansi(shell_attrs['instance_keys']),
955
+ 'on ' + get_config('shell', 'ansi', 'instance', 'rich', 'style')
871
956
  )
872
957
  if ANSI
873
958
  else colored(shell_attrs['instance_keys'], 'on white')
874
959
  )
875
960
  repo_colored = (
876
961
  colored(
877
- shell_attrs['repo_keys'],
962
+ remove_ansi(shell_attrs['repo_keys']),
878
963
  'on ' + get_config('shell', 'ansi', 'repo', 'rich', 'style')
879
964
  )
880
965
  if ANSI
881
966
  else colored(shell_attrs['repo_keys'], 'on white')
882
967
  )
968
+ executor_colored = (
969
+ colored(
970
+ remove_ansi(shell_attrs['executor_keys']),
971
+ 'on ' + get_config('shell', 'ansi', 'executor', 'rich', 'style')
972
+ )
973
+ if ANSI
974
+ else colored(remove_ansi(shell_attrs['executor_keys']), 'on white')
975
+ )
976
+
883
977
  try:
884
978
  typ, label = shell_attrs['instance_keys'].split(':')
885
979
  connected = typ in connectors and label in connectors[typ]
@@ -898,8 +992,12 @@ def input_with_sigint(_input, session, shell: Optional[Shell] = None):
898
992
  )
899
993
 
900
994
  left = (
901
- colored(' Instance: ', 'on white') + instance_colored
902
- + colored(' Repo: ', 'on white') + repo_colored
995
+ ' '
996
+ + instance_colored
997
+ + colored(' | ', 'on white')
998
+ + executor_colored
999
+ + colored(' | ', 'on white')
1000
+ + repo_colored
903
1001
  )
904
1002
  right = connection_text
905
1003
  buffer_size = (
@@ -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
- action: Union[str, List[str]],
16
- _actions: Optional[Dict[str, Callable[[Any], Any]]] = None,
17
- ) -> Dict[str, Callable[[Any], Any]]:
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
- action: Union[str, List[str]],
56
- _actions: Optional[Dict[str, Callable[[Any], Any]]] = None,
57
- ) -> Union[Callable[[Any], Any], None]:
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/api.py CHANGED
@@ -10,12 +10,12 @@ import os
10
10
  from meerschaum.utils.typing import SuccessTuple, Optional, List, Any
11
11
 
12
12
  def api(
13
- action: Optional[List[str]] = None,
14
- sysargs: Optional[List[str]] = None,
15
- debug: bool = False,
16
- mrsm_instance: Optional[str] = None,
17
- **kw: Any
18
- ) -> SuccessTuple:
13
+ action: Optional[List[str]] = None,
14
+ sysargs: Optional[List[str]] = None,
15
+ debug: bool = False,
16
+ mrsm_instance: Optional[str] = None,
17
+ **kw: Any
18
+ ) -> SuccessTuple:
19
19
  """
20
20
  Send commands to a Meerschaum WebAPI instance.
21
21
 
@@ -37,7 +37,8 @@ def api(
37
37
  """
38
38
  from meerschaum.utils.warnings import warn, info
39
39
  from meerschaum.utils.formatting import print_tuple
40
- from meerschaum.utils.packages import attempt_import
40
+ from meerschaum._internal.arguments._parse_arguments import parse_dict_to_sysargs
41
+
41
42
  if action is None:
42
43
  action = []
43
44
  if sysargs is None:
@@ -52,7 +53,6 @@ def api(
52
53
 
53
54
  from meerschaum.config import get_config
54
55
  from meerschaum.connectors import get_connector
55
- requests = attempt_import('requests')
56
56
  if debug:
57
57
  from meerschaum.utils.formatting import pprint
58
58
  api_configs = get_config('meerschaum', 'connectors', 'api', patch=True)
@@ -68,12 +68,13 @@ def api(
68
68
  del action[0]
69
69
  if len(args_to_send) > 1:
70
70
  del args_to_send[0]
71
+
71
72
  kw['action'] = action
72
73
  kw['debug'] = debug
73
74
  kw['sysargs'] = args_to_send
74
75
  kw['yes'] = True
75
76
 
76
- api_conn = get_connector(type='api', label=api_label)
77
+ api_conn = get_connector(f'api:{api_label}')
77
78
 
78
79
  if mrsm_instance is not None and str(mrsm_instance) == str(api_conn):
79
80
  warn(
@@ -83,9 +84,8 @@ def api(
83
84
  elif mrsm_instance is not None:
84
85
  kw['mrsm_instance'] = str(mrsm_instance)
85
86
 
86
- success, message = api_conn.do_action(**kw)
87
- print_tuple((success, message), common_only=True)
88
- msg = f"Action " + ('succeeded' if success else 'failed') + " with message:\n" + str(message)
87
+ sysargs = parse_dict_to_sysargs(kw)
88
+ success, message = api_conn.do_action(sysargs)
89
89
  return success, message
90
90
 
91
91
  def _api_start(