meerschaum 3.0.4__py3-none-any.whl → 3.0.6__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/static.py +10 -0
- meerschaum/actions/copy.py +1 -1
- meerschaum/actions/delete.py +1 -1
- meerschaum/api/dash/callbacks/dashboard.py +20 -3
- meerschaum/api/dash/pipes.py +25 -9
- meerschaum/config/_version.py +1 -1
- meerschaum/connectors/__init__.py +13 -6
- meerschaum/connectors/parse.py +2 -2
- meerschaum/connectors/sql/_SQLConnector.py +4 -3
- meerschaum/connectors/sql/_cli.py +6 -1
- meerschaum/connectors/sql/_create_engine.py +2 -1
- meerschaum/connectors/sql/_fetch.py +25 -22
- meerschaum/connectors/sql/_pipes.py +19 -6
- meerschaum/connectors/sql/_sql.py +31 -1
- meerschaum/connectors/sql/_uri.py +1 -1
- meerschaum/connectors/sql/tables/__init__.py +2 -2
- meerschaum/core/Pipe/_attributes.py +1 -1
- meerschaum/core/Plugin/_Plugin.py +9 -3
- meerschaum/plugins/__init__.py +24 -7
- meerschaum/utils/_get_pipes.py +2 -2
- meerschaum/utils/dtypes/sql.py +68 -1
- meerschaum/utils/packages/__init__.py +10 -8
- meerschaum/utils/sql.py +86 -9
- meerschaum/utils/venv/_Venv.py +18 -6
- meerschaum/utils/venv/__init__.py +20 -4
- {meerschaum-3.0.4.dist-info → meerschaum-3.0.6.dist-info}/METADATA +1 -1
- {meerschaum-3.0.4.dist-info → meerschaum-3.0.6.dist-info}/RECORD +33 -33
- {meerschaum-3.0.4.dist-info → meerschaum-3.0.6.dist-info}/WHEEL +0 -0
- {meerschaum-3.0.4.dist-info → meerschaum-3.0.6.dist-info}/entry_points.txt +0 -0
- {meerschaum-3.0.4.dist-info → meerschaum-3.0.6.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-3.0.4.dist-info → meerschaum-3.0.6.dist-info}/licenses/NOTICE +0 -0
- {meerschaum-3.0.4.dist-info → meerschaum-3.0.6.dist-info}/top_level.txt +0 -0
- {meerschaum-3.0.4.dist-info → meerschaum-3.0.6.dist-info}/zip-safe +0 -0
meerschaum/_internal/static.py
CHANGED
@@ -192,6 +192,16 @@ STATIC_CONFIG: Dict[str, Any] = {
|
|
192
192
|
'requirements': {'database'},
|
193
193
|
'defaults': {},
|
194
194
|
},
|
195
|
+
'geopackage': {
|
196
|
+
'engine': 'sqlite',
|
197
|
+
'create_engine': _default_create_engine_args,
|
198
|
+
'omit_create_engine': {'method',},
|
199
|
+
'to_sql': {
|
200
|
+
'method': 'multi',
|
201
|
+
},
|
202
|
+
'requirements': {'database'},
|
203
|
+
'defaults': {},
|
204
|
+
},
|
195
205
|
'duckdb': {
|
196
206
|
'engine': 'duckdb',
|
197
207
|
'create_engine': {},
|
meerschaum/actions/copy.py
CHANGED
meerschaum/actions/delete.py
CHANGED
@@ -349,7 +349,7 @@ def _delete_connectors(
|
|
349
349
|
for c in to_delete:
|
350
350
|
try:
|
351
351
|
### Remove database files.
|
352
|
-
if c.flavor in ('sqlite', 'duckdb'):
|
352
|
+
if c.flavor in ('sqlite', 'duckdb', 'geopackage'):
|
353
353
|
if ':memory:' not in c.database and pathlib.Path(c.database).exists():
|
354
354
|
if force or yes_no(
|
355
355
|
f"Detected '{c.flavor}' database '{c.database}'. "
|
@@ -51,7 +51,7 @@ from meerschaum.utils.misc import filter_keywords, flatten_list, string_to_dict
|
|
51
51
|
from meerschaum.utils.yaml import yaml
|
52
52
|
from meerschaum.actions import get_subactions, actions
|
53
53
|
from meerschaum._internal.arguments._parser import parser
|
54
|
-
from meerschaum.connectors.sql._fetch import set_pipe_query
|
54
|
+
from meerschaum.connectors.sql._fetch import set_pipe_query, get_pipe_query
|
55
55
|
dash = attempt_import('dash', lazy=False, check_update=CHECK_UPDATE)
|
56
56
|
dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
|
57
57
|
dcc, html = import_dcc(check_update=CHECK_UPDATE), import_html(check_update=CHECK_UPDATE)
|
@@ -919,14 +919,31 @@ def update_pipe_sql_click(n_clicks, sql_editor_text):
|
|
919
919
|
success, msg = False, f"Unable to update SQL definition for {pipe}."
|
920
920
|
else:
|
921
921
|
try:
|
922
|
-
set_pipe_query(pipe, sql_editor_text)
|
923
|
-
success, msg = pipe.edit(debug=debug)
|
922
|
+
success, msg = set_pipe_query(pipe, sql_editor_text)
|
924
923
|
except Exception as e:
|
925
924
|
success, msg = False, f"Invalid SQL query:\n{e}"
|
926
925
|
|
927
926
|
return alert_from_success_tuple((success, msg))
|
928
927
|
|
929
928
|
|
929
|
+
@dash_app.callback(
|
930
|
+
Output({'type': 'sql-editor', 'index': MATCH}, 'value'),
|
931
|
+
Input({'type': 'resolve-symlinks-switch', 'index': MATCH}, 'value'),
|
932
|
+
prevent_initial_call=True,
|
933
|
+
)
|
934
|
+
def toggle_sql_symlinks(value):
|
935
|
+
triggered = dash.callback_context.triggered
|
936
|
+
if triggered[0]['value'] is None:
|
937
|
+
raise PreventUpdate
|
938
|
+
|
939
|
+
pipe = pipe_from_ctx(triggered, 'value')
|
940
|
+
if pipe is None:
|
941
|
+
raise PreventUpdate
|
942
|
+
|
943
|
+
query = get_pipe_query(pipe, apply_symlinks=value)
|
944
|
+
return query
|
945
|
+
|
946
|
+
|
930
947
|
@dash_app.callback(
|
931
948
|
Output({'type': 'sync-success-div', 'index': MATCH}, 'children'),
|
932
949
|
Input({'type': 'update-sync-button', 'index': MATCH}, 'n_clicks'),
|
meerschaum/api/dash/pipes.py
CHANGED
@@ -690,16 +690,32 @@ def accordion_items_from_pipe(
|
|
690
690
|
items_bodies['sql'] = html.Div([
|
691
691
|
sql_editor,
|
692
692
|
html.Br(),
|
693
|
-
dbc.Row(
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
693
|
+
dbc.Row(
|
694
|
+
[
|
695
|
+
dbc.Col(update_sql_button, width='auto'),
|
696
|
+
dbc.Col(
|
697
|
+
dbc.Switch(
|
698
|
+
label="Resolve symlinks",
|
699
|
+
value=True,
|
700
|
+
id={'type': 'resolve-symlinks-switch', 'index': pipe_meta_str},
|
701
|
+
),
|
702
|
+
width='auto',
|
703
|
+
),
|
699
704
|
],
|
700
|
-
|
701
|
-
|
702
|
-
|
705
|
+
justify='between',
|
706
|
+
align='center',
|
707
|
+
),
|
708
|
+
dbc.Row(
|
709
|
+
dbc.Col(
|
710
|
+
html.Div(
|
711
|
+
id={
|
712
|
+
'type': 'update-sql-success-div',
|
713
|
+
'index': pipe_meta_str,
|
714
|
+
},
|
715
|
+
),
|
716
|
+
width=True,
|
717
|
+
),
|
718
|
+
),
|
703
719
|
])
|
704
720
|
|
705
721
|
if 'recent-data' in active_items:
|
meerschaum/config/_version.py
CHANGED
@@ -69,6 +69,7 @@ def get_connector(
|
|
69
69
|
label: str = None,
|
70
70
|
refresh: bool = False,
|
71
71
|
debug: bool = False,
|
72
|
+
_load_plugins: bool = True,
|
72
73
|
**kw: Any
|
73
74
|
) -> Connector:
|
74
75
|
"""
|
@@ -122,11 +123,12 @@ def get_connector(
|
|
122
123
|
if isinstance(type, str) and not label and ':' in type:
|
123
124
|
type, label = type.split(':', maxsplit=1)
|
124
125
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
126
|
+
if _load_plugins:
|
127
|
+
with _locks['_loaded_plugin_connectors']:
|
128
|
+
if not _loaded_plugin_connectors:
|
129
|
+
load_plugin_connectors()
|
130
|
+
_load_builtin_custom_connectors()
|
131
|
+
_loaded_plugin_connectors = True
|
130
132
|
|
131
133
|
if type is None and label is None:
|
132
134
|
default_instance_keys = get_config('meerschaum', 'instance', patch=True)
|
@@ -337,12 +339,16 @@ def load_plugin_connectors():
|
|
337
339
|
for plugin in get_plugins():
|
338
340
|
if plugin is None:
|
339
341
|
continue
|
342
|
+
|
340
343
|
with open(plugin.__file__, encoding='utf-8') as f:
|
341
344
|
text = f.read()
|
345
|
+
|
342
346
|
if 'make_connector' in text or 'Connector' in text:
|
343
347
|
to_import.append(plugin.name)
|
348
|
+
|
344
349
|
if not to_import:
|
345
350
|
return
|
351
|
+
|
346
352
|
import_plugins(*to_import)
|
347
353
|
|
348
354
|
|
@@ -354,7 +360,7 @@ def unload_plugin_connectors(
|
|
354
360
|
Unload custom connectors added by plugins.
|
355
361
|
"""
|
356
362
|
from meerschaum.plugins import get_plugins_names
|
357
|
-
global custom_types, _known_custom_types, types, plugins_types, connectors
|
363
|
+
global custom_types, _known_custom_types, types, plugins_types, connectors, _loaded_plugin_connectors
|
358
364
|
|
359
365
|
plugin_names = plugin_names or get_plugins_names()
|
360
366
|
|
@@ -368,6 +374,7 @@ def unload_plugin_connectors(
|
|
368
374
|
|
369
375
|
custom_types.clear()
|
370
376
|
custom_types.update(_known_custom_types)
|
377
|
+
_loaded_plugin_connectors = False
|
371
378
|
|
372
379
|
|
373
380
|
def get_connector_plugin(
|
meerschaum/connectors/parse.py
CHANGED
@@ -119,11 +119,11 @@ def parse_repo_keys(keys: Optional[str] = None, **kw):
|
|
119
119
|
if not keys.startswith('api:'):
|
120
120
|
raise ValueError("Only APIConnectors may be treated as repositories.")
|
121
121
|
|
122
|
-
return parse_connector_keys(keys, **kw)
|
122
|
+
return parse_connector_keys(keys, _load_plugins=kw.pop('_load_plugins', False), **kw)
|
123
123
|
|
124
124
|
|
125
125
|
def parse_executor_keys(keys: Optional[str] = None, **kw):
|
126
|
-
"""Parse the executor keys into an APIConnector or string."""
|
126
|
+
"""Parse the executor keys into an APIConnector, SystemdConnector, or string."""
|
127
127
|
from meerschaum.jobs import get_executor_keys_from_context
|
128
128
|
if keys is None:
|
129
129
|
keys = get_executor_keys_from_context()
|
@@ -36,6 +36,7 @@ class SQLConnector(InstanceConnector):
|
|
36
36
|
exec_queries,
|
37
37
|
get_connection,
|
38
38
|
_cleanup_connections,
|
39
|
+
_init_geopackage_table,
|
39
40
|
)
|
40
41
|
from meerschaum.utils.sql import test_connection
|
41
42
|
from ._fetch import fetch, get_pipe_metadef
|
@@ -176,7 +177,7 @@ class SQLConnector(InstanceConnector):
|
|
176
177
|
**kw
|
177
178
|
)
|
178
179
|
|
179
|
-
if self.__dict__.get('flavor', None)
|
180
|
+
if self.__dict__.get('flavor', None) in ('sqlite', 'geopackage'):
|
180
181
|
self._reset_attributes()
|
181
182
|
self._set_attributes(
|
182
183
|
'sql',
|
@@ -187,7 +188,7 @@ class SQLConnector(InstanceConnector):
|
|
187
188
|
### For backwards compatability reasons, set the path for sql:local if its missing.
|
188
189
|
if self.label == 'local' and not self.__dict__.get('database', None):
|
189
190
|
from meerschaum.config._paths import SQLITE_DB_PATH
|
190
|
-
self.database =
|
191
|
+
self.database = SQLITE_DB_PATH.as_posix()
|
191
192
|
|
192
193
|
### ensure flavor and label are set accordingly
|
193
194
|
if 'flavor' not in self.__dict__:
|
@@ -291,7 +292,7 @@ class SQLConnector(InstanceConnector):
|
|
291
292
|
"""
|
292
293
|
if self.flavor in ('duckdb', 'oracle'):
|
293
294
|
return False
|
294
|
-
if self.flavor
|
295
|
+
if self.flavor in ('sqlite', 'geopackage'):
|
295
296
|
return ':memory:' not in self.URI
|
296
297
|
return True
|
297
298
|
|
@@ -24,6 +24,7 @@ flavor_clis = {
|
|
24
24
|
'mariadb' : 'mycli',
|
25
25
|
'percona' : 'mycli',
|
26
26
|
'sqlite' : 'litecli',
|
27
|
+
'geopackage' : 'litecli',
|
27
28
|
'mssql' : 'mssqlcli',
|
28
29
|
'duckdb' : 'gadwall',
|
29
30
|
}
|
@@ -43,6 +44,10 @@ def cli(
|
|
43
44
|
"""
|
44
45
|
from meerschaum.utils.warnings import dprint
|
45
46
|
from meerschaum.utils.venv import venv_exec
|
47
|
+
|
48
|
+
### Initialize the engine so that dependencies are resolved.
|
49
|
+
_ = self.engine
|
50
|
+
|
46
51
|
env = copy.deepcopy(dict(os.environ))
|
47
52
|
env_key = f"MRSM_SQL_{self.label.upper()}"
|
48
53
|
env_val = json.dumps(self.meta)
|
@@ -101,7 +106,7 @@ def _cli_exit(
|
|
101
106
|
### NOTE: The `DATABASE_URL` property must be initialized first in case the database is not
|
102
107
|
### yet defined (e.g. 'sql:local').
|
103
108
|
cli_arg_str = self.DATABASE_URL
|
104
|
-
if self.flavor in ('sqlite', 'duckdb'):
|
109
|
+
if self.flavor in ('sqlite', 'duckdb', 'geopackage'):
|
105
110
|
cli_arg_str = (
|
106
111
|
str(self.database)
|
107
112
|
if 'database' in self.__dict__
|
@@ -19,6 +19,7 @@ from meerschaum._internal.static import STATIC_CONFIG
|
|
19
19
|
flavor_configs = STATIC_CONFIG['sql']['create_engine_flavors']
|
20
20
|
install_flavor_drivers = {
|
21
21
|
'sqlite': ['aiosqlite'],
|
22
|
+
'geopackage': ['aiosqlite'],
|
22
23
|
'duckdb': ['duckdb', 'duckdb_engine'],
|
23
24
|
'mysql': ['pymysql'],
|
24
25
|
'mariadb': ['pymysql'],
|
@@ -91,7 +92,7 @@ def create_engine(
|
|
91
92
|
sqlalchemy.dialects.registry.register(*flavor_dialects[self.flavor])
|
92
93
|
|
93
94
|
### self._sys_config was deepcopied and can be updated safely
|
94
|
-
if self.flavor in ("sqlite", "duckdb"):
|
95
|
+
if self.flavor in ("sqlite", "duckdb", "geopackage"):
|
95
96
|
engine_str = f"{_engine}:///{_database}" if not _uri else _uri
|
96
97
|
if 'create_engine' not in self._sys_config:
|
97
98
|
self._sys_config['create_engine'] = {}
|
@@ -221,7 +221,11 @@ def get_pipe_metadef(
|
|
221
221
|
return meta_def.rstrip()
|
222
222
|
|
223
223
|
|
224
|
-
def get_pipe_query(
|
224
|
+
def get_pipe_query(
|
225
|
+
pipe: mrsm.Pipe,
|
226
|
+
apply_symlinks: bool = True,
|
227
|
+
warn: bool = True,
|
228
|
+
) -> Union[str, None]:
|
225
229
|
"""
|
226
230
|
Run through the possible keys for a pipe's query and return the first match.
|
227
231
|
|
@@ -233,14 +237,17 @@ def get_pipe_query(pipe: mrsm.Pipe, warn: bool = True) -> Union[str, None]:
|
|
233
237
|
import textwrap
|
234
238
|
from meerschaum.utils.warnings import warn as _warn
|
235
239
|
from meerschaum.utils.pipes import replace_pipes_syntax
|
236
|
-
|
237
|
-
|
240
|
+
|
241
|
+
parameters = pipe.get_parameters(apply_symlinks=apply_symlinks)
|
242
|
+
|
243
|
+
if parameters.get('fetch', {}).get('definition', None):
|
244
|
+
definition = parameters['fetch']['definition']
|
238
245
|
elif pipe.parameters.get('definition', None):
|
239
|
-
definition =
|
246
|
+
definition = parameters['definition']
|
240
247
|
elif pipe.parameters.get('query', None):
|
241
|
-
definition =
|
248
|
+
definition = parameters['query']
|
242
249
|
elif pipe.parameters.get('sql', None):
|
243
|
-
definition =
|
250
|
+
definition = parameters['sql']
|
244
251
|
else:
|
245
252
|
if warn:
|
246
253
|
_warn(
|
@@ -249,11 +256,12 @@ def get_pipe_query(pipe: mrsm.Pipe, warn: bool = True) -> Union[str, None]:
|
|
249
256
|
)
|
250
257
|
return None
|
251
258
|
|
252
|
-
|
259
|
+
if apply_symlinks:
|
260
|
+
definition = replace_pipes_syntax(definition)
|
253
261
|
return textwrap.dedent(definition.lstrip().rstrip())
|
254
262
|
|
255
263
|
|
256
|
-
def set_pipe_query(pipe: mrsm.Pipe, query: str) ->
|
264
|
+
def set_pipe_query(pipe: mrsm.Pipe, query: str) -> mrsm.SuccessTuple:
|
257
265
|
"""
|
258
266
|
Run through the possible keys for a pipe's query and set the first match.
|
259
267
|
|
@@ -262,22 +270,17 @@ def set_pipe_query(pipe: mrsm.Pipe, query: str) -> None:
|
|
262
270
|
- query
|
263
271
|
- sql
|
264
272
|
"""
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
elif '
|
271
|
-
|
272
|
-
key_to_set = 'definition'
|
273
|
-
elif 'query' in pipe.parameters:
|
274
|
-
dict_to_set = pipe.parameters
|
275
|
-
key_to_set = 'query'
|
273
|
+
parameters = pipe.get_parameters()
|
274
|
+
if 'fetch' in parameters and 'definition' in parameters['fetch']:
|
275
|
+
patch_dict = {'fetch': {'defintion': query}}
|
276
|
+
elif 'definition' in parameters:
|
277
|
+
patch_dict = {'definition': query}
|
278
|
+
elif 'query' in parameters:
|
279
|
+
patch_dict = {'query': query}
|
276
280
|
else:
|
277
|
-
|
278
|
-
key_to_set = 'sql'
|
281
|
+
patch_dict = {'sql': query}
|
279
282
|
|
280
|
-
|
283
|
+
return pipe.update_parameters(patch_dict)
|
281
284
|
|
282
285
|
|
283
286
|
def _simple_fetch_query(
|
@@ -706,7 +706,7 @@ def get_create_index_queries(
|
|
706
706
|
),
|
707
707
|
])
|
708
708
|
elif not autoincrement and primary_key in existing_cols_pd_types:
|
709
|
-
if self.flavor
|
709
|
+
if self.flavor in ('sqlite', 'geopackage'):
|
710
710
|
new_table_name = sql_item_name(
|
711
711
|
f'_new_{pipe.target}',
|
712
712
|
self.flavor,
|
@@ -888,7 +888,7 @@ def get_create_index_queries(
|
|
888
888
|
f"CREATE UNIQUE INDEX {unique_index_name} ON {_pipe_name} ({unique_index_cols_str})"
|
889
889
|
)
|
890
890
|
constraint_queries = [create_unique_index_query]
|
891
|
-
if self.flavor
|
891
|
+
if self.flavor not in ('sqlite', 'geopackage'):
|
892
892
|
constraint_queries.append(add_constraint_query)
|
893
893
|
if upsert and indices_cols_str:
|
894
894
|
index_queries[unique_index_name] = constraint_queries
|
@@ -977,7 +977,12 @@ def get_drop_index_queries(
|
|
977
977
|
if ix_unquoted.lower() not in existing_indices:
|
978
978
|
continue
|
979
979
|
|
980
|
-
if
|
980
|
+
if (
|
981
|
+
ix_key == 'unique'
|
982
|
+
and upsert
|
983
|
+
and self.flavor not in ('sqlite', 'geopackage')
|
984
|
+
and not is_hypertable
|
985
|
+
):
|
981
986
|
constraint_name_unquoted = ix_unquoted.replace('IX_', 'UQ_')
|
982
987
|
constraint_name = sql_item_name(constraint_name_unquoted, self.flavor)
|
983
988
|
constraint_or_index = (
|
@@ -1558,6 +1563,11 @@ def create_pipe_table_from_df(
|
|
1558
1563
|
get_create_schema_if_not_exists_queries,
|
1559
1564
|
)
|
1560
1565
|
from meerschaum.utils.dtypes.sql import get_db_type_from_pd_type
|
1566
|
+
if self.flavor == 'geopackage':
|
1567
|
+
init_success, init_msg = self._init_geopackage_table(df, pipe.target, debug=debug)
|
1568
|
+
if not init_success:
|
1569
|
+
return init_success, init_msg
|
1570
|
+
|
1561
1571
|
primary_key = pipe.columns.get('primary', None)
|
1562
1572
|
primary_key_typ = (
|
1563
1573
|
pipe.dtypes.get(primary_key, str(df.dtypes.get(primary_key, 'int')))
|
@@ -1614,6 +1624,9 @@ def create_pipe_table_from_df(
|
|
1614
1624
|
if success
|
1615
1625
|
else f"Failed to create {target_name}."
|
1616
1626
|
)
|
1627
|
+
if success and self.flavor == 'geopackage':
|
1628
|
+
return self._init_geopackage_table(df, target, debug=debug)
|
1629
|
+
|
1617
1630
|
return success, msg
|
1618
1631
|
|
1619
1632
|
|
@@ -3078,7 +3091,7 @@ def get_pipe_columns_types(
|
|
3078
3091
|
if not pipe.exists(debug=debug):
|
3079
3092
|
return {}
|
3080
3093
|
|
3081
|
-
if self.flavor not in ('oracle', 'mysql', 'mariadb', 'sqlite'):
|
3094
|
+
if self.flavor not in ('oracle', 'mysql', 'mariadb', 'sqlite', 'geopackage'):
|
3082
3095
|
return get_table_cols_types(
|
3083
3096
|
pipe.target,
|
3084
3097
|
self,
|
@@ -3436,7 +3449,7 @@ def get_alter_columns_queries(
|
|
3436
3449
|
for col, (db_typ, typ) in altered_cols.items()
|
3437
3450
|
}
|
3438
3451
|
|
3439
|
-
if self.flavor
|
3452
|
+
if self.flavor in ('sqlite', 'geopackage'):
|
3440
3453
|
temp_table_name = '-' + session_id + '_' + target
|
3441
3454
|
rename_query = (
|
3442
3455
|
"ALTER TABLE "
|
@@ -3884,7 +3897,7 @@ def get_pipe_schema(self, pipe: mrsm.Pipe) -> Union[str, None]:
|
|
3884
3897
|
-------
|
3885
3898
|
A schema string or `None` if nothing is configured.
|
3886
3899
|
"""
|
3887
|
-
if self.flavor
|
3900
|
+
if self.flavor in ('sqlite', 'geopackage'):
|
3888
3901
|
return self.schema
|
3889
3902
|
return pipe.parameters.get('schema', self.schema)
|
3890
3903
|
|
@@ -27,7 +27,7 @@ _bulk_flavors = {
|
|
27
27
|
}
|
28
28
|
### flavors that do not support chunks
|
29
29
|
_disallow_chunks_flavors = ['duckdb']
|
30
|
-
_max_chunks_flavors = {'sqlite': 1000}
|
30
|
+
_max_chunks_flavors = {'sqlite': 1000, 'geopackage': 1000}
|
31
31
|
SKIP_READ_TRANSACTION_FLAVORS: list[str] = ['mssql']
|
32
32
|
|
33
33
|
|
@@ -1347,3 +1347,33 @@ def _cleanup_connections(self) -> None:
|
|
1347
1347
|
connection.close()
|
1348
1348
|
except Exception:
|
1349
1349
|
pass
|
1350
|
+
|
1351
|
+
|
1352
|
+
def _init_geopackage_table(
|
1353
|
+
self,
|
1354
|
+
df: 'pd.DataFrame',
|
1355
|
+
table: str,
|
1356
|
+
debug: bool = False,
|
1357
|
+
) -> SuccessTuple:
|
1358
|
+
"""
|
1359
|
+
Initialize the geopackage schema tables from a DataFrame.
|
1360
|
+
"""
|
1361
|
+
import pathlib
|
1362
|
+
database = self.__dict__.get('database', self.parse_uri(self.URI).get('database', None))
|
1363
|
+
if not database:
|
1364
|
+
return False, f"Could not determine database for '{self}'."
|
1365
|
+
|
1366
|
+
database_path = pathlib.Path(database)
|
1367
|
+
mode = 'w' if not database_path.exists() else 'a'
|
1368
|
+
|
1369
|
+
try:
|
1370
|
+
df.head(0).to_file(
|
1371
|
+
database_path.as_posix(),
|
1372
|
+
layer=table,
|
1373
|
+
driver='GPKG',
|
1374
|
+
index=False,
|
1375
|
+
mode=mode,
|
1376
|
+
)
|
1377
|
+
except Exception as e:
|
1378
|
+
return False, f"Failed to init table '{table}':\n{e}"
|
1379
|
+
return True, "Success"
|
@@ -51,7 +51,7 @@ def from_uri(
|
|
51
51
|
if 'database' not in params:
|
52
52
|
error("Unable to determine the database from the provided URI.")
|
53
53
|
|
54
|
-
if flavor in ('sqlite', 'duckdb'):
|
54
|
+
if flavor in ('sqlite', 'duckdb', 'geopackage'):
|
55
55
|
if params['database'] == ':memory:':
|
56
56
|
params['label'] = label or f'memory_{flavor}'
|
57
57
|
else:
|
@@ -74,7 +74,7 @@ def get_tables(
|
|
74
74
|
cache_expired = refresh or (
|
75
75
|
(
|
76
76
|
_check_create_cache(conn, debug=debug)
|
77
|
-
if conn.flavor
|
77
|
+
if conn.flavor not in ('sqlite', 'duckdb', 'geopackage')
|
78
78
|
else True
|
79
79
|
)
|
80
80
|
if conn.type == 'sql'
|
@@ -264,7 +264,7 @@ def get_tables(
|
|
264
264
|
|
265
265
|
_write_create_cache(mrsm.get_connector(str(mrsm_instance)), debug=debug)
|
266
266
|
|
267
|
-
if conn.flavor
|
267
|
+
if conn.flavor not in ('sqlite', 'duckdb', 'geopackage'):
|
268
268
|
with open(pickle_path, 'wb') as f:
|
269
269
|
pickle.dump(conn.metadata, f)
|
270
270
|
|
@@ -155,7 +155,7 @@ def columns(self) -> Union[Dict[str, str], None]:
|
|
155
155
|
cols = self.parameters.get('columns', {})
|
156
156
|
if not isinstance(cols, dict):
|
157
157
|
return {}
|
158
|
-
return {col_ix: col for col_ix, col in cols.items() if col}
|
158
|
+
return {col_ix: col for col_ix, col in cols.items() if col and col_ix}
|
159
159
|
|
160
160
|
|
161
161
|
@columns.setter
|
@@ -776,7 +776,13 @@ class Plugin:
|
|
776
776
|
return [_d for _d in _deps if not _d.startswith('plugin:')]
|
777
777
|
|
778
778
|
|
779
|
-
def activate_venv(
|
779
|
+
def activate_venv(
|
780
|
+
self,
|
781
|
+
dependencies: bool = True,
|
782
|
+
init_if_not_exists: bool = True,
|
783
|
+
debug: bool = False,
|
784
|
+
**kw
|
785
|
+
) -> bool:
|
780
786
|
"""
|
781
787
|
Activate the virtual environments for the plugin and its dependencies.
|
782
788
|
|
@@ -796,7 +802,7 @@ class Plugin:
|
|
796
802
|
|
797
803
|
if dependencies:
|
798
804
|
for plugin in self.get_required_plugins(debug=debug):
|
799
|
-
plugin.activate_venv(debug=debug, **kw)
|
805
|
+
plugin.activate_venv(debug=debug, init_if_not_exists=init_if_not_exists, **kw)
|
800
806
|
|
801
807
|
vtp = venv_target_path(self.name, debug=debug, allow_nonexistent=True)
|
802
808
|
venv_meerschaum_path = vtp / 'meerschaum'
|
@@ -812,7 +818,7 @@ class Plugin:
|
|
812
818
|
if not success:
|
813
819
|
warn(f"Unable to create symlink {venv_meerschaum_path} to {PACKAGE_ROOT_PATH}:\n{msg}")
|
814
820
|
|
815
|
-
return activate_venv(self.name, debug=debug, **kw)
|
821
|
+
return activate_venv(self.name, init_if_not_exists=init_if_not_exists, debug=debug, **kw)
|
816
822
|
|
817
823
|
|
818
824
|
def deactivate_venv(self, dependencies: bool=True, debug: bool = False, **kw) -> bool:
|
meerschaum/plugins/__init__.py
CHANGED
@@ -13,7 +13,7 @@ import functools
|
|
13
13
|
|
14
14
|
import meerschaum as mrsm
|
15
15
|
from meerschaum.utils.typing import Callable, Any, Union, Optional, Dict, List, Tuple
|
16
|
-
from meerschaum.utils.threading import
|
16
|
+
from meerschaum.utils.threading import RLock
|
17
17
|
from meerschaum.core.Plugin import Plugin
|
18
18
|
|
19
19
|
_api_plugins: Dict[str, List[Callable[['fastapi.App'], Any]]] = {}
|
@@ -573,7 +573,7 @@ def import_plugins(
|
|
573
573
|
for plugin_name in flatten_list(plugins_to_import):
|
574
574
|
plugin = Plugin(plugin_name)
|
575
575
|
try:
|
576
|
-
with Venv(plugin):
|
576
|
+
with Venv(plugin, init_if_not_exists=False):
|
577
577
|
imported_plugins.append(
|
578
578
|
importlib.import_module(
|
579
579
|
f'{PLUGINS_RESOURCES_PATH.stem}.{plugin_name}'
|
@@ -760,7 +760,7 @@ def unload_custom_actions(plugins: Optional[List[str]] = None, debug: bool = Fal
|
|
760
760
|
from meerschaum._internal.entry import _shell
|
761
761
|
import meerschaum._internal.shell as shell_pkg
|
762
762
|
|
763
|
-
plugins = plugins
|
763
|
+
plugins = plugins if plugins is not None else list(_plugins_actions)
|
764
764
|
|
765
765
|
for plugin in plugins:
|
766
766
|
action_names = _plugins_actions.get(plugin, [])
|
@@ -798,7 +798,8 @@ def unload_plugins(
|
|
798
798
|
_loaded_plugins = False
|
799
799
|
_synced_symlinks = False
|
800
800
|
|
801
|
-
|
801
|
+
all_plugins = get_plugins_names()
|
802
|
+
plugins = plugins if plugins is not None else all_plugins
|
802
803
|
if debug:
|
803
804
|
dprint(f"Unloading plugins: {plugins}")
|
804
805
|
|
@@ -808,12 +809,26 @@ def unload_plugins(
|
|
808
809
|
module_prefix = f"{PLUGINS_RESOURCES_PATH.stem}."
|
809
810
|
loaded_modules = [mod_name for mod_name in sys.modules if mod_name.startswith(module_prefix)]
|
810
811
|
|
811
|
-
|
812
|
+
root_plugins_mod = (
|
813
|
+
sys.modules.get(PLUGINS_RESOURCES_PATH.stem, None)
|
814
|
+
if sorted(plugins) != sorted(all_plugins)
|
815
|
+
else sys.modules.pop(PLUGINS_RESOURCES_PATH, None)
|
816
|
+
)
|
817
|
+
|
812
818
|
for plugin_name in plugins:
|
813
819
|
for mod_name in loaded_modules:
|
814
|
-
if
|
820
|
+
if (
|
821
|
+
mod_name[len(PLUGINS_RESOURCES_PATH.stem):].startswith(plugin_name + '.')
|
822
|
+
or mod_name[len(PLUGINS_RESOURCES_PATH.stem):] == plugin_name
|
823
|
+
):
|
815
824
|
_ = sys.modules.pop(mod_name, None)
|
816
825
|
|
826
|
+
if root_plugins_mod is not None and plugin_name in root_plugins_mod.__dict__:
|
827
|
+
try:
|
828
|
+
delattr(root_plugins_mod, plugin_name)
|
829
|
+
except Exception:
|
830
|
+
pass
|
831
|
+
|
817
832
|
### Unload sync hooks.
|
818
833
|
_ = _pre_sync_hooks.pop(plugin_name, None)
|
819
834
|
_ = _post_sync_hooks.pop(plugin_name, None)
|
@@ -896,7 +911,9 @@ def get_plugins(*to_load, try_import: bool = True) -> Union[Tuple[Plugin], Plugi
|
|
896
911
|
(
|
897
912
|
name if (PLUGINS_RESOURCES_PATH / name).is_dir()
|
898
913
|
else name[:-3]
|
899
|
-
)
|
914
|
+
)
|
915
|
+
for name in os.listdir(PLUGINS_RESOURCES_PATH)
|
916
|
+
if name != '__init__.py'
|
900
917
|
]
|
901
918
|
)
|
902
919
|
]
|
meerschaum/utils/_get_pipes.py
CHANGED
@@ -38,6 +38,7 @@ def get_pipes(
|
|
38
38
|
method: str = 'registered',
|
39
39
|
workers: Optional[int] = None,
|
40
40
|
debug: bool = False,
|
41
|
+
_cache_parameters: bool = True,
|
41
42
|
**kw: Any
|
42
43
|
) -> Union[PipesDict, List[mrsm.Pipe], Dict[str, mrsm.Pipe]]:
|
43
44
|
"""
|
@@ -94,7 +95,6 @@ def get_pipes(
|
|
94
95
|
|
95
96
|
**kw: Any:
|
96
97
|
Keyword arguments to pass to the `meerschaum.Pipe` constructor.
|
97
|
-
|
98
98
|
|
99
99
|
Returns
|
100
100
|
-------
|
@@ -209,7 +209,7 @@ def get_pipes(
|
|
209
209
|
pipe_tags_or_parameters
|
210
210
|
if isinstance(pipe_tags_or_parameters, list)
|
211
211
|
else (
|
212
|
-
pipe_tags_or_parameters.get('tags',
|
212
|
+
pipe_tags_or_parameters.get('tags', [])
|
213
213
|
if isinstance(pipe_tags_or_parameters, dict)
|
214
214
|
else None
|
215
215
|
)
|