meerschaum 2.8.2__py3-none-any.whl → 2.8.4__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.
@@ -367,6 +367,11 @@ groups['sync'].add_argument(
367
367
  "Only compare row-counts when verifying pipes."
368
368
  ),
369
369
  )
370
+ groups['sync'].add_argument(
371
+ '--skip-hooks', action='store_true', help=(
372
+ "Skip calling the sync hooks."
373
+ )
374
+ )
370
375
  groups['sync'].add_argument(
371
376
  '--cache', action='store_true',
372
377
  help=(
@@ -86,7 +86,7 @@ def _drop_pipes(
86
86
  pprint(success_dict)
87
87
 
88
88
  msg = (
89
- f"Finished dropping {len(pipes)} pipes"
89
+ f"Finished dropping {len(pipes)} pipe"
90
90
  + ('s' if len(pipes) != 1 else '')
91
91
  + f"\n ({successes} succeeded, {fails} failed)."
92
92
  )
@@ -568,9 +568,9 @@ def _start_pipeline(
568
568
  Examples
569
569
  --------
570
570
 
571
- `sync pipes -i sql:local + sync pipes -i sql:main :: -s 'daily'`
571
+ `sync pipes -i sql:local + sync pipes -i sql:main : -s 'daily'`
572
572
 
573
- `show version + show arguments :: --loop`
573
+ `show version + show arguments : --loop`
574
574
 
575
575
  """
576
576
  import json
@@ -580,10 +580,11 @@ def _start_pipeline(
580
580
  from meerschaum.utils.warnings import info, warn
581
581
  from meerschaum.utils.misc import is_int
582
582
  from meerschaum.utils.venv import venv_exec
583
- from meerschaum.utils.process import poll_process
583
+ from meerschaum.utils.process import poll_process, _stop_process
584
584
  fence_begin, fence_end = '<MRSM_RESULT>', '</MRSM_RESULT>'
585
585
 
586
- success, msg = False, "Did not run pipeline."
586
+ default_msg = "Did not pipeline."
587
+ success, msg = False, default_msg
587
588
  def write_line(line):
588
589
  nonlocal success, msg
589
590
  decoded = line.decode('utf-8')
@@ -610,7 +611,9 @@ def _start_pipeline(
610
611
  do_n_times = (
611
612
  int(action[0].lstrip('x'))
612
613
  if action and is_int(action[0].lstrip('x'))
613
- else 1
614
+ else (
615
+ 1 if not loop else -1
616
+ )
614
617
  )
615
618
 
616
619
  params = params or {}
@@ -623,8 +626,9 @@ def _start_pipeline(
623
626
  if min_seconds is None:
624
627
  min_seconds = 1.0
625
628
 
629
+ proc = None
626
630
  def do_entry() -> None:
627
- nonlocal success, msg
631
+ nonlocal success, msg, proc
628
632
  if timeout_seconds is None:
629
633
  success, msg = entry(sub_args_line, _patch_args=patch_args)
630
634
  return
@@ -674,6 +678,10 @@ def _start_pipeline(
674
678
  run_loop()
675
679
  except KeyboardInterrupt:
676
680
  warn("Cancelled pipeline.", stack=False)
681
+ if proc is not None:
682
+ _stop_process(proc)
683
+ if msg == default_msg:
684
+ msg = "Pipeline was cancelled."
677
685
 
678
686
  if do_n_times != 1:
679
687
  info(f"Ran pipeline {ran_n_times} time" + ('s' if ran_n_times != 1 else '') + '.')
@@ -50,6 +50,7 @@ def _pipes_lap(
50
50
  bounded: Optional[bool] = None,
51
51
  chunk_interval: Union[timedelta, int, None] = None,
52
52
  check_rowcounts_only: bool = False,
53
+ skip_hooks: bool = False,
53
54
  mrsm_instance: Optional[str] = None,
54
55
  timeout_seconds: Optional[int] = None,
55
56
  nopretty: bool = False,
@@ -95,6 +96,7 @@ def _pipes_lap(
95
96
  'bounded': bounded,
96
97
  'chunk_interval': chunk_interval,
97
98
  'check_rowcounts_only': check_rowcounts_only,
99
+ 'skip_hooks': skip_hooks,
98
100
  })
99
101
  locks = {'remaining_count': Lock(), 'results_dict': Lock(), 'pipes_threads': Lock(),}
100
102
  pipes = get_pipes(
@@ -257,6 +259,7 @@ def _sync_pipes(
257
259
  bounded: Optional[bool] = None,
258
260
  chunk_interval: Union[timedelta, int, None] = None,
259
261
  check_rowcounts_only: bool = False,
262
+ skip_hooks: bool = False,
260
263
  shell: bool = False,
261
264
  nopretty: bool = False,
262
265
  debug: bool = False,
@@ -289,6 +292,8 @@ def _sync_pipes(
289
292
 
290
293
  noninteractive_val = os.environ.get(STATIC_CONFIG['environment']['noninteractive'], None)
291
294
  noninteractive = str(noninteractive_val).lower() in ('1', 'true', 'yes')
295
+ if check_rowcounts_only:
296
+ skip_hooks = True
292
297
 
293
298
  run = True
294
299
  msg = ""
@@ -312,6 +317,7 @@ def _sync_pipes(
312
317
  bounded=bounded,
313
318
  chunk_interval=chunk_interval,
314
319
  check_rowcounts_only=check_rowcounts_only,
320
+ skip_hooks=skip_hooks,
315
321
  unblock=unblock,
316
322
  debug=debug,
317
323
  nopretty=nopretty,
@@ -403,6 +409,7 @@ def _wrap_pipe(
403
409
  deduplicate: bool = False,
404
410
  bounded: Optional[bool] = None,
405
411
  chunk_interval: Union[timedelta, int, None] = None,
412
+ skip_hooks: bool = False,
406
413
  **kw
407
414
  ):
408
415
  """
@@ -462,6 +469,8 @@ def _wrap_pipe(
462
469
 
463
470
  pre_hook_results, post_hook_results = [], []
464
471
  def apply_hooks(is_pre_sync: bool):
472
+ if skip_hooks:
473
+ return
465
474
  _sync_hooks = (_pre_sync_hooks if is_pre_sync else _post_sync_hooks)
466
475
  _hook_results = (pre_hook_results if is_pre_sync else post_hook_results)
467
476
  for module_name, sync_hooks in _sync_hooks.items():
@@ -9,6 +9,7 @@ from __future__ import annotations
9
9
 
10
10
  import os
11
11
  from collections import defaultdict
12
+ from fnmatch import fnmatch
12
13
 
13
14
  import meerschaum as mrsm
14
15
  from meerschaum.utils.typing import Dict, Any, Optional, PipesDict
@@ -21,6 +22,7 @@ from meerschaum.plugins import _api_plugins
21
22
  from meerschaum.utils.warnings import warn, dprint
22
23
  from meerschaum.utils.threading import RLock
23
24
  from meerschaum.utils.misc import is_pipe_registered
25
+ from meerschaum.connectors.parse import parse_instance_keys
24
26
 
25
27
  from meerschaum import __version__ as version
26
28
  __version__ = version
@@ -99,7 +101,6 @@ production = get_uvicorn_config().get('production', False)
99
101
  _include_dash = (not no_dash)
100
102
  docs_enabled = not production or sys_config.get('endpoints', {}).get('docs_in_production', True)
101
103
 
102
- connector = None
103
104
  default_instance_keys = None
104
105
  _instance_connectors = defaultdict(lambda: None)
105
106
  def get_api_connector(instance_keys: Optional[str] = None):
@@ -119,23 +120,35 @@ def get_api_connector(instance_keys: Optional[str] = None):
119
120
  )
120
121
 
121
122
  allowed_instance_keys = permissions_config.get(
122
- 'instance', {}
123
+ 'instances', {}
123
124
  ).get(
124
125
  'allowed_instance_keys',
125
126
  ['*']
126
127
  )
127
- if allowed_instance_keys != ['*'] and instance_keys not in allowed_instance_keys:
128
+ found_match: bool = False
129
+ for allowed_keys_pattern in allowed_instance_keys:
130
+ if fnmatch(instance_keys, allowed_keys_pattern):
131
+ found_match = True
132
+ break
133
+ if not found_match:
128
134
  raise APIPermissionError(
129
- f"Instance keys '{instance_keys}' not in list of allowed instances."
135
+ f"Instance keys '{instance_keys}' does not match the allowed instances patterns."
130
136
  )
131
137
 
132
138
  with _locks[f'instance-{instance_keys}']:
133
- connector = _instance_connectors[instance_keys]
134
- if connector is None:
135
- from meerschaum.connectors.parse import parse_instance_keys
136
- connector = parse_instance_keys(instance_keys, debug=debug)
137
- _instance_connectors[instance_keys] = connector
138
- return connector
139
+ if _instance_connectors[instance_keys] is None:
140
+ try:
141
+ is_valid_connector = True
142
+ _instance_connectors[instance_keys] = parse_instance_keys(instance_keys, debug=debug)
143
+ except Exception:
144
+ is_valid_connector = False
145
+
146
+ if not is_valid_connector:
147
+ raise fastapi.HTTPException(
148
+ status_code=422,
149
+ detail="Invalid instance keys.",
150
+ )
151
+ return _instance_connectors[instance_keys]
139
152
 
140
153
 
141
154
  cache_connector = None
@@ -164,7 +177,6 @@ def get_cache_connector(connector_keys: Optional[str] = None):
164
177
  return None
165
178
 
166
179
  if cache_connector is None:
167
- from meerschaum.connectors.parse import parse_instance_keys
168
180
  cache_connector = parse_instance_keys(connector_keys)
169
181
 
170
182
  if debug:
@@ -272,7 +272,7 @@ async def get_pipes_by_connector_and_metric(
272
272
 
273
273
 
274
274
  @app.get(pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}', tags=['Pipes'])
275
- async def get_pipes_by_connector_and_metric_and_location(
275
+ async def get_pipe_by_connector_and_metric_and_location(
276
276
  connector_keys: str,
277
277
  metric_key: str,
278
278
  location_key: str,
@@ -299,7 +299,7 @@ async def get_pipes_by_connector_and_metric_and_location(
299
299
  detail=f"location_key '{location_key}' not found."
300
300
  )
301
301
 
302
- return str(pipes(instance_keys)[connector_keys][metric_key][location_key])
302
+ return pipes(instance_keys)[connector_keys][metric_key][location_key].attributes
303
303
 
304
304
 
305
305
  @app.get(pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/sync_time', tags=['Pipes'])
@@ -311,7 +311,7 @@ def get_sync_time(
311
311
  newest: bool = True,
312
312
  remote: bool = False,
313
313
  round_down: bool = True,
314
- debug: bool = False,
314
+ instance_keys: Optional[str] = None,
315
315
  curr_user = (
316
316
  fastapi.Depends(manager) if not no_auth else None
317
317
  ),
@@ -322,12 +322,11 @@ def get_sync_time(
322
322
  """
323
323
  if location_key == '[None]':
324
324
  location_key = None
325
- pipe = get_pipe(connector_keys, metric_key, location_key)
325
+ pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
326
326
  sync_time = pipe.get_sync_time(
327
- params = params,
328
- newest = newest,
329
- debug = debug,
330
- round_down = round_down,
327
+ params=params,
328
+ newest=newest,
329
+ round_down=round_down,
331
330
  )
332
331
  if isinstance(sync_time, datetime):
333
332
  sync_time = sync_time.isoformat()
@@ -339,7 +338,8 @@ def sync_pipe(
339
338
  connector_keys: str,
340
339
  metric_key: str,
341
340
  location_key: str,
342
- data: dict = None,
341
+ data: Union[List[Dict[Any, Any]], Dict[Any, Any]],
342
+ instance_keys: Optional[str] = None,
343
343
  check_existing: bool = True,
344
344
  blocking: bool = True,
345
345
  force: bool = False,
@@ -354,9 +354,9 @@ def sync_pipe(
354
354
  Add data to an existing Pipe.
355
355
  See [`meerschaum.Pipe.sync`](https://docs.meerschaum.io/meerschaum.html#Pipe.sync).
356
356
  """
357
- if data is None:
358
- data = {}
359
- pipe = get_pipe(connector_keys, metric_key, location_key)
357
+ if not data:
358
+ return [True, "No data to sync."]
359
+ pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
360
360
  if pipe.target in ('mrsm_users', 'mrsm_plugins', 'mrsm_pipes'):
361
361
  raise fastapi.HTTPException(
362
362
  status_code=409,
@@ -365,21 +365,16 @@ def sync_pipe(
365
365
 
366
366
  if not pipe.columns and columns is not None:
367
367
  pipe.columns = json.loads(columns)
368
- if not pipe.columns and not is_pipe_registered(pipe, pipes(refresh=True)):
369
- raise fastapi.HTTPException(
370
- status_code=409,
371
- detail="Pipe must be registered with index columns specified."
372
- )
373
368
 
374
- result = list(pipe.sync(
369
+ success, msg = pipe.sync(
375
370
  data,
376
371
  debug=debug,
377
372
  check_existing=check_existing,
378
373
  blocking=blocking,
379
374
  force=force,
380
375
  workers=workers,
381
- ))
382
- return result
376
+ )
377
+ return list((success, msg))
383
378
 
384
379
 
385
380
  @app.get(pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/data', tags=['Pipes'])
@@ -527,7 +522,6 @@ def get_pipe_csv(
527
522
  if params == 'null':
528
523
  params = None
529
524
  if params is not None:
530
- import json
531
525
  try:
532
526
  _params = json.loads(params)
533
527
  except Exception:
@@ -543,7 +537,7 @@ def get_pipe_csv(
543
537
  if not is_pipe_registered(pipe, pipes(instance_keys, refresh=True)):
544
538
  raise fastapi.HTTPException(
545
539
  status_code=409,
546
- detail="Pipe must be registered with the datetime column specified."
540
+ detail="Pipe must be registered."
547
541
  )
548
542
 
549
543
  dt_col = pipe.columns.get('datetime', None)
@@ -173,6 +173,9 @@ default_pipes_config = {
173
173
  'sync': {
174
174
  'filter_params_index_limit': 250,
175
175
  },
176
+ 'verify': {
177
+ 'max_chunks_syncs': 3,
178
+ },
176
179
  }
177
180
  default_plugins_config = {}
178
181
  default_experimental_config = {
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "2.8.2"
5
+ __version__ = "2.8.4"
@@ -137,7 +137,7 @@ STATIC_CONFIG: Dict[str, Any] = {
137
137
  'pbkdf2_sha256__default_rounds': 1_000_000,
138
138
  },
139
139
  'min_username_length': 1,
140
- 'max_username_length': 26,
140
+ 'max_username_length': 60,
141
141
  'min_password_length': 5,
142
142
  },
143
143
  'plugins': {
@@ -184,7 +184,7 @@ class SQLConnector(Connector):
184
184
  ### ensure flavor and label are set accordingly
185
185
  if 'flavor' not in self.__dict__:
186
186
  if flavor is None and 'uri' not in self.__dict__:
187
- raise Exception(
187
+ raise ValueError(
188
188
  f" Missing flavor. Provide flavor as a key for '{self}'."
189
189
  )
190
190
  self.flavor = flavor or self.parse_uri(self.__dict__['uri']).get('flavor', None)
@@ -100,7 +100,13 @@ def _cli_exit(
100
100
  ### yet defined (e.g. 'sql:local').
101
101
  cli_arg_str = self.DATABASE_URL
102
102
  if self.flavor in ('sqlite', 'duckdb'):
103
- cli_arg_str = str(self.database)
103
+ cli_arg_str = (
104
+ str(self.database)
105
+ if 'database' in self.__dict__
106
+ else self.parse_uri(self.URI).get('database', None)
107
+ )
108
+ if not cli_arg_str:
109
+ raise ValueError(f"Cannot determine database from connector '{self}'.")
104
110
  if cli_arg_str.startswith('postgresql+psycopg://'):
105
111
  cli_arg_str = cli_arg_str.replace('postgresql+psycopg://', 'postgresql://')
106
112
 
@@ -256,8 +256,11 @@ def create_engine(
256
256
  ) if not _uri else _uri
257
257
 
258
258
  ### Sometimes the timescaledb:// flavor can slip in.
259
- if _uri and self.flavor in ('timescaledb',) and self.flavor in _uri:
260
- engine_str = engine_str.replace(f'{self.flavor}', 'postgresql', 1)
259
+ if _uri and self.flavor in _uri:
260
+ if self.flavor == 'timescaledb':
261
+ engine_str = engine_str.replace(f'{self.flavor}', 'postgresql', 1)
262
+ elif _uri.startswith('postgresql://'):
263
+ engine_str = engine_str.replace('postgresql://', 'postgresql+psycopg2://')
261
264
 
262
265
  if debug:
263
266
  dprint(
@@ -303,7 +306,7 @@ def create_engine(
303
306
  echo = debug,
304
307
  **_create_engine_args
305
308
  )
306
- except Exception as e:
309
+ except Exception:
307
310
  warn(f"Failed to create connector '{self}':\n{traceback.format_exc()}", stack=False)
308
311
  engine = None
309
312
 
@@ -43,7 +43,9 @@ def register_user(
43
43
  'password_hash': user.password_hash,
44
44
  'user_type': user.type,
45
45
  'attributes': (
46
- json.dumps(user.attributes) if self.flavor not in json_flavors else user.attributes
46
+ json.dumps(user.attributes)
47
+ if self.flavor not in json_flavors
48
+ else user.attributes
47
49
  ),
48
50
  }
49
51
  if old_id is not None:
@@ -109,7 +111,7 @@ def edit_user(
109
111
  user_id = user.user_id if user.user_id is not None else self.get_user_id(user, debug=debug)
110
112
  if user_id is None:
111
113
  return False, (
112
- f"User '{user.username}' does not exist. " +
114
+ f"User '{user.username}' does not exist. "
113
115
  f"Register user '{user.username}' before editing."
114
116
  )
115
117
  user.user_id = user_id
@@ -7,6 +7,7 @@ Verify the contents of a pipe by resyncing its interval.
7
7
  """
8
8
 
9
9
  from datetime import datetime, timedelta
10
+ import time
10
11
 
11
12
  import meerschaum as mrsm
12
13
  from meerschaum.utils.typing import SuccessTuple, Any, Optional, Union, Tuple, Dict
@@ -118,7 +119,7 @@ def verify(
118
119
  begin, end = self.parse_date_bounds(begin, end)
119
120
  cannot_determine_bounds = bounded and begin is None and end is None
120
121
 
121
- if cannot_determine_bounds:
122
+ if cannot_determine_bounds and not check_rowcounts_only:
122
123
  warn(f"Cannot determine sync bounds for {self}. Syncing instead...", stack=False)
123
124
  sync_success, sync_msg = self.sync(
124
125
  begin=begin,
@@ -183,9 +184,12 @@ def verify(
183
184
  )
184
185
  )
185
186
  message_header = f"{begin_to_print} - {end_to_print}"
187
+ max_chunks_syncs = mrsm.get_config('pipes', 'verify', 'max_chunks_syncs')
186
188
 
187
189
  info(
188
- f"Verifying {self}:\n Syncing {len(chunk_bounds)} chunk"
190
+ f"Verifying {self}:\n "
191
+ + ("Syncing" if not check_rowcounts_only else "Checking")
192
+ + f" {len(chunk_bounds)} chunk"
189
193
  + ('s' if len(chunk_bounds) != 1 else '')
190
194
  + f" ({'un' if not bounded else ''}bounded)"
191
195
  + f" of size '{interval_str(chunk_interval)}'"
@@ -219,9 +223,9 @@ def verify(
219
223
  debug=debug,
220
224
  )
221
225
  checked_rows_str = (
222
- f"checked {existing_rowcount} row"
226
+ f"checked {existing_rowcount:,} row"
223
227
  + ("s" if existing_rowcount != 1 else '')
224
- + f" vs {remote_rowcount} remote"
228
+ + f" vs {remote_rowcount:,} remote"
225
229
  )
226
230
  if (
227
231
  existing_rowcount is not None
@@ -239,19 +243,26 @@ def verify(
239
243
  f"Row-counts are out-of-sync ({checked_rows_str})."
240
244
  )
241
245
 
242
- chunk_success, chunk_msg = self.sync(
243
- begin=chunk_begin,
244
- end=chunk_end,
245
- params=params,
246
- workers=_workers,
247
- debug=debug,
248
- **kwargs
249
- ) if do_sync else (chunk_success, chunk_msg)
246
+ num_syncs = 0
247
+ while num_syncs < max_chunks_syncs:
248
+ chunk_success, chunk_msg = self.sync(
249
+ begin=chunk_begin,
250
+ end=chunk_end,
251
+ params=params,
252
+ workers=_workers,
253
+ debug=debug,
254
+ **kwargs
255
+ ) if do_sync else (chunk_success, chunk_msg)
256
+ if chunk_success:
257
+ break
258
+ num_syncs += 1
259
+ time.sleep(num_syncs**2)
250
260
  chunk_msg = chunk_msg.strip()
251
261
  if ' - ' not in chunk_msg:
252
262
  chunk_label = f"{chunk_begin} - {chunk_end}"
253
263
  chunk_msg = f'Verified chunk for {self}:\n{chunk_label}\n{chunk_msg}'
254
264
  mrsm.pprint((chunk_success, chunk_msg))
265
+
255
266
  return chunk_begin_and_end, (chunk_success, chunk_msg)
256
267
 
257
268
  ### If we have more than one chunk, attempt to sync the first one and return if its fails.
@@ -284,6 +295,17 @@ def verify(
284
295
  _batch_begin = batch_chunk_bounds[0][0]
285
296
  _batch_end = batch_chunk_bounds[-1][-1]
286
297
  batch_message_header = f"{_batch_begin} - {_batch_end}"
298
+
299
+ if check_rowcounts_only:
300
+ info(f"Checking row-counts for batch bounds:\n {batch_message_header}")
301
+ _, (batch_init_success, batch_init_msg) = process_chunk_bounds(
302
+ (_batch_begin, _batch_end)
303
+ )
304
+ mrsm.pprint((batch_init_success, batch_init_msg))
305
+ if batch_init_success and 'up-to-date' in batch_init_msg:
306
+ info("Entire batch is up-to-date.")
307
+ return batch_init_success, batch_init_msg
308
+
287
309
  batch_bounds_success_tuples = dict(pool.map(process_chunk_bounds, batch_chunk_bounds))
288
310
  bounds_success_tuples.update(batch_bounds_success_tuples)
289
311
  batch_bounds_success_bools = {
@@ -395,7 +417,7 @@ def verify(
395
417
  retry_failed_batch = False
396
418
 
397
419
  batch_msg_to_print = (
398
- f"{make_header('Completed batch ' + batch_counter_str + ' ' + for_self + ':')}\n{batch_msg}"
420
+ f"{make_header('Completed batch ' + batch_counter_str + ':')}\n{batch_msg}"
399
421
  )
400
422
  mrsm.pprint((batch_success, batch_msg_to_print))
401
423
 
@@ -471,11 +493,13 @@ def get_chunks_success_message(
471
493
  header = (header + "\n") if header else ""
472
494
  stats_msg = items_str(
473
495
  (
474
- ([f'inserted {num_inserted:,}'] if num_inserted else [])
475
- + ([f'updated {num_updated:,}'] if num_updated else [])
476
- + ([f'upserted {num_upserted:,}'] if num_upserted else [])
477
- + ([f'checked {num_checked:,}'] if num_checked else [])
478
- ) or ['synced 0'],
496
+ (
497
+ ([f'inserted {num_inserted:,}'] if num_inserted else [])
498
+ + ([f'updated {num_updated:,}'] if num_updated else [])
499
+ + ([f'upserted {num_upserted:,}'] if num_upserted else [])
500
+ + ([f'checked {num_checked:,}'] if num_checked else [])
501
+ ) or ['synced 0']
502
+ ),
479
503
  quotes=False,
480
504
  and_=False,
481
505
  )
@@ -154,7 +154,7 @@ class SystemdExecutor(Executor):
154
154
  STATIC_CONFIG['environment']['systemd_delete_job']: (
155
155
  '1'
156
156
  if job.delete_after_completion
157
- else '0',
157
+ else '0'
158
158
  ),
159
159
  })
160
160
 
@@ -222,7 +222,7 @@ def filter_unseen_df(
222
222
  numeric_cols_precisions_scales = {
223
223
  col: get_numeric_precision_scale(None, typ)
224
224
  for col, typ in dtypes.items()
225
- if col and typ and typ.startswith('numeric')
225
+ if col and str(typ).lower().startswith('numeric')
226
226
  }
227
227
 
228
228
  dt_dtypes = {
@@ -58,7 +58,7 @@ packages: Dict[str, Dict[str, str]] = {
58
58
  '_internal' : {
59
59
  'apscheduler' : (
60
60
  f"{_MRSM_PACKAGE_ARCHIVES_PREFIX}"
61
- "APScheduler-4.0.0a5.post75+mrsm-py3-none-any.whl>=4.0.0a5"
61
+ "APScheduler-4.0.0a5.post87+mrsm-py3-none-any.whl>=4.0.0a5"
62
62
  ),
63
63
  'dataclass_wizard' : 'dataclass-wizard>=0.28.0',
64
64
  },
@@ -9,15 +9,25 @@ See `meerschaum.utils.pool` for multiprocessing and
9
9
  """
10
10
 
11
11
  from __future__ import annotations
12
- import os, signal, subprocess, sys, platform, traceback
12
+
13
+ import os
14
+ import signal
15
+ import subprocess
16
+ import sys
17
+ import platform
18
+
19
+ import meerschaum as mrsm
13
20
  from meerschaum.utils.typing import Union, Optional, Any, Callable, Dict, Tuple
14
21
  from meerschaum.config.static import STATIC_CONFIG
15
22
 
16
23
  _child_processes = []
17
24
  def signal_handler(sig, frame):
18
25
  for child in _child_processes:
19
- child.send_signal(sig)
20
- child.wait()
26
+ try:
27
+ child.send_signal(sig)
28
+ child.wait()
29
+ except Exception:
30
+ pass
21
31
 
22
32
  def run_process(
23
33
  *args,
@@ -223,3 +233,17 @@ def poll_process(
223
233
  watchdog_thread.cancel()
224
234
 
225
235
  return proc.poll()
236
+
237
+
238
+ def _stop_process(
239
+ proc: subprocess.Popen,
240
+ timeout_seconds: int = 8,
241
+ ):
242
+ """
243
+ Stop a `subproccess.Popen` object.
244
+ """
245
+ proc.terminate()
246
+ try:
247
+ proc.wait(timeout=timeout_seconds)
248
+ except subprocess.TimeoutExpired:
249
+ proc.kill()
@@ -95,7 +95,7 @@ def schedule_function(
95
95
  A `SuccessTuple` upon exit.
96
96
  """
97
97
  import asyncio
98
- from meerschaum.utils.misc import filter_keywords, round_time
98
+ from meerschaum.utils.misc import filter_keywords
99
99
 
100
100
  global _scheduler
101
101
  kw['debug'] = debug
@@ -103,7 +103,7 @@ def schedule_function(
103
103
 
104
104
  _ = mrsm.attempt_import('attrs', lazy=False)
105
105
  apscheduler = mrsm.attempt_import('apscheduler', lazy=False)
106
- now = round_time(datetime.now(timezone.utc), timedelta(minutes=1))
106
+ now = datetime.now(timezone.utc)
107
107
  trigger = parse_schedule(schedule, now=now)
108
108
  _scheduler = apscheduler.AsyncScheduler(identity='mrsm-scheduler')
109
109
  try:
@@ -296,7 +296,7 @@ def parse_start_time(schedule: str, now: Optional[datetime] = None) -> datetime:
296
296
  dateutil_parser = mrsm.attempt_import('dateutil.parser')
297
297
  starting_parts = schedule.split(STARTING_KEYWORD)
298
298
  starting_str = ('now' if len(starting_parts) == 1 else starting_parts[-1]).strip()
299
- now = now or round_time(datetime.now(timezone.utc), timedelta(minutes=1))
299
+ now = now or datetime.now(timezone.utc)
300
300
  try:
301
301
  if starting_str == 'now':
302
302
  starting_ts = now
meerschaum/utils/sql.py CHANGED
@@ -1548,7 +1548,7 @@ def get_update_queries(
1548
1548
  from meerschaum.utils.debug import dprint
1549
1549
  from meerschaum.utils.dtypes import are_dtypes_equal
1550
1550
  from meerschaum.utils.dtypes.sql import DB_FLAVORS_CAST_DTYPES, get_pd_type_from_db_type
1551
- flavor = flavor or (connectable.flavor if isinstance(connectable, SQLConnector) else None)
1551
+ flavor = flavor or getattr(connectable, 'flavor', None)
1552
1552
  if not flavor:
1553
1553
  raise ValueError("Provide a flavor if using a SQLAlchemy session.")
1554
1554
  if (
@@ -595,7 +595,7 @@ def venv_exec(
595
595
  as_proc: bool = False,
596
596
  capture_output: bool = True,
597
597
  debug: bool = False,
598
- ) -> Union[bool, Tuple[int, bytes, bytes]]:
598
+ ) -> Union[bool, Tuple[int, bytes, bytes], 'subprocess.Popen']:
599
599
  """
600
600
  Execute Python code in a subprocess via a virtual environment's interpeter.
601
601
  Return `True` if the code successfully executes, `False` on failure.
@@ -630,6 +630,8 @@ def venv_exec(
630
630
  import subprocess
631
631
  import platform
632
632
  from meerschaum.utils.debug import dprint
633
+ from meerschaum.utils.process import _child_processes
634
+
633
635
  executable = venv_executable(venv=venv)
634
636
  cmd_list = [executable, '-c', code]
635
637
  if env is None:
@@ -656,6 +658,7 @@ def venv_exec(
656
658
  **group_kwargs
657
659
  )
658
660
  if as_proc:
661
+ _child_processes.append(process)
659
662
  return process
660
663
  stdout, stderr = process.communicate()
661
664
  exit_code = process.returncode
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: meerschaum
3
- Version: 2.8.2
3
+ Version: 2.8.4
4
4
  Summary: Sync Time-Series Pipes with Meerschaum
5
5
  Home-page: https://meerschaum.io
6
6
  Author: Bennett Meares
@@ -4,7 +4,7 @@ meerschaum/_internal/__init__.py,sha256=ilC7utfKtin7GAvuN34fKyUQYfPyqH0Mm3MJF5iy
4
4
  meerschaum/_internal/entry.py,sha256=Y1m2ar3TWBo_XntPL1P9ehUpjEAfXp2ZGZ-V6r5fXWQ,12438
5
5
  meerschaum/_internal/arguments/__init__.py,sha256=_nSKKVLXNsJeSv-buxEZsx8_c0BAbkhRyE4nT6Bv6q0,541
6
6
  meerschaum/_internal/arguments/_parse_arguments.py,sha256=TtHX7NvdHLD-nVVlMctc6SVULRHXlTgB6sV6UMBEDqY,16430
7
- meerschaum/_internal/arguments/_parser.py,sha256=Mn4kgAqREsF4OcYfk_Ft-Y_WhTtb5yHb3CEvKAJdtlY,17039
7
+ meerschaum/_internal/arguments/_parser.py,sha256=9AfOJ4Kc37y2gLeRabU3cDwF4RRnW-3nGKfN2l0P3rQ,17163
8
8
  meerschaum/_internal/docs/__init__.py,sha256=ZQYHWo6n0kfLLkyG36YXqTYvv2Pc7it5HZHMylT6cBA,126
9
9
  meerschaum/_internal/docs/index.py,sha256=ZkqXj-GhoLEpgMysy4ugvOlFhWVlnq7tFvzMhy43jUQ,24670
10
10
  meerschaum/_internal/gui/__init__.py,sha256=KF6Opae0aBOjIndMZ2txoPs7ozCXRlR-lcTsicLO7fc,1313
@@ -29,7 +29,7 @@ meerschaum/actions/clear.py,sha256=v_xHn7-Pu7iwFNJ07q9eJt2hqPV7OwNZHUYa9dvixs4,4
29
29
  meerschaum/actions/copy.py,sha256=QmlR_Ky-9SSgCV4Tmd3tgOBF63xchA1iZoEPFCYX8zc,6981
30
30
  meerschaum/actions/deduplicate.py,sha256=puYyxeFYEUy1Sd2IOcZB2e6MrNxAZl2bTLmNzFDkCiw,1167
31
31
  meerschaum/actions/delete.py,sha256=H5oP0nE7qIWnFvkuFhZQZQYBVC0IbUevpaQ_2YQKXRA,18559
32
- meerschaum/actions/drop.py,sha256=UZg16dPx1o9f-hBrbIiZ6-F2QOTaTAdmFqLaSQla0t8,4434
32
+ meerschaum/actions/drop.py,sha256=xNgHWFPyvs4lm923BCRwYJULUqGYMbcVnRPb25Y-lzs,4433
33
33
  meerschaum/actions/edit.py,sha256=VpP6bJ-i7GR3tRzs_PILcDKcFCBeTUaeXpZDwxJ4fFk,16224
34
34
  meerschaum/actions/index.py,sha256=byhcGi7P1S0TTcJPBxq3jGKmBF_gZ8t77oqtg0kGSdI,1826
35
35
  meerschaum/actions/install.py,sha256=P_M2dnn3sVC2mAn5hwvu4VHWkoh4r9TBUzNcU_Kukpc,7437
@@ -45,14 +45,14 @@ meerschaum/actions/sh.py,sha256=hSkGNTVqP5dNjhJ64zy3V3VCFPTKnDlH3PxdKdxtkGQ,1990
45
45
  meerschaum/actions/show.py,sha256=y-IKxVo9VDg3z_W5VsfEH5unr7EWUo17y-ZhLuYo5LU,28413
46
46
  meerschaum/actions/sql.py,sha256=PhbBMBq20LL8Yh3q-yqGMe7f8ffmyjbD8q_G8Q0WxZc,4527
47
47
  meerschaum/actions/stack.py,sha256=ZwrCTGJ0x3jjZkRieWcvqasQHYCqNtB1HYvTX-r3Z3g,5996
48
- meerschaum/actions/start.py,sha256=slQ49iImNlV3UsU7CzfMPD6Qms142KguDlEkSitdx6Y,21686
48
+ meerschaum/actions/start.py,sha256=7BPebj698D71ZdlwTG9E-5jLPJ4CeHJ7-XaW3OXoNdY,21927
49
49
  meerschaum/actions/stop.py,sha256=5fdUw70YN-yuUWrC-NhA88cxr9FZ5NbssbQ8xXO8nFU,4632
50
- meerschaum/actions/sync.py,sha256=lKFRUSHat3-m6uNmtVt0KKDD7aPnNnXl-pjnxsBG_EA,17415
50
+ meerschaum/actions/sync.py,sha256=doXa0gQTRoj2X3t3as0_gBCWCO6Dp2x6iujT-5YVvak,17679
51
51
  meerschaum/actions/tag.py,sha256=SJf5qFW0ccLXjqlTdkK_0MCcrCMdg6xhYrhKdco0hdA,3053
52
52
  meerschaum/actions/uninstall.py,sha256=tBXhdXggSieGEQe4EPGxpgMK0MZTJCweNvAJ9-59El0,5776
53
53
  meerschaum/actions/upgrade.py,sha256=AjuC93Te-I_GWwIfuNkFJ2q1zVHDQ2Oco34S4JgK2iA,6485
54
54
  meerschaum/actions/verify.py,sha256=81Km4sPkLVAzPG6pDbMfoBpEWsk8pxjVGXgu-k8seSs,5019
55
- meerschaum/api/__init__.py,sha256=X_ZM0v1OmNBRO9yOeiF6pR7lBvPZcl13jttHBhjteRk,10049
55
+ meerschaum/api/__init__.py,sha256=UsAFx7oOTB9Z8m1u0DRfiQFdSxjoyqgVaICuwv7loDg,10414
56
56
  meerschaum/api/_chain.py,sha256=h8-WXUGXX6AqzdALfsBC5uv0FkAcLdHJXCGzqzuq89k,875
57
57
  meerschaum/api/_events.py,sha256=f-98AXHU10IL9zRGX1FrZFANxxiMz5ryeJnfFWaU8R8,2232
58
58
  meerschaum/api/_exceptions.py,sha256=xfbWp8F8JYrMUdtDXesn8C8e39_jAXHz51IosIGjkVM,463
@@ -126,7 +126,7 @@ meerschaum/api/routes/_index.py,sha256=Z8kuyqm3vmJadw8iIYyswYI4-3IOJ7KXdkhDTv1oU
126
126
  meerschaum/api/routes/_jobs.py,sha256=sEt-UtVd5pN-hJgikTvj1oTKJQ2hhNe8XhjkclwOXOE,12568
127
127
  meerschaum/api/routes/_login.py,sha256=tygEp50EVOMgvTG6CEASlShflbtEK8viJ9O07o0lnnE,2434
128
128
  meerschaum/api/routes/_misc.py,sha256=XxfSvXNGAm8rdvXePXWxX8wc5tjeAdBOSZwYveL3oAM,2591
129
- meerschaum/api/routes/_pipes.py,sha256=kwXuYh8Tm0ZEdjsqywLHXuu7VrU-8vuaRogbT9y2cPs,25403
129
+ meerschaum/api/routes/_pipes.py,sha256=4cHjGNXKnPQsF7_TZClpTv3QyILLFlgYPKQjxNYCqy8,25247
130
130
  meerschaum/api/routes/_plugins.py,sha256=okstNlv9Bhoiy6JvQWgwjxEi4kQ8adPUcir6k3Y7hH8,6329
131
131
  meerschaum/api/routes/_users.py,sha256=i55LuLTQ2cuzIyWz0PxkWji6aQQUIBPf_FEryKwXI50,7197
132
132
  meerschaum/api/routes/_version.py,sha256=-3A0i4Gk54netFOOwjI_x3YQik9vgHjtq7G_VYbzIJo,750
@@ -134,7 +134,7 @@ meerschaum/api/routes/_webterm.py,sha256=S7RXV8vvaTFbmVeehh4UhyXb4NCgcsyOQzoAG7j
134
134
  meerschaum/api/tables/__init__.py,sha256=e2aNC0CdlWICTUMx1i9RauF8Pm426J0RZJbsJWv4SWo,482
135
135
  meerschaum/config/__init__.py,sha256=5ZBq71P9t3nb74r5CGvMfNuauPscfegBX-nkaAUi5C4,11541
136
136
  meerschaum/config/_dash.py,sha256=BJHl4xMrQB-YHUEU7ldEW8q_nOPoIRSOqLrfGElc6Dw,187
137
- meerschaum/config/_default.py,sha256=F5xaXGQ6-v0fZpmUj2y8qE7iO-enQviwGAnUbvz8_3k,6419
137
+ meerschaum/config/_default.py,sha256=F1xVug-lhVeIXTL1wvHrIPFmBsL2dGz4lHFakIleeZk,6473
138
138
  meerschaum/config/_edit.py,sha256=M9yX_SDD24gV5kWITZpy7p9AWTizJsIAGWAs3WZx-Ws,9087
139
139
  meerschaum/config/_environment.py,sha256=Vv4DLDfc2vKLbCLsMvkQDj77K4kEvHKEBmUBo-wCrgo,4419
140
140
  meerschaum/config/_formatting.py,sha256=OMuqS1EWOsj_34wSs2tOqGIWci3bTMIZ5l-uelZgsIM,6672
@@ -145,7 +145,7 @@ meerschaum/config/_preprocess.py,sha256=-AEA8m_--KivZwTQ1sWN6LTn5sio_fUr2XZ51BO6
145
145
  meerschaum/config/_read_config.py,sha256=RLC3HHi_1ndj7ITVDKLD9_uULY3caGRwSz3ATYE-ixA,15014
146
146
  meerschaum/config/_shell.py,sha256=46_m49Txc5q1rGfCgO49ca48BODx45DQJi8D0zz1R18,4245
147
147
  meerschaum/config/_sync.py,sha256=jHcWRkxd82_BgX8Xo8agsWvf7BSbv3qHLWmYl6ehp_0,4242
148
- meerschaum/config/_version.py,sha256=B0gGbZXyLfnqfiT23qBJe-NHZMivqpuqpG5wrd_mK5o,71
148
+ meerschaum/config/_version.py,sha256=qo9eC8a6skjLulPkTYodkLffX9t1hvHTg8JGgbZo4Do,71
149
149
  meerschaum/config/paths.py,sha256=JjibeGN3YAdSNceRwsd42aNmeUrIgM6ndzC8qZAmNI0,621
150
150
  meerschaum/config/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
151
151
  meerschaum/config/stack/__init__.py,sha256=2UukC0Lmk-aVL1o1qXzumqmuIrw3vu9fD7iCuz4XD4I,10544
@@ -153,7 +153,7 @@ meerschaum/config/stack/grafana/__init__.py,sha256=LNXQw2FvHKrD68RDhqDmi2wJjAHaK
153
153
  meerschaum/config/stack/mosquitto/__init__.py,sha256=-OwOjq8KiBoSH_pmgCAAF3Dp3CRD4KgAEdimZSadROs,186
154
154
  meerschaum/config/stack/mosquitto/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
155
155
  meerschaum/config/stack/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
156
- meerschaum/config/static/__init__.py,sha256=X2iAAJs2OFRolAUApHY6MLcSmc9PG3QgcKyacRmO9wg,5519
156
+ meerschaum/config/static/__init__.py,sha256=ccm5oaYnLu0j5B85C9a8Y7jWsw4ReJq5RqyvLd-rW_s,5519
157
157
  meerschaum/connectors/_Connector.py,sha256=VaVNg0SlQCTXk4shl3c68QdkbymA2Y9ScUlUjckk8PY,6795
158
158
  meerschaum/connectors/__init__.py,sha256=bpWsnU0uvoowWyUkFTwfEkadK84pssZUJ4M7yReudOU,12703
159
159
  meerschaum/connectors/parse.py,sha256=tnqzkzt_suOXYzktn_XVUrprtfym9ThijUf8HXZZlhY,4194
@@ -172,17 +172,17 @@ meerschaum/connectors/api/_uri.py,sha256=HWxqGx4R1cHZ3ywy9Ro9ePbFxxusw4RLaC3hpGt
172
172
  meerschaum/connectors/api/_users.py,sha256=kzb7ENgXwQ19OJYKOuuWzx2rwVuUZCly9dTnyvVuT2Q,5275
173
173
  meerschaum/connectors/plugin/PluginConnector.py,sha256=aQ1QaB7MordCFimZqoGLb0R12PfDUN_nWks2J5mzeAs,2084
174
174
  meerschaum/connectors/plugin/__init__.py,sha256=pwF7TGY4WNz2_HaVdmK4rPQ9ZwTOEuPHgzOqsGcoXJw,198
175
- meerschaum/connectors/sql/_SQLConnector.py,sha256=B2-ltIIQy_n2qjeeT-sZk60tLJrg2Oefscytt7sGqb4,12083
175
+ meerschaum/connectors/sql/_SQLConnector.py,sha256=JVTK2LdUAtgK_fg0zgHOxgJHw7X7s0SHOunBZ9b-ai8,12084
176
176
  meerschaum/connectors/sql/__init__.py,sha256=3cqYiDkVasn7zWdtOTAZbT4bo95AuvGOmDD2TkaAxtw,205
177
- meerschaum/connectors/sql/_cli.py,sha256=xzTPMplDU-E2kPq_gPgN1Cq9RqpcEFwGavAYunAwEaw,4873
178
- meerschaum/connectors/sql/_create_engine.py,sha256=VfGtqF2hDNbgl2g-ZPcz28gKkfL6UOk4rKxUB4tKHdU,10567
177
+ meerschaum/connectors/sql/_cli.py,sha256=3wXRfPSr5mXlM6Wt8UqrBYfWvkLVZ4jTzKRUd04enCo,5116
178
+ meerschaum/connectors/sql/_create_engine.py,sha256=h7c1nwdDWi33PBkRioPomHRT8h1DLc08EQ7INWyC_1Q,10717
179
179
  meerschaum/connectors/sql/_fetch.py,sha256=mVe5zQo7SM9PSUU3Vjhacg4Bq1-Vttb7KkXL4p5YQdQ,12818
180
180
  meerschaum/connectors/sql/_instance.py,sha256=xCc8M0xWMzF5Tu_1uWIFivAoHey5N1ccFhN_Z7u04zk,6304
181
181
  meerschaum/connectors/sql/_pipes.py,sha256=8pp10lOYX8oVEqKHB9907Jny4nkT9fTgF8PrvhsdohI,128855
182
182
  meerschaum/connectors/sql/_plugins.py,sha256=OVEdZ_UHTi-x5sF-5lu2TmR9ONxddp6SwDOmFo5TpU8,8051
183
183
  meerschaum/connectors/sql/_sql.py,sha256=3hrANOId2DAoXsl8nvePnxvoXo5rtB5UfQsJK_fCY9s,42696
184
184
  meerschaum/connectors/sql/_uri.py,sha256=BFzu5pjlbL3kxLH13vHWlpKGYTPfg8wuA2j58O9NsCM,3440
185
- meerschaum/connectors/sql/_users.py,sha256=Dbe79FV07ms5QVHwp68cruDzkGeKR4cE1-xc9Gza8Hs,9932
185
+ meerschaum/connectors/sql/_users.py,sha256=mRyjsUCfPV52nfTQUbpu9gMXfV_DHXNqEhw4N-lSS4Q,9954
186
186
  meerschaum/connectors/sql/tools.py,sha256=jz8huOaRCwGlYdtGfAqAh7SoK8uydYBrasKQba9FT38,187
187
187
  meerschaum/connectors/sql/tables/__init__.py,sha256=53EeJsvmGjj68SpSShdt6kyLuk5Md5O8DnvWC1ra3u8,8876
188
188
  meerschaum/connectors/sql/tables/types.py,sha256=Jc_MTHIBM-KHpQt__Lckp39CeOo7tGOiAk5faDx-znY,1573
@@ -209,29 +209,29 @@ meerschaum/core/Pipe/_index.py,sha256=cYgaVwBVfAYxJBZ6j6MXDqOxnOrD_QnYi33_kIwy_F
209
209
  meerschaum/core/Pipe/_register.py,sha256=Sd5xaAW8H7uLTIoommcKb-6kHPRuHJLWNSbPnt2UbvA,2240
210
210
  meerschaum/core/Pipe/_show.py,sha256=nG50y8eBT9TVuKkRgAKtNDNIxysJvMNxfu__lkL1F9k,1352
211
211
  meerschaum/core/Pipe/_sync.py,sha256=NGiGhBI7M_nyIvVvyIA9_4jUKis1YpK4YL_pnceYGjg,38077
212
- meerschaum/core/Pipe/_verify.py,sha256=3BcFYGYCTQMTiAA7SVbWuKlTKPr96XYYgbdEMqk2bpM,21630
212
+ meerschaum/core/Pipe/_verify.py,sha256=aXRpD6azrN9A9Z3AXkKQw6NJFqNWqZVpgidjh5BsfLE,22565
213
213
  meerschaum/core/Plugin/__init__.py,sha256=UXg64EvJPgI1PCxkY_KM02-ZmBm4FZpLPIQR_uSJJDc,137
214
214
  meerschaum/core/User/_User.py,sha256=qbI0GIkr3G0PI4d9S49uatbJQ2kH_-z5-GoVJ0fuEtA,6624
215
215
  meerschaum/core/User/__init__.py,sha256=8WkDRSBmxsprnKE7GB6j1yXSfEz9RinRkSzW0bzVP1I,870
216
216
  meerschaum/jobs/_Executor.py,sha256=qM62BhFTM4tyJ7p90KOM0y3qyeRY9k3ZV_aTDJMHnO8,1682
217
217
  meerschaum/jobs/_Job.py,sha256=D4TFrEQQ9EYqZrnepLBO7T3Sx2mOOdgBs2JnBdC1dNc,32257
218
218
  meerschaum/jobs/__init__.py,sha256=YmZr1ZU8MLUjlTSLQ61Y6eE-a-SYuSMSMhiIpJKao_Y,12829
219
- meerschaum/jobs/systemd.py,sha256=Rq-tsDPslG17ZhpKMrVJ5r8Z0IPr6DEc9APObfIoXCg,24614
219
+ meerschaum/jobs/systemd.py,sha256=CZbBkm-2z8z7G3CNmP5yBRj2VtWuxmxXVIv97cGhZGc,24613
220
220
  meerschaum/plugins/_Plugin.py,sha256=bIo4HX8TTWIcwIHROwMt4VK6OoEUhY_3Qc8q-2dp-ZA,33895
221
221
  meerschaum/plugins/__init__.py,sha256=Kl7Dz0CwUUxyjRC5RWnYo6WMLsOvdX2eQ38Rh3BjdzY,26465
222
222
  meerschaum/plugins/bootstrap.py,sha256=VwjpZAuYdqPJW0YoVgAoM_taHkdQHqP902-8T7OWWCI,11339
223
223
  meerschaum/utils/__init__.py,sha256=QrK1K9hIbPCRCM5k2nZGFqGnrqhA0Eh-iSmCU7FG6Cs,612
224
224
  meerschaum/utils/_get_pipes.py,sha256=tu4xKPoDn79Dz2kWM13cXTP4DSCkn-3G9M8KiLftopw,11073
225
- meerschaum/utils/dataframe.py,sha256=eMJO7WnHsbYpRiidOE0i2qSPsnsusvGQon186dF_VRA,49336
225
+ meerschaum/utils/dataframe.py,sha256=f9h3fmG_ePHHls2NQmGHNqUurUEZBBvpS1UR7tQgwjI,49341
226
226
  meerschaum/utils/debug.py,sha256=GyIzJmunkoPnOcZNYVQdT4Sgd-aOb5MI2VbIgATOjIQ,3695
227
227
  meerschaum/utils/interactive.py,sha256=t-6jWozXSqL7lYGDHuwiOjTgr-UKhdcg61q_eR5mikI,3196
228
228
  meerschaum/utils/misc.py,sha256=8TOQQlFyF_aYnc8tnx98lccXr9tFrdlS-ngXeOQjHHY,47407
229
229
  meerschaum/utils/networking.py,sha256=Sr_eYUGW8_UV9-k9LqRFf7xLtbUcsDucODyLCRsFRUc,1006
230
230
  meerschaum/utils/pool.py,sha256=vkE42af4fjrTEJTxf6Ek3xGucm1MtEkpsSEiaVzNKHs,2655
231
- meerschaum/utils/process.py,sha256=9O8PPPJjY9Q5W2f39I3B3lFU6TlSiRiI3bgrzdOOyOw,7843
231
+ meerschaum/utils/process.py,sha256=as0-CjG4mqFP0TydVvmAmgki6er4thS5BqUopeiq98Q,8216
232
232
  meerschaum/utils/prompt.py,sha256=qj1As1tuiL0GZTku_YOC6I5DmOU6L5otDR7DW7LA5fM,19397
233
- meerschaum/utils/schedule.py,sha256=bUsaCO9CGn2vJO5UvoISScHDDGIiMdCPHxpTFmu7vwE,11531
234
- meerschaum/utils/sql.py,sha256=1NXEnflYE3GyZGzR6BDX5cDLmdvdDqnvwhE5qQzKhf0,80322
233
+ meerschaum/utils/schedule.py,sha256=Vrcd2Qs-UPVn6xBayNUIgludf0Mlb6Wrgq6ATdyhV8c,11451
234
+ meerschaum/utils/sql.py,sha256=2p8pa3kBCE5lM6tDle5HV4AamZCgOpnKmCB6mdkiLUA,80287
235
235
  meerschaum/utils/threading.py,sha256=awjbVL_QR6G-o_9Qk85utac9cSdqkiC8tQSdERCdrG8,2814
236
236
  meerschaum/utils/typing.py,sha256=U3MC347sh1umpa3Xr1k71eADyDmk4LB6TnVCpq8dVzI,2830
237
237
  meerschaum/utils/warnings.py,sha256=n-phr3BftNNgyPnvnXC_VMSjtCvjiCZ-ewmVfcROhkc,6611
@@ -250,15 +250,15 @@ meerschaum/utils/formatting/_pipes.py,sha256=gwl8-xCN5GYqBZJ7SkY20BebcofY0nU5X8Y
250
250
  meerschaum/utils/formatting/_pprint.py,sha256=wyTmjHFnsHbxfyuytjTWzH-D42Z65GuIisQ_W6UnRPg,3096
251
251
  meerschaum/utils/formatting/_shell.py,sha256=XH7VFLteNv7NGtWhJl7FdIGt80sKeTiDoJokGSDAwBM,3761
252
252
  meerschaum/utils/packages/__init__.py,sha256=TdKaj2tmN4bFwzusOfMv24P5ET7Zv73vyoOf9GOIr5E,64427
253
- meerschaum/utils/packages/_packages.py,sha256=UcQ0yEK3lIy4jSETHxrOylArUw5qnf6RFSFiuwL9bQY,8868
253
+ meerschaum/utils/packages/_packages.py,sha256=yeeMIUWr7TkMknGmkzUj4_BJtidOoCadW7m0C389_UQ,8868
254
254
  meerschaum/utils/packages/lazy_loader.py,sha256=VHnph3VozH29R4JnSSBfwtA5WKZYZQFT_GeQSShCnuc,2540
255
255
  meerschaum/utils/venv/_Venv.py,sha256=gc1TCeAj-kTZbQFAT9xl1bi4HXFV5ApT0dPOJfxwr78,3748
256
- meerschaum/utils/venv/__init__.py,sha256=-Mpfvz1mJ41TzVL0xKJCKOsU3nFi6XabbNiN7UbdVXs,27067
257
- meerschaum-2.8.2.dist-info/LICENSE,sha256=jG2zQEdRNt88EgHUWPpXVWmOrOduUQRx7MnYV9YIPaw,11359
258
- meerschaum-2.8.2.dist-info/METADATA,sha256=BbCV2FeNpThFdyh2xezk358MuN_0KrxqiyI9r9wnlU4,24489
259
- meerschaum-2.8.2.dist-info/NOTICE,sha256=OTA9Fcthjf5BRvWDDIcBC_xfLpeDV-RPZh3M-HQBRtQ,114
260
- meerschaum-2.8.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
261
- meerschaum-2.8.2.dist-info/entry_points.txt,sha256=5YBVzibw-0rNA_1VjB16z5GABsOGf-CDhW4yqH8C7Gc,88
262
- meerschaum-2.8.2.dist-info/top_level.txt,sha256=bNoSiDj0El6buocix-FRoAtJOeq1qOF5rRm2u9i7Q6A,11
263
- meerschaum-2.8.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
264
- meerschaum-2.8.2.dist-info/RECORD,,
256
+ meerschaum/utils/venv/__init__.py,sha256=AJH9BEO2Eadwn17E_uynca_picaJTlMPP_s4y5RHpJw,27187
257
+ meerschaum-2.8.4.dist-info/LICENSE,sha256=jG2zQEdRNt88EgHUWPpXVWmOrOduUQRx7MnYV9YIPaw,11359
258
+ meerschaum-2.8.4.dist-info/METADATA,sha256=ADfvDN1hru7HrOqr5MwrRFd0qLB9Y2ewvoa3H0nE9Ss,24489
259
+ meerschaum-2.8.4.dist-info/NOTICE,sha256=OTA9Fcthjf5BRvWDDIcBC_xfLpeDV-RPZh3M-HQBRtQ,114
260
+ meerschaum-2.8.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
261
+ meerschaum-2.8.4.dist-info/entry_points.txt,sha256=5YBVzibw-0rNA_1VjB16z5GABsOGf-CDhW4yqH8C7Gc,88
262
+ meerschaum-2.8.4.dist-info/top_level.txt,sha256=bNoSiDj0El6buocix-FRoAtJOeq1qOF5rRm2u9i7Q6A,11
263
+ meerschaum-2.8.4.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
264
+ meerschaum-2.8.4.dist-info/RECORD,,