meerschaum 2.3.5.dev0__py3-none-any.whl → 2.4.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 (99) hide show
  1. meerschaum/_internal/arguments/__init__.py +2 -1
  2. meerschaum/_internal/arguments/_parse_arguments.py +88 -12
  3. meerschaum/_internal/docs/index.py +3 -2
  4. meerschaum/_internal/entry.py +42 -20
  5. meerschaum/_internal/shell/Shell.py +38 -44
  6. meerschaum/_internal/term/TermPageHandler.py +2 -3
  7. meerschaum/_internal/term/__init__.py +13 -11
  8. meerschaum/actions/api.py +26 -23
  9. meerschaum/actions/bootstrap.py +38 -11
  10. meerschaum/actions/copy.py +3 -3
  11. meerschaum/actions/delete.py +4 -1
  12. meerschaum/actions/register.py +1 -3
  13. meerschaum/actions/stack.py +24 -19
  14. meerschaum/actions/start.py +41 -41
  15. meerschaum/actions/sync.py +53 -52
  16. meerschaum/api/__init__.py +48 -14
  17. meerschaum/api/_events.py +26 -17
  18. meerschaum/api/_oauth2.py +2 -2
  19. meerschaum/api/_websockets.py +5 -4
  20. meerschaum/api/dash/__init__.py +7 -16
  21. meerschaum/api/dash/callbacks/__init__.py +1 -0
  22. meerschaum/api/dash/callbacks/dashboard.py +52 -58
  23. meerschaum/api/dash/callbacks/jobs.py +15 -16
  24. meerschaum/api/dash/callbacks/login.py +16 -10
  25. meerschaum/api/dash/callbacks/pipes.py +41 -0
  26. meerschaum/api/dash/callbacks/plugins.py +1 -1
  27. meerschaum/api/dash/callbacks/register.py +15 -11
  28. meerschaum/api/dash/components.py +54 -59
  29. meerschaum/api/dash/jobs.py +5 -9
  30. meerschaum/api/dash/pages/__init__.py +1 -0
  31. meerschaum/api/dash/pages/pipes.py +19 -0
  32. meerschaum/api/dash/pipes.py +86 -58
  33. meerschaum/api/dash/plugins.py +6 -4
  34. meerschaum/api/dash/sessions.py +176 -0
  35. meerschaum/api/dash/users.py +3 -41
  36. meerschaum/api/dash/webterm.py +12 -17
  37. meerschaum/api/resources/static/js/terminado.js +1 -1
  38. meerschaum/api/routes/_actions.py +4 -118
  39. meerschaum/api/routes/_jobs.py +45 -24
  40. meerschaum/api/routes/_login.py +4 -4
  41. meerschaum/api/routes/_pipes.py +3 -3
  42. meerschaum/api/routes/_webterm.py +5 -6
  43. meerschaum/config/_default.py +15 -3
  44. meerschaum/config/_version.py +1 -1
  45. meerschaum/config/stack/__init__.py +64 -21
  46. meerschaum/config/static/__init__.py +6 -0
  47. meerschaum/connectors/{Connector.py → _Connector.py} +19 -13
  48. meerschaum/connectors/__init__.py +24 -14
  49. meerschaum/connectors/api/{APIConnector.py → _APIConnector.py} +3 -1
  50. meerschaum/connectors/api/__init__.py +2 -1
  51. meerschaum/connectors/api/_actions.py +22 -36
  52. meerschaum/connectors/api/_jobs.py +1 -0
  53. meerschaum/connectors/parse.py +18 -16
  54. meerschaum/connectors/poll.py +30 -24
  55. meerschaum/connectors/sql/__init__.py +3 -1
  56. meerschaum/connectors/sql/_pipes.py +172 -197
  57. meerschaum/connectors/sql/_plugins.py +45 -43
  58. meerschaum/connectors/sql/_users.py +46 -38
  59. meerschaum/connectors/valkey/_ValkeyConnector.py +535 -0
  60. meerschaum/connectors/valkey/__init__.py +10 -0
  61. meerschaum/connectors/valkey/_fetch.py +75 -0
  62. meerschaum/connectors/valkey/_pipes.py +844 -0
  63. meerschaum/connectors/valkey/_plugins.py +265 -0
  64. meerschaum/connectors/valkey/_users.py +305 -0
  65. meerschaum/core/Pipe/__init__.py +3 -0
  66. meerschaum/core/Pipe/_attributes.py +1 -2
  67. meerschaum/core/Pipe/_clear.py +16 -13
  68. meerschaum/core/Pipe/_copy.py +106 -0
  69. meerschaum/core/Pipe/_data.py +165 -101
  70. meerschaum/core/Pipe/_drop.py +4 -4
  71. meerschaum/core/Pipe/_dtypes.py +14 -14
  72. meerschaum/core/Pipe/_edit.py +15 -14
  73. meerschaum/core/Pipe/_sync.py +134 -53
  74. meerschaum/core/Pipe/_verify.py +11 -11
  75. meerschaum/core/User/_User.py +14 -12
  76. meerschaum/jobs/_Job.py +27 -14
  77. meerschaum/jobs/__init__.py +7 -2
  78. meerschaum/jobs/systemd.py +20 -8
  79. meerschaum/plugins/_Plugin.py +17 -13
  80. meerschaum/utils/_get_pipes.py +14 -20
  81. meerschaum/utils/dataframe.py +291 -101
  82. meerschaum/utils/dtypes/__init__.py +31 -6
  83. meerschaum/utils/dtypes/sql.py +4 -4
  84. meerschaum/utils/formatting/_shell.py +5 -6
  85. meerschaum/utils/misc.py +3 -3
  86. meerschaum/utils/packages/__init__.py +14 -9
  87. meerschaum/utils/packages/_packages.py +2 -0
  88. meerschaum/utils/prompt.py +1 -1
  89. meerschaum/utils/schedule.py +1 -0
  90. {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/METADATA +7 -1
  91. {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/RECORD +98 -89
  92. {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/WHEEL +1 -1
  93. meerschaum/api/dash/actions.py +0 -255
  94. /meerschaum/connectors/sql/{SQLConnector.py → _SQLConnector.py} +0 -0
  95. {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/LICENSE +0 -0
  96. {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/NOTICE +0 -0
  97. {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/entry_points.txt +0 -0
  98. {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/top_level.txt +0 -0
  99. {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/zip-safe +0 -0
@@ -9,7 +9,8 @@ This package includes argument parsing utilities.
9
9
  from meerschaum._internal.arguments._parse_arguments import (
10
10
  parse_arguments, parse_line, remove_leading_action,
11
11
  parse_dict_to_sysargs, split_chained_sysargs, split_pipeline_sysargs,
12
- sysargs_has_api_executor_keys,
12
+ sysargs_has_api_executor_keys, get_pipeline_sysargs,
13
+ compress_pipeline_sysargs, remove_api_executor_keys,
13
14
  )
14
15
  from meerschaum._internal.arguments._parser import parser
15
16
  from meerschaum.plugins import add_plugin_argument
@@ -18,6 +18,7 @@ _locks = {
18
18
  }
19
19
  _loaded_plugins_args: bool = False
20
20
 
21
+
21
22
  def split_pipeline_sysargs(sysargs: List[str]) -> Tuple[List[str], List[str]]:
22
23
  """
23
24
  Split `sysargs` into the main pipeline and the flags following the pipeline separator (`:`).
@@ -89,7 +90,6 @@ def parse_arguments(sysargs: List[str]) -> Dict[str, Any]:
89
90
 
90
91
  """
91
92
  import shlex
92
- import copy
93
93
  from meerschaum.config.static import STATIC_CONFIG
94
94
  from meerschaum._internal.arguments._parser import parser
95
95
 
@@ -261,8 +261,6 @@ def parse_dict_to_sysargs(
261
261
  """Revert an arguments dictionary back to a command line list."""
262
262
  import shlex
263
263
  from meerschaum._internal.arguments._parser import get_arguments_triggers
264
- from meerschaum.config.static import STATIC_CONFIG
265
- from meerschaum.utils.warnings import warn
266
264
 
267
265
  action = args_dict.get('action', None)
268
266
  sysargs: List[str] = []
@@ -284,7 +282,6 @@ def parse_dict_to_sysargs(
284
282
  if isinstance(args_dict[a], (list, tuple)):
285
283
  if len(args_dict[a]) > 0:
286
284
  if a == 'sub_args' and args_dict[a] != ['']:
287
- print(f"{args_dict[a]=}")
288
285
  sysargs.extend(
289
286
  [
290
287
  '-A',
@@ -386,7 +383,7 @@ def remove_leading_action(
386
383
  if not action_str.replace(UNDERSCORE_STANDIN, '_').startswith(action_name):
387
384
  warn(f"Unable to parse '{action_str}' for action '{action_name}'.")
388
385
  return action
389
-
386
+
390
387
  parsed_action = action_str[len(action_name)+1:].split('_')
391
388
 
392
389
  ### Substitute the underscores back in.
@@ -424,12 +421,91 @@ def sysargs_has_api_executor_keys(sysargs: List[str]) -> bool:
424
421
  if '-e' not in sysargs and '--executor-keys' not in sysargs:
425
422
  return False
426
423
 
427
- executor_keys_flag = '-e' if '-e' in sysargs else '--executor-keys'
428
- executor_keys_flag_ix = sysargs.index(executor_keys_flag)
429
- executor_keys_ix = executor_keys_flag_ix + 1
424
+ for i, arg in enumerate(sysargs):
425
+ if arg not in ('-e', '--executor-keys'):
426
+ continue
430
427
 
431
- if len(sysargs) < executor_keys_ix:
432
- return False
428
+ executor_keys_ix = i + 1
429
+ if len(sysargs) <= executor_keys_ix:
430
+ return False
431
+
432
+ executor_keys = sysargs[executor_keys_ix]
433
+ if executor_keys.startswith('api:'):
434
+ return True
435
+
436
+ return False
437
+
438
+
439
+ def remove_api_executor_keys(sysargs: List[str]) -> List[str]:
440
+ """
441
+ Remove any api executor keys from `sysargs`.
442
+ """
443
+ from meerschaum.utils.misc import flatten_list
444
+
445
+ if not sysargs_has_api_executor_keys(sysargs):
446
+ return sysargs
447
+
448
+ skip_indices = set(flatten_list(
449
+ [
450
+ [i, i+1]
451
+ for i, arg in enumerate(sysargs)
452
+ if arg in ('-e', '--executor-keys')
453
+ ]
454
+ ))
433
455
 
434
- executor_keys = sysargs[executor_keys_ix]
435
- return executor_keys.startswith('api:')
456
+ return [
457
+ arg
458
+ for i, arg in enumerate(sysargs)
459
+ if i not in skip_indices
460
+ ]
461
+
462
+
463
+ def get_pipeline_sysargs(
464
+ sysargs: List[str],
465
+ pipeline_args: List[str],
466
+ _patch_args: Optional[Dict[str, Any]] = None,
467
+ ) -> List[str]:
468
+ """
469
+ Parse `sysargs` and `pipeline_args` into a single `start pipeline` sysargs.
470
+ """
471
+ import shlex
472
+ start_pipeline_params = {
473
+ 'sub_args_line': shlex.join(sysargs),
474
+ 'patch_args': _patch_args,
475
+ }
476
+ return (
477
+ ['start', 'pipeline']
478
+ + [str(arg) for arg in pipeline_args]
479
+ + ['-P', json.dumps(start_pipeline_params, separators=(',', ':'))]
480
+ )
481
+
482
+
483
+ def compress_pipeline_sysargs(pipeline_sysargs: List[str]) -> List[str]:
484
+ """
485
+ Given a `start pipeline` sysargs, return a condensed syntax rendition.
486
+ """
487
+ import shlex
488
+
489
+ if pipeline_sysargs[:2] != ['start', 'pipeline']:
490
+ return pipeline_sysargs
491
+
492
+ if '-P' not in pipeline_sysargs:
493
+ return pipeline_sysargs
494
+
495
+ params_ix = pipeline_sysargs.index('-P')
496
+ pipeline_args = pipeline_sysargs[2:params_ix]
497
+ params_str = pipeline_sysargs[-1]
498
+ try:
499
+ start_pipeline_params = json.loads(params_str)
500
+ except Exception:
501
+ return pipeline_sysargs
502
+
503
+ sub_args_line = start_pipeline_params.get('sub_args_line', None)
504
+ if not sub_args_line:
505
+ return pipeline_sysargs
506
+
507
+ return (
508
+ shlex.split(sub_args_line)
509
+ + [':']
510
+ + pipeline_args
511
+ )
@@ -436,8 +436,9 @@ def init_dash(dash_app):
436
436
  - `meerschaum.connectors.is_connected()`
437
437
  - `meerschaum.connectors.poll.retry_connect()`
438
438
  - `meerschaum.connectors.Connector`
439
- - `meerschaum.connectors.SQLConnector`
440
- - `meerschaum.connectors.APIConnector`
439
+ - `meerschaum.connectors.sql.SQLConnector`
440
+ - `meerschaum.connectors.api.APIConnector`
441
+ - `meerschaum.connectors.valkey.ValkeyConnector`
441
442
 
442
443
  </details>
443
444
 
@@ -11,6 +11,8 @@ from __future__ import annotations
11
11
 
12
12
  import os
13
13
  import sys
14
+ import pathlib
15
+
14
16
  from meerschaum.utils.typing import SuccessTuple, List, Optional, Dict, Callable, Any
15
17
  from meerschaum.config.static import STATIC_CONFIG as _STATIC_CONFIG
16
18
 
@@ -19,8 +21,17 @@ if (_STATIC_CONFIG['environment']['systemd_log_path']) in os.environ:
19
21
  from meerschaum.utils.daemon import RotatingFile as _RotatingFile, StdinFile as _StdinFile
20
22
  from meerschaum.config import get_config as _get_config
21
23
 
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_result_path = pathlib.Path(
25
+ os.environ[_STATIC_CONFIG['environment']['systemd_result_path']]
26
+ )
27
+ _systemd_log_path = pathlib.Path(
28
+ os.environ[_STATIC_CONFIG['environment']['systemd_log_path']]
29
+ )
30
+ _systemd_delete_job = (
31
+ (os.environ.get(_STATIC_CONFIG['environment']['systemd_delete_job'], None) or '0')
32
+ not in (None, '0', 'false')
33
+ )
34
+ _job_name = os.environ[_STATIC_CONFIG['environment']['daemon_id']]
24
35
  _systemd_log = _RotatingFile(
25
36
  _systemd_log_path,
26
37
  write_timestamps=True,
@@ -51,6 +62,7 @@ def entry(
51
62
  split_chained_sysargs,
52
63
  split_pipeline_sysargs,
53
64
  sysargs_has_api_executor_keys,
65
+ get_pipeline_sysargs,
54
66
  )
55
67
  from meerschaum.config.static import STATIC_CONFIG
56
68
  if sysargs is None:
@@ -72,15 +84,7 @@ def entry(
72
84
  else split_chained_sysargs(sysargs)
73
85
  )
74
86
  if pipeline_args:
75
- start_pipeline_params = {
76
- 'sub_args_line': shlex.join(sysargs),
77
- 'patch_args': _patch_args,
78
- }
79
- chained_sysargs = [
80
- ['start', 'pipeline']
81
- + [str(arg) for arg in pipeline_args]
82
- + ['-P', json.dumps(start_pipeline_params, separators=(',', ':'))]
83
- ]
87
+ chained_sysargs = [get_pipeline_sysargs(sysargs, pipeline_args, _patch_args=_patch_args)]
84
88
 
85
89
  results: List[SuccessTuple] = []
86
90
 
@@ -169,8 +173,20 @@ def entry(
169
173
 
170
174
  if _systemd_result_path:
171
175
  import json
172
- with open(_systemd_result_path, 'w+', encoding='utf-8') as f:
173
- json.dump((success, msg), f)
176
+ from meerschaum.utils.warnings import warn
177
+ import meerschaum as mrsm
178
+
179
+ job = mrsm.Job(_job_name, executor_keys='systemd')
180
+ if job.delete_after_completion:
181
+ delete_success, delete_msg = job.delete()
182
+ mrsm.pprint((delete_success, delete_msg))
183
+ else:
184
+ try:
185
+ if _systemd_result_path.parent.exists():
186
+ with open(_systemd_result_path, 'w+', encoding='utf-8') as f:
187
+ json.dump((success, msg), f)
188
+ except Exception as e:
189
+ warn(f"Failed to write job result:\n{e}")
174
190
 
175
191
  return success, msg
176
192
 
@@ -183,13 +199,13 @@ def entry_with_args(
183
199
  """Execute a Meerschaum action with keyword arguments.
184
200
  Use `_entry()` for parsing sysargs before executing.
185
201
  """
186
- import sys
187
202
  import functools
188
203
  import inspect
189
- from meerschaum.actions import get_action, get_main_action_name
204
+ from meerschaum.actions import get_action
190
205
  from meerschaum._internal.arguments import remove_leading_action
191
206
  from meerschaum.utils.venv import active_venvs, deactivate_venv
192
207
  from meerschaum.config.static import STATIC_CONFIG
208
+ from meerschaum.utils.typing import is_success_tuple
193
209
 
194
210
  if _patch_args:
195
211
  kw.update(_patch_args)
@@ -209,7 +225,8 @@ def entry_with_args(
209
225
  or
210
226
  (kw['action'][0] == 'mrsm' and len(kw['action'][1:]) == 0)
211
227
  ):
212
- return get_shell().cmdloop()
228
+ _ = get_shell(**kw).cmdloop()
229
+ return True, "Success"
213
230
 
214
231
  skip_schedule = False
215
232
 
@@ -280,12 +297,15 @@ def entry_with_args(
280
297
  for venv in [venv for venv in active_venvs]:
281
298
  deactivate_venv(venv, debug=kw.get('debug', False), force=True)
282
299
 
300
+ if not is_success_tuple(result):
301
+ return True, str(result)
302
+
283
303
  return result
284
304
 
285
305
 
286
306
  def _do_action_wrapper(action_function, plugin_name, **kw):
287
307
  from meerschaum.plugins import Plugin
288
- from meerschaum.utils.venv import Venv, active_venvs, deactivate_venv
308
+ from meerschaum.utils.venv import Venv
289
309
  from meerschaum.utils.misc import filter_keywords
290
310
  plugin = Plugin(plugin_name) if plugin_name else None
291
311
  with Venv(plugin, debug=kw.get('debug', False)):
@@ -314,7 +334,9 @@ _shell = None
314
334
  def get_shell(
315
335
  sysargs: Optional[List[str]] = None,
316
336
  reload: bool = False,
317
- debug: bool = False
337
+ debug: bool = False,
338
+ mrsm_instance: Optional[str] = None,
339
+ **kwargs: Any
318
340
  ):
319
341
  """Initialize and return the Meerschaum shell object."""
320
342
  global _shell
@@ -330,9 +352,9 @@ def get_shell(
330
352
 
331
353
  if _shell is None:
332
354
  shell_pkg._insert_shell_actions()
333
- _shell = shell_pkg.Shell(actions, sysargs=sysargs)
355
+ _shell = shell_pkg.Shell(actions, sysargs=sysargs, instance_keys=mrsm_instance)
334
356
  elif reload:
335
- _shell.__init__()
357
+ _shell.__init__(instance_keys=mrsm_instance)
336
358
 
337
359
  _shells.append(_shell)
338
360
  return _shell
@@ -81,10 +81,10 @@ PIPELINE_KEY: str = STATIC_CONFIG['system']['arguments']['pipeline_key']
81
81
  ESCAPED_PIPELINE_KEY: str = STATIC_CONFIG['system']['arguments']['escaped_pipeline_key']
82
82
 
83
83
  def _insert_shell_actions(
84
- _shell: Optional['Shell'] = None,
85
- actions: Optional[Dict[str, Callable[[Any], SuccessTuple]]] = None,
86
- keep_self: bool = False,
87
- ) -> None:
84
+ _shell: Optional['Shell'] = None,
85
+ actions: Optional[Dict[str, Callable[[Any], SuccessTuple]]] = None,
86
+ keep_self: bool = False,
87
+ ) -> None:
88
88
  """
89
89
  Update the Shell with Meerschaum actions.
90
90
  """
@@ -111,9 +111,10 @@ def _insert_shell_actions(
111
111
  completer = _completer_wrapper(_completer)
112
112
  setattr(_shell_class, 'complete_' + a, completer)
113
113
 
114
+
114
115
  def _completer_wrapper(
115
116
  target: Callable[[Any], List[str]]
116
- ) -> Callable[['meerschaum._internal.shell.Shell', str, str, int, int], Any]:
117
+ ) -> Callable[['mrsm._internal.shell.Shell', str, str, int, int], Any]:
117
118
  """
118
119
  Wrapper for `complete_` functions so they can instead use Meerschaum arguments.
119
120
  """
@@ -162,21 +163,21 @@ def default_action_completer(
162
163
  possibilities.append(sa)
163
164
  return sorted(possibilities)
164
165
 
166
+
165
167
  def _check_complete_keys(line: str) -> Optional[List[str]]:
166
- from meerschaum._internal.arguments._parser import parser, get_arguments_triggers
168
+ from meerschaum._internal.arguments._parser import get_arguments_triggers
167
169
 
168
170
  ### TODO Add all triggers
169
171
  trigger_args = {
170
- '-c' : 'connector_keys',
171
- '--connector-keys' : 'connector_keys',
172
- '-r' : 'repository',
173
- '--repository' : 'repository',
174
- '-i' : 'mrsm_instance',
175
- '--instance' : 'mrsm_instance',
176
- '--mrsm-instance' : 'mrsm_instance',
172
+ '-c': 'connector_keys',
173
+ '--connector-keys': 'connector_keys',
174
+ '-r': 'repository',
175
+ '--repository': 'repository',
176
+ '-i': 'mrsm_instance',
177
+ '--instance': 'mrsm_instance',
178
+ '--mrsm-instance': 'mrsm_instance',
177
179
  }
178
180
 
179
-
180
181
  ### TODO Find out arg possibilities
181
182
  possibilities = []
182
183
  last_word = line.rstrip(' ').split(' ')[-1]
@@ -236,10 +237,14 @@ def get_shell_intro(with_color: bool = True) -> str:
236
237
 
237
238
 
238
239
  class Shell(cmd.Cmd):
240
+ """
241
+ The interactive Meerschaum shell.
242
+ """
239
243
  def __init__(
240
244
  self,
241
245
  actions: Optional[Dict[str, Any]] = None,
242
- sysargs: Optional[List[str]] = None
246
+ sysargs: Optional[List[str]] = None,
247
+ instance_keys: Optional[str] = None,
243
248
  ):
244
249
  """
245
250
  Customize the CLI from configuration
@@ -268,26 +273,13 @@ class Shell(cmd.Cmd):
268
273
  reserve_space_for_menu=False,
269
274
  )
270
275
 
271
- try: ### try cmd2 arguments first
272
- super().__init__(
273
- allow_cli_args = False,
274
- auto_load_commands = False,
275
- persistent_history_length = 1000,
276
- persistent_history_file = None,
277
- )
278
- _init = True
279
- except Exception as e:
280
- ### fall back to default init (cmd)
281
- _init = False
282
-
283
- if not _init:
284
- super().__init__()
276
+ super().__init__()
285
277
 
286
278
  ### remove default commands from the Cmd class
287
279
  for command in commands_to_remove:
288
280
  try:
289
281
  delattr(cmd.Cmd, f'do_{command}')
290
- except Exception as e:
282
+ except Exception:
291
283
  pass
292
284
 
293
285
  ### NOTE: custom actions must be added to the self._actions dictionary
@@ -301,7 +293,7 @@ class Shell(cmd.Cmd):
301
293
  shell_attrs['_old_bottom_toolbar'] = ''
302
294
  shell_attrs['debug'] = False
303
295
  shell_attrs['_reload'] = True
304
- self.load_config()
296
+ self.load_config(instance=instance_keys)
305
297
  self.hidden_commands = []
306
298
  ### update hidden commands list (cmd2 only)
307
299
  try:
@@ -314,7 +306,6 @@ class Shell(cmd.Cmd):
314
306
  from meerschaum._internal.shell.updates import run_version_check_thread
315
307
  self._update_thread = run_version_check_thread(debug=shell_attrs.get('debug', False))
316
308
 
317
-
318
309
  def load_config(self, instance: Optional[str] = None):
319
310
  """
320
311
  Set attributes from the shell configuration.
@@ -478,13 +469,13 @@ class Shell(cmd.Cmd):
478
469
  """
479
470
  Pass line string to parent actions.
480
471
  Pass parsed arguments to custom actions
481
-
472
+
482
473
  Overrides `default`. If an action does not exist, assume the action is `shell`
483
474
  """
484
475
  ### Preserve the working directory.
485
476
  old_cwd = os.getcwd()
486
477
 
487
- from meerschaum._internal.entry import _shell, get_shell
478
+ from meerschaum._internal.entry import get_shell
488
479
  self = get_shell(sysargs=shell_attrs['_sysargs'], debug=shell_attrs.get('debug', False))
489
480
 
490
481
  ### make a backup of line for later
@@ -522,7 +513,6 @@ class Shell(cmd.Cmd):
522
513
  if line.startswith(help_token):
523
514
  return "help " + line[len(help_token):]
524
515
 
525
- from meerschaum._internal.arguments import parse_line
526
516
  try:
527
517
  sysargs = shlex.split(line)
528
518
  except ValueError as e:
@@ -628,18 +618,24 @@ class Shell(cmd.Cmd):
628
618
  for i, kwargs in enumerate([_ for _ in chained_kwargs]):
629
619
  kwargs.update(patches[i])
630
620
 
631
- from meerschaum._internal.entry import entry_with_args, entry
621
+ from meerschaum._internal.entry import entry
632
622
  sysargs_to_execute = []
633
623
  for i, kwargs in enumerate(chained_kwargs):
634
624
  step_kwargs = {k: v for k, v in kwargs.items() if k != 'line'}
635
- step_sysargs = parse_dict_to_sysargs(step_kwargs)
625
+ step_action = kwargs.get('action', None)
626
+ step_action_name = step_action[0] if step_action else None
627
+ ### NOTE: For `stack`, revert argument parsing.
628
+ step_sysargs = (
629
+ parse_dict_to_sysargs(step_kwargs)
630
+ if step_action_name != 'stack'
631
+ else chained_sysargs[i]
632
+ )
636
633
  sysargs_to_execute.extend(step_sysargs)
637
634
  sysargs_to_execute.append(AND_KEY)
638
-
635
+
639
636
  sysargs_to_execute = sysargs_to_execute[:-1] + (
640
637
  ([':'] + pipeline_args) if pipeline_args else []
641
638
  )
642
-
643
639
  try:
644
640
  success_tuple = entry(sysargs_to_execute, _patch_args=patch_args)
645
641
  except Exception as e:
@@ -763,7 +759,6 @@ class Shell(cmd.Cmd):
763
759
 
764
760
  return True, "Success"
765
761
 
766
-
767
762
  def complete_instance(
768
763
  self,
769
764
  text: str,
@@ -776,9 +771,10 @@ class Shell(cmd.Cmd):
776
771
  from meerschaum.utils.misc import get_connector_labels
777
772
  from meerschaum._internal.arguments._parse_arguments import parse_line
778
773
  from meerschaum.connectors import instance_types, _load_builtin_custom_connectors
779
- if _executor:
774
+ if not self.__dict__.get('_loaded_custom_connectors', None):
780
775
  _load_builtin_custom_connectors()
781
- from meerschaum.jobs import executor_types
776
+ self.__dict__['_loaded_custom_connectors'] = True
777
+ from meerschaum.jobs import executor_types
782
778
 
783
779
  conn_types = instance_types if not _executor else executor_types
784
780
 
@@ -792,7 +788,6 @@ class Shell(cmd.Cmd):
792
788
  _additional_options=_additional_options,
793
789
  )
794
790
 
795
-
796
791
  def do_repo(
797
792
  self,
798
793
  action: Optional[List[str]] = None,
@@ -992,7 +987,6 @@ class Shell(cmd.Cmd):
992
987
  """
993
988
  Patch builtin cmdloop with my own input (defined below).
994
989
  """
995
- import signal, os
996
990
  cmd.__builtins__['input'] = input_with_sigint(
997
991
  _old_input,
998
992
  shell_attrs['session'],
@@ -12,10 +12,9 @@ tornado_web = attempt_import('tornado.web', lazy=False)
12
12
 
13
13
  class TermPageHandler(tornado_web.RequestHandler):
14
14
  def get(self):
15
- from meerschaum.api import endpoints
16
15
  return self.render(
17
16
  "termpage.html",
18
- static = self.static_url,
19
- ws_url_path = "/websocket"
17
+ static=self.static_url,
18
+ ws_url_path="/websocket",
20
19
  )
21
20
 
@@ -7,10 +7,8 @@ Build the web console virtual terminal using Tornado and xterm.js.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
- import os
11
- import platform
12
- import sys
13
- from typing import List, Tuple
10
+
11
+ from typing import Optional, Tuple
14
12
  from meerschaum.utils.packages import attempt_import
15
13
  from meerschaum._internal.term.TermPageHandler import TermPageHandler
16
14
  from meerschaum.config._paths import API_TEMPLATES_PATH, API_STATIC_PATH
@@ -20,10 +18,13 @@ tornado, tornado_ioloop, terminado = attempt_import(
20
18
  'tornado', 'tornado.ioloop', 'terminado', lazy=False,
21
19
  )
22
20
 
23
- def get_webterm_app_and_manager() -> Tuple[
24
- tornado.web.Application,
25
- terminado.UniqueTermManager,
26
- ]:
21
+
22
+ def get_webterm_app_and_manager(
23
+ instance_keys: Optional[str] = None,
24
+ ) -> Tuple[
25
+ tornado.web.Application,
26
+ terminado.UniqueTermManager,
27
+ ]:
27
28
  """
28
29
  Construct the Tornado web app and term manager from the provided sysargs.
29
30
 
@@ -31,12 +32,13 @@ def get_webterm_app_and_manager() -> Tuple[
31
32
  -------
32
33
  A tuple of the Tornado web application and term manager.
33
34
  """
35
+ shell_kwargs_str = f"mrsm_instance='{instance_keys}'" if instance_keys else ""
34
36
  commands = [
35
37
  venv_executable(None),
36
38
  '-c',
37
39
  "import os; _ = os.environ.pop('COLUMNS', None); _ = os.environ.pop('LINES', None); "
38
40
  "from meerschaum._internal.entry import get_shell; "
39
- "get_shell([]).cmdloop()"
41
+ f"get_shell({shell_kwargs_str}).cmdloop()"
40
42
  ]
41
43
 
42
44
  term_manager = terminado.UniqueTermManager(shell_command=commands)
@@ -53,7 +55,7 @@ def get_webterm_app_and_manager() -> Tuple[
53
55
  ]
54
56
  tornado_app = tornado.web.Application(
55
57
  handlers,
56
- static_path = API_STATIC_PATH,
57
- template_path = API_TEMPLATES_PATH,
58
+ static_path=API_STATIC_PATH,
59
+ template_path=API_TEMPLATES_PATH,
58
60
  )
59
61
  return tornado_app, term_manager
meerschaum/actions/api.py CHANGED
@@ -89,22 +89,22 @@ def api(
89
89
  return success, message
90
90
 
91
91
  def _api_start(
92
- action: Optional[List[str]] = None,
93
- host: Optional[str] = None,
94
- port: Optional[int] = None,
95
- workers: Optional[int] = None,
96
- mrsm_instance: Optional[str] = None,
97
- no_dash: bool = False,
98
- no_auth: bool = False,
99
- private: bool = False,
100
- secure: bool = False,
101
- debug: bool = False,
102
- nopretty: bool = False,
103
- production: bool = False,
104
- keyfile: Optional[str] = None,
105
- certfile: Optional[str] = None,
106
- **kw: Any
107
- ) -> SuccessTuple:
92
+ action: Optional[List[str]] = None,
93
+ host: Optional[str] = None,
94
+ port: Optional[int] = None,
95
+ workers: Optional[int] = None,
96
+ mrsm_instance: Optional[str] = None,
97
+ no_dash: bool = False,
98
+ no_auth: bool = False,
99
+ private: bool = False,
100
+ secure: bool = False,
101
+ debug: bool = False,
102
+ nopretty: bool = False,
103
+ production: bool = False,
104
+ keyfile: Optional[str] = None,
105
+ certfile: Optional[str] = None,
106
+ **kw: Any
107
+ ) -> SuccessTuple:
108
108
  """Start the API server.
109
109
 
110
110
  Parameters
@@ -167,7 +167,7 @@ def _api_start(
167
167
 
168
168
  ### Uvicorn must be installed on the host because of multiprocessing reasons.
169
169
  ### `check_update` must be False, because otherwise Uvicorn's hidden imports will break things.
170
- dotenv = attempt_import('dotenv', lazy=False)
170
+ _ = attempt_import('dotenv', lazy=False)
171
171
  uvicorn, gunicorn = attempt_import(
172
172
  'uvicorn', 'gunicorn', venv=None, lazy=False, check_update=False,
173
173
  )
@@ -235,6 +235,7 @@ def _api_start(
235
235
  'no_dash': no_dash,
236
236
  'no_auth': no_auth,
237
237
  'private': private,
238
+ 'production': production,
238
239
  })
239
240
  if debug:
240
241
  uvicorn_config['reload'] = debug
@@ -251,7 +252,7 @@ def _api_start(
251
252
  if secure:
252
253
  cf['system']['api']['permissions']['actions']['non_admin'] = False
253
254
 
254
- custom_keys = ['mrsm_instance', 'no_dash', 'no_auth', 'private', 'debug']
255
+ custom_keys = ['mrsm_instance', 'no_dash', 'no_auth', 'private', 'debug', 'production']
255
256
 
256
257
  ### write config to a temporary file to communicate with uvicorn threads
257
258
  import json, sys
@@ -342,7 +343,7 @@ def _api_start(
342
343
  run_python_package(
343
344
  'gunicorn',
344
345
  gunicorn_args,
345
- env = {
346
+ env={
346
347
  k: (
347
348
  json.dumps(v)
348
349
  if isinstance(v, (dict, list))
@@ -350,14 +351,16 @@ def _api_start(
350
351
  )
351
352
  for k, v in env_dict.items()
352
353
  },
353
- venv = None,
354
- debug = debug,
354
+ venv=None,
355
+ debug=debug,
355
356
  )
356
357
  except KeyboardInterrupt:
357
358
  pass
358
359
 
359
-
360
- _run_uvicorn() if not production else _run_gunicorn()
360
+ if production:
361
+ _run_gunicorn()
362
+ else:
363
+ _run_uvicorn()
361
364
 
362
365
  ### Cleanup
363
366
  if uvicorn_config_path.parent.exists():