meerschaum 2.2.6__py3-none-any.whl → 2.3.0.dev1__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 (61) hide show
  1. meerschaum/__init__.py +4 -1
  2. meerschaum/__main__.py +10 -5
  3. meerschaum/_internal/arguments/_parser.py +44 -15
  4. meerschaum/_internal/entry.py +35 -14
  5. meerschaum/_internal/shell/Shell.py +155 -53
  6. meerschaum/_internal/shell/updates.py +175 -0
  7. meerschaum/actions/api.py +12 -12
  8. meerschaum/actions/attach.py +95 -0
  9. meerschaum/actions/delete.py +35 -26
  10. meerschaum/actions/register.py +19 -5
  11. meerschaum/actions/show.py +119 -148
  12. meerschaum/actions/start.py +85 -75
  13. meerschaum/actions/stop.py +68 -39
  14. meerschaum/actions/sync.py +3 -3
  15. meerschaum/actions/upgrade.py +28 -36
  16. meerschaum/api/_events.py +18 -1
  17. meerschaum/api/_oauth2.py +2 -0
  18. meerschaum/api/_websockets.py +2 -2
  19. meerschaum/api/dash/jobs.py +5 -2
  20. meerschaum/api/routes/__init__.py +1 -0
  21. meerschaum/api/routes/_actions.py +122 -44
  22. meerschaum/api/routes/_jobs.py +340 -0
  23. meerschaum/api/routes/_pipes.py +25 -25
  24. meerschaum/config/_default.py +1 -0
  25. meerschaum/config/_formatting.py +1 -0
  26. meerschaum/config/_paths.py +5 -0
  27. meerschaum/config/_shell.py +84 -67
  28. meerschaum/config/_version.py +1 -1
  29. meerschaum/config/static/__init__.py +9 -0
  30. meerschaum/connectors/__init__.py +9 -11
  31. meerschaum/connectors/api/APIConnector.py +18 -1
  32. meerschaum/connectors/api/_actions.py +60 -71
  33. meerschaum/connectors/api/_jobs.py +260 -0
  34. meerschaum/connectors/api/_misc.py +1 -1
  35. meerschaum/connectors/api/_request.py +13 -9
  36. meerschaum/connectors/parse.py +23 -7
  37. meerschaum/core/Pipe/_sync.py +3 -0
  38. meerschaum/plugins/__init__.py +89 -5
  39. meerschaum/utils/daemon/Daemon.py +333 -149
  40. meerschaum/utils/daemon/FileDescriptorInterceptor.py +19 -10
  41. meerschaum/utils/daemon/RotatingFile.py +18 -7
  42. meerschaum/utils/daemon/StdinFile.py +110 -0
  43. meerschaum/utils/daemon/__init__.py +40 -27
  44. meerschaum/utils/formatting/__init__.py +83 -37
  45. meerschaum/utils/formatting/_jobs.py +118 -51
  46. meerschaum/utils/formatting/_shell.py +6 -0
  47. meerschaum/utils/jobs/_Job.py +684 -0
  48. meerschaum/utils/jobs/__init__.py +245 -0
  49. meerschaum/utils/misc.py +18 -17
  50. meerschaum/utils/packages/__init__.py +21 -15
  51. meerschaum/utils/packages/_packages.py +2 -2
  52. meerschaum/utils/prompt.py +20 -7
  53. meerschaum/utils/schedule.py +21 -15
  54. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/METADATA +9 -9
  55. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/RECORD +61 -54
  56. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/WHEEL +1 -1
  57. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/LICENSE +0 -0
  58. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/NOTICE +0 -0
  59. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/entry_points.txt +0 -0
  60. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/top_level.txt +0 -0
  61. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/zip-safe +0 -0
@@ -0,0 +1,245 @@
1
+ #! /usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # vim:fenc=utf-8
4
+
5
+ """
6
+ Higher-level utilities for managing `meerschaum.utils.daemon.Daemon`.
7
+ """
8
+
9
+ import pathlib
10
+
11
+ import meerschaum as mrsm
12
+ from meerschaum.utils.jobs._Job import Job, StopMonitoringLogs
13
+ from meerschaum.utils.typing import Dict, Optional, List, Callable, Any
14
+
15
+ __all__ = (
16
+ 'Job',
17
+ 'get_jobs',
18
+ 'get_filtered_jobs',
19
+ 'get_restart_jobs',
20
+ 'get_running_jobs',
21
+ 'get_stopped_jobs',
22
+ 'get_paused_jobs',
23
+ 'get_restart_jobs',
24
+ 'check_restart_jobs',
25
+ 'start_check_jobs_thread',
26
+ 'stop_check_jobs_thread',
27
+ )
28
+
29
+
30
+ def get_jobs(
31
+ executor_keys: Optional[str] = None,
32
+ debug: bool = False,
33
+ ) -> Dict[str, Job]:
34
+ """
35
+ Return a dictionary of the existing jobs.
36
+
37
+ Parameters
38
+ ----------
39
+ executor_keys: Optional[str], default None
40
+ If provided, return remote jobs on the given API instance.
41
+ Otherwise return local jobs.
42
+
43
+ Returns
44
+ -------
45
+ A dictionary mapping job names to jobs.
46
+ """
47
+ from meerschaum.connectors.parse import parse_connector_keys
48
+ if executor_keys == 'local':
49
+ executor_keys = None
50
+
51
+ if executor_keys is not None:
52
+ try:
53
+ _ = parse_connector_keys(executor_keys, construct=False)
54
+ conn = mrsm.get_connector(executor_keys)
55
+ return conn.get_jobs(debug=debug)
56
+ except Exception:
57
+ return {}
58
+
59
+ from meerschaum.utils.daemon import get_daemons
60
+ daemons = get_daemons()
61
+ return {
62
+ daemon.daemon_id: Job(name=daemon.daemon_id)
63
+ for daemon in daemons
64
+ }
65
+
66
+
67
+ def get_filtered_jobs(
68
+ executor_keys: Optional[str] = None,
69
+ filter_list: Optional[List[str]] = None,
70
+ warn: bool = False,
71
+ debug: bool = False,
72
+ ) -> Dict[str, Job]:
73
+ """
74
+ Return a list of jobs filtered by the user.
75
+ """
76
+ from meerschaum.utils.warnings import warn as _warn
77
+ jobs = get_jobs(executor_keys, debug=debug)
78
+
79
+ if not filter_list:
80
+ return {
81
+ name: job
82
+ for name, job in jobs.items()
83
+ if not job.hidden
84
+ }
85
+
86
+ jobs_to_return = {}
87
+ for name in filter_list:
88
+ job = jobs.get(name, None)
89
+ if job is None:
90
+ if warn:
91
+ _warn(
92
+ f"Job '{name}' does not exist.",
93
+ stack=False,
94
+ )
95
+ continue
96
+ jobs_to_return[name] = job
97
+
98
+ return jobs_to_return
99
+
100
+
101
+ def get_restart_jobs(
102
+ executor_keys: Optional[str] = None,
103
+ jobs: Optional[Dict[str, Job]] = None,
104
+ debug: bool = False,
105
+ ) -> Dict[str, Job]:
106
+ """
107
+ Return jobs which were created with `--restart` or `--loop`.
108
+ """
109
+ if jobs is None:
110
+ jobs = get_jobs(executor_keys, debug=debug)
111
+
112
+ return {
113
+ name: job
114
+ for name, job in jobs.items()
115
+ if job.restart
116
+ }
117
+
118
+
119
+ def get_running_jobs(
120
+ executor_keys: Optional[str] = None,
121
+ jobs: Optional[Dict[str, Job]] = None,
122
+ debug: bool = False,
123
+ ) -> Dict[str, Job]:
124
+ """
125
+ Return a dictionary of running jobs.
126
+ """
127
+ if jobs is None:
128
+ jobs = get_jobs(executor_keys, debug=debug)
129
+
130
+ return {
131
+ name: job
132
+ for name, job in jobs.items()
133
+ if job.status == 'running'
134
+ }
135
+
136
+
137
+ def get_paused_jobs(
138
+ executor_keys: Optional[str] = None,
139
+ jobs: Optional[Dict[str, Job]] = None,
140
+ debug: bool = False,
141
+ ) -> Dict[str, Job]:
142
+ """
143
+ Return a dictionary of paused jobs.
144
+ """
145
+ if jobs is None:
146
+ jobs = get_jobs(executor_keys, debug=debug)
147
+
148
+ return {
149
+ name: job
150
+ for name, job in jobs.items()
151
+ if job.status == 'paused'
152
+ }
153
+
154
+
155
+ def get_stopped_jobs(
156
+ executor_keys: Optional[str] = None,
157
+ jobs: Optional[Dict[str, Job]] = None,
158
+ debug: bool = False,
159
+ ) -> Dict[str, Job]:
160
+ """
161
+ Return a dictionary of stopped jobs.
162
+ """
163
+ if jobs is None:
164
+ jobs = get_jobs(executor_keys, debug=debug)
165
+
166
+ return {
167
+ name: job
168
+ for name, job in jobs.items()
169
+ if job.status == 'stopped'
170
+ }
171
+
172
+
173
+ def check_restart_jobs(
174
+ executor_keys: Optional[str] = None,
175
+ silent: bool = False,
176
+ ) -> None:
177
+ """
178
+ Restart any stopped jobs which were created with `--restart`.
179
+
180
+ Parameters
181
+ ----------
182
+ executor_keys: Optional[str], default None
183
+ If provided, check jobs on the given remote API instance.
184
+ Otherwise check local jobs.
185
+
186
+ silent: bool, default False
187
+ If `True`, do not print the restart success message.
188
+ """
189
+ jobs = get_jobs(executor_keys)
190
+ for name, job in jobs.items():
191
+ success, msg = job.check_restart()
192
+ if not silent:
193
+ mrsm.pprint((success, msg))
194
+
195
+
196
+ def _check_restart_jobs_against_lock(*args, **kwargs):
197
+ from meerschaum.config.paths import CHECK_JOBS_LOCK_PATH
198
+ fasteners = mrsm.attempt_import('fasteners')
199
+ lock = fasteners.InterProcessLock(CHECK_JOBS_LOCK_PATH)
200
+ with lock:
201
+ check_restart_jobs(*args, **kwargs)
202
+
203
+
204
+ _check_loop_stop_thread = None
205
+ def start_check_jobs_thread():
206
+ """
207
+ Start a thread to regularly monitor jobs.
208
+ """
209
+ import atexit
210
+ from functools import partial
211
+ from meerschaum.utils.threading import RepeatTimer
212
+ from meerschaum.config.static import STATIC_CONFIG
213
+
214
+ global _check_loop_stop_thread
215
+ sleep_seconds = STATIC_CONFIG['jobs']['check_restart_seconds']
216
+
217
+ _check_loop_stop_thread = RepeatTimer(
218
+ sleep_seconds,
219
+ partial(
220
+ _check_restart_jobs_against_lock,
221
+ silent=True,
222
+ )
223
+ )
224
+ _check_loop_stop_thread.daemon = False
225
+ atexit.register(stop_check_jobs_thread)
226
+
227
+ _check_loop_stop_thread.start()
228
+
229
+
230
+ def stop_check_jobs_thread():
231
+ """
232
+ Stop the job monitoring thread.
233
+ """
234
+ from meerschaum.config.paths import CHECK_JOBS_LOCK_PATH
235
+ from meerschaum.utils.warnings import warn
236
+ if _check_loop_stop_thread is None:
237
+ return
238
+
239
+ _check_loop_stop_thread.cancel()
240
+
241
+ try:
242
+ if CHECK_JOBS_LOCK_PATH.exists():
243
+ CHECK_JOBS_LOCK_PATH.unlink()
244
+ except Exception as e:
245
+ warn(f"Failed to remove check jobs lock file:\n{e}")
meerschaum/utils/misc.py CHANGED
@@ -875,7 +875,7 @@ def dict_from_od(od: collections.OrderedDict) -> Dict[Any, Any]:
875
875
  _d[k] = dict_from_od(v)
876
876
  return _d
877
877
 
878
- def remove_ansi(s : str) -> str:
878
+ def remove_ansi(s: str) -> str:
879
879
  """
880
880
  Remove ANSI escape characters from a string.
881
881
 
@@ -899,10 +899,10 @@ def remove_ansi(s : str) -> str:
899
899
 
900
900
 
901
901
  def get_connector_labels(
902
- *types: str,
903
- search_term: str = '',
904
- ignore_exact_match = True,
905
- ) -> List[str]:
902
+ *types: str,
903
+ search_term: str = '',
904
+ ignore_exact_match = True,
905
+ ) -> List[str]:
906
906
  """
907
907
  Read connector labels from the configuration dictionary.
908
908
 
@@ -1063,7 +1063,7 @@ def async_wrap(func):
1063
1063
  loop = asyncio.get_event_loop()
1064
1064
  pfunc = partial(func, *args, **kwargs)
1065
1065
  return await loop.run_in_executor(executor, pfunc)
1066
- return run
1066
+ return run
1067
1067
 
1068
1068
 
1069
1069
  def debug_trace(browser: bool = True):
@@ -1077,17 +1077,17 @@ def debug_trace(browser: bool = True):
1077
1077
 
1078
1078
 
1079
1079
  def items_str(
1080
- items: List[Any],
1081
- quotes: bool = True,
1082
- quote_str: str = "'",
1083
- commas: bool = True,
1084
- comma_str: str = ',',
1085
- and_: bool = True,
1086
- and_str: str = 'and',
1087
- oxford_comma: bool = True,
1088
- spaces: bool = True,
1089
- space_str = ' ',
1090
- ) -> str:
1080
+ items: List[Any],
1081
+ quotes: bool = True,
1082
+ quote_str: str = "'",
1083
+ commas: bool = True,
1084
+ comma_str: str = ',',
1085
+ and_: bool = True,
1086
+ and_str: str = 'and',
1087
+ oxford_comma: bool = True,
1088
+ spaces: bool = True,
1089
+ space_str = ' ',
1090
+ ) -> str:
1091
1091
  """
1092
1092
  Return a formatted string if list items separated by commas.
1093
1093
 
@@ -1522,6 +1522,7 @@ def safely_extract_tar(tarf: 'file', output_dir: Union[str, 'pathlib.Path']) ->
1522
1522
 
1523
1523
  return safe_extract(tarf, output_dir)
1524
1524
 
1525
+
1525
1526
  ##################
1526
1527
  # Legacy imports #
1527
1528
  ##################
@@ -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
 
@@ -48,7 +48,7 @@ packages: Dict[str, Dict[str, str]] = {
48
48
  'prompt_toolkit' : 'prompt-toolkit>=3.0.39',
49
49
  'more_itertools' : 'more-itertools>=8.7.0',
50
50
  'daemon' : 'python-daemon>=0.2.3',
51
- 'fasteners' : 'fasteners>=0.18.0',
51
+ 'fasteners' : 'fasteners>=0.19.0',
52
52
  'psutil' : 'psutil>=5.8.0',
53
53
  'watchfiles' : 'watchfiles>=0.21.0',
54
54
  'dill' : 'dill>=0.3.3',
@@ -58,7 +58,7 @@ packages: Dict[str, Dict[str, str]] = {
58
58
  },
59
59
  'drivers': {
60
60
  'cryptography' : 'cryptography>=38.0.1',
61
- 'psycopg' : 'psycopg[binary]>=3.1.18',
61
+ 'psycopg' : 'psycopg[binary]>=3.2.1',
62
62
  'pymysql' : 'PyMySQL>=0.9.0',
63
63
  'aiomysql' : 'aiomysql>=0.0.21',
64
64
  'sqlalchemy_cockroachdb' : 'sqlalchemy-cockroachdb>=2.0.0',
@@ -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.
@@ -62,6 +66,7 @@ def prompt(
62
66
  from meerschaum.config import get_config
63
67
  from meerschaum.config.static import _static_config
64
68
  from meerschaum.utils.misc import filter_keywords
69
+ from meerschaum.utils.daemon import running_in_daemon
65
70
  noask = check_noask(noask)
66
71
  if not noask:
67
72
  prompt_toolkit = attempt_import('prompt_toolkit')
@@ -98,13 +103,21 @@ def prompt(
98
103
  question += '\n' + other_lines
99
104
  question += ' '
100
105
 
101
- answer = (
102
- prompt_toolkit.prompt(
103
- prompt_toolkit.formatted_text.ANSI(question),
104
- wrap_lines = wrap_lines,
105
- **filter_keywords(prompt_toolkit.prompt, **kw)
106
- ) if not noask else ''
107
- )
106
+ if not running_in_daemon():
107
+ answer = (
108
+ prompt_toolkit.prompt(
109
+ prompt_toolkit.formatted_text.ANSI(question),
110
+ wrap_lines = wrap_lines,
111
+ default = default_editable or '',
112
+ **filter_keywords(prompt_toolkit.prompt, **kw)
113
+ ) if not noask else ''
114
+ )
115
+ else:
116
+ print(question, end='', flush=True)
117
+ try:
118
+ answer = input()
119
+ except EOFError:
120
+ answer = ''
108
121
  if noask:
109
122
  print(question)
110
123
  if answer == '' and default is not None:
@@ -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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: meerschaum
3
- Version: 2.2.6
3
+ Version: 2.3.0.dev1
4
4
  Summary: Sync Time-Series Pipes with Meerschaum
5
5
  Home-page: https://meerschaum.io
6
6
  Author: Bennett Meares
@@ -51,7 +51,7 @@ Requires-Dist: databases >=0.4.0 ; extra == 'api'
51
51
  Requires-Dist: aiosqlite >=0.16.0 ; extra == 'api'
52
52
  Requires-Dist: asyncpg >=0.21.0 ; extra == 'api'
53
53
  Requires-Dist: cryptography >=38.0.1 ; extra == 'api'
54
- Requires-Dist: psycopg[binary] >=3.1.18 ; extra == 'api'
54
+ Requires-Dist: psycopg[binary] >=3.2.1 ; extra == 'api'
55
55
  Requires-Dist: PyMySQL >=0.9.0 ; extra == 'api'
56
56
  Requires-Dist: aiomysql >=0.0.21 ; extra == 'api'
57
57
  Requires-Dist: sqlalchemy-cockroachdb >=2.0.0 ; extra == 'api'
@@ -74,7 +74,7 @@ Requires-Dist: packaging >=21.3.0 ; extra == 'api'
74
74
  Requires-Dist: prompt-toolkit >=3.0.39 ; extra == 'api'
75
75
  Requires-Dist: more-itertools >=8.7.0 ; extra == 'api'
76
76
  Requires-Dist: python-daemon >=0.2.3 ; extra == 'api'
77
- Requires-Dist: fasteners >=0.18.0 ; extra == 'api'
77
+ Requires-Dist: fasteners >=0.19.0 ; extra == 'api'
78
78
  Requires-Dist: psutil >=5.8.0 ; extra == 'api'
79
79
  Requires-Dist: watchfiles >=0.21.0 ; extra == 'api'
80
80
  Requires-Dist: dill >=0.3.3 ; extra == 'api'
@@ -124,7 +124,7 @@ Requires-Dist: packaging >=21.3.0 ; extra == 'core'
124
124
  Requires-Dist: prompt-toolkit >=3.0.39 ; extra == 'core'
125
125
  Requires-Dist: more-itertools >=8.7.0 ; extra == 'core'
126
126
  Requires-Dist: python-daemon >=0.2.3 ; extra == 'core'
127
- Requires-Dist: fasteners >=0.18.0 ; extra == 'core'
127
+ Requires-Dist: fasteners >=0.19.0 ; extra == 'core'
128
128
  Requires-Dist: psutil >=5.8.0 ; extra == 'core'
129
129
  Requires-Dist: watchfiles >=0.21.0 ; extra == 'core'
130
130
  Requires-Dist: dill >=0.3.3 ; extra == 'core'
@@ -161,7 +161,7 @@ Requires-Dist: mkdocs-redirects >=1.0.4 ; extra == 'docs'
161
161
  Requires-Dist: jinja2 ==3.0.3 ; extra == 'docs'
162
162
  Provides-Extra: drivers
163
163
  Requires-Dist: cryptography >=38.0.1 ; extra == 'drivers'
164
- Requires-Dist: psycopg[binary] >=3.1.18 ; extra == 'drivers'
164
+ Requires-Dist: psycopg[binary] >=3.2.1 ; extra == 'drivers'
165
165
  Requires-Dist: PyMySQL >=0.9.0 ; extra == 'drivers'
166
166
  Requires-Dist: aiomysql >=0.0.21 ; extra == 'drivers'
167
167
  Requires-Dist: sqlalchemy-cockroachdb >=2.0.0 ; extra == 'drivers'
@@ -211,7 +211,7 @@ Requires-Dist: packaging >=21.3.0 ; extra == 'full'
211
211
  Requires-Dist: prompt-toolkit >=3.0.39 ; extra == 'full'
212
212
  Requires-Dist: more-itertools >=8.7.0 ; extra == 'full'
213
213
  Requires-Dist: python-daemon >=0.2.3 ; extra == 'full'
214
- Requires-Dist: fasteners >=0.18.0 ; extra == 'full'
214
+ Requires-Dist: fasteners >=0.19.0 ; extra == 'full'
215
215
  Requires-Dist: psutil >=5.8.0 ; extra == 'full'
216
216
  Requires-Dist: watchfiles >=0.21.0 ; extra == 'full'
217
217
  Requires-Dist: dill >=0.3.3 ; extra == 'full'
@@ -219,7 +219,7 @@ Requires-Dist: virtualenv >=20.1.0 ; extra == 'full'
219
219
  Requires-Dist: APScheduler >=4.0.0a5 ; extra == 'full'
220
220
  Requires-Dist: uv >=0.2.11 ; extra == 'full'
221
221
  Requires-Dist: cryptography >=38.0.1 ; extra == 'full'
222
- Requires-Dist: psycopg[binary] >=3.1.18 ; extra == 'full'
222
+ Requires-Dist: psycopg[binary] >=3.2.1 ; extra == 'full'
223
223
  Requires-Dist: PyMySQL >=0.9.0 ; extra == 'full'
224
224
  Requires-Dist: aiomysql >=0.0.21 ; extra == 'full'
225
225
  Requires-Dist: sqlalchemy-cockroachdb >=2.0.0 ; extra == 'full'
@@ -273,7 +273,7 @@ Requires-Dist: databases >=0.4.0 ; extra == 'sql'
273
273
  Requires-Dist: aiosqlite >=0.16.0 ; extra == 'sql'
274
274
  Requires-Dist: asyncpg >=0.21.0 ; extra == 'sql'
275
275
  Requires-Dist: cryptography >=38.0.1 ; extra == 'sql'
276
- Requires-Dist: psycopg[binary] >=3.1.18 ; extra == 'sql'
276
+ Requires-Dist: psycopg[binary] >=3.2.1 ; extra == 'sql'
277
277
  Requires-Dist: PyMySQL >=0.9.0 ; extra == 'sql'
278
278
  Requires-Dist: aiomysql >=0.0.21 ; extra == 'sql'
279
279
  Requires-Dist: sqlalchemy-cockroachdb >=2.0.0 ; extra == 'sql'
@@ -296,7 +296,7 @@ Requires-Dist: packaging >=21.3.0 ; extra == 'sql'
296
296
  Requires-Dist: prompt-toolkit >=3.0.39 ; extra == 'sql'
297
297
  Requires-Dist: more-itertools >=8.7.0 ; extra == 'sql'
298
298
  Requires-Dist: python-daemon >=0.2.3 ; extra == 'sql'
299
- Requires-Dist: fasteners >=0.18.0 ; extra == 'sql'
299
+ Requires-Dist: fasteners >=0.19.0 ; extra == 'sql'
300
300
  Requires-Dist: psutil >=5.8.0 ; extra == 'sql'
301
301
  Requires-Dist: watchfiles >=0.21.0 ; extra == 'sql'
302
302
  Requires-Dist: dill >=0.3.3 ; extra == 'sql'