meerschaum 2.8.4__py3-none-any.whl → 2.9.0rc1__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/api/_chunks.py +67 -0
- meerschaum/api/dash/callbacks/custom.py +23 -2
- meerschaum/api/dash/callbacks/dashboard.py +41 -3
- meerschaum/api/dash/components.py +27 -19
- meerschaum/api/dash/pages/dashboard.py +11 -9
- meerschaum/api/dash/pages/plugins.py +31 -27
- meerschaum/api/dash/webterm.py +6 -3
- meerschaum/api/resources/static/css/dash.css +1 -1
- meerschaum/api/resources/templates/termpage.html +4 -0
- meerschaum/api/routes/_pipes.py +191 -78
- meerschaum/config/_default.py +4 -0
- meerschaum/config/_version.py +1 -1
- meerschaum/connectors/api/_APIConnector.py +12 -1
- meerschaum/connectors/api/_pipes.py +27 -15
- meerschaum/connectors/api/_plugins.py +51 -45
- meerschaum/connectors/api/_request.py +1 -1
- meerschaum/connectors/parse.py +1 -2
- meerschaum/connectors/sql/_SQLConnector.py +3 -0
- meerschaum/connectors/sql/_cli.py +1 -0
- meerschaum/connectors/sql/_create_engine.py +51 -4
- meerschaum/connectors/sql/_pipes.py +13 -2
- meerschaum/connectors/sql/_sql.py +35 -4
- meerschaum/core/Pipe/_data.py +1 -2
- meerschaum/plugins/_Plugin.py +21 -5
- meerschaum/plugins/__init__.py +6 -4
- meerschaum/utils/dataframe.py +87 -2
- meerschaum/utils/dtypes/__init__.py +182 -1
- meerschaum/utils/dtypes/sql.py +114 -2
- meerschaum/utils/formatting/_shell.py +1 -4
- meerschaum/utils/packages/_packages.py +3 -0
- meerschaum/utils/sql.py +17 -5
- meerschaum/utils/venv/__init__.py +2 -0
- {meerschaum-2.8.4.dist-info → meerschaum-2.9.0rc1.dist-info}/METADATA +10 -1
- {meerschaum-2.8.4.dist-info → meerschaum-2.9.0rc1.dist-info}/RECORD +40 -39
- {meerschaum-2.8.4.dist-info → meerschaum-2.9.0rc1.dist-info}/WHEEL +1 -1
- {meerschaum-2.8.4.dist-info → meerschaum-2.9.0rc1.dist-info}/LICENSE +0 -0
- {meerschaum-2.8.4.dist-info → meerschaum-2.9.0rc1.dist-info}/NOTICE +0 -0
- {meerschaum-2.8.4.dist-info → meerschaum-2.9.0rc1.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.8.4.dist-info → meerschaum-2.9.0rc1.dist-info}/top_level.txt +0 -0
- {meerschaum-2.8.4.dist-info → meerschaum-2.9.0rc1.dist-info}/zip-safe +0 -0
@@ -7,21 +7,25 @@ Manage plugins via the API connector
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
-
|
10
|
+
|
11
|
+
import meerschaum as mrsm
|
12
|
+
from meerschaum.utils.typing import Union, Any, Optional, SuccessTuple, List, Dict
|
13
|
+
|
11
14
|
|
12
15
|
def plugin_r_url(
|
13
|
-
|
14
|
-
|
16
|
+
plugin: Union[mrsm.core.Plugin, str],
|
17
|
+
) -> str:
|
15
18
|
"""Generate a relative URL path from a Plugin."""
|
16
19
|
from meerschaum.config.static import STATIC_CONFIG
|
17
20
|
return f"{STATIC_CONFIG['api']['endpoints']['plugins']}/{plugin}"
|
18
21
|
|
22
|
+
|
19
23
|
def register_plugin(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
24
|
+
self,
|
25
|
+
plugin: mrsm.core.Plugin,
|
26
|
+
make_archive: bool = True,
|
27
|
+
debug: bool = False,
|
28
|
+
) -> SuccessTuple:
|
25
29
|
"""Register a plugin and upload its archive."""
|
26
30
|
import json
|
27
31
|
archive_path = plugin.make_tar(debug=debug) if make_archive else plugin.archive_path
|
@@ -34,27 +38,30 @@ def register_plugin(
|
|
34
38
|
r_url = plugin_r_url(plugin)
|
35
39
|
try:
|
36
40
|
response = self.post(r_url, files=files, params=metadata, debug=debug)
|
37
|
-
except Exception
|
41
|
+
except Exception:
|
38
42
|
return False, f"Failed to register plugin '{plugin}'."
|
39
43
|
finally:
|
40
44
|
file_pointer.close()
|
41
45
|
|
42
46
|
try:
|
43
47
|
success, msg = json.loads(response.text)
|
44
|
-
except Exception
|
48
|
+
except Exception:
|
45
49
|
return False, response.text
|
46
50
|
|
47
51
|
return success, msg
|
48
52
|
|
53
|
+
|
49
54
|
def install_plugin(
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
self,
|
56
|
+
name: str,
|
57
|
+
skip_deps: bool = False,
|
58
|
+
force: bool = False,
|
59
|
+
debug: bool = False
|
60
|
+
) -> SuccessTuple:
|
56
61
|
"""Download and attempt to install a plugin from the API."""
|
57
|
-
import os
|
62
|
+
import os
|
63
|
+
import pathlib
|
64
|
+
import json
|
58
65
|
from meerschaum.core import Plugin
|
59
66
|
from meerschaum.config._paths import PLUGINS_TEMP_RESOURCES_PATH
|
60
67
|
from meerschaum.utils.debug import dprint
|
@@ -75,41 +82,39 @@ def install_plugin(
|
|
75
82
|
success, msg = tuple(j)
|
76
83
|
elif isinstance(j, dict) and 'detail' in j:
|
77
84
|
success, msg = False, fail_msg
|
78
|
-
except Exception
|
85
|
+
except Exception:
|
79
86
|
success, msg = False, fail_msg
|
80
87
|
return success, msg
|
81
88
|
plugin = Plugin(name, archive_path=archive_path, repo_connector=self)
|
82
89
|
return plugin.install(skip_deps=skip_deps, force=force, debug=debug)
|
83
90
|
|
91
|
+
|
84
92
|
def get_plugins(
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
93
|
+
self,
|
94
|
+
user_id: Optional[int] = None,
|
95
|
+
search_term: Optional[str] = None,
|
96
|
+
debug: bool = False
|
97
|
+
) -> List[str]:
|
90
98
|
"""Return a list of registered plugin names.
|
91
99
|
|
92
100
|
Parameters
|
93
101
|
----------
|
94
|
-
user_id
|
102
|
+
user_id: Optional[int], default None
|
95
103
|
If specified, return all plugins from a certain user.
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
(Default value = None)
|
100
|
-
debug : bool :
|
101
|
-
(Default value = False)
|
104
|
+
|
105
|
+
search_term: Optional[str], default None
|
106
|
+
If specified, return plugins beginning with this string.
|
102
107
|
|
103
108
|
Returns
|
104
109
|
-------
|
105
|
-
|
110
|
+
A list of plugin names.
|
106
111
|
"""
|
107
112
|
import json
|
108
|
-
from meerschaum.utils.warnings import
|
113
|
+
from meerschaum.utils.warnings import error
|
109
114
|
from meerschaum.config.static import STATIC_CONFIG
|
110
115
|
response = self.get(
|
111
116
|
STATIC_CONFIG['api']['endpoints']['plugins'],
|
112
|
-
params = {'user_id'
|
117
|
+
params = {'user_id': user_id, 'search_term': search_term},
|
113
118
|
use_token = True,
|
114
119
|
debug = debug
|
115
120
|
)
|
@@ -120,11 +125,12 @@ def get_plugins(
|
|
120
125
|
error(response.text)
|
121
126
|
return plugins
|
122
127
|
|
128
|
+
|
123
129
|
def get_plugin_attributes(
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
130
|
+
self,
|
131
|
+
plugin: mrsm.core.Plugin,
|
132
|
+
debug: bool = False
|
133
|
+
) -> Dict[str, Any]:
|
128
134
|
"""
|
129
135
|
Return a plugin's attributes.
|
130
136
|
"""
|
@@ -136,7 +142,7 @@ def get_plugin_attributes(
|
|
136
142
|
if isinstance(attributes, str) and attributes and attributes[0] == '{':
|
137
143
|
try:
|
138
144
|
attributes = json.loads(attributes)
|
139
|
-
except Exception
|
145
|
+
except Exception:
|
140
146
|
pass
|
141
147
|
if not isinstance(attributes, dict):
|
142
148
|
error(response.text)
|
@@ -145,23 +151,23 @@ def get_plugin_attributes(
|
|
145
151
|
return {}
|
146
152
|
return attributes
|
147
153
|
|
154
|
+
|
148
155
|
def delete_plugin(
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
156
|
+
self,
|
157
|
+
plugin: mrsm.core.Plugin,
|
158
|
+
debug: bool = False
|
159
|
+
) -> SuccessTuple:
|
153
160
|
"""Delete a plugin from an API repository."""
|
154
161
|
import json
|
155
162
|
r_url = plugin_r_url(plugin)
|
156
163
|
try:
|
157
164
|
response = self.delete(r_url, debug=debug)
|
158
|
-
except Exception
|
165
|
+
except Exception:
|
159
166
|
return False, f"Failed to delete plugin '{plugin}'."
|
160
167
|
|
161
168
|
try:
|
162
169
|
success, msg = json.loads(response.text)
|
163
|
-
except Exception
|
170
|
+
except Exception:
|
164
171
|
return False, response.text
|
165
172
|
|
166
173
|
return success, msg
|
167
|
-
|
meerschaum/connectors/parse.py
CHANGED
@@ -151,6 +151,9 @@ class SQLConnector(Connector):
|
|
151
151
|
if uri.startswith('timescaledb://'):
|
152
152
|
uri = uri.replace('timescaledb://', 'postgresql+psycopg://', 1)
|
153
153
|
flavor = 'timescaledb'
|
154
|
+
if uri.startswith('postgis://'):
|
155
|
+
uri = uri.replace('postgis://', 'postgresql+psycopg://', 1)
|
156
|
+
flavor = 'postgis'
|
154
157
|
kw['uri'] = uri
|
155
158
|
from_uri_params = self.from_uri(kw['uri'], as_dict=True)
|
156
159
|
label = label or from_uri_params.get('label', None)
|
@@ -7,6 +7,7 @@ This module contains the logic that builds the sqlalchemy engine string.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
import traceback
|
10
|
+
import meerschaum as mrsm
|
10
11
|
from meerschaum.utils.debug import dprint
|
11
12
|
|
12
13
|
### determine driver and requirements from flavor
|
@@ -47,6 +48,16 @@ flavor_configs = {
|
|
47
48
|
'port': 5432,
|
48
49
|
},
|
49
50
|
},
|
51
|
+
'postgis': {
|
52
|
+
'engine': 'postgresql+psycopg',
|
53
|
+
'create_engine': default_create_engine_args,
|
54
|
+
'omit_create_engine': {'method',},
|
55
|
+
'to_sql': {},
|
56
|
+
'requirements': default_requirements,
|
57
|
+
'defaults': {
|
58
|
+
'port': 5432,
|
59
|
+
},
|
60
|
+
},
|
50
61
|
'citus': {
|
51
62
|
'engine': 'postgresql+psycopg',
|
52
63
|
'create_engine': default_create_engine_args,
|
@@ -162,6 +173,7 @@ install_flavor_drivers = {
|
|
162
173
|
'mariadb': ['pymysql'],
|
163
174
|
'timescaledb': ['psycopg'],
|
164
175
|
'postgresql': ['psycopg'],
|
176
|
+
'postgis': ['psycopg', 'geoalchemy'],
|
165
177
|
'citus': ['psycopg'],
|
166
178
|
'cockroachdb': ['psycopg', 'sqlalchemy_cockroachdb', 'sqlalchemy_cockroachdb.psycopg'],
|
167
179
|
'mssql': ['pyodbc'],
|
@@ -198,8 +210,7 @@ def create_engine(
|
|
198
210
|
warn=False,
|
199
211
|
)
|
200
212
|
if self.flavor == 'mssql':
|
201
|
-
|
202
|
-
pyodbc.pooling = False
|
213
|
+
_init_mssql_sqlalchemy()
|
203
214
|
if self.flavor in require_patching_flavors:
|
204
215
|
from meerschaum.utils.packages import determine_version, _monkey_patch_get_distribution
|
205
216
|
import pathlib
|
@@ -257,8 +268,8 @@ def create_engine(
|
|
257
268
|
|
258
269
|
### Sometimes the timescaledb:// flavor can slip in.
|
259
270
|
if _uri and self.flavor in _uri:
|
260
|
-
if self.flavor
|
261
|
-
engine_str = engine_str.replace(
|
271
|
+
if self.flavor in ('timescaledb', 'postgis'):
|
272
|
+
engine_str = engine_str.replace(self.flavor, 'postgresql', 1)
|
262
273
|
elif _uri.startswith('postgresql://'):
|
263
274
|
engine_str = engine_str.replace('postgresql://', 'postgresql+psycopg2://')
|
264
275
|
|
@@ -313,3 +324,39 @@ def create_engine(
|
|
313
324
|
if include_uri:
|
314
325
|
return engine, engine_str
|
315
326
|
return engine
|
327
|
+
|
328
|
+
|
329
|
+
def _init_mssql_sqlalchemy():
|
330
|
+
"""
|
331
|
+
When first instantiating a SQLAlchemy connection to MSSQL,
|
332
|
+
monkey-patch `pyodbc` handling in SQLAlchemy.
|
333
|
+
"""
|
334
|
+
pyodbc, sqlalchemy_dialects_mssql_pyodbc = mrsm.attempt_import(
|
335
|
+
'pyodbc',
|
336
|
+
'sqlalchemy.dialects.mssql.pyodbc',
|
337
|
+
lazy=False,
|
338
|
+
warn=False,
|
339
|
+
)
|
340
|
+
pyodbc.pooling = False
|
341
|
+
|
342
|
+
MSDialect_pyodbc = sqlalchemy_dialects_mssql_pyodbc.MSDialect_pyodbc
|
343
|
+
|
344
|
+
def _handle_geometry(val):
|
345
|
+
from binascii import hexlify
|
346
|
+
hex_str = f"0x{hexlify(val).decode().upper()}"
|
347
|
+
return hex_str
|
348
|
+
|
349
|
+
def custom_on_connect(self):
|
350
|
+
super_ = super(MSDialect_pyodbc, self).on_connect()
|
351
|
+
|
352
|
+
def _on_connect(conn):
|
353
|
+
if super_ is not None:
|
354
|
+
super_(conn)
|
355
|
+
|
356
|
+
self._setup_timestampoffset_type(conn)
|
357
|
+
conn.add_output_converter(-151, _handle_geometry)
|
358
|
+
|
359
|
+
return _on_connect
|
360
|
+
|
361
|
+
### TODO: Parse proprietary MSSQL geometry bytes into WKB.
|
362
|
+
# MSDialect_pyodbc.on_connect = custom_on_connect
|
@@ -731,7 +731,7 @@ def get_create_index_queries(
|
|
731
731
|
) + f"{primary_key_name})"
|
732
732
|
),
|
733
733
|
])
|
734
|
-
elif self.flavor in ('citus', 'postgresql', 'duckdb'):
|
734
|
+
elif self.flavor in ('citus', 'postgresql', 'duckdb', 'postgis'):
|
735
735
|
primary_queries.extend([
|
736
736
|
(
|
737
737
|
f"ALTER TABLE {_pipe_name}\n"
|
@@ -1052,6 +1052,7 @@ def get_pipe_data(
|
|
1052
1052
|
attempt_cast_to_numeric,
|
1053
1053
|
attempt_cast_to_uuid,
|
1054
1054
|
attempt_cast_to_bytes,
|
1055
|
+
attempt_cast_to_geometry,
|
1055
1056
|
are_dtypes_equal,
|
1056
1057
|
)
|
1057
1058
|
from meerschaum.utils.dtypes.sql import get_pd_type_from_db_type
|
@@ -1138,6 +1139,11 @@ def get_pipe_data(
|
|
1138
1139
|
for col, typ in pipe.dtypes.items()
|
1139
1140
|
if typ == 'bytes' and col in dtypes
|
1140
1141
|
]
|
1142
|
+
geometry_columns = [
|
1143
|
+
col
|
1144
|
+
for col, typ in pipe.dtypes.items()
|
1145
|
+
if typ.startswith('geometry') and col in dtypes
|
1146
|
+
]
|
1141
1147
|
|
1142
1148
|
kw['coerce_float'] = kw.get('coerce_float', (len(numeric_columns) == 0))
|
1143
1149
|
|
@@ -1162,6 +1168,11 @@ def get_pipe_data(
|
|
1162
1168
|
continue
|
1163
1169
|
df[col] = df[col].apply(attempt_cast_to_bytes)
|
1164
1170
|
|
1171
|
+
for col in geometry_columns:
|
1172
|
+
if col not in df.columns:
|
1173
|
+
continue
|
1174
|
+
df[col] = df[col].apply(attempt_cast_to_geometry)
|
1175
|
+
|
1165
1176
|
if self.flavor == 'sqlite':
|
1166
1177
|
ignore_dt_cols = [
|
1167
1178
|
col
|
@@ -1511,7 +1522,7 @@ def get_pipe_attributes(
|
|
1511
1522
|
if isinstance(parameters, str) and parameters[0] == '{':
|
1512
1523
|
parameters = json.loads(parameters)
|
1513
1524
|
attributes['parameters'] = parameters
|
1514
|
-
except Exception
|
1525
|
+
except Exception:
|
1515
1526
|
attributes['parameters'] = {}
|
1516
1527
|
|
1517
1528
|
return attributes
|
@@ -17,7 +17,7 @@ from meerschaum.utils.debug import dprint
|
|
17
17
|
from meerschaum.utils.warnings import warn
|
18
18
|
|
19
19
|
### database flavors that can use bulk insert
|
20
|
-
_bulk_flavors = {'postgresql', 'timescaledb', 'citus', 'mssql'}
|
20
|
+
_bulk_flavors = {'postgresql', 'postgis', 'timescaledb', 'citus', 'mssql'}
|
21
21
|
### flavors that do not support chunks
|
22
22
|
_disallow_chunks_flavors = ['duckdb']
|
23
23
|
_max_chunks_flavors = {'sqlite': 1000}
|
@@ -798,6 +798,7 @@ def to_sql(
|
|
798
798
|
get_numeric_cols,
|
799
799
|
get_uuid_cols,
|
800
800
|
get_bytes_cols,
|
801
|
+
get_geometry_cols,
|
801
802
|
)
|
802
803
|
from meerschaum.utils.dtypes import (
|
803
804
|
are_dtypes_equal,
|
@@ -805,7 +806,9 @@ def to_sql(
|
|
805
806
|
encode_bytes_for_bytea,
|
806
807
|
serialize_bytes,
|
807
808
|
serialize_decimal,
|
809
|
+
serialize_geometry,
|
808
810
|
json_serialize_value,
|
811
|
+
get_geometry_type_srid,
|
809
812
|
)
|
810
813
|
from meerschaum.utils.dtypes.sql import (
|
811
814
|
PD_TO_SQLALCHEMY_DTYPES_FLAVORS,
|
@@ -822,6 +825,7 @@ def to_sql(
|
|
822
825
|
|
823
826
|
bytes_cols = get_bytes_cols(df)
|
824
827
|
numeric_cols = get_numeric_cols(df)
|
828
|
+
geometry_cols = get_geometry_cols(df)
|
825
829
|
### NOTE: This excludes non-numeric serialized Decimals (e.g. SQLite).
|
826
830
|
numeric_cols_dtypes = {
|
827
831
|
col: typ
|
@@ -830,7 +834,6 @@ def to_sql(
|
|
830
834
|
col in df.columns
|
831
835
|
and 'numeric' in str(typ).lower()
|
832
836
|
)
|
833
|
-
|
834
837
|
}
|
835
838
|
numeric_cols.extend([col for col in numeric_cols_dtypes if col not in numeric_cols])
|
836
839
|
numeric_cols_precisions_scales = {
|
@@ -841,6 +844,22 @@ def to_sql(
|
|
841
844
|
)
|
842
845
|
for col, typ in numeric_cols_dtypes.items()
|
843
846
|
}
|
847
|
+
geometry_cols_dtypes = {
|
848
|
+
col: typ
|
849
|
+
for col, typ in kw.get('dtype', {}).items()
|
850
|
+
if (
|
851
|
+
col in df.columns
|
852
|
+
and 'geometry' in str(typ).lower() or 'geography' in str(typ).lower()
|
853
|
+
)
|
854
|
+
}
|
855
|
+
geometry_cols.extend([col for col in geometry_cols_dtypes if col not in geometry_cols])
|
856
|
+
geometry_cols_types_srids = {
|
857
|
+
col: (typ.geometry_type, typ.srid)
|
858
|
+
if hasattr(typ, 'srid')
|
859
|
+
else get_geometry_type_srid()
|
860
|
+
for col, typ in geometry_cols_dtypes.items()
|
861
|
+
}
|
862
|
+
|
844
863
|
cols_pd_types = {
|
845
864
|
col: get_pd_type_from_db_type(str(typ))
|
846
865
|
for col, typ in kw.get('dtype', {}).items()
|
@@ -856,8 +875,9 @@ def to_sql(
|
|
856
875
|
}
|
857
876
|
|
858
877
|
enable_bulk_insert = mrsm.get_config(
|
859
|
-
'system', 'connectors', 'sql', 'bulk_insert'
|
860
|
-
|
878
|
+
'system', 'connectors', 'sql', 'bulk_insert', self.flavor,
|
879
|
+
warn=False,
|
880
|
+
) or False
|
861
881
|
stats = {'target': name}
|
862
882
|
### resort to defaults if None
|
863
883
|
copied = False
|
@@ -901,6 +921,17 @@ def to_sql(
|
|
901
921
|
)
|
902
922
|
)
|
903
923
|
|
924
|
+
for col in geometry_cols:
|
925
|
+
geometry_type, srid = geometry_cols_types_srids.get(col, get_geometry_type_srid())
|
926
|
+
with warnings.catch_warnings():
|
927
|
+
warnings.simplefilter("ignore")
|
928
|
+
df[col] = df[col].apply(
|
929
|
+
functools.partial(
|
930
|
+
serialize_geometry,
|
931
|
+
as_wkt=(self.flavor == 'mssql')
|
932
|
+
)
|
933
|
+
)
|
934
|
+
|
904
935
|
stats['method'] = method.__name__ if hasattr(method, '__name__') else str(method)
|
905
936
|
|
906
937
|
default_chunksize = self._sys_config.get('chunksize', None)
|
meerschaum/core/Pipe/_data.py
CHANGED
@@ -88,9 +88,8 @@ def get_data(
|
|
88
88
|
limit: Optional[int], default None
|
89
89
|
If provided, cap the dataframe to this many rows.
|
90
90
|
|
91
|
-
fresh: bool, default
|
91
|
+
fresh: bool, default False
|
92
92
|
If `True`, skip local cache and directly query the instance connector.
|
93
|
-
Defaults to `True`.
|
94
93
|
|
95
94
|
debug: bool, default False
|
96
95
|
Verbosity toggle.
|
meerschaum/plugins/_Plugin.py
CHANGED
@@ -450,7 +450,7 @@ class Plugin:
|
|
450
450
|
success, msg = False, (
|
451
451
|
f"Failed to run post-install setup for plugin '{self}'." + '\n' +
|
452
452
|
f"Check `setup()` in '{self.__file__}' for more information " +
|
453
|
-
|
453
|
+
"(no error message provided)."
|
454
454
|
)
|
455
455
|
else:
|
456
456
|
success, msg = True, success_msg
|
@@ -458,7 +458,7 @@ class Plugin:
|
|
458
458
|
success = True
|
459
459
|
msg = (
|
460
460
|
f"Post-install for plugin '{self}' returned None. " +
|
461
|
-
|
461
|
+
"Assuming plugin successfully installed."
|
462
462
|
)
|
463
463
|
warn(msg)
|
464
464
|
else:
|
@@ -469,7 +469,7 @@ class Plugin:
|
|
469
469
|
)
|
470
470
|
|
471
471
|
_ongoing_installations.remove(self.full_name)
|
472
|
-
|
472
|
+
_ = self.module
|
473
473
|
return success, msg
|
474
474
|
|
475
475
|
|
@@ -716,13 +716,14 @@ class Plugin:
|
|
716
716
|
return required
|
717
717
|
|
718
718
|
|
719
|
-
def get_required_plugins(self, debug: bool=False) -> List[
|
719
|
+
def get_required_plugins(self, debug: bool=False) -> List[mrsm.plugins.Plugin]:
|
720
720
|
"""
|
721
721
|
Return a list of required Plugin objects.
|
722
722
|
"""
|
723
723
|
from meerschaum.utils.warnings import warn
|
724
724
|
from meerschaum.config import get_config
|
725
725
|
from meerschaum.config.static import STATIC_CONFIG
|
726
|
+
from meerschaum.connectors.parse import is_valid_connector_keys
|
726
727
|
plugins = []
|
727
728
|
_deps = self.get_dependencies(debug=debug)
|
728
729
|
sep = STATIC_CONFIG['plugins']['repo_separator']
|
@@ -731,11 +732,13 @@ class Plugin:
|
|
731
732
|
if _d.startswith('plugin:') and len(_d) > len('plugin:')
|
732
733
|
]
|
733
734
|
default_repo_keys = get_config('meerschaum', 'default_repository')
|
735
|
+
skipped_repo_keys = set()
|
736
|
+
|
734
737
|
for _plugin_name in plugin_names:
|
735
738
|
if sep in _plugin_name:
|
736
739
|
try:
|
737
740
|
_plugin_name, _repo_keys = _plugin_name.split(sep)
|
738
|
-
except Exception
|
741
|
+
except Exception:
|
739
742
|
_repo_keys = default_repo_keys
|
740
743
|
warn(
|
741
744
|
f"Invalid repo keys for required plugin '{_plugin_name}'.\n "
|
@@ -744,7 +747,20 @@ class Plugin:
|
|
744
747
|
)
|
745
748
|
else:
|
746
749
|
_repo_keys = default_repo_keys
|
750
|
+
|
751
|
+
if _repo_keys in skipped_repo_keys:
|
752
|
+
continue
|
753
|
+
|
754
|
+
if not is_valid_connector_keys(_repo_keys):
|
755
|
+
warn(
|
756
|
+
f"Invalid connector '{_repo_keys}'.\n"
|
757
|
+
f" Skipping required plugins from repository '{_repo_keys}'",
|
758
|
+
stack=False,
|
759
|
+
)
|
760
|
+
continue
|
761
|
+
|
747
762
|
plugins.append(Plugin(_plugin_name, repo=_repo_keys))
|
763
|
+
|
748
764
|
return plugins
|
749
765
|
|
750
766
|
|
meerschaum/plugins/__init__.py
CHANGED
@@ -166,10 +166,11 @@ def post_sync_hook(
|
|
166
166
|
|
167
167
|
_plugin_endpoints_to_pages = {}
|
168
168
|
def web_page(
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
169
|
+
page: Union[str, None, Callable[[Any], Any]] = None,
|
170
|
+
login_required: bool = True,
|
171
|
+
skip_navbar: bool = False,
|
172
|
+
**kwargs
|
173
|
+
) -> Any:
|
173
174
|
"""
|
174
175
|
Quickly add pages to the dash application.
|
175
176
|
|
@@ -200,6 +201,7 @@ def web_page(
|
|
200
201
|
_plugin_endpoints_to_pages[page_str] = {
|
201
202
|
'function': _func,
|
202
203
|
'login_required': login_required,
|
204
|
+
'skip_navbar': skip_navbar,
|
203
205
|
}
|
204
206
|
return wrapper
|
205
207
|
|