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.
- meerschaum/_internal/arguments/_parser.py +14 -2
- meerschaum/_internal/cli/__init__.py +6 -0
- meerschaum/_internal/cli/daemons.py +103 -0
- meerschaum/_internal/cli/entry.py +220 -0
- meerschaum/_internal/cli/workers.py +435 -0
- meerschaum/_internal/docs/index.py +1 -2
- meerschaum/_internal/entry.py +44 -8
- meerschaum/_internal/shell/Shell.py +115 -24
- meerschaum/_internal/shell/__init__.py +4 -1
- meerschaum/_internal/static.py +4 -1
- meerschaum/_internal/term/TermPageHandler.py +1 -2
- meerschaum/_internal/term/__init__.py +40 -6
- meerschaum/_internal/term/tools.py +33 -8
- meerschaum/actions/__init__.py +6 -4
- meerschaum/actions/api.py +39 -11
- meerschaum/actions/attach.py +1 -0
- meerschaum/actions/delete.py +4 -2
- meerschaum/actions/edit.py +27 -8
- meerschaum/actions/login.py +8 -8
- meerschaum/actions/register.py +13 -7
- meerschaum/actions/reload.py +22 -5
- meerschaum/actions/restart.py +14 -0
- meerschaum/actions/show.py +69 -4
- meerschaum/actions/start.py +135 -14
- meerschaum/actions/stop.py +36 -3
- meerschaum/actions/sync.py +6 -1
- meerschaum/api/__init__.py +35 -13
- meerschaum/api/_events.py +2 -2
- meerschaum/api/_oauth2.py +47 -4
- meerschaum/api/dash/callbacks/dashboard.py +29 -0
- meerschaum/api/dash/callbacks/jobs.py +3 -2
- meerschaum/api/dash/callbacks/login.py +10 -1
- meerschaum/api/dash/callbacks/register.py +9 -2
- meerschaum/api/dash/pages/login.py +2 -2
- meerschaum/api/dash/pipes.py +72 -36
- meerschaum/api/dash/webterm.py +14 -6
- meerschaum/api/models/_pipes.py +7 -1
- meerschaum/api/resources/static/js/terminado.js +3 -0
- meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
- meerschaum/api/resources/templates/termpage.html +1 -0
- meerschaum/api/routes/_jobs.py +23 -11
- meerschaum/api/routes/_login.py +73 -5
- meerschaum/api/routes/_pipes.py +6 -4
- meerschaum/api/routes/_webterm.py +3 -3
- meerschaum/config/__init__.py +60 -13
- meerschaum/config/_default.py +89 -61
- meerschaum/config/_edit.py +10 -8
- meerschaum/config/_formatting.py +2 -0
- meerschaum/config/_patch.py +4 -2
- meerschaum/config/_paths.py +127 -12
- meerschaum/config/_read_config.py +32 -12
- meerschaum/config/_version.py +1 -1
- meerschaum/config/environment.py +262 -0
- meerschaum/config/stack/__init__.py +7 -5
- meerschaum/connectors/_Connector.py +1 -2
- meerschaum/connectors/__init__.py +37 -2
- meerschaum/connectors/api/_APIConnector.py +1 -1
- meerschaum/connectors/api/_jobs.py +11 -0
- meerschaum/connectors/api/_pipes.py +7 -1
- meerschaum/connectors/instance/_plugins.py +9 -1
- meerschaum/connectors/instance/_tokens.py +20 -3
- meerschaum/connectors/instance/_users.py +8 -1
- meerschaum/connectors/parse.py +1 -1
- meerschaum/connectors/sql/_create_engine.py +3 -0
- meerschaum/connectors/sql/_pipes.py +93 -79
- meerschaum/connectors/sql/_users.py +8 -1
- meerschaum/connectors/valkey/_ValkeyConnector.py +3 -3
- meerschaum/connectors/valkey/_pipes.py +7 -5
- meerschaum/core/Pipe/__init__.py +45 -71
- meerschaum/core/Pipe/_attributes.py +66 -90
- meerschaum/core/Pipe/_cache.py +555 -0
- meerschaum/core/Pipe/_clear.py +0 -11
- meerschaum/core/Pipe/_data.py +0 -50
- meerschaum/core/Pipe/_deduplicate.py +0 -13
- meerschaum/core/Pipe/_delete.py +12 -21
- meerschaum/core/Pipe/_drop.py +11 -23
- meerschaum/core/Pipe/_dtypes.py +1 -1
- meerschaum/core/Pipe/_index.py +8 -14
- meerschaum/core/Pipe/_sync.py +12 -18
- meerschaum/core/Plugin/_Plugin.py +7 -1
- meerschaum/core/Token/_Token.py +1 -1
- meerschaum/core/User/_User.py +1 -2
- meerschaum/jobs/_Executor.py +88 -4
- meerschaum/jobs/_Job.py +146 -36
- meerschaum/jobs/systemd.py +7 -2
- meerschaum/plugins/__init__.py +277 -81
- meerschaum/utils/daemon/Daemon.py +197 -42
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
- meerschaum/utils/daemon/RotatingFile.py +63 -36
- meerschaum/utils/daemon/StdinFile.py +53 -13
- meerschaum/utils/daemon/__init__.py +18 -5
- meerschaum/utils/daemon/_names.py +6 -3
- meerschaum/utils/debug.py +34 -4
- meerschaum/utils/dtypes/__init__.py +5 -1
- meerschaum/utils/formatting/__init__.py +4 -1
- meerschaum/utils/formatting/_jobs.py +1 -1
- meerschaum/utils/formatting/_pipes.py +47 -46
- meerschaum/utils/formatting/_shell.py +33 -9
- meerschaum/utils/misc.py +22 -38
- meerschaum/utils/packages/__init__.py +15 -13
- meerschaum/utils/packages/_packages.py +1 -0
- meerschaum/utils/pipes.py +33 -5
- meerschaum/utils/process.py +1 -1
- meerschaum/utils/prompt.py +172 -143
- meerschaum/utils/sql.py +12 -2
- meerschaum/utils/threading.py +42 -0
- meerschaum/utils/venv/__init__.py +2 -0
- meerschaum/utils/warnings.py +19 -13
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/METADATA +3 -1
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/RECORD +116 -110
- meerschaum/config/_environment.py +0 -145
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/WHEEL +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/entry_points.txt +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/licenses/NOTICE +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/top_level.txt +0 -0
- {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', '
|
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
|
-
|
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', '
|
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(
|
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
|
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', '
|
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
|
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(
|
1034
|
+
print(textwrap.dedent(action_func.__doc__))
|
962
1035
|
except Exception:
|
963
|
-
print(f"No help on '{args['action']
|
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
|
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
|
-
|
1066
|
-
|
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
|
-
|
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
|
-
('
|
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',
|
1216
|
+
'formatting', connected_status_str, CHARSET, 'icon', warn=False,
|
1125
1217
|
) or ''
|
1126
1218
|
connection_text = (
|
1127
|
-
|
1128
|
-
colored(connected_str
|
1129
|
-
'formatting',
|
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
|
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,
|
11
|
+
default_action_completer,
|
12
|
+
_completer_wrapper,
|
13
|
+
_insert_shell_actions,
|
14
|
+
_remove_shell_actions,
|
12
15
|
)
|
meerschaum/_internal/static.py
CHANGED
@@ -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('
|
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
|
44
|
-
"
|
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('
|
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
|
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('
|
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
|
59
|
-
if
|
83
|
+
for session, session_port in mrsm_sessions_ports
|
84
|
+
if session_port == port
|
60
85
|
]
|
meerschaum/actions/__init__.py
CHANGED
@@ -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
|
-
|
366
|
+
|
367
|
+
meerschaum.plugins.load_plugins(skip_if_loaded=True)
|