meerschaum 2.7.3__py3-none-any.whl → 2.7.5__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/actions/sql.py CHANGED
@@ -21,7 +21,7 @@ def sql(
21
21
  nopretty: bool = False,
22
22
  debug: bool = False,
23
23
  **kw: Any
24
- ):
24
+ ) -> SuccessTuple:
25
25
  """Execute a SQL query or launch an interactive CLI. All positional arguments are optional.
26
26
 
27
27
  Usage:
@@ -125,10 +125,9 @@ def sql(
125
125
  if result is False:
126
126
  return (False, f"Failed to execute query:\n\n{query}")
127
127
 
128
- from meerschaum.utils.packages import attempt_import, import_pandas
128
+ from meerschaum.utils.packages import attempt_import
129
129
  from meerschaum.utils.formatting import print_tuple, pprint
130
- sqlalchemy_engine_result = attempt_import('sqlalchemy.engine.result')
131
- pd = import_pandas()
130
+ _ = attempt_import('sqlalchemy.engine.result')
132
131
  if 'sqlalchemy' in str(type(result)):
133
132
  if not nopretty:
134
133
  print_tuple((True, f"Successfully executed query:\n\n{query}"))
@@ -145,3 +144,14 @@ def sql(
145
144
  )
146
145
 
147
146
  return (True, "Success")
147
+
148
+
149
+ def _complete_sql(
150
+ action: Optional[List[str]] = None, **kw: Any
151
+ ) -> List[str]:
152
+ from meerschaum.utils.misc import get_connector_labels
153
+ _text = action[0] if action else ""
154
+ return [
155
+ label.split('sql:', maxsplit=1)[-1]
156
+ for label in get_connector_labels('sql', search_term=_text, ignore_exact_match=True)
157
+ ]
@@ -183,7 +183,7 @@ def _start_jobs(
183
183
  ### Cannot find dameon_id
184
184
  else:
185
185
  msg = (
186
- f"Unknown job" + ('s' if len(action) != 1 else '') + ' '
186
+ "Unknown job" + ('s' if len(action) != 1 else '') + ' '
187
187
  + items_str(action, and_str='or') + '.'
188
188
  )
189
189
  return False, msg
@@ -216,7 +216,7 @@ def _start_jobs(
216
216
  return job.start(debug=debug), name
217
217
 
218
218
  def _run_existing_job(name: str):
219
- job = Job(name, executor_keys=executor_keys)
219
+ job = jobs.get(name, Job(name, executor_keys=executor_keys))
220
220
  return job.start(debug=debug), name
221
221
 
222
222
  if not names:
@@ -68,6 +68,12 @@ default_system_config = {
68
68
  'pandas': 'pandas',
69
69
  },
70
70
  'sql': {
71
+ 'bulk_insert': {
72
+ 'postgresql': True,
73
+ 'citus': True,
74
+ 'timescaledb': True,
75
+ 'mssql': True,
76
+ },
71
77
  'instance': {
72
78
  'stale_temporary_tables_minutes': 1440,
73
79
  },
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "2.7.3"
5
+ __version__ = "2.7.5"
@@ -26,46 +26,52 @@ flavor_clis = {
26
26
  'duckdb' : 'gadwall',
27
27
  }
28
28
  cli_deps = {
29
- 'pgcli': ['pgspecial', 'pendulum'],
29
+ 'pgcli': ['pgspecial', 'pendulum', 'cli_helpers'],
30
30
  'mycli': ['cryptography'],
31
+ 'mssql': ['cli_helpers'],
31
32
  }
32
33
 
33
34
 
34
35
  def cli(
35
- self,
36
- debug: bool = False,
37
- ) -> SuccessTuple:
36
+ self,
37
+ debug: bool = False,
38
+ ) -> SuccessTuple:
38
39
  """
39
40
  Launch a subprocess for an interactive CLI.
40
41
  """
42
+ from meerschaum.utils.warnings import dprint
41
43
  from meerschaum.utils.venv import venv_exec
42
44
  env = copy.deepcopy(dict(os.environ))
43
- env[f'MRSM_SQL_{self.label.upper()}'] = json.dumps(self.meta)
45
+ env_key = f"MRSM_SQL_{self.label.upper()}"
46
+ env_val = json.dumps(self.meta)
47
+ env[env_key] = env_val
44
48
  cli_code = (
45
49
  "import sys\n"
46
50
  "import meerschaum as mrsm\n"
51
+ "import os\n"
47
52
  f"conn = mrsm.get_connector('sql:{self.label}')\n"
48
53
  "success, msg = conn._cli_exit()\n"
49
54
  "mrsm.pprint((success, msg))\n"
50
55
  "if not success:\n"
51
56
  " raise Exception(msg)"
52
57
  )
58
+ if debug:
59
+ dprint(cli_code)
53
60
  try:
54
- _ = venv_exec(cli_code, venv=None, debug=debug, capture_output=False)
61
+ _ = venv_exec(cli_code, venv=None, env=env, debug=debug, capture_output=False)
55
62
  except Exception as e:
56
63
  return False, f"[{self}] Failed to start CLI:\n{e}"
57
64
  return True, "Success"
58
65
 
59
66
 
60
67
  def _cli_exit(
61
- self,
62
- debug: bool = False
63
- ) -> SuccessTuple:
68
+ self,
69
+ debug: bool = False
70
+ ) -> SuccessTuple:
64
71
  """Launch an interactive CLI for the SQLConnector's flavor."""
65
- from meerschaum.utils.packages import venv_exec, attempt_import
72
+ import os
73
+ from meerschaum.utils.packages import attempt_import
66
74
  from meerschaum.utils.debug import dprint
67
- from meerschaum.utils.warnings import error
68
- import sys, subprocess, os
69
75
 
70
76
  if self.flavor not in flavor_clis:
71
77
  return False, f"No CLI available for flavor '{self.flavor}'."
@@ -61,6 +61,7 @@ flavor_configs = {
61
61
  'engine': 'mssql+pyodbc',
62
62
  'create_engine': {
63
63
  'fast_executemany': True,
64
+ 'use_insertmanyvalues': False,
64
65
  'isolation_level': 'AUTOCOMMIT',
65
66
  'use_setinputsizes': False,
66
67
  'pool_pre_ping': True,
@@ -3064,7 +3064,12 @@ def get_alter_columns_queries(
3064
3064
  return []
3065
3065
  if pipe.static:
3066
3066
  return
3067
- from meerschaum.utils.sql import sql_item_name, DROP_IF_EXISTS_FLAVORS, get_table_cols_types
3067
+ from meerschaum.utils.sql import (
3068
+ sql_item_name,
3069
+ get_table_cols_types,
3070
+ DROP_IF_EXISTS_FLAVORS,
3071
+ SINGLE_ALTER_TABLE_FLAVORS,
3072
+ )
3068
3073
  from meerschaum.utils.dataframe import get_numeric_cols
3069
3074
  from meerschaum.utils.dtypes import are_dtypes_equal
3070
3075
  from meerschaum.utils.dtypes.sql import (
@@ -3308,14 +3313,19 @@ def get_alter_columns_queries(
3308
3313
  else 'TYPE '
3309
3314
  )
3310
3315
  column_str = 'COLUMN' if self.flavor != 'oracle' else ''
3311
- query += (
3316
+ query_suffix = (
3312
3317
  f"\n{alter_col_prefix} {column_str} "
3313
3318
  + sql_item_name(col, self.flavor, None)
3314
3319
  + " " + type_prefix + typ + ","
3315
3320
  )
3321
+ if self.flavor not in SINGLE_ALTER_TABLE_FLAVORS:
3322
+ query += query_suffix
3323
+ else:
3324
+ queries.append(query + query_suffix[:-1])
3325
+
3326
+ if self.flavor not in SINGLE_ALTER_TABLE_FLAVORS:
3327
+ queries.append(query[:-1])
3316
3328
 
3317
- query = query[:-1]
3318
- queries.append(query)
3319
3329
  if self.flavor != 'duckdb':
3320
3330
  return queries
3321
3331
 
@@ -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, Mapping, List, Dict, SuccessTuple, Optional, Any, Iterable, Callable,
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}
@@ -779,6 +781,7 @@ def to_sql(
779
781
  from meerschaum.utils.warnings import error, warn
780
782
  import warnings
781
783
  import functools
784
+
782
785
  if name is None:
783
786
  error(f"Name must not be `None` to insert data into {self}.")
784
787
 
@@ -805,6 +808,7 @@ def to_sql(
805
808
  quantize_decimal,
806
809
  coerce_timezone,
807
810
  encode_bytes_for_bytea,
811
+ serialize_bytes,
808
812
  )
809
813
  from meerschaum.utils.dtypes.sql import (
810
814
  NUMERIC_PRECISION_FLAVORS,
@@ -821,24 +825,36 @@ def to_sql(
821
825
  bytes_cols = get_bytes_cols(df)
822
826
  numeric_cols = get_numeric_cols(df)
823
827
 
824
- stats = {'target': name,}
828
+ enable_bulk_insert = mrsm.get_config(
829
+ 'system', 'connectors', 'sql', 'bulk_insert'
830
+ ).get(self.flavor, False)
831
+ stats = {'target': name}
825
832
  ### resort to defaults if None
826
833
  copied = False
827
- use_psql_copy = False
834
+ use_bulk_insert = False
828
835
  if method == "":
829
- if self.flavor in _bulk_flavors:
830
- method = functools.partial(psql_insert_copy, schema=self.schema)
831
- use_psql_copy = True
836
+ if enable_bulk_insert:
837
+ method = (
838
+ functools.partial(mssql_insert_json, debug=debug)
839
+ if self.flavor == 'mssql'
840
+ else functools.partial(psql_insert_copy, debug=debug)
841
+ )
842
+ use_bulk_insert = True
832
843
  else:
833
844
  ### Should resolve to 'multi' or `None`.
834
845
  method = flavor_configs.get(self.flavor, {}).get('to_sql', {}).get('method', 'multi')
835
846
 
836
- if bytes_cols and (use_psql_copy or self.flavor == 'oracle'):
847
+ if bytes_cols and (use_bulk_insert or self.flavor == 'oracle'):
837
848
  if safe_copy and not copied:
838
849
  df = df.copy()
839
850
  copied = True
851
+ bytes_serializer = (
852
+ functools.partial(encode_bytes_for_bytea, with_prefix=(self.flavor != 'oracle'))
853
+ if self.flavor != 'mssql'
854
+ else serialize_bytes
855
+ )
840
856
  for col in bytes_cols:
841
- df[col] = df[col].apply(encode_bytes_for_bytea, with_prefix=(self.flavor != 'oracle'))
857
+ df[col] = df[col].apply(bytes_serializer)
842
858
 
843
859
  if self.flavor in NUMERIC_AS_TEXT_FLAVORS:
844
860
  if safe_copy and not copied:
@@ -988,7 +1004,7 @@ def to_sql(
988
1004
  stats['duration'] = end - start
989
1005
 
990
1006
  if debug:
991
- print(f" done.", flush=True)
1007
+ print(" done.", flush=True)
992
1008
  dprint(msg)
993
1009
 
994
1010
  stats['success'] = success
@@ -1005,7 +1021,7 @@ def psql_insert_copy(
1005
1021
  conn: Union[sqlalchemy.engine.Engine, sqlalchemy.engine.Connection],
1006
1022
  keys: List[str],
1007
1023
  data_iter: Iterable[Any],
1008
- schema: Optional[str] = None,
1024
+ debug: bool = False,
1009
1025
  ) -> None:
1010
1026
  """
1011
1027
  Execute SQL statement inserting data for PostgreSQL.
@@ -1022,18 +1038,15 @@ def psql_insert_copy(
1022
1038
  data_iter: Iterable[Any]
1023
1039
  Iterable that iterates the values to be inserted
1024
1040
 
1025
- schema: Optional[str], default None
1026
- Optionally specify the schema of the table to be inserted into.
1027
-
1028
1041
  Returns
1029
1042
  -------
1030
1043
  None
1031
1044
  """
1032
1045
  import csv
1033
- from io import StringIO
1034
1046
  import json
1035
1047
 
1036
1048
  from meerschaum.utils.sql import sql_item_name
1049
+ from meerschaum.utils.warnings import dprint
1037
1050
 
1038
1051
  ### NOTE: PostgreSQL doesn't support NUL chars in text, so they're removed from strings.
1039
1052
  data_iter = (
@@ -1057,6 +1070,8 @@ def psql_insert_copy(
1057
1070
  table_name = sql_item_name(table.name, 'postgresql', table.schema)
1058
1071
  columns = ', '.join(f'"{k}"' for k in keys)
1059
1072
  sql = f"COPY {table_name} ({columns}) FROM STDIN WITH CSV NULL '\\N'"
1073
+ if debug:
1074
+ dprint(sql)
1060
1075
 
1061
1076
  dbapi_conn = conn.connection
1062
1077
  with dbapi_conn.cursor() as cur:
@@ -1065,6 +1080,76 @@ def psql_insert_copy(
1065
1080
  writer.writerows(data_iter)
1066
1081
 
1067
1082
 
1083
+ def mssql_insert_json(
1084
+ table: pandas.io.sql.SQLTable,
1085
+ conn: Union[sqlalchemy.engine.Engine, sqlalchemy.engine.Connection],
1086
+ keys: List[str],
1087
+ data_iter: Iterable[Any],
1088
+ cols_types: Optional[Dict[str, str]] = None,
1089
+ debug: bool = False,
1090
+ ):
1091
+ """
1092
+ Execute SQL statement inserting data via OPENJSON.
1093
+
1094
+ Adapted from this snippet:
1095
+ https://gist.github.com/gordthompson/1fb0f1c3f5edbf6192e596de8350f205
1096
+
1097
+ Parameters
1098
+ ----------
1099
+ table: pandas.io.sql.SQLTable
1100
+
1101
+ conn: Union[sqlalchemy.engine.Engine, sqlalchemy.engine.Connection]
1102
+
1103
+ keys: List[str]
1104
+ Column names
1105
+
1106
+ data_iter: Iterable[Any]
1107
+ Iterable that iterates the values to be inserted
1108
+
1109
+ cols_types: Optional[Dict[str, str]], default None
1110
+ If provided, use these as the columns and types for the table.
1111
+
1112
+ Returns
1113
+ -------
1114
+ None
1115
+ """
1116
+ import json
1117
+ from meerschaum.utils.sql import sql_item_name
1118
+ from meerschaum.utils.dtypes.sql import get_pd_type_from_db_type, get_db_type_from_pd_type
1119
+ from meerschaum.utils.warnings import dprint
1120
+ table_name = sql_item_name(table.name, 'mssql', table.schema)
1121
+ if not cols_types:
1122
+ pd_types = {
1123
+ str(column.name): get_pd_type_from_db_type(str(column.type))
1124
+ for column in table.table.columns
1125
+ }
1126
+ cols_types = {
1127
+ col: get_db_type_from_pd_type(typ, 'mssql')
1128
+ for col, typ in pd_types.items()
1129
+ }
1130
+ columns = ",\n ".join([f"[{k}]" for k in keys])
1131
+ json_data = [dict(zip(keys, row)) for row in data_iter]
1132
+ with_clause = ",\n ".join(
1133
+ [
1134
+ f"[{col_name}] {col_type} '$.\"{col_name}\"'"
1135
+ for col_name, col_type in cols_types.items()
1136
+ ]
1137
+ )
1138
+ placeholder = "?" if conn.dialect.paramstyle == "qmark" else "%s"
1139
+ sql = (
1140
+ f"INSERT INTO {table_name} (\n {columns}\n)\n"
1141
+ f"SELECT\n {columns}\n"
1142
+ f"FROM OPENJSON({placeholder})\n"
1143
+ "WITH (\n"
1144
+ f" {with_clause}\n"
1145
+ ");"
1146
+ )
1147
+ if debug:
1148
+ dprint(sql)
1149
+
1150
+ conn.exec_driver_sql(sql, (json.dumps(json_data, default=str),))
1151
+
1152
+
1068
1153
  def format_sql_query_for_dask(query: str) -> 'sqlalchemy.sql.selectable.Select':
1069
1154
  """
1070
1155
  Given a `SELECT` query, return a `sqlalchemy` query for Dask to use.
@@ -6,8 +6,6 @@
6
6
  Higher-level utilities for managing `meerschaum.utils.daemon.Daemon`.
7
7
  """
8
8
 
9
- import pathlib
10
-
11
9
  import meerschaum as mrsm
12
10
  from meerschaum.utils.typing import Dict, Optional, List, SuccessTuple
13
11
 
@@ -13,7 +13,6 @@ import pathlib
13
13
  import json
14
14
  import shutil
15
15
  import signal
16
- import sys
17
16
  import time
18
17
  import traceback
19
18
  from functools import partial
@@ -262,7 +261,10 @@ class Daemon:
262
261
  -------
263
262
  Nothing — this will exit the parent process.
264
263
  """
265
- import platform, sys, os, traceback
264
+ import platform
265
+ import sys
266
+ import os
267
+ import traceback
266
268
  from meerschaum.utils.warnings import warn
267
269
  from meerschaum.config import get_config
268
270
  daemon = attempt_import('daemon')
@@ -301,6 +303,7 @@ class Daemon:
301
303
  os.environ['LINES'], os.environ['COLUMNS'] = str(int(lines)), str(int(columns))
302
304
  with self._daemon_context:
303
305
  sys.stdin = self.stdin_file
306
+ _ = os.environ.pop(STATIC_CONFIG['environment']['systemd_stdin_path'], None)
304
307
  os.environ[STATIC_CONFIG['environment']['daemon_id']] = self.daemon_id
305
308
  os.environ['PYTHONUNBUFFERED'] = '1'
306
309
 
@@ -1085,7 +1088,8 @@ class Daemon:
1085
1088
 
1086
1089
  def write_pickle(self) -> SuccessTuple:
1087
1090
  """Write the pickle file for the daemon."""
1088
- import pickle, traceback
1091
+ import pickle
1092
+ import traceback
1089
1093
  try:
1090
1094
  self.path.mkdir(parents=True, exist_ok=True)
1091
1095
  with open(self.pickle_path, 'wb+') as pickle_file:
@@ -104,7 +104,10 @@ class StdinFile(io.TextIOBase):
104
104
  if self._file_handler is not None:
105
105
  self.sel.unregister(self._file_handler)
106
106
  self._file_handler.close()
107
- os.close(self._fd)
107
+ try:
108
+ os.close(self._fd)
109
+ except OSError:
110
+ pass
108
111
  self._file_handler = None
109
112
  self._fd = None
110
113
 
@@ -831,7 +831,6 @@ def pip_install(
831
831
 
832
832
  """
833
833
  from meerschaum.config._paths import VIRTENV_RESOURCES_PATH
834
- from meerschaum.config import get_config
835
834
  from meerschaum.config.static import STATIC_CONFIG
836
835
  from meerschaum.utils.warnings import warn
837
836
  if args is None:
@@ -966,6 +965,10 @@ def pip_install(
966
965
 
967
966
  if '--target' not in _args and '-t' not in _args and not (not use_uv_pip and _uninstall):
968
967
  if venv is not None:
968
+ vtp = venv_target_path(venv, allow_nonexistent=True, debug=debug)
969
+ if not vtp.exists():
970
+ if not init_venv(venv, force=True):
971
+ vtp.mkdir(parents=True, exist_ok=True)
969
972
  _args += ['--target', venv_target_path(venv, debug=debug)]
970
973
  elif (
971
974
  '--target' not in _args
@@ -62,8 +62,13 @@ class Venv:
62
62
  If a `meerschaum.plugins.Plugin` was provided, its dependent virtual environments
63
63
  will also be activated.
64
64
  """
65
- from meerschaum.utils.venv import active_venvs
65
+ from meerschaum.utils.venv import active_venvs, init_venv
66
66
  self._kwargs['previously_active_venvs'] = copy.deepcopy(active_venvs)
67
+ try:
68
+ return self._activate(debug=(debug or self._debug), **self._kwargs)
69
+ except OSError as e:
70
+ if not init_venv(self._venv, force=True):
71
+ raise e
67
72
  return self._activate(debug=(debug or self._debug), **self._kwargs)
68
73
 
69
74
 
@@ -176,23 +176,26 @@ def deactivate_venv(
176
176
  if sys.path is None:
177
177
  return False
178
178
 
179
- target = venv_target_path(venv, allow_nonexistent=force, debug=debug).as_posix()
179
+ target = venv_target_path(venv, allow_nonexistent=True, debug=debug).as_posix()
180
180
  with LOCKS['sys.path']:
181
181
  if target in sys.path:
182
- sys.path.remove(target)
182
+ try:
183
+ sys.path.remove(target)
184
+ except Exception:
185
+ pass
183
186
  try:
184
187
  active_venvs_order.remove(venv)
185
- except Exception as e:
188
+ except Exception:
186
189
  pass
187
190
 
188
191
  return True
189
192
 
190
193
 
191
194
  def is_venv_active(
192
- venv: str = 'mrsm',
193
- color : bool = True,
194
- debug: bool = False
195
- ) -> bool:
195
+ venv: str = 'mrsm',
196
+ color : bool = True,
197
+ debug: bool = False
198
+ ) -> bool:
196
199
  """
197
200
  Check if a virtual environment is active.
198
201
 
@@ -395,9 +398,13 @@ def init_venv(
395
398
 
396
399
  def update_lock(active: bool):
397
400
  try:
398
- if active:
401
+ if not active:
402
+ if debug:
403
+ print(f"Releasing lock: '{lock_path}'")
399
404
  lock_path.unlink()
400
405
  else:
406
+ if debug:
407
+ print(f"Acquiring lock: '{lock_path}'")
401
408
  lock_path.touch()
402
409
  except Exception:
403
410
  pass
@@ -406,9 +413,12 @@ def init_venv(
406
413
  max_lock_seconds = 1.0
407
414
  step_sleep_seconds = 0.1
408
415
  init_venv_check_start = time.perf_counter()
409
- while (time.perf_counter() - init_venv_check_start < max_lock_seconds):
416
+ while ((time.perf_counter() - init_venv_check_start) < max_lock_seconds):
410
417
  if not lock_path.exists():
411
- continue
418
+ break
419
+
420
+ if debug:
421
+ print(f"Lock exists for '{venv}', sleeping...")
412
422
  time.sleep(step_sleep_seconds)
413
423
  update_lock(False)
414
424
 
@@ -441,10 +451,9 @@ def init_venv(
441
451
  rename_vtp = vtp.exists() and not temp_vtp.exists()
442
452
 
443
453
  if rename_vtp:
444
- try:
445
- vtp.rename(temp_vtp)
446
- except FileExistsError:
447
- pass
454
+ if debug:
455
+ print(f"Moving '{vtp}' to '{temp_vtp}'...")
456
+ shutil.move(vtp, temp_vtp)
448
457
 
449
458
  wait_for_lock()
450
459
  update_lock(True)
@@ -491,7 +500,9 @@ def init_venv(
491
500
  + "Please install `virtualenv` via pip then restart Meerschaum."
492
501
  )
493
502
  if rename_vtp and temp_vtp.exists():
494
- temp_vtp.rename(vtp)
503
+ if debug:
504
+ print(f"Moving '{temp_vtp}' back to '{vtp}'...")
505
+ shutil.move(temp_vtp, vtp)
495
506
  update_lock(False)
496
507
  return False
497
508
 
@@ -530,7 +541,7 @@ def init_venv(
530
541
  import traceback
531
542
  traceback.print_exc()
532
543
  if rename_vtp and temp_vtp.exists():
533
- temp_vtp.rename(vtp)
544
+ shutil.move(temp_vtp, vtp)
534
545
  update_lock(False)
535
546
  return False
536
547
  if verify:
@@ -538,7 +549,9 @@ def init_venv(
538
549
  verified_venvs.add(venv)
539
550
 
540
551
  if rename_vtp and temp_vtp.exists():
541
- temp_vtp.rename(vtp)
552
+ if debug:
553
+ print(f"Cleanup: move '{temp_vtp}' back to '{vtp}'.")
554
+ shutil.move(temp_vtp, vtp)
542
555
 
543
556
  update_lock(False)
544
557
  return True
@@ -653,10 +666,10 @@ def venv_exists(venv: Union[str, None], debug: bool = False) -> bool:
653
666
 
654
667
 
655
668
  def venv_target_path(
656
- venv: Union[str, None],
657
- allow_nonexistent: bool = False,
658
- debug: bool = False,
659
- ) -> 'pathlib.Path':
669
+ venv: Union[str, None],
670
+ allow_nonexistent: bool = False,
671
+ debug: bool = False,
672
+ ) -> 'pathlib.Path':
660
673
  """
661
674
  Return a virtual environment's site-package path.
662
675
 
@@ -673,7 +686,11 @@ def venv_target_path(
673
686
  The `pathlib.Path` object for the virtual environment's path.
674
687
 
675
688
  """
676
- import os, sys, platform, pathlib, site
689
+ import os
690
+ import sys
691
+ import platform
692
+ import pathlib
693
+ import site
677
694
  from meerschaum.config._paths import VIRTENV_RESOURCES_PATH
678
695
  from meerschaum.config.static import STATIC_CONFIG
679
696
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: meerschaum
3
- Version: 2.7.3
3
+ Version: 2.7.5
4
4
  Summary: Sync Time-Series Pipes with Meerschaum
5
5
  Home-page: https://meerschaum.io
6
6
  Author: Bennett Meares
@@ -42,9 +42,9 @@ meerschaum/actions/restart.py,sha256=6ffp3-X9eTEgunVSdD41HnOwqp71yjuSAmXJ5j39ONI
42
42
  meerschaum/actions/setup.py,sha256=KkAGWcgwzl_L6A19fTmTX1KtBjW2FwD8QenLjPy0mQQ,3205
43
43
  meerschaum/actions/sh.py,sha256=hSkGNTVqP5dNjhJ64zy3V3VCFPTKnDlH3PxdKdxtkGQ,1990
44
44
  meerschaum/actions/show.py,sha256=T8Ol1o-762cI9rlUzd-8svvwgT4slYXYfOPQETh9Koo,28446
45
- meerschaum/actions/sql.py,sha256=vAsxbSl-Hkw3wfrM1BLnKex_kJrZwIJICAXysprQGWM,4220
45
+ meerschaum/actions/sql.py,sha256=zG-KydYR654RHaF-O4Id3ACCDWeogk2B1R-OFLwFbG0,4515
46
46
  meerschaum/actions/stack.py,sha256=ZwrCTGJ0x3jjZkRieWcvqasQHYCqNtB1HYvTX-r3Z3g,5996
47
- meerschaum/actions/start.py,sha256=26i2Pqg3c_Ltw3lgYzl3sZL2wG0XBgOuJrPK8qPPHdI,21281
47
+ meerschaum/actions/start.py,sha256=NtdVzeB00PQBnDSUSu8ypHmMrZT8_Idmw5BfVZ--Zic,21296
48
48
  meerschaum/actions/stop.py,sha256=5fdUw70YN-yuUWrC-NhA88cxr9FZ5NbssbQ8xXO8nFU,4632
49
49
  meerschaum/actions/sync.py,sha256=QGhf0ZyrNaplPL6TkwHO9VQLEu_Gemmid93vdaovXoo,17153
50
50
  meerschaum/actions/tag.py,sha256=SJf5qFW0ccLXjqlTdkK_0MCcrCMdg6xhYrhKdco0hdA,3053
@@ -132,7 +132,7 @@ meerschaum/api/routes/_webterm.py,sha256=MenDvWXnZ8CWEmryB46pKohMf0PN6o1xJGZ3LFj
132
132
  meerschaum/api/tables/__init__.py,sha256=e2aNC0CdlWICTUMx1i9RauF8Pm426J0RZJbsJWv4SWo,482
133
133
  meerschaum/config/__init__.py,sha256=5ZBq71P9t3nb74r5CGvMfNuauPscfegBX-nkaAUi5C4,11541
134
134
  meerschaum/config/_dash.py,sha256=BJHl4xMrQB-YHUEU7ldEW8q_nOPoIRSOqLrfGElc6Dw,187
135
- meerschaum/config/_default.py,sha256=3HhlLLceFbf7zn3sciIr0DWGynuHpzRmkmXeTyT9J9s,5652
135
+ meerschaum/config/_default.py,sha256=IDRo8nLWija_cawiqeS6cPwZ9deDF9179m_rwGntNLA,5831
136
136
  meerschaum/config/_edit.py,sha256=M9yX_SDD24gV5kWITZpy7p9AWTizJsIAGWAs3WZx-Ws,9087
137
137
  meerschaum/config/_environment.py,sha256=Vv4DLDfc2vKLbCLsMvkQDj77K4kEvHKEBmUBo-wCrgo,4419
138
138
  meerschaum/config/_formatting.py,sha256=OMuqS1EWOsj_34wSs2tOqGIWci3bTMIZ5l-uelZgsIM,6672
@@ -143,7 +143,7 @@ meerschaum/config/_preprocess.py,sha256=-AEA8m_--KivZwTQ1sWN6LTn5sio_fUr2XZ51BO6
143
143
  meerschaum/config/_read_config.py,sha256=RLC3HHi_1ndj7ITVDKLD9_uULY3caGRwSz3ATYE-ixA,15014
144
144
  meerschaum/config/_shell.py,sha256=46_m49Txc5q1rGfCgO49ca48BODx45DQJi8D0zz1R18,4245
145
145
  meerschaum/config/_sync.py,sha256=jHcWRkxd82_BgX8Xo8agsWvf7BSbv3qHLWmYl6ehp_0,4242
146
- meerschaum/config/_version.py,sha256=15LlFyoMeeTpuVYA_73PnLwLlzhwvMJLmuNnG_C0Rys,71
146
+ meerschaum/config/_version.py,sha256=FtGr91JvNFrz0tCWU-8xhZY04Aon0uXjcSuAPXv1SUI,71
147
147
  meerschaum/config/paths.py,sha256=JjibeGN3YAdSNceRwsd42aNmeUrIgM6ndzC8qZAmNI0,621
148
148
  meerschaum/config/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
149
149
  meerschaum/config/stack/__init__.py,sha256=2UukC0Lmk-aVL1o1qXzumqmuIrw3vu9fD7iCuz4XD4I,10544
@@ -172,13 +172,13 @@ meerschaum/connectors/plugin/PluginConnector.py,sha256=aQ1QaB7MordCFimZqoGLb0R12
172
172
  meerschaum/connectors/plugin/__init__.py,sha256=pwF7TGY4WNz2_HaVdmK4rPQ9ZwTOEuPHgzOqsGcoXJw,198
173
173
  meerschaum/connectors/sql/_SQLConnector.py,sha256=g9SFK30CZp7CTJI-SdpOanL1NQUBFQeUng7FSGacJA4,11985
174
174
  meerschaum/connectors/sql/__init__.py,sha256=3cqYiDkVasn7zWdtOTAZbT4bo95AuvGOmDD2TkaAxtw,205
175
- meerschaum/connectors/sql/_cli.py,sha256=1SgnWeMIAihoxp4FzbNrcq1npXf0dSOQnCntpU9hUXA,4405
176
- meerschaum/connectors/sql/_create_engine.py,sha256=MqgFhtWUMSgkx6Qu6Q6Ny3jIz-GWix_Eawko59RLm1o,10512
175
+ meerschaum/connectors/sql/_cli.py,sha256=VqAHkdXC0HVXuHaZik2q-cTVmIsuS4DWMcPMJPP_g-Q,4514
176
+ meerschaum/connectors/sql/_create_engine.py,sha256=RzJz4Qc43P4JS6tkgVvAhbdjEejIepWbxymIfVZ44Nk,10555
177
177
  meerschaum/connectors/sql/_fetch.py,sha256=GOU1JnPOBgaI3dDr0JdAmfTMPLIMM0EFHrsqDgDIO74,14070
178
178
  meerschaum/connectors/sql/_instance.py,sha256=mmZnodccuCqmZN-UnznnFZ7JodxtT47kwjDDaDKgwUg,6274
179
- meerschaum/connectors/sql/_pipes.py,sha256=EhuCZeGDTeywHvcbIlkipxouSGqgq_blU1z6-jrWLTY,121316
179
+ meerschaum/connectors/sql/_pipes.py,sha256=n0tHoVnBmno08qqO5qaFuSlfIW96_VVm8T1k5ZRdPQw,121592
180
180
  meerschaum/connectors/sql/_plugins.py,sha256=wbxcNxqTtjfDsxPvdVGTllasYf6NHHzODaQ72hEUSBQ,8135
181
- meerschaum/connectors/sql/_sql.py,sha256=ktXYiI0EpP7syRaW-PCvB14oaMFtWlfCVOjKCUNYBu8,38509
181
+ meerschaum/connectors/sql/_sql.py,sha256=Q4MKuUlqrgXuYCbpCtoQlvqOvASDQ7BkkLLSYPLh_JE,41052
182
182
  meerschaum/connectors/sql/_uri.py,sha256=0BrhQtqQdzg9mR04gWBZINs_BbPFtSlTECXT_TCUwik,3460
183
183
  meerschaum/connectors/sql/_users.py,sha256=FJjYeJGhr-TDHziNc6p_5mupGRtGjezKPIYgHFEVSnY,9956
184
184
  meerschaum/connectors/sql/tools.py,sha256=jz8huOaRCwGlYdtGfAqAh7SoK8uydYBrasKQba9FT38,187
@@ -212,7 +212,7 @@ meerschaum/core/User/_User.py,sha256=qbI0GIkr3G0PI4d9S49uatbJQ2kH_-z5-GoVJ0fuEtA
212
212
  meerschaum/core/User/__init__.py,sha256=9qNy-Gobui4x6GiaE8U7-WOggsdniOM3_wegLN3SVKs,988
213
213
  meerschaum/jobs/_Executor.py,sha256=qM62BhFTM4tyJ7p90KOM0y3qyeRY9k3ZV_aTDJMHnO8,1682
214
214
  meerschaum/jobs/_Job.py,sha256=wRsmrQ1bm898BxXELbHTltKYBj0bK0GmegE8zeL0FG8,32161
215
- meerschaum/jobs/__init__.py,sha256=1GFkF-bnkC_rnemKCW_WM9oIq7oyLglux3TssXGBwz0,12189
215
+ meerschaum/jobs/__init__.py,sha256=YjwB6t8HCT593ckrOlImgk0kLX-1NgAYzNXVwS-uvyY,12173
216
216
  meerschaum/jobs/systemd.py,sha256=Rq-tsDPslG17ZhpKMrVJ5r8Z0IPr6DEc9APObfIoXCg,24614
217
217
  meerschaum/plugins/_Plugin.py,sha256=bIo4HX8TTWIcwIHROwMt4VK6OoEUhY_3Qc8q-2dp-ZA,33895
218
218
  meerschaum/plugins/__init__.py,sha256=6krcqaMKyzuVqesXMqEL0XEy2SJQ4xfNt2-oI_fJ6v0,26278
@@ -233,10 +233,10 @@ meerschaum/utils/threading.py,sha256=awjbVL_QR6G-o_9Qk85utac9cSdqkiC8tQSdERCdrG8
233
233
  meerschaum/utils/typing.py,sha256=U3MC347sh1umpa3Xr1k71eADyDmk4LB6TnVCpq8dVzI,2830
234
234
  meerschaum/utils/warnings.py,sha256=n-phr3BftNNgyPnvnXC_VMSjtCvjiCZ-ewmVfcROhkc,6611
235
235
  meerschaum/utils/yaml.py,sha256=PoC1du0pn2hLwTHwL-zuOf_EBWZSbCGOz-P-AZ4BWN0,3901
236
- meerschaum/utils/daemon/Daemon.py,sha256=R_LM0zZS4Tng5OQLNZ-Dzyk2dn_EPR7z8FNiphYgvBs,42622
236
+ meerschaum/utils/daemon/Daemon.py,sha256=zxU15r7XJCTU5r5IWdiH9GQEenUFSnV439f0Vq1EzgY,42760
237
237
  meerschaum/utils/daemon/FileDescriptorInterceptor.py,sha256=MJKMO0Syf3d8yWUs6xXcQzg8Ptsuvh2aCRRoglOjusA,5257
238
238
  meerschaum/utils/daemon/RotatingFile.py,sha256=ePm_svjwyFDWh6V1k-bp1RHXCSWlyxDtlFu4SU4XvPU,24369
239
- meerschaum/utils/daemon/StdinFile.py,sha256=J6tyUReM8NEp3bBQAxMfe8mjJG5mWi6CzHN4x86VQBI,3237
239
+ meerschaum/utils/daemon/StdinFile.py,sha256=qdZ8E_RSOkURypwnS50mWeyWyRig1bAY9tKWMTVKajc,3307
240
240
  meerschaum/utils/daemon/__init__.py,sha256=o9jWb4lRTIyny4EPt7fPXFgV_vIf1mUofsTwoE1ZecA,8751
241
241
  meerschaum/utils/daemon/_names.py,sha256=d2ZwTxBoTAqXZkCfZ5LuX2XrkQmLNUq1OTlUqfoH5dA,4515
242
242
  meerschaum/utils/dtypes/__init__.py,sha256=c6DoYyCbWvMdRapBRKP5UJYLRUWtkTIlC_8HRzXFh2s,12166
@@ -246,16 +246,16 @@ meerschaum/utils/formatting/_jobs.py,sha256=izsqPJhTtUkXUUtWnbXtReYsUYwulXtci3pB
246
246
  meerschaum/utils/formatting/_pipes.py,sha256=OISJmmFiilaDbZxkiXck_g39MnnTfk_fJJyJ-YInvXA,19559
247
247
  meerschaum/utils/formatting/_pprint.py,sha256=tgrT3FyGyu5CWJYysqK3kX1xdZYorlbOk9fcU_vt9Qg,3096
248
248
  meerschaum/utils/formatting/_shell.py,sha256=XH7VFLteNv7NGtWhJl7FdIGt80sKeTiDoJokGSDAwBM,3761
249
- meerschaum/utils/packages/__init__.py,sha256=Op93VJkAX3OL4H-js_p3dAaa_PT82jvjCna27aHOsUk,64199
249
+ meerschaum/utils/packages/__init__.py,sha256=vg1C9dpj1MLOWgvNGrKkqLRe4Z9hUnNdQDOCvReCBh4,64392
250
250
  meerschaum/utils/packages/_packages.py,sha256=ykannoLv2Fm4iwZwiIlNAGZvt654cMJhjXr1VJPoEDo,8867
251
251
  meerschaum/utils/packages/lazy_loader.py,sha256=VHnph3VozH29R4JnSSBfwtA5WKZYZQFT_GeQSShCnuc,2540
252
- meerschaum/utils/venv/_Venv.py,sha256=QsnDGEE_YBqXIq53NTWi2TANA1FbkqJBl_xQi9jQP_U,3537
253
- meerschaum/utils/venv/__init__.py,sha256=BZQ-c8aJi3xu3kdx0Ey0bnOa5n6XEVbpWZGgQRN6nwg,26208
254
- meerschaum-2.7.3.dist-info/LICENSE,sha256=jG2zQEdRNt88EgHUWPpXVWmOrOduUQRx7MnYV9YIPaw,11359
255
- meerschaum-2.7.3.dist-info/METADATA,sha256=hvljI_CruYI5E630gkA_zwnUf53OTK_PCeJup-c9BIs,24224
256
- meerschaum-2.7.3.dist-info/NOTICE,sha256=OTA9Fcthjf5BRvWDDIcBC_xfLpeDV-RPZh3M-HQBRtQ,114
257
- meerschaum-2.7.3.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
258
- meerschaum-2.7.3.dist-info/entry_points.txt,sha256=5YBVzibw-0rNA_1VjB16z5GABsOGf-CDhW4yqH8C7Gc,88
259
- meerschaum-2.7.3.dist-info/top_level.txt,sha256=bNoSiDj0El6buocix-FRoAtJOeq1qOF5rRm2u9i7Q6A,11
260
- meerschaum-2.7.3.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
261
- meerschaum-2.7.3.dist-info/RECORD,,
252
+ meerschaum/utils/venv/_Venv.py,sha256=gc1TCeAj-kTZbQFAT9xl1bi4HXFV5ApT0dPOJfxwr78,3748
253
+ meerschaum/utils/venv/__init__.py,sha256=wqjp0ocIkp6nsHYLUObXIhfYY5CjX-JnS6psm783ga0,26755
254
+ meerschaum-2.7.5.dist-info/LICENSE,sha256=jG2zQEdRNt88EgHUWPpXVWmOrOduUQRx7MnYV9YIPaw,11359
255
+ meerschaum-2.7.5.dist-info/METADATA,sha256=kQOkmZPuhuo-Ee1b_2JlzUnmfGYOZOBBUWs3pHZ_g1M,24224
256
+ meerschaum-2.7.5.dist-info/NOTICE,sha256=OTA9Fcthjf5BRvWDDIcBC_xfLpeDV-RPZh3M-HQBRtQ,114
257
+ meerschaum-2.7.5.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
258
+ meerschaum-2.7.5.dist-info/entry_points.txt,sha256=5YBVzibw-0rNA_1VjB16z5GABsOGf-CDhW4yqH8C7Gc,88
259
+ meerschaum-2.7.5.dist-info/top_level.txt,sha256=bNoSiDj0El6buocix-FRoAtJOeq1qOF5rRm2u9i7Q6A,11
260
+ meerschaum-2.7.5.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
261
+ meerschaum-2.7.5.dist-info/RECORD,,