meerschaum 2.7.4__py3-none-any.whl → 2.7.6__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- meerschaum/_internal/shell/Shell.py +4 -6
- meerschaum/_internal/shell/ShellCompleter.py +6 -5
- meerschaum/actions/clear.py +6 -3
- meerschaum/actions/copy.py +33 -27
- meerschaum/actions/sql.py +14 -4
- meerschaum/actions/sync.py +22 -18
- meerschaum/api/dash/pipes.py +2 -3
- meerschaum/config/_default.py +11 -0
- meerschaum/config/_version.py +1 -1
- meerschaum/connectors/api/_misc.py +3 -2
- meerschaum/connectors/api/_pipes.py +8 -9
- meerschaum/connectors/sql/_SQLConnector.py +1 -0
- meerschaum/connectors/sql/_cli.py +18 -12
- meerschaum/connectors/sql/_create_engine.py +1 -0
- meerschaum/connectors/sql/_pipes.py +51 -14
- meerschaum/connectors/sql/_sql.py +109 -16
- meerschaum/jobs/_Job.py +1 -0
- meerschaum/plugins/__init__.py +7 -3
- meerschaum/utils/daemon/Daemon.py +11 -3
- meerschaum/utils/daemon/__init__.py +2 -2
- meerschaum/utils/misc.py +7 -6
- meerschaum/utils/packages/__init__.py +35 -28
- meerschaum/utils/packages/_packages.py +1 -1
- meerschaum/utils/prompt.py +54 -36
- meerschaum/utils/venv/_Venv.py +6 -1
- meerschaum/utils/venv/__init__.py +32 -16
- {meerschaum-2.7.4.dist-info → meerschaum-2.7.6.dist-info}/METADATA +4 -4
- {meerschaum-2.7.4.dist-info → meerschaum-2.7.6.dist-info}/RECORD +34 -34
- {meerschaum-2.7.4.dist-info → meerschaum-2.7.6.dist-info}/WHEEL +1 -1
- {meerschaum-2.7.4.dist-info → meerschaum-2.7.6.dist-info}/LICENSE +0 -0
- {meerschaum-2.7.4.dist-info → meerschaum-2.7.6.dist-info}/NOTICE +0 -0
- {meerschaum-2.7.4.dist-info → meerschaum-2.7.6.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.7.4.dist-info → meerschaum-2.7.6.dist-info}/top_level.txt +0 -0
- {meerschaum-2.7.4.dist-info → meerschaum-2.7.6.dist-info}/zip-safe +0 -0
@@ -6,8 +6,10 @@ This module contains SQLConnector functions for executing SQL queries.
|
|
6
6
|
"""
|
7
7
|
|
8
8
|
from __future__ import annotations
|
9
|
+
|
10
|
+
import meerschaum as mrsm
|
9
11
|
from meerschaum.utils.typing import (
|
10
|
-
Union,
|
12
|
+
Union, List, Dict, SuccessTuple, Optional, Any, Iterable, Callable,
|
11
13
|
Tuple, Hashable,
|
12
14
|
)
|
13
15
|
|
@@ -15,7 +17,7 @@ from meerschaum.utils.debug import dprint
|
|
15
17
|
from meerschaum.utils.warnings import warn
|
16
18
|
|
17
19
|
### database flavors that can use bulk insert
|
18
|
-
_bulk_flavors = {'postgresql', 'timescaledb', 'citus'}
|
20
|
+
_bulk_flavors = {'postgresql', 'timescaledb', 'citus', 'mssql'}
|
19
21
|
### flavors that do not support chunks
|
20
22
|
_disallow_chunks_flavors = ['duckdb']
|
21
23
|
_max_chunks_flavors = {'sqlite': 1000}
|
@@ -776,9 +778,11 @@ def to_sql(
|
|
776
778
|
import time
|
777
779
|
import json
|
778
780
|
from decimal import Decimal
|
781
|
+
from datetime import timedelta
|
779
782
|
from meerschaum.utils.warnings import error, warn
|
780
783
|
import warnings
|
781
784
|
import functools
|
785
|
+
|
782
786
|
if name is None:
|
783
787
|
error(f"Name must not be `None` to insert data into {self}.")
|
784
788
|
|
@@ -805,6 +809,7 @@ def to_sql(
|
|
805
809
|
quantize_decimal,
|
806
810
|
coerce_timezone,
|
807
811
|
encode_bytes_for_bytea,
|
812
|
+
serialize_bytes,
|
808
813
|
)
|
809
814
|
from meerschaum.utils.dtypes.sql import (
|
810
815
|
NUMERIC_PRECISION_FLAVORS,
|
@@ -812,6 +817,7 @@ def to_sql(
|
|
812
817
|
PD_TO_SQLALCHEMY_DTYPES_FLAVORS,
|
813
818
|
get_db_type_from_pd_type,
|
814
819
|
)
|
820
|
+
from meerschaum.utils.misc import interval_str
|
815
821
|
from meerschaum.connectors.sql._create_engine import flavor_configs
|
816
822
|
from meerschaum.utils.packages import attempt_import, import_pandas
|
817
823
|
sqlalchemy = attempt_import('sqlalchemy', debug=debug)
|
@@ -821,24 +827,36 @@ def to_sql(
|
|
821
827
|
bytes_cols = get_bytes_cols(df)
|
822
828
|
numeric_cols = get_numeric_cols(df)
|
823
829
|
|
824
|
-
|
830
|
+
enable_bulk_insert = mrsm.get_config(
|
831
|
+
'system', 'connectors', 'sql', 'bulk_insert'
|
832
|
+
).get(self.flavor, False)
|
833
|
+
stats = {'target': name}
|
825
834
|
### resort to defaults if None
|
826
835
|
copied = False
|
827
|
-
|
836
|
+
use_bulk_insert = False
|
828
837
|
if method == "":
|
829
|
-
if
|
830
|
-
method =
|
831
|
-
|
838
|
+
if enable_bulk_insert:
|
839
|
+
method = (
|
840
|
+
functools.partial(mssql_insert_json, debug=debug)
|
841
|
+
if self.flavor == 'mssql'
|
842
|
+
else functools.partial(psql_insert_copy, debug=debug)
|
843
|
+
)
|
844
|
+
use_bulk_insert = True
|
832
845
|
else:
|
833
846
|
### Should resolve to 'multi' or `None`.
|
834
847
|
method = flavor_configs.get(self.flavor, {}).get('to_sql', {}).get('method', 'multi')
|
835
848
|
|
836
|
-
if bytes_cols and (
|
849
|
+
if bytes_cols and (use_bulk_insert or self.flavor == 'oracle'):
|
837
850
|
if safe_copy and not copied:
|
838
851
|
df = df.copy()
|
839
852
|
copied = True
|
853
|
+
bytes_serializer = (
|
854
|
+
functools.partial(encode_bytes_for_bytea, with_prefix=(self.flavor != 'oracle'))
|
855
|
+
if self.flavor != 'mssql'
|
856
|
+
else serialize_bytes
|
857
|
+
)
|
840
858
|
for col in bytes_cols:
|
841
|
-
df[col] = df[col].apply(
|
859
|
+
df[col] = df[col].apply(bytes_serializer)
|
842
860
|
|
843
861
|
if self.flavor in NUMERIC_AS_TEXT_FLAVORS:
|
844
862
|
if safe_copy and not copied:
|
@@ -982,13 +1000,19 @@ def to_sql(
|
|
982
1000
|
|
983
1001
|
end = time.perf_counter()
|
984
1002
|
if success:
|
985
|
-
|
1003
|
+
num_rows = len(df)
|
1004
|
+
msg = (
|
1005
|
+
f"It took {interval_str(timedelta(seconds=(end - start)))} "
|
1006
|
+
+ f"to sync {num_rows:,} row"
|
1007
|
+
+ ('s' if num_rows != 1 else '')
|
1008
|
+
+ f" to {name}."
|
1009
|
+
)
|
986
1010
|
stats['start'] = start
|
987
1011
|
stats['end'] = end
|
988
1012
|
stats['duration'] = end - start
|
989
1013
|
|
990
1014
|
if debug:
|
991
|
-
print(
|
1015
|
+
print(" done.", flush=True)
|
992
1016
|
dprint(msg)
|
993
1017
|
|
994
1018
|
stats['success'] = success
|
@@ -1005,7 +1029,7 @@ def psql_insert_copy(
|
|
1005
1029
|
conn: Union[sqlalchemy.engine.Engine, sqlalchemy.engine.Connection],
|
1006
1030
|
keys: List[str],
|
1007
1031
|
data_iter: Iterable[Any],
|
1008
|
-
|
1032
|
+
debug: bool = False,
|
1009
1033
|
) -> None:
|
1010
1034
|
"""
|
1011
1035
|
Execute SQL statement inserting data for PostgreSQL.
|
@@ -1022,18 +1046,15 @@ def psql_insert_copy(
|
|
1022
1046
|
data_iter: Iterable[Any]
|
1023
1047
|
Iterable that iterates the values to be inserted
|
1024
1048
|
|
1025
|
-
schema: Optional[str], default None
|
1026
|
-
Optionally specify the schema of the table to be inserted into.
|
1027
|
-
|
1028
1049
|
Returns
|
1029
1050
|
-------
|
1030
1051
|
None
|
1031
1052
|
"""
|
1032
1053
|
import csv
|
1033
|
-
from io import StringIO
|
1034
1054
|
import json
|
1035
1055
|
|
1036
1056
|
from meerschaum.utils.sql import sql_item_name
|
1057
|
+
from meerschaum.utils.warnings import dprint
|
1037
1058
|
|
1038
1059
|
### NOTE: PostgreSQL doesn't support NUL chars in text, so they're removed from strings.
|
1039
1060
|
data_iter = (
|
@@ -1057,6 +1078,8 @@ def psql_insert_copy(
|
|
1057
1078
|
table_name = sql_item_name(table.name, 'postgresql', table.schema)
|
1058
1079
|
columns = ', '.join(f'"{k}"' for k in keys)
|
1059
1080
|
sql = f"COPY {table_name} ({columns}) FROM STDIN WITH CSV NULL '\\N'"
|
1081
|
+
if debug:
|
1082
|
+
dprint(sql)
|
1060
1083
|
|
1061
1084
|
dbapi_conn = conn.connection
|
1062
1085
|
with dbapi_conn.cursor() as cur:
|
@@ -1065,6 +1088,76 @@ def psql_insert_copy(
|
|
1065
1088
|
writer.writerows(data_iter)
|
1066
1089
|
|
1067
1090
|
|
1091
|
+
def mssql_insert_json(
|
1092
|
+
table: pandas.io.sql.SQLTable,
|
1093
|
+
conn: Union[sqlalchemy.engine.Engine, sqlalchemy.engine.Connection],
|
1094
|
+
keys: List[str],
|
1095
|
+
data_iter: Iterable[Any],
|
1096
|
+
cols_types: Optional[Dict[str, str]] = None,
|
1097
|
+
debug: bool = False,
|
1098
|
+
):
|
1099
|
+
"""
|
1100
|
+
Execute SQL statement inserting data via OPENJSON.
|
1101
|
+
|
1102
|
+
Adapted from this snippet:
|
1103
|
+
https://gist.github.com/gordthompson/1fb0f1c3f5edbf6192e596de8350f205
|
1104
|
+
|
1105
|
+
Parameters
|
1106
|
+
----------
|
1107
|
+
table: pandas.io.sql.SQLTable
|
1108
|
+
|
1109
|
+
conn: Union[sqlalchemy.engine.Engine, sqlalchemy.engine.Connection]
|
1110
|
+
|
1111
|
+
keys: List[str]
|
1112
|
+
Column names
|
1113
|
+
|
1114
|
+
data_iter: Iterable[Any]
|
1115
|
+
Iterable that iterates the values to be inserted
|
1116
|
+
|
1117
|
+
cols_types: Optional[Dict[str, str]], default None
|
1118
|
+
If provided, use these as the columns and types for the table.
|
1119
|
+
|
1120
|
+
Returns
|
1121
|
+
-------
|
1122
|
+
None
|
1123
|
+
"""
|
1124
|
+
import json
|
1125
|
+
from meerschaum.utils.sql import sql_item_name
|
1126
|
+
from meerschaum.utils.dtypes.sql import get_pd_type_from_db_type, get_db_type_from_pd_type
|
1127
|
+
from meerschaum.utils.warnings import dprint
|
1128
|
+
table_name = sql_item_name(table.name, 'mssql', table.schema)
|
1129
|
+
if not cols_types:
|
1130
|
+
pd_types = {
|
1131
|
+
str(column.name): get_pd_type_from_db_type(str(column.type))
|
1132
|
+
for column in table.table.columns
|
1133
|
+
}
|
1134
|
+
cols_types = {
|
1135
|
+
col: get_db_type_from_pd_type(typ, 'mssql')
|
1136
|
+
for col, typ in pd_types.items()
|
1137
|
+
}
|
1138
|
+
columns = ",\n ".join([f"[{k}]" for k in keys])
|
1139
|
+
json_data = [dict(zip(keys, row)) for row in data_iter]
|
1140
|
+
with_clause = ",\n ".join(
|
1141
|
+
[
|
1142
|
+
f"[{col_name}] {col_type} '$.\"{col_name}\"'"
|
1143
|
+
for col_name, col_type in cols_types.items()
|
1144
|
+
]
|
1145
|
+
)
|
1146
|
+
placeholder = "?" if conn.dialect.paramstyle == "qmark" else "%s"
|
1147
|
+
sql = (
|
1148
|
+
f"INSERT INTO {table_name} (\n {columns}\n)\n"
|
1149
|
+
f"SELECT\n {columns}\n"
|
1150
|
+
f"FROM OPENJSON({placeholder})\n"
|
1151
|
+
"WITH (\n"
|
1152
|
+
f" {with_clause}\n"
|
1153
|
+
");"
|
1154
|
+
)
|
1155
|
+
if debug:
|
1156
|
+
dprint(sql)
|
1157
|
+
|
1158
|
+
conn.exec_driver_sql(sql, (json.dumps(json_data, default=str),))
|
1159
|
+
|
1160
|
+
|
1068
1161
|
def format_sql_query_for_dask(query: str) -> 'sqlalchemy.sql.selectable.Select':
|
1069
1162
|
"""
|
1070
1163
|
Given a `SELECT` query, return a `sqlalchemy` query for Dask to use.
|
meerschaum/jobs/_Job.py
CHANGED
meerschaum/plugins/__init__.py
CHANGED
@@ -126,8 +126,8 @@ def pre_sync_hook(
|
|
126
126
|
|
127
127
|
|
128
128
|
def post_sync_hook(
|
129
|
-
|
130
|
-
|
129
|
+
function: Callable[[Any], Any],
|
130
|
+
) -> Callable[[Any], Any]:
|
131
131
|
"""
|
132
132
|
Register a function as a sync hook to be executed upon completion of a sync.
|
133
133
|
|
@@ -143,10 +143,14 @@ def post_sync_hook(
|
|
143
143
|
Examples
|
144
144
|
--------
|
145
145
|
>>> from meerschaum.plugins import post_sync_hook
|
146
|
+
>>> from meerschaum.utils.misc import interval_str
|
147
|
+
>>> from datetime import timedelta
|
146
148
|
>>>
|
147
149
|
>>> @post_sync_hook
|
148
150
|
... def log_sync(pipe, success_tuple, duration=None, **kwargs):
|
149
|
-
...
|
151
|
+
... duration_delta = timedelta(seconds=duration)
|
152
|
+
... duration_text = interval_str(duration_delta)
|
153
|
+
... print(f"It took {duration_text} to sync {pipe}.")
|
150
154
|
>>>
|
151
155
|
"""
|
152
156
|
with _locks['_post_sync_hooks']:
|
@@ -261,7 +261,10 @@ class Daemon:
|
|
261
261
|
-------
|
262
262
|
Nothing — this will exit the parent process.
|
263
263
|
"""
|
264
|
-
import platform
|
264
|
+
import platform
|
265
|
+
import sys
|
266
|
+
import os
|
267
|
+
import traceback
|
265
268
|
from meerschaum.utils.warnings import warn
|
266
269
|
from meerschaum.config import get_config
|
267
270
|
daemon = attempt_import('daemon')
|
@@ -1014,7 +1017,8 @@ class Daemon:
|
|
1014
1017
|
|
1015
1018
|
def read_pickle(self) -> Daemon:
|
1016
1019
|
"""Read a Daemon's pickle file and return the `Daemon`."""
|
1017
|
-
import pickle
|
1020
|
+
import pickle
|
1021
|
+
import traceback
|
1018
1022
|
if not self.pickle_path.exists():
|
1019
1023
|
error(f"Pickle file does not exist for daemon '{self.daemon_id}'.")
|
1020
1024
|
|
@@ -1050,6 +1054,9 @@ class Daemon:
|
|
1050
1054
|
if self._properties is None:
|
1051
1055
|
self._properties = {}
|
1052
1056
|
|
1057
|
+
if self._properties.get('result', None) is None:
|
1058
|
+
_ = self._properties.pop('result', None)
|
1059
|
+
|
1053
1060
|
if _file_properties is not None:
|
1054
1061
|
self._properties = apply_patch_to_config(
|
1055
1062
|
_file_properties,
|
@@ -1085,7 +1092,8 @@ class Daemon:
|
|
1085
1092
|
|
1086
1093
|
def write_pickle(self) -> SuccessTuple:
|
1087
1094
|
"""Write the pickle file for the daemon."""
|
1088
|
-
import pickle
|
1095
|
+
import pickle
|
1096
|
+
import traceback
|
1089
1097
|
try:
|
1090
1098
|
self.path.mkdir(parents=True, exist_ok=True)
|
1091
1099
|
with open(self.pickle_path, 'wb+') as pickle_file:
|
@@ -37,7 +37,7 @@ def daemon_entry(sysargs: Optional[List[str]] = None) -> SuccessTuple:
|
|
37
37
|
filtered_sysargs = [arg for arg in sysargs if arg not in ('-d', '--daemon')]
|
38
38
|
try:
|
39
39
|
label = shlex.join(filtered_sysargs) if sysargs else None
|
40
|
-
except Exception
|
40
|
+
except Exception:
|
41
41
|
label = ' '.join(filtered_sysargs) if sysargs else None
|
42
42
|
|
43
43
|
name = _args.get('name', None)
|
@@ -45,7 +45,7 @@ def daemon_entry(sysargs: Optional[List[str]] = None) -> SuccessTuple:
|
|
45
45
|
if name:
|
46
46
|
try:
|
47
47
|
daemon = Daemon(daemon_id=name)
|
48
|
-
except Exception
|
48
|
+
except Exception:
|
49
49
|
daemon = None
|
50
50
|
|
51
51
|
if daemon is not None:
|
meerschaum/utils/misc.py
CHANGED
@@ -90,20 +90,21 @@ def add_method_to_class(
|
|
90
90
|
|
91
91
|
|
92
92
|
def generate_password(length: int = 12) -> str:
|
93
|
-
"""
|
93
|
+
"""
|
94
|
+
Generate a secure password of given length.
|
94
95
|
|
95
96
|
Parameters
|
96
97
|
----------
|
97
|
-
length
|
98
|
+
length: int, default 12
|
98
99
|
The length of the password.
|
99
100
|
|
100
101
|
Returns
|
101
102
|
-------
|
102
103
|
A random password string.
|
103
|
-
|
104
104
|
"""
|
105
|
-
import secrets
|
106
|
-
|
105
|
+
import secrets
|
106
|
+
import string
|
107
|
+
return ''.join((secrets.choice(string.ascii_letters + string.digits) for i in range(length)))
|
107
108
|
|
108
109
|
def is_int(s : str) -> bool:
|
109
110
|
"""
|
@@ -121,7 +122,7 @@ def is_int(s : str) -> bool:
|
|
121
122
|
"""
|
122
123
|
try:
|
123
124
|
float(s)
|
124
|
-
except Exception
|
125
|
+
except Exception:
|
125
126
|
return False
|
126
127
|
|
127
128
|
return float(s).is_integer()
|
@@ -337,14 +337,14 @@ def get_install_no_version(install_name: str) -> str:
|
|
337
337
|
|
338
338
|
import_versions = {}
|
339
339
|
def determine_version(
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
340
|
+
path: pathlib.Path,
|
341
|
+
import_name: Optional[str] = None,
|
342
|
+
venv: Optional[str] = 'mrsm',
|
343
|
+
search_for_metadata: bool = True,
|
344
|
+
split: bool = True,
|
345
|
+
warn: bool = False,
|
346
|
+
debug: bool = False,
|
347
|
+
) -> Union[str, None]:
|
348
348
|
"""
|
349
349
|
Determine a module's `__version__` string from its filepath.
|
350
350
|
|
@@ -381,11 +381,8 @@ def determine_version(
|
|
381
381
|
with _locks['import_versions']:
|
382
382
|
if venv not in import_versions:
|
383
383
|
import_versions[venv] = {}
|
384
|
-
import
|
385
|
-
import re, os
|
384
|
+
import os
|
386
385
|
old_cwd = os.getcwd()
|
387
|
-
if debug:
|
388
|
-
from meerschaum.utils.debug import dprint
|
389
386
|
from meerschaum.utils.warnings import warn as warn_function
|
390
387
|
if import_name is None:
|
391
388
|
import_name = path.parent.stem if path.stem == '__init__' else path.stem
|
@@ -395,7 +392,10 @@ def determine_version(
|
|
395
392
|
_version = None
|
396
393
|
module_parent_dir = (
|
397
394
|
path.parent.parent if path.stem == '__init__' else path.parent
|
398
|
-
) if path is not None else venv_target_path(venv, debug=debug)
|
395
|
+
) if path is not None else venv_target_path(venv, allow_nonexistent=True, debug=debug)
|
396
|
+
|
397
|
+
if not module_parent_dir.exists():
|
398
|
+
return None
|
399
399
|
|
400
400
|
installed_dir_name = _import_to_dir_name(import_name)
|
401
401
|
clean_installed_dir_name = installed_dir_name.lower().replace('-', '_')
|
@@ -403,7 +403,11 @@ def determine_version(
|
|
403
403
|
### First, check if a dist-info directory exists.
|
404
404
|
_found_versions = []
|
405
405
|
if search_for_metadata:
|
406
|
-
|
406
|
+
try:
|
407
|
+
filenames = os.listdir(module_parent_dir)
|
408
|
+
except FileNotFoundError:
|
409
|
+
filenames = []
|
410
|
+
for filename in filenames:
|
407
411
|
if not filename.endswith('.dist-info'):
|
408
412
|
continue
|
409
413
|
filename_lower = filename.lower()
|
@@ -430,7 +434,7 @@ def determine_version(
|
|
430
434
|
try:
|
431
435
|
os.chdir(module_parent_dir)
|
432
436
|
_version = importlib_metadata.metadata(import_name)['Version']
|
433
|
-
except Exception
|
437
|
+
except Exception:
|
434
438
|
_version = None
|
435
439
|
finally:
|
436
440
|
os.chdir(old_cwd)
|
@@ -698,7 +702,7 @@ def need_update(
|
|
698
702
|
(not semver.Version.parse(version).match(required_version))
|
699
703
|
if required_version else False
|
700
704
|
)
|
701
|
-
except AttributeError
|
705
|
+
except AttributeError:
|
702
706
|
pip_install(_import_to_install_name('semver'), venv='mrsm', debug=debug)
|
703
707
|
semver = manually_import_module('semver', venv='mrsm', debug=debug)
|
704
708
|
return (
|
@@ -724,10 +728,10 @@ def need_update(
|
|
724
728
|
|
725
729
|
|
726
730
|
def get_pip(
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
+
venv: Optional[str] = 'mrsm',
|
732
|
+
color: bool = True,
|
733
|
+
debug: bool = False,
|
734
|
+
) -> bool:
|
731
735
|
"""
|
732
736
|
Download and run the get-pip.py script.
|
733
737
|
|
@@ -747,7 +751,8 @@ def get_pip(
|
|
747
751
|
A bool indicating success.
|
748
752
|
|
749
753
|
"""
|
750
|
-
import sys
|
754
|
+
import sys
|
755
|
+
import subprocess
|
751
756
|
from meerschaum.utils.misc import wget
|
752
757
|
from meerschaum.config._paths import CACHE_RESOURCES_PATH
|
753
758
|
from meerschaum.config.static import STATIC_CONFIG
|
@@ -755,7 +760,7 @@ def get_pip(
|
|
755
760
|
dest = CACHE_RESOURCES_PATH / 'get-pip.py'
|
756
761
|
try:
|
757
762
|
wget(url, dest, color=False, debug=debug)
|
758
|
-
except Exception
|
763
|
+
except Exception:
|
759
764
|
print(f"Failed to fetch pip from '{url}'. Please install pip and restart Meerschaum.")
|
760
765
|
sys.exit(1)
|
761
766
|
if venv is not None:
|
@@ -776,6 +781,7 @@ def pip_install(
|
|
776
781
|
_uninstall: bool = False,
|
777
782
|
_from_completely_uninstall: bool = False,
|
778
783
|
_install_uv_pip: bool = True,
|
784
|
+
_use_uv_pip: bool = True,
|
779
785
|
color: bool = True,
|
780
786
|
silent: bool = False,
|
781
787
|
debug: bool = False,
|
@@ -831,15 +837,11 @@ def pip_install(
|
|
831
837
|
|
832
838
|
"""
|
833
839
|
from meerschaum.config._paths import VIRTENV_RESOURCES_PATH
|
834
|
-
from meerschaum.config import get_config
|
835
840
|
from meerschaum.config.static import STATIC_CONFIG
|
836
841
|
from meerschaum.utils.warnings import warn
|
837
842
|
if args is None:
|
838
843
|
args = ['--upgrade'] if not _uninstall else []
|
839
|
-
if color
|
840
|
-
ANSI, UNICODE = True, True
|
841
|
-
else:
|
842
|
-
ANSI, UNICODE = False, False
|
844
|
+
ANSI = True if color else False
|
843
845
|
if check_wheel:
|
844
846
|
have_wheel = venv_contains_package('wheel', venv=venv, debug=debug)
|
845
847
|
|
@@ -878,7 +880,8 @@ def pip_install(
|
|
878
880
|
)
|
879
881
|
|
880
882
|
use_uv_pip = (
|
881
|
-
|
883
|
+
_use_uv_pip
|
884
|
+
and venv_contains_package('uv', venv=None, debug=debug)
|
882
885
|
and uv_bin is not None
|
883
886
|
and venv is not None
|
884
887
|
and is_uv_enabled()
|
@@ -966,6 +969,10 @@ def pip_install(
|
|
966
969
|
|
967
970
|
if '--target' not in _args and '-t' not in _args and not (not use_uv_pip and _uninstall):
|
968
971
|
if venv is not None:
|
972
|
+
vtp = venv_target_path(venv, allow_nonexistent=True, debug=debug)
|
973
|
+
if not vtp.exists():
|
974
|
+
if not init_venv(venv, force=True):
|
975
|
+
vtp.mkdir(parents=True, exist_ok=True)
|
969
976
|
_args += ['--target', venv_target_path(venv, debug=debug)]
|
970
977
|
elif (
|
971
978
|
'--target' not in _args
|
@@ -136,7 +136,7 @@ packages['sql'] = {
|
|
136
136
|
'numpy' : 'numpy>=1.18.5',
|
137
137
|
'pandas' : 'pandas[parquet]>=2.0.1',
|
138
138
|
'pyarrow' : 'pyarrow>=16.1.0',
|
139
|
-
'dask' : 'dask[complete]>=2024.
|
139
|
+
'dask' : 'dask[complete]>=2024.12.1',
|
140
140
|
'partd' : 'partd>=1.4.2',
|
141
141
|
'pytz' : 'pytz',
|
142
142
|
'joblib' : 'joblib>=0.17.0',
|