meerschaum 2.2.1__py3-none-any.whl → 2.2.2__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/shell/Shell.py +40 -16
- meerschaum/_internal/term/__init__.py +3 -2
- meerschaum/_internal/term/tools.py +1 -1
- meerschaum/actions/api.py +65 -31
- meerschaum/actions/python.py +56 -24
- meerschaum/actions/start.py +2 -4
- meerschaum/actions/uninstall.py +5 -9
- meerschaum/actions/upgrade.py +11 -3
- meerschaum/api/__init__.py +1 -0
- meerschaum/api/dash/callbacks/__init__.py +4 -0
- meerschaum/api/dash/callbacks/custom.py +39 -0
- meerschaum/api/dash/callbacks/dashboard.py +39 -6
- meerschaum/api/dash/callbacks/login.py +3 -1
- meerschaum/api/dash/components.py +5 -2
- meerschaum/api/dash/pipes.py +145 -97
- meerschaum/config/_default.py +1 -0
- meerschaum/config/_paths.py +12 -12
- meerschaum/config/_version.py +1 -1
- meerschaum/config/paths.py +10 -0
- meerschaum/config/static/__init__.py +1 -1
- meerschaum/connectors/__init__.py +1 -1
- meerschaum/core/Pipe/__init__.py +5 -0
- meerschaum/core/Pipe/_sync.py +2 -3
- meerschaum/plugins/__init__.py +67 -9
- meerschaum/utils/daemon/Daemon.py +7 -2
- meerschaum/utils/misc.py +6 -0
- meerschaum/utils/packages/__init__.py +212 -53
- meerschaum/utils/packages/_packages.py +1 -0
- meerschaum/utils/process.py +12 -2
- meerschaum/utils/schedule.py +1 -1
- meerschaum/utils/venv/__init__.py +46 -11
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/METADATA +5 -1
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/RECORD +39 -37
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/WHEEL +1 -1
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/LICENSE +0 -0
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/NOTICE +0 -0
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/top_level.txt +0 -0
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/zip-safe +0 -0
@@ -12,7 +12,7 @@ from meerschaum.utils.packages import attempt_import, import_dcc, import_html
|
|
12
12
|
from meerschaum.utils.typing import SuccessTuple, List
|
13
13
|
from meerschaum.config.static import STATIC_CONFIG
|
14
14
|
from meerschaum.utils.misc import remove_ansi
|
15
|
-
from meerschaum.
|
15
|
+
from meerschaum._internal.shell.Shell import get_shell_intro
|
16
16
|
from meerschaum.api import endpoints, CHECK_UPDATE
|
17
17
|
from meerschaum.connectors import instance_types
|
18
18
|
from meerschaum.utils.misc import get_connector_labels
|
@@ -69,7 +69,10 @@ bottom_buttons_content = dbc.Card(
|
|
69
69
|
])
|
70
70
|
)
|
71
71
|
)
|
72
|
-
console_div = html.Div(
|
72
|
+
console_div = html.Div(
|
73
|
+
id = 'console-div',
|
74
|
+
children = [html.Pre(get_shell_intro(), id='console-pre')],
|
75
|
+
)
|
73
76
|
|
74
77
|
location = dcc.Location(id='location', refresh=False)
|
75
78
|
|
meerschaum/api/dash/pipes.py
CHANGED
@@ -22,7 +22,7 @@ from meerschaum.api.dash import (
|
|
22
22
|
dash_app, debug, _get_pipes
|
23
23
|
)
|
24
24
|
from meerschaum.api.dash.connectors import get_web_connector
|
25
|
-
from meerschaum.api.dash.components import alert_from_success_tuple
|
25
|
+
from meerschaum.api.dash.components import alert_from_success_tuple, build_cards_grid
|
26
26
|
from meerschaum.api.dash.users import is_session_authenticated
|
27
27
|
from meerschaum.config import get_config
|
28
28
|
import meerschaum as mrsm
|
@@ -101,6 +101,132 @@ def pipes_from_state(
|
|
101
101
|
return False, str(e)
|
102
102
|
return _pipes
|
103
103
|
|
104
|
+
|
105
|
+
def build_pipe_card(
|
106
|
+
pipe: mrsm.Pipe,
|
107
|
+
authenticated: bool = False,
|
108
|
+
_build_children_num: int = 10,
|
109
|
+
) -> 'dbc.Card':
|
110
|
+
"""
|
111
|
+
Return a card for the given pipe.
|
112
|
+
|
113
|
+
Parameters
|
114
|
+
----------
|
115
|
+
pipe: mrsm.Pipe
|
116
|
+
The pipe from which to build the card.
|
117
|
+
|
118
|
+
authenticated: bool, default False
|
119
|
+
If `True`, allow editing functionality to the card.
|
120
|
+
|
121
|
+
Returns
|
122
|
+
-------
|
123
|
+
A dash bootstrap components Card representation of the pipe.
|
124
|
+
"""
|
125
|
+
meta_str = json.dumps(pipe.meta)
|
126
|
+
footer_children = dbc.Row(
|
127
|
+
[
|
128
|
+
dbc.Col(
|
129
|
+
(
|
130
|
+
dbc.DropdownMenu(
|
131
|
+
label = "Manage",
|
132
|
+
children = [
|
133
|
+
dbc.DropdownMenuItem(
|
134
|
+
'Open in Python',
|
135
|
+
id = {
|
136
|
+
'type': 'manage-pipe-button',
|
137
|
+
'index': meta_str,
|
138
|
+
'action': 'python',
|
139
|
+
},
|
140
|
+
),
|
141
|
+
dbc.DropdownMenuItem(
|
142
|
+
'Delete',
|
143
|
+
id = {
|
144
|
+
'type': 'manage-pipe-button',
|
145
|
+
'index': meta_str,
|
146
|
+
'action': 'delete',
|
147
|
+
},
|
148
|
+
),
|
149
|
+
dbc.DropdownMenuItem(
|
150
|
+
'Drop',
|
151
|
+
id = {
|
152
|
+
'type': 'manage-pipe-button',
|
153
|
+
'index': meta_str,
|
154
|
+
'action': 'drop',
|
155
|
+
},
|
156
|
+
),
|
157
|
+
dbc.DropdownMenuItem(
|
158
|
+
'Clear',
|
159
|
+
id = {
|
160
|
+
'type': 'manage-pipe-button',
|
161
|
+
'index': meta_str,
|
162
|
+
'action': 'clear',
|
163
|
+
},
|
164
|
+
),
|
165
|
+
dbc.DropdownMenuItem(
|
166
|
+
'Verify',
|
167
|
+
id = {
|
168
|
+
'type': 'manage-pipe-button',
|
169
|
+
'index': meta_str,
|
170
|
+
'action': 'verify',
|
171
|
+
},
|
172
|
+
),
|
173
|
+
dbc.DropdownMenuItem(
|
174
|
+
'Sync',
|
175
|
+
id = {
|
176
|
+
'type': 'manage-pipe-button',
|
177
|
+
'index': meta_str,
|
178
|
+
'action': 'sync',
|
179
|
+
},
|
180
|
+
),
|
181
|
+
],
|
182
|
+
direction = "up",
|
183
|
+
menu_variant = "dark",
|
184
|
+
size = 'sm',
|
185
|
+
color = 'secondary',
|
186
|
+
)
|
187
|
+
) if authenticated else [],
|
188
|
+
width = 2,
|
189
|
+
),
|
190
|
+
dbc.Col(width=6),
|
191
|
+
dbc.Col(
|
192
|
+
dbc.Button(
|
193
|
+
'Download CSV',
|
194
|
+
size = 'sm',
|
195
|
+
color = 'link',
|
196
|
+
style = {'float': 'right'},
|
197
|
+
id = {'type': 'pipe-download-csv-button', 'index': meta_str},
|
198
|
+
),
|
199
|
+
width = 4,
|
200
|
+
),
|
201
|
+
],
|
202
|
+
justify = 'start',
|
203
|
+
)
|
204
|
+
card_body_children = [
|
205
|
+
html.H5(
|
206
|
+
html.B(str(pipe)),
|
207
|
+
className = 'card-title',
|
208
|
+
style = {'font-family': ['monospace']}
|
209
|
+
),
|
210
|
+
html.Div(
|
211
|
+
dbc.Accordion(
|
212
|
+
accordion_items_from_pipe(
|
213
|
+
pipe,
|
214
|
+
authenticated = authenticated,
|
215
|
+
_build_children_num = _build_children_num,
|
216
|
+
),
|
217
|
+
flush = True,
|
218
|
+
start_collapsed = True,
|
219
|
+
id = {'type': 'pipe-accordion', 'index': meta_str},
|
220
|
+
)
|
221
|
+
)
|
222
|
+
|
223
|
+
]
|
224
|
+
return dbc.Card([
|
225
|
+
dbc.CardBody(children=card_body_children),
|
226
|
+
dbc.CardFooter(children=footer_children),
|
227
|
+
])
|
228
|
+
|
229
|
+
|
104
230
|
def get_pipes_cards(*keys, session_data: Optional[Dict[str, Any]] = None):
|
105
231
|
"""
|
106
232
|
Returns a tuple:
|
@@ -120,99 +246,7 @@ def get_pipes_cards(*keys, session_data: Optional[Dict[str, Any]] = None):
|
|
120
246
|
overflow_pipes = pipes[max_num_pipes_cards:]
|
121
247
|
|
122
248
|
for pipe in pipes[:max_num_pipes_cards]:
|
123
|
-
|
124
|
-
footer_children = dbc.Row(
|
125
|
-
[
|
126
|
-
dbc.Col(
|
127
|
-
(
|
128
|
-
dbc.DropdownMenu(
|
129
|
-
label = "Manage",
|
130
|
-
children = [
|
131
|
-
dbc.DropdownMenuItem(
|
132
|
-
'Delete',
|
133
|
-
id = {
|
134
|
-
'type': 'manage-pipe-button',
|
135
|
-
'index': meta_str,
|
136
|
-
'action': 'delete',
|
137
|
-
},
|
138
|
-
),
|
139
|
-
dbc.DropdownMenuItem(
|
140
|
-
'Drop',
|
141
|
-
id = {
|
142
|
-
'type': 'manage-pipe-button',
|
143
|
-
'index': meta_str,
|
144
|
-
'action': 'drop',
|
145
|
-
},
|
146
|
-
),
|
147
|
-
dbc.DropdownMenuItem(
|
148
|
-
'Clear',
|
149
|
-
id = {
|
150
|
-
'type': 'manage-pipe-button',
|
151
|
-
'index': meta_str,
|
152
|
-
'action': 'clear',
|
153
|
-
},
|
154
|
-
),
|
155
|
-
dbc.DropdownMenuItem(
|
156
|
-
'Verify',
|
157
|
-
id = {
|
158
|
-
'type': 'manage-pipe-button',
|
159
|
-
'index': meta_str,
|
160
|
-
'action': 'verify',
|
161
|
-
},
|
162
|
-
),
|
163
|
-
dbc.DropdownMenuItem(
|
164
|
-
'Sync',
|
165
|
-
id = {
|
166
|
-
'type': 'manage-pipe-button',
|
167
|
-
'index': meta_str,
|
168
|
-
'action': 'sync',
|
169
|
-
},
|
170
|
-
),
|
171
|
-
],
|
172
|
-
direction = "up",
|
173
|
-
menu_variant = "dark",
|
174
|
-
size = 'sm',
|
175
|
-
color = 'secondary',
|
176
|
-
)
|
177
|
-
) if authenticated else [],
|
178
|
-
width = 2,
|
179
|
-
),
|
180
|
-
dbc.Col(width=6),
|
181
|
-
dbc.Col(
|
182
|
-
dbc.Button(
|
183
|
-
'Download CSV',
|
184
|
-
size = 'sm',
|
185
|
-
color = 'link',
|
186
|
-
style = {'float': 'right'},
|
187
|
-
id = {'type': 'pipe-download-csv-button', 'index': meta_str},
|
188
|
-
),
|
189
|
-
width = 4,
|
190
|
-
),
|
191
|
-
],
|
192
|
-
justify = 'start',
|
193
|
-
)
|
194
|
-
card_body_children = [
|
195
|
-
html.H5(
|
196
|
-
html.B(str(pipe)),
|
197
|
-
className = 'card-title',
|
198
|
-
style = {'font-family': ['monospace']}
|
199
|
-
),
|
200
|
-
html.Div(
|
201
|
-
dbc.Accordion(
|
202
|
-
accordion_items_from_pipe(pipe, authenticated=authenticated),
|
203
|
-
flush = True,
|
204
|
-
start_collapsed = True,
|
205
|
-
id = {'type': 'pipe-accordion', 'index': meta_str},
|
206
|
-
)
|
207
|
-
)
|
208
|
-
|
209
|
-
]
|
210
|
-
cards.append(
|
211
|
-
dbc.Card([
|
212
|
-
dbc.CardBody(children=card_body_children),
|
213
|
-
dbc.CardFooter(children=footer_children),
|
214
|
-
])
|
215
|
-
)
|
249
|
+
cards.append(build_pipe_card(pipe, authenticated=authenticated))
|
216
250
|
|
217
251
|
if overflow_pipes:
|
218
252
|
cards.append(
|
@@ -239,7 +273,8 @@ def accordion_items_from_pipe(
|
|
239
273
|
pipe: mrsm.Pipe,
|
240
274
|
active_items: Optional[List[str]] = None,
|
241
275
|
authenticated: bool = False,
|
242
|
-
|
276
|
+
_build_children_num: int = 10,
|
277
|
+
) -> 'List[dbc.AccordionItem]':
|
243
278
|
"""
|
244
279
|
Build the accordion items for a given pipe.
|
245
280
|
"""
|
@@ -401,7 +436,7 @@ def accordion_items_from_pipe(
|
|
401
436
|
size = 'sm',
|
402
437
|
style = {'text-decoration': 'none', 'margin-left': '10px'},
|
403
438
|
)
|
404
|
-
|
439
|
+
parameters_div_children = [
|
405
440
|
parameters_editor,
|
406
441
|
html.Br(),
|
407
442
|
dbc.Row([
|
@@ -422,8 +457,21 @@ def accordion_items_from_pipe(
|
|
422
457
|
width=True,
|
423
458
|
)
|
424
459
|
]),
|
460
|
+
]
|
461
|
+
if _build_children_num > 0 and pipe.children:
|
462
|
+
children_cards = [
|
463
|
+
build_pipe_card(
|
464
|
+
child_pipe,
|
465
|
+
authenticated = authenticated,
|
466
|
+
_build_children_num = (_build_children_num - 1),
|
467
|
+
)
|
468
|
+
for child_pipe in pipe.children
|
469
|
+
]
|
470
|
+
children_grid = build_cards_grid(children_cards, num_columns=1)
|
471
|
+
chidren_div_items = [html.Br(), html.H3('Children Pipes'), html.Br(), children_grid]
|
472
|
+
parameters_div_children.extend([html.Br()] + chidren_div_items)
|
425
473
|
|
426
|
-
])
|
474
|
+
items_bodies['parameters'] = html.Div(parameters_div_children)
|
427
475
|
|
428
476
|
if 'sql' in active_items:
|
429
477
|
query = dedent((get_pipe_query(pipe, warn=False) or "")).lstrip().rstrip()
|
meerschaum/config/_default.py
CHANGED
meerschaum/config/_paths.py
CHANGED
@@ -82,24 +82,24 @@ for _plugin_path in _plugins_paths_to_remove:
|
|
82
82
|
|
83
83
|
ENVIRONMENT_VENVS_DIR = STATIC_CONFIG['environment']['venvs']
|
84
84
|
if ENVIRONMENT_VENVS_DIR in os.environ:
|
85
|
-
|
86
|
-
if not
|
85
|
+
_VENVS_DIR_PATH = Path(os.environ[ENVIRONMENT_VENVS_DIR]).resolve()
|
86
|
+
if not _VENVS_DIR_PATH.exists():
|
87
87
|
try:
|
88
|
-
|
88
|
+
_VENVS_DIR_PATH.mkdir(parents=True, exist_ok=True)
|
89
89
|
except Exception as e:
|
90
90
|
print(
|
91
91
|
f"Invalid path set for environment variable '{ENVIRONMENT_VENVS_DIR}':\n"
|
92
|
-
+ f"{
|
92
|
+
+ f"{_VENVS_DIR_PATH}"
|
93
93
|
)
|
94
|
-
|
95
|
-
print(f"Will use the following path for venvs instead:\n{
|
94
|
+
_VENVS_DIR_PATH = (_ROOT_DIR_PATH / 'venvs').resolve()
|
95
|
+
print(f"Will use the following path for venvs instead:\n{_VENVS_DIR_PATH}")
|
96
96
|
else:
|
97
|
-
|
97
|
+
_VENVS_DIR_PATH = _ROOT_DIR_PATH / 'venvs'
|
98
98
|
|
99
99
|
paths = {
|
100
|
-
'PACKAGE_ROOT_PATH' :
|
101
|
-
'ROOT_DIR_PATH' :
|
102
|
-
'VIRTENV_RESOURCES_PATH' :
|
100
|
+
'PACKAGE_ROOT_PATH' : Path(__file__).parent.parent.resolve().as_posix(),
|
101
|
+
'ROOT_DIR_PATH' : _ROOT_DIR_PATH.as_posix(),
|
102
|
+
'VIRTENV_RESOURCES_PATH' : _VENVS_DIR_PATH.as_posix(),
|
103
103
|
'CONFIG_DIR_PATH' : ('{ROOT_DIR_PATH}', 'config'),
|
104
104
|
'DEFAULT_CONFIG_DIR_PATH' : ('{ROOT_DIR_PATH}', 'default_config'),
|
105
105
|
'PATCH_DIR_PATH' : ('{ROOT_DIR_PATH}', 'patch_config'),
|
@@ -114,6 +114,7 @@ paths = {
|
|
114
114
|
|
115
115
|
'SHELL_RESOURCES_PATH' : ('{ROOT_DIR_PATH}', ),
|
116
116
|
'SHELL_HISTORY_PATH' : ('{SHELL_RESOURCES_PATH}', '.mrsm_history'),
|
117
|
+
'PYTHON_RESOURCES_PATH' : ('{INTERNAL_RESOURCES_PATH}', 'python'),
|
117
118
|
|
118
119
|
'API_RESOURCES_PATH' : ('{PACKAGE_ROOT_PATH}', 'api', 'resources'),
|
119
120
|
'API_STATIC_PATH' : ('{API_RESOURCES_PATH}', 'static'),
|
@@ -186,7 +187,6 @@ def __getattr__(name: str) -> Path:
|
|
186
187
|
if name.endswith('RESOURCES_PATH') or name == 'CONFIG_DIR_PATH':
|
187
188
|
path.mkdir(parents=True, exist_ok=True)
|
188
189
|
elif 'FILENAME' in name:
|
189
|
-
path =
|
190
|
+
path = path.as_posix()
|
190
191
|
|
191
192
|
return path
|
192
|
-
|
meerschaum/config/_version.py
CHANGED
@@ -110,7 +110,7 @@ STATIC_CONFIG: Dict[str, Any] = {
|
|
110
110
|
'pbkdf2_sha256',
|
111
111
|
],
|
112
112
|
'default': 'pbkdf2_sha256',
|
113
|
-
'pbkdf2_sha256__default_rounds':
|
113
|
+
'pbkdf2_sha256__default_rounds': 1_000_000,
|
114
114
|
},
|
115
115
|
'min_username_length': 1,
|
116
116
|
'max_username_length': 26,
|
@@ -328,7 +328,7 @@ def load_plugin_connectors():
|
|
328
328
|
continue
|
329
329
|
with open(plugin.__file__, encoding='utf-8') as f:
|
330
330
|
text = f.read()
|
331
|
-
if 'make_connector' in text:
|
331
|
+
if 'make_connector' in text or 'Connector' in text:
|
332
332
|
to_import.append(plugin.name)
|
333
333
|
if not to_import:
|
334
334
|
return
|
meerschaum/core/Pipe/__init__.py
CHANGED
@@ -436,6 +436,11 @@ class Pipe:
|
|
436
436
|
def __repr__(self, **kw) -> str:
|
437
437
|
return pipe_repr(self, **kw)
|
438
438
|
|
439
|
+
def __pt_repr__(self):
|
440
|
+
from meerschaum.utils.packages import attempt_import
|
441
|
+
prompt_toolkit_formatted_text = attempt_import('prompt_toolkit.formatted_text', lazy=False)
|
442
|
+
return prompt_toolkit_formatted_text.ANSI(self.__repr__())
|
443
|
+
|
439
444
|
def __getstate__(self) -> Dict[str, Any]:
|
440
445
|
"""
|
441
446
|
Define the state dictionary (pickling).
|
meerschaum/core/Pipe/_sync.py
CHANGED
@@ -215,9 +215,8 @@ def sync(
|
|
215
215
|
|
216
216
|
### Activate and invoke `sync(pipe)` for plugin connectors with `sync` methods.
|
217
217
|
try:
|
218
|
-
if p.connector
|
219
|
-
|
220
|
-
with Venv(connector_plugin, debug=debug):
|
218
|
+
if getattr(p.connector, 'sync', None) is not None:
|
219
|
+
with Venv(get_connector_plugin(p.connector), debug=debug):
|
221
220
|
return_tuple = p.connector.sync(p, debug=debug, **kw)
|
222
221
|
p._exists = None
|
223
222
|
if not isinstance(return_tuple, tuple):
|
meerschaum/plugins/__init__.py
CHANGED
@@ -7,6 +7,7 @@ Expose plugin management APIs from the `meerschaum.plugins` module.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
+
import functools
|
10
11
|
from meerschaum.utils.typing import Callable, Any, Union, Optional, Dict, List, Tuple
|
11
12
|
from meerschaum.utils.threading import Lock, RLock
|
12
13
|
from meerschaum.plugins._Plugin import Plugin
|
@@ -16,6 +17,7 @@ _pre_sync_hooks: Dict[str, List[Callable[[Any], Any]]] = {}
|
|
16
17
|
_post_sync_hooks: Dict[str, List[Callable[[Any], Any]]] = {}
|
17
18
|
_locks = {
|
18
19
|
'_api_plugins': RLock(),
|
20
|
+
'_dash_plugins': RLock(),
|
19
21
|
'_pre_sync_hooks': RLock(),
|
20
22
|
'_post_sync_hooks': RLock(),
|
21
23
|
'__path__': RLock(),
|
@@ -156,6 +158,71 @@ def post_sync_hook(
|
|
156
158
|
return function
|
157
159
|
|
158
160
|
|
161
|
+
_plugin_endpoints_to_pages = {}
|
162
|
+
def web_page(
|
163
|
+
page: Union[str, None, Callable[[Any], Any]] = None,
|
164
|
+
login_required: bool = True,
|
165
|
+
**kwargs
|
166
|
+
) -> Any:
|
167
|
+
"""
|
168
|
+
Quickly add pages to the dash application.
|
169
|
+
|
170
|
+
Examples
|
171
|
+
--------
|
172
|
+
>>> import meerschaum as mrsm
|
173
|
+
>>> from meerschaum.plugins import web_page
|
174
|
+
>>> html = mrsm.attempt_import('dash.html')
|
175
|
+
>>>
|
176
|
+
>>> @web_page('foo/bar', login_required=False)
|
177
|
+
>>> def foo_bar():
|
178
|
+
... return html.Div([html.H1("Hello, World!")])
|
179
|
+
>>>
|
180
|
+
"""
|
181
|
+
page_str = None
|
182
|
+
|
183
|
+
def _decorator(_func: Callable[[Any], Any]) -> Callable[[Any], Any]:
|
184
|
+
nonlocal page_str
|
185
|
+
|
186
|
+
@functools.wraps(_func)
|
187
|
+
def wrapper(*_args, **_kwargs):
|
188
|
+
return _func(*_args, **_kwargs)
|
189
|
+
|
190
|
+
if page_str is None:
|
191
|
+
page_str = _func.__name__
|
192
|
+
|
193
|
+
page_str = page_str.lstrip('/').rstrip('/').strip()
|
194
|
+
_plugin_endpoints_to_pages[page_str] = {
|
195
|
+
'function': _func,
|
196
|
+
'login_required': login_required,
|
197
|
+
}
|
198
|
+
return wrapper
|
199
|
+
|
200
|
+
if callable(page):
|
201
|
+
decorator_to_return = _decorator(page)
|
202
|
+
page_str = page.__name__
|
203
|
+
else:
|
204
|
+
decorator_to_return = _decorator
|
205
|
+
page_str = page
|
206
|
+
|
207
|
+
return decorator_to_return
|
208
|
+
|
209
|
+
|
210
|
+
_dash_plugins = {}
|
211
|
+
def dash_plugin(function: Callable[[Any], Any]) -> Callable[[Any], Any]:
|
212
|
+
"""
|
213
|
+
Execute the function when starting the Dash application.
|
214
|
+
"""
|
215
|
+
with _locks['_dash_plugins']:
|
216
|
+
try:
|
217
|
+
if function.__module__ not in _dash_plugins:
|
218
|
+
_dash_plugins[function.__module__] = []
|
219
|
+
_dash_plugins[function.__module__].append(function)
|
220
|
+
except Exception as e:
|
221
|
+
from meerschaum.utils.warnings import warn
|
222
|
+
warn(e)
|
223
|
+
return function
|
224
|
+
|
225
|
+
|
159
226
|
def api_plugin(function: Callable[[Any], Any]) -> Callable[[Any], Any]:
|
160
227
|
"""
|
161
228
|
Execute the function when initializing the Meerschaum API module.
|
@@ -164,15 +231,6 @@ def api_plugin(function: Callable[[Any], Any]) -> Callable[[Any], Any]:
|
|
164
231
|
|
165
232
|
The FastAPI app will be passed as the only parameter.
|
166
233
|
|
167
|
-
Parameters
|
168
|
-
----------
|
169
|
-
function: Callable[[Any, Any]]
|
170
|
-
The function to be called before starting the Meerschaum API.
|
171
|
-
|
172
|
-
Returns
|
173
|
-
-------
|
174
|
-
Another function (decorator function).
|
175
|
-
|
176
234
|
Examples
|
177
235
|
--------
|
178
236
|
>>> from meerschaum.plugins import api_plugin
|
@@ -465,8 +465,9 @@ class Daemon:
|
|
465
465
|
Handle `SIGINT` within the Daemon context.
|
466
466
|
This method is injected into the `DaemonContext`.
|
467
467
|
"""
|
468
|
-
|
469
|
-
|
468
|
+
from meerschaum.utils.process import signal_handler
|
469
|
+
signal_handler(signal_number, stack_frame)
|
470
|
+
|
470
471
|
self.rotating_log.stop_log_fd_interception(unused_only=False)
|
471
472
|
timer = self.__dict__.get('_log_refresh_timer', None)
|
472
473
|
if timer is not None:
|
@@ -477,6 +478,7 @@ class Daemon:
|
|
477
478
|
daemon_context.close()
|
478
479
|
|
479
480
|
_close_pools()
|
481
|
+
|
480
482
|
import threading
|
481
483
|
for thread in threading.enumerate():
|
482
484
|
if thread.name == 'MainThread':
|
@@ -495,6 +497,9 @@ class Daemon:
|
|
495
497
|
Handle `SIGTERM` within the `Daemon` context.
|
496
498
|
This method is injected into the `DaemonContext`.
|
497
499
|
"""
|
500
|
+
from meerschaum.utils.process import signal_handler
|
501
|
+
signal_handler(signal_number, stack_frame)
|
502
|
+
|
498
503
|
timer = self.__dict__.get('_log_refresh_timer', None)
|
499
504
|
if timer is not None:
|
500
505
|
timer.cancel()
|
meerschaum/utils/misc.py
CHANGED
@@ -1103,6 +1103,12 @@ def is_docker_available() -> bool:
|
|
1103
1103
|
return has_docker
|
1104
1104
|
|
1105
1105
|
|
1106
|
+
def is_android() -> bool:
|
1107
|
+
"""Return `True` if the current platform is Android."""
|
1108
|
+
import sys
|
1109
|
+
return hasattr(sys, 'getandroidapilevel')
|
1110
|
+
|
1111
|
+
|
1106
1112
|
def is_bcp_available() -> bool:
|
1107
1113
|
"""Check if the MSSQL `bcp` utility is installed."""
|
1108
1114
|
import subprocess
|