meerschaum 2.9.5__py3-none-any.whl → 3.0.0rc1__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 (153) 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 +17 -1
  5. meerschaum/_internal/entry.py +6 -6
  6. meerschaum/_internal/shell/Shell.py +1 -1
  7. meerschaum/_internal/static.py +372 -0
  8. meerschaum/actions/api.py +12 -2
  9. meerschaum/actions/bootstrap.py +7 -7
  10. meerschaum/actions/edit.py +142 -18
  11. meerschaum/actions/register.py +137 -6
  12. meerschaum/actions/show.py +117 -29
  13. meerschaum/actions/stop.py +4 -1
  14. meerschaum/actions/sync.py +1 -1
  15. meerschaum/actions/tag.py +9 -8
  16. meerschaum/api/__init__.py +9 -2
  17. meerschaum/api/_events.py +39 -2
  18. meerschaum/api/_oauth2.py +118 -8
  19. meerschaum/api/_tokens.py +102 -0
  20. meerschaum/api/dash/__init__.py +0 -1
  21. meerschaum/api/dash/callbacks/custom.py +2 -2
  22. meerschaum/api/dash/callbacks/dashboard.py +102 -18
  23. meerschaum/api/dash/callbacks/plugins.py +0 -1
  24. meerschaum/api/dash/callbacks/register.py +1 -1
  25. meerschaum/api/dash/callbacks/settings/__init__.py +1 -0
  26. meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
  27. meerschaum/api/dash/callbacks/settings/tokens.py +388 -0
  28. meerschaum/api/dash/components.py +30 -8
  29. meerschaum/api/dash/keys.py +19 -93
  30. meerschaum/api/dash/pages/dashboard.py +1 -20
  31. meerschaum/api/dash/pages/settings/__init__.py +1 -0
  32. meerschaum/api/dash/pages/settings/password_reset.py +1 -1
  33. meerschaum/api/dash/pages/settings/tokens.py +55 -0
  34. meerschaum/api/dash/pipes.py +94 -59
  35. meerschaum/api/dash/sessions.py +12 -0
  36. meerschaum/api/dash/tokens.py +606 -0
  37. meerschaum/api/dash/websockets.py +1 -1
  38. meerschaum/api/dash/webterm.py +4 -0
  39. meerschaum/api/models/__init__.py +23 -3
  40. meerschaum/api/models/_actions.py +22 -0
  41. meerschaum/api/models/_pipes.py +85 -7
  42. meerschaum/api/models/_tokens.py +81 -0
  43. meerschaum/api/resources/templates/termpage.html +12 -0
  44. meerschaum/api/routes/__init__.py +1 -0
  45. meerschaum/api/routes/_actions.py +3 -4
  46. meerschaum/api/routes/_connectors.py +3 -7
  47. meerschaum/api/routes/_jobs.py +14 -35
  48. meerschaum/api/routes/_login.py +49 -12
  49. meerschaum/api/routes/_misc.py +5 -10
  50. meerschaum/api/routes/_pipes.py +134 -111
  51. meerschaum/api/routes/_plugins.py +38 -28
  52. meerschaum/api/routes/_tokens.py +236 -0
  53. meerschaum/api/routes/_users.py +47 -35
  54. meerschaum/api/routes/_version.py +3 -3
  55. meerschaum/config/__init__.py +43 -20
  56. meerschaum/config/_default.py +32 -5
  57. meerschaum/config/_edit.py +28 -24
  58. meerschaum/config/_environment.py +1 -1
  59. meerschaum/config/_patch.py +6 -6
  60. meerschaum/config/_paths.py +5 -1
  61. meerschaum/config/_read_config.py +65 -34
  62. meerschaum/config/_sync.py +6 -3
  63. meerschaum/config/_version.py +1 -1
  64. meerschaum/config/stack/__init__.py +24 -5
  65. meerschaum/config/static.py +18 -0
  66. meerschaum/connectors/_Connector.py +10 -4
  67. meerschaum/connectors/__init__.py +4 -20
  68. meerschaum/connectors/api/_APIConnector.py +34 -6
  69. meerschaum/connectors/api/_actions.py +2 -2
  70. meerschaum/connectors/api/_jobs.py +1 -1
  71. meerschaum/connectors/api/_login.py +33 -7
  72. meerschaum/connectors/api/_misc.py +2 -2
  73. meerschaum/connectors/api/_pipes.py +15 -14
  74. meerschaum/connectors/api/_plugins.py +2 -2
  75. meerschaum/connectors/api/_request.py +1 -1
  76. meerschaum/connectors/api/_tokens.py +146 -0
  77. meerschaum/connectors/api/_users.py +70 -58
  78. meerschaum/connectors/instance/_InstanceConnector.py +83 -0
  79. meerschaum/connectors/instance/__init__.py +10 -0
  80. meerschaum/connectors/instance/_pipes.py +442 -0
  81. meerschaum/connectors/instance/_plugins.py +151 -0
  82. meerschaum/connectors/instance/_tokens.py +296 -0
  83. meerschaum/connectors/instance/_users.py +181 -0
  84. meerschaum/connectors/parse.py +4 -1
  85. meerschaum/connectors/sql/_SQLConnector.py +8 -5
  86. meerschaum/connectors/sql/_cli.py +12 -11
  87. meerschaum/connectors/sql/_create_engine.py +6 -154
  88. meerschaum/connectors/sql/_fetch.py +2 -18
  89. meerschaum/connectors/sql/_pipes.py +42 -31
  90. meerschaum/connectors/sql/_plugins.py +29 -0
  91. meerschaum/connectors/sql/_sql.py +8 -1
  92. meerschaum/connectors/sql/_users.py +29 -2
  93. meerschaum/connectors/sql/tables/__init__.py +1 -1
  94. meerschaum/connectors/valkey/_ValkeyConnector.py +2 -4
  95. meerschaum/connectors/valkey/_pipes.py +9 -10
  96. meerschaum/connectors/valkey/_plugins.py +2 -26
  97. meerschaum/core/Pipe/__init__.py +31 -14
  98. meerschaum/core/Pipe/_attributes.py +156 -58
  99. meerschaum/core/Pipe/_bootstrap.py +54 -24
  100. meerschaum/core/Pipe/_data.py +41 -1
  101. meerschaum/core/Pipe/_dtypes.py +29 -14
  102. meerschaum/core/Pipe/_edit.py +12 -4
  103. meerschaum/core/Pipe/_show.py +5 -5
  104. meerschaum/core/Pipe/_sync.py +48 -53
  105. meerschaum/core/Pipe/_verify.py +1 -1
  106. meerschaum/{plugins → core/Plugin}/_Plugin.py +9 -11
  107. meerschaum/core/Plugin/__init__.py +1 -1
  108. meerschaum/core/Token/_Token.py +221 -0
  109. meerschaum/core/Token/__init__.py +12 -0
  110. meerschaum/core/User/_User.py +34 -8
  111. meerschaum/core/User/__init__.py +9 -1
  112. meerschaum/core/__init__.py +1 -0
  113. meerschaum/jobs/_Job.py +3 -2
  114. meerschaum/jobs/__init__.py +3 -2
  115. meerschaum/jobs/systemd.py +1 -1
  116. meerschaum/models/__init__.py +35 -0
  117. meerschaum/models/pipes.py +247 -0
  118. meerschaum/models/tokens.py +38 -0
  119. meerschaum/models/users.py +26 -0
  120. meerschaum/plugins/__init__.py +22 -7
  121. meerschaum/plugins/bootstrap.py +2 -1
  122. meerschaum/utils/_get_pipes.py +68 -27
  123. meerschaum/utils/daemon/Daemon.py +2 -1
  124. meerschaum/utils/daemon/__init__.py +30 -2
  125. meerschaum/utils/dataframe.py +95 -14
  126. meerschaum/utils/dtypes/__init__.py +91 -18
  127. meerschaum/utils/dtypes/sql.py +44 -0
  128. meerschaum/utils/formatting/__init__.py +1 -1
  129. meerschaum/utils/formatting/_pipes.py +5 -4
  130. meerschaum/utils/formatting/_shell.py +11 -9
  131. meerschaum/utils/misc.py +237 -80
  132. meerschaum/utils/packages/__init__.py +3 -6
  133. meerschaum/utils/packages/_packages.py +34 -32
  134. meerschaum/utils/pipes.py +181 -0
  135. meerschaum/utils/process.py +1 -1
  136. meerschaum/utils/prompt.py +3 -1
  137. meerschaum/utils/schedule.py +1 -0
  138. meerschaum/utils/sql.py +114 -37
  139. meerschaum/utils/typing.py +1 -4
  140. meerschaum/utils/venv/_Venv.py +2 -2
  141. meerschaum/utils/venv/__init__.py +5 -7
  142. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/METADATA +88 -80
  143. meerschaum-3.0.0rc1.dist-info/RECORD +282 -0
  144. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/WHEEL +1 -1
  145. meerschaum/api/models/_interfaces.py +0 -15
  146. meerschaum/api/models/_locations.py +0 -15
  147. meerschaum/api/models/_metrics.py +0 -15
  148. meerschaum/config/static/__init__.py +0 -186
  149. meerschaum-2.9.5.dist-info/RECORD +0 -263
  150. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/entry_points.txt +0 -0
  151. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/licenses/LICENSE +0 -0
  152. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/top_level.txt +0 -0
  153. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/zip-safe +0 -0
@@ -0,0 +1,247 @@
1
+ #! /usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # vim:fenc=utf-8
4
+
5
+ """
6
+ Pydantic model for a pipe's keys.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Optional, List, Tuple, Dict, Any
12
+
13
+ import meerschaum as mrsm
14
+
15
+ pydantic = mrsm.attempt_import('pydantic', lazy=False)
16
+ from pydantic import (
17
+ BaseModel,
18
+ RootModel,
19
+ field_validator,
20
+ ValidationInfo,
21
+ ConfigDict,
22
+ )
23
+
24
+
25
+ class ConnectorKeysModel(RootModel[str]):
26
+ """
27
+ Validate the connector keys:
28
+ - May not begin with an underscore (_).
29
+ - Must be at least one character long.
30
+ - May contain one or zero colons (:).
31
+ """
32
+ model_config = ConfigDict(
33
+ frozen=True,
34
+ json_schema_extra={
35
+ 'example': 'sql:main',
36
+ },
37
+ )
38
+ @field_validator('root')
39
+ @classmethod
40
+ def validate_connector_keys(cls, v: str, info: ValidationInfo) -> str:
41
+ """Validate the connector keys."""
42
+ if not v:
43
+ raise ValueError("Connector keys must be at least one character long.")
44
+ if v.startswith('_'):
45
+ raise ValueError("Connector keys may not begin with an underscore.")
46
+ if v.count(':') > 1:
47
+ raise ValueError("Connector keys may contain at most one colon.")
48
+ return v
49
+
50
+
51
+ class MetricKeyModel(RootModel[str]):
52
+ """
53
+ Validate the metric key:
54
+
55
+ - May not begin with an underscore (_).
56
+ - Must be at least one character long.
57
+ """
58
+ model_config = ConfigDict(
59
+ frozen=True,
60
+ json_schema_extra={
61
+ 'example': 'weather',
62
+ },
63
+ )
64
+ @field_validator('root')
65
+ @classmethod
66
+ def validate_metric_key(cls, v: str, info: ValidationInfo) -> str:
67
+ """Validate the metric key."""
68
+ if not v:
69
+ raise ValueError("Metric key must be at least one character long.")
70
+ if v.startswith('_'):
71
+ raise ValueError("Metric key may not begin with an underscore.")
72
+ return v
73
+
74
+
75
+ class LocationKeyModel(RootModel[Optional[str]]):
76
+ """
77
+ Validate the location key:
78
+
79
+ - May not begin with an underscore (_).
80
+ - May be null (`None`).
81
+ - If not null, must be at least one character long.
82
+ """
83
+ model_config = ConfigDict(
84
+ frozen=True,
85
+ json_schema_extra={
86
+ 'example': 'us.co.denver',
87
+ },
88
+ )
89
+ @field_validator('root')
90
+ @classmethod
91
+ def validate_location_key(cls, v: Optional[str], info: ValidationInfo) -> Optional[str]:
92
+ """Validate the location key."""
93
+ if v is None:
94
+ return v
95
+ if not v:
96
+ raise ValueError("Location key must be at least one character long if not null.")
97
+ if v.startswith('_'):
98
+ raise ValueError("Location key may not begin with an underscore.")
99
+ return v
100
+
101
+
102
+ class InstanceKeysModel(RootModel[str]):
103
+ """
104
+ Validate the instance keys. The instance keys are connector keys which must have one colon
105
+ (e.g. `'sql:main'`).
106
+ """
107
+ model_config = ConfigDict(
108
+ frozen=True,
109
+ json_schema_extra={
110
+ 'example': 'sql:main',
111
+ },
112
+ )
113
+ @field_validator('root')
114
+ @classmethod
115
+ def validate_instance_keys(cls, v: str, info: ValidationInfo) -> str:
116
+ """Validate the instance keys."""
117
+ if not v:
118
+ raise ValueError("Instance keys must be at least one character long.")
119
+ if v.startswith('_'):
120
+ raise ValueError("Instance keys may not begin with an underscore.")
121
+ if v.count(':') != 1:
122
+ raise ValueError("Instance keys must contain exactly one colon.")
123
+ return v
124
+
125
+
126
+ class PipeModel(BaseModel):
127
+ """
128
+ Define the four components to uniquely identify a pipe.
129
+ """
130
+ connector_keys: ConnectorKeysModel
131
+ metric_key: MetricKeyModel
132
+ location_key: Optional[LocationKeyModel] = None
133
+ instance_keys: Optional[InstanceKeysModel] = None
134
+ model_config = ConfigDict(
135
+ json_schema_extra={
136
+ 'example': {
137
+ 'connector_keys': 'sql:main',
138
+ 'metric_key': 'weather',
139
+ 'location_key': 'atl',
140
+ 'instance_keys': 'sql:main',
141
+ },
142
+ },
143
+ )
144
+
145
+
146
+ class PipeWithParametersModel(PipeModel):
147
+ """
148
+ A Pipe's model including its parameters dictionary.
149
+ """
150
+ parameters: Dict[str, Any]
151
+ model_config = ConfigDict(
152
+ json_schema_extra={
153
+ 'example': {
154
+ 'connector_keys': 'sql:main',
155
+ 'metric_key': 'weather',
156
+ 'location_key': 'atl',
157
+ 'instance_keys': 'sql:main',
158
+ 'parameters': {
159
+ 'columns': {
160
+ 'datetime': 'dt',
161
+ 'id': 'id',
162
+ },
163
+ },
164
+ },
165
+ },
166
+ )
167
+
168
+ class PipesWithParametersDictModel(
169
+ RootModel[
170
+ Dict[
171
+ str,
172
+ Dict[
173
+ str,
174
+ Dict[
175
+ str,
176
+ PipeWithParametersModel
177
+ ]
178
+ ]
179
+ ]
180
+ ]
181
+ ):
182
+ """
183
+ A dictionary of pipes' parameters, nested by connector, metric, and location keys.
184
+ """
185
+ @field_validator('root')
186
+ @classmethod
187
+ def validate_keys(cls, v: dict, info: ValidationInfo) -> dict:
188
+ """
189
+ Validate the dictionary keys.
190
+ """
191
+ for c_key, metrics in v.items():
192
+ ConnectorKeysModel(root=c_key)
193
+ for m_key, locations in metrics.items():
194
+ MetricKeyModel(root=m_key)
195
+ for l_key in locations:
196
+ LocationKeyModel(root=l_key if l_key not in ('None', 'null', '[None]') else None)
197
+ return v
198
+
199
+ model_config = ConfigDict(
200
+ json_schema_extra={
201
+ 'example': {
202
+ "plugin:noaa": {
203
+ "weather": {
204
+ "None": {
205
+ "pipe_id": 1,
206
+ "connector_keys": "plugin:noaa",
207
+ "metric_key": "weather",
208
+ "location_key": "clemson",
209
+ "parameters": {
210
+ "columns": {
211
+ "datetime": "timestamp",
212
+ "id": "station"
213
+ },
214
+ "noaa": {
215
+ "stations": {
216
+ "KCEU": {
217
+ "name": "Clemson, Clemson-Oconee County Airport",
218
+ "geometry": {
219
+ "type": "Point",
220
+ "coordinates": [
221
+ -82.88139,
222
+ 34.67222
223
+ ]
224
+ }
225
+ }
226
+ }
227
+ },
228
+ "verify": {
229
+ "chunk_minutes": 20160
230
+ },
231
+ "target": "plugin_noaa_weather",
232
+ "tags": [],
233
+ "dtypes": {
234
+ "timestamp": "datetime",
235
+ "geometry": "json",
236
+ "cloudLayers": "json",
237
+ "presentWeather": "json"
238
+ },
239
+ "static": False,
240
+ "enforce": True
241
+ }
242
+ }
243
+ }
244
+ }
245
+ },
246
+ },
247
+ )
@@ -0,0 +1,38 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Define Pydantic models for tokens.
6
+ """
7
+
8
+ import uuid
9
+ from typing import Optional, List, Union
10
+ from datetime import datetime
11
+
12
+ import meerschaum as mrsm
13
+
14
+ pydantic = mrsm.attempt_import('pydantic', lazy=False)
15
+ from pydantic import (
16
+ BaseModel,
17
+ RootModel,
18
+ field_validator,
19
+ ValidationInfo,
20
+ ConfigDict,
21
+ Field,
22
+ )
23
+ from meerschaum.models.users import UserModel
24
+ from meerschaum._internal.static import STATIC_CONFIG
25
+
26
+
27
+ class TokenModel(BaseModel):
28
+ """Pydantic model for a Meerschaum token."""
29
+ model_config = ConfigDict(from_attributes=True)
30
+
31
+ id: Optional[uuid.UUID] = Field(default=None)
32
+ creation: Optional[datetime] = Field(default=None)
33
+ expiration: Optional[datetime] = Field(default=None)
34
+ label: Optional[str] = Field(default=None)
35
+ user_id: Optional[Union[int, uuid.UUID]] = Field(default=None)
36
+ secret_hash: Optional[str] = Field(default=None)
37
+ scopes: Optional[List[str]] = Field(default=list(STATIC_CONFIG['tokens']['scopes']))
38
+ is_valid: bool = Field(default=True)
@@ -0,0 +1,26 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Define Pydantic models for users.
6
+ """
7
+
8
+ from __future__ import annotations
9
+ from typing import Optional, Dict, Any, Union
10
+ import uuid
11
+
12
+ import meerschaum as mrsm
13
+
14
+ pydantic = mrsm.attempt_import('pydantic', lazy=False)
15
+ from pydantic import BaseModel, Field, ConfigDict
16
+
17
+
18
+ class UserModel(BaseModel):
19
+ """Pydantic model for a Meerschaum User."""
20
+ model_config = ConfigDict(from_attributes=True)
21
+
22
+ user_id: Optional[Union[int, uuid.UUID]] = Field(default=None)
23
+ username: str
24
+ email: Optional[str] = Field(default=None)
25
+ type: Optional[str] = Field(default=None)
26
+ attributes: Optional[Dict[str, Any]] = Field(default=None)
@@ -7,11 +7,13 @@ Expose plugin management APIs from the `meerschaum.plugins` module.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
+
10
11
  import functools
12
+
11
13
  import meerschaum as mrsm
12
14
  from meerschaum.utils.typing import Callable, Any, Union, Optional, Dict, List, Tuple
13
15
  from meerschaum.utils.threading import Lock, RLock
14
- from meerschaum.plugins._Plugin import Plugin
16
+ from meerschaum.core.Plugin import Plugin
15
17
 
16
18
  _api_plugins: Dict[str, List[Callable[['fastapi.App'], Any]]] = {}
17
19
  _pre_sync_hooks: Dict[str, List[Callable[[Any], Any]]] = {}
@@ -28,15 +30,28 @@ _locks = {
28
30
  'PLUGINS_INTERNAL_LOCK_PATH': RLock(),
29
31
  }
30
32
  __all__ = (
31
- "Plugin", "make_action", "api_plugin", "dash_plugin", "web_page",
32
- "import_plugins", "from_plugin_import",
33
- "reload_plugins", "get_plugins", "get_data_plugins", "add_plugin_argument",
34
- "pre_sync_hook", "post_sync_hook",
33
+ "Plugin",
34
+ "make_action",
35
+ "api_plugin",
36
+ "dash_plugin",
37
+ "web_page",
38
+ "import_plugins",
39
+ "from_plugin_import",
40
+ "reload_plugins",
41
+ "get_plugins",
42
+ "get_data_plugins",
43
+ "add_plugin_argument",
44
+ "pre_sync_hook",
45
+ "post_sync_hook",
35
46
  )
36
47
  __pdoc__ = {
37
- 'venvs': False, 'data': False, 'stack': False, 'plugins': False,
48
+ 'venvs': False,
49
+ 'data': False,
50
+ 'stack': False,
51
+ 'plugins': False,
38
52
  }
39
53
 
54
+
40
55
  def make_action(
41
56
  function: Callable[[Any], Any],
42
57
  shell: bool = False,
@@ -298,7 +313,7 @@ def sync_plugins_symlinks(debug: bool = False, warn: bool = True) -> None:
298
313
  import importlib.util
299
314
  from meerschaum.utils.misc import flatten_list, make_symlink, is_symlink
300
315
  from meerschaum.utils.warnings import error, warn as _warn
301
- from meerschaum.config.static import STATIC_CONFIG
316
+ from meerschaum._internal.static import STATIC_CONFIG
302
317
  from meerschaum.utils.venv import Venv, activate_venv, deactivate_venv, is_venv_active
303
318
  from meerschaum.config._paths import (
304
319
  PLUGINS_RESOURCES_PATH,
@@ -129,9 +129,10 @@ FEATURE_LINES: Dict[str, str] = {
129
129
  " import dash.html as html\n"
130
130
  " import dash.dcc as dcc\n"
131
131
  " from dash import Input, Output, State, no_update\n"
132
+ " from dash.exceptions import PreventUpdate\n"
132
133
  " import dash_bootstrap_components as dbc\n\n"
133
134
  " # Create a new page at the path `/dash/{plugin_name}`.\n"
134
- " @web_page('{plugin_name}', login_required=False)\n"
135
+ " @web_page('{plugin_name}', page_group='{plugin_name}', login_required=False)\n"
135
136
  " def page_layout():\n"
136
137
  " \"\"\"Return the layout objects for this page.\"\"\"\n"
137
138
  " return dbc.Container([\n"
@@ -10,11 +10,21 @@ from __future__ import annotations
10
10
 
11
11
  import meerschaum as mrsm
12
12
  from meerschaum.utils.typing import (
13
- Sequence, Optional, Union, Mapping, Any, InstanceConnector, PipesDict, List, Dict, Tuple
13
+ Sequence,
14
+ Optional,
15
+ Union,
16
+ Mapping,
17
+ Any,
18
+ PipesDict,
19
+ List,
20
+ Dict,
21
+ Tuple,
22
+ InstanceConnector,
14
23
  )
15
24
 
16
25
  __pdoc__ = {'get_pipes': True, 'fetch_pipes_keys': True}
17
26
 
27
+
18
28
  def get_pipes(
19
29
  connector_keys: Union[str, List[str], None] = None,
20
30
  metric_keys: Union[str, List[str], None] = None,
@@ -24,10 +34,12 @@ def get_pipes(
24
34
  mrsm_instance: Union[str, InstanceConnector, None] = None,
25
35
  instance: Union[str, InstanceConnector, None] = None,
26
36
  as_list: bool = False,
37
+ as_tags_dict: bool = False,
27
38
  method: str = 'registered',
39
+ workers: Optional[int] = None,
28
40
  debug: bool = False,
29
41
  **kw: Any
30
- ) -> Union[PipesDict, List[mrsm.Pipe]]:
42
+ ) -> Union[PipesDict, List[mrsm.Pipe], Dict[str, mrsm.Pipe]]:
31
43
  """
32
44
  Return a dictionary or list of `meerschaum.Pipe` objects.
33
45
 
@@ -62,6 +74,10 @@ def get_pipes(
62
74
  `False` : `{connector_keys: {metric_key: {location_key: Pipe}}}`
63
75
  `True` : `[Pipe]`
64
76
 
77
+ as_tags_dict: bool, default False
78
+ If `True`, return a dictionary mapping tags to pipes.
79
+ Pipes with multiple tags will be repeated.
80
+
65
81
  method: str, default 'registered'
66
82
  Available options: `['registered', 'explicit', 'all']`
67
83
  If `'registered'` (default), create pipes based on registered keys in the connector's pipes table
@@ -71,6 +87,11 @@ def get_pipes(
71
87
  If `'all'`, create pipes from predefined metrics and locations. Required `connector_keys`.
72
88
  **NOTE:** Method `'all'` is not implemented!
73
89
 
90
+ workers: Optional[int], default None
91
+ If provided (and `as_tags_dict` is `True`), set the number of workers for the pool
92
+ to fetch tags.
93
+ Only takes effect if the instance connector supports multi-threading
94
+
74
95
  **kw: Any:
75
96
  Keyword arguments to pass to the `meerschaum.Pipe` constructor.
76
97
 
@@ -80,6 +101,7 @@ def get_pipes(
80
101
  A dictionary of dictionaries and `meerschaum.Pipe` objects
81
102
  in the connector, metric, location hierarchy.
82
103
  If `as_list` is `True`, return a list of `meerschaum.Pipe` objects.
104
+ If `as_tags_dict` is `True`, return a dictionary mapping tags to pipes.
83
105
 
84
106
  Examples
85
107
  --------
@@ -100,14 +122,17 @@ def get_pipes(
100
122
  >>> pipes['sql:main']['weather'][None]
101
123
  >>> ### Return a list instead:
102
124
  >>> get_pipes(as_list=True)
103
- [sql_main_weather]
104
- >>>
125
+ [Pipe('sql:main', 'weather')]
126
+ >>> get_pipes(as_tags_dict=True)
127
+ {'gvl': Pipe('sql:main', 'weather')}
105
128
  ```
106
129
  """
107
130
 
108
131
  from meerschaum.config import get_config
109
132
  from meerschaum.utils.warnings import error
110
133
  from meerschaum.utils.misc import filter_keywords
134
+ from meerschaum.utils.pool import get_pool
135
+ from collections import defaultdict
111
136
 
112
137
  if connector_keys is None:
113
138
  connector_keys = []
@@ -159,6 +184,7 @@ def get_pipes(
159
184
  location_keys = location_keys,
160
185
  tags = tags,
161
186
  params = params,
187
+ workers = workers,
162
188
  debug = debug
163
189
  )
164
190
  if result is None:
@@ -182,17 +208,32 @@ def get_pipes(
182
208
  **filter_keywords(Pipe, **kw)
183
209
  )
184
210
 
185
- if not as_list:
211
+ if not as_list and not as_tags_dict:
186
212
  return pipes
213
+
187
214
  from meerschaum.utils.misc import flatten_pipes_dict
188
- return flatten_pipes_dict(pipes)
215
+ pipes_list = flatten_pipes_dict(pipes)
216
+ if as_list:
217
+ return pipes_list
218
+
219
+ pool = get_pool(workers=(workers if connector.IS_THREAD_SAFE else 1))
220
+ def gather_pipe_tags(pipe: mrsm.Pipe) -> Tuple[mrsm.Pipe, List[str]]:
221
+ return pipe, (pipe.tags or [])
222
+
223
+ tags_pipes = defaultdict(lambda: [])
224
+ pipes_tags = dict(pool.map(gather_pipe_tags, pipes_list))
225
+ for pipe, tags in pipes_tags.items():
226
+ for tag in (tags or []):
227
+ tags_pipes[tag].append(pipe)
228
+
229
+ return dict(tags_pipes)
189
230
 
190
231
 
191
232
  def fetch_pipes_keys(
192
- method: str,
193
- connector: 'meerschaum.connectors.Connector',
194
- **kw: Any
195
- ) -> List[Tuple[str, str, str]]:
233
+ method: str,
234
+ connector: 'mrsm.connectors.InstanceConnector',
235
+ **kw: Any
236
+ ) -> List[Tuple[str, str, str]]:
196
237
  """
197
238
  Fetch keys for pipes according to a method.
198
239
 
@@ -201,7 +242,7 @@ def fetch_pipes_keys(
201
242
  method: str
202
243
  The method by which to fetch the keys. See `get_pipes()` above.
203
244
 
204
- connector: meerschaum.connectors.Connector
245
+ connector: meerschaum.connectors.InstanceConnector
205
246
  The connector to use to fetch the keys.
206
247
  Must be of type `meerschaum.connectors.sql.SQLConnector.SQLConnector`
207
248
  or `meerschaum.connectors.api.APIConnector.APIConnector`.
@@ -234,14 +275,14 @@ def fetch_pipes_keys(
234
275
  from meerschaum.utils.warnings import error
235
276
 
236
277
  def _registered(
237
- connector_keys: Optional[List[str]] = None,
238
- metric_keys: Optional[List[str]] = None,
239
- location_keys: Optional[List[str]] = None,
240
- tags: Optional[List[str]] = None,
241
- params: Optional[Dict[str, Any]] = None,
242
- debug: bool = False,
243
- **kw
244
- ) -> List[Tuple[str, str, str]]:
278
+ connector_keys: Optional[List[str]] = None,
279
+ metric_keys: Optional[List[str]] = None,
280
+ location_keys: Optional[List[str]] = None,
281
+ tags: Optional[List[str]] = None,
282
+ params: Optional[Dict[str, Any]] = None,
283
+ debug: bool = False,
284
+ **kw
285
+ ) -> List[Tuple[str, str, str]]:
245
286
  """
246
287
  Get keys from the pipes table or the API directly.
247
288
  Builds query or URL based on provided keys and parameters.
@@ -269,14 +310,14 @@ def fetch_pipes_keys(
269
310
  )
270
311
 
271
312
  def _explicit(
272
- connector_keys: Optional[List[str]] = None,
273
- metric_keys: Optional[List[str]] = None,
274
- location_keys: Optional[List[str]] = None,
275
- params: Optional[Dict[str, Any]] = None,
276
- tags: Optional[List[str]] = None,
277
- debug: bool = False,
278
- **kw
279
- ) -> List[Tuple[str, str, str]]:
313
+ connector_keys: Optional[List[str]] = None,
314
+ metric_keys: Optional[List[str]] = None,
315
+ location_keys: Optional[List[str]] = None,
316
+ params: Optional[Dict[str, Any]] = None,
317
+ tags: Optional[List[str]] = None,
318
+ debug: bool = False,
319
+ **kw
320
+ ) -> List[Tuple[str, str, str]]:
280
321
  """
281
322
  Explicitly build Pipes based on provided keys.
282
323
  Raises an error if `connector_keys` or `metric_keys` is empty,
@@ -24,7 +24,7 @@ from meerschaum.utils.typing import (
24
24
  is_success_tuple, Tuple,
25
25
  )
26
26
  from meerschaum.config import get_config
27
- from meerschaum.config.static import STATIC_CONFIG
27
+ from meerschaum._internal.static import STATIC_CONFIG
28
28
  from meerschaum.config._paths import (
29
29
  DAEMON_RESOURCES_PATH, LOGS_RESOURCES_PATH, DAEMON_ERROR_LOG_PATH,
30
30
  )
@@ -338,6 +338,7 @@ class Daemon:
338
338
  result = False, str(e)
339
339
  finally:
340
340
  _results[self.daemon_id] = result
341
+ self.properties['result'] = result
341
342
 
342
343
  if keep_daemon_output:
343
344
  self._capture_process_timestamp('ended')
@@ -7,7 +7,15 @@ Manage Daemons via the `Daemon` class.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
- import os, pathlib, shutil, json, datetime, threading, shlex
10
+
11
+ import os
12
+ import pathlib
13
+ import shutil
14
+ import json
15
+ import datetime
16
+ import threading
17
+ import shlex
18
+
11
19
  from meerschaum.utils.typing import SuccessTuple, List, Optional, Callable, Any, Dict
12
20
  from meerschaum.config._paths import DAEMON_RESOURCES_PATH
13
21
  from meerschaum.utils.daemon.StdinFile import StdinFile
@@ -17,6 +25,26 @@ from meerschaum.utils.daemon.FileDescriptorInterceptor import FileDescriptorInte
17
25
  from meerschaum.utils.daemon._names import get_new_daemon_name
18
26
 
19
27
 
28
+ __all__ = (
29
+ 'daemon_action',
30
+ 'daemon_entry',
31
+ 'get_daemons',
32
+ 'get_daemon_ids',
33
+ 'get_running_daemons',
34
+ 'get_stopped_daemons',
35
+ 'get_paused_daemons',
36
+ 'get_filtered_daemons',
37
+ 'get_new_daemon_name',
38
+ 'run_daemon',
39
+ 'running_in_daemon',
40
+ 'Daemon',
41
+ 'StdinFile',
42
+ 'RotatingFile',
43
+ 'FileDescriptorInterceptor',
44
+ 'DAEMON_RESOURCES_PATH',
45
+ )
46
+
47
+
20
48
  def daemon_entry(sysargs: Optional[List[str]] = None) -> SuccessTuple:
21
49
  """Parse sysargs and execute a Meerschaum action as a daemon.
22
50
 
@@ -280,6 +308,6 @@ def running_in_daemon() -> bool:
280
308
  """
281
309
  Return whether the current thread is running in a Daemon context.
282
310
  """
283
- from meerschaum.config.static import STATIC_CONFIG
311
+ from meerschaum._internal.static import STATIC_CONFIG
284
312
  daemon_env_var = STATIC_CONFIG['environment']['daemon_id']
285
313
  return daemon_env_var in os.environ