meerschaum 2.1.2__py3-none-any.whl → 2.1.3.dev3__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 (35) hide show
  1. meerschaum/__init__.py +10 -4
  2. meerschaum/_internal/docs/index.py +32 -37
  3. meerschaum/_internal/entry.py +3 -0
  4. meerschaum/_internal/shell/Shell.py +106 -78
  5. meerschaum/_internal/shell/ShellCompleter.py +8 -4
  6. meerschaum/actions/__init__.py +2 -1
  7. meerschaum/actions/reload.py +2 -5
  8. meerschaum/actions/show.py +72 -1
  9. meerschaum/actions/sync.py +45 -17
  10. meerschaum/config/_edit.py +10 -34
  11. meerschaum/config/_formatting.py +4 -0
  12. meerschaum/config/_version.py +1 -1
  13. meerschaum/config/static/__init__.py +2 -0
  14. meerschaum/connectors/sql/_pipes.py +26 -20
  15. meerschaum/core/Pipe/_data.py +11 -11
  16. meerschaum/core/Pipe/_fetch.py +4 -4
  17. meerschaum/plugins/_Plugin.py +6 -8
  18. meerschaum/plugins/__init__.py +76 -1
  19. meerschaum/utils/__init__.py +21 -6
  20. meerschaum/utils/daemon/__init__.py +1 -1
  21. meerschaum/utils/formatting/__init__.py +4 -0
  22. meerschaum/utils/formatting/_pipes.py +2 -2
  23. meerschaum/utils/get_pipes.py +1 -1
  24. meerschaum/utils/misc.py +2 -1
  25. meerschaum/utils/packages/__init__.py +46 -7
  26. meerschaum/utils/schedule.py +1 -1
  27. meerschaum/utils/venv/__init__.py +22 -3
  28. {meerschaum-2.1.2.dist-info → meerschaum-2.1.3.dev3.dist-info}/METADATA +2 -2
  29. {meerschaum-2.1.2.dist-info → meerschaum-2.1.3.dev3.dist-info}/RECORD +35 -35
  30. {meerschaum-2.1.2.dist-info → meerschaum-2.1.3.dev3.dist-info}/LICENSE +0 -0
  31. {meerschaum-2.1.2.dist-info → meerschaum-2.1.3.dev3.dist-info}/NOTICE +0 -0
  32. {meerschaum-2.1.2.dist-info → meerschaum-2.1.3.dev3.dist-info}/WHEEL +0 -0
  33. {meerschaum-2.1.2.dist-info → meerschaum-2.1.3.dev3.dist-info}/entry_points.txt +0 -0
  34. {meerschaum-2.1.2.dist-info → meerschaum-2.1.3.dev3.dist-info}/top_level.txt +0 -0
  35. {meerschaum-2.1.2.dist-info → meerschaum-2.1.3.dev3.dist-info}/zip-safe +0 -0
@@ -7,7 +7,8 @@ This module contains functions for printing elements.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
- from meerschaum.utils.typing import SuccessTuple, Union, Sequence, Any, Optional, List, Dict
10
+ import meerschaum as mrsm
11
+ from meerschaum.utils.typing import SuccessTuple, Union, Sequence, Any, Optional, List, Dict, Tuple
11
12
 
12
13
  def show(
13
14
  action: Optional[List[str]] = None,
@@ -39,6 +40,7 @@ def show(
39
40
  'users' : _show_users,
40
41
  'jobs' : _show_jobs,
41
42
  'logs' : _show_logs,
43
+ 'tags' : _show_tags,
42
44
  }
43
45
  return choose_subaction(action, show_options, **kw)
44
46
 
@@ -735,6 +737,75 @@ def _show_environment(
735
737
  return True, "Success"
736
738
 
737
739
 
740
+ def _show_tags(
741
+ tags: Optional[List[str]] = None,
742
+ workers: Optional[int] = None,
743
+ nopretty: bool = False,
744
+ **kwargs
745
+ ) -> SuccessTuple:
746
+ """
747
+ Show the existing tags and their associated pipes.
748
+ """
749
+ import json
750
+ from collections import defaultdict
751
+ import meerschaum as mrsm
752
+ from meerschaum.utils.formatting import pipe_repr, UNICODE, ANSI
753
+ from meerschaum.utils.pool import get_pool
754
+ from meerschaum.config import get_config
755
+ rich_tree, rich_panel, rich_text, rich_console, rich_columns = (
756
+ mrsm.attempt_import('rich.tree', 'rich.panel', 'rich.text', 'rich.console', 'rich.columns')
757
+ )
758
+ panel = rich_panel.Panel.fit('Tags')
759
+ tree = rich_tree.Tree(panel)
760
+ pipes = mrsm.get_pipes(as_list=True, **kwargs)
761
+ pool = get_pool(workers=workers)
762
+ tag_prefix = get_config('formatting', 'pipes', 'unicode', 'icons', 'tag') if UNICODE else ''
763
+ tag_style = get_config('formatting', 'pipes', 'ansi', 'styles', 'tags') if ANSI else None
764
+
765
+ tags_pipes = defaultdict(lambda: [])
766
+ gather_pipe_tags = lambda pipe: (pipe, (pipe.tags or []))
767
+
768
+ pipes_tags = dict(pool.map(gather_pipe_tags, pipes))
769
+
770
+ for pipe, tags in pipes_tags.items():
771
+ for tag in tags:
772
+ tags_pipes[tag].append(pipe)
773
+
774
+ columns = []
775
+ sorted_tags = sorted([tag for tag in tags_pipes])
776
+ for tag in sorted_tags:
777
+ _pipes = tags_pipes[tag]
778
+ tag_text = (
779
+ rich_text.Text(tag_prefix)
780
+ + rich_text.Text(
781
+ tag,
782
+ style = tag_style,
783
+ )
784
+ )
785
+ pipes_texts = [
786
+ pipe_repr(pipe, as_rich_text=True)
787
+ for pipe in _pipes
788
+ ]
789
+ tag_group = rich_console.Group(*pipes_texts)
790
+ tag_panel = rich_panel.Panel(tag_group, title=tag_text, title_align='left')
791
+ columns.append(tag_panel)
792
+
793
+ if not nopretty:
794
+ mrsm.pprint(
795
+ rich_columns.Columns(
796
+ columns,
797
+ equal = True,
798
+ ),
799
+ )
800
+ else:
801
+ for tag, _pipes in tags_pipes.items():
802
+ print(tag)
803
+ for pipe in _pipes:
804
+ print(json.dumps(pipe.meta))
805
+
806
+ return True, "Success"
807
+
808
+
738
809
 
739
810
  ### NOTE: This must be the final statement of the module.
740
811
  ### Any subactions added below these lines will not
@@ -10,6 +10,7 @@ NOTE: `sync` required a SQL connection and is not intended for client use
10
10
 
11
11
  from __future__ import annotations
12
12
  from datetime import timedelta
13
+ import meerschaum as mrsm
13
14
  from meerschaum.utils.typing import SuccessTuple, Any, List, Optional, Tuple, Union
14
15
 
15
16
  def sync(
@@ -400,33 +401,60 @@ def _wrap_pipe(
400
401
  """
401
402
  Wrapper function for handling exceptions.
402
403
  """
404
+ import time
403
405
  from meerschaum.connectors import get_connector_plugin
404
406
  from meerschaum.utils.venv import Venv
407
+ from meerschaum.plugins import _pre_sync_hooks, _post_sync_hooks
408
+ from meerschaum.utils.misc import filter_keywords
409
+
410
+ sync_start = time.perf_counter()
411
+ sync_kwargs = {k: v for k, v in kw.items() if k != 'blocking'}
412
+ sync_kwargs.update({
413
+ 'blocking': (not unblock),
414
+ 'force': force,
415
+ 'debug': debug,
416
+ 'min_seconds': min_seconds,
417
+ 'workers': workers,
418
+ 'bounded': 'bounded',
419
+ 'chunk_interval': chunk_interval,
420
+ })
421
+ if not verify and not deduplicate:
422
+ sync_method = pipe.sync
423
+ elif not verify and deduplicate:
424
+ sync_method = pipe.deduplicate
425
+ else:
426
+ sync_method = pipe.verify
427
+ sync_kwargs['deduplicate'] = deduplicate
428
+
429
+ for module_name, pre_sync_hooks in _pre_sync_hooks.items():
430
+ plugin_name = module_name.split('.')[-1] if module_name.startswith('plugins.') else None
431
+ plugin = mrsm.Plugin(plugin_name) if plugin_name else None
432
+ with Venv(plugin):
433
+ for pre_sync_hook in pre_sync_hooks:
434
+ _ = pre_sync_hook(pipe, **filter_keywords(pre_sync_hook, **sync_kwargs))
435
+
405
436
  try:
406
437
  with Venv(get_connector_plugin(pipe.connector), debug=debug):
407
- if not verify and not deduplicate:
408
- sync_method = pipe.sync
409
- elif not verify and deduplicate:
410
- sync_method = pipe.deduplicate
411
- else:
412
- sync_method = pipe.verify
413
- kw['deduplicate'] = deduplicate
414
- return_tuple = sync_method(
415
- blocking = (not unblock),
416
- force = force,
417
- debug = debug,
418
- min_seconds = min_seconds,
419
- workers = workers,
420
- bounded = bounded,
421
- chunk_interval = chunk_interval,
422
- **{k: v for k, v in kw.items() if k != 'blocking'}
423
- )
438
+ return_tuple = sync_method(**sync_kwargs)
424
439
  except Exception as e:
425
440
  import traceback
426
441
  traceback.print_exception(type(e), e, e.__traceback__)
427
442
  print("Error: " + str(e))
428
443
  return_tuple = (False, f"Failed to sync {pipe} with exception:" + "\n" + str(e))
429
444
 
445
+ duration = time.perf_counter() - sync_start
446
+ sync_kwargs['duration'] = duration
447
+ for module_name, post_sync_hooks in _post_sync_hooks.items():
448
+ plugin_name = module_name.split('.')[-1] if module_name.startswith('plugins.') else None
449
+ plugin = mrsm.Plugin(plugin_name) if plugin_name else None
450
+ with Venv(plugin):
451
+ for post_sync_hook in post_sync_hooks:
452
+ _ = post_sync_hook(
453
+ pipe,
454
+ return_tuple,
455
+ **filter_keywords(post_sync_hook, **sync_kwargs)
456
+ )
457
+
430
458
  return return_tuple
431
459
 
432
460
 
@@ -6,9 +6,9 @@
6
6
  Functions for editing the configuration file
7
7
  """
8
8
 
9
- # from meerschaum.utils.debug import dprint
10
9
  from __future__ import annotations
11
10
  import sys
11
+ import pathlib
12
12
  from meerschaum.utils.typing import Optional, Any, SuccessTuple, Mapping, Dict, List, Union
13
13
 
14
14
  def edit_config(
@@ -21,7 +21,7 @@ def edit_config(
21
21
  from meerschaum.config import get_config, config
22
22
  from meerschaum.config._read_config import get_keyfile_path
23
23
  from meerschaum.config._paths import CONFIG_DIR_PATH
24
- from meerschaum.utils.packages import reload_package
24
+ from meerschaum.utils.packages import reload_meerschaum
25
25
  from meerschaum.utils.misc import edit_file
26
26
  from meerschaum.utils.debug import dprint
27
27
 
@@ -35,10 +35,7 @@ def edit_config(
35
35
  get_config(k, write_missing=True, warn=False)
36
36
  edit_file(get_keyfile_path(k, create_new=True))
37
37
 
38
- if debug:
39
- dprint("Reloading configuration...")
40
- reload_package('meerschaum', debug=debug, **kw)
41
-
38
+ reload_meerschaum(debug=debug)
42
39
  return (True, "Success")
43
40
 
44
41
 
@@ -78,7 +75,7 @@ def write_config(
78
75
  from meerschaum.utils.debug import dprint
79
76
  from meerschaum.utils.yaml import yaml
80
77
  from meerschaum.utils.misc import filter_keywords
81
- import json, os, pathlib
78
+ import json, os
82
79
  if config_dict is None:
83
80
  from meerschaum.config import _config
84
81
  cf = _config()
@@ -162,7 +159,6 @@ def general_write_yaml_config(
162
159
  """
163
160
 
164
161
  from meerschaum.utils.debug import dprint
165
- from pathlib import Path
166
162
 
167
163
  if files is None:
168
164
  files = {}
@@ -172,7 +168,7 @@ def general_write_yaml_config(
172
168
  header = None
173
169
  if isinstance(value, tuple):
174
170
  config, header = value
175
- path = Path(fp)
171
+ path = pathlib.Path(fp)
176
172
  path.parent.mkdir(parents=True, exist_ok=True)
177
173
  path.touch(exist_ok=True)
178
174
  with open(path, 'w+') as f:
@@ -193,32 +189,12 @@ def general_write_yaml_config(
193
189
  return True
194
190
 
195
191
  def general_edit_config(
196
- action : Optional[List[str]] = None,
197
- files : Optional[Dict[str, Union[str, pathlib.Path]]] = None,
198
- default : str = None,
199
- debug : bool = False
192
+ action: Optional[List[str]] = None,
193
+ files: Optional[Dict[str, Union[str, pathlib.Path]]] = None,
194
+ default: Optional[str] = None,
195
+ debug: bool = False
200
196
  ):
201
- """Edit any config files
202
-
203
- Parameters
204
- ----------
205
- action : Optional[List[str]] :
206
- (Default value = None)
207
- files : Optional[Dict[str :
208
-
209
- Union[str :
210
-
211
- pathlib.Path]]] :
212
- (Default value = None)
213
- default : str :
214
- (Default value = None)
215
- debug : bool :
216
- (Default value = False)
217
-
218
- Returns
219
- -------
220
-
221
- """
197
+ """Prompt the user to edit any config files."""
222
198
  if default is None:
223
199
  raise Exception("Provide a default choice for which file to edit")
224
200
  import os
@@ -35,6 +35,7 @@ default_formatting_config = {
35
35
  'running' : '🟢',
36
36
  'paused' : '🟡',
37
37
  'stopped' : '🔴',
38
+ 'tag' : '🏷️',
38
39
  },
39
40
  'pipes' : {
40
41
  'unicode' : {
@@ -43,6 +44,7 @@ default_formatting_config = {
43
44
  'metric' : 'MRSM{formatting:emoji:metric} ',
44
45
  'location' : 'MRSM{formatting:emoji:location} ',
45
46
  'key' : 'MRSM{formatting:emoji:key} ',
47
+ 'tag' : 'MRSM{formatting:emoji:tag} ',
46
48
  },
47
49
  },
48
50
  'ascii' : {
@@ -51,6 +53,7 @@ default_formatting_config = {
51
53
  'metric' : '',
52
54
  'location' : '',
53
55
  'key' : '',
56
+ 'tag' : '',
54
57
  },
55
58
  },
56
59
  'ansi' : {
@@ -61,6 +64,7 @@ default_formatting_config = {
61
64
  'key' : '',
62
65
  'guide' : 'dim',
63
66
  'none' : 'black on magenta',
67
+ 'tags' : 'bold yellow underline',
64
68
  },
65
69
  },
66
70
  '__repr__' : {
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "2.1.2"
5
+ __version__ = "2.1.3.dev3"
@@ -56,6 +56,8 @@ STATIC_CONFIG: Dict[str, Any] = {
56
56
  'dep_group': 'MRSM_DEP_GROUP',
57
57
  'home': 'MRSM_HOME',
58
58
  'src': 'MRSM_SRC',
59
+ 'uid': 'MRSM_UID',
60
+ 'gid': 'MRSM_GID',
59
61
  'noask': 'MRSM_NOASK',
60
62
  'id': 'MRSM_SERVER_ID',
61
63
  'uri_regex': r'MRSM_([a-zA-Z0-9]*)_(\d*[a-zA-Z][a-zA-Z0-9-_+]*$)',
@@ -171,7 +171,7 @@ def fetch_pipes_keys(
171
171
  """
172
172
  from meerschaum.utils.debug import dprint
173
173
  from meerschaum.utils.packages import attempt_import
174
- from meerschaum.utils.misc import separate_negation_values
174
+ from meerschaum.utils.misc import separate_negation_values, flatten_list
175
175
  from meerschaum.utils.sql import OMIT_NULLSFIRST_FLAVORS, table_exists
176
176
  from meerschaum.config.static import STATIC_CONFIG
177
177
  import json
@@ -254,25 +254,31 @@ def fetch_pipes_keys(
254
254
  q = q.where(coalesce(pipes_tbl.c[c], 'None').not_in(_ex_vals)) if _ex_vals else q
255
255
 
256
256
  ### Finally, parse tags.
257
- _in_tags, _ex_tags = separate_negation_values(tags)
258
- ors = []
259
- for nt in _in_tags:
260
- ors.append(
261
- sqlalchemy.cast(
262
- pipes_tbl.c['parameters'],
263
- sqlalchemy.String,
264
- ).like(f'%"tags":%"{nt}"%')
265
- )
266
- q = q.where(sqlalchemy.and_(sqlalchemy.or_(*ors).self_group())) if ors else q
267
- ors = []
268
- for xt in _ex_tags:
269
- ors.append(
270
- sqlalchemy.cast(
271
- pipes_tbl.c['parameters'],
272
- sqlalchemy.String,
273
- ).not_like(f'%"tags":%"{xt}"%')
274
- )
275
- q = q.where(sqlalchemy.and_(sqlalchemy.or_(*ors).self_group())) if ors else q
257
+ tag_groups = [tag.split(',') for tag in tags]
258
+ in_ex_tag_groups = [separate_negation_values(tag_group) for tag_group in tag_groups]
259
+
260
+ ors, nands = [], []
261
+ for _in_tags, _ex_tags in in_ex_tag_groups:
262
+ sub_ands = []
263
+ for nt in _in_tags:
264
+ sub_ands.append(
265
+ sqlalchemy.cast(
266
+ pipes_tbl.c['parameters'],
267
+ sqlalchemy.String,
268
+ ).like(f'%"tags":%"{nt}"%')
269
+ )
270
+ ors.append(sqlalchemy.and_(*sub_ands))
271
+
272
+ for xt in _ex_tags:
273
+ nands.append(
274
+ sqlalchemy.cast(
275
+ pipes_tbl.c['parameters'],
276
+ sqlalchemy.String,
277
+ ).not_like(f'%"tags":%"{xt}"%')
278
+ )
279
+
280
+ q = q.where(sqlalchemy.and_(*nands)) if nands else q
281
+ q = q.where(sqlalchemy.or_(*ors)) if ors else q
276
282
  loc_asc = sqlalchemy.asc(pipes_tbl.c['location_key'])
277
283
  if self.flavor not in OMIT_NULLSFIRST_FLAVORS:
278
284
  loc_asc = sqlalchemy.nullsfirst(loc_asc)
@@ -8,7 +8,7 @@ Retrieve Pipes' data from instances.
8
8
 
9
9
  from __future__ import annotations
10
10
  from datetime import datetime, timedelta
11
- from meerschaum.utils.typing import Optional, Dict, Any, Union, Generator
11
+ from meerschaum.utils.typing import Optional, Dict, Any, Union, Generator, List, Tuple
12
12
  from meerschaum.config import get_config
13
13
 
14
14
  def get_data(
@@ -350,20 +350,20 @@ def get_backtrack_data(
350
350
  If begin is `None` (default), use the most recent observed datetime
351
351
  (AKA sync_time).
352
352
 
353
+ ```
354
+ E.g. begin = 02:00
355
+
356
+ Search this region. Ignore this, even if there's data.
357
+ / / / / / / / / / |
358
+ -----|----------|----------|----------|----------|----------|
359
+ 00:00 01:00 02:00 03:00 04:00 05:00
360
+
361
+ ```
362
+
353
363
  params: Optional[Dict[str, Any]], default None
354
364
  The standard Meerschaum `params` query dictionary.
355
365
 
356
366
 
357
- ```
358
- E.g. begin = 02:00
359
-
360
- Search this region. Ignore this, even if there's data.
361
- / / / / / / / / / |
362
- -----|----------|----------|----------|----------|----------|
363
- 00:00 01:00 02:00 03:00 04:00 05:00
364
-
365
- ```
366
-
367
367
  fresh: bool, default False
368
368
  If `True`, Ignore local cache and pull directly from the instance connector.
369
369
  Only comes into effect if a pipe was created with `cache=True`.
@@ -15,8 +15,8 @@ from meerschaum.utils.warnings import warn
15
15
 
16
16
  def fetch(
17
17
  self,
18
- begin: Optional[datetime.datetime, str] = '',
19
- end: Optional[datetime.datetime] = None,
18
+ begin: Union[datetime, str, None] = '',
19
+ end: Optional[datetime] = None,
20
20
  check_existing: bool = True,
21
21
  sync_chunks: bool = False,
22
22
  debug: bool = False,
@@ -27,10 +27,10 @@ def fetch(
27
27
 
28
28
  Parameters
29
29
  ----------
30
- begin: Optional[datetime.datetime, str], default '':
30
+ begin: Union[datetime, str, None], default '':
31
31
  If provided, only fetch data newer than or equal to `begin`.
32
32
 
33
- end: Optional[datetime.datetime], default None:
33
+ end: Optional[datetime], default None:
34
34
  If provided, only fetch data older than or equal to `end`.
35
35
 
36
36
  check_existing: bool, default True
@@ -283,8 +283,8 @@ class Plugin:
283
283
  import tarfile
284
284
  import re
285
285
  import ast
286
- from meerschaum.plugins import reload_plugins, sync_plugins_symlinks
287
- from meerschaum.utils.packages import attempt_import, determine_version, reload_package
286
+ from meerschaum.plugins import sync_plugins_symlinks
287
+ from meerschaum.utils.packages import attempt_import, determine_version, reload_meerschaum
288
288
  from meerschaum.utils.venv import init_venv
289
289
  from meerschaum.utils.misc import safely_extract_tar
290
290
  old_cwd = os.getcwd()
@@ -415,8 +415,7 @@ class Plugin:
415
415
  if '_module' in self.__dict__:
416
416
  del self.__dict__['_module']
417
417
  init_venv(venv=self.name, force=True, debug=debug)
418
- reload_package('meerschaum')
419
- reload_plugins([self.name], debug=debug)
418
+ reload_meerschaum(debug=debug)
420
419
 
421
420
  ### if we've already failed, return here
422
421
  if not success or abort:
@@ -493,8 +492,8 @@ class Plugin:
493
492
  """
494
493
  Remove a plugin, its virtual environment, and archive file.
495
494
  """
496
- from meerschaum.utils.packages import reload_package
497
- from meerschaum.plugins import reload_plugins, sync_plugins_symlinks
495
+ from meerschaum.utils.packages import reload_meerschaum
496
+ from meerschaum.plugins import sync_plugins_symlinks
498
497
  from meerschaum.utils.warnings import warn, info
499
498
  warnings_thrown_count: int = 0
500
499
  max_warnings: int = 3
@@ -529,8 +528,7 @@ class Plugin:
529
528
  success = warnings_thrown_count < max_warnings
530
529
  sync_plugins_symlinks(debug=debug)
531
530
  self.deactivate_venv(force=True, debug=debug)
532
- reload_package('meerschaum')
533
- reload_plugins(debug=debug)
531
+ reload_meerschaum(debug=debug)
534
532
  return success, (
535
533
  f"Successfully uninstalled plugin '{self}'." if success
536
534
  else f"Failed to uninstall plugin '{self}'."
@@ -12,8 +12,12 @@ from meerschaum.utils.threading import Lock, RLock
12
12
  from meerschaum.plugins._Plugin import Plugin
13
13
 
14
14
  _api_plugins: Dict[str, List[Callable[['fastapi.App'], Any]]] = {}
15
+ _pre_sync_hooks: Dict[str, List[Callable[[Any], Any]]] = {}
16
+ _post_sync_hooks: Dict[str, List[Callable[[Any], Any]]] = {}
15
17
  _locks = {
16
18
  '_api_plugins': RLock(),
19
+ '_pre_sync_hooks': RLock(),
20
+ '_post_sync_hooks': RLock(),
17
21
  '__path__': RLock(),
18
22
  'sys.path': RLock(),
19
23
  'internal_plugins': RLock(),
@@ -23,6 +27,7 @@ _locks = {
23
27
  __all__ = (
24
28
  "Plugin", "make_action", "api_plugin", "import_plugins",
25
29
  "reload_plugins", "get_plugins", "get_data_plugins", "add_plugin_argument",
30
+ "pre_sync_hook", "post_sync_hook",
26
31
  )
27
32
  __pdoc__ = {
28
33
  'venvs': False, 'data': False, 'stack': False, 'plugins': False,
@@ -81,6 +86,76 @@ def make_action(
81
86
  return function
82
87
 
83
88
 
89
+ def pre_sync_hook(
90
+ function: Callable[[Any], Any],
91
+ ) -> Callable[[Any], Any]:
92
+ """
93
+ Register a function as a sync hook to be executed right before sync.
94
+
95
+ Parameters
96
+ ----------
97
+ function: Callable[[Any], Any]
98
+ The function to execute right before a sync.
99
+
100
+ Returns
101
+ -------
102
+ Another function (this is a decorator function).
103
+
104
+ Examples
105
+ --------
106
+ >>> from meerschaum.plugins import pre_sync_hook
107
+ >>>
108
+ >>> @pre_sync_hook
109
+ ... def log_sync(pipe, **kwargs):
110
+ ... print(f"About to sync {pipe} with kwargs:\n{kwargs}.")
111
+ >>>
112
+ """
113
+ with _locks['_pre_sync_hooks']:
114
+ try:
115
+ if function.__module__ not in _pre_sync_hooks:
116
+ _pre_sync_hooks[function.__module__] = []
117
+ _pre_sync_hooks[function.__module__].append(function)
118
+ except Exception as e:
119
+ from meerschaum.utils.warnings import warn
120
+ warn(e)
121
+ return function
122
+
123
+
124
+ def post_sync_hook(
125
+ function: Callable[[Any], Any],
126
+ ) -> Callable[[Any], Any]:
127
+ """
128
+ Register a function as a sync hook to be executed upon completion of a sync.
129
+
130
+ Parameters
131
+ ----------
132
+ function: Callable[[Any], Any]
133
+ The function to execute upon completion of a sync.
134
+
135
+ Returns
136
+ -------
137
+ Another function (this is a decorator function).
138
+
139
+ Examples
140
+ --------
141
+ >>> from meerschaum.plugins import post_sync_hook
142
+ >>>
143
+ >>> @post_sync_hook
144
+ ... def log_sync(pipe, success_tuple, duration=None, **kwargs):
145
+ ... print(f"It took {round(duration, 2)} seconds to sync {pipe}.")
146
+ >>>
147
+ """
148
+ with _locks['_post_sync_hooks']:
149
+ try:
150
+ if function.__module__ not in _post_sync_hooks:
151
+ _post_sync_hooks[function.__module__] = []
152
+ _post_sync_hooks[function.__module__].append(function)
153
+ except Exception as e:
154
+ from meerschaum.utils.warnings import warn
155
+ warn(e)
156
+ return function
157
+
158
+
84
159
  def api_plugin(function: Callable[[Any], Any]) -> Callable[[Any], Any]:
85
160
  """
86
161
  Execute the function when initializing the Meerschaum API module.
@@ -106,7 +181,7 @@ def api_plugin(function: Callable[[Any], Any]) -> Callable[[Any], Any]:
106
181
  >>> def initialize_plugin(app):
107
182
  ... @app.get('/my/new/path')
108
183
  ... def new_path():
109
- ... return {'message' : 'It works!'}
184
+ ... return {'message': 'It works!'}
110
185
  >>>
111
186
  """
112
187
  with _locks['_api_plugins']:
@@ -8,10 +8,25 @@ These include tools from primary utilities (get_pipes)
8
8
  to miscellaneous helper functions.
9
9
  """
10
10
 
11
- ### import_children will be depreciated in a future release.
12
- ### Explicit is better than implicit (mostly).
13
-
14
- # from meerschaum.utils.packages import import_children
15
- # import_children()
16
-
11
+ __all__ = (
12
+ 'daemon',
13
+ 'dataframe',
14
+ 'debug',
15
+ 'dtypes',
16
+ 'formatting',
17
+ 'interactive',
18
+ 'misc',
19
+ 'networking',
20
+ 'packages',
21
+ 'pool',
22
+ 'process',
23
+ 'prompt',
24
+ 'schedule',
25
+ 'sql',
26
+ 'threading',
27
+ 'typing',
28
+ 'venv',
29
+ 'warnings',
30
+ 'yaml',
31
+ )
17
32
  from meerschaum.utils.get_pipes import get_pipes
@@ -3,7 +3,7 @@
3
3
  # vim:fenc=utf-8
4
4
 
5
5
  """
6
- Daemonize processes via daemoniker.
6
+ Manage Daemons via the `Daemon` class.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
@@ -19,6 +19,7 @@ from meerschaum.utils.formatting._pipes import (
19
19
  format_pipe_success_tuple,
20
20
  print_pipes_results,
21
21
  extract_stats_from_message,
22
+ pipe_repr,
22
23
  )
23
24
  from meerschaum.utils.threading import Lock, RLock
24
25
 
@@ -39,6 +40,9 @@ __all__ = sorted([
39
40
  'highlight_pipes',
40
41
  'pprint_pipes',
41
42
  'make_header',
43
+ 'pipe_repr',
44
+ 'print_pipes_results',
45
+ 'extract_stats_from_message',
42
46
  ])
43
47
  __pdoc__ = {}
44
48
  _locks = {
@@ -276,8 +276,8 @@ def pprint_pipe_columns(
276
276
 
277
277
 
278
278
  def pipe_repr(
279
- pipe: 'meerschaum.Pipe',
280
- as_rich_text: bool=False,
279
+ pipe: mrsm.Pipe,
280
+ as_rich_text: bool = False,
281
281
  ansi: Optional[bool] = None,
282
282
  ) -> Union[str, 'rich.text.Text']:
283
283
  """
@@ -18,7 +18,7 @@ def get_pipes(
18
18
  connector_keys: Union[str, List[str], None] = None,
19
19
  metric_keys: Union[str, List[str], None] = None,
20
20
  location_keys: Union[str, List[str], None] = None,
21
- tags: Optional[List[str], None] = None,
21
+ tags: Optional[List[str]] = None,
22
22
  params: Optional[Dict[str, Any]] = None,
23
23
  mrsm_instance: Union[str, InstanceConnector, None] = None,
24
24
  instance: Union[str, InstanceConnector, None] = None,