meerschaum 3.0.0rc1__py3-none-any.whl → 3.0.0rc2__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.
Files changed (45) hide show
  1. meerschaum/_internal/arguments/_parser.py +2 -1
  2. meerschaum/_internal/docs/index.py +49 -2
  3. meerschaum/_internal/static.py +8 -24
  4. meerschaum/actions/verify.py +5 -8
  5. meerschaum/api/__init__.py +2 -1
  6. meerschaum/api/dash/__init__.py +0 -2
  7. meerschaum/api/dash/callbacks/dashboard.py +1 -1
  8. meerschaum/api/dash/tokens.py +2 -2
  9. meerschaum/api/routes/_pipes.py +47 -37
  10. meerschaum/config/_default.py +11 -1
  11. meerschaum/config/_version.py +1 -1
  12. meerschaum/config/stack/__init__.py +9 -8
  13. meerschaum/connectors/api/_pipes.py +2 -18
  14. meerschaum/connectors/api/_tokens.py +2 -2
  15. meerschaum/connectors/instance/_tokens.py +4 -4
  16. meerschaum/connectors/sql/_create_engine.py +3 -14
  17. meerschaum/connectors/sql/_pipes.py +118 -163
  18. meerschaum/connectors/sql/_sql.py +38 -20
  19. meerschaum/connectors/valkey/_pipes.py +44 -16
  20. meerschaum/core/Pipe/__init__.py +28 -5
  21. meerschaum/core/Pipe/_attributes.py +270 -46
  22. meerschaum/core/Pipe/_data.py +55 -17
  23. meerschaum/core/Pipe/_dtypes.py +19 -4
  24. meerschaum/core/Pipe/_edit.py +2 -0
  25. meerschaum/core/Pipe/_fetch.py +1 -1
  26. meerschaum/core/Pipe/_sync.py +90 -160
  27. meerschaum/core/Pipe/_verify.py +3 -3
  28. meerschaum/core/Token/_Token.py +3 -4
  29. meerschaum/utils/dataframe.py +379 -68
  30. meerschaum/utils/debug.py +15 -15
  31. meerschaum/utils/dtypes/__init__.py +388 -22
  32. meerschaum/utils/dtypes/sql.py +326 -30
  33. meerschaum/utils/misc.py +9 -68
  34. meerschaum/utils/packages/__init__.py +7 -21
  35. meerschaum/utils/packages/_packages.py +7 -2
  36. meerschaum/utils/schedule.py +1 -1
  37. meerschaum/utils/sql.py +7 -7
  38. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc2.dist-info}/METADATA +5 -17
  39. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc2.dist-info}/RECORD +45 -44
  40. meerschaum-3.0.0rc2.dist-info/licenses/NOTICE +2 -0
  41. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc2.dist-info}/WHEEL +0 -0
  42. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc2.dist-info}/entry_points.txt +0 -0
  43. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc2.dist-info}/licenses/LICENSE +0 -0
  44. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc2.dist-info}/top_level.txt +0 -0
  45. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc2.dist-info}/zip-safe +0 -0
@@ -28,6 +28,7 @@ from meerschaum.utils.typing import (
28
28
  List,
29
29
  )
30
30
  from meerschaum.utils.warnings import warn, error
31
+ from meerschaum._internal.static import STATIC_CONFIG
31
32
 
32
33
  if TYPE_CHECKING:
33
34
  pd = mrsm.attempt_import('pandas')
@@ -136,7 +137,7 @@ def sync(
136
137
  from meerschaum.utils.misc import df_is_chunk_generator, filter_keywords, filter_arguments
137
138
  from meerschaum.utils.pool import get_pool
138
139
  from meerschaum.config import get_config
139
- from meerschaum.utils.dtypes import are_dtypes_equal
140
+ from meerschaum.utils.dtypes import are_dtypes_equal, get_current_timestamp
140
141
 
141
142
  if (callback is not None or error_callback is not None) and blocking:
142
143
  warn("Callback functions are only executed when blocking = False. Ignoring...")
@@ -164,8 +165,8 @@ def sync(
164
165
  'safe_copy': True,
165
166
  })
166
167
 
167
- ### NOTE: Invalidate `_exists` cache before and after syncing.
168
- self._exists = None
168
+ self._invalidate_cache(debug=debug)
169
+ self._sync_ts = get_current_timestamp('ms')
169
170
 
170
171
  def _sync(
171
172
  p: mrsm.Pipe,
@@ -178,7 +179,7 @@ def sync(
178
179
  ] = InferFetch,
179
180
  ) -> SuccessTuple:
180
181
  if df is None:
181
- p._exists = None
182
+ p._invalidate_cache(debug=debug)
182
183
  return (
183
184
  False,
184
185
  f"You passed `None` instead of data into `sync()` for {p}.\n"
@@ -190,7 +191,7 @@ def sync(
190
191
  register_success, register_msg = p.register(debug=debug)
191
192
  if not register_success:
192
193
  if 'already' not in register_msg:
193
- p._exists = None
194
+ p._invalidate_cache(debug=debug)
194
195
  return register_success, register_msg
195
196
 
196
197
  if isinstance(df, str):
@@ -211,10 +212,10 @@ def sync(
211
212
  msg = f"{p} does not have a valid connector."
212
213
  if p.connector_keys.startswith('plugin:'):
213
214
  msg += f"\n Perhaps {p.connector_keys} has a syntax error?"
214
- p._exists = None
215
+ p._invalidate_cache(debug=debug)
215
216
  return False, msg
216
217
  except Exception:
217
- p._exists = None
218
+ p._invalidate_cache(debug=debug)
218
219
  return False, f"Unable to create the connector for {p}."
219
220
 
220
221
  ### Sync in place if possible.
@@ -228,7 +229,7 @@ def sync(
228
229
  get_config('system', 'experimental', 'inplace_sync')
229
230
  ):
230
231
  with Venv(get_connector_plugin(self.instance_connector)):
231
- p._exists = None
232
+ p._invalidate_cache(debug=debug)
232
233
  _args, _kwargs = filter_arguments(
233
234
  p.instance_connector.sync_pipe_inplace,
234
235
  p,
@@ -251,7 +252,7 @@ def sync(
251
252
  **kw
252
253
  )
253
254
  return_tuple = p.connector.sync(*_args, **_kwargs)
254
- p._exists = None
255
+ p._invalidate_cache(debug=debug)
255
256
  if not isinstance(return_tuple, tuple):
256
257
  return_tuple = (
257
258
  False,
@@ -264,7 +265,7 @@ def sync(
264
265
  msg = f"Failed to sync {p} with exception: '" + str(e) + "'"
265
266
  if debug:
266
267
  error(msg, silent=False)
267
- p._exists = None
268
+ p._invalidate_cache(debug=debug)
268
269
  return False, msg
269
270
 
270
271
  ### Fetch the dataframe from the connector's `fetch()` method.
@@ -289,7 +290,7 @@ def sync(
289
290
  df = None
290
291
 
291
292
  if df is None:
292
- p._exists = None
293
+ p._invalidate_cache(debug=debug)
293
294
  return False, f"No data were fetched for {p}."
294
295
 
295
296
  if isinstance(df, list):
@@ -303,7 +304,7 @@ def sync(
303
304
  return success, message
304
305
 
305
306
  if df is True:
306
- p._exists = None
307
+ p._invalidate_cache(debug=debug)
307
308
  return True, f"{p} is being synced in parallel."
308
309
 
309
310
  ### CHECKPOINT: Retrieved the DataFrame.
@@ -347,7 +348,7 @@ def sync(
347
348
  + f"(attempt {_chunk_attempts} / {_max_chunk_attempts}).\n"
348
349
  + f"Sleeping for {_sleep_seconds} second"
349
350
  + ('s' if _sleep_seconds != 1 else '')
350
- + ":\n{_chunk_msg}"
351
+ + f":\n{_chunk_msg}"
351
352
  ),
352
353
  stack=False,
353
354
  )
@@ -400,34 +401,45 @@ def sync(
400
401
  return success, msg
401
402
 
402
403
  ### Cast to a dataframe and ensure datatypes are what we expect.
403
- df = self.enforce_dtypes(
404
+ dtypes = p.get_dtypes(debug=debug)
405
+ df = p.enforce_dtypes(
404
406
  df,
405
407
  chunksize=chunksize,
406
408
  enforce=enforce_dtypes,
409
+ dtypes=dtypes,
407
410
  debug=debug,
408
411
  )
409
412
  if p.autotime:
410
- dt_col = p.columns.get('datetime', 'ts')
411
- dt_typ = p.dtypes.get(dt_col, 'datetime') if dt_col else 'datetime'
412
- if dt_col and hasattr(df, 'columns') and dt_col not in df.columns:
413
- now = datetime.now(timezone.utc)
414
- now_val = (
415
- int(now.timestamp() * 1000)
416
- if are_dtypes_equal(dt_typ, 'int')
417
- else now
413
+ dt_col = p.columns.get('datetime', None)
414
+ ts_col = dt_col or mrsm.get_config(
415
+ 'pipes', 'autotime', 'column_name_if_datetime_missing'
416
+ )
417
+ ts_typ = dtypes.get(ts_col, 'datetime') if ts_col else 'datetime'
418
+ if ts_col and hasattr(df, 'columns') and ts_col not in df.columns:
419
+ precision = p.get_precision(debug=debug)
420
+ now = get_current_timestamp(
421
+ precision_unit=precision.get(
422
+ 'unit',
423
+ STATIC_CONFIG['dtypes']['datetime']['default_precision_unit']
424
+ ),
425
+ precision_interval=precision.get('interval', 1),
426
+ round_to=(precision.get('round_to', 'down')),
427
+ as_int=(are_dtypes_equal(ts_typ, 'int')),
418
428
  )
419
429
  if debug:
420
- dprint(f"Adding current timestamp to dataframe synced to {p}: {now_val}")
430
+ dprint(f"Adding current timestamp to dataframe synced to {p}: {now}")
421
431
 
422
- df[dt_col] = now_val
423
- kw['check_existing'] = False
432
+ df[ts_col] = now
433
+ kw['check_existing'] = dt_col is not None
424
434
 
425
- ### Capture `numeric`, `uuid`, `json`, and `bytes` columns.
426
- self._persist_new_json_columns(df, debug=debug)
427
- self._persist_new_numeric_columns(df, debug=debug)
428
- self._persist_new_uuid_columns(df, debug=debug)
429
- self._persist_new_bytes_columns(df, debug=debug)
430
- self._persist_new_geometry_columns(df, debug=debug)
435
+ ### Capture special columns.
436
+ capture_success, capture_msg = self._persist_new_special_columns(
437
+ df,
438
+ dtypes=dtypes,
439
+ debug=debug,
440
+ )
441
+ if not capture_success:
442
+ warn(f"Failed to capture new special columns for {self}:\n{capture_msg}")
431
443
 
432
444
  if debug:
433
445
  dprint(
@@ -467,18 +479,17 @@ def sync(
467
479
 
468
480
  ### CHECKPOINT: Finished syncing. Handle caching.
469
481
  _checkpoint(**kw)
470
- if self.cache_pipe is not None:
482
+ if p.cache_pipe is not None:
471
483
  if debug:
472
484
  dprint("Caching retrieved dataframe.", **kw)
473
- _sync_cache_tuple = self.cache_pipe.sync(df, debug=debug, **kw)
485
+ _sync_cache_tuple = p.cache_pipe.sync(df, debug=debug, **kw)
474
486
  if not _sync_cache_tuple[0]:
475
487
  warn(f"Failed to sync local cache for {self}.")
476
488
 
477
- self._exists = None
489
+ p._invalidate_cache(debug=debug)
478
490
  return return_tuple
479
491
 
480
492
  if blocking:
481
- self._exists = None
482
493
  return _sync(self, df=df)
483
494
 
484
495
  from meerschaum.utils.threading import Thread
@@ -503,10 +514,10 @@ def sync(
503
514
  )
504
515
  thread.start()
505
516
  except Exception as e:
506
- self._exists = None
517
+ self._invalidate_cache(debug=debug)
507
518
  return False, str(e)
508
519
 
509
- self._exists = None
520
+ self._invalidate_cache(debug=debug)
510
521
  return True, f"Spawned asyncronous sync for {self}."
511
522
 
512
523
 
@@ -552,7 +563,8 @@ def get_sync_time(
552
563
  """
553
564
  from meerschaum.utils.venv import Venv
554
565
  from meerschaum.connectors import get_connector_plugin
555
- from meerschaum.utils.misc import round_time, filter_keywords
566
+ from meerschaum.utils.misc import filter_keywords
567
+ from meerschaum.utils.dtypes import round_time
556
568
  from meerschaum.utils.warnings import warn
557
569
 
558
570
  if not self.columns.get('datetime', None):
@@ -611,17 +623,16 @@ def exists(
611
623
  import time
612
624
  from meerschaum.utils.venv import Venv
613
625
  from meerschaum.connectors import get_connector_plugin
614
- from meerschaum.config import STATIC_CONFIG
615
626
  from meerschaum.utils.debug import dprint
616
627
  now = time.perf_counter()
617
- exists_timeout_seconds = STATIC_CONFIG['pipes']['exists_timeout_seconds']
628
+ cache_seconds = mrsm.get_config('pipes', 'sync', 'exists_cache_seconds')
618
629
 
619
630
  _exists = self.__dict__.get('_exists', None)
620
631
  if _exists:
621
632
  exists_timestamp = self.__dict__.get('_exists_timestamp', None)
622
633
  if exists_timestamp is not None:
623
634
  delta = now - exists_timestamp
624
- if delta < exists_timeout_seconds:
635
+ if delta < cache_seconds:
625
636
  if debug:
626
637
  dprint(f"Returning cached `exists` for {self} ({round(delta, 2)} seconds old).")
627
638
  return _exists
@@ -686,7 +697,6 @@ def filter_existing(
686
697
  from meerschaum.utils.warnings import warn
687
698
  from meerschaum.utils.debug import dprint
688
699
  from meerschaum.utils.packages import attempt_import, import_pandas
689
- from meerschaum.utils.misc import round_time
690
700
  from meerschaum.utils.dataframe import (
691
701
  filter_unseen_df,
692
702
  add_missing_cols_to_df,
@@ -698,6 +708,7 @@ def filter_existing(
698
708
  to_datetime,
699
709
  are_dtypes_equal,
700
710
  value_is_null,
711
+ round_time,
701
712
  )
702
713
  from meerschaum.config import get_config
703
714
  pd = import_pandas()
@@ -713,11 +724,13 @@ def filter_existing(
713
724
  merge = pd.merge
714
725
  NA = pd.NA
715
726
 
716
- primary_key = self.columns.get('primary', None)
717
- dt_col = self.columns.get('datetime', None)
718
- autoincrement = self.parameters.get('autoincrement', False)
719
- autotime = self.parameters.get('autotime', False)
720
- pipe_columns = self.columns.copy()
727
+ parameters = self.parameters
728
+ pipe_columns = parameters.get('columns', {})
729
+ primary_key = pipe_columns.get('primary', None)
730
+ dt_col = pipe_columns.get('datetime', None)
731
+ dt_type = parameters.get('dtypes', {}).get(dt_col, 'datetime') if dt_col else None
732
+ autoincrement = parameters.get('autoincrement', False)
733
+ autotime = parameters.get('autotime', False)
721
734
 
722
735
  if primary_key and autoincrement and df is not None and primary_key in df.columns:
723
736
  if safe_copy:
@@ -738,7 +751,7 @@ def filter_existing(
738
751
  def get_empty_df():
739
752
  empty_df = pd.DataFrame([])
740
753
  dtypes = dict(df.dtypes) if df is not None else {}
741
- dtypes.update(self.dtypes)
754
+ dtypes.update(self.dtypes) if self.enforce else {}
742
755
  pd_dtypes = {
743
756
  col: to_pandas_dtype(str(typ))
744
757
  for col, typ in dtypes.items()
@@ -754,9 +767,6 @@ def filter_existing(
754
767
 
755
768
  ### begin is the oldest data in the new dataframe
756
769
  begin, end = None, None
757
- dt_col = pipe_columns.get('datetime', None)
758
- primary_key = pipe_columns.get('primary', None)
759
- dt_type = self.dtypes.get(dt_col, 'datetime64[ns, UTC]') if dt_col else None
760
770
 
761
771
  if autoincrement and primary_key == dt_col and dt_col not in df.columns:
762
772
  if enforce_dtypes:
@@ -884,7 +894,8 @@ def filter_existing(
884
894
  and col in backtrack_df.columns
885
895
  )
886
896
  ] if not primary_key else [primary_key]
887
- self_dtypes = self.dtypes
897
+
898
+ self_dtypes = self.get_dtypes(debug=debug) if self.enforce else {}
888
899
  on_cols_dtypes = {
889
900
  col: to_pandas_dtype(typ)
890
901
  for col, typ in self_dtypes.items()
@@ -1037,117 +1048,36 @@ def get_num_workers(self, workers: Optional[int] = None) -> int:
1037
1048
  )
1038
1049
 
1039
1050
 
1040
- def _persist_new_numeric_columns(self, df, debug: bool = False) -> SuccessTuple:
1041
- """
1042
- Check for new numeric columns and update the parameters.
1043
- """
1044
- from meerschaum.utils.dataframe import get_numeric_cols
1045
- numeric_cols = get_numeric_cols(df)
1046
- existing_numeric_cols = [col for col, typ in self.dtypes.items() if typ.startswith('numeric')]
1047
- new_numeric_cols = [col for col in numeric_cols if col not in existing_numeric_cols]
1048
- if not new_numeric_cols:
1049
- return True, "Success"
1050
-
1051
- self._attributes_sync_time = None
1052
- dtypes = self.parameters.get('dtypes', {})
1053
- dtypes.update({col: 'numeric' for col in new_numeric_cols})
1054
- return self.update_parameters({'dtypes': dtypes}, debug=debug)
1055
-
1056
-
1057
- def _persist_new_uuid_columns(self, df, debug: bool = False) -> SuccessTuple:
1058
- """
1059
- Check for new numeric columns and update the parameters.
1060
- """
1061
- from meerschaum.utils.dataframe import get_uuid_cols
1062
- uuid_cols = get_uuid_cols(df)
1063
- existing_uuid_cols = [col for col, typ in self.dtypes.items() if typ == 'uuid']
1064
- new_uuid_cols = [col for col in uuid_cols if col not in existing_uuid_cols]
1065
- if not new_uuid_cols:
1066
- return True, "Success"
1067
-
1068
- self._attributes_sync_time = None
1069
- dtypes = self.parameters.get('dtypes', {})
1070
- dtypes.update({col: 'uuid' for col in new_uuid_cols})
1071
- return self.update_parameters({'dtypes': dtypes}, debug=debug)
1072
-
1073
-
1074
- def _persist_new_json_columns(self, df, debug: bool = False) -> SuccessTuple:
1075
- """
1076
- Check for new JSON columns and update the parameters.
1077
- """
1078
- from meerschaum.utils.dataframe import get_json_cols
1079
- json_cols = get_json_cols(df)
1080
- existing_json_cols = [col for col, typ in self.dtypes.items() if typ == 'json']
1081
- new_json_cols = [col for col in json_cols if col not in existing_json_cols]
1082
- if not new_json_cols:
1083
- return True, "Success"
1084
-
1085
- self._attributes_sync_time = None
1086
- dtypes = self.parameters.get('dtypes', {})
1087
- dtypes.update({col: 'json' for col in new_json_cols})
1088
- return self.update_parameters({'dtypes': dtypes}, debug=debug)
1089
-
1090
-
1091
- def _persist_new_bytes_columns(self, df, debug: bool = False) -> SuccessTuple:
1051
+ def _persist_new_special_columns(
1052
+ self,
1053
+ df: 'pd.DataFrame',
1054
+ dtypes: Optional[Dict[str, str]] = None,
1055
+ debug: bool = False,
1056
+ ) -> mrsm.SuccessTuple:
1092
1057
  """
1093
- Check for new `bytes` columns and update the parameters.
1058
+ Check for new special columns and update the parameters accordingly.
1094
1059
  """
1095
- from meerschaum.utils.dataframe import get_bytes_cols
1096
- bytes_cols = get_bytes_cols(df)
1097
- existing_bytes_cols = [col for col, typ in self.dtypes.items() if typ == 'bytes']
1098
- new_bytes_cols = [col for col in bytes_cols if col not in existing_bytes_cols]
1099
- if not new_bytes_cols:
1060
+ from meerschaum.utils.dataframe import get_special_cols
1061
+ from meerschaum.utils.dtypes import dtype_is_special
1062
+ from meerschaum.utils.warnings import dprint
1063
+
1064
+ special_cols = get_special_cols(df)
1065
+ dtypes = dtypes or self.get_dtypes(debug=debug)
1066
+ existing_special_cols = {
1067
+ col: typ
1068
+ for col, typ in dtypes.items()
1069
+ if dtype_is_special(typ)
1070
+ }
1071
+ new_special_cols = {
1072
+ col: typ
1073
+ for col, typ in special_cols.items()
1074
+ if col not in existing_special_cols
1075
+ }
1076
+ if not new_special_cols:
1100
1077
  return True, "Success"
1101
1078
 
1102
- self._attributes_sync_time = None
1103
- dtypes = self.parameters.get('dtypes', {})
1104
- dtypes.update({col: 'bytes' for col in new_bytes_cols})
1105
- return self.update_parameters({'dtypes': dtypes}, debug=debug)
1106
-
1107
-
1108
- def _persist_new_geometry_columns(self, df, debug: bool = False) -> SuccessTuple:
1109
- """
1110
- Check for new `geometry` columns and update the parameters.
1111
- """
1112
- from meerschaum.utils.dataframe import get_geometry_cols
1113
- geometry_cols_types_srids = get_geometry_cols(df, with_types_srids=True)
1114
- existing_geometry_cols = [
1115
- col
1116
- for col, typ in self.dtypes.items()
1117
- if typ.startswith('geometry') or typ.startswith('geography')
1118
- ]
1119
- new_geometry_cols = [
1120
- col
1121
- for col in geometry_cols_types_srids
1122
- if col not in existing_geometry_cols
1123
- ]
1124
- if not new_geometry_cols:
1125
- return True, "Success"
1079
+ if debug:
1080
+ dprint(f"New special columns:\n{new_special_cols}")
1126
1081
 
1127
1082
  self._attributes_sync_time = None
1128
- dtypes = self.parameters.get('dtypes', {})
1129
-
1130
- new_cols_types = {}
1131
- for col, (geometry_type, srid) in geometry_cols_types_srids.items():
1132
- if col not in new_geometry_cols:
1133
- continue
1134
-
1135
- new_dtype = "geometry"
1136
- modifier = ""
1137
- if not srid and geometry_type.lower() == 'geometry':
1138
- new_cols_types[col] = new_dtype
1139
- continue
1140
-
1141
- modifier = "["
1142
- if geometry_type.lower() != 'geometry':
1143
- modifier += f"{geometry_type}"
1144
-
1145
- if srid:
1146
- if modifier != '[':
1147
- modifier += ", "
1148
- modifier += f"{srid}"
1149
- modifier += "]"
1150
- new_cols_types[col] = f"{new_dtype}{modifier}"
1151
-
1152
- dtypes.update(new_cols_types)
1153
- return self.update_parameters({'dtypes': dtypes})
1083
+ return self.update_parameters({'dtypes': new_special_cols}, debug=debug)
@@ -418,7 +418,7 @@ def verify(
418
418
  retry_failed_batch = False
419
419
 
420
420
  batch_msg_to_print = (
421
- f"{make_header('Completed batch ' + batch_counter_str + ':')}\n{batch_msg}"
421
+ f"{make_header('Completed batch ' + batch_counter_str + ':', left_pad=0)}\n{batch_msg}"
422
422
  )
423
423
  mrsm.pprint((batch_success, batch_msg_to_print))
424
424
 
@@ -426,7 +426,7 @@ def verify(
426
426
  info(f"Retrying batch {batch_counter_str}...")
427
427
  retry_batch_success, retry_batch_msg = process_batch(batch)
428
428
  retry_batch_msg_to_print = (
429
- f"Retried {make_header('batch ' + batch_label)}\n{retry_batch_msg}"
429
+ f"Retried {make_header('batch ' + batch_label, left_pad=0)}\n{retry_batch_msg}"
430
430
  )
431
431
  mrsm.pprint((retry_batch_success, retry_batch_msg_to_print))
432
432
 
@@ -587,7 +587,7 @@ def get_bound_interval(self, debug: bool = False) -> Union[timedelta, int, None]
587
587
  if not dt_col:
588
588
  return bound_time_value
589
589
 
590
- dt_typ = self.dtypes.get(dt_col, 'datetime64[ns, UTC]')
590
+ dt_typ = self.dtypes.get(dt_col, 'datetime')
591
591
  if 'int' in dt_typ.lower():
592
592
  return int(bound_time_value)
593
593
 
@@ -14,7 +14,6 @@ from typing import Optional, Union, List, Tuple
14
14
  from datetime import datetime, timedelta, timezone
15
15
 
16
16
  import meerschaum as mrsm
17
- from meerschaum.models import TokenModel
18
17
 
19
18
  _PLACEHOLDER_EXPIRATION = datetime(2000, 1, 1)
20
19
 
@@ -38,9 +37,8 @@ class Token:
38
37
  secret: Optional[str] = None,
39
38
  secret_hash: Optional[str] = None,
40
39
  ):
41
- from meerschaum.utils.dtypes import coerce_timezone
40
+ from meerschaum.utils.dtypes import coerce_timezone, round_time
42
41
  from meerschaum.utils.daemon import get_new_daemon_name
43
- from meerschaum.utils.misc import round_time
44
42
  from meerschaum._internal.static import STATIC_CONFIG
45
43
  now = datetime.now(timezone.utc)
46
44
  default_expiration_days = mrsm.get_config(
@@ -153,10 +151,11 @@ class Token:
153
151
  return False
154
152
  return self.instance_connector.token_exists(self.id, debug=debug)
155
153
 
156
- def to_model(self, refresh: bool = False, debug: bool = False) -> TokenModel:
154
+ def to_model(self, refresh: bool = False, debug: bool = False) -> 'TokenModel':
157
155
  """
158
156
  Export the current state to a `TokenModel`.
159
157
  """
158
+ from meerschaum.models import TokenModel
160
159
  in_memory_doc = {
161
160
  'id': self.id,
162
161
  'label': self.label,