meerschaum 2.9.4__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 (201) 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 +228 -117
  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 +438 -88
  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/css/dash.css +16 -0
  68. meerschaum/api/resources/static/js/terminado.js +3 -0
  69. meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
  70. meerschaum/api/resources/templates/termpage.html +13 -0
  71. meerschaum/api/routes/__init__.py +1 -0
  72. meerschaum/api/routes/_actions.py +3 -4
  73. meerschaum/api/routes/_connectors.py +3 -7
  74. meerschaum/api/routes/_jobs.py +26 -35
  75. meerschaum/api/routes/_login.py +120 -15
  76. meerschaum/api/routes/_misc.py +5 -10
  77. meerschaum/api/routes/_pipes.py +178 -143
  78. meerschaum/api/routes/_plugins.py +38 -28
  79. meerschaum/api/routes/_tokens.py +236 -0
  80. meerschaum/api/routes/_users.py +47 -35
  81. meerschaum/api/routes/_version.py +3 -3
  82. meerschaum/api/routes/_webterm.py +3 -3
  83. meerschaum/config/__init__.py +100 -30
  84. meerschaum/config/_default.py +132 -64
  85. meerschaum/config/_edit.py +38 -32
  86. meerschaum/config/_formatting.py +2 -0
  87. meerschaum/config/_patch.py +10 -8
  88. meerschaum/config/_paths.py +133 -13
  89. meerschaum/config/_read_config.py +87 -36
  90. meerschaum/config/_sync.py +6 -3
  91. meerschaum/config/_version.py +1 -1
  92. meerschaum/config/environment.py +262 -0
  93. meerschaum/config/stack/__init__.py +37 -15
  94. meerschaum/config/static.py +18 -0
  95. meerschaum/connectors/_Connector.py +11 -6
  96. meerschaum/connectors/__init__.py +41 -22
  97. meerschaum/connectors/api/_APIConnector.py +34 -6
  98. meerschaum/connectors/api/_actions.py +2 -2
  99. meerschaum/connectors/api/_jobs.py +12 -1
  100. meerschaum/connectors/api/_login.py +33 -7
  101. meerschaum/connectors/api/_misc.py +2 -2
  102. meerschaum/connectors/api/_pipes.py +23 -32
  103. meerschaum/connectors/api/_plugins.py +2 -2
  104. meerschaum/connectors/api/_request.py +1 -1
  105. meerschaum/connectors/api/_tokens.py +146 -0
  106. meerschaum/connectors/api/_users.py +70 -58
  107. meerschaum/connectors/instance/_InstanceConnector.py +83 -0
  108. meerschaum/connectors/instance/__init__.py +10 -0
  109. meerschaum/connectors/instance/_pipes.py +442 -0
  110. meerschaum/connectors/instance/_plugins.py +159 -0
  111. meerschaum/connectors/instance/_tokens.py +317 -0
  112. meerschaum/connectors/instance/_users.py +188 -0
  113. meerschaum/connectors/parse.py +5 -2
  114. meerschaum/connectors/sql/_SQLConnector.py +22 -5
  115. meerschaum/connectors/sql/_cli.py +12 -11
  116. meerschaum/connectors/sql/_create_engine.py +12 -168
  117. meerschaum/connectors/sql/_fetch.py +2 -18
  118. meerschaum/connectors/sql/_pipes.py +295 -278
  119. meerschaum/connectors/sql/_plugins.py +29 -0
  120. meerschaum/connectors/sql/_sql.py +47 -22
  121. meerschaum/connectors/sql/_users.py +36 -2
  122. meerschaum/connectors/sql/tables/__init__.py +254 -122
  123. meerschaum/connectors/valkey/_ValkeyConnector.py +5 -7
  124. meerschaum/connectors/valkey/_pipes.py +60 -31
  125. meerschaum/connectors/valkey/_plugins.py +2 -26
  126. meerschaum/core/Pipe/__init__.py +115 -85
  127. meerschaum/core/Pipe/_attributes.py +425 -124
  128. meerschaum/core/Pipe/_bootstrap.py +54 -24
  129. meerschaum/core/Pipe/_cache.py +555 -0
  130. meerschaum/core/Pipe/_clear.py +0 -11
  131. meerschaum/core/Pipe/_data.py +96 -68
  132. meerschaum/core/Pipe/_deduplicate.py +0 -13
  133. meerschaum/core/Pipe/_delete.py +12 -21
  134. meerschaum/core/Pipe/_drop.py +11 -23
  135. meerschaum/core/Pipe/_dtypes.py +49 -19
  136. meerschaum/core/Pipe/_edit.py +14 -4
  137. meerschaum/core/Pipe/_fetch.py +1 -1
  138. meerschaum/core/Pipe/_index.py +8 -14
  139. meerschaum/core/Pipe/_show.py +5 -5
  140. meerschaum/core/Pipe/_sync.py +123 -204
  141. meerschaum/core/Pipe/_verify.py +4 -4
  142. meerschaum/{plugins → core/Plugin}/_Plugin.py +16 -12
  143. meerschaum/core/Plugin/__init__.py +1 -1
  144. meerschaum/core/Token/_Token.py +220 -0
  145. meerschaum/core/Token/__init__.py +12 -0
  146. meerschaum/core/User/_User.py +35 -10
  147. meerschaum/core/User/__init__.py +9 -1
  148. meerschaum/core/__init__.py +1 -0
  149. meerschaum/jobs/_Executor.py +88 -4
  150. meerschaum/jobs/_Job.py +149 -38
  151. meerschaum/jobs/__init__.py +3 -2
  152. meerschaum/jobs/systemd.py +8 -3
  153. meerschaum/models/__init__.py +35 -0
  154. meerschaum/models/pipes.py +247 -0
  155. meerschaum/models/tokens.py +38 -0
  156. meerschaum/models/users.py +26 -0
  157. meerschaum/plugins/__init__.py +301 -88
  158. meerschaum/plugins/bootstrap.py +510 -4
  159. meerschaum/utils/_get_pipes.py +97 -30
  160. meerschaum/utils/daemon/Daemon.py +199 -43
  161. meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
  162. meerschaum/utils/daemon/RotatingFile.py +63 -36
  163. meerschaum/utils/daemon/StdinFile.py +53 -13
  164. meerschaum/utils/daemon/__init__.py +47 -6
  165. meerschaum/utils/daemon/_names.py +6 -3
  166. meerschaum/utils/dataframe.py +480 -82
  167. meerschaum/utils/debug.py +49 -19
  168. meerschaum/utils/dtypes/__init__.py +478 -37
  169. meerschaum/utils/dtypes/sql.py +369 -29
  170. meerschaum/utils/formatting/__init__.py +5 -2
  171. meerschaum/utils/formatting/_jobs.py +1 -1
  172. meerschaum/utils/formatting/_pipes.py +52 -50
  173. meerschaum/utils/formatting/_pprint.py +1 -0
  174. meerschaum/utils/formatting/_shell.py +44 -18
  175. meerschaum/utils/misc.py +268 -186
  176. meerschaum/utils/packages/__init__.py +25 -40
  177. meerschaum/utils/packages/_packages.py +42 -34
  178. meerschaum/utils/pipes.py +213 -0
  179. meerschaum/utils/process.py +2 -2
  180. meerschaum/utils/prompt.py +175 -144
  181. meerschaum/utils/schedule.py +2 -1
  182. meerschaum/utils/sql.py +135 -49
  183. meerschaum/utils/threading.py +42 -0
  184. meerschaum/utils/typing.py +1 -4
  185. meerschaum/utils/venv/_Venv.py +2 -2
  186. meerschaum/utils/venv/__init__.py +7 -7
  187. meerschaum/utils/warnings.py +19 -13
  188. {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/METADATA +94 -96
  189. meerschaum-3.0.0.dist-info/RECORD +289 -0
  190. {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/WHEEL +1 -1
  191. meerschaum-3.0.0.dist-info/licenses/NOTICE +2 -0
  192. meerschaum/api/models/_interfaces.py +0 -15
  193. meerschaum/api/models/_locations.py +0 -15
  194. meerschaum/api/models/_metrics.py +0 -15
  195. meerschaum/config/_environment.py +0 -145
  196. meerschaum/config/static/__init__.py +0 -186
  197. meerschaum-2.9.4.dist-info/RECORD +0 -263
  198. {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/entry_points.txt +0 -0
  199. {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/licenses/LICENSE +0 -0
  200. {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/top_level.txt +0 -0
  201. {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/zip-safe +0 -0
@@ -13,7 +13,7 @@ from meerschaum.utils.typing import SuccessTuple, Any, Union, Optional, Dict, Li
13
13
  from meerschaum.utils.misc import string_to_dict
14
14
  from meerschaum.utils.dtypes import json_serialize_value
15
15
  from meerschaum.utils.warnings import warn, dprint
16
- from meerschaum.config.static import STATIC_CONFIG
16
+ from meerschaum._internal.static import STATIC_CONFIG
17
17
 
18
18
  PIPES_TABLE: str = 'mrsm_pipes'
19
19
  PIPES_COUNTER: str = 'mrsm_pipes:counter'
@@ -276,7 +276,7 @@ def edit_pipe(
276
276
  return False, f"{pipe} is not registered."
277
277
 
278
278
  parameters_key = get_pipe_parameters_key(pipe)
279
- parameters_str = json.dumps(pipe.parameters, separators=(',', ':'))
279
+ parameters_str = json.dumps(pipe.get_parameters(apply_symlinks=False), separators=(',', ':'))
280
280
  self.set(parameters_key, parameters_str)
281
281
  return True, "Success"
282
282
 
@@ -321,14 +321,40 @@ def drop_pipe(
321
321
  -------
322
322
  A `SuccessTuple` indicating success.
323
323
  """
324
- for chunk_begin, chunk_end in pipe.get_chunk_bounds(debug=debug):
325
- clear_chunk_success, clear_chunk_msg = pipe.clear(
326
- begin=chunk_begin,
327
- end=chunk_end,
328
- debug=debug,
324
+ if not pipe.exists(debug=debug):
325
+ return True, f"{pipe} does not exist, so it was not dropped."
326
+
327
+ table_name = self.quote_table(pipe.target)
328
+ dt_col = pipe.columns.get('datetime', None)
329
+
330
+ try:
331
+ members = (
332
+ self.client.zrange(table_name, 0, -1)
333
+ if dt_col
334
+ else self.client.smembers(table_name)
329
335
  )
330
- if not clear_chunk_success:
331
- return clear_chunk_success, clear_chunk_msg
336
+
337
+ keys_to_delete = []
338
+ for member_bytes in members:
339
+ member_str = member_bytes.decode('utf-8')
340
+ member_doc = json.loads(member_str)
341
+ ix_str = member_doc.get('ix')
342
+ if not ix_str:
343
+ continue
344
+
345
+ ix_doc = string_to_dict(ix_str.replace(COLON, ':'))
346
+ doc_key = self.get_document_key(ix_doc, list(ix_doc.keys()), table_name)
347
+ keys_to_delete.append(doc_key)
348
+
349
+ if keys_to_delete:
350
+ batch_size = 1000
351
+ for i in range(0, len(keys_to_delete), batch_size):
352
+ batch = keys_to_delete[i:i+batch_size]
353
+ self.client.delete(*batch)
354
+
355
+ except Exception as e:
356
+ return False, f"Failed to delete documents for {pipe}:\n{e}"
357
+
332
358
  try:
333
359
  self.drop_table(pipe.target, debug=debug)
334
360
  except Exception as e:
@@ -337,7 +363,7 @@ def drop_pipe(
337
363
  if 'valkey' not in pipe.parameters:
338
364
  return True, "Success"
339
365
 
340
- pipe.parameters['valkey']['dtypes'] = {}
366
+ pipe._attributes['parameters']['valkey']['dtypes'] = {}
341
367
  if not pipe.temporary:
342
368
  edit_success, edit_msg = pipe.edit(debug=debug)
343
369
  if not edit_success:
@@ -558,11 +584,7 @@ def sync_pipe(
558
584
 
559
585
  valkey_dtypes = pipe.parameters.get('valkey', {}).get('dtypes', {})
560
586
  new_dtypes = {
561
- str(key): (
562
- str(val)
563
- if not are_dtypes_equal(str(val), 'datetime')
564
- else 'datetime64[ns, UTC]'
565
- )
587
+ str(key): str(val)
566
588
  for key, val in df.dtypes.items()
567
589
  if str(key) not in valkey_dtypes
568
590
  }
@@ -571,19 +593,20 @@ def sync_pipe(
571
593
  try:
572
594
  df[col] = df[col].astype(typ)
573
595
  except Exception:
596
+ import traceback
597
+ traceback.print_exc()
574
598
  valkey_dtypes[col] = 'string'
575
599
  new_dtypes[col] = 'string'
576
600
  df[col] = df[col].astype('string')
577
601
 
578
602
  if new_dtypes and (not static or not valkey_dtypes):
579
603
  valkey_dtypes.update(new_dtypes)
580
- if 'valkey' not in pipe.parameters:
581
- pipe.parameters['valkey'] = {}
582
- pipe.parameters['valkey']['dtypes'] = valkey_dtypes
583
- if not pipe.temporary:
584
- edit_success, edit_msg = pipe.edit(debug=debug)
585
- if not edit_success:
586
- return edit_success, edit_msg
604
+ update_success, update_msg = pipe.update_parameters(
605
+ {'valkey': {'dtypes': valkey_dtypes}},
606
+ debug=debug,
607
+ )
608
+ if not update_success:
609
+ return False, update_msg
587
610
 
588
611
  unseen_df, update_df, delta_df = (
589
612
  pipe.filter_existing(df, include_unchanged_columns=True, debug=debug)
@@ -781,7 +804,7 @@ def get_sync_time(
781
804
  """
782
805
  from meerschaum.utils.dtypes import are_dtypes_equal
783
806
  dt_col = pipe.columns.get('datetime', None)
784
- dt_typ = pipe.dtypes.get(dt_col, 'datetime64[ns, UTC]')
807
+ dt_typ = pipe.dtypes.get(dt_col, 'datetime')
785
808
  if not dt_col:
786
809
  return None
787
810
 
@@ -789,14 +812,18 @@ def get_sync_time(
789
812
  table_name = self.quote_table(pipe.target)
790
813
  try:
791
814
  vals = (
792
- self.client.zrevrange(table_name, 0, 0)
815
+ self.client.zrevrange(table_name, 0, 0, withscores=True)
793
816
  if newest
794
- else self.client.zrange(table_name, 0, 0)
817
+ else self.client.zrange(table_name, 0, 0, withscores=True)
795
818
  )
796
819
  if not vals:
797
820
  return None
798
- val = vals[0]
821
+ val = vals[0][0]
822
+ if isinstance(val, bytes):
823
+ val = val.decode('utf-8')
799
824
  except Exception:
825
+ import traceback
826
+ traceback.print_exc()
800
827
  return None
801
828
 
802
829
  doc = json.loads(val)
@@ -861,7 +888,9 @@ def fetch_pipes_keys(
861
888
  tags: Optional[List[str]] = None,
862
889
  params: Optional[Dict[str, Any]] = None,
863
890
  debug: bool = False
864
- ) -> Optional[List[Tuple[str, str, Optional[str]]]]:
891
+ ) -> List[
892
+ Tuple[str, str, Union[str, None], Dict[str, Any]]
893
+ ]:
865
894
  """
866
895
  Return the keys for the registered pipes.
867
896
  """
@@ -892,6 +921,7 @@ def fetch_pipes_keys(
892
921
  doc['connector_keys'],
893
922
  doc['metric_key'],
894
923
  doc['location_key'],
924
+ doc.get('parameters', {})
895
925
  )
896
926
  for doc in df.to_dict(orient='records')
897
927
  ]
@@ -902,9 +932,8 @@ def fetch_pipes_keys(
902
932
  in_ex_tag_groups = [separate_negation_values(tag_group) for tag_group in tag_groups]
903
933
 
904
934
  filtered_keys = []
905
- for ck, mk, lk in keys:
906
- pipe = mrsm.Pipe(ck, mk, lk, instance=self)
907
- pipe_tags = set(pipe.tags)
935
+ for ck, mk, lk, parameters in keys:
936
+ pipe_tags = set(parameters.get('tags', []))
908
937
 
909
938
  include_pipe = True
910
939
  for in_tags, ex_tags in in_ex_tag_groups:
@@ -916,6 +945,6 @@ def fetch_pipes_keys(
916
945
  continue
917
946
 
918
947
  if include_pipe:
919
- filtered_keys.append((ck, mk, lk))
948
+ filtered_keys.append((ck, mk, lk, parameters))
920
949
 
921
950
  return filtered_keys
@@ -127,7 +127,8 @@ def get_plugin_id(
127
127
  """
128
128
  Return a plugin's ID.
129
129
  """
130
- return plugin.name
130
+ user_id = self.get_plugin_user_id(plugin, debug=debug)
131
+ return plugin.name if user_id is not None else None
131
132
 
132
133
 
133
134
  def get_plugin_version(
@@ -199,31 +200,6 @@ def get_plugin_attributes(
199
200
  return {}
200
201
 
201
202
 
202
- def get_plugins(
203
- self,
204
- user_id: Optional[int] = None,
205
- search_term: Optional[str] = None,
206
- debug: bool = False,
207
- **kw: Any
208
- ) -> List[str]:
209
- """
210
- Return a list of plugin names.
211
- """
212
- plugins_pipe = self.get_plugins_pipe()
213
- params = {}
214
- if user_id:
215
- params['user_id'] = user_id
216
-
217
- df = plugins_pipe.get_data(['plugin_name'], params=params, debug=debug)
218
- docs = df.to_dict(orient='records')
219
-
220
- return [
221
- doc['plugin_name']
222
- for doc in docs
223
- if (plugin_name := doc['plugin_name']).startswith(search_term or '')
224
- ]
225
-
226
-
227
203
  def delete_plugin(
228
204
  self,
229
205
  plugin: 'mrsm.core.Plugin',
@@ -50,12 +50,16 @@ with correct credentials, as well as a network connection and valid permissions.
50
50
  """
51
51
 
52
52
  from __future__ import annotations
53
+
53
54
  import sys
54
55
  import copy
55
- from meerschaum.utils.typing import Optional, Dict, Any, Union, InstanceConnector, List
56
+
57
+ import meerschaum as mrsm
58
+ from meerschaum.utils.typing import Optional, Dict, Any, Union, List, InstanceConnector
56
59
  from meerschaum.utils.formatting._pipes import pipe_repr
57
60
  from meerschaum.config import get_config
58
61
 
62
+
59
63
  class Pipe:
60
64
  """
61
65
  Access Meerschaum pipes via Pipe objects.
@@ -89,6 +93,9 @@ class Pipe:
89
93
  get_data,
90
94
  get_backtrack_data,
91
95
  get_rowcount,
96
+ get_data,
97
+ get_doc,
98
+ get_value,
92
99
  _get_data_as_iterator,
93
100
  get_chunk_interval,
94
101
  get_chunk_bounds,
@@ -104,15 +111,20 @@ class Pipe:
104
111
  indexes,
105
112
  dtypes,
106
113
  autoincrement,
114
+ autotime,
107
115
  upsert,
108
116
  static,
109
117
  tzinfo,
110
118
  enforce,
111
119
  null_indices,
120
+ mixed_numerics,
112
121
  get_columns,
113
122
  get_columns_types,
114
123
  get_columns_indices,
115
124
  get_indices,
125
+ get_parameters,
126
+ get_dtypes,
127
+ update_parameters,
116
128
  tags,
117
129
  get_id,
118
130
  id,
@@ -123,6 +135,30 @@ class Pipe:
123
135
  target,
124
136
  _target_legacy,
125
137
  guess_datetime,
138
+ precision,
139
+ get_precision,
140
+ )
141
+ from ._cache import (
142
+ _get_cache_connector,
143
+ _cache_value,
144
+ _get_cached_value,
145
+ _invalidate_cache,
146
+ _get_cache_dir_path,
147
+ _write_cache_key,
148
+ _write_cache_file,
149
+ _write_cache_conn_key,
150
+ _read_cache_key,
151
+ _read_cache_file,
152
+ _read_cache_conn_key,
153
+ _load_cache_keys,
154
+ _load_cache_files,
155
+ _load_cache_conn_keys,
156
+ _get_cache_keys,
157
+ _get_cache_file_keys,
158
+ _get_cache_conn_keys,
159
+ _clear_cache_key,
160
+ _clear_cache_file,
161
+ _clear_cache_conn_key,
126
162
  )
127
163
  from ._show import show
128
164
  from ._edit import edit, edit_definition, update
@@ -133,11 +169,7 @@ class Pipe:
133
169
  filter_existing,
134
170
  _get_chunk_label,
135
171
  get_num_workers,
136
- _persist_new_json_columns,
137
- _persist_new_numeric_columns,
138
- _persist_new_uuid_columns,
139
- _persist_new_bytes_columns,
140
- _persist_new_geometry_columns,
172
+ _persist_new_special_columns,
141
173
  )
142
174
  from ._verify import (
143
175
  verify,
@@ -165,20 +197,24 @@ class Pipe:
165
197
  target: Optional[str] = None,
166
198
  dtypes: Optional[Dict[str, str]] = None,
167
199
  instance: Optional[Union[str, InstanceConnector]] = None,
168
- temporary: bool = False,
169
200
  upsert: Optional[bool] = None,
170
201
  autoincrement: Optional[bool] = None,
202
+ autotime: Optional[bool] = None,
203
+ precision: Union[str, Dict[str, Union[str, int]], None] = None,
171
204
  static: Optional[bool] = None,
172
205
  enforce: Optional[bool] = None,
173
206
  null_indices: Optional[bool] = None,
207
+ mixed_numerics: Optional[bool] = None,
208
+ temporary: bool = False,
209
+ cache: Optional[bool] = None,
210
+ cache_connector_keys: Optional[str] = None,
174
211
  mrsm_instance: Optional[Union[str, InstanceConnector]] = None,
175
- cache: bool = False,
176
- debug: bool = False,
177
212
  connector_keys: Optional[str] = None,
178
213
  metric_key: Optional[str] = None,
179
214
  location_key: Optional[str] = None,
180
215
  instance_keys: Optional[str] = None,
181
216
  indexes: Union[Dict[str, str], List[str], None] = None,
217
+ debug: bool = False,
182
218
  ):
183
219
  """
184
220
  Parameters
@@ -226,6 +262,16 @@ class Pipe:
226
262
  autoincrement: Optional[bool], default None
227
263
  If `True`, set `autoincrement` in the parameters.
228
264
 
265
+ autotime: Optional[bool], default None
266
+ If `True`, set `autotime` in the parameters.
267
+
268
+ precision: Union[str, Dict[str, Union[str, int]], None], default None
269
+ If provided, set `precision` in the parameters.
270
+ This may be either a string (the precision unit) or a dictionary of in the form
271
+ `{'unit': <unit>, 'interval': <interval>}`.
272
+ Default is determined by the `datetime` column dtype
273
+ (e.g. `datetime64[us]` is `microsecond` precision).
274
+
229
275
  static: Optional[bool], default None
230
276
  If `True`, set `static` in the parameters.
231
277
 
@@ -237,12 +283,21 @@ class Pipe:
237
283
  Set to `False` if there will be no null values in the index columns.
238
284
  Defaults to `True`.
239
285
 
286
+ mixed_numerics: bool, default None
287
+ If `True`, integer columns will be converted to `numeric` when floats are synced.
288
+ Set to `False` to disable this behavior.
289
+ Defaults to `True`.
290
+
240
291
  temporary: bool, default False
241
292
  If `True`, prevent instance tables (pipes, users, plugins) from being created.
242
293
 
243
- cache: bool, default False
244
- If `True`, cache fetched data into a local database file.
245
- Defaults to `False`.
294
+ cache: Optional[bool], default None
295
+ If `True`, cache the pipe's metadata to disk (in addition to in-memory caching).
296
+ If `cache` is not explicitly `True`, it is set to `False` if `temporary` is `True`.
297
+ Defaults to `True` (from `None`).
298
+
299
+ cache_connector_keys: Optional[str], default None
300
+ If provided, use the keys to a Valkey connector (e.g. `valkey:main`).
246
301
  """
247
302
  from meerschaum.utils.warnings import error, warn
248
303
  if (not connector and not connector_keys) or (not metric and not metric_key):
@@ -264,7 +319,7 @@ class Pipe:
264
319
  if location in ('[None]', 'None'):
265
320
  location = None
266
321
 
267
- from meerschaum.config.static import STATIC_CONFIG
322
+ from meerschaum._internal.static import STATIC_CONFIG
268
323
  negation_prefix = STATIC_CONFIG['system']['fetch_pipes_keys']['negation_prefix']
269
324
  for k in (connector, metric, location, *(tags or [])):
270
325
  if str(k).startswith(negation_prefix):
@@ -275,8 +330,15 @@ class Pipe:
275
330
  self.metric_key = metric
276
331
  self.location_key = location
277
332
  self.temporary = temporary
333
+ self.cache = cache if cache is not None else (not temporary)
334
+ self.cache_connector_keys = (
335
+ str(cache_connector_keys)
336
+ if cache_connector_keys is not None
337
+ else None
338
+ )
339
+ self.debug = debug
278
340
 
279
- self._attributes = {
341
+ self._attributes: Dict[str, Any] = {
280
342
  'connector_keys': self.connector_keys,
281
343
  'metric_key': self.metric_key,
282
344
  'location_key': self.location_key,
@@ -291,11 +353,13 @@ class Pipe:
291
353
  warn(f"The provided parameters are of invalid type '{type(parameters)}'.")
292
354
  self._attributes['parameters'] = {}
293
355
 
294
- columns = columns or self._attributes.get('parameters', {}).get('columns', {})
295
- if isinstance(columns, list):
356
+ columns = columns or self._attributes.get('parameters', {}).get('columns', None)
357
+ if isinstance(columns, (list, tuple)):
296
358
  columns = {str(col): str(col) for col in columns}
297
359
  if isinstance(columns, dict):
298
360
  self._attributes['parameters']['columns'] = columns
361
+ elif isinstance(columns, str) and 'Pipe(' in columns:
362
+ pass
299
363
  elif columns is not None:
300
364
  warn(f"The provided columns are of invalid type '{type(columns)}'.")
301
365
 
@@ -334,8 +398,17 @@ class Pipe:
334
398
  if isinstance(autoincrement, bool):
335
399
  self._attributes['parameters']['autoincrement'] = autoincrement
336
400
 
401
+ if isinstance(autotime, bool):
402
+ self._attributes['parameters']['autotime'] = autotime
403
+
404
+ if isinstance(precision, dict):
405
+ self._attributes['parameters']['precision'] = precision
406
+ elif isinstance(precision, str):
407
+ self._attributes['parameters']['precision'] = {'unit': precision}
408
+
337
409
  if isinstance(static, bool):
338
410
  self._attributes['parameters']['static'] = static
411
+ self._static = static
339
412
 
340
413
  if isinstance(enforce, bool):
341
414
  self._attributes['parameters']['enforce'] = enforce
@@ -343,6 +416,9 @@ class Pipe:
343
416
  if isinstance(null_indices, bool):
344
417
  self._attributes['parameters']['null_indices'] = null_indices
345
418
 
419
+ if isinstance(mixed_numerics, bool):
420
+ self._attributes['parameters']['mixed_numerics'] = mixed_numerics
421
+
346
422
  ### NOTE: The parameters dictionary is {} by default.
347
423
  ### A Pipe may be registered without parameters, then edited,
348
424
  ### or a Pipe may be registered with parameters set in-memory first.
@@ -353,10 +429,12 @@ class Pipe:
353
429
  if not isinstance(_mrsm_instance, str):
354
430
  self._instance_connector = _mrsm_instance
355
431
  self.instance_keys = str(_mrsm_instance)
356
- else: ### NOTE: must be SQL or API Connector for this work
432
+ else:
357
433
  self.instance_keys = _mrsm_instance
358
434
 
359
- self._cache = cache and get_config('system', 'experimental', 'cache')
435
+ if self.instance_keys == 'sql:memory':
436
+ self.cache = False
437
+
360
438
 
361
439
  @property
362
440
  def meta(self):
@@ -383,9 +461,7 @@ class Pipe:
383
461
  @property
384
462
  def instance_connector(self) -> Union[InstanceConnector, None]:
385
463
  """
386
- The connector to where this pipe resides.
387
- May either be of type `meerschaum.connectors.sql.SQLConnector` or
388
- `meerschaum.connectors.api.APIConnector`.
464
+ The instance connector on which this pipe resides.
389
465
  """
390
466
  if '_instance_connector' not in self.__dict__:
391
467
  from meerschaum.connectors.parse import parse_instance_keys
@@ -397,7 +473,7 @@ class Pipe:
397
473
  return self._instance_connector
398
474
 
399
475
  @property
400
- def connector(self) -> Union[meerschaum.connectors.Connector, None]:
476
+ def connector(self) -> Union['Connector', None]:
401
477
  """
402
478
  The connector to the data source.
403
479
  """
@@ -416,68 +492,6 @@ class Pipe:
416
492
  return None
417
493
  return self._connector
418
494
 
419
- @property
420
- def cache_connector(self) -> Union[meerschaum.connectors.sql.SQLConnector, None]:
421
- """
422
- If the pipe was created with `cache=True`, return the connector to the pipe's
423
- SQLite database for caching.
424
- """
425
- if not self._cache:
426
- return None
427
-
428
- if '_cache_connector' not in self.__dict__:
429
- from meerschaum.connectors import get_connector
430
- from meerschaum.config._paths import DUCKDB_RESOURCES_PATH, SQLITE_RESOURCES_PATH
431
- _resources_path = SQLITE_RESOURCES_PATH
432
- self._cache_connector = get_connector(
433
- 'sql', '_cache_' + str(self),
434
- flavor='sqlite',
435
- database=str(_resources_path / ('_cache_' + str(self) + '.db')),
436
- )
437
-
438
- return self._cache_connector
439
-
440
- @property
441
- def cache_pipe(self) -> Union['meerschaum.Pipe', None]:
442
- """
443
- If the pipe was created with `cache=True`, return another `meerschaum.Pipe` used to
444
- manage the local data.
445
- """
446
- if self.cache_connector is None:
447
- return None
448
- if '_cache_pipe' not in self.__dict__:
449
- from meerschaum.config._patch import apply_patch_to_config
450
- from meerschaum.utils.sql import sql_item_name
451
- _parameters = copy.deepcopy(self.parameters)
452
- _fetch_patch = {
453
- 'fetch': ({
454
- 'definition': (
455
- "SELECT * FROM "
456
- + sql_item_name(
457
- str(self.target),
458
- self.instance_connector.flavor,
459
- self.instance_connector.get_pipe_schema(self),
460
- )
461
- ),
462
- }) if self.instance_connector.type == 'sql' else ({
463
- 'connector_keys': self.connector_keys,
464
- 'metric_key': self.metric_key,
465
- 'location_key': self.location_key,
466
- })
467
- }
468
- _parameters = apply_patch_to_config(_parameters, _fetch_patch)
469
- self._cache_pipe = Pipe(
470
- self.instance_keys,
471
- (self.connector_keys + '_' + self.metric_key + '_cache'),
472
- self.location_key,
473
- mrsm_instance = self.cache_connector,
474
- parameters = _parameters,
475
- cache = False,
476
- temporary = True,
477
- )
478
-
479
- return self._cache_pipe
480
-
481
495
  def __str__(self, ansi: bool=False):
482
496
  return pipe_repr(self, ansi=ansi)
483
497
 
@@ -522,7 +536,7 @@ class Pipe:
522
536
  'connector_keys': self.connector_keys,
523
537
  'metric_key': self.metric_key,
524
538
  'location_key': self.location_key,
525
- 'parameters': self.parameters,
539
+ 'parameters': self._attributes.get('parameters', None),
526
540
  'instance_keys': self.instance_keys,
527
541
  }
528
542
 
@@ -558,3 +572,19 @@ class Pipe:
558
572
  if aliased_key is not None:
559
573
  key = aliased_key
560
574
  return getattr(self, key, None)
575
+
576
+ def __copy__(self):
577
+ """
578
+ Return a shallow copy of the current pipe.
579
+ """
580
+ return mrsm.Pipe(
581
+ self.connector_keys, self.metric_key, self.location_key,
582
+ instance=self.instance_keys,
583
+ parameters=self._attributes.get('parameters', None),
584
+ )
585
+
586
+ def __deepcopy__(self, memo):
587
+ """
588
+ Return a deep copy of the current pipe.
589
+ """
590
+ return self.__copy__()