meerschaum 2.2.2rc2__py3-none-any.whl → 2.2.2rc4__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 (29) hide show
  1. meerschaum/_internal/shell/Shell.py +9 -3
  2. meerschaum/_internal/term/__init__.py +2 -1
  3. meerschaum/actions/api.py +65 -66
  4. meerschaum/actions/python.py +34 -18
  5. meerschaum/actions/uninstall.py +5 -9
  6. meerschaum/actions/upgrade.py +10 -2
  7. meerschaum/api/dash/callbacks/__init__.py +4 -0
  8. meerschaum/api/dash/callbacks/custom.py +39 -0
  9. meerschaum/api/dash/callbacks/dashboard.py +39 -6
  10. meerschaum/api/dash/callbacks/login.py +3 -1
  11. meerschaum/api/dash/pipes.py +145 -97
  12. meerschaum/config/_paths.py +1 -0
  13. meerschaum/config/_version.py +1 -1
  14. meerschaum/connectors/__init__.py +1 -1
  15. meerschaum/core/Pipe/__init__.py +5 -0
  16. meerschaum/plugins/__init__.py +67 -9
  17. meerschaum/utils/daemon/Daemon.py +7 -2
  18. meerschaum/utils/misc.py +6 -0
  19. meerschaum/utils/packages/__init__.py +105 -21
  20. meerschaum/utils/process.py +12 -1
  21. meerschaum/utils/venv/__init__.py +25 -1
  22. {meerschaum-2.2.2rc2.dist-info → meerschaum-2.2.2rc4.dist-info}/METADATA +1 -1
  23. {meerschaum-2.2.2rc2.dist-info → meerschaum-2.2.2rc4.dist-info}/RECORD +29 -28
  24. {meerschaum-2.2.2rc2.dist-info → meerschaum-2.2.2rc4.dist-info}/WHEEL +1 -1
  25. {meerschaum-2.2.2rc2.dist-info → meerschaum-2.2.2rc4.dist-info}/LICENSE +0 -0
  26. {meerschaum-2.2.2rc2.dist-info → meerschaum-2.2.2rc4.dist-info}/NOTICE +0 -0
  27. {meerschaum-2.2.2rc2.dist-info → meerschaum-2.2.2rc4.dist-info}/entry_points.txt +0 -0
  28. {meerschaum-2.2.2rc2.dist-info → meerschaum-2.2.2rc4.dist-info}/top_level.txt +0 -0
  29. {meerschaum-2.2.2rc2.dist-info → meerschaum-2.2.2rc4.dist-info}/zip-safe +0 -0
@@ -867,11 +867,17 @@ def input_with_sigint(_input, session, shell: Optional[Shell] = None):
867
867
  shell_attrs['instance_keys'], 'on ' + get_config(
868
868
  'shell', 'ansi', 'instance', 'rich', 'style'
869
869
  )
870
- ) if ANSI else colored(shell_attrs['instance_keys'], 'on white')
870
+ )
871
+ if ANSI
872
+ else colored(shell_attrs['instance_keys'], 'on white')
871
873
  )
872
874
  repo_colored = (
873
- colored(shell_attrs['repo_keys'], 'on ' + get_config('shell', 'ansi', 'repo', 'rich', 'style'))
874
- if ANSI else colored(shell_attrs['repo_keys'], 'on white')
875
+ colored(
876
+ shell_attrs['repo_keys'],
877
+ 'on ' + get_config('shell', 'ansi', 'repo', 'rich', 'style')
878
+ )
879
+ if ANSI
880
+ else colored(shell_attrs['repo_keys'], 'on white')
875
881
  )
876
882
  try:
877
883
  typ, label = shell_attrs['instance_keys'].split(':')
@@ -14,6 +14,7 @@ from typing import List, Tuple
14
14
  from meerschaum.utils.packages import attempt_import
15
15
  from meerschaum._internal.term.TermPageHandler import TermPageHandler
16
16
  from meerschaum.config._paths import API_TEMPLATES_PATH, API_STATIC_PATH
17
+ from meerschaum.utils.venv import venv_executable
17
18
 
18
19
  tornado, tornado_ioloop, terminado = attempt_import(
19
20
  'tornado', 'tornado.ioloop', 'terminado', lazy=False,
@@ -31,7 +32,7 @@ def get_webterm_app_and_manager() -> Tuple[
31
32
  A tuple of the Tornado web application and term manager.
32
33
  """
33
34
  commands = [
34
- sys.executable,
35
+ venv_executable('mrsm'),
35
36
  '-c',
36
37
  "import os; _ = os.environ.pop('COLUMNS', None); _ = os.environ.pop('LINES', None); "
37
38
  "from meerschaum._internal.entry import get_shell; "
meerschaum/actions/api.py CHANGED
@@ -156,6 +156,7 @@ def _api_start(
156
156
  from meerschaum.config.static import STATIC_CONFIG, SERVER_ID
157
157
  from meerschaum.connectors.parse import parse_instance_keys
158
158
  from meerschaum.utils.pool import get_pool
159
+ from meerschaum.utils.venv import get_module_venv
159
160
  import shutil
160
161
  from copy import deepcopy
161
162
 
@@ -169,7 +170,10 @@ def _api_start(
169
170
  ### `check_update` must be False, because otherwise Uvicorn's hidden imports will break things.
170
171
  dotenv = attempt_import('dotenv', lazy=False)
171
172
  uvicorn, gunicorn = attempt_import(
172
- 'uvicorn', 'gunicorn', lazy=False, check_update=False,
173
+ 'uvicorn', 'gunicorn',
174
+ lazy = False,
175
+ check_update = False,
176
+ venv = 'mrsm',
173
177
  )
174
178
 
175
179
  uvicorn_config_path = API_UVICORN_RESOURCES_PATH / SERVER_ID / 'config.json'
@@ -305,54 +309,51 @@ def _api_start(
305
309
  ### remove custom keys before calling uvicorn
306
310
 
307
311
  def _run_uvicorn():
308
- try:
309
- uvicorn_flags = [
310
- '--host', host,
311
- '--port', str(port),
312
- (
313
- '--proxy-headers'
314
- if uvicorn_config.get('proxy_headers')
315
- else '--no-proxy-headers'
316
- ),
317
- (
318
- '--use-colors'
319
- if uvicorn_config.get('use_colors')
320
- else '--no-use-colors'
321
- ),
322
- '--env-file', uvicorn_config['env_file'],
323
- ]
324
- if uvicorn_reload := uvicorn_config.get('reload'):
325
- uvicorn_flags.append('--reload')
326
- if (
327
- uvicorn_reload
328
- and (reload_dirs := uvicorn_config.get('reload_dirs'))
329
- ):
330
- if not isinstance(reload_dirs, list):
331
- reload_dirs = [reload_dirs]
332
- for reload_dir in reload_dirs:
333
- uvicorn_flags += ['--reload-dir', reload_dir]
334
- if (
335
- uvicorn_reload
336
- and (reload_excludes := uvicorn_config.get('reload_excludes'))
337
- ):
338
- if not isinstance(reload_excludes, list):
339
- reload_excludes = [reload_excludes]
340
- for reload_exclude in reload_excludes:
341
- uvicorn_flags += ['--reload-exclude', reload_exclude]
342
- if (uvicorn_workers := uvicorn_config.get('workers')) is not None:
343
- uvicorn_flags += ['--workers', str(uvicorn_workers)]
344
-
345
- uvicorn_args = uvicorn_flags + ['meerschaum.api:app']
346
- proc = run_python_package(
347
- 'uvicorn',
348
- uvicorn_args,
349
- venv = 'mrsm',
350
- as_proc = True,
351
- foreground = True,
352
- debug = debug,
353
- )
354
- except KeyboardInterrupt:
355
- pass
312
+ uvicorn_flags = [
313
+ '--host', host,
314
+ '--port', str(port),
315
+ (
316
+ '--proxy-headers'
317
+ if uvicorn_config.get('proxy_headers')
318
+ else '--no-proxy-headers'
319
+ ),
320
+ (
321
+ '--use-colors'
322
+ if uvicorn_config.get('use_colors')
323
+ else '--no-use-colors'
324
+ ),
325
+ '--env-file', uvicorn_config['env_file'],
326
+ ]
327
+ if uvicorn_reload := uvicorn_config.get('reload'):
328
+ uvicorn_flags.append('--reload')
329
+ if (
330
+ uvicorn_reload
331
+ and (reload_dirs := uvicorn_config.get('reload_dirs'))
332
+ ):
333
+ if not isinstance(reload_dirs, list):
334
+ reload_dirs = [reload_dirs]
335
+ for reload_dir in reload_dirs:
336
+ uvicorn_flags += ['--reload-dir', reload_dir]
337
+ if (
338
+ uvicorn_reload
339
+ and (reload_excludes := uvicorn_config.get('reload_excludes'))
340
+ ):
341
+ if not isinstance(reload_excludes, list):
342
+ reload_excludes = [reload_excludes]
343
+ for reload_exclude in reload_excludes:
344
+ uvicorn_flags += ['--reload-exclude', reload_exclude]
345
+ if (uvicorn_workers := uvicorn_config.get('workers')) is not None:
346
+ uvicorn_flags += ['--workers', str(uvicorn_workers)]
347
+
348
+ uvicorn_args = uvicorn_flags + ['meerschaum.api:app']
349
+ run_python_package(
350
+ 'uvicorn',
351
+ uvicorn_args,
352
+ venv = get_module_venv(uvicorn),
353
+ as_proc = False,
354
+ foreground = True,
355
+ debug = debug,
356
+ )
356
357
 
357
358
  def _run_gunicorn():
358
359
  gunicorn_args = [
@@ -373,23 +374,21 @@ def _api_start(
373
374
  ]
374
375
  if debug:
375
376
  gunicorn_args += ['--log-level=debug', '--enable-stdio-inheritance', '--reload']
376
- try:
377
- run_python_package(
378
- 'gunicorn',
379
- gunicorn_args,
380
- env = {
381
- k: (
382
- json.dumps(v)
383
- if isinstance(v, (dict, list))
384
- else v
385
- )
386
- for k, v in env_dict.items()
387
- },
388
- venv = 'mrsm',
389
- debug = debug,
390
- )
391
- except KeyboardInterrupt:
392
- pass
377
+
378
+ run_python_package(
379
+ 'gunicorn',
380
+ gunicorn_args,
381
+ env = {
382
+ k: (
383
+ json.dumps(v)
384
+ if isinstance(v, (dict, list))
385
+ else v
386
+ )
387
+ for k, v in env_dict.items()
388
+ },
389
+ venv = get_module_venv(gunicorn),
390
+ debug = debug,
391
+ )
393
392
 
394
393
 
395
394
  _run_uvicorn() if not production else _run_gunicorn()
@@ -30,10 +30,11 @@ def python(
30
30
  import sys, subprocess, os
31
31
  from meerschaum.utils.debug import dprint
32
32
  from meerschaum.utils.warnings import warn, error
33
- from meerschaum.utils.process import run_process
34
33
  from meerschaum.utils.venv import venv_executable
34
+ from meerschaum.utils.misc import generate_password
35
35
  from meerschaum.config import __version__ as _version
36
- from meerschaum.config.paths import VIRTENV_RESOURCES_PATH
36
+ from meerschaum.config.paths import VIRTENV_RESOURCES_PATH, PYTHON_RESOURCES_PATH
37
+ from meerschaum.utils.packages import run_python_package, attempt_import
37
38
 
38
39
  if action is None:
39
40
  action = []
@@ -41,7 +42,7 @@ def python(
41
42
  if venv == 'None':
42
43
  venv = None
43
44
 
44
- joined_actions = ['import meerschaum as mrsm']
45
+ joined_actions = ["import meerschaum as mrsm"]
45
46
  line = ""
46
47
  for i, a in enumerate(action):
47
48
  if a == '':
@@ -53,48 +54,63 @@ def python(
53
54
 
54
55
  ### ensure meerschaum is imported
55
56
  if debug:
56
- dprint(joined_actions)
57
+ dprint(str(joined_actions))
57
58
 
58
- print_command = 'import sys; print("""'
59
- ps1 = ">>> "
59
+ ### TODO: format the pre-executed code using the pygments lexer.
60
+ print_command = (
61
+ 'from meerschaum.utils.packages import attempt_import; '
62
+ + 'ptft = attempt_import("prompt_toolkit.formatted_text", lazy=False); '
63
+ + 'pts = attempt_import("prompt_toolkit.shortcuts"); '
64
+ + 'ansi = ptft.ANSI("""'
65
+ )
66
+ ps1 = "\\033[1m>>> \\033[0m"
60
67
  for i, a in enumerate(joined_actions):
61
68
  line = ps1 + f"{a}".replace(';', '\n')
62
69
  if '\n' not in line and i != len(joined_actions) - 1:
63
70
  line += "\n"
64
71
  print_command += line
65
- print_command += '""")'
72
+ print_command += (
73
+ '"""); '
74
+ + 'pts.print_formatted_text(ansi); '
75
+ )
66
76
 
67
- command = ""
77
+ command = print_command
68
78
  for a in joined_actions:
69
79
  command += a
70
80
  if not a.endswith(';'):
71
81
  command += ';'
72
82
  command += ' '
73
83
 
74
- command += print_command
75
-
76
84
  if debug:
77
85
  dprint(f"command:\n{command}")
78
86
 
87
+ init_script_path = PYTHON_RESOURCES_PATH / (generate_password(8) + '.py')
88
+ with open(init_script_path, 'w', encoding='utf-8') as f:
89
+ f.write(command)
90
+
79
91
  env_dict = os.environ.copy()
80
92
  venv_path = (VIRTENV_RESOURCES_PATH / venv) if venv is not None else None
81
93
  if venv_path is not None:
82
94
  env_dict.update({'VIRTUAL_ENV': venv_path.as_posix()})
83
95
 
84
- args_to_run = (
85
- [venv_executable(venv), '-i', '-c', command]
86
- if not sub_args
87
- else [venv_executable(venv)] + sub_args
88
- )
89
-
90
96
  try:
91
- return_code = run_process(
92
- args_to_run,
97
+ ptpython = attempt_import('ptpython', venv=venv, allow_outside_venv=False)
98
+ return_code = run_python_package(
99
+ 'ptpython',
100
+ sub_args or ['--dark-bg', '-i', init_script_path.as_posix()],
101
+ venv = venv,
93
102
  foreground = True,
94
103
  env = env_dict,
95
104
  )
96
105
  except KeyboardInterrupt:
97
106
  return_code = 1
107
+
108
+ try:
109
+ if init_script_path.exists():
110
+ init_script_path.unlink()
111
+ except Exception as e:
112
+ warn(f"Failed to clean up tempory file '{init_script_path}'.")
113
+
98
114
  return return_code == 0, (
99
115
  "Success" if return_code == 0
100
116
  else f"Python interpreter returned {return_code}."
@@ -7,7 +7,7 @@ Uninstall plugins and pip packages.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
- from meerschaum.utils.typing import List, Any, SuccessTuple, Optional
10
+ from meerschaum.utils.typing import List, Any, SuccessTuple, Optional, Union
11
11
 
12
12
  def uninstall(
13
13
  action: Optional[List[str]] = None,
@@ -145,13 +145,10 @@ def _complete_uninstall_plugins(action: Optional[List[str]] = None, **kw) -> Lis
145
145
  possibilities.append(name)
146
146
  return possibilities
147
147
 
148
- class NoVenv:
149
- pass
150
-
151
148
  def _uninstall_packages(
152
149
  action: Optional[List[str]] = None,
153
150
  sub_args: Optional[List[str]] = None,
154
- venv: Union[str, None, NoVenv] = NoVenv,
151
+ venv: Optional[str] = 'mrsm',
155
152
  yes: bool = False,
156
153
  force: bool = False,
157
154
  noask: bool = False,
@@ -169,9 +166,7 @@ def _uninstall_packages(
169
166
 
170
167
  from meerschaum.utils.warnings import info
171
168
  from meerschaum.utils.packages import pip_uninstall
172
-
173
- if venv is NoVenv:
174
- venv = 'mrsm'
169
+ from meerschaum.utils.misc import items_str
175
170
 
176
171
  if not (yes or force) and noask:
177
172
  return False, "Skipping uninstallation. Add `-y` or `-f` to agree to the uninstall prompt."
@@ -183,7 +178,8 @@ def _uninstall_packages(
183
178
  debug = debug,
184
179
  ):
185
180
  return True, (
186
- f"Successfully removed packages from virtual environment 'mrsm':\n" + ', '.join(action)
181
+ f"Successfully removed packages from virtual environment '{venv}':\n"
182
+ + items_str(action)
187
183
  )
188
184
 
189
185
  return False, f"Failed to uninstall packages:\n" + ', '.join(action)
@@ -130,7 +130,7 @@ def _upgrade_packages(
130
130
  upgrade packages docs
131
131
  ```
132
132
  """
133
- from meerschaum.utils.packages import packages, pip_install
133
+ from meerschaum.utils.packages import packages, pip_install, get_prerelease_dependencies
134
134
  from meerschaum.utils.warnings import info, warn
135
135
  from meerschaum.utils.prompt import yes_no
136
136
  from meerschaum.utils.formatting import make_header, pprint
@@ -148,7 +148,7 @@ def _upgrade_packages(
148
148
  invalid_msg = f"Invalid dependency group '{group}'."
149
149
  avail_msg = make_header("Available groups:")
150
150
  for k in packages:
151
- avail_msg += "\n - {k}"
151
+ avail_msg += f"\n - {k}"
152
152
 
153
153
  warn(invalid_msg + "\n\n" + avail_msg, stack=False)
154
154
  return False, invalid_msg
@@ -160,10 +160,18 @@ def _upgrade_packages(
160
160
  f"(dependency group '{group}')?"
161
161
  )
162
162
  to_install = [install_name for import_name, install_name in packages[group].items()]
163
+ prereleases_to_install = get_prerelease_dependencies(to_install)
164
+ to_install = [
165
+ install_name
166
+ for install_name in to_install
167
+ if install_name not in prereleases_to_install
168
+ ]
163
169
 
164
170
  success, msg = False, f"Nothing installed."
165
171
  if force or yes_no(question, noask=noask, yes=yes):
166
172
  success = pip_install(*to_install, debug=debug)
173
+ if success and prereleases_to_install:
174
+ success = pip_install(*prereleases_to_install, debug=debug)
167
175
  msg = (
168
176
  f"Successfully installed {len(packages[group])} packages." if success
169
177
  else f"Failed to install packages in dependency group '{group}'."
@@ -11,3 +11,7 @@ import meerschaum.api.dash.callbacks.login
11
11
  import meerschaum.api.dash.callbacks.plugins
12
12
  import meerschaum.api.dash.callbacks.jobs
13
13
  import meerschaum.api.dash.callbacks.register
14
+ from meerschaum.api.dash.callbacks.custom import init_dash_plugins, add_plugin_pages
15
+
16
+ init_dash_plugins()
17
+ add_plugin_pages()
@@ -0,0 +1,39 @@
1
+ #! /usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # vim:fenc=utf-8
4
+
5
+ """
6
+ Import custom callbacks created by plugins.
7
+ """
8
+
9
+ import traceback
10
+ from meerschaum.api.dash import dash_app
11
+ from meerschaum.plugins import _dash_plugins, _plugin_endpoints_to_pages
12
+ from meerschaum.utils.warnings import warn
13
+ from meerschaum.api.dash.callbacks.dashboard import _paths, _required_login
14
+
15
+
16
+ def init_dash_plugins():
17
+ """
18
+ Fire the initial callbacks for Dash plugins.
19
+ """
20
+ for _module_name, _functions in _dash_plugins.items():
21
+ for _function in _functions:
22
+ try:
23
+ _function(dash_app)
24
+ except Exception as e:
25
+ warn(
26
+ f"Failed to load function '{_function.__name__}' "
27
+ + f"from plugin '{_module_name}':\n"
28
+ + traceback.format_exc()
29
+ )
30
+
31
+
32
+ def add_plugin_pages():
33
+ """
34
+ Allow users to add pages via the `@web_page` decorator.
35
+ """
36
+ for _endpoint, _page_dict in _plugin_endpoints_to_pages.items():
37
+ _paths[_endpoint] = _page_dict['function']()
38
+ if _page_dict['login_required']:
39
+ _required_login.add(_endpoint)
@@ -94,6 +94,8 @@ omit_actions = {
94
94
  'repo',
95
95
  'instance',
96
96
  }
97
+
98
+ ### Map endpoints to page layouts.
97
99
  _paths = {
98
100
  'login' : pages.login.layout,
99
101
  '' : pages.dashboard.layout,
@@ -101,7 +103,8 @@ _paths = {
101
103
  'register': pages.register.layout,
102
104
  }
103
105
  _required_login = {''}
104
-
106
+
107
+
105
108
  @dash_app.callback(
106
109
  Output('page-layout-div', 'children'),
107
110
  Output('session-store', 'data'),
@@ -147,16 +150,31 @@ def update_page_layout_div(
147
150
  else:
148
151
  session_store_to_return = dash.no_update
149
152
 
150
- _path = (
153
+ base_path = (
151
154
  pathname.rstrip('/') + '/'
152
155
  ).replace(
153
156
  (dash_endpoint + '/'),
154
157
  ''
155
158
  ).rstrip('/').split('/')[0]
159
+
160
+ complete_path = (
161
+ pathname.rstrip('/') + '/'
162
+ ).replace(
163
+ dash_endpoint + '/',
164
+ ''
165
+ ).rstrip('/')
166
+
167
+ if complete_path in _paths:
168
+ path_str = complete_path
169
+ elif base_path in _paths:
170
+ path_str = base_path
171
+ else:
172
+ path_str = ''
173
+
156
174
  path = (
157
- _path
158
- if no_auth or _path not in _required_login else (
159
- _path
175
+ path_str
176
+ if no_auth or path_str not in _required_login else (
177
+ path_str
160
178
  if session_id in active_sessions
161
179
  else 'login'
162
180
  )
@@ -868,10 +886,25 @@ dash_app.clientside_callback(
868
886
  location = "None";
869
887
  }
870
888
 
889
+ var subaction = "pipes";
890
+ if (action == "python"){
891
+ subaction = (
892
+ '"' + "pipe = mrsm.Pipe('"
893
+ + pipe_meta.connector
894
+ + "', '"
895
+ + pipe_meta.metric
896
+ + "'"
897
+ );
898
+ if (location != "None"){
899
+ subaction += ", '" + location + "'";
900
+ }
901
+ subaction += ", instance='" + pipe_meta.instance + "')" + '"';
902
+ }
903
+
871
904
  iframe.contentWindow.postMessage(
872
905
  {
873
906
  action: action,
874
- subaction: "pipes",
907
+ subaction: subaction,
875
908
  connector_keys: [pipe_meta.connector],
876
909
  metric_keys: [pipe_meta.metric],
877
910
  location_keys: [location],
@@ -45,6 +45,7 @@ def show_registration_disabled_collapse(n_clicks, is_open):
45
45
  State('username-input', 'value'),
46
46
  State('password-input', 'value'),
47
47
  State('location', 'href'),
48
+ State('location', 'pathname'),
48
49
  )
49
50
  def login_button_click(
50
51
  username_submit,
@@ -53,6 +54,7 @@ def login_button_click(
53
54
  username,
54
55
  password,
55
56
  location_href,
57
+ location_pathname,
56
58
  ):
57
59
  """
58
60
  When the user submits the login form, check the login.
@@ -69,4 +71,4 @@ def login_button_click(
69
71
  except HTTPException:
70
72
  form_class += ' is-invalid'
71
73
  session_data = None
72
- return session_data, form_class, (dash.no_update if not session_data else endpoints['dash'])
74
+ return session_data, form_class, dash.no_update