meerschaum 2.7.3__py3-none-any.whl → 2.7.5__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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,,