meerschaum 2.9.5__py3-none-any.whl → 3.0.0__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 (200) hide show
  1. meerschaum/__init__.py +5 -2
  2. meerschaum/_internal/__init__.py +1 -0
  3. meerschaum/_internal/arguments/_parse_arguments.py +4 -4
  4. meerschaum/_internal/arguments/_parser.py +33 -4
  5. meerschaum/_internal/cli/__init__.py +6 -0
  6. meerschaum/_internal/cli/daemons.py +103 -0
  7. meerschaum/_internal/cli/entry.py +220 -0
  8. meerschaum/_internal/cli/workers.py +435 -0
  9. meerschaum/_internal/docs/index.py +48 -2
  10. meerschaum/_internal/entry.py +50 -14
  11. meerschaum/_internal/shell/Shell.py +121 -29
  12. meerschaum/_internal/shell/__init__.py +4 -1
  13. meerschaum/_internal/static.py +359 -0
  14. meerschaum/_internal/term/TermPageHandler.py +1 -2
  15. meerschaum/_internal/term/__init__.py +40 -6
  16. meerschaum/_internal/term/tools.py +33 -8
  17. meerschaum/actions/__init__.py +6 -4
  18. meerschaum/actions/api.py +53 -13
  19. meerschaum/actions/attach.py +1 -0
  20. meerschaum/actions/bootstrap.py +8 -8
  21. meerschaum/actions/delete.py +4 -2
  22. meerschaum/actions/edit.py +171 -25
  23. meerschaum/actions/login.py +8 -8
  24. meerschaum/actions/register.py +143 -6
  25. meerschaum/actions/reload.py +22 -5
  26. meerschaum/actions/restart.py +14 -0
  27. meerschaum/actions/show.py +184 -31
  28. meerschaum/actions/start.py +166 -17
  29. meerschaum/actions/stop.py +38 -2
  30. meerschaum/actions/sync.py +7 -2
  31. meerschaum/actions/tag.py +9 -8
  32. meerschaum/actions/verify.py +5 -8
  33. meerschaum/api/__init__.py +45 -15
  34. meerschaum/api/_events.py +46 -4
  35. meerschaum/api/_oauth2.py +162 -9
  36. meerschaum/api/_tokens.py +102 -0
  37. meerschaum/api/dash/__init__.py +0 -3
  38. meerschaum/api/dash/callbacks/__init__.py +1 -0
  39. meerschaum/api/dash/callbacks/custom.py +4 -3
  40. meerschaum/api/dash/callbacks/dashboard.py +198 -118
  41. meerschaum/api/dash/callbacks/jobs.py +14 -7
  42. meerschaum/api/dash/callbacks/login.py +10 -1
  43. meerschaum/api/dash/callbacks/pipes.py +194 -14
  44. meerschaum/api/dash/callbacks/plugins.py +0 -1
  45. meerschaum/api/dash/callbacks/register.py +10 -3
  46. meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
  47. meerschaum/api/dash/callbacks/tokens.py +389 -0
  48. meerschaum/api/dash/components.py +36 -15
  49. meerschaum/api/dash/jobs.py +1 -1
  50. meerschaum/api/dash/keys.py +35 -93
  51. meerschaum/api/dash/pages/__init__.py +2 -1
  52. meerschaum/api/dash/pages/dashboard.py +1 -20
  53. meerschaum/api/dash/pages/{job.py → jobs.py} +10 -7
  54. meerschaum/api/dash/pages/login.py +2 -2
  55. meerschaum/api/dash/pages/pipes.py +16 -5
  56. meerschaum/api/dash/pages/settings/password_reset.py +1 -1
  57. meerschaum/api/dash/pages/tokens.py +53 -0
  58. meerschaum/api/dash/pipes.py +382 -95
  59. meerschaum/api/dash/sessions.py +12 -0
  60. meerschaum/api/dash/tokens.py +603 -0
  61. meerschaum/api/dash/websockets.py +1 -1
  62. meerschaum/api/dash/webterm.py +18 -6
  63. meerschaum/api/models/__init__.py +23 -3
  64. meerschaum/api/models/_actions.py +22 -0
  65. meerschaum/api/models/_pipes.py +91 -7
  66. meerschaum/api/models/_tokens.py +81 -0
  67. meerschaum/api/resources/static/js/terminado.js +3 -0
  68. meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
  69. meerschaum/api/resources/templates/termpage.html +13 -0
  70. meerschaum/api/routes/__init__.py +1 -0
  71. meerschaum/api/routes/_actions.py +3 -4
  72. meerschaum/api/routes/_connectors.py +3 -7
  73. meerschaum/api/routes/_jobs.py +26 -35
  74. meerschaum/api/routes/_login.py +120 -15
  75. meerschaum/api/routes/_misc.py +5 -10
  76. meerschaum/api/routes/_pipes.py +178 -143
  77. meerschaum/api/routes/_plugins.py +38 -28
  78. meerschaum/api/routes/_tokens.py +236 -0
  79. meerschaum/api/routes/_users.py +47 -35
  80. meerschaum/api/routes/_version.py +3 -3
  81. meerschaum/api/routes/_webterm.py +3 -3
  82. meerschaum/config/__init__.py +100 -30
  83. meerschaum/config/_default.py +132 -64
  84. meerschaum/config/_edit.py +38 -32
  85. meerschaum/config/_formatting.py +2 -0
  86. meerschaum/config/_patch.py +10 -8
  87. meerschaum/config/_paths.py +133 -13
  88. meerschaum/config/_read_config.py +87 -36
  89. meerschaum/config/_sync.py +6 -3
  90. meerschaum/config/_version.py +1 -1
  91. meerschaum/config/environment.py +262 -0
  92. meerschaum/config/stack/__init__.py +37 -15
  93. meerschaum/config/static.py +18 -0
  94. meerschaum/connectors/_Connector.py +11 -6
  95. meerschaum/connectors/__init__.py +41 -22
  96. meerschaum/connectors/api/_APIConnector.py +34 -6
  97. meerschaum/connectors/api/_actions.py +2 -2
  98. meerschaum/connectors/api/_jobs.py +12 -1
  99. meerschaum/connectors/api/_login.py +33 -7
  100. meerschaum/connectors/api/_misc.py +2 -2
  101. meerschaum/connectors/api/_pipes.py +23 -32
  102. meerschaum/connectors/api/_plugins.py +2 -2
  103. meerschaum/connectors/api/_request.py +1 -1
  104. meerschaum/connectors/api/_tokens.py +146 -0
  105. meerschaum/connectors/api/_users.py +70 -58
  106. meerschaum/connectors/instance/_InstanceConnector.py +83 -0
  107. meerschaum/connectors/instance/__init__.py +10 -0
  108. meerschaum/connectors/instance/_pipes.py +442 -0
  109. meerschaum/connectors/instance/_plugins.py +159 -0
  110. meerschaum/connectors/instance/_tokens.py +317 -0
  111. meerschaum/connectors/instance/_users.py +188 -0
  112. meerschaum/connectors/parse.py +5 -2
  113. meerschaum/connectors/sql/_SQLConnector.py +22 -5
  114. meerschaum/connectors/sql/_cli.py +12 -11
  115. meerschaum/connectors/sql/_create_engine.py +12 -168
  116. meerschaum/connectors/sql/_fetch.py +2 -18
  117. meerschaum/connectors/sql/_pipes.py +295 -278
  118. meerschaum/connectors/sql/_plugins.py +29 -0
  119. meerschaum/connectors/sql/_sql.py +46 -21
  120. meerschaum/connectors/sql/_users.py +36 -2
  121. meerschaum/connectors/sql/tables/__init__.py +254 -122
  122. meerschaum/connectors/valkey/_ValkeyConnector.py +5 -7
  123. meerschaum/connectors/valkey/_pipes.py +60 -31
  124. meerschaum/connectors/valkey/_plugins.py +2 -26
  125. meerschaum/core/Pipe/__init__.py +115 -85
  126. meerschaum/core/Pipe/_attributes.py +425 -124
  127. meerschaum/core/Pipe/_bootstrap.py +54 -24
  128. meerschaum/core/Pipe/_cache.py +555 -0
  129. meerschaum/core/Pipe/_clear.py +0 -11
  130. meerschaum/core/Pipe/_data.py +96 -68
  131. meerschaum/core/Pipe/_deduplicate.py +0 -13
  132. meerschaum/core/Pipe/_delete.py +12 -21
  133. meerschaum/core/Pipe/_drop.py +11 -23
  134. meerschaum/core/Pipe/_dtypes.py +49 -19
  135. meerschaum/core/Pipe/_edit.py +14 -4
  136. meerschaum/core/Pipe/_fetch.py +1 -1
  137. meerschaum/core/Pipe/_index.py +8 -14
  138. meerschaum/core/Pipe/_show.py +5 -5
  139. meerschaum/core/Pipe/_sync.py +123 -204
  140. meerschaum/core/Pipe/_verify.py +4 -4
  141. meerschaum/{plugins → core/Plugin}/_Plugin.py +16 -12
  142. meerschaum/core/Plugin/__init__.py +1 -1
  143. meerschaum/core/Token/_Token.py +220 -0
  144. meerschaum/core/Token/__init__.py +12 -0
  145. meerschaum/core/User/_User.py +35 -10
  146. meerschaum/core/User/__init__.py +9 -1
  147. meerschaum/core/__init__.py +1 -0
  148. meerschaum/jobs/_Executor.py +88 -4
  149. meerschaum/jobs/_Job.py +149 -38
  150. meerschaum/jobs/__init__.py +3 -2
  151. meerschaum/jobs/systemd.py +8 -3
  152. meerschaum/models/__init__.py +35 -0
  153. meerschaum/models/pipes.py +247 -0
  154. meerschaum/models/tokens.py +38 -0
  155. meerschaum/models/users.py +26 -0
  156. meerschaum/plugins/__init__.py +301 -88
  157. meerschaum/plugins/bootstrap.py +510 -4
  158. meerschaum/utils/_get_pipes.py +97 -30
  159. meerschaum/utils/daemon/Daemon.py +199 -43
  160. meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
  161. meerschaum/utils/daemon/RotatingFile.py +63 -36
  162. meerschaum/utils/daemon/StdinFile.py +53 -13
  163. meerschaum/utils/daemon/__init__.py +47 -6
  164. meerschaum/utils/daemon/_names.py +6 -3
  165. meerschaum/utils/dataframe.py +479 -81
  166. meerschaum/utils/debug.py +49 -19
  167. meerschaum/utils/dtypes/__init__.py +476 -34
  168. meerschaum/utils/dtypes/sql.py +369 -29
  169. meerschaum/utils/formatting/__init__.py +5 -2
  170. meerschaum/utils/formatting/_jobs.py +1 -1
  171. meerschaum/utils/formatting/_pipes.py +52 -50
  172. meerschaum/utils/formatting/_pprint.py +1 -0
  173. meerschaum/utils/formatting/_shell.py +44 -18
  174. meerschaum/utils/misc.py +268 -186
  175. meerschaum/utils/packages/__init__.py +25 -40
  176. meerschaum/utils/packages/_packages.py +42 -34
  177. meerschaum/utils/pipes.py +213 -0
  178. meerschaum/utils/process.py +2 -2
  179. meerschaum/utils/prompt.py +175 -144
  180. meerschaum/utils/schedule.py +2 -1
  181. meerschaum/utils/sql.py +134 -47
  182. meerschaum/utils/threading.py +42 -0
  183. meerschaum/utils/typing.py +1 -4
  184. meerschaum/utils/venv/_Venv.py +2 -2
  185. meerschaum/utils/venv/__init__.py +7 -7
  186. meerschaum/utils/warnings.py +19 -13
  187. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/METADATA +94 -96
  188. meerschaum-3.0.0.dist-info/RECORD +289 -0
  189. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/WHEEL +1 -1
  190. meerschaum-3.0.0.dist-info/licenses/NOTICE +2 -0
  191. meerschaum/api/models/_interfaces.py +0 -15
  192. meerschaum/api/models/_locations.py +0 -15
  193. meerschaum/api/models/_metrics.py +0 -15
  194. meerschaum/config/_environment.py +0 -145
  195. meerschaum/config/static/__init__.py +0 -186
  196. meerschaum-2.9.5.dist-info/RECORD +0 -263
  197. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/entry_points.txt +0 -0
  198. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/licenses/LICENSE +0 -0
  199. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/top_level.txt +0 -0
  200. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/zip-safe +0 -0
@@ -29,6 +29,7 @@ def get_data(
29
29
  as_iterator: bool = False,
30
30
  as_chunks: bool = False,
31
31
  as_dask: bool = False,
32
+ add_missing_columns: bool = False,
32
33
  chunk_interval: Union[timedelta, int, None] = None,
33
34
  order: Optional[str] = 'asc',
34
35
  limit: Optional[int] = None,
@@ -72,6 +73,9 @@ def get_data(
72
73
  If `True`, return a `dask.DataFrame`
73
74
  (which may be loaded into a Pandas DataFrame with `df.compute()`).
74
75
 
76
+ add_missing_columns: bool, default False
77
+ If `True`, add any missing columns from `Pipe.dtypes` to the dataframe.
78
+
75
79
  chunk_interval: Union[timedelta, int, None], default None
76
80
  If `as_iterator`, then return chunks with `begin` and `end` separated by this interval.
77
81
  This may be set under `pipe.parameters['chunk_minutes']`.
@@ -103,13 +107,13 @@ def get_data(
103
107
  from meerschaum.utils.warnings import warn
104
108
  from meerschaum.utils.venv import Venv
105
109
  from meerschaum.connectors import get_connector_plugin
106
- from meerschaum.utils.misc import iterate_chunks, items_str
107
- from meerschaum.utils.dtypes import to_pandas_dtype, coerce_timezone
110
+ from meerschaum.utils.dtypes import to_pandas_dtype
108
111
  from meerschaum.utils.dataframe import add_missing_cols_to_df, df_is_chunk_generator
109
112
  from meerschaum.utils.packages import attempt_import
113
+ from meerschaum.utils.warnings import dprint
110
114
  dd = attempt_import('dask.dataframe') if as_dask else None
111
115
  dask = attempt_import('dask') if as_dask else None
112
- dateutil_parser = attempt_import('dateutil.parser')
116
+ _ = attempt_import('partd', lazy=False) if as_dask else None
113
117
 
114
118
  if select_columns == '*':
115
119
  select_columns = None
@@ -188,48 +192,22 @@ def get_data(
188
192
  order=order,
189
193
  limit=limit,
190
194
  fresh=fresh,
195
+ add_missing_columns=True,
191
196
  debug=debug,
192
197
  )
193
198
  for (chunk_begin, chunk_end) in bounds
194
199
  ]
195
200
  dask_meta = {
196
201
  col: to_pandas_dtype(typ)
197
- for col, typ in self.dtypes.items()
202
+ for col, typ in self.get_dtypes(refresh=True, infer=True, debug=debug).items()
198
203
  }
204
+ if debug:
205
+ dprint(f"Dask meta:\n{dask_meta}")
199
206
  return _sort_df(dd.from_delayed(dask_chunks, meta=dask_meta))
200
207
 
201
208
  if not self.exists(debug=debug):
202
209
  return None
203
210
 
204
- if self.cache_pipe is not None:
205
- if not fresh:
206
- _sync_cache_tuple = self.cache_pipe.sync(
207
- begin=begin,
208
- end=end,
209
- params=params,
210
- debug=debug,
211
- **kw
212
- )
213
- if not _sync_cache_tuple[0]:
214
- warn(f"Failed to sync cache for {self}:\n" + _sync_cache_tuple[1])
215
- fresh = True
216
- else: ### Successfully synced cache.
217
- return self.enforce_dtypes(
218
- self.cache_pipe.get_data(
219
- select_columns=select_columns,
220
- omit_columns=omit_columns,
221
- begin=begin,
222
- end=end,
223
- params=params,
224
- order=order,
225
- limit=limit,
226
- debug=debug,
227
- fresh=True,
228
- **kw
229
- ),
230
- debug=debug,
231
- )
232
-
233
211
  with Venv(get_connector_plugin(self.instance_connector)):
234
212
  df = self.instance_connector.get_pipe_data(
235
213
  pipe=self,
@@ -249,6 +227,7 @@ def get_data(
249
227
  if not select_columns:
250
228
  select_columns = [col for col in df.columns]
251
229
 
230
+ pipe_dtypes = self.get_dtypes(refresh=False, debug=debug)
252
231
  cols_to_omit = [
253
232
  col
254
233
  for col in df.columns
@@ -262,7 +241,11 @@ def get_data(
262
241
  col
263
242
  for col in select_columns
264
243
  if col not in df.columns
265
- ]
244
+ ] + ([
245
+ col
246
+ for col in pipe_dtypes
247
+ if col not in df.columns
248
+ ] if add_missing_columns else [])
266
249
  if cols_to_omit:
267
250
  warn(
268
251
  (
@@ -278,16 +261,26 @@ def get_data(
278
261
  df = df[_cols_to_select]
279
262
 
280
263
  if cols_to_add:
281
- warn(
282
- (
283
- f"Specified columns {items_str(cols_to_add)} were not found on {self}. "
284
- + "Adding these to the DataFrame as null columns."
285
- ),
286
- stack=False,
264
+ if not add_missing_columns:
265
+ from meerschaum.utils.misc import items_str
266
+ warn(
267
+ f"Will add columns {items_str(cols_to_add)} as nulls to dataframe.",
268
+ stack=False,
269
+ )
270
+
271
+ df = add_missing_cols_to_df(
272
+ df,
273
+ {
274
+ col: pipe_dtypes.get(col, 'string')
275
+ for col in cols_to_add
276
+ },
287
277
  )
288
- df = add_missing_cols_to_df(df, {col: 'string' for col in cols_to_add})
289
278
 
290
- enforced_df = self.enforce_dtypes(df, debug=debug)
279
+ enforced_df = self.enforce_dtypes(
280
+ df,
281
+ dtypes=pipe_dtypes,
282
+ debug=debug,
283
+ )
291
284
 
292
285
  if order:
293
286
  return _sort_df(enforced_df)
@@ -311,7 +304,7 @@ def _get_data_as_iterator(
311
304
  """
312
305
  Return a pipe's data as a generator.
313
306
  """
314
- from meerschaum.utils.misc import round_time
307
+ from meerschaum.utils.dtypes import round_time
315
308
  begin, end = self.parse_date_bounds(begin, end)
316
309
  if not self.exists(debug=debug):
317
310
  return
@@ -452,27 +445,6 @@ def get_backtrack_data(
452
445
  else backtrack_interval
453
446
  )
454
447
 
455
- if self.cache_pipe is not None:
456
- if not fresh:
457
- _sync_cache_tuple = self.cache_pipe.sync(begin=begin, params=params, debug=debug, **kw)
458
- if not _sync_cache_tuple[0]:
459
- warn(f"Failed to sync cache for {self}:\n" + _sync_cache_tuple[1])
460
- fresh = True
461
- else: ### Successfully synced cache.
462
- return self.enforce_dtypes(
463
- self.cache_pipe.get_backtrack_data(
464
- fresh=True,
465
- begin=begin,
466
- backtrack_minutes=backtrack_minutes,
467
- params=params,
468
- limit=limit,
469
- order=kw.get('order', 'desc'),
470
- debug=debug,
471
- **kw
472
- ),
473
- debug=debug,
474
- )
475
-
476
448
  if hasattr(self.instance_connector, 'get_backtrack_data'):
477
449
  with Venv(get_connector_plugin(self.instance_connector)):
478
450
  return self.enforce_dtypes(
@@ -624,7 +596,7 @@ def get_chunk_interval(
624
596
  if dt_col is None:
625
597
  return timedelta(minutes=chunk_minutes)
626
598
 
627
- dt_dtype = self.dtypes.get(dt_col, 'datetime64[ns, UTC]')
599
+ dt_dtype = self.dtypes.get(dt_col, 'datetime')
628
600
  if 'int' in dt_dtype.lower():
629
601
  return chunk_minutes
630
602
  return timedelta(minutes=chunk_minutes)
@@ -688,11 +660,26 @@ def get_chunk_bounds(
688
660
  elif are_dtypes_equal(str(type(end)), 'int'):
689
661
  end += 1
690
662
  consolidate_end_chunk = True
663
+
691
664
  if begin is None and end is None:
692
665
  return [(None, None)]
693
666
 
694
667
  begin, end = self.parse_date_bounds(begin, end)
695
668
 
669
+ if begin and end:
670
+ if begin >= end:
671
+ return (
672
+ [(begin, begin)]
673
+ if bounded
674
+ else [(begin, None)]
675
+ )
676
+ if end <= begin:
677
+ return (
678
+ [(end, end)]
679
+ if bounded
680
+ else [(None, begin)]
681
+ )
682
+
696
683
  ### Set the chunk interval under `pipe.parameters['verify']['chunk_minutes']`.
697
684
  chunk_interval = self.get_chunk_interval(chunk_interval, debug=debug)
698
685
 
@@ -799,7 +786,7 @@ def parse_date_bounds(self, *dt_vals: Union[datetime, int, None]) -> Union[
799
786
  Given a date bound (begin, end), coerce a timezone if necessary.
800
787
  """
801
788
  from meerschaum.utils.misc import is_int
802
- from meerschaum.utils.dtypes import coerce_timezone
789
+ from meerschaum.utils.dtypes import coerce_timezone, MRSM_PD_DTYPES
803
790
  from meerschaum.utils.warnings import warn
804
791
  dateutil_parser = mrsm.attempt_import('dateutil.parser')
805
792
 
@@ -824,12 +811,53 @@ def parse_date_bounds(self, *dt_vals: Union[datetime, int, None]) -> Union[
824
811
  return None
825
812
 
826
813
  dt_col = self.columns.get('datetime', None)
827
- dt_typ = str(self.dtypes.get(dt_col, 'datetime64[ns, UTC]'))
814
+ dt_typ = str(self.dtypes.get(dt_col, 'datetime'))
828
815
  if dt_typ == 'datetime':
829
- dt_typ = 'datetime64[ns, UTC]'
816
+ dt_typ = MRSM_PD_DTYPES['datetime']
830
817
  return coerce_timezone(dt_val, strip_utc=('utc' not in dt_typ.lower()))
831
818
 
832
819
  bounds = tuple(_parse_date_bound(dt_val) for dt_val in dt_vals)
833
820
  if len(bounds) == 1:
834
821
  return bounds[0]
835
822
  return bounds
823
+
824
+
825
+ def get_doc(self, **kwargs) -> Union[Dict[str, Any], None]:
826
+ """
827
+ Convenience function to return a single row as a dictionary (or `None`) from `Pipe.get_data().
828
+ Keywords arguments are passed to `Pipe.get_data()`.
829
+ """
830
+ from meerschaum.utils.warnings import warn
831
+ kwargs['limit'] = 1
832
+ try:
833
+ result_df = self.get_data(**kwargs)
834
+ if result_df is None or len(result_df) == 0:
835
+ return None
836
+ return result_df.reset_index(drop=True).iloc[0].to_dict()
837
+ except Exception as e:
838
+ warn(f"Failed to read value from {self}:\n{e}", stack=False)
839
+ return None
840
+
841
+ def get_value(
842
+ self,
843
+ column: str,
844
+ params: Optional[Dict[str, Any]] = None,
845
+ **kwargs: Any
846
+ ) -> Any:
847
+ """
848
+ Convenience function to return a single value (or `None`) from `Pipe.get_data()`.
849
+ Keywords arguments are passed to `Pipe.get_data()`.
850
+ """
851
+ from meerschaum.utils.warnings import warn
852
+ kwargs['select_columns'] = [column]
853
+ kwargs['limit'] = 1
854
+ try:
855
+ result_df = self.get_data(params=params, **kwargs)
856
+ if result_df is None or len(result_df) == 0:
857
+ return None
858
+ if column not in result_df.columns:
859
+ raise ValueError(f"Column '{column}' was not included in the result set.")
860
+ return result_df[column][0]
861
+ except Exception as e:
862
+ warn(f"Failed to read value from {self}:\n{e}", stack=False)
863
+ return None
@@ -67,19 +67,6 @@ def deduplicate(
67
67
 
68
68
  begin, end = self.parse_date_bounds(begin, end)
69
69
 
70
- if self.cache_pipe is not None:
71
- success, msg = self.cache_pipe.deduplicate(
72
- begin=begin,
73
- end=end,
74
- params=params,
75
- bounded=bounded,
76
- debug=debug,
77
- _use_instance_method=_use_instance_method,
78
- **kwargs
79
- )
80
- if not success:
81
- warn(msg)
82
-
83
70
  workers = self.get_num_workers(workers=workers)
84
71
  pool = get_pool(workers=workers)
85
72
 
@@ -9,11 +9,11 @@ Delete a Pipe's contents and registration
9
9
  from meerschaum.utils.typing import SuccessTuple
10
10
 
11
11
  def delete(
12
- self,
13
- drop: bool = True,
14
- debug: bool = False,
15
- **kw
16
- ) -> SuccessTuple:
12
+ self,
13
+ drop: bool = True,
14
+ debug: bool = False,
15
+ **kw
16
+ ) -> SuccessTuple:
17
17
  """
18
18
  Call the Pipe's instance connector's `delete_pipe()` method.
19
19
 
@@ -30,29 +30,22 @@ def delete(
30
30
  A `SuccessTuple` of success (`bool`), message (`str`).
31
31
 
32
32
  """
33
- import os, pathlib
34
33
  from meerschaum.utils.warnings import warn
35
34
  from meerschaum.utils.venv import Venv
36
35
  from meerschaum.connectors import get_connector_plugin
37
36
 
38
37
  if self.temporary:
38
+ if self.cache:
39
+ invalidate_success, invalidate_msg = self._invalidate_cache(hard=True, debug=debug)
40
+ if not invalidate_success:
41
+ return invalidate_success, invalidate_msg
42
+
39
43
  return (
40
44
  False,
41
45
  "Cannot delete pipes created with `temporary=True` (read-only). "
42
46
  + "You may want to call `pipe.drop()` instead."
43
47
  )
44
48
 
45
- if self.cache_pipe is not None:
46
- _drop_cache_tuple = self.cache_pipe.drop(debug=debug, **kw)
47
- if not _drop_cache_tuple[0]:
48
- warn(_drop_cache_tuple[1])
49
- if getattr(self.cache_connector, 'flavor', None) == 'sqlite':
50
- _cache_db_path = pathlib.Path(self.cache_connector.database)
51
- try:
52
- os.remove(_cache_db_path)
53
- except Exception as e:
54
- warn(f"Could not delete cache file '{_cache_db_path}' for {self}:\n{e}")
55
-
56
49
  if drop:
57
50
  drop_success, drop_msg = self.drop(debug=debug)
58
51
  if not drop_success:
@@ -65,8 +58,6 @@ def delete(
65
58
  return False, f"Received an unexpected result from '{self.instance_connector}': {result}"
66
59
 
67
60
  if result[0]:
68
- to_delete = ['_id']
69
- for member in to_delete:
70
- if member in self.__dict__:
71
- del self.__dict__[member]
61
+ self._invalidate_cache(hard=True, debug=debug)
62
+
72
63
  return result
@@ -28,15 +28,10 @@ def drop(
28
28
  A `SuccessTuple` of success, message.
29
29
 
30
30
  """
31
- self._exists = False
32
- from meerschaum.utils.warnings import warn
33
31
  from meerschaum.utils.venv import Venv
34
32
  from meerschaum.connectors import get_connector_plugin
35
33
 
36
- if self.cache_pipe is not None:
37
- _drop_cache_tuple = self.cache_pipe.drop(debug=debug, **kw)
38
- if not _drop_cache_tuple[0]:
39
- warn(_drop_cache_tuple[1])
34
+ self._clear_cache_key('_exists', debug=debug)
40
35
 
41
36
  with Venv(get_connector_plugin(self.instance_connector)):
42
37
  if hasattr(self.instance_connector, 'drop_pipe'):
@@ -50,9 +45,8 @@ def drop(
50
45
  )
51
46
  )
52
47
 
53
-
54
- _ = self.__dict__.pop('_exists', None)
55
- _ = self.__dict__.pop('_exists_timestamp', None)
48
+ self._clear_cache_key('_exists', debug=debug)
49
+ self._clear_cache_key('_exists_timestamp', debug=debug)
56
50
 
57
51
  return result
58
52
 
@@ -79,19 +73,13 @@ def drop_indices(
79
73
  A `SuccessTuple` of success, message.
80
74
 
81
75
  """
82
- from meerschaum.utils.warnings import warn
83
76
  from meerschaum.utils.venv import Venv
84
77
  from meerschaum.connectors import get_connector_plugin
85
78
 
86
- _ = self.__dict__.pop('_columns_indices', None)
87
- _ = self.__dict__.pop('_columns_indices_timestamp', None)
88
- _ = self.__dict__.pop('_columns_types_timestamp', None)
89
- _ = self.__dict__.pop('_columns_types', None)
90
-
91
- if self.cache_pipe is not None:
92
- _drop_cache_tuple = self.cache_pipe.drop_indices(columns=columns, debug=debug, **kw)
93
- if not _drop_cache_tuple[0]:
94
- warn(_drop_cache_tuple[1])
79
+ self._clear_cache_key('_columns_indices', debug=debug)
80
+ self._clear_cache_key('_columns_indices_timestamp', debug=debug)
81
+ self._clear_cache_key('_columns_types', debug=debug)
82
+ self._clear_cache_key('_columns_types_timestamp', debug=debug)
95
83
 
96
84
  with Venv(get_connector_plugin(self.instance_connector)):
97
85
  if hasattr(self.instance_connector, 'drop_pipe_indices'):
@@ -110,9 +98,9 @@ def drop_indices(
110
98
  )
111
99
  )
112
100
 
113
- _ = self.__dict__.pop('_columns_indices', None)
114
- _ = self.__dict__.pop('_columns_indices_timestamp', None)
115
- _ = self.__dict__.pop('_columns_types_timestamp', None)
116
- _ = self.__dict__.pop('_columns_types', None)
101
+ self._clear_cache_key('_columns_indices', debug=debug)
102
+ self._clear_cache_key('_columns_indices_timestamp', debug=debug)
103
+ self._clear_cache_key('_columns_types', debug=debug)
104
+ self._clear_cache_key('_columns_types_timestamp', debug=debug)
117
105
 
118
106
  return result
@@ -22,6 +22,7 @@ def enforce_dtypes(
22
22
  chunksize: Optional[int] = -1,
23
23
  enforce: bool = True,
24
24
  safe_copy: bool = True,
25
+ dtypes: Optional[Dict[str, str]] = None,
25
26
  debug: bool = False,
26
27
  ) -> 'pd.DataFrame':
27
28
  """
@@ -31,7 +32,11 @@ def enforce_dtypes(
31
32
  import traceback
32
33
  from meerschaum.utils.warnings import warn
33
34
  from meerschaum.utils.debug import dprint
34
- from meerschaum.utils.dataframe import parse_df_datetimes, enforce_dtypes as _enforce_dtypes
35
+ from meerschaum.utils.dataframe import (
36
+ parse_df_datetimes,
37
+ enforce_dtypes as _enforce_dtypes,
38
+ parse_simple_lines,
39
+ )
35
40
  from meerschaum.utils.dtypes import are_dtypes_equal
36
41
  from meerschaum.utils.packages import import_pandas
37
42
  pd = import_pandas(debug=debug)
@@ -45,23 +50,35 @@ def enforce_dtypes(
45
50
 
46
51
  if not self.enforce:
47
52
  enforce = False
48
- pipe_dtypes = self.dtypes if enforce else {}
53
+
54
+ explicit_dtypes = self.get_dtypes(infer=False, debug=debug) if enforce else {}
55
+ pipe_dtypes = self.get_dtypes(infer=True, debug=debug) if not dtypes else dtypes
49
56
 
50
57
  try:
51
58
  if isinstance(df, str):
52
- df = parse_df_datetimes(
53
- pd.read_json(StringIO(df)),
54
- ignore_cols=[
55
- col
56
- for col, dtype in pipe_dtypes.items()
57
- if (not enforce or not are_dtypes_equal(dtype, 'datetime'))
58
- ],
59
- ignore_all=(not enforce),
60
- strip_timezone=(self.tzinfo is None),
61
- chunksize=chunksize,
62
- debug=debug,
63
- )
64
- elif isinstance(df, (dict, list)):
59
+ if df.strip() and df.strip()[0] not in ('{', '['):
60
+ df = parse_df_datetimes(
61
+ parse_simple_lines(df),
62
+ ignore_cols=[
63
+ col
64
+ for col, dtype in pipe_dtypes.items()
65
+ if (not enforce or not are_dtypes_equal(dtype, 'datetime'))
66
+ ],
67
+ )
68
+ else:
69
+ df = parse_df_datetimes(
70
+ pd.read_json(StringIO(df)),
71
+ ignore_cols=[
72
+ col
73
+ for col, dtype in pipe_dtypes.items()
74
+ if (not enforce or not are_dtypes_equal(dtype, 'datetime'))
75
+ ],
76
+ ignore_all=(not enforce),
77
+ strip_timezone=(self.tzinfo is None),
78
+ chunksize=chunksize,
79
+ debug=debug,
80
+ )
81
+ elif isinstance(df, (dict, list, tuple)):
65
82
  df = parse_df_datetimes(
66
83
  df,
67
84
  ignore_cols=[
@@ -81,21 +98,28 @@ def enforce_dtypes(
81
98
  if debug:
82
99
  dprint(
83
100
  f"Could not find dtypes for {self}.\n"
84
- + " Skipping dtype enforcement..."
101
+ + "Skipping dtype enforcement..."
85
102
  )
86
103
  return df
87
104
 
88
105
  return _enforce_dtypes(
89
106
  df,
90
107
  pipe_dtypes,
108
+ explicit_dtypes=explicit_dtypes,
91
109
  safe_copy=safe_copy,
92
110
  strip_timezone=(self.tzinfo is None),
111
+ coerce_numeric=self.mixed_numerics,
93
112
  coerce_timezone=enforce,
94
113
  debug=debug,
95
114
  )
96
115
 
97
116
 
98
- def infer_dtypes(self, persist: bool = False, debug: bool = False) -> Dict[str, Any]:
117
+ def infer_dtypes(
118
+ self,
119
+ persist: bool = False,
120
+ refresh: bool = False,
121
+ debug: bool = False,
122
+ ) -> Dict[str, Any]:
99
123
  """
100
124
  If `dtypes` is not set in `meerschaum.Pipe.parameters`,
101
125
  infer the data types from the underlying table if it exists.
@@ -104,6 +128,11 @@ def infer_dtypes(self, persist: bool = False, debug: bool = False) -> Dict[str,
104
128
  ----------
105
129
  persist: bool, default False
106
130
  If `True`, persist the inferred data types to `meerschaum.Pipe.parameters`.
131
+ NOTE: Use with caution! Generally `dtypes` is meant to be user-configurable only.
132
+
133
+ refresh: bool, default False
134
+ If `True`, retrieve the latest columns-types for the pipe.
135
+ See `Pipe.get_columns.types()`.
107
136
 
108
137
  Returns
109
138
  -------
@@ -117,7 +146,7 @@ def infer_dtypes(self, persist: bool = False, debug: bool = False) -> Dict[str,
117
146
 
118
147
  ### NOTE: get_columns_types() may return either the types as
119
148
  ### PostgreSQL- or Pandas-style.
120
- columns_types = self.get_columns_types(debug=debug)
149
+ columns_types = self.get_columns_types(refresh=refresh, debug=debug)
121
150
 
122
151
  remote_pd_dtypes = {
123
152
  c: (
@@ -130,7 +159,8 @@ def infer_dtypes(self, persist: bool = False, debug: bool = False) -> Dict[str,
130
159
  if not persist:
131
160
  return remote_pd_dtypes
132
161
 
133
- dtypes = self.parameters.get('dtypes', {})
162
+ parameters = self.get_parameters(refresh=refresh, debug=debug)
163
+ dtypes = parameters.get('dtypes', {})
134
164
  dtypes.update({
135
165
  col: typ
136
166
  for col, typ in remote_pd_dtypes.items()
@@ -3,7 +3,7 @@
3
3
  # vim:fenc=utf-8
4
4
 
5
5
  """
6
- Edit a Pipe's parameters here.
6
+ Edit a Pipe's parameters.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
@@ -47,6 +47,15 @@ def edit(
47
47
  if self.temporary:
48
48
  return False, "Cannot edit pipes created with `temporary=True` (read-only)."
49
49
 
50
+ self._invalidate_cache(hard=True, debug=debug)
51
+
52
+ if hasattr(self, '_symlinks'):
53
+ from meerschaum.utils.misc import get_val_from_dict_path, set_val_in_dict_path
54
+ for path, vals in self._symlinks.items():
55
+ current_val = get_val_from_dict_path(self.parameters, path)
56
+ if current_val == vals['substituted']:
57
+ set_val_in_dict_path(self.parameters, path, vals['original'])
58
+
50
59
  if not interactive:
51
60
  with Venv(get_connector_plugin(self.instance_connector)):
52
61
  return self.instance_connector.edit_pipe(self, patch=patch, debug=debug, **kw)
@@ -65,7 +74,8 @@ def edit(
65
74
  from meerschaum.config import get_config
66
75
  parameters = dict(get_config('pipes', 'parameters', patch=True))
67
76
  from meerschaum.config._patch import apply_patch_to_config
68
- parameters = apply_patch_to_config(parameters, self.parameters)
77
+ raw_parameters = self.attributes.get('parameters', {})
78
+ parameters = apply_patch_to_config(parameters, raw_parameters)
69
79
 
70
80
  ### write parameters to yaml file
71
81
  with open(parameters_path, 'w+') as f:
@@ -194,7 +204,7 @@ def edit_definition(
194
204
  return True, "Success"
195
205
 
196
206
  def _edit_sql():
197
- import pathlib, os, textwrap
207
+ import textwrap
198
208
  from meerschaum.config._paths import PIPES_CACHE_RESOURCES_PATH
199
209
  from meerschaum.utils.misc import edit_file
200
210
  definition_filename = str(self) + '.sql'
@@ -214,7 +224,7 @@ def edit_definition(
214
224
 
215
225
  edit_file(definition_path)
216
226
  try:
217
- with open(definition_path, 'r') as f:
227
+ with open(definition_path, 'r', encoding='utf-8') as f:
218
228
  file_definition = f.read()
219
229
  except Exception as e:
220
230
  return False, f"Failed reading file '{definition_path}':\n" + str(e)
@@ -127,7 +127,7 @@ def get_backtrack_interval(
127
127
  if dt_col is None:
128
128
  return backtrack_interval
129
129
 
130
- dt_dtype = self.dtypes.get(dt_col, 'datetime64[ns, UTC]')
130
+ dt_dtype = self.dtypes.get(dt_col, 'datetime')
131
131
  if 'int' in dt_dtype.lower():
132
132
  return backtrack_minutes
133
133
 
@@ -29,19 +29,13 @@ def create_indices(
29
29
  A `SuccessTuple` of success, message.
30
30
 
31
31
  """
32
- from meerschaum.utils.warnings import warn
33
32
  from meerschaum.utils.venv import Venv
34
33
  from meerschaum.connectors import get_connector_plugin
35
34
 
36
- _ = self.__dict__.pop('_columns_indices', None)
37
- _ = self.__dict__.pop('_columns_indices_timestamp', None)
38
- _ = self.__dict__.pop('_columns_types_timestamp', None)
39
- _ = self.__dict__.pop('_columns_types', None)
40
-
41
- if self.cache_pipe is not None:
42
- cache_success, cache_msg = self.cache_pipe.index(columns=columns, debug=debug, **kw)
43
- if not cache_success:
44
- warn(cache_msg)
35
+ self._clear_cache_key('_columns_indices', debug=debug)
36
+ self._clear_cache_key('_columns_indices_timestamp', debug=debug)
37
+ self._clear_cache_key('_columns_types', debug=debug)
38
+ self._clear_cache_key('_columns_types_timestamp', debug=debug)
45
39
 
46
40
  with Venv(get_connector_plugin(self.instance_connector)):
47
41
  if hasattr(self.instance_connector, 'create_pipe_indices'):
@@ -60,9 +54,9 @@ def create_indices(
60
54
  )
61
55
  )
62
56
 
63
- _ = self.__dict__.pop('_columns_indices', None)
64
- _ = self.__dict__.pop('_columns_indices_timestamp', None)
65
- _ = self.__dict__.pop('_columns_types_timestamp', None)
66
- _ = self.__dict__.pop('_columns_types', None)
57
+ self._clear_cache_key('_columns_indices', debug=debug)
58
+ self._clear_cache_key('_columns_indices_timestamp', debug=debug)
59
+ self._clear_cache_key('_columns_types', debug=debug)
60
+ self._clear_cache_key('_columns_types_timestamp', debug=debug)
67
61
 
68
62
  return result
@@ -9,11 +9,11 @@ Show information about a Pipe
9
9
  from meerschaum.utils.typing import SuccessTuple
10
10
 
11
11
  def show(
12
- self,
13
- nopretty: bool = False,
14
- debug: bool = False,
15
- **kw
16
- ) -> SuccessTuple:
12
+ self,
13
+ nopretty: bool = False,
14
+ debug: bool = False,
15
+ **kw
16
+ ) -> SuccessTuple:
17
17
  """
18
18
  Show attributes of a Pipe.
19
19