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
@@ -0,0 +1,83 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Define the interface for instance connectors.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import abc
11
+ from typing import Any, Union, Dict, List, Tuple, Optional
12
+
13
+ import meerschaum as mrsm
14
+ from meerschaum.connectors._Connector import Connector
15
+
16
+
17
+ class InstanceConnector(Connector):
18
+ """
19
+ Instance connectors define the interface for managing pipes and provide methods
20
+ for management of users, plugins, tokens, and other metadata built atop pipes.
21
+ """
22
+
23
+ IS_INSTANCE: bool = True
24
+ IS_THREAD_SAFE: bool = False
25
+
26
+ from ._users import (
27
+ get_users_pipe,
28
+ register_user,
29
+ get_user_id,
30
+ get_username,
31
+ get_users,
32
+ edit_user,
33
+ delete_user,
34
+ get_user_password_hash,
35
+ get_user_type,
36
+ get_user_attributes,
37
+ )
38
+
39
+ from ._plugins import (
40
+ get_plugins_pipe,
41
+ register_plugin,
42
+ get_plugin_user_id,
43
+ delete_plugin,
44
+ get_plugin_id,
45
+ get_plugin_version,
46
+ get_plugins,
47
+ get_plugin_user_id,
48
+ get_plugin_username,
49
+ get_plugin_attributes,
50
+ )
51
+
52
+ from ._tokens import (
53
+ get_tokens_pipe,
54
+ register_token,
55
+ edit_token,
56
+ invalidate_token,
57
+ delete_token,
58
+ get_token,
59
+ get_tokens,
60
+ get_token_model,
61
+ get_token_secret_hash,
62
+ token_exists,
63
+ get_token_scopes,
64
+ )
65
+
66
+ from ._pipes import (
67
+ register_pipe,
68
+ get_pipe_attributes,
69
+ get_pipe_id,
70
+ edit_pipe,
71
+ delete_pipe,
72
+ fetch_pipes_keys,
73
+ pipe_exists,
74
+ drop_pipe,
75
+ drop_pipe_indices,
76
+ sync_pipe,
77
+ create_pipe_indices,
78
+ clear_pipe,
79
+ get_pipe_data,
80
+ get_sync_time,
81
+ get_pipe_columns_types,
82
+ get_pipe_columns_indices,
83
+ )
@@ -0,0 +1,10 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Define high-level instance connector methods.
6
+ """
7
+
8
+ from meerschaum.connectors.instance._InstanceConnector import InstanceConnector
9
+
10
+ __all__ = ('InstanceConnector',)
@@ -0,0 +1,442 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Define function signatures for pipes' methods.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import abc
11
+ from typing import Any, Union, Dict, List, Tuple, Optional
12
+ from datetime import datetime
13
+
14
+ import meerschaum as mrsm
15
+
16
+ @abc.abstractmethod
17
+ def register_pipe(
18
+ self,
19
+ pipe: mrsm.Pipe,
20
+ debug: bool = False,
21
+ **kwargs: Any
22
+ ) -> mrsm.SuccessTuple:
23
+ """
24
+ Insert the pipe's attributes into the internal `pipes` table.
25
+
26
+ Parameters
27
+ ----------
28
+ pipe: mrsm.Pipe
29
+ The pipe to be registered.
30
+
31
+ Returns
32
+ -------
33
+ A `SuccessTuple` of the result.
34
+ """
35
+
36
+ @abc.abstractmethod
37
+ def get_pipe_attributes(
38
+ self,
39
+ pipe: mrsm.Pipe,
40
+ debug: bool = False,
41
+ **kwargs: Any
42
+ ) -> Dict[str, Any]:
43
+ """
44
+ Return the pipe's document from the internal `pipes` table.
45
+
46
+ Parameters
47
+ ----------
48
+ pipe: mrsm.Pipe
49
+ The pipe whose attributes should be retrieved.
50
+
51
+ Returns
52
+ -------
53
+ The document that matches the keys of the pipe.
54
+ """
55
+
56
+ @abc.abstractmethod
57
+ def get_pipe_id(
58
+ self,
59
+ pipe: mrsm.Pipe,
60
+ debug: bool = False,
61
+ **kwargs: Any
62
+ ) -> Union[str, int, None]:
63
+ """
64
+ Return the `id` for the pipe if it exists.
65
+
66
+ Parameters
67
+ ----------
68
+ pipe: mrsm.Pipe
69
+ The pipe whose `id` to fetch.
70
+
71
+ Returns
72
+ -------
73
+ The `id` for the pipe's document or `None`.
74
+ """
75
+
76
+ def edit_pipe(
77
+ self,
78
+ pipe: mrsm.Pipe,
79
+ debug: bool = False,
80
+ **kwargs: Any
81
+ ) -> mrsm.SuccessTuple:
82
+ """
83
+ Edit the attributes of the pipe.
84
+
85
+ Parameters
86
+ ----------
87
+ pipe: mrsm.Pipe
88
+ The pipe whose in-memory parameters must be persisted.
89
+
90
+ Returns
91
+ -------
92
+ A `SuccessTuple` indicating success.
93
+ """
94
+ raise NotImplementedError
95
+
96
+ def delete_pipe(
97
+ self,
98
+ pipe: mrsm.Pipe,
99
+ debug: bool = False,
100
+ **kwargs: Any
101
+ ) -> mrsm.SuccessTuple:
102
+ """
103
+ Delete a pipe's registration from the `pipes` collection.
104
+
105
+ Parameters
106
+ ----------
107
+ pipe: mrsm.Pipe
108
+ The pipe to be deleted.
109
+
110
+ Returns
111
+ -------
112
+ A `SuccessTuple` indicating success.
113
+ """
114
+ raise NotImplementedError
115
+
116
+ @abc.abstractmethod
117
+ def fetch_pipes_keys(
118
+ self,
119
+ connector_keys: Optional[List[str]] = None,
120
+ metric_keys: Optional[List[str]] = None,
121
+ location_keys: Optional[List[str]] = None,
122
+ tags: Optional[List[str]] = None,
123
+ debug: bool = False,
124
+ **kwargs: Any
125
+ ) -> List[Tuple[str, str, str]]:
126
+ """
127
+ Return a list of tuples for the registered pipes' keys according to the provided filters.
128
+
129
+ Parameters
130
+ ----------
131
+ connector_keys: list[str] | None, default None
132
+ The keys passed via `-c`.
133
+
134
+ metric_keys: list[str] | None, default None
135
+ The keys passed via `-m`.
136
+
137
+ location_keys: list[str] | None, default None
138
+ The keys passed via `-l`.
139
+
140
+ tags: List[str] | None, default None
141
+ Tags passed via `--tags` which are stored under `parameters:tags`.
142
+
143
+ Returns
144
+ -------
145
+ A list of connector, metric, and location keys in tuples.
146
+ You may return the string "None" for location keys in place of nulls.
147
+
148
+ Examples
149
+ --------
150
+ >>> import meerschaum as mrsm
151
+ >>> conn = mrsm.get_connector('example:demo')
152
+ >>>
153
+ >>> pipe_a = mrsm.Pipe('a', 'demo', tags=['foo'], instance=conn)
154
+ >>> pipe_b = mrsm.Pipe('b', 'demo', tags=['bar'], instance=conn)
155
+ >>> pipe_a.register()
156
+ >>> pipe_b.register()
157
+ >>>
158
+ >>> conn.fetch_pipes_keys(['a', 'b'])
159
+ [('a', 'demo', 'None'), ('b', 'demo', 'None')]
160
+ >>> conn.fetch_pipes_keys(metric_keys=['demo'])
161
+ [('a', 'demo', 'None'), ('b', 'demo', 'None')]
162
+ >>> conn.fetch_pipes_keys(tags=['foo'])
163
+ [('a', 'demo', 'None')]
164
+ >>> conn.fetch_pipes_keys(location_keys=[None])
165
+ [('a', 'demo', 'None'), ('b', 'demo', 'None')]
166
+ """
167
+
168
+ @abc.abstractmethod
169
+ def pipe_exists(
170
+ self,
171
+ pipe: mrsm.Pipe,
172
+ debug: bool = False,
173
+ **kwargs: Any
174
+ ) -> bool:
175
+ """
176
+ Check whether a pipe's target table exists.
177
+
178
+ Parameters
179
+ ----------
180
+ pipe: mrsm.Pipe
181
+ The pipe to check whether its table exists.
182
+
183
+ Returns
184
+ -------
185
+ A `bool` indicating the table exists.
186
+ """
187
+
188
+ @abc.abstractmethod
189
+ def drop_pipe(
190
+ self,
191
+ pipe: mrsm.Pipe,
192
+ debug: bool = False,
193
+ **kwargs: Any
194
+ ) -> mrsm.SuccessTuple:
195
+ """
196
+ Drop a pipe's collection if it exists.
197
+
198
+ Parameters
199
+ ----------
200
+ pipe: mrsm.Pipe
201
+ The pipe to be dropped.
202
+
203
+ Returns
204
+ -------
205
+ A `SuccessTuple` indicating success.
206
+ """
207
+ raise NotImplementedError
208
+
209
+ def drop_pipe_indices(
210
+ self,
211
+ pipe: mrsm.Pipe,
212
+ debug: bool = False,
213
+ **kwargs: Any
214
+ ) -> mrsm.SuccessTuple:
215
+ """
216
+ Drop a pipe's indices.
217
+
218
+ Parameters
219
+ ----------
220
+ pipe: mrsm.Pipe
221
+ The pipe whose indices need to be dropped.
222
+
223
+ Returns
224
+ -------
225
+ A `SuccessTuple` indicating success.
226
+ """
227
+ return False, f"Cannot drop indices for instance connectors of type '{self.type}'."
228
+
229
+ @abc.abstractmethod
230
+ def sync_pipe(
231
+ self,
232
+ pipe: mrsm.Pipe,
233
+ df: 'pd.DataFrame' = None,
234
+ begin: Union[datetime, int, None] = None,
235
+ end: Union[datetime, int, None] = None,
236
+ chunksize: Optional[int] = -1,
237
+ check_existing: bool = True,
238
+ debug: bool = False,
239
+ **kwargs: Any
240
+ ) -> mrsm.SuccessTuple:
241
+ """
242
+ Sync a pipe using a database connection.
243
+
244
+ Parameters
245
+ ----------
246
+ pipe: mrsm.Pipe
247
+ The Meerschaum Pipe instance into which to sync the data.
248
+
249
+ df: Optional[pd.DataFrame]
250
+ An optional DataFrame or equivalent to sync into the pipe.
251
+ Defaults to `None`.
252
+
253
+ begin: Union[datetime, int, None], default None
254
+ Optionally specify the earliest datetime to search for data.
255
+ Defaults to `None`.
256
+
257
+ end: Union[datetime, int, None], default None
258
+ Optionally specify the latest datetime to search for data.
259
+ Defaults to `None`.
260
+
261
+ chunksize: Optional[int], default -1
262
+ Specify the number of rows to sync per chunk.
263
+ If `-1`, resort to system configuration (default is `900`).
264
+ A `chunksize` of `None` will sync all rows in one transaction.
265
+ Defaults to `-1`.
266
+
267
+ check_existing: bool, default True
268
+ If `True`, pull and diff with existing data from the pipe. Defaults to `True`.
269
+
270
+ debug: bool, default False
271
+ Verbosity toggle. Defaults to False.
272
+
273
+ Returns
274
+ -------
275
+ A `SuccessTuple` of success (`bool`) and message (`str`).
276
+ """
277
+
278
+ def create_pipe_indices(
279
+ self,
280
+ pipe: mrsm.Pipe,
281
+ debug: bool = False,
282
+ **kwargs: Any
283
+ ) -> mrsm.SuccessTuple:
284
+ """
285
+ Create a pipe's indices.
286
+
287
+ Parameters
288
+ ----------
289
+ pipe: mrsm.Pipe
290
+ The pipe whose indices need to be created.
291
+
292
+ Returns
293
+ -------
294
+ A `SuccessTuple` indicating success.
295
+ """
296
+ return False, f"Cannot create indices for instance connectors of type '{self.type}'."
297
+
298
+ def clear_pipe(
299
+ self,
300
+ pipe: mrsm.Pipe,
301
+ begin: Union[datetime, int, None] = None,
302
+ end: Union[datetime, int, None] = None,
303
+ params: Optional[Dict[str, Any]] = None,
304
+ debug: bool = False,
305
+ **kwargs: Any
306
+ ) -> mrsm.SuccessTuple:
307
+ """
308
+ Delete rows within `begin`, `end`, and `params`.
309
+
310
+ Parameters
311
+ ----------
312
+ pipe: mrsm.Pipe
313
+ The pipe whose rows to clear.
314
+
315
+ begin: datetime | int | None, default None
316
+ If provided, remove rows >= `begin`.
317
+
318
+ end: datetime | int | None, default None
319
+ If provided, remove rows < `end`.
320
+
321
+ params: dict[str, Any] | None, default None
322
+ If provided, only remove rows which match the `params` filter.
323
+
324
+ Returns
325
+ -------
326
+ A `SuccessTuple` indicating success.
327
+ """
328
+ raise NotImplementedError
329
+
330
+ @abc.abstractmethod
331
+ def get_pipe_data(
332
+ self,
333
+ pipe: mrsm.Pipe,
334
+ select_columns: Optional[List[str]] = None,
335
+ omit_columns: Optional[List[str]] = None,
336
+ begin: Union[datetime, int, None] = None,
337
+ end: Union[datetime, int, None] = None,
338
+ params: Optional[Dict[str, Any]] = None,
339
+ debug: bool = False,
340
+ **kwargs: Any
341
+ ) -> Union['pd.DataFrame', None]:
342
+ """
343
+ Query a pipe's target table and return the DataFrame.
344
+
345
+ Parameters
346
+ ----------
347
+ pipe: mrsm.Pipe
348
+ The pipe with the target table from which to read.
349
+
350
+ select_columns: list[str] | None, default None
351
+ If provided, only select these given columns.
352
+ Otherwise select all available columns (i.e. `SELECT *`).
353
+
354
+ omit_columns: list[str] | None, default None
355
+ If provided, remove these columns from the selection.
356
+
357
+ begin: datetime | int | None, default None
358
+ The earliest `datetime` value to search from (inclusive).
359
+
360
+ end: datetime | int | None, default None
361
+ The lastest `datetime` value to search from (exclusive).
362
+
363
+ params: dict[str | str] | None, default None
364
+ Additional filters to apply to the query.
365
+
366
+ Returns
367
+ -------
368
+ The target table's data as a DataFrame.
369
+ """
370
+
371
+ @abc.abstractmethod
372
+ def get_sync_time(
373
+ self,
374
+ pipe: mrsm.Pipe,
375
+ params: Optional[Dict[str, Any]] = None,
376
+ newest: bool = True,
377
+ debug: bool = False,
378
+ **kwargs: Any
379
+ ) -> datetime | int | None:
380
+ """
381
+ Return the most recent value for the `datetime` axis.
382
+
383
+ Parameters
384
+ ----------
385
+ pipe: mrsm.Pipe
386
+ The pipe whose collection contains documents.
387
+
388
+ params: dict[str, Any] | None, default None
389
+ Filter certain parameters when determining the sync time.
390
+
391
+ newest: bool, default True
392
+ If `True`, return the maximum value for the column.
393
+
394
+ Returns
395
+ -------
396
+ The largest `datetime` or `int` value of the `datetime` axis.
397
+ """
398
+
399
+ @abc.abstractmethod
400
+ def get_pipe_columns_types(
401
+ self,
402
+ pipe: mrsm.Pipe,
403
+ debug: bool = False,
404
+ **kwargs: Any
405
+ ) -> Dict[str, str]:
406
+ """
407
+ Return the data types for the columns in the target table for data type enforcement.
408
+
409
+ Parameters
410
+ ----------
411
+ pipe: mrsm.Pipe
412
+ The pipe whose target table contains columns and data types.
413
+
414
+ Returns
415
+ -------
416
+ A dictionary mapping columns to data types.
417
+ """
418
+
419
+ def get_pipe_columns_indices(
420
+ self,
421
+ debug: bool = False,
422
+ ) -> Dict[str, List[Dict[str, str]]]:
423
+ """
424
+ Return a dictionary mapping columns to metadata about related indices.
425
+
426
+ Parameters
427
+ ----------
428
+ pipe: mrsm.Pipe
429
+ The pipe whose target table has related indices.
430
+
431
+ Returns
432
+ -------
433
+ A list of dictionaries with the keys "type" and "name".
434
+
435
+ Examples
436
+ --------
437
+ >>> pipe = mrsm.Pipe('demo', 'shirts', columns={'primary': 'id'}, indices={'size_color': ['color', 'size']})
438
+ >>> pipe.sync([{'color': 'red', 'size': 'M'}])
439
+ >>> pipe.get_columns_indices()
440
+ {'id': [{'name': 'demo_shirts_pkey', 'type': 'PRIMARY KEY'}], 'color': [{'name': 'IX_demo_shirts_color_size', 'type': 'INDEX'}], 'size': [{'name': 'IX_demo_shirts_color_size', 'type': 'INDEX'}]}
441
+ """
442
+ return {}
@@ -0,0 +1,159 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Define high-level plugins methods for instance connectors.
6
+ """
7
+
8
+ import uuid
9
+ from typing import Union, Optional, List, Dict, Any
10
+
11
+ import meerschaum as mrsm
12
+ from meerschaum.core import Plugin
13
+
14
+
15
+ def get_plugins_pipe(self) -> 'mrsm.Pipe':
16
+ """
17
+ Return the internal pipe for syncing plugins metadata.
18
+ """
19
+ if '_plugins_pipe' in self.__dict__:
20
+ return self._plugins_pipe
21
+
22
+ cache_connector = self.__dict__.get('_cache_connector', None)
23
+ users_pipe = self.get_users_pipe()
24
+ user_id_dtype = users_pipe.dtypes.get('user_id', 'uuid')
25
+
26
+ self._plugins_pipe = mrsm.Pipe(
27
+ 'mrsm', 'plugins',
28
+ instance=self,
29
+ target='mrsm_plugins',
30
+ temporary=True,
31
+ cache=True,
32
+ cache_connector_keys=cache_connector,
33
+ static=True,
34
+ null_indices=False,
35
+ columns={
36
+ 'primary': 'plugin_name',
37
+ 'user_id': 'user_id',
38
+ },
39
+ dtypes={
40
+ 'plugin_name': 'string',
41
+ 'user_id': user_id_dtype,
42
+ 'attributes': 'json',
43
+ 'version': 'string',
44
+ },
45
+ )
46
+ return self._plugins_pipe
47
+
48
+
49
+ def register_plugin(self, plugin: Plugin, debug: bool = False) -> mrsm.SuccessTuple:
50
+ """
51
+ Register a new plugin to the plugins table.
52
+ """
53
+ plugins_pipe = self.get_plugins_pipe()
54
+ users_pipe = self.get_users_pipe()
55
+ user_id = self.get_plugin_user_id(plugin)
56
+ if user_id is not None:
57
+ username = self.get_username(user_id, debug=debug)
58
+ return False, f"{plugin} is already registered to '{username}'."
59
+
60
+ doc = {
61
+ 'plugin_name': plugin.name,
62
+ 'version': plugin.version,
63
+ 'attributes': plugin.attributes,
64
+ 'user_id': plugin.user_id,
65
+ }
66
+
67
+ sync_success, sync_msg = plugins_pipe.sync(
68
+ [doc],
69
+ check_existing=False,
70
+ debug=debug,
71
+ )
72
+ if not sync_success:
73
+ return False, f"Failed to register {plugin}:\n{sync_msg}"
74
+
75
+ return True, "Success"
76
+
77
+
78
+ def get_plugin_user_id(self, plugin: Plugin, debug: bool = False) -> Union[uuid.UUID, None]:
79
+ """
80
+ Return the user ID for plugin's owner.
81
+ """
82
+ plugins_pipe = self.get_plugins_pipe()
83
+ return plugins_pipe.get_value('user_id', {'plugin_name': plugin.name}, debug=debug)
84
+
85
+
86
+ def get_plugin_username(self, plugin: Plugin, debug: bool = False) -> Union[uuid.UUID, None]:
87
+ """
88
+ Return the username for plugin's owner.
89
+ """
90
+ user_id = self.get_plugin_user_id(plugin, debug=debug)
91
+ if user_id is None:
92
+ return None
93
+ return self.get_username(user_id, debug=debug)
94
+
95
+
96
+ def get_plugin_id(self, plugin: Plugin, debug: bool = False) -> Union[str, None]:
97
+ """
98
+ Return a plugin's ID.
99
+ """
100
+ user_id = self.get_plugin_user_id(plugin, debug=debug)
101
+ return plugin.name if user_id is not None else None
102
+
103
+
104
+ def delete_plugin(self, plugin: Plugin, debug: bool = False) -> mrsm.SuccessTuple:
105
+ """
106
+ Delete a plugin's registration.
107
+ """
108
+ plugin_id = self.get_plugin_id(plugin, debug=debug)
109
+ if plugin_id is None:
110
+ return False, f"{plugin} is not registered."
111
+
112
+ plugins_pipe = self.get_plugins_pipe()
113
+ clear_success, clear_msg = plugins_pipe.clear(params={'plugin_name': plugin.name}, debug=debug)
114
+ if not clear_success:
115
+ return False, f"Failed to delete {plugin}:\n{clear_msg}"
116
+ return True, "Success"
117
+
118
+
119
+ def get_plugin_version(self, plugin: Plugin, debug: bool = False) -> Union[str, None]:
120
+ """
121
+ Return the version for a plugin.
122
+ """
123
+ plugins_pipe = self.get_plugins_pipe()
124
+ return plugins_pipe.get_value('version', {'plugin_name': plugin.name}, debug=debug)
125
+
126
+
127
+ def get_plugin_attributes(self, plugin: Plugin, debug: bool = False) -> Dict[str, Any]:
128
+ """
129
+ Return the attributes for a plugin.
130
+ """
131
+ plugins_pipe = self.get_plugins_pipe()
132
+ return plugins_pipe.get_value('attributes', {'plugin_name': plugin.name}, debug=debug) or {}
133
+
134
+
135
+ def get_plugins(
136
+ self,
137
+ user_id: Optional[int] = None,
138
+ search_term: Optional[str] = None,
139
+ debug: bool = False,
140
+ **kw: Any
141
+ ) -> List[str]:
142
+ """
143
+ Return a list of plugin names.
144
+ """
145
+ plugins_pipe = self.get_plugins_pipe()
146
+ params = {}
147
+ if user_id:
148
+ params['user_id'] = user_id
149
+
150
+ df = plugins_pipe.get_data(['plugin_name'], params=params, debug=debug)
151
+ if df is None:
152
+ return []
153
+
154
+ docs = df.to_dict(orient='records')
155
+ return [
156
+ plugin_name
157
+ for doc in docs
158
+ if (plugin_name := doc['plugin_name']).startswith(search_term or '')
159
+ ]