meerschaum 2.2.5.dev3__py3-none-any.whl → 2.2.7__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 (65) hide show
  1. meerschaum/__init__.py +4 -1
  2. meerschaum/__main__.py +10 -5
  3. meerschaum/_internal/arguments/_parser.py +13 -2
  4. meerschaum/_internal/docs/index.py +523 -26
  5. meerschaum/_internal/entry.py +13 -13
  6. meerschaum/_internal/shell/Shell.py +26 -22
  7. meerschaum/_internal/shell/updates.py +175 -0
  8. meerschaum/_internal/term/__init__.py +2 -2
  9. meerschaum/actions/bootstrap.py +13 -14
  10. meerschaum/actions/python.py +11 -8
  11. meerschaum/actions/register.py +149 -37
  12. meerschaum/actions/show.py +79 -71
  13. meerschaum/actions/stop.py +11 -11
  14. meerschaum/actions/sync.py +3 -3
  15. meerschaum/actions/upgrade.py +28 -36
  16. meerschaum/api/dash/callbacks/login.py +21 -13
  17. meerschaum/api/dash/pages/login.py +2 -2
  18. meerschaum/api/routes/_login.py +5 -5
  19. meerschaum/api/routes/_pipes.py +20 -20
  20. meerschaum/config/__init__.py +8 -1
  21. meerschaum/config/_formatting.py +1 -0
  22. meerschaum/config/_paths.py +24 -2
  23. meerschaum/config/_shell.py +78 -66
  24. meerschaum/config/_version.py +1 -1
  25. meerschaum/config/paths.py +21 -2
  26. meerschaum/config/static/__init__.py +2 -0
  27. meerschaum/connectors/Connector.py +7 -2
  28. meerschaum/connectors/__init__.py +7 -5
  29. meerschaum/connectors/api/APIConnector.py +7 -2
  30. meerschaum/connectors/api/_actions.py +23 -31
  31. meerschaum/connectors/api/_misc.py +1 -1
  32. meerschaum/connectors/api/_request.py +13 -9
  33. meerschaum/connectors/api/_uri.py +5 -5
  34. meerschaum/core/Pipe/__init__.py +7 -3
  35. meerschaum/core/Pipe/_data.py +23 -15
  36. meerschaum/core/Pipe/_deduplicate.py +1 -1
  37. meerschaum/core/Pipe/_dtypes.py +5 -0
  38. meerschaum/core/Pipe/_fetch.py +18 -16
  39. meerschaum/core/Pipe/_sync.py +23 -15
  40. meerschaum/plugins/_Plugin.py +6 -6
  41. meerschaum/plugins/__init__.py +1 -1
  42. meerschaum/utils/daemon/Daemon.py +88 -129
  43. meerschaum/utils/daemon/FileDescriptorInterceptor.py +14 -5
  44. meerschaum/utils/daemon/RotatingFile.py +23 -17
  45. meerschaum/utils/daemon/__init__.py +28 -21
  46. meerschaum/utils/dataframe.py +12 -4
  47. meerschaum/utils/debug.py +9 -15
  48. meerschaum/utils/formatting/__init__.py +92 -46
  49. meerschaum/utils/formatting/_jobs.py +47 -9
  50. meerschaum/utils/misc.py +117 -11
  51. meerschaum/utils/packages/__init__.py +28 -16
  52. meerschaum/utils/prompt.py +5 -0
  53. meerschaum/utils/schedule.py +21 -15
  54. meerschaum/utils/typing.py +1 -0
  55. meerschaum/utils/venv/__init__.py +5 -1
  56. meerschaum/utils/warnings.py +8 -1
  57. meerschaum/utils/yaml.py +2 -2
  58. {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/METADATA +1 -1
  59. {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/RECORD +65 -64
  60. {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/WHEEL +1 -1
  61. {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/LICENSE +0 -0
  62. {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/NOTICE +0 -0
  63. {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/entry_points.txt +0 -0
  64. {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/top_level.txt +0 -0
  65. {meerschaum-2.2.5.dev3.dist-info → meerschaum-2.2.7.dist-info}/zip-safe +0 -0
meerschaum/utils/misc.py CHANGED
@@ -6,6 +6,7 @@ Miscellaneous functions go here
6
6
  """
7
7
 
8
8
  from __future__ import annotations
9
+ import sys
9
10
  from datetime import timedelta, datetime, timezone
10
11
  from meerschaum.utils.typing import (
11
12
  Union,
@@ -22,8 +23,11 @@ from meerschaum.utils.typing import (
22
23
  Hashable,
23
24
  Generator,
24
25
  Iterator,
26
+ TYPE_CHECKING,
25
27
  )
26
28
  import meerschaum as mrsm
29
+ if TYPE_CHECKING:
30
+ import collections
27
31
 
28
32
  __pdoc__: Dict[str, bool] = {
29
33
  'to_pandas_dtype': False,
@@ -208,6 +212,7 @@ def parse_config_substitution(
208
212
 
209
213
  return leading_key[len(leading_key):][len():-1].split(delimeter)
210
214
 
215
+
211
216
  def edit_file(
212
217
  path: Union[pathlib.Path, str],
213
218
  default_editor: str = 'pyvim',
@@ -233,7 +238,6 @@ def edit_file(
233
238
  Returns
234
239
  -------
235
240
  A bool indicating the file was successfully edited.
236
-
237
241
  """
238
242
  import os
239
243
  from subprocess import call
@@ -254,7 +258,7 @@ def edit_file(
254
258
 
255
259
 
256
260
  def is_pipe_registered(
257
- pipe: 'meerschaum.Pipe',
261
+ pipe: mrsm.Pipe,
258
262
  pipes: PipesDict,
259
263
  debug: bool = False
260
264
  ) -> bool:
@@ -726,25 +730,53 @@ def replace_password(d: Dict[str, Any], replace_with: str = '*') -> Dict[str, An
726
730
  return _d
727
731
 
728
732
 
733
+ def filter_arguments(
734
+ func: Callable[[Any], Any],
735
+ *args: Any,
736
+ **kwargs: Any
737
+ ) -> Tuple[Tuple[Any], Dict[str, Any]]:
738
+ """
739
+ Filter out unsupported positional and keyword arguments.
740
+
741
+ Parameters
742
+ ----------
743
+ func: Callable[[Any], Any]
744
+ The function to inspect.
745
+
746
+ *args: Any
747
+ Positional arguments to filter and pass to `func`.
748
+
749
+ **kwargs
750
+ Keyword arguments to filter and pass to `func`.
751
+
752
+ Returns
753
+ -------
754
+ The `args` and `kwargs` accepted by `func`.
755
+ """
756
+ args = filter_positionals(func, *args)
757
+ kwargs = filter_keywords(func, **kwargs)
758
+ return args, kwargs
759
+
760
+
729
761
  def filter_keywords(
730
- func: Callable[[Any], Any],
731
- **kw: Any
732
- ) -> Dict[str, Any]:
762
+ func: Callable[[Any], Any],
763
+ **kw: Any
764
+ ) -> Dict[str, Any]:
733
765
  """
734
- Filter out unsupported keywords.
766
+ Filter out unsupported keyword arguments.
735
767
 
736
768
  Parameters
737
769
  ----------
738
770
  func: Callable[[Any], Any]
739
771
  The function to inspect.
740
-
772
+
741
773
  **kw: Any
742
774
  The arguments to be filtered and passed into `func`.
743
775
 
744
776
  Returns
745
777
  -------
746
778
  A dictionary of keyword arguments accepted by `func`.
747
-
779
+
748
780
  Examples
749
781
  --------
750
782
  ```python
@@ -766,6 +798,69 @@ def filter_keywords(
766
798
  return {k: v for k, v in kw.items() if k in func_params}
767
799
 
768
800
 
801
+ def filter_positionals(
802
+ func: Callable[[Any], Any],
803
+ *args: Any
804
+ ) -> Tuple[Any]:
805
+ """
806
+ Filter out unsupported positional arguments.
807
+
808
+ Parameters
809
+ ----------
810
+ func: Callable[[Any], Any]
811
+ The function to inspect.
812
+
813
+ *args: Any
814
+ The arguments to be filtered and passed into `func`.
815
+ NOTE: If the function signature expects more arguments than provided,
816
+ the missing slots will be filled with `None`.
817
+
818
+ Returns
819
+ -------
820
+ A tuple of positional arguments accepted by `func`.
821
+
822
+ Examples
823
+ --------
824
+ ```python
825
+ >>> def foo(a, b):
826
+ ... return a * b
827
+ >>> filter_positionals(foo, 2, 4, 6)
828
+ (2, 4)
829
+ >>> foo(*filter_positionals(foo, 2, 4, 6))
830
+ 8
831
+ ```
832
+
833
+ """
834
+ import inspect
835
+ from meerschaum.utils.warnings import warn
836
+ func_params = inspect.signature(func).parameters
837
+ acceptable_args: List[Any] = []
838
+
839
+ def _warn_invalids(_num_invalid):
840
+ if _num_invalid > 0:
841
+ warn(
842
+ "Too few arguments were provided. "
843
+ + f"{_num_invalid} argument"
844
+ + ('s have ' if _num_invalid != 1 else " has ")
845
+ + " been filled with `None`.",
846
+ )
847
+
848
+ num_invalid: int = 0
849
+ for i, (param, val) in enumerate(func_params.items()):
850
+ if '=' in str(val) or '*' in str(val):
851
+ _warn_invalids(num_invalid)
852
+ return tuple(acceptable_args)
853
+
854
+ try:
855
+ acceptable_args.append(args[i])
856
+ except IndexError:
857
+ acceptable_args.append(None)
858
+ num_invalid += 1
859
+
860
+ _warn_invalids(num_invalid)
861
+ return tuple(acceptable_args)
862
+
863
+
769
864
  def dict_from_od(od: collections.OrderedDict) -> Dict[Any, Any]:
770
865
  """
771
866
  Convert an ordered dict to a dict.
@@ -974,10 +1069,11 @@ def async_wrap(func):
974
1069
  def debug_trace(browser: bool = True):
975
1070
  """
976
1071
  Open a web-based debugger to trace the execution of the program.
1072
+
1073
+ This is an alias import for `meerschaum.utils.debug.debug_trace`.
977
1074
  """
978
- from meerschaum.utils.packages import attempt_import
979
- heartrate = attempt_import('heartrate')
980
- heartrate.trace(files=heartrate.files.all, browser=browser)
1075
+ from meerschaum.utils.debug import trace
1076
+ trace(browser=browser)
981
1077
 
982
1078
 
983
1079
  def items_str(
@@ -1554,3 +1650,13 @@ def _get_subaction_names(*args, **kwargs) -> Any:
1554
1650
  """
1555
1651
  from meerschaum.actions import _get_subaction_names as real_function
1556
1652
  return real_function(*args, **kwargs)
1653
+
1654
+
1655
+ _current_module = sys.modules[__name__]
1656
+ __all__ = tuple(
1657
+ name
1658
+ for name, obj in globals().items()
1659
+ if callable(obj)
1660
+ and name not in __pdoc__
1661
+ and getattr(obj, '__module__', None) == _current_module.__name__
1662
+ )
@@ -816,6 +816,7 @@ def pip_install(
816
816
  """
817
817
  from meerschaum.config._paths import VIRTENV_RESOURCES_PATH
818
818
  from meerschaum.config import get_config
819
+ from meerschaum.config.static import STATIC_CONFIG
819
820
  from meerschaum.utils.warnings import warn
820
821
  from meerschaum.utils.misc import is_android
821
822
  if args is None:
@@ -827,6 +828,11 @@ def pip_install(
827
828
  if check_wheel:
828
829
  have_wheel = venv_contains_package('wheel', venv=venv, debug=debug)
829
830
 
831
+ daemon_env_var = STATIC_CONFIG['environment']['daemon_id']
832
+ inside_daemon = daemon_env_var in os.environ
833
+ if inside_daemon:
834
+ silent = True
835
+
830
836
  _args = list(args)
831
837
  have_pip = venv_contains_package('pip', venv=None, debug=debug)
832
838
  try:
@@ -844,16 +850,16 @@ def pip_install(
844
850
  if have_pip and not have_uv_pip and _install_uv_pip and not is_android():
845
851
  if not pip_install(
846
852
  'uv',
847
- venv = None,
848
- debug = debug,
849
- _install_uv_pip = False,
850
- check_update = False,
851
- check_pypi = False,
852
- check_wheel = False,
853
- ):
853
+ venv=None,
854
+ debug=debug,
855
+ _install_uv_pip=False,
856
+ check_update=False,
857
+ check_pypi=False,
858
+ check_wheel=False,
859
+ ) and not silent:
854
860
  warn(
855
861
  f"Failed to install `uv` for virtual environment '{venv}'.",
856
- color = False,
862
+ color=False,
857
863
  )
858
864
 
859
865
  use_uv_pip = (
@@ -909,13 +915,13 @@ def pip_install(
909
915
  check_wheel = False,
910
916
  debug = debug,
911
917
  _install_uv_pip = False,
912
- ):
918
+ ) and not silent:
913
919
  warn(
914
920
  (
915
921
  "Failed to install `setuptools`, `wheel`, and `uv` for virtual "
916
922
  + f"environment '{venv}'."
917
923
  ),
918
- color = False,
924
+ color=False,
919
925
  )
920
926
 
921
927
  if requirements_file_path is not None:
@@ -975,7 +981,7 @@ def pip_install(
975
981
  if not completely_uninstall_package(
976
982
  _install_no_version,
977
983
  venv=venv, debug=debug,
978
- ):
984
+ ) and not silent:
979
985
  warn(
980
986
  f"Failed to clean up package '{_install_no_version}'.",
981
987
  )
@@ -989,9 +995,9 @@ def pip_install(
989
995
  rc = run_python_package(
990
996
  ('pip' if not use_uv_pip else 'uv'),
991
997
  _args + _packages,
992
- venv = None,
993
- env = _get_pip_os_env(color=color),
994
- debug = debug,
998
+ venv=None,
999
+ env=_get_pip_os_env(color=color),
1000
+ debug=debug,
995
1001
  )
996
1002
  if debug:
997
1003
  print(f"{rc=}")
@@ -1003,7 +1009,7 @@ def pip_install(
1003
1009
  )
1004
1010
  if not silent:
1005
1011
  print(msg)
1006
- if debug:
1012
+ if debug and not silent:
1007
1013
  print('pip ' + ('un' if _uninstall else '') + 'install returned:', success)
1008
1014
  return success
1009
1015
 
@@ -1849,10 +1855,16 @@ def _get_pip_os_env(color: bool = True):
1849
1855
  Return the environment variables context in which `pip` should be run.
1850
1856
  See PEP 668 for why we are overriding the environment.
1851
1857
  """
1852
- import os
1858
+ import os, sys, platform
1859
+ python_bin_path = pathlib.Path(sys.executable)
1853
1860
  pip_os_env = os.environ.copy()
1861
+ path_str = pip_os_env.get('PATH', '') or ''
1862
+ path_sep = ':' if platform.system() != 'Windows' else ';'
1854
1863
  pip_os_env.update({
1855
1864
  'PIP_BREAK_SYSTEM_PACKAGES': 'true',
1856
1865
  ('FORCE_COLOR' if color else 'NO_COLOR'): '1',
1857
1866
  })
1867
+ if str(python_bin_path) not in path_str:
1868
+ pip_os_env['PATH'] = str(python_bin_path.parent) + path_sep + path_str
1869
+
1858
1870
  return pip_os_env
@@ -14,6 +14,7 @@ def prompt(
14
14
  question: str,
15
15
  icon: bool = True,
16
16
  default: Union[str, Tuple[str, str], None] = None,
17
+ default_editable: Optional[str] = None,
17
18
  detect_password: bool = True,
18
19
  is_password: bool = False,
19
20
  wrap_lines: bool = True,
@@ -37,6 +38,9 @@ def prompt(
37
38
  default: Union[str, Tuple[str, str], None], default None
38
39
  If the response is '', return the default value.
39
40
 
41
+ default_editable: Optional[str], default None
42
+ If provided, auto-type this user-editable string in the prompt.
43
+
40
44
  detect_password: bool, default True
41
45
  If `True`, set the input method to a censored password box if the word `password`
42
46
  appears in the question.
@@ -102,6 +106,7 @@ def prompt(
102
106
  prompt_toolkit.prompt(
103
107
  prompt_toolkit.formatted_text.ANSI(question),
104
108
  wrap_lines = wrap_lines,
109
+ default = default_editable or '',
105
110
  **filter_keywords(prompt_toolkit.prompt, **kw)
106
111
  ) if not noask else ''
107
112
  )
@@ -7,10 +7,12 @@ Schedule processes and threads.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
- import sys
10
+ import signal
11
+ import traceback
11
12
  from datetime import datetime, timezone, timedelta
12
13
  import meerschaum as mrsm
13
14
  from meerschaum.utils.typing import Callable, Any, Optional, List, Dict
15
+ from meerschaum.utils.warnings import warn, error
14
16
 
15
17
  STARTING_KEYWORD: str = 'starting'
16
18
  INTERVAL_UNITS: List[str] = ['months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'years']
@@ -70,15 +72,15 @@ SCHEDULE_ALIASES: Dict[str, str] = {
70
72
 
71
73
  _scheduler = None
72
74
  def schedule_function(
73
- function: Callable[[Any], Any],
74
- schedule: str,
75
- *args,
76
- debug: bool = False,
77
- **kw
78
- ) -> None:
75
+ function: Callable[[Any], Any],
76
+ schedule: str,
77
+ *args,
78
+ debug: bool = False,
79
+ **kw
80
+ ) -> mrsm.SuccessTuple:
79
81
  """
80
82
  Block the process and execute the function intermittently according to the frequency.
81
- https://rocketry.readthedocs.io/en/stable/condition_syntax/index.html
83
+ https://meerschaum.io/reference/background-jobs/#-schedules
82
84
 
83
85
  Parameters
84
86
  ----------
@@ -88,10 +90,13 @@ def schedule_function(
88
90
  schedule: str
89
91
  The frequency schedule at which `function` should be executed (e.g. `'daily'`).
90
92
 
93
+ Returns
94
+ -------
95
+ A `SuccessTuple` upon exit.
91
96
  """
92
97
  import asyncio
93
- from meerschaum.utils.warnings import warn
94
98
  from meerschaum.utils.misc import filter_keywords, round_time
99
+
95
100
  global _scheduler
96
101
  kw['debug'] = debug
97
102
  kw = filter_keywords(function, **kw)
@@ -105,15 +110,16 @@ def schedule_function(
105
110
  except RuntimeError:
106
111
  loop = asyncio.new_event_loop()
107
112
 
113
+
108
114
  async def run_scheduler():
109
115
  async with _scheduler:
110
116
  job = await _scheduler.add_schedule(
111
117
  function,
112
118
  trigger,
113
- args = args,
114
- kwargs = kw,
115
- max_running_jobs = 1,
116
- conflict_policy = apscheduler.ConflictPolicy.replace,
119
+ args=args,
120
+ kwargs=kw,
121
+ max_running_jobs=1,
122
+ conflict_policy=apscheduler.ConflictPolicy.replace,
117
123
  )
118
124
  try:
119
125
  await _scheduler.run_until_stopped()
@@ -126,12 +132,13 @@ def schedule_function(
126
132
  except (KeyboardInterrupt, SystemExit) as e:
127
133
  loop.run_until_complete(_stop_scheduler())
128
134
 
135
+ return True, "Success"
136
+
129
137
 
130
138
  def parse_schedule(schedule: str, now: Optional[datetime] = None):
131
139
  """
132
140
  Parse a schedule string (e.g. 'daily') into a Trigger object.
133
141
  """
134
- from meerschaum.utils.warnings import error
135
142
  from meerschaum.utils.misc import items_str, is_int
136
143
  (
137
144
  apscheduler_triggers_cron,
@@ -279,7 +286,6 @@ def parse_start_time(schedule: str, now: Optional[datetime] = None) -> datetime:
279
286
  datetime.datetime(2024, 5, 13, 0, 30, tzinfo=datetime.timezone.utc)
280
287
  """
281
288
  from meerschaum.utils.misc import round_time
282
- from meerschaum.utils.warnings import error, warn
283
289
  dateutil_parser = mrsm.attempt_import('dateutil.parser')
284
290
  starting_parts = schedule.split(STARTING_KEYWORD)
285
291
  starting_str = ('now' if len(starting_parts) == 1 else starting_parts[-1]).strip()
@@ -14,6 +14,7 @@ try:
14
14
  Hashable,
15
15
  Generator,
16
16
  Iterator,
17
+ TYPE_CHECKING,
17
18
  )
18
19
  except Exception as e:
19
20
  import urllib.request, sys, pathlib, os
@@ -616,7 +616,11 @@ def venv_target_path(
616
616
  return site_packages_path
617
617
 
618
618
  if not inside_venv():
619
- site_path = pathlib.Path(site.getusersitepackages())
619
+ user_site_packages = site.getusersitepackages()
620
+ if user_site_packages is None:
621
+ raise EnvironmentError("Could not determine user site packages.")
622
+
623
+ site_path = pathlib.Path(user_site_packages)
620
624
  if not site_path.exists():
621
625
 
622
626
  ### Windows does not have `os.geteuid()`.
@@ -3,7 +3,7 @@
3
3
  # vim:fenc=utf-8
4
4
 
5
5
  """
6
- Handle all things warnings and errors here
6
+ Handle all things warnings and errors.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
@@ -13,6 +13,13 @@ from meerschaum.utils.debug import dprint
13
13
  import sys
14
14
  import warnings
15
15
 
16
+ __all__ = (
17
+ 'warn',
18
+ 'info',
19
+ 'error',
20
+ 'dprint',
21
+ )
22
+
16
23
  warnings.filterwarnings(
17
24
  "always",
18
25
  category = UserWarning
meerschaum/utils/yaml.py CHANGED
@@ -10,7 +10,7 @@ This is so switching between PyYAML and ruamel.yaml is smoother.
10
10
 
11
11
  from meerschaum.utils.misc import filter_keywords
12
12
  from meerschaum.utils.packages import attempt_import, all_packages, _import_module
13
- from meerschaum.utils.warnings import error, warn
13
+ from meerschaum.utils.warnings import error
14
14
  from meerschaum.utils.threading import Lock
15
15
 
16
16
  _lib = None
@@ -49,7 +49,7 @@ class yaml:
49
49
  """
50
50
  global _yaml, _lib, _dumper
51
51
  if _import_name is None:
52
- error(f"No YAML library declared.")
52
+ error("No YAML library declared.")
53
53
  with _locks['_lib']:
54
54
  try:
55
55
  _lib = _import_module(_import_name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: meerschaum
3
- Version: 2.2.5.dev3
3
+ Version: 2.2.7
4
4
  Summary: Sync Time-Series Pipes with Meerschaum
5
5
  Home-page: https://meerschaum.io
6
6
  Author: Bennett Meares