meerschaum 3.0.0rc4__py3-none-any.whl → 3.0.0rc7__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 (117) hide show
  1. meerschaum/_internal/arguments/_parser.py +14 -2
  2. meerschaum/_internal/cli/__init__.py +6 -0
  3. meerschaum/_internal/cli/daemons.py +103 -0
  4. meerschaum/_internal/cli/entry.py +220 -0
  5. meerschaum/_internal/cli/workers.py +434 -0
  6. meerschaum/_internal/docs/index.py +1 -2
  7. meerschaum/_internal/entry.py +44 -8
  8. meerschaum/_internal/shell/Shell.py +113 -19
  9. meerschaum/_internal/shell/__init__.py +4 -1
  10. meerschaum/_internal/static.py +3 -1
  11. meerschaum/_internal/term/TermPageHandler.py +1 -2
  12. meerschaum/_internal/term/__init__.py +40 -6
  13. meerschaum/_internal/term/tools.py +33 -8
  14. meerschaum/actions/__init__.py +6 -4
  15. meerschaum/actions/api.py +39 -11
  16. meerschaum/actions/attach.py +1 -0
  17. meerschaum/actions/delete.py +4 -2
  18. meerschaum/actions/edit.py +27 -8
  19. meerschaum/actions/login.py +8 -8
  20. meerschaum/actions/register.py +13 -7
  21. meerschaum/actions/reload.py +22 -5
  22. meerschaum/actions/restart.py +14 -0
  23. meerschaum/actions/show.py +69 -4
  24. meerschaum/actions/start.py +135 -14
  25. meerschaum/actions/stop.py +36 -3
  26. meerschaum/actions/sync.py +6 -1
  27. meerschaum/api/__init__.py +35 -13
  28. meerschaum/api/_events.py +2 -2
  29. meerschaum/api/_oauth2.py +47 -4
  30. meerschaum/api/dash/callbacks/dashboard.py +29 -0
  31. meerschaum/api/dash/callbacks/jobs.py +3 -2
  32. meerschaum/api/dash/callbacks/login.py +10 -1
  33. meerschaum/api/dash/callbacks/register.py +9 -2
  34. meerschaum/api/dash/pages/login.py +2 -2
  35. meerschaum/api/dash/pipes.py +72 -36
  36. meerschaum/api/dash/webterm.py +14 -6
  37. meerschaum/api/models/_pipes.py +7 -1
  38. meerschaum/api/resources/static/js/terminado.js +3 -0
  39. meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
  40. meerschaum/api/resources/templates/termpage.html +1 -0
  41. meerschaum/api/routes/_jobs.py +23 -11
  42. meerschaum/api/routes/_login.py +73 -5
  43. meerschaum/api/routes/_pipes.py +6 -4
  44. meerschaum/api/routes/_webterm.py +3 -3
  45. meerschaum/config/__init__.py +60 -13
  46. meerschaum/config/_default.py +89 -61
  47. meerschaum/config/_edit.py +10 -8
  48. meerschaum/config/_formatting.py +2 -0
  49. meerschaum/config/_patch.py +4 -2
  50. meerschaum/config/_paths.py +127 -12
  51. meerschaum/config/_read_config.py +20 -10
  52. meerschaum/config/_version.py +1 -1
  53. meerschaum/config/environment.py +262 -0
  54. meerschaum/config/stack/__init__.py +7 -5
  55. meerschaum/connectors/_Connector.py +1 -2
  56. meerschaum/connectors/__init__.py +37 -2
  57. meerschaum/connectors/api/_APIConnector.py +1 -1
  58. meerschaum/connectors/api/_jobs.py +11 -0
  59. meerschaum/connectors/api/_pipes.py +7 -1
  60. meerschaum/connectors/instance/_plugins.py +9 -1
  61. meerschaum/connectors/instance/_tokens.py +20 -3
  62. meerschaum/connectors/instance/_users.py +8 -1
  63. meerschaum/connectors/parse.py +1 -1
  64. meerschaum/connectors/sql/_create_engine.py +3 -0
  65. meerschaum/connectors/sql/_pipes.py +93 -79
  66. meerschaum/connectors/sql/_users.py +8 -1
  67. meerschaum/connectors/valkey/_ValkeyConnector.py +3 -3
  68. meerschaum/connectors/valkey/_pipes.py +7 -5
  69. meerschaum/core/Pipe/__init__.py +45 -71
  70. meerschaum/core/Pipe/_attributes.py +66 -90
  71. meerschaum/core/Pipe/_cache.py +555 -0
  72. meerschaum/core/Pipe/_clear.py +0 -11
  73. meerschaum/core/Pipe/_data.py +0 -50
  74. meerschaum/core/Pipe/_deduplicate.py +0 -13
  75. meerschaum/core/Pipe/_delete.py +12 -21
  76. meerschaum/core/Pipe/_drop.py +11 -23
  77. meerschaum/core/Pipe/_dtypes.py +1 -1
  78. meerschaum/core/Pipe/_index.py +8 -14
  79. meerschaum/core/Pipe/_sync.py +12 -18
  80. meerschaum/core/Plugin/_Plugin.py +7 -1
  81. meerschaum/core/Token/_Token.py +1 -1
  82. meerschaum/core/User/_User.py +1 -2
  83. meerschaum/jobs/_Executor.py +88 -4
  84. meerschaum/jobs/_Job.py +135 -35
  85. meerschaum/jobs/systemd.py +7 -2
  86. meerschaum/plugins/__init__.py +277 -81
  87. meerschaum/utils/daemon/Daemon.py +195 -41
  88. meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
  89. meerschaum/utils/daemon/RotatingFile.py +63 -36
  90. meerschaum/utils/daemon/StdinFile.py +53 -13
  91. meerschaum/utils/daemon/__init__.py +18 -5
  92. meerschaum/utils/daemon/_names.py +6 -3
  93. meerschaum/utils/debug.py +34 -4
  94. meerschaum/utils/dtypes/__init__.py +5 -1
  95. meerschaum/utils/formatting/__init__.py +4 -1
  96. meerschaum/utils/formatting/_jobs.py +1 -1
  97. meerschaum/utils/formatting/_pipes.py +47 -46
  98. meerschaum/utils/formatting/_shell.py +16 -6
  99. meerschaum/utils/misc.py +18 -38
  100. meerschaum/utils/packages/__init__.py +15 -13
  101. meerschaum/utils/packages/_packages.py +1 -0
  102. meerschaum/utils/pipes.py +33 -5
  103. meerschaum/utils/process.py +1 -1
  104. meerschaum/utils/prompt.py +171 -144
  105. meerschaum/utils/sql.py +12 -2
  106. meerschaum/utils/threading.py +42 -0
  107. meerschaum/utils/venv/__init__.py +2 -0
  108. meerschaum/utils/warnings.py +19 -13
  109. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/METADATA +3 -1
  110. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/RECORD +116 -110
  111. meerschaum/config/_environment.py +0 -145
  112. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/WHEEL +0 -0
  113. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/entry_points.txt +0 -0
  114. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/licenses/LICENSE +0 -0
  115. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/licenses/NOTICE +0 -0
  116. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/top_level.txt +0 -0
  117. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/zip-safe +0 -0
@@ -13,6 +13,7 @@ import os
13
13
  import selectors
14
14
  import traceback
15
15
 
16
+ import meerschaum as mrsm
16
17
  from meerschaum.utils.typing import Optional, Union
17
18
  from meerschaum.utils.warnings import warn
18
19
 
@@ -25,6 +26,8 @@ class StdinFile(io.TextIOBase):
25
26
  self,
26
27
  file_path: Union[pathlib.Path, str],
27
28
  lock_file_path: Optional[pathlib.Path] = None,
29
+ decode: bool = True,
30
+ refresh_seconds: Union[int, float, None] = None,
28
31
  ):
29
32
  if isinstance(file_path, str):
30
33
  file_path = pathlib.Path(file_path)
@@ -38,6 +41,13 @@ class StdinFile(io.TextIOBase):
38
41
  self._file_handler = None
39
42
  self._fd = None
40
43
  self.sel = selectors.DefaultSelector()
44
+ self.decode = decode
45
+ self._write_fp = None
46
+ self._refresh_seconds = refresh_seconds
47
+
48
+ @property
49
+ def encoding(self):
50
+ return 'utf-8'
41
51
 
42
52
  @property
43
53
  def file_handler(self):
@@ -47,11 +57,9 @@ class StdinFile(io.TextIOBase):
47
57
  if self._file_handler is not None:
48
58
  return self._file_handler
49
59
 
50
- if self.file_path.exists():
51
- self.file_path.unlink()
52
-
53
- self.file_path.parent.mkdir(parents=True, exist_ok=True)
54
- os.mkfifo(self.file_path.as_posix(), mode=0o600)
60
+ if not self.file_path.exists():
61
+ self.file_path.parent.mkdir(parents=True, exist_ok=True)
62
+ os.mkfifo(self.file_path.as_posix(), mode=0o600)
55
63
 
56
64
  self._fd = os.open(self.file_path, os.O_RDONLY | os.O_NONBLOCK)
57
65
  self._file_handler = os.fdopen(self._fd, 'rb', buffering=0)
@@ -59,11 +67,19 @@ class StdinFile(io.TextIOBase):
59
67
  return self._file_handler
60
68
 
61
69
  def write(self, data):
70
+ if self._write_fp is None:
71
+ self.file_path.parent.mkdir(parents=True, exist_ok=True)
72
+ if not self.file_path.exists():
73
+ os.mkfifo(self.file_path.as_posix(), mode=0o600)
74
+ self._write_fp = open(self.file_path, 'wb')
75
+
62
76
  if isinstance(data, str):
63
77
  data = data.encode('utf-8')
64
-
65
- with open(self.file_path, 'wb') as f:
66
- f.write(data)
78
+ try:
79
+ self._write_fp.write(data)
80
+ self._write_fp.flush()
81
+ except BrokenPipeError:
82
+ pass
67
83
 
68
84
  def fileno(self):
69
85
  fileno = self.file_handler.fileno()
@@ -83,18 +99,19 @@ class StdinFile(io.TextIOBase):
83
99
  self.blocking_file_path.unlink()
84
100
  except Exception:
85
101
  warn(traceback.format_exc())
86
- return data.decode('utf-8')
102
+ return data.decode('utf-8') if self.decode else data
87
103
  except (OSError, EOFError):
88
104
  pass
89
105
 
90
- self.blocking_file_path.touch()
91
- time.sleep(0.1)
106
+ if not self.blocking_file_path.exists():
107
+ self.blocking_file_path.touch()
108
+ time.sleep(self.refresh_seconds)
92
109
 
93
110
  def readline(self, size=-1):
94
- line = ''
111
+ line = '' if self.decode else b''
95
112
  while True:
96
113
  data = self.read(1)
97
- if not data or data == '\n':
114
+ if not data or ((data == '\n') if self.decode else (data == b'\n')):
98
115
  break
99
116
  line += data
100
117
 
@@ -111,11 +128,34 @@ class StdinFile(io.TextIOBase):
111
128
  self._file_handler = None
112
129
  self._fd = None
113
130
 
131
+ if self._write_fp is not None:
132
+ try:
133
+ self._write_fp.close()
134
+ except BrokenPipeError:
135
+ pass
136
+ self._write_fp = None
137
+
138
+ try:
139
+ if self.blocking_file_path.exists():
140
+ self.blocking_file_path.unlink()
141
+ except Exception:
142
+ pass
114
143
  super().close()
115
144
 
116
145
  def is_open(self):
117
146
  return self._file_handler is not None
118
147
 
148
+ def isatty(self) -> bool:
149
+ return False
150
+
151
+ @property
152
+ def refresh_seconds(self) -> Union[int, float]:
153
+ """
154
+ How many seconds between checking for blocking functions.
155
+ """
156
+ if not self._refresh_seconds:
157
+ self._refresh_seconds = mrsm.get_config('system', 'cli', 'refresh_seconds')
158
+ return self._refresh_seconds
119
159
 
120
160
  def __str__(self) -> str:
121
161
  return f"StdinFile('{self.file_path}')"
@@ -16,8 +16,7 @@ import datetime
16
16
  import threading
17
17
  import shlex
18
18
 
19
- from meerschaum.utils.typing import SuccessTuple, List, Optional, Callable, Any, Dict
20
- from meerschaum.config._paths import DAEMON_RESOURCES_PATH
19
+ from meerschaum.utils.typing import SuccessTuple, List, Optional, Callable, Any, Dict, Union
21
20
  from meerschaum.utils.daemon.StdinFile import StdinFile
22
21
  from meerschaum.utils.daemon.Daemon import Daemon
23
22
  from meerschaum.utils.daemon.RotatingFile import RotatingFile
@@ -41,9 +40,10 @@ __all__ = (
41
40
  'StdinFile',
42
41
  'RotatingFile',
43
42
  'FileDescriptorInterceptor',
44
- 'DAEMON_RESOURCES_PATH',
45
43
  )
46
44
 
45
+ _daemons = {}
46
+
47
47
 
48
48
  def daemon_entry(sysargs: Optional[List[str]] = None) -> SuccessTuple:
49
49
  """Parse sysargs and execute a Meerschaum action as a daemon.
@@ -211,6 +211,7 @@ def get_daemon_ids() -> List[str]:
211
211
  """
212
212
  Return the IDs of all daemons on disk.
213
213
  """
214
+ from meerschaum.config._paths import DAEMON_RESOURCES_PATH
214
215
  return [
215
216
  daemon_dir
216
217
  for daemon_dir in sorted(os.listdir(DAEMON_RESOURCES_PATH))
@@ -298,8 +299,6 @@ def get_filtered_daemons(
298
299
  if warn:
299
300
  _warn(f"Daemon '{d_id}' does not exist.", stack=False)
300
301
  continue
301
- if d.hidden:
302
- pass
303
302
  daemons.append(d)
304
303
  return daemons
305
304
 
@@ -311,3 +310,17 @@ def running_in_daemon() -> bool:
311
310
  from meerschaum._internal.static import STATIC_CONFIG
312
311
  daemon_env_var = STATIC_CONFIG['environment']['daemon_id']
313
312
  return daemon_env_var in os.environ
313
+
314
+
315
+ def get_current_daemon() -> Union[Daemon, None]:
316
+ """
317
+ If running withing a daemon context, return the corresponding `Daemon`.
318
+ Otherwise return `None`.
319
+ """
320
+ from meerschaum._internal.static import STATIC_CONFIG
321
+ daemon_env_var = STATIC_CONFIG['environment']['daemon_id']
322
+ daemon_id = os.environ.get(daemon_env_var, None)
323
+ if daemon_id is None:
324
+ return None
325
+
326
+ return _daemons.get(daemon_id, Daemon(daemon_id=daemon_id))
@@ -7,9 +7,11 @@ Generate random names for jobs.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
- import os, random
11
- from meerschaum.utils.typing import Dict, List, Tuple
12
- from meerschaum.config._paths import DAEMON_RESOURCES_PATH
10
+
11
+ import os
12
+ import random
13
+
14
+ from meerschaum.utils.typing import Dict, List
13
15
 
14
16
  _bank: Dict[str, Dict[str, List[str]]] = {
15
17
  'adjectives': {
@@ -116,6 +118,7 @@ def get_new_daemon_name() -> str:
116
118
  Generate a new random name until a unique one is found
117
119
  (up to ~6000 maximum possibilities).
118
120
  """
121
+ from meerschaum.config._paths import DAEMON_RESOURCES_PATH
119
122
  existing_names = (
120
123
  os.listdir(DAEMON_RESOURCES_PATH)
121
124
  if DAEMON_RESOURCES_PATH.exists()
meerschaum/utils/debug.py CHANGED
@@ -7,11 +7,31 @@ Functions to handle debug statements
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
+
11
+ from datetime import datetime, timezone
12
+ import meerschaum as mrsm
10
13
  from meerschaum.utils.typing import Union, Optional, List
11
14
 
15
+
16
+ _rich_text = None
17
+ def _import_rich_text_for_dprint():
18
+ """
19
+ Avoid calling `attempt_import()` on every dprint.
20
+ """
21
+ global _rich_text
22
+ if _rich_text is not None:
23
+ return _rich_text
24
+
25
+ from meerschaum.utils.packages import import_rich, attempt_import
26
+ _ = import_rich()
27
+ _rich_text = attempt_import('rich.text', lazy=False)
28
+ return _rich_text
29
+
30
+
12
31
  def dprint(
13
32
  msg: str,
14
33
  leader: bool = True,
34
+ timestamp: bool = True,
15
35
  package: bool = True,
16
36
  color: Optional[Union[str, List[str]]] = None,
17
37
  attrs: Optional[List[str]] = None,
@@ -35,6 +55,12 @@ def dprint(
35
55
  else:
36
56
  CHARSET, ANSI, colored, _color, cf = 'ascii', False, None, None, None
37
57
 
58
+ if timestamp:
59
+ from meerschaum.utils.dtypes import get_current_timestamp
60
+ now = get_current_timestamp('ms').replace(tzinfo=None)
61
+ else:
62
+ now = None
63
+
38
64
  import logging, sys, inspect
39
65
  logging.basicConfig(format='%(message)s')
40
66
  log = logging.getLogger(__name__)
@@ -46,8 +72,13 @@ def dprint(
46
72
  parent_package = parent_globals['__name__']
47
73
  msg = str(msg)
48
74
  premsg = ""
75
+
76
+ if now:
77
+ premsg = now.isoformat().split('T', maxsplit=1)[-1][:-3] + (' | ' if package else ':')
49
78
  if package:
50
- premsg = parent_package + ':' + str(parent_lineno) + '\n'
79
+ premsg = premsg + parent_package + ':' + str(parent_lineno)
80
+ if premsg:
81
+ premsg += "\n"
51
82
  if leader and cf is not None:
52
83
  try:
53
84
  debug_leader = cf['formatting']['debug'][CHARSET]['icon'] if cf is not None else ''
@@ -75,10 +106,9 @@ def dprint(
75
106
  _color = {}
76
107
  if colored is not None:
77
108
  premsg = colored(premsg, **_color)
109
+
78
110
  if _progress is not None:
79
- from meerschaum.utils.packages import import_rich, attempt_import
80
- rich = import_rich()
81
- rich_text = attempt_import('rich.text')
111
+ rich_text = _import_rich_text_for_dprint()
82
112
  text = rich_text.Text.from_ansi(premsg + msg)
83
113
  _progress.console.log(text)
84
114
  else:
@@ -1054,7 +1054,7 @@ def get_current_timestamp(
1054
1054
  return ts_val.as_unit(MRSM_PRECISION_UNITS_ABBREVIATIONS[true_precision_unit])
1055
1055
 
1056
1056
 
1057
- def dtype_is_special(type_: str) -> bool:
1057
+ def is_dtype_special(type_: str) -> bool:
1058
1058
  """
1059
1059
  Return whether a dtype should be treated as a special Meerschaum dtype.
1060
1060
  This is not the same as a Meerschaum alias.
@@ -1069,6 +1069,7 @@ def dtype_is_special(type_: str) -> bool:
1069
1069
  'geometry',
1070
1070
  'geography',
1071
1071
  'date',
1072
+ 'bool',
1072
1073
  ):
1073
1074
  return True
1074
1075
 
@@ -1081,6 +1082,9 @@ def dtype_is_special(type_: str) -> bool:
1081
1082
  if true_type.startswith('numeric'):
1082
1083
  return True
1083
1084
 
1085
+ if true_type.startswith('bool'):
1086
+ return True
1087
+
1084
1088
  if true_type.startswith('geometry'):
1085
1089
  return True
1086
1090
 
@@ -209,6 +209,8 @@ def get_console():
209
209
  try:
210
210
  console = rich_console.Console(force_terminal=True, color_system='truecolor')
211
211
  except Exception:
212
+ import traceback
213
+ traceback.print_exc()
212
214
  console = None
213
215
  return console
214
216
 
@@ -488,8 +490,9 @@ def fill_ansi(string: str, style: str = '') -> str:
488
490
  msg = Text.from_ansi(string)
489
491
  except AttributeError:
490
492
  import traceback
491
- traceback.print_stack()
493
+ traceback.print_exc()
492
494
  msg = ''
495
+
493
496
  plain_indices = []
494
497
  for left_span, right_span in iterate_chunks(msg.spans, 2, fillvalue=len(msg)):
495
498
  left = left_span.end
@@ -97,7 +97,7 @@ def pprint_jobs(
97
97
  msg = '\n'.join(line.lstrip().rstrip() for line in lines)
98
98
  success_tuple = success, msg
99
99
  success_tuple_str = (
100
- format_success_tuple(success_tuple, left_padding=1)
100
+ format_success_tuple(success_tuple, left_padding=0)
101
101
  if success_tuple is not None
102
102
  else None
103
103
  )
@@ -277,18 +277,31 @@ def pprint_pipe_columns(
277
277
 
278
278
 
279
279
  def pipe_repr(
280
- pipe: mrsm.Pipe,
280
+ pipe: Union[mrsm.Pipe, Dict[str, Any]],
281
281
  as_rich_text: bool = False,
282
282
  ansi: Optional[bool] = None,
283
283
  ) -> Union[str, 'rich.text.Text']:
284
284
  """
285
285
  Return a formatted string for representing a `meerschaum.Pipe`.
286
286
  """
287
- from meerschaum.utils.formatting import UNICODE, ANSI, CHARSET, colored, rich_text_to_str
287
+ from meerschaum.utils.formatting import ANSI, colored, rich_text_to_str
288
288
  from meerschaum.utils.packages import import_rich, attempt_import
289
- rich = import_rich()
289
+ import meerschaum as mrsm
290
+
291
+ _ = import_rich()
290
292
  Text = attempt_import('rich.text').Text
291
293
 
294
+ if isinstance(pipe, mrsm.Pipe):
295
+ connector_keys = pipe.connector_keys
296
+ metric_key = pipe.metric_key
297
+ location_key = pipe.location_key
298
+ instance_keys = pipe.instance_keys
299
+ else:
300
+ connector_keys = pipe.get('connector_keys')
301
+ metric_key = pipe.get('metric_key')
302
+ location_key = pipe.get('location_key')
303
+ instance_keys = pipe.get('instance_keys', get_config('meerschaum', 'instance'))
304
+
292
305
  styles = get_config('formatting', 'pipes', '__repr__', 'ansi', 'styles')
293
306
  if not ANSI or (ansi is False):
294
307
  styles = {k: '' for k in styles}
@@ -298,26 +311,26 @@ def pipe_repr(
298
311
  )
299
312
  text_obj = (
300
313
  Text.from_markup(_pipe_style_prefix + "Pipe(" + _pipe_style_suffix)
301
- + colored(("'" + pipe.connector_keys + "'"), style=styles['connector'], as_rich_text=True)
314
+ + colored(("'" + connector_keys + "'"), style=styles['connector'], as_rich_text=True)
302
315
  + Text.from_markup(_pipe_style_prefix + ", " + _pipe_style_suffix)
303
- + colored(("'" + pipe.metric_key + "'"), style=styles['metric'], as_rich_text=True)
316
+ + colored(("'" + metric_key + "'"), style=styles['metric'], as_rich_text=True)
304
317
  + (
305
318
  (
306
319
  colored(', ', style=styles['punctuation'], as_rich_text=True)
307
320
  + colored(
308
- ("'" + pipe.location_key + "'"),
321
+ ("'" + location_key + "'"),
309
322
  style=styles['location'], as_rich_text=True
310
323
  )
311
- ) if pipe.location_key is not None
324
+ ) if location_key is not None
312
325
  else colored('', style='', as_rich_text=True)
313
326
  ) + (
314
327
  ( ### Add the `instance=` argument.
315
328
  colored(', instance=', style=styles['punctuation'], as_rich_text=True)
316
329
  + colored(
317
- ("'" + pipe.instance_keys + "'"),
330
+ ("'" + instance_keys + "'"),
318
331
  style=styles['instance'], as_rich_text=True
319
332
  )
320
- ) if pipe.instance_keys != get_config('meerschaum', 'instance')
333
+ ) if instance_keys != get_config('meerschaum', 'instance')
321
334
  else colored('', style='', as_rich_text=True)
322
335
  )
323
336
  + Text.from_markup(_pipe_style_prefix + ")" + _pipe_style_suffix)
@@ -327,6 +340,7 @@ def pipe_repr(
327
340
  return rich_text_to_str(text_obj).replace('\n', '')
328
341
 
329
342
 
343
+
330
344
  def highlight_pipes(message: str) -> str:
331
345
  """
332
346
  Add syntax highlighting to an info message containing stringified `meerschaum.Pipe` objects.
@@ -334,48 +348,35 @@ def highlight_pipes(message: str) -> str:
334
348
  if 'Pipe(' not in message:
335
349
  return message
336
350
 
337
- from meerschaum import Pipe
351
+ from meerschaum.utils.misc import parse_arguments_str
338
352
  segments = message.split('Pipe(')
339
353
  msg = ''
340
- _d = {}
341
354
  for i, segment in enumerate(segments):
342
- comma_index = segment.find(',')
343
- paren_index = segment.find(')')
344
- single_quote_index = segment.find("'")
345
- double_quote_index = segment.find('"')
346
-
347
- has_comma = comma_index != -1
348
- has_paren = paren_index != -1
349
- has_single_quote = single_quote_index != -1
350
- has_double_quote = double_quote_index != -1
351
- has_quote = has_single_quote or has_double_quote
352
- quote_index = (
353
- min(single_quote_index, double_quote_index)
354
- if has_double_quote and has_single_quote
355
- else (single_quote_index if has_single_quote else double_quote_index)
356
- )
357
-
358
- has_pipe = (
359
- has_comma
360
- and
361
- has_paren
362
- and
363
- has_quote
364
- and not
365
- (comma_index > paren_index or quote_index > paren_index)
366
- )
355
+ if i == 0:
356
+ msg += segment
357
+ continue
367
358
 
368
- if has_pipe:
369
- code = "_d['pipe'] = Pipe(" + segment[:paren_index + 1]
370
- try:
371
- exec(code)
372
- _to_add = pipe_repr(_d['pipe']) + segment[paren_index + 1:]
373
- _ = _d.pop('pipe', None)
374
- except Exception as e:
375
- _to_add = 'Pipe(' + segment
376
- msg += _to_add
359
+ paren_index = segment.find(')')
360
+ if paren_index == -1:
361
+ msg += 'Pipe(' + segment
377
362
  continue
378
- msg += segment
363
+
364
+ pipe_args_str = segment[:paren_index]
365
+ try:
366
+ args, kwargs = parse_arguments_str(pipe_args_str)
367
+ pipe_dict = {
368
+ 'connector_keys': args[0],
369
+ 'metric_key': args[1],
370
+ }
371
+ if len(args) > 2:
372
+ pipe_dict['location_key'] = args[2]
373
+ if 'instance' in kwargs:
374
+ pipe_dict['instance_keys'] = kwargs['instance']
375
+
376
+ _to_add = pipe_repr(pipe_dict) + segment[paren_index + 1:]
377
+ except Exception:
378
+ _to_add = 'Pipe(' + segment
379
+ msg += _to_add
379
380
  return msg
380
381
 
381
382
 
@@ -6,7 +6,6 @@
6
6
  Formatting functions for the interactive shell
7
7
  """
8
8
 
9
- from re import sub
10
9
  from meerschaum.utils.threading import Lock
11
10
  _locks = {'_tried_clear_command': Lock()}
12
11
 
@@ -51,28 +50,39 @@ def clear_screen(debug: bool = False) -> bool:
51
50
  from meerschaum.utils.debug import dprint
52
51
  from meerschaum.config import get_config
53
52
  from meerschaum.utils.daemon import running_in_daemon
53
+ from meerschaum._internal.static import STATIC_CONFIG
54
54
  global _tried_clear_command
55
+ clear_string = '\033[2J'
56
+ reset_string = '\033[0m'
57
+ clear_token = STATIC_CONFIG['jobs']['clear_token']
55
58
 
56
- if running_in_daemon():
59
+ if not get_config('shell', 'clear_screen'):
57
60
  return True
58
61
 
59
- if not get_config('shell', 'clear_screen'):
62
+ if running_in_daemon():
63
+ if debug:
64
+ dprint("Skip printing clear token.")
65
+ print('\n', end='', flush=True)
66
+ return True
67
+ print(clear_token, flush=True)
60
68
  return True
61
69
 
70
+
62
71
  print("", end="", flush=True)
63
72
  if debug:
64
73
  dprint("Skipping screen clear.")
65
74
  return True
75
+
66
76
  if ANSI and platform.system() != 'Windows':
67
77
  if get_console() is not None:
68
78
  get_console().clear()
69
79
  print("", end="", flush=True)
70
80
  return True
71
- clear_string = '\033[2J'
72
- reset_string = '\033[0m'
81
+
73
82
  print(clear_string + reset_string, end="")
74
83
  print("", end="", flush=True)
75
84
  return True
85
+
76
86
  ### ANSI support is disabled, try system level instead
77
87
  if _tried_clear_command is not None:
78
88
  return os.system(_tried_clear_command) == 0
@@ -81,7 +91,7 @@ def clear_screen(debug: bool = False) -> bool:
81
91
  command = 'clear' if platform.system() != 'Windows' else 'cls'
82
92
  try:
83
93
  rc = os.system(command)
84
- except Exception as e:
94
+ except Exception:
85
95
  pass
86
96
  if rc == 0:
87
97
  with _locks['_tried_clear_command']:
meerschaum/utils/misc.py CHANGED
@@ -6,9 +6,11 @@ Miscellaneous functions go here
6
6
  """
7
7
 
8
8
  from __future__ import annotations
9
+
9
10
  import sys
10
11
  import functools
11
- from datetime import timedelta, datetime, timezone
12
+ from datetime import timedelta, datetime
13
+
12
14
  from meerschaum.utils.typing import (
13
15
  Union,
14
16
  Any,
@@ -20,13 +22,8 @@ from meerschaum.utils.typing import (
20
22
  Iterable,
21
23
  PipesDict,
22
24
  Tuple,
23
- InstanceConnector,
24
- Hashable,
25
- Generator,
26
- Iterator,
27
25
  TYPE_CHECKING,
28
26
  )
29
- import meerschaum as mrsm
30
27
  if TYPE_CHECKING:
31
28
  import collections
32
29
 
@@ -43,6 +40,9 @@ __pdoc__: Dict[str, bool] = {
43
40
  'df_is_chunk_generator': False,
44
41
  'choices_docstring': False,
45
42
  '_get_subaction_names': False,
43
+ 'is_pipe_registered': False,
44
+ 'replace_pipes_in_dict': False,
45
+ 'round_time': False,
46
46
  }
47
47
 
48
48
 
@@ -347,37 +347,6 @@ def edit_file(
347
347
  return rc == 0
348
348
 
349
349
 
350
- def is_pipe_registered(
351
- pipe: mrsm.Pipe,
352
- pipes: PipesDict,
353
- debug: bool = False
354
- ) -> bool:
355
- """
356
- Check if a Pipe is inside the pipes dictionary.
357
-
358
- Parameters
359
- ----------
360
- pipe: meerschaum.Pipe
361
- The pipe to see if it's in the dictionary.
362
-
363
- pipes: PipesDict
364
- The dictionary to search inside.
365
-
366
- debug: bool, default False
367
- Verbosity toggle.
368
-
369
- Returns
370
- -------
371
- A bool indicating whether the pipe is inside the dictionary.
372
- """
373
- from meerschaum.utils.debug import dprint
374
- ck, mk, lk = pipe.connector_keys, pipe.metric_key, pipe.location_key
375
- if debug:
376
- dprint(f'{ck}, {mk}, {lk}')
377
- dprint(f'{pipe}, {pipes}')
378
- return ck in pipes and mk in pipes[ck] and lk in pipes[ck][mk]
379
-
380
-
381
350
  def get_cols_lines(default_cols: int = 100, default_lines: int = 120) -> Tuple[int, int]:
382
351
  """
383
352
  Determine the columns and lines in the terminal.
@@ -1263,11 +1232,13 @@ def is_systemd_available() -> bool:
1263
1232
  import subprocess
1264
1233
  try:
1265
1234
  has_systemctl = subprocess.call(
1266
- ['systemctl', '-h'],
1235
+ ['systemctl', 'whoami'],
1267
1236
  stdout=subprocess.DEVNULL,
1268
1237
  stderr=subprocess.STDOUT,
1269
1238
  ) == 0
1270
1239
  except Exception:
1240
+ import traceback
1241
+ traceback.print_exc()
1271
1242
  has_systemctl = False
1272
1243
  return has_systemctl
1273
1244
 
@@ -1844,6 +1815,15 @@ def replace_pipes_in_dict(*args, **kwargs):
1844
1815
  return replace_pipes_in_dict(*args, **kwargs)
1845
1816
 
1846
1817
 
1818
+ def is_pipe_registered(*args, **kwargs):
1819
+ """
1820
+ Placeholder function to prevent breaking legacy behavior.
1821
+ See `meerschaum.utils.pipes.is_pipe_registered`.
1822
+ """
1823
+ from meerschaum.utils.pipes import is_pipe_registered
1824
+ return is_pipe_registered(*args, **kwargs)
1825
+
1826
+
1847
1827
  def round_time(*args, **kwargs):
1848
1828
  """
1849
1829
  Placeholder function to prevent breaking legacy behavior.