meerschaum 3.0.0rc4__py3-none-any.whl → 3.0.0rc8__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 (117) hide show
  1. meerschaum/_internal/arguments/_parser.py +14 -2
  2. meerschaum/_internal/cli/__init__.py +6 -0
  3. meerschaum/_internal/cli/daemons.py +103 -0
  4. meerschaum/_internal/cli/entry.py +220 -0
  5. meerschaum/_internal/cli/workers.py +435 -0
  6. meerschaum/_internal/docs/index.py +1 -2
  7. meerschaum/_internal/entry.py +44 -8
  8. meerschaum/_internal/shell/Shell.py +115 -24
  9. meerschaum/_internal/shell/__init__.py +4 -1
  10. meerschaum/_internal/static.py +4 -1
  11. meerschaum/_internal/term/TermPageHandler.py +1 -2
  12. meerschaum/_internal/term/__init__.py +40 -6
  13. meerschaum/_internal/term/tools.py +33 -8
  14. meerschaum/actions/__init__.py +6 -4
  15. meerschaum/actions/api.py +39 -11
  16. meerschaum/actions/attach.py +1 -0
  17. meerschaum/actions/delete.py +4 -2
  18. meerschaum/actions/edit.py +27 -8
  19. meerschaum/actions/login.py +8 -8
  20. meerschaum/actions/register.py +13 -7
  21. meerschaum/actions/reload.py +22 -5
  22. meerschaum/actions/restart.py +14 -0
  23. meerschaum/actions/show.py +69 -4
  24. meerschaum/actions/start.py +135 -14
  25. meerschaum/actions/stop.py +36 -3
  26. meerschaum/actions/sync.py +6 -1
  27. meerschaum/api/__init__.py +35 -13
  28. meerschaum/api/_events.py +2 -2
  29. meerschaum/api/_oauth2.py +47 -4
  30. meerschaum/api/dash/callbacks/dashboard.py +29 -0
  31. meerschaum/api/dash/callbacks/jobs.py +3 -2
  32. meerschaum/api/dash/callbacks/login.py +10 -1
  33. meerschaum/api/dash/callbacks/register.py +9 -2
  34. meerschaum/api/dash/pages/login.py +2 -2
  35. meerschaum/api/dash/pipes.py +72 -36
  36. meerschaum/api/dash/webterm.py +14 -6
  37. meerschaum/api/models/_pipes.py +7 -1
  38. meerschaum/api/resources/static/js/terminado.js +3 -0
  39. meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
  40. meerschaum/api/resources/templates/termpage.html +1 -0
  41. meerschaum/api/routes/_jobs.py +23 -11
  42. meerschaum/api/routes/_login.py +73 -5
  43. meerschaum/api/routes/_pipes.py +6 -4
  44. meerschaum/api/routes/_webterm.py +3 -3
  45. meerschaum/config/__init__.py +60 -13
  46. meerschaum/config/_default.py +89 -61
  47. meerschaum/config/_edit.py +10 -8
  48. meerschaum/config/_formatting.py +2 -0
  49. meerschaum/config/_patch.py +4 -2
  50. meerschaum/config/_paths.py +127 -12
  51. meerschaum/config/_read_config.py +32 -12
  52. meerschaum/config/_version.py +1 -1
  53. meerschaum/config/environment.py +262 -0
  54. meerschaum/config/stack/__init__.py +7 -5
  55. meerschaum/connectors/_Connector.py +1 -2
  56. meerschaum/connectors/__init__.py +37 -2
  57. meerschaum/connectors/api/_APIConnector.py +1 -1
  58. meerschaum/connectors/api/_jobs.py +11 -0
  59. meerschaum/connectors/api/_pipes.py +7 -1
  60. meerschaum/connectors/instance/_plugins.py +9 -1
  61. meerschaum/connectors/instance/_tokens.py +20 -3
  62. meerschaum/connectors/instance/_users.py +8 -1
  63. meerschaum/connectors/parse.py +1 -1
  64. meerschaum/connectors/sql/_create_engine.py +3 -0
  65. meerschaum/connectors/sql/_pipes.py +93 -79
  66. meerschaum/connectors/sql/_users.py +8 -1
  67. meerschaum/connectors/valkey/_ValkeyConnector.py +3 -3
  68. meerschaum/connectors/valkey/_pipes.py +7 -5
  69. meerschaum/core/Pipe/__init__.py +45 -71
  70. meerschaum/core/Pipe/_attributes.py +66 -90
  71. meerschaum/core/Pipe/_cache.py +555 -0
  72. meerschaum/core/Pipe/_clear.py +0 -11
  73. meerschaum/core/Pipe/_data.py +0 -50
  74. meerschaum/core/Pipe/_deduplicate.py +0 -13
  75. meerschaum/core/Pipe/_delete.py +12 -21
  76. meerschaum/core/Pipe/_drop.py +11 -23
  77. meerschaum/core/Pipe/_dtypes.py +1 -1
  78. meerschaum/core/Pipe/_index.py +8 -14
  79. meerschaum/core/Pipe/_sync.py +12 -18
  80. meerschaum/core/Plugin/_Plugin.py +7 -1
  81. meerschaum/core/Token/_Token.py +1 -1
  82. meerschaum/core/User/_User.py +1 -2
  83. meerschaum/jobs/_Executor.py +88 -4
  84. meerschaum/jobs/_Job.py +146 -36
  85. meerschaum/jobs/systemd.py +7 -2
  86. meerschaum/plugins/__init__.py +277 -81
  87. meerschaum/utils/daemon/Daemon.py +197 -42
  88. meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
  89. meerschaum/utils/daemon/RotatingFile.py +63 -36
  90. meerschaum/utils/daemon/StdinFile.py +53 -13
  91. meerschaum/utils/daemon/__init__.py +18 -5
  92. meerschaum/utils/daemon/_names.py +6 -3
  93. meerschaum/utils/debug.py +34 -4
  94. meerschaum/utils/dtypes/__init__.py +5 -1
  95. meerschaum/utils/formatting/__init__.py +4 -1
  96. meerschaum/utils/formatting/_jobs.py +1 -1
  97. meerschaum/utils/formatting/_pipes.py +47 -46
  98. meerschaum/utils/formatting/_shell.py +33 -9
  99. meerschaum/utils/misc.py +22 -38
  100. meerschaum/utils/packages/__init__.py +15 -13
  101. meerschaum/utils/packages/_packages.py +1 -0
  102. meerschaum/utils/pipes.py +33 -5
  103. meerschaum/utils/process.py +1 -1
  104. meerschaum/utils/prompt.py +172 -143
  105. meerschaum/utils/sql.py +12 -2
  106. meerschaum/utils/threading.py +42 -0
  107. meerschaum/utils/venv/__init__.py +2 -0
  108. meerschaum/utils/warnings.py +19 -13
  109. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/METADATA +3 -1
  110. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/RECORD +116 -110
  111. meerschaum/config/_environment.py +0 -145
  112. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/WHEEL +0 -0
  113. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/entry_points.txt +0 -0
  114. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/licenses/LICENSE +0 -0
  115. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/licenses/NOTICE +0 -0
  116. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/top_level.txt +0 -0
  117. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/zip-safe +0 -0
@@ -31,11 +31,13 @@ prompt_toolkit = attempt_import('prompt_toolkit', lazy=False, warn=False, instal
31
31
  )
32
32
  from meerschaum._internal.shell.ValidAutoSuggest import ValidAutoSuggest
33
33
  from meerschaum._internal.shell.ShellCompleter import ShellCompleter
34
+ from meerschaum._internal.cli.daemons import get_cli_daemon, get_cli_session_id
34
35
  _clear_screen = get_config('shell', 'clear_screen', patch=True)
35
36
  from meerschaum.utils.misc import string_width, remove_ansi
36
37
  from meerschaum.utils.warnings import warn
37
38
  from meerschaum.jobs import get_executor_keys_from_context
38
39
  from meerschaum._internal.static import STATIC_CONFIG
40
+ from meerschaum.utils.formatting._shell import clear_screen, flush_stdout
39
41
  from meerschaum._internal.arguments._parse_arguments import (
40
42
  split_chained_sysargs,
41
43
  split_pipeline_sysargs,
@@ -81,6 +83,7 @@ ESCAPED_AND_KEY: str = STATIC_CONFIG['system']['arguments']['escaped_and_key']
81
83
  PIPELINE_KEY: str = STATIC_CONFIG['system']['arguments']['pipeline_key']
82
84
  ESCAPED_PIPELINE_KEY: str = STATIC_CONFIG['system']['arguments']['escaped_pipeline_key']
83
85
 
86
+
84
87
  def _insert_shell_actions(
85
88
  _shell: Optional['Shell'] = None,
86
89
  actions: Optional[Dict[str, Callable[[Any], SuccessTuple]]] = None,
@@ -99,6 +102,10 @@ def _insert_shell_actions(
99
102
  _shell_class = _shell if _shell is not None else shell_pkg.Shell
100
103
 
101
104
  for a, f in actions.items():
105
+ existing_method = getattr(_shell_class, 'do_' + a, None)
106
+ if existing_method == f:
107
+ continue
108
+
102
109
  add_method_to_class(
103
110
  func = f,
104
111
  class_def = _shell_class,
@@ -113,6 +120,35 @@ def _insert_shell_actions(
113
120
  setattr(_shell_class, 'complete_' + a, completer)
114
121
 
115
122
 
123
+ def _remove_shell_actions(
124
+ _shell: Optional['Shell'] = None,
125
+ actions: Optional[Dict[str, Callable[[Any], SuccessTuple]]] = None,
126
+ ) -> None:
127
+ """
128
+ Remove the actions added to the shell.
129
+ """
130
+ import meerschaum._internal.shell as shell_pkg
131
+ if actions is None:
132
+ from meerschaum.actions import actions as _actions
133
+ actions = _actions
134
+
135
+ _shell_class = _shell if _shell is not None else shell_pkg.Shell
136
+
137
+ for a, f in actions.items():
138
+ try:
139
+ delattr(_shell_class, 'do_' + a)
140
+ except AttributeError:
141
+ pass
142
+
143
+ if a in reserved_completers:
144
+ continue
145
+
146
+ try:
147
+ delattr(_shell_class, 'complete_' + a)
148
+ except AttributeError:
149
+ pass
150
+
151
+
116
152
  def _completer_wrapper(
117
153
  target: Callable[[Any], List[str]]
118
154
  ) -> Callable[['mrsm._internal.shell.Shell', str, str, int, int], Any]:
@@ -294,16 +330,19 @@ class Shell(cmd.Cmd):
294
330
  pass
295
331
 
296
332
  ### NOTE: custom actions must be added to the self._actions dictionary
333
+ shell_attrs['_session_id'] = get_cli_session_id()
297
334
  shell_attrs['_actions'] = actions
298
335
  shell_attrs['_sysargs'] = sysargs
299
336
  shell_attrs['_actions']['instance'] = self.do_instance
300
337
  shell_attrs['_actions']['repo'] = self.do_repo
301
338
  shell_attrs['_actions']['executor'] = self.do_executor
302
339
  shell_attrs['_actions']['debug'] = self.do_debug
340
+ shell_attrs['_actions']['daemon'] = self.do_daemon
303
341
  shell_attrs['_update_bottom_toolbar'] = True
304
342
  shell_attrs['_old_bottom_toolbar'] = ''
305
343
  shell_attrs['debug'] = False
306
344
  shell_attrs['_reload'] = True
345
+ shell_attrs['daemon'] = get_config('system', 'experimental', 'cli_daemon')
307
346
  self.load_config(instance=instance_keys)
308
347
  self.hidden_commands = []
309
348
  ### update hidden commands list (cmd2 only)
@@ -353,7 +392,7 @@ class Shell(cmd.Cmd):
353
392
  shell_attrs['instance'] = instance
354
393
  shell_attrs['instance_keys'] = remove_ansi(str(instance))
355
394
  if shell_attrs.get('repo_keys', None) is None:
356
- shell_attrs['repo_keys'] = get_config('meerschaum', 'default_repository', patch=patch)
395
+ shell_attrs['repo_keys'] = get_config('meerschaum', 'repository', patch=patch)
357
396
  if shell_attrs.get('executor_keys', None) is None:
358
397
  shell_attrs['executor_keys'] = get_executor_keys_from_context()
359
398
 
@@ -496,8 +535,7 @@ class Shell(cmd.Cmd):
496
535
  )
497
536
  )
498
537
  shell_attrs['prompt'] = self.prompt
499
- ### flush stdout
500
- print("", end="", flush=True)
538
+ flush_stdout()
501
539
 
502
540
 
503
541
  def precmd(self, line: str):
@@ -530,7 +568,6 @@ class Shell(cmd.Cmd):
530
568
 
531
569
  ### if the user specifies, clear the screen before executing any commands
532
570
  if _clear_screen:
533
- from meerschaum.utils.formatting._shell import clear_screen
534
571
  clear_screen(debug=shell_attrs['debug'])
535
572
 
536
573
  ### return blank commands (spaces break argparse)
@@ -618,7 +655,7 @@ class Shell(cmd.Cmd):
618
655
  if key == 'mrsm_instance':
619
656
  default_value = get_config('meerschaum', 'instance')
620
657
  elif key == 'repository':
621
- default_value = get_config('meerschaum', 'default_repository')
658
+ default_value = get_config('meerschaum', 'repository')
622
659
  elif key == 'executor_keys':
623
660
  default_value = get_executor_keys_from_context()
624
661
  else:
@@ -672,7 +709,12 @@ class Shell(cmd.Cmd):
672
709
  ([':'] + pipeline_args) if pipeline_args else []
673
710
  )
674
711
  try:
675
- success_tuple = entry(sysargs_to_execute, _patch_args=patch_args)
712
+ success_tuple = entry(
713
+ sysargs_to_execute,
714
+ _patch_args=patch_args,
715
+ _session_id=shell_attrs.get('session_id', None),
716
+ _use_cli_daemon=shell_attrs.get('daemon', False),
717
+ )
676
718
  except Exception as e:
677
719
  success_tuple = False, str(e)
678
720
 
@@ -732,6 +774,35 @@ class Shell(cmd.Cmd):
732
774
 
733
775
  info(f"Debug mode is {'on' if shell_attrs['debug'] else 'off'}.")
734
776
 
777
+ def do_daemon(self, action: Optional[List[str]] = None, executor_keys=None, **kw):
778
+ """
779
+ Toggle whether to route commands through the CLI daemon.
780
+
781
+ Command:
782
+ `debug {on/true | off/false}`
783
+ Ommitting on / off will toggle the existing value.
784
+ """
785
+ from meerschaum.utils.warnings import info
786
+ on_commands = {'on', 'true', 'True'}
787
+ off_commands = {'off', 'false', 'False'}
788
+ if action is None:
789
+ action = []
790
+ try:
791
+ state = action[0]
792
+ except (IndexError, AttributeError):
793
+ state = ''
794
+ if state == '':
795
+ shell_attrs['daemon'] = not shell_attrs['daemon']
796
+ elif state.lower() in on_commands:
797
+ shell_attrs['daemon'] = True
798
+ elif state.lower() in off_commands:
799
+ shell_attrs['daemon'] = False
800
+ else:
801
+ info(f"Unknown state '{state}'. Ignoring...")
802
+
803
+ info(f"CLI daemon mode is {'on' if shell_attrs['daemon'] else 'off'}.")
804
+ return True, "Success"
805
+
735
806
  def do_instance(
736
807
  self,
737
808
  action: Optional[List[str]] = None,
@@ -833,7 +904,7 @@ class Shell(cmd.Cmd):
833
904
  """
834
905
  Temporarily set a default Meerschaum repository for the duration of the shell.
835
906
  The default repository (mrsm.io) is loaded from the Meerschaum configuraton file
836
- (at keys 'meerschaum:default_repository').
907
+ (at keys 'meerschaum.repository').
837
908
 
838
909
  You can change the default repository with `edit config`.
839
910
 
@@ -864,7 +935,7 @@ class Shell(cmd.Cmd):
864
935
  except (IndexError, AttributeError):
865
936
  repo_keys = ''
866
937
  if repo_keys == '':
867
- repo_keys = get_config('meerschaum', 'default_repository', patch=True)
938
+ repo_keys = get_config('meerschaum', 'repository', patch=True)
868
939
 
869
940
  conn = parse_repo_keys(repo_keys, debug=debug)
870
941
  if conn is None or not conn:
@@ -947,7 +1018,7 @@ class Shell(cmd.Cmd):
947
1018
  show pipes -h
948
1019
  ```
949
1020
  """
950
- from meerschaum.actions import actions
1021
+ from meerschaum.actions import get_action
951
1022
  from meerschaum._internal.arguments._parser import parse_help
952
1023
  from meerschaum._internal.arguments._parse_arguments import parse_line
953
1024
  import textwrap
@@ -956,12 +1027,15 @@ class Shell(cmd.Cmd):
956
1027
  del args['action']
957
1028
  shell_attrs['_actions']['show'](['actions'], **args)
958
1029
  return ""
1030
+
959
1031
  if args['action'][0] not in shell_attrs['_actions']:
1032
+ action_func = get_action(args['action'])
960
1033
  try:
961
- print(textwrap.dedent(getattr(self, f"do_{args['action'][0]}").__doc__))
1034
+ print(textwrap.dedent(action_func.__doc__))
962
1035
  except Exception:
963
- print(f"No help on '{args['action'][0]}'.")
1036
+ print(f"No help on '{shlex.join(args['action'])}'.")
964
1037
  return ""
1038
+
965
1039
  parse_help(args)
966
1040
  return ""
967
1041
 
@@ -1029,7 +1103,6 @@ class Shell(cmd.Cmd):
1029
1103
 
1030
1104
  ### if the user specifies, clear the screen before initializing the shell
1031
1105
  if _clear_screen:
1032
- from meerschaum.utils.formatting._shell import clear_screen
1033
1106
  clear_screen(debug=shell_attrs['debug'])
1034
1107
 
1035
1108
  ### if sysargs are provided, skip printing the intro and execute instead
@@ -1045,9 +1118,10 @@ def input_with_sigint(_input, session, shell: Optional[Shell] = None):
1045
1118
  Replace built-in `input()` with prompt_toolkit.prompt.
1046
1119
  """
1047
1120
  from meerschaum.utils.formatting import CHARSET, ANSI, colored, UNICODE
1048
- from meerschaum.connectors import is_connected, connectors
1121
+ from meerschaum.connectors import connectors
1049
1122
  from meerschaum.utils.misc import remove_ansi, truncate_text_for_display
1050
1123
  from meerschaum.config import get_config
1124
+
1051
1125
  import platform
1052
1126
  if shell is None:
1053
1127
  from meerschaum.actions import get_shell
@@ -1062,8 +1136,16 @@ def input_with_sigint(_input, session, shell: Optional[Shell] = None):
1062
1136
  return None
1063
1137
  if not shell_attrs['_update_bottom_toolbar'] and platform.system() == 'Windows':
1064
1138
  return shell_attrs['_old_bottom_toolbar']
1065
- size = os.get_terminal_size()
1066
- num_cols, num_lines = size.columns, size.lines
1139
+ try:
1140
+ size = os.get_terminal_size()
1141
+ num_cols, num_lines = size.columns, size.lines
1142
+ except Exception:
1143
+ from meerschaum.utils.misc import is_int
1144
+ num_cols, num_lines = os.environ.get('COLUMNS', 80), os.environ.get('LINES', 120)
1145
+ if is_int(str(num_cols)):
1146
+ num_cols = int(num_cols)
1147
+ if is_int(str(num_lines)):
1148
+ num_lines = int(num_lines)
1067
1149
  truncation_suffix = (
1068
1150
  '…'
1069
1151
  if UNICODE
@@ -1112,24 +1194,34 @@ def input_with_sigint(_input, session, shell: Optional[Shell] = None):
1112
1194
 
1113
1195
  try:
1114
1196
  typ, label = shell_attrs['instance_keys'].split(':', maxsplit=1)
1115
- connected = typ in connectors and label in connectors[typ]
1197
+ cli_worker = (
1198
+ get_cli_daemon()
1199
+ if shell_attrs.get('daemon', False)
1200
+ else None
1201
+ )
1202
+ connected = (
1203
+ cli_worker.job.status == 'running'
1204
+ if cli_worker is not None
1205
+ else (typ in connectors and label in connectors[typ])
1206
+ )
1116
1207
  except Exception:
1117
1208
  connected = False
1118
1209
  last_connected = connected
1119
1210
  connected_str = truncate_text_for_display(
1120
- ('dis' if not connected else '') + 'connected',
1211
+ ('daemon' if cli_worker is not None else remove_ansi(shell_attrs['instance_keys'])),
1121
1212
  **truncation_kwargs
1122
1213
  )
1214
+ connected_status_str = 'disconnected' if not connected else 'connected'
1123
1215
  connected_icon = get_config(
1124
- 'formatting', connected_str, CHARSET, 'icon', warn=False,
1216
+ 'formatting', connected_status_str, CHARSET, 'icon', warn=False,
1125
1217
  ) or ''
1126
1218
  connection_text = (
1127
- connected_icon + ' ' + (
1128
- colored(connected_str.capitalize(), 'on ' + (get_config(
1129
- 'formatting', connected_str, 'ansi', 'rich', 'style',
1219
+ (
1220
+ colored(connected_str, 'on ' + (get_config(
1221
+ 'formatting', connected_status_str, 'ansi', 'rich', 'style',
1130
1222
  warn=False,
1131
- ) or '') + ' ') if ANSI else (colored(connected_str.capitalize(), 'on white') + ' ')
1132
- )
1223
+ ) or '') + ' ') if ANSI else (colored(connected_str, 'on white') + ' ')
1224
+ ) + ' ' + connected_icon
1133
1225
  )
1134
1226
 
1135
1227
  left = (
@@ -1164,7 +1256,6 @@ def input_with_sigint(_input, session, shell: Optional[Shell] = None):
1164
1256
  ### NOTE: would it be better to do nothing instead?
1165
1257
  if len(parsed.strip()) == 0:
1166
1258
  if _clear_screen:
1167
- from meerschaum.utils.formatting._shell import clear_screen
1168
1259
  clear_screen()
1169
1260
  except KeyboardInterrupt:
1170
1261
  print("^C")
@@ -8,5 +8,8 @@ Import the Shell class definition
8
8
 
9
9
  from meerschaum._internal.shell.Shell import Shell
10
10
  from meerschaum._internal.shell.Shell import (
11
- default_action_completer, _completer_wrapper, _insert_shell_actions,
11
+ default_action_completer,
12
+ _completer_wrapper,
13
+ _insert_shell_actions,
14
+ _remove_shell_actions,
12
15
  )
@@ -240,6 +240,7 @@ STATIC_CONFIG: Dict[str, Any] = {
240
240
  'noask': 'MRSM_NOASK',
241
241
  'noninteractive': 'MRSM_NONINTERACTIVE',
242
242
  'id': 'MRSM_SERVER_ID',
243
+ 'test_flavors': 'MRSM_TEST_FLAVORS',
243
244
  'daemon_id': 'MRSM_DAEMON_ID',
244
245
  'systemd_log_path': 'MRSM_SYSTEMD_LOG_PATH',
245
246
  'systemd_stdin_path': 'MRSM_SYSTEMD_STDIN_PATH',
@@ -322,6 +323,9 @@ STATIC_CONFIG: Dict[str, Any] = {
322
323
  },
323
324
  'jobs': {
324
325
  'check_restart_seconds': 1.0,
326
+ 'stop_token': '<------- MRSM_STOP_TOKEN ------->',
327
+ 'clear_token': '<------- MRSM_CLEAR_TOKEN ------->',
328
+ 'flush_token': '<------- MRSM_FLUSH_TOKEN ------->\n',
325
329
  },
326
330
  'tokens': {
327
331
  'minimum_length': 24,
@@ -352,5 +356,4 @@ STATIC_CONFIG: Dict[str, Any] = {
352
356
  'users:delete': "Delete the associated user account (or other users for admins).",
353
357
  },
354
358
  },
355
-
356
359
  }
@@ -10,11 +10,10 @@ from __future__ import annotations
10
10
  from typing import Any
11
11
 
12
12
  import meerschaum as mrsm
13
- from meerschaum.utils.warnings import warn
14
13
  tornado_web = mrsm.attempt_import('tornado.web', lazy=False)
15
14
  terminado = mrsm.attempt_import('terminado', lazy=False)
16
15
 
17
- tmux_suffix = mrsm.get_config('system', 'webterm', 'tmux', 'session_suffix')
16
+ tmux_suffix = mrsm.get_config('api', 'webterm', 'tmux', 'session_suffix')
18
17
 
19
18
 
20
19
  class TermPageHandler(tornado_web.RequestHandler):
@@ -8,6 +8,9 @@ Build the web console virtual terminal using Tornado and xterm.js.
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ import os
12
+ import json
13
+ import pathlib
11
14
  from typing import Optional, Tuple
12
15
 
13
16
  import meerschaum as mrsm
@@ -16,7 +19,6 @@ from meerschaum._internal.term.TermPageHandler import TermPageHandler, CustomTer
16
19
  from meerschaum.config._paths import API_TEMPLATES_PATH, API_STATIC_PATH
17
20
  from meerschaum.utils.venv import venv_executable
18
21
  from meerschaum.utils.misc import is_tmux_available
19
- from meerschaum.utils.daemon._names import get_new_daemon_name
20
22
 
21
23
  tornado, tornado_ioloop, terminado = attempt_import(
22
24
  'tornado', 'tornado.ioloop', 'terminado', lazy=False,
@@ -25,6 +27,8 @@ tornado, tornado_ioloop, terminado = attempt_import(
25
27
 
26
28
  def get_webterm_app_and_manager(
27
29
  instance_keys: Optional[str] = None,
30
+ port: Optional[int] = None,
31
+ env_path: Optional[pathlib.Path] = None,
28
32
  ) -> Tuple[
29
33
  tornado.web.Application,
30
34
  terminado.UniqueTermManager,
@@ -36,19 +40,49 @@ def get_webterm_app_and_manager(
36
40
  -------
37
41
  A tuple of the Tornado web application and term manager.
38
42
  """
43
+ from meerschaum.config.environment import get_env_vars
44
+ from meerschaum._internal.static import STATIC_CONFIG
45
+ if env_path is None:
46
+ from meerschaum.config.paths import WEBTERM_INTERNAL_RESOURCES_PATH
47
+ env_path = WEBTERM_INTERNAL_RESOURCES_PATH / (str(port) + '.json')
48
+
49
+ env_vars = get_env_vars()
50
+ env_dict = {
51
+ env_var: os.environ[env_var]
52
+ for env_var in env_vars
53
+ if env_var not in (
54
+ STATIC_CONFIG['environment']['systemd_log_path'],
55
+ STATIC_CONFIG['environment']['systemd_result_path'],
56
+ STATIC_CONFIG['environment']['systemd_delete_job'],
57
+ STATIC_CONFIG['environment']['systemd_stdin_path'],
58
+ STATIC_CONFIG['environment']['daemon_id'],
59
+ )
60
+ }
61
+ with open(env_path, 'w+', encoding='utf-8') as f:
62
+ json.dump(env_dict, f)
63
+
39
64
  shell_kwargs_str = f"mrsm_instance='{instance_keys}'" if instance_keys else ""
40
65
  commands = [
41
66
  venv_executable(None),
42
67
  '-c',
43
- "import os; _ = os.environ.pop('COLUMNS', None); _ = os.environ.pop('LINES', None); "
44
- "from meerschaum._internal.entry import get_shell; "
68
+ "import os\n"
69
+ "import pathlib\n"
70
+ "import json\n"
71
+ f"env_path = pathlib.Path('{env_path.as_posix()}')\n"
72
+ "with open(env_path, 'r', encoding='utf-8') as f:\n"
73
+ " env_dict = json.load(f)\n"
74
+ "os.environ.update(env_dict)\n"
75
+ "_ = os.environ.pop('COLUMNS', None)\n"
76
+ "_ = os.environ.pop('LINES', None)\n"
77
+ "from meerschaum._internal.entry import get_shell\n"
45
78
  f"get_shell({shell_kwargs_str}).cmdloop()"
46
79
  ]
47
- webterm_cf = mrsm.get_config('system', 'webterm')
80
+ webterm_cf = mrsm.get_config('api', 'webterm')
48
81
  if webterm_cf.get('tmux', {}).get('enabled', False) and is_tmux_available():
49
- commands = ['tmux', 'new-session', '-A', '-s', 'MRSM_SESSION'] + commands
82
+ commands = ['tmux', 'new-session', '-A', '-s', f'MRSM_SESSION--{port}'] + commands
50
83
 
51
- term_manager = terminado.NamedTermManager(shell_command=commands)
84
+ term_manager = terminado.NamedTermManager(shell_command=commands, extra_env=env_dict)
85
+ term_manager._port = port
52
86
  handlers = [
53
87
  (
54
88
  r"/websocket/(.+)/?",
@@ -6,13 +6,14 @@
6
6
  Utility functions regarding the webterm.
7
7
  """
8
8
 
9
+ from typing import List, Optional
10
+
9
11
  import meerschaum as mrsm
10
- from meerschaum.utils.typing import List
11
12
 
12
13
 
13
14
  def is_webterm_running(
14
- host: str,
15
- port: int,
15
+ host: Optional[str] = None,
16
+ port: Optional[int] = None,
16
17
  protocol: str = 'http',
17
18
  session_id: str = 'mrsm',
18
19
  ) -> int:
@@ -20,10 +21,12 @@ def is_webterm_running(
20
21
  Determine whether the webterm service is running on a given host and port.
21
22
  """
22
23
  requests = mrsm.attempt_import('requests', lazy=False)
24
+ host = host or mrsm.get_config('api', 'webterm', 'host')
25
+ port = port or mrsm.get_config('api', 'webterm', 'port')
23
26
  url = f'{protocol}://{host}:{port}/webterm/{session_id}'
24
27
  try:
25
28
  r = requests.get(url, timeout=3)
26
- except Exception as e:
29
+ except Exception:
27
30
  return False
28
31
  if not r:
29
32
  return False
@@ -39,22 +42,44 @@ def kill_tmux_session(session: str) -> bool:
39
42
  return run_process(command, capture_output=True) == 0
40
43
 
41
44
 
42
- def get_mrsm_tmux_sessions() -> List[str]:
45
+ def get_mrsm_tmux_sessions(port: Optional[int] = None) -> List[str]:
43
46
  """
44
47
  Return a list of tmux sessions created by Meerschaum.
45
48
  """
46
49
  from meerschaum.utils.process import run_process
47
- tmux_suffix = mrsm.get_config('system', 'webterm', 'tmux', 'session_suffix')
50
+ tmux_suffix = mrsm.get_config('api', 'webterm', 'tmux', 'session_suffix')
48
51
  command = ['tmux', 'ls']
49
52
  proc = run_process(command, capture_output=True, as_proc=True)
50
53
  if proc.returncode != 0:
51
54
  return []
55
+
56
+ port = port or mrsm.get_config('api', 'webterm', 'port')
57
+
52
58
  sessions = [
53
59
  line.split(':', maxsplit=1)[0]
54
60
  for line in proc.stdout.read().decode('utf-8').split('\n')
55
61
  ]
62
+ mrsm_sessions_ports = []
63
+ for session in sessions:
64
+ if '--' not in session:
65
+ continue
66
+
67
+ parts = session.split('--', maxsplit=1)
68
+ if len(parts) != 2:
69
+ continue
70
+
71
+ if not parts[0].endswith(tmux_suffix):
72
+ continue
73
+
74
+ try:
75
+ session_port = int(parts[1])
76
+ except Exception:
77
+ continue
78
+
79
+ mrsm_sessions_ports.append((session, session_port))
80
+
56
81
  return [
57
82
  session
58
- for session in sessions
59
- if session.endswith(tmux_suffix)
83
+ for session, session_port in mrsm_sessions_ports
84
+ if session_port == port
60
85
  ]
@@ -9,7 +9,6 @@ Default actions available to the mrsm CLI.
9
9
  from __future__ import annotations
10
10
  from meerschaum.utils.typing import Callable, Any, Optional, Union, List, Dict, SuccessTuple
11
11
  from meerschaum.utils.packages import get_modules_from_package
12
- _custom_actions = []
13
12
 
14
13
  __all__ = (
15
14
  'get_action',
@@ -307,6 +306,8 @@ __all__ = ['actions', 'get_subactions', 'get_action', 'get_main_action_name', 'g
307
306
  ### functions that do not begin with '_' from all submodules.
308
307
  from inspect import getmembers, isfunction
309
308
  actions = {}
309
+ _custom_actions_plugins: Dict[str, str] = {}
310
+ _plugins_actions: Dict[str, List[str]] = {}
310
311
 
311
312
  for module in modules:
312
313
  ### A couple important things happening here:
@@ -329,8 +330,8 @@ for module in modules:
329
330
  if isfunction(ob[1])
330
331
  ### check that the function belongs to the module
331
332
  and ob[0] == module.__name__.replace('_', '').split('.')[-1]
332
- ### skip functions that start with '_'
333
- and ob[0][0] != '_'
333
+ ### skip functions that start with '__'
334
+ and ob[0][0] != '__'
334
335
  ]
335
336
  )
336
337
  )
@@ -362,4 +363,5 @@ __pdoc__ = {
362
363
  }
363
364
  for a in actions:
364
365
  __pdoc__[a] = False
365
- meerschaum.plugins.load_plugins()
366
+
367
+ meerschaum.plugins.load_plugins(skip_if_loaded=True)