meerschaum 2.7.8__py3-none-any.whl → 2.7.9__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- meerschaum/_internal/term/TermPageHandler.py +54 -4
- meerschaum/_internal/term/__init__.py +13 -5
- meerschaum/_internal/term/tools.py +41 -6
- meerschaum/actions/start.py +25 -10
- meerschaum/api/dash/callbacks/dashboard.py +43 -2
- meerschaum/api/dash/components.py +13 -6
- meerschaum/api/dash/keys.py +82 -108
- meerschaum/api/dash/pages/dashboard.py +17 -17
- meerschaum/api/dash/sessions.py +1 -0
- meerschaum/api/dash/webterm.py +17 -6
- meerschaum/api/resources/static/js/terminado.js +0 -2
- meerschaum/api/resources/templates/termpage.html +47 -4
- meerschaum/api/routes/_webterm.py +15 -11
- meerschaum/config/_default.py +6 -0
- meerschaum/config/_version.py +1 -1
- meerschaum/config/static/__init__.py +2 -2
- meerschaum/core/Pipe/_sync.py +7 -7
- meerschaum/core/Pipe/_verify.py +1 -1
- meerschaum/utils/daemon/Daemon.py +15 -9
- meerschaum/utils/dtypes/__init__.py +9 -0
- meerschaum/utils/dtypes/sql.py +19 -13
- meerschaum/utils/formatting/_pprint.py +1 -1
- meerschaum/utils/misc.py +16 -0
- meerschaum/utils/venv/__init__.py +10 -14
- {meerschaum-2.7.8.dist-info → meerschaum-2.7.9.dist-info}/METADATA +1 -1
- {meerschaum-2.7.8.dist-info → meerschaum-2.7.9.dist-info}/RECORD +32 -32
- {meerschaum-2.7.8.dist-info → meerschaum-2.7.9.dist-info}/LICENSE +0 -0
- {meerschaum-2.7.8.dist-info → meerschaum-2.7.9.dist-info}/NOTICE +0 -0
- {meerschaum-2.7.8.dist-info → meerschaum-2.7.9.dist-info}/WHEEL +0 -0
- {meerschaum-2.7.8.dist-info → meerschaum-2.7.9.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.7.8.dist-info → meerschaum-2.7.9.dist-info}/top_level.txt +0 -0
- {meerschaum-2.7.8.dist-info → meerschaum-2.7.9.dist-info}/zip-safe +0 -0
@@ -7,14 +7,64 @@ Define the terminal page handler class.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
-
from
|
11
|
-
|
10
|
+
from typing import Any
|
11
|
+
|
12
|
+
import meerschaum as mrsm
|
13
|
+
from meerschaum.utils.warnings import warn
|
14
|
+
tornado_web = mrsm.attempt_import('tornado.web', lazy=False)
|
15
|
+
terminado = mrsm.attempt_import('terminado', lazy=False)
|
16
|
+
|
17
|
+
tmux_suffix = mrsm.get_config('system', 'webterm', 'tmux', 'session_suffix')
|
18
|
+
|
12
19
|
|
13
20
|
class TermPageHandler(tornado_web.RequestHandler):
|
14
|
-
|
21
|
+
|
22
|
+
def get(self, term_name):
|
23
|
+
term_manager = self.application.settings['term_manager']
|
24
|
+
shell_command = [
|
25
|
+
cmd.replace('MRSM_SESSION', term_name + tmux_suffix)
|
26
|
+
for cmd in term_manager.shell_command
|
27
|
+
]
|
28
|
+
terminal = term_manager.new_terminal(shell_command=shell_command)
|
29
|
+
terminal.term_name = term_name
|
30
|
+
term_manager.terminals[term_name] = terminal
|
31
|
+
term_manager.start_reading(terminal)
|
15
32
|
return self.render(
|
16
33
|
"termpage.html",
|
17
34
|
static=self.static_url,
|
18
|
-
ws_url_path="/
|
35
|
+
ws_url_path=f"/_websocket/{term_name}",
|
19
36
|
)
|
20
37
|
|
38
|
+
|
39
|
+
class CustomTermSocket(terminado.TermSocket):
|
40
|
+
|
41
|
+
def open(self, url_component: Any = None) -> None:
|
42
|
+
super(terminado.TermSocket, self).open(url_component)
|
43
|
+
url_component = (
|
44
|
+
url_component.decode("utf-8")
|
45
|
+
if isinstance(url_component, bytes)
|
46
|
+
else url_component
|
47
|
+
)
|
48
|
+
self.term_name = url_component
|
49
|
+
shell_command = [
|
50
|
+
cmd.replace('MRSM_SESSION', self.term_name + tmux_suffix)
|
51
|
+
for cmd in self.term_manager.shell_command
|
52
|
+
]
|
53
|
+
if self.term_name not in self.term_manager.terminals:
|
54
|
+
self.terminal = self.term_manager.new_terminal(shell_command=shell_command)
|
55
|
+
self.terminal.term_name = self.term_name
|
56
|
+
self.term_manager.terminals[self.term_name] = self.terminal
|
57
|
+
self.term_manager.start_reading(self.terminal)
|
58
|
+
else:
|
59
|
+
self.terminal = self.term_manager.terminals[self.term_name]
|
60
|
+
self.terminal.clients.append(self)
|
61
|
+
self.send_json_message(["setup", {}])
|
62
|
+
buffered = ""
|
63
|
+
preopen_buffer = self.terminal.read_buffer.copy()
|
64
|
+
while True:
|
65
|
+
if not preopen_buffer:
|
66
|
+
break
|
67
|
+
s = preopen_buffer.popleft()
|
68
|
+
buffered += s
|
69
|
+
if buffered:
|
70
|
+
self.on_pty_read(buffered)
|
@@ -9,10 +9,14 @@ Build the web console virtual terminal using Tornado and xterm.js.
|
|
9
9
|
from __future__ import annotations
|
10
10
|
|
11
11
|
from typing import Optional, Tuple
|
12
|
+
|
13
|
+
import meerschaum as mrsm
|
12
14
|
from meerschaum.utils.packages import attempt_import
|
13
|
-
from meerschaum._internal.term.TermPageHandler import TermPageHandler
|
15
|
+
from meerschaum._internal.term.TermPageHandler import TermPageHandler, CustomTermSocket
|
14
16
|
from meerschaum.config._paths import API_TEMPLATES_PATH, API_STATIC_PATH
|
15
17
|
from meerschaum.utils.venv import venv_executable
|
18
|
+
from meerschaum.utils.misc import is_tmux_available
|
19
|
+
from meerschaum.utils.daemon._names import get_new_daemon_name
|
16
20
|
|
17
21
|
tornado, tornado_ioloop, terminado = attempt_import(
|
18
22
|
'tornado', 'tornado.ioloop', 'terminado', lazy=False,
|
@@ -40,16 +44,19 @@ def get_webterm_app_and_manager(
|
|
40
44
|
"from meerschaum._internal.entry import get_shell; "
|
41
45
|
f"get_shell({shell_kwargs_str}).cmdloop()"
|
42
46
|
]
|
47
|
+
webterm_cf = mrsm.get_config('system', 'webterm')
|
48
|
+
if webterm_cf.get('tmux', {}).get('enabled', False) and is_tmux_available():
|
49
|
+
commands = ['tmux', 'new-session', '-A', '-s', 'MRSM_SESSION'] + commands
|
43
50
|
|
44
|
-
term_manager = terminado.
|
51
|
+
term_manager = terminado.NamedTermManager(shell_command=commands)
|
45
52
|
handlers = [
|
46
53
|
(
|
47
|
-
r"/
|
48
|
-
|
54
|
+
r"/_websocket/(.+)/?",
|
55
|
+
CustomTermSocket,
|
49
56
|
{'term_manager': term_manager}
|
50
57
|
),
|
51
58
|
(
|
52
|
-
r"/",
|
59
|
+
r"/webterm/(.+)/?",
|
53
60
|
TermPageHandler
|
54
61
|
),
|
55
62
|
]
|
@@ -57,5 +64,6 @@ def get_webterm_app_and_manager(
|
|
57
64
|
handlers,
|
58
65
|
static_path=API_STATIC_PATH,
|
59
66
|
template_path=API_TEMPLATES_PATH,
|
67
|
+
term_manager=term_manager,
|
60
68
|
)
|
61
69
|
return tornado_app, term_manager
|
@@ -6,16 +6,21 @@
|
|
6
6
|
Utility functions regarding the webterm.
|
7
7
|
"""
|
8
8
|
|
9
|
-
|
9
|
+
import meerschaum as mrsm
|
10
|
+
from meerschaum.utils.typing import List
|
10
11
|
|
11
|
-
|
12
|
+
|
13
|
+
def is_webterm_running(
|
14
|
+
host: str,
|
15
|
+
port: int,
|
16
|
+
protocol: str = 'http',
|
17
|
+
session_id: str = 'mrsm',
|
18
|
+
) -> int:
|
12
19
|
"""
|
13
20
|
Determine whether the webterm service is running on a given host and port.
|
14
21
|
"""
|
15
|
-
|
16
|
-
|
17
|
-
requests = attempt_import('requests')
|
18
|
-
url = f'{protocol}://{host}:{port}'
|
22
|
+
requests = mrsm.attempt_import('requests', lazy=False)
|
23
|
+
url = f'{protocol}://{host}:{port}/webterm/{session_id}'
|
19
24
|
try:
|
20
25
|
r = requests.get(url, timeout=3)
|
21
26
|
except Exception as e:
|
@@ -23,3 +28,33 @@ def is_webterm_running(host: str, port: int, protocol: str = 'http') -> int:
|
|
23
28
|
if not r:
|
24
29
|
return False
|
25
30
|
return '<title>Meerschaum Shell</title>' in r.text
|
31
|
+
|
32
|
+
|
33
|
+
def kill_tmux_session(session: str) -> bool:
|
34
|
+
"""
|
35
|
+
Kill a tmux session if it exists.
|
36
|
+
"""
|
37
|
+
from meerschaum.utils.process import run_process
|
38
|
+
command = ['tmux', 'kill-session', '-t', session]
|
39
|
+
return run_process(command, capture_output=True) == 0
|
40
|
+
|
41
|
+
|
42
|
+
def get_mrsm_tmux_sessions() -> List[str]:
|
43
|
+
"""
|
44
|
+
Return a list of tmux sessions created by Meerschaum.
|
45
|
+
"""
|
46
|
+
from meerschaum.utils.process import run_process
|
47
|
+
tmux_suffix = mrsm.get_config('system', 'webterm', 'tmux', 'session_suffix')
|
48
|
+
command = ['tmux', 'ls']
|
49
|
+
proc = run_process(command, capture_output=True, as_proc=True)
|
50
|
+
if proc.returncode != 0:
|
51
|
+
return []
|
52
|
+
sessions = [
|
53
|
+
line.split(':', maxsplit=1)[0]
|
54
|
+
for line in proc.stdout.read().decode('utf-8').split('\n')
|
55
|
+
]
|
56
|
+
return [
|
57
|
+
session
|
58
|
+
for session in sessions
|
59
|
+
if session.endswith(tmux_suffix)
|
60
|
+
]
|
meerschaum/actions/start.py
CHANGED
@@ -314,6 +314,11 @@ def _start_gui(
|
|
314
314
|
"""
|
315
315
|
Start the Meerschaum GUI application.
|
316
316
|
"""
|
317
|
+
import json
|
318
|
+
import time
|
319
|
+
import uuid
|
320
|
+
import platform
|
321
|
+
|
317
322
|
from meerschaum.utils.venv import venv_exec
|
318
323
|
from meerschaum.utils.packages import attempt_import
|
319
324
|
from meerschaum.utils.warnings import warn
|
@@ -321,17 +326,14 @@ def _start_gui(
|
|
321
326
|
from meerschaum.utils.networking import find_open_ports
|
322
327
|
from meerschaum.connectors.parse import parse_instance_keys
|
323
328
|
from meerschaum._internal.term.tools import is_webterm_running
|
324
|
-
import platform
|
325
329
|
webview, requests = attempt_import('webview', 'requests')
|
326
|
-
import json
|
327
|
-
import time
|
328
330
|
|
329
331
|
success, msg = True, "Success"
|
330
332
|
host = '127.0.0.1'
|
331
333
|
if port is None:
|
332
334
|
port = 8765
|
333
335
|
|
334
|
-
if not is_webterm_running(host, port):
|
336
|
+
if not is_webterm_running(host, port, session_id='mrsm'):
|
335
337
|
port = next(find_open_ports(port, 9000))
|
336
338
|
|
337
339
|
api_kw = {
|
@@ -350,7 +352,7 @@ def _start_gui(
|
|
350
352
|
)
|
351
353
|
if debug:
|
352
354
|
print(start_tornado_code)
|
353
|
-
base_url = 'http://' + api_kw['host'] + ':' + str(api_kw['port'])
|
355
|
+
base_url = 'http://' + api_kw['host'] + ':' + str(api_kw['port']) + '/webterm/mrsm'
|
354
356
|
|
355
357
|
process = venv_exec(
|
356
358
|
start_tornado_code, as_proc=True, debug=debug, capture_output=(not debug)
|
@@ -375,7 +377,7 @@ def _start_gui(
|
|
375
377
|
try:
|
376
378
|
webview.create_window(
|
377
379
|
'Meerschaum Shell',
|
378
|
-
f'http://127.0.0.1:{port}',
|
380
|
+
f'http://127.0.0.1:{port}/webterm/mrsm',
|
379
381
|
height=650,
|
380
382
|
width=1000
|
381
383
|
)
|
@@ -415,8 +417,13 @@ def _start_webterm(
|
|
415
417
|
- `-i`, '--instance'
|
416
418
|
The default instance to use for the Webterm shell.
|
417
419
|
"""
|
420
|
+
import uuid
|
418
421
|
from meerschaum._internal.term import get_webterm_app_and_manager, tornado_ioloop
|
419
|
-
from meerschaum._internal.term.tools import
|
422
|
+
from meerschaum._internal.term.tools import (
|
423
|
+
is_webterm_running,
|
424
|
+
get_mrsm_tmux_sessions,
|
425
|
+
kill_tmux_session,
|
426
|
+
)
|
420
427
|
from meerschaum.utils.networking import find_open_ports
|
421
428
|
from meerschaum.utils.warnings import info
|
422
429
|
|
@@ -426,19 +433,23 @@ def _start_webterm(
|
|
426
433
|
port = 8765
|
427
434
|
if sysargs is None:
|
428
435
|
sysargs = ['start', 'webterm']
|
436
|
+
session_id = 'mrsm'
|
429
437
|
tornado_app, term_manager = get_webterm_app_and_manager(instance_keys=mrsm_instance)
|
430
438
|
|
431
|
-
if is_webterm_running(host, port):
|
439
|
+
if is_webterm_running(host, port, session_id=session_id):
|
432
440
|
if force:
|
433
441
|
port = next(find_open_ports(port + 1, 9000))
|
434
442
|
else:
|
435
443
|
return False, (
|
436
|
-
f"The webterm is already running at http://{host}:{port}\n\n"
|
444
|
+
f"The webterm is already running at http://{host}:{port}/webterm/{session_id}\n\n"
|
437
445
|
+ " Include `-f` to start another server on a new port\n"
|
438
446
|
+ " or specify a different port with `-p`."
|
439
447
|
)
|
440
448
|
if not nopretty:
|
441
|
-
info(
|
449
|
+
info(
|
450
|
+
f"Starting the webterm at http://{host}:{port}/webterm/{session_id} ..."
|
451
|
+
"\n Press CTRL+C to quit."
|
452
|
+
)
|
442
453
|
tornado_app.listen(port, host)
|
443
454
|
loop = tornado_ioloop.IOLoop.instance()
|
444
455
|
try:
|
@@ -451,6 +462,10 @@ def _start_webterm(
|
|
451
462
|
term_manager.shutdown()
|
452
463
|
loop.close()
|
453
464
|
|
465
|
+
sessions = get_mrsm_tmux_sessions()
|
466
|
+
for session in sessions:
|
467
|
+
kill_tmux_session(session)
|
468
|
+
|
454
469
|
return True, "Success"
|
455
470
|
|
456
471
|
|
@@ -190,6 +190,7 @@ def update_page_layout_div(
|
|
190
190
|
Input('go-button', 'n_clicks'),
|
191
191
|
Input('get-pipes-button', 'n_clicks'),
|
192
192
|
Input('get-jobs-button', 'n_clicks'),
|
193
|
+
Input('show-webterm-button', 'n_clicks'),
|
193
194
|
Input('get-plugins-button', 'n_clicks'),
|
194
195
|
Input('get-users-button', 'n_clicks'),
|
195
196
|
Input('get-graphs-button', 'n_clicks'),
|
@@ -229,7 +230,7 @@ def update_content(*args):
|
|
229
230
|
### NOTE: functions MUST return a list of content and a list of alerts
|
230
231
|
triggers = {
|
231
232
|
'go-button': lambda x: ([], []),
|
232
|
-
'
|
233
|
+
'show-webterm-button': lambda x: ([], []),
|
233
234
|
'get-pipes-button': get_pipes_cards,
|
234
235
|
'get-jobs-button': get_jobs_cards,
|
235
236
|
'get-plugins-button': get_plugins_cards,
|
@@ -251,7 +252,12 @@ def update_content(*args):
|
|
251
252
|
webterm_style = {
|
252
253
|
'display': (
|
253
254
|
'none'
|
254
|
-
if trigger not in (
|
255
|
+
if trigger not in (
|
256
|
+
'instance-select',
|
257
|
+
'cancel-button',
|
258
|
+
'go-button',
|
259
|
+
'show-webterm-button',
|
260
|
+
)
|
255
261
|
else 'block'
|
256
262
|
)
|
257
263
|
}
|
@@ -650,6 +656,41 @@ dash_app.clientside_callback(
|
|
650
656
|
State('mrsm-location', 'href'),
|
651
657
|
)
|
652
658
|
|
659
|
+
dash_app.clientside_callback(
|
660
|
+
"""
|
661
|
+
function(n_clicks, url){
|
662
|
+
if (!n_clicks) { return dash_clientside.no_update; }
|
663
|
+
iframe = document.getElementById('webterm-iframe');
|
664
|
+
if (!iframe){ return dash_clientside.no_update; }
|
665
|
+
|
666
|
+
iframe.contentWindow.postMessage(
|
667
|
+
{
|
668
|
+
action: "__TMUX_NEW_WINDOW"
|
669
|
+
},
|
670
|
+
url
|
671
|
+
);
|
672
|
+
return dash_clientside.no_update;
|
673
|
+
}
|
674
|
+
""",
|
675
|
+
Output('mrsm-location', 'href'),
|
676
|
+
Input('webterm-new-tab-button', 'n_clicks'),
|
677
|
+
State('mrsm-location', 'href'),
|
678
|
+
)
|
679
|
+
|
680
|
+
dash_app.clientside_callback(
|
681
|
+
"""
|
682
|
+
function(n_clicks, url){
|
683
|
+
if (!n_clicks) { return dash_clientside.no_update; }
|
684
|
+
iframe = document.getElementById('webterm-iframe');
|
685
|
+
if (!iframe){ return dash_clientside.no_update; }
|
686
|
+
iframe.src = iframe.src;
|
687
|
+
return dash_clientside.no_update;
|
688
|
+
}
|
689
|
+
""",
|
690
|
+
Output('mrsm-location', 'href'),
|
691
|
+
Input('webterm-refresh-button', 'n_clicks'),
|
692
|
+
State('mrsm-location', 'href'),
|
693
|
+
)
|
653
694
|
|
654
695
|
@dash_app.callback(
|
655
696
|
Output(component_id='connector-keys-input', component_property='value'),
|
@@ -23,7 +23,7 @@ dex = attempt_import('dash_extensions', lazy=False, check_update=CHECK_UPDATE)
|
|
23
23
|
dash_ace = attempt_import('dash_ace', lazy=False, check_update=CHECK_UPDATE)
|
24
24
|
_load_builtin_custom_connectors()
|
25
25
|
|
26
|
-
go_button = dbc.Button('
|
26
|
+
go_button = dbc.Button('Exec', id='go-button', color='primary', style={'width': '100%'})
|
27
27
|
test_button = dbc.Button('Test', id='test-button', color='danger', style={'display': 'none'})
|
28
28
|
get_items_menu = dbc.DropdownMenu(
|
29
29
|
label='More', id='get-items-menu', children=[
|
@@ -50,19 +50,26 @@ show_jobs_button = dbc.Button(
|
|
50
50
|
style={'width': '100%'},
|
51
51
|
)
|
52
52
|
cancel_button = dbc.Button(
|
53
|
-
'
|
53
|
+
'Term',
|
54
54
|
id='cancel-button',
|
55
55
|
color='dark',
|
56
56
|
style={'width': '100%', 'background-color': 'black', 'display': 'none'},
|
57
57
|
)
|
58
|
+
show_webterm_button = dbc.Button(
|
59
|
+
'Term',
|
60
|
+
id='show-webterm-button',
|
61
|
+
color='dark',
|
62
|
+
style={'width': '100%', 'background-color': 'black'},
|
63
|
+
)
|
58
64
|
bottom_buttons_content = dbc.Card(
|
59
65
|
dbc.CardBody(
|
60
66
|
dbc.Row([
|
61
|
-
dbc.Col(go_button, lg=3, md=
|
62
|
-
dbc.Col(show_pipes_button, lg=3, md=
|
63
|
-
dbc.Col(show_jobs_button, lg=3, md=
|
67
|
+
dbc.Col(go_button, xl=2, lg=3, md=3, sm=12),
|
68
|
+
dbc.Col(show_pipes_button, xl=2, lg=3, md=3, sm=12),
|
69
|
+
dbc.Col(show_jobs_button, xl=2, lg=3, md=3, sm=12),
|
70
|
+
dbc.Col(show_webterm_button, xl=2, lg=3, md=3, sm=12),
|
64
71
|
dbc.Col(lg=True, md=False, sm=False),
|
65
|
-
dbc.Col(get_items_menu,
|
72
|
+
dbc.Col(get_items_menu, xl=2, lg=12, md=12, sm=12),
|
66
73
|
])
|
67
74
|
)
|
68
75
|
)
|