orionis 0.520.0__py3-none-any.whl → 0.522.0__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.
- orionis/console/base/scheduler.py +165 -0
- orionis/console/commands/scheduler_work.py +11 -11
- orionis/console/tasks/schedule.py +448 -110
- orionis/metadata/framework.py +1 -1
- {orionis-0.520.0.dist-info → orionis-0.522.0.dist-info}/METADATA +1 -1
- {orionis-0.520.0.dist-info → orionis-0.522.0.dist-info}/RECORD +10 -10
- {orionis-0.520.0.dist-info → orionis-0.522.0.dist-info}/WHEEL +0 -0
- {orionis-0.520.0.dist-info → orionis-0.522.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.520.0.dist-info → orionis-0.522.0.dist-info}/top_level.txt +0 -0
- {orionis-0.520.0.dist-info → orionis-0.522.0.dist-info}/zip-safe +0 -0
|
@@ -18,6 +18,171 @@ class BaseScheduler(IBaseScheduler):
|
|
|
18
18
|
# Finalize Global Scheduler at a specific time
|
|
19
19
|
FINALIZE_AT: datetime = None
|
|
20
20
|
|
|
21
|
+
def pauseAt(self, timezone: str = None) -> datetime:
|
|
22
|
+
"""
|
|
23
|
+
Retrieves the datetime at which the global scheduler should be paused.
|
|
24
|
+
|
|
25
|
+
This method returns the `PAUSE_AT` attribute as a timezone-aware datetime object.
|
|
26
|
+
If `PAUSE_AT` is not set (i.e., `None`), the method will return `None`.
|
|
27
|
+
If a timezone is provided, it converts the naive datetime to a timezone-aware
|
|
28
|
+
datetime using the specified timezone. If no timezone is provided, the naive
|
|
29
|
+
datetime is returned as is.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
timezone : str, optional
|
|
34
|
+
The name of the timezone to use for converting the naive datetime
|
|
35
|
+
to a timezone-aware datetime. For example, "UTC" or "America/New_York".
|
|
36
|
+
If not provided, the method returns the naive datetime.
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
datetime or None
|
|
41
|
+
- A timezone-aware datetime object representing the pause time of the scheduler
|
|
42
|
+
if `PAUSE_AT` is set and a valid timezone is provided.
|
|
43
|
+
- A naive datetime object representing the pause time if `PAUSE_AT` is set
|
|
44
|
+
but no timezone is provided.
|
|
45
|
+
- `None` if `PAUSE_AT` is not set.
|
|
46
|
+
|
|
47
|
+
Notes
|
|
48
|
+
-----
|
|
49
|
+
- The `PAUSE_AT` attribute is expected to be a naive datetime object.
|
|
50
|
+
- The method uses the `pytz` library to localize the naive datetime to the specified timezone.
|
|
51
|
+
- If the `PAUSE_AT` attribute is `None`, the method will return `None` without performing any conversion.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
# Retrieve the naive datetime value for the pause time
|
|
55
|
+
dt_naive = self.PAUSE_AT
|
|
56
|
+
|
|
57
|
+
# If no pause time is set, return None
|
|
58
|
+
if dt_naive is None:
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
# If no timezone is provided, return the naive datetime as is
|
|
62
|
+
if timezone is None:
|
|
63
|
+
return dt_naive
|
|
64
|
+
|
|
65
|
+
# Import the pytz library for timezone handling
|
|
66
|
+
import pytz
|
|
67
|
+
|
|
68
|
+
# Get the specified timezone using pytz
|
|
69
|
+
# This will raise an exception if the timezone string is invalid
|
|
70
|
+
tz = pytz.timezone(timezone)
|
|
71
|
+
|
|
72
|
+
# Convert the naive datetime to a timezone-aware datetime
|
|
73
|
+
# This ensures the datetime is localized to the specified timezone
|
|
74
|
+
return tz.localize(dt_naive)
|
|
75
|
+
|
|
76
|
+
def resumeAt(self, timezone: str = None) -> datetime:
|
|
77
|
+
"""
|
|
78
|
+
Retrieves the datetime at which the global scheduler should be resumed.
|
|
79
|
+
|
|
80
|
+
This method returns the `RESUME_AT` attribute as a timezone-aware datetime object.
|
|
81
|
+
If `RESUME_AT` is not set (i.e., `None`), the method will return `None`.
|
|
82
|
+
If a timezone is provided, it converts the naive datetime to a timezone-aware
|
|
83
|
+
datetime using the specified timezone. If no timezone is provided, the naive
|
|
84
|
+
datetime is returned as is.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
timezone : str, optional
|
|
89
|
+
The name of the timezone to use for converting the naive datetime
|
|
90
|
+
to a timezone-aware datetime. For example, "UTC" or "America/New_York".
|
|
91
|
+
If not provided, the method returns the naive datetime.
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
-------
|
|
95
|
+
datetime or None
|
|
96
|
+
- A timezone-aware datetime object representing the resume time of the scheduler
|
|
97
|
+
if `RESUME_AT` is set and a valid timezone is provided.
|
|
98
|
+
- A naive datetime object representing the resume time if `RESUME_AT` is set
|
|
99
|
+
but no timezone is provided.
|
|
100
|
+
- `None` if `RESUME_AT` is not set.
|
|
101
|
+
|
|
102
|
+
Notes
|
|
103
|
+
-----
|
|
104
|
+
- The `RESUME_AT` attribute is expected to be a naive datetime object.
|
|
105
|
+
- The method uses the `pytz` library to localize the naive datetime to the specified timezone.
|
|
106
|
+
- If the `RESUME_AT` attribute is `None`, the method will return `None` without performing any conversion.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
# Retrieve the naive datetime value for the resume time
|
|
110
|
+
dt_naive = self.RESUME_AT
|
|
111
|
+
|
|
112
|
+
# If no resume time is set, return None
|
|
113
|
+
if dt_naive is None:
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
# If no timezone is provided, return the naive datetime as is
|
|
117
|
+
if timezone is None:
|
|
118
|
+
return dt_naive
|
|
119
|
+
|
|
120
|
+
# Import the pytz library for timezone handling
|
|
121
|
+
import pytz
|
|
122
|
+
|
|
123
|
+
# Get the specified timezone using pytz
|
|
124
|
+
# This will raise an exception if the timezone string is invalid
|
|
125
|
+
tz = pytz.timezone(timezone)
|
|
126
|
+
|
|
127
|
+
# Convert the naive datetime to a timezone-aware datetime
|
|
128
|
+
# This ensures the datetime is localized to the specified timezone
|
|
129
|
+
return tz.localize(dt_naive)
|
|
130
|
+
|
|
131
|
+
def finalizeAt(self, timezone: str = None) -> datetime:
|
|
132
|
+
"""
|
|
133
|
+
Retrieves the datetime at which the global scheduler should be finalized.
|
|
134
|
+
|
|
135
|
+
This method returns the `FINALIZE_AT` attribute as a timezone-aware datetime object.
|
|
136
|
+
If `FINALIZE_AT` is not set (i.e., `None`), the method will return `None`.
|
|
137
|
+
If a timezone is provided, it converts the naive datetime to a timezone-aware
|
|
138
|
+
datetime using the specified timezone. If no timezone is provided, the naive
|
|
139
|
+
datetime is returned as is.
|
|
140
|
+
|
|
141
|
+
Parameters
|
|
142
|
+
----------
|
|
143
|
+
timezone : str, optional
|
|
144
|
+
The name of the timezone to use for converting the naive datetime
|
|
145
|
+
to a timezone-aware datetime. For example, "UTC" or "America/New_York".
|
|
146
|
+
If not provided, the method returns the naive datetime.
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
datetime or None
|
|
151
|
+
- A timezone-aware datetime object representing the finalize time of the scheduler
|
|
152
|
+
if `FINALIZE_AT` is set and a valid timezone is provided.
|
|
153
|
+
- A naive datetime object representing the finalize time if `FINALIZE_AT` is set
|
|
154
|
+
but no timezone is provided.
|
|
155
|
+
- `None` if `FINALIZE_AT` is not set.
|
|
156
|
+
|
|
157
|
+
Notes
|
|
158
|
+
-----
|
|
159
|
+
- The `FINALIZE_AT` attribute is expected to be a naive datetime object.
|
|
160
|
+
- The method uses the `pytz` library to localize the naive datetime to the specified timezone.
|
|
161
|
+
- If the `FINALIZE_AT` attribute is `None`, the method will return `None` without performing any conversion.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
# Retrieve the naive datetime value for the finalize time
|
|
165
|
+
dt_naive = self.FINALIZE_AT
|
|
166
|
+
|
|
167
|
+
# If no finalize time is set, return None
|
|
168
|
+
if dt_naive is None:
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
# If no timezone is provided, return the naive datetime as is
|
|
172
|
+
if timezone is None:
|
|
173
|
+
return dt_naive
|
|
174
|
+
|
|
175
|
+
# Import the pytz library for timezone handling
|
|
176
|
+
import pytz
|
|
177
|
+
|
|
178
|
+
# Get the specified timezone using pytz
|
|
179
|
+
# This will raise an exception if the timezone string is invalid
|
|
180
|
+
tz = pytz.timezone(timezone)
|
|
181
|
+
|
|
182
|
+
# Convert the naive datetime to a timezone-aware datetime
|
|
183
|
+
# This ensures the datetime is localized to the specified timezone
|
|
184
|
+
return tz.localize(dt_naive)
|
|
185
|
+
|
|
21
186
|
async def tasks(self, schedule: ISchedule):
|
|
22
187
|
"""
|
|
23
188
|
Defines and registers scheduled tasks for the application.
|
|
@@ -110,21 +110,21 @@ class ScheduleWorkCommand(BaseCommand):
|
|
|
110
110
|
if hasattr(scheduler, "onError") and callable(scheduler.onError):
|
|
111
111
|
schedule_service.setListener(ListeningEvent.SCHEDULER_ERROR, scheduler.onError)
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
schedule_service.pauseEverythingAt(scheduler.PAUSE_AT)
|
|
113
|
+
if hasattr(scheduler, "FINALIZE_AT") and scheduler.FINALIZE_AT is not None:
|
|
114
|
+
if not isinstance(scheduler.FINALIZE_AT, datetime):
|
|
115
|
+
raise CLIOrionisRuntimeError("FINALIZE_AT must be a datetime instance.")
|
|
116
|
+
schedule_service.shutdownEverythingAt(scheduler.finalizeAt())
|
|
118
117
|
|
|
119
118
|
if hasattr(scheduler, "RESUME_AT") and scheduler.RESUME_AT is not None:
|
|
120
119
|
if not isinstance(scheduler.RESUME_AT, datetime):
|
|
121
120
|
raise CLIOrionisRuntimeError("RESUME_AT must be a datetime instance.")
|
|
122
|
-
schedule_service.resumeEverythingAt(scheduler.
|
|
121
|
+
schedule_service.resumeEverythingAt(scheduler.resumeAt())
|
|
123
122
|
|
|
124
|
-
if
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
# Check if the scheduler has specific pause, resume, and finalize times
|
|
124
|
+
if hasattr(scheduler, "PAUSE_AT") and scheduler.PAUSE_AT is not None:
|
|
125
|
+
if not isinstance(scheduler.PAUSE_AT, datetime):
|
|
126
|
+
raise CLIOrionisRuntimeError("PAUSE_AT must be a datetime instance.")
|
|
127
|
+
schedule_service.pauseEverythingAt(scheduler.pauseAt())
|
|
128
128
|
|
|
129
129
|
# Start the scheduler worker asynchronously
|
|
130
130
|
await schedule_service.start()
|
|
@@ -137,4 +137,4 @@ class ScheduleWorkCommand(BaseCommand):
|
|
|
137
137
|
# Raise any unexpected exceptions as CLIOrionisRuntimeError
|
|
138
138
|
raise CLIOrionisRuntimeError(
|
|
139
139
|
f"An unexpected error occurred while starting the scheduler worker: {e}"
|
|
140
|
-
)
|
|
140
|
+
)
|
|
@@ -50,7 +50,6 @@ class Scheduler(ISchedule):
|
|
|
50
50
|
self,
|
|
51
51
|
reactor: IReactor,
|
|
52
52
|
app: IApplication,
|
|
53
|
-
console: IConsole,
|
|
54
53
|
rich_console: Console
|
|
55
54
|
) -> None:
|
|
56
55
|
"""
|
|
@@ -74,10 +73,7 @@ class Scheduler(ISchedule):
|
|
|
74
73
|
"""
|
|
75
74
|
|
|
76
75
|
# Store the application instance for configuration access.
|
|
77
|
-
self.__app = app
|
|
78
|
-
|
|
79
|
-
# Store the console instance for output operations.
|
|
80
|
-
self.__console = console
|
|
76
|
+
self.__app: IApplication = app
|
|
81
77
|
|
|
82
78
|
# Store the rich console instance for advanced output formatting.
|
|
83
79
|
self.__rich_console = rich_console
|
|
@@ -99,7 +95,7 @@ class Scheduler(ISchedule):
|
|
|
99
95
|
self.__logger: ILogger = self.__app.make('x-orionis.services.log.log_service')
|
|
100
96
|
|
|
101
97
|
# Store the reactor instance for command management.
|
|
102
|
-
self.__reactor = reactor
|
|
98
|
+
self.__reactor: IReactor = reactor
|
|
103
99
|
|
|
104
100
|
# Retrieve and store all available commands from the reactor.
|
|
105
101
|
self.__available_commands = self.__getCommands()
|
|
@@ -113,6 +109,9 @@ class Scheduler(ISchedule):
|
|
|
113
109
|
# Initialize the listeners dictionary to manage event listeners.
|
|
114
110
|
self.__listeners: Dict[str, callable] = {}
|
|
115
111
|
|
|
112
|
+
# Add this line to the existing __init__ method
|
|
113
|
+
self._stop_event: Optional[asyncio.Event] = None
|
|
114
|
+
|
|
116
115
|
def __getCurrentTime(
|
|
117
116
|
self
|
|
118
117
|
) -> str:
|
|
@@ -263,7 +262,7 @@ class Scheduler(ISchedule):
|
|
|
263
262
|
"""
|
|
264
263
|
|
|
265
264
|
# Prevent adding new commands while the scheduler is running
|
|
266
|
-
if self.
|
|
265
|
+
if self.isRunning():
|
|
267
266
|
raise CLIOrionisRuntimeError("Cannot add new commands while the scheduler is running.")
|
|
268
267
|
|
|
269
268
|
# Validate that the command signature is a non-empty string
|
|
@@ -278,8 +277,10 @@ class Scheduler(ISchedule):
|
|
|
278
277
|
if not self.__isAvailable(signature):
|
|
279
278
|
raise CLIOrionisValueError(f"The command '{signature}' is not available or does not exist.")
|
|
280
279
|
|
|
281
|
-
#
|
|
280
|
+
# Import Event here to avoid circular dependency issues
|
|
282
281
|
from orionis.console.tasks.event import Event
|
|
282
|
+
|
|
283
|
+
# Store the command and its arguments for scheduling
|
|
283
284
|
self.__events[signature] = Event(
|
|
284
285
|
signature=signature,
|
|
285
286
|
args=args or [],
|
|
@@ -363,6 +364,8 @@ class Scheduler(ISchedule):
|
|
|
363
364
|
|
|
364
365
|
# Check if a listener is registered for the specified event
|
|
365
366
|
if scheduler_event in self.__listeners:
|
|
367
|
+
|
|
368
|
+
# Retrieve the listener for the specified event
|
|
366
369
|
listener = self.__listeners[scheduler_event]
|
|
367
370
|
|
|
368
371
|
# Ensure the listener is callable before invoking it
|
|
@@ -372,21 +375,29 @@ class Scheduler(ISchedule):
|
|
|
372
375
|
|
|
373
376
|
# If the listener is a coroutine, schedule it as an asyncio task
|
|
374
377
|
if asyncio.iscoroutinefunction(listener):
|
|
378
|
+
|
|
379
|
+
# Try to get the running event loop
|
|
375
380
|
try:
|
|
376
|
-
# Try to get the running event loop
|
|
377
381
|
loop = asyncio.get_running_loop()
|
|
378
382
|
loop.create_task(listener(event_data, self))
|
|
383
|
+
|
|
384
|
+
# If no event loop is running, log a warning instead of creating one
|
|
379
385
|
except RuntimeError:
|
|
380
|
-
|
|
381
|
-
|
|
386
|
+
|
|
387
|
+
# Raise an error to inform the caller that the listener could not be invoked
|
|
388
|
+
raise CLIOrionisRuntimeError(
|
|
389
|
+
f"Cannot run async listener for '{scheduler_event}': no event loop running"
|
|
390
|
+
)
|
|
391
|
+
|
|
382
392
|
# Otherwise, invoke the listener directly as a regular function
|
|
383
393
|
else:
|
|
384
394
|
listener(event_data, self)
|
|
385
395
|
|
|
386
396
|
except Exception as e:
|
|
387
397
|
|
|
388
|
-
#
|
|
389
|
-
|
|
398
|
+
# Re-raise CLIOrionisRuntimeError exceptions
|
|
399
|
+
if isinstance(e, CLIOrionisRuntimeError):
|
|
400
|
+
raise e
|
|
390
401
|
|
|
391
402
|
# Raise a runtime error if listener invocation fails
|
|
392
403
|
raise CLIOrionisRuntimeError(
|
|
@@ -449,19 +460,43 @@ class Scheduler(ISchedule):
|
|
|
449
460
|
|
|
450
461
|
# Check if the listener has a method corresponding to the event type
|
|
451
462
|
if hasattr(listener, scheduler_event) and callable(getattr(listener, scheduler_event)):
|
|
463
|
+
|
|
464
|
+
# Retrieve the method from the listener
|
|
452
465
|
listener_method = getattr(listener, scheduler_event)
|
|
453
466
|
|
|
467
|
+
# Try to invoke the listener method
|
|
454
468
|
try:
|
|
469
|
+
|
|
455
470
|
# Invoke the listener method, handling both coroutine and regular functions
|
|
456
471
|
if asyncio.iscoroutinefunction(listener_method):
|
|
457
|
-
|
|
458
|
-
|
|
472
|
+
|
|
473
|
+
# Try to get the running event loop
|
|
474
|
+
try:
|
|
475
|
+
loop = asyncio.get_running_loop()
|
|
476
|
+
loop.create_task(listener_method(event_data, self))
|
|
477
|
+
|
|
478
|
+
# If no event loop is running, log a warning
|
|
479
|
+
except RuntimeError:
|
|
480
|
+
|
|
481
|
+
# Raise an error to inform the caller that the listener could not be invoked
|
|
482
|
+
raise CLIOrionisRuntimeError(
|
|
483
|
+
f"Cannot run async listener for '{scheduler_event}' on job '{event_data.job_id}': no event loop running"
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
# Call the regular listener method directly
|
|
459
487
|
else:
|
|
460
|
-
# Call the regular listener method directly
|
|
461
488
|
listener_method(event_data, self)
|
|
489
|
+
|
|
462
490
|
except Exception as e:
|
|
463
|
-
|
|
464
|
-
|
|
491
|
+
|
|
492
|
+
# Re-raise CLIOrionisRuntimeError exceptions
|
|
493
|
+
if isinstance(e, CLIOrionisRuntimeError):
|
|
494
|
+
raise e
|
|
495
|
+
|
|
496
|
+
# Raise a runtime error if listener invocation fails
|
|
497
|
+
raise CLIOrionisRuntimeError(
|
|
498
|
+
f"An error occurred while invoking the listener for event '{scheduler_event}' on job '{event_data.job_id}': {str(e)}"
|
|
499
|
+
)
|
|
465
500
|
|
|
466
501
|
def __startedListener(
|
|
467
502
|
self,
|
|
@@ -899,27 +934,36 @@ class Scheduler(ISchedule):
|
|
|
899
934
|
# Iterate through all scheduled jobs in the AsyncIOScheduler
|
|
900
935
|
for signature, event in self.__events.items():
|
|
901
936
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
937
|
+
try:
|
|
938
|
+
# Convert the event to its entity representation
|
|
939
|
+
entity = event.toEntity()
|
|
940
|
+
|
|
941
|
+
# Add the job to the internal jobs list
|
|
942
|
+
self.__jobs.append(entity)
|
|
943
|
+
|
|
944
|
+
# Create a unique key for the job based on its signature
|
|
945
|
+
def create_job_func(cmd, args_list):
|
|
946
|
+
return lambda: self.__reactor.call(cmd, args_list)
|
|
947
|
+
|
|
948
|
+
# Add the job to the scheduler with the specified trigger and parameters
|
|
949
|
+
self.__scheduler.add_job(
|
|
950
|
+
func=create_job_func(signature, list(entity.args)),
|
|
951
|
+
trigger=entity.trigger,
|
|
952
|
+
id=signature,
|
|
953
|
+
name=signature,
|
|
954
|
+
replace_existing=True
|
|
955
|
+
)
|
|
907
956
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
957
|
+
# If a listener is associated with the event, register it
|
|
958
|
+
if entity.listener:
|
|
959
|
+
self.setListener(signature, entity.listener)
|
|
911
960
|
|
|
912
|
-
|
|
913
|
-
func=create_job_func(signature, list(entity.args)),
|
|
914
|
-
trigger=entity.trigger,
|
|
915
|
-
id=signature,
|
|
916
|
-
name=signature,
|
|
917
|
-
replace_existing=True
|
|
918
|
-
)
|
|
961
|
+
except Exception as e:
|
|
919
962
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
963
|
+
# Raise a runtime error if loading the scheduled event fails
|
|
964
|
+
raise CLIOrionisRuntimeError(
|
|
965
|
+
f"Failed to load scheduled event '{signature}': {str(e)}"
|
|
966
|
+
) from e
|
|
923
967
|
|
|
924
968
|
def setListener(
|
|
925
969
|
self,
|
|
@@ -978,9 +1022,9 @@ class Scheduler(ISchedule):
|
|
|
978
1022
|
"""
|
|
979
1023
|
Schedule the scheduler to pause all operations at a specific datetime.
|
|
980
1024
|
|
|
981
|
-
This method
|
|
982
|
-
|
|
983
|
-
|
|
1025
|
+
This method schedules a job that pauses the AsyncIOScheduler at the specified datetime.
|
|
1026
|
+
The job is added to the scheduler with a 'date' trigger, ensuring it executes exactly
|
|
1027
|
+
at the given time. If a pause job already exists, it is replaced to avoid conflicts.
|
|
984
1028
|
|
|
985
1029
|
Parameters
|
|
986
1030
|
----------
|
|
@@ -997,21 +1041,43 @@ class Scheduler(ISchedule):
|
|
|
997
1041
|
Raises
|
|
998
1042
|
------
|
|
999
1043
|
ValueError
|
|
1000
|
-
If the 'at' parameter is not a valid datetime object.
|
|
1044
|
+
If the 'at' parameter is not a valid datetime object or is not in the future.
|
|
1045
|
+
CLIOrionisRuntimeError
|
|
1046
|
+
If the scheduler is not running or if an error occurs during job scheduling.
|
|
1001
1047
|
"""
|
|
1002
1048
|
|
|
1003
1049
|
# Validate that the 'at' parameter is a datetime object
|
|
1004
1050
|
if not isinstance(at, datetime):
|
|
1005
1051
|
raise ValueError("The 'at' parameter must be a datetime object.")
|
|
1006
1052
|
|
|
1007
|
-
#
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1053
|
+
# Define a function to pause the scheduler
|
|
1054
|
+
def schedule_pause():
|
|
1055
|
+
if self.isRunning():
|
|
1056
|
+
self.__scheduler.pause()
|
|
1057
|
+
|
|
1058
|
+
try:
|
|
1059
|
+
# Remove any existing pause job to avoid conflicts
|
|
1060
|
+
try:
|
|
1061
|
+
self.__scheduler.remove_job("scheduler_pause_at")
|
|
1062
|
+
except:
|
|
1063
|
+
pass # If the job doesn't exist, it's fine to proceed
|
|
1064
|
+
|
|
1065
|
+
# Add a job to the scheduler to pause it at the specified datetime
|
|
1066
|
+
self.__scheduler.add_job(
|
|
1067
|
+
func=schedule_pause, # Function to pause the scheduler
|
|
1068
|
+
trigger=DateTrigger(run_date=at), # Trigger type is 'date' for one-time execution
|
|
1069
|
+
id="scheduler_pause_at", # Unique job ID for pausing the scheduler
|
|
1070
|
+
name="Pause Scheduler", # Descriptive name for the job
|
|
1071
|
+
replace_existing=True # Replace any existing job with the same ID
|
|
1072
|
+
)
|
|
1073
|
+
|
|
1074
|
+
# Log the scheduled pause
|
|
1075
|
+
self.__logger.info(f"Scheduler pause scheduled for {at.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
1076
|
+
|
|
1077
|
+
except Exception as e:
|
|
1078
|
+
|
|
1079
|
+
# Handle exceptions that may occur during job scheduling
|
|
1080
|
+
raise CLIOrionisRuntimeError(f"Failed to schedule scheduler pause: {str(e)}") from e
|
|
1015
1081
|
|
|
1016
1082
|
def resumeEverythingAt(
|
|
1017
1083
|
self,
|
|
@@ -1020,9 +1086,9 @@ class Scheduler(ISchedule):
|
|
|
1020
1086
|
"""
|
|
1021
1087
|
Schedule the scheduler to resume all operations at a specific datetime.
|
|
1022
1088
|
|
|
1023
|
-
This method
|
|
1024
|
-
|
|
1025
|
-
|
|
1089
|
+
This method schedules a job that resumes the AsyncIOScheduler at the specified datetime.
|
|
1090
|
+
The job is added to the scheduler with a 'date' trigger, ensuring it executes exactly
|
|
1091
|
+
at the given time. If a resume job already exists, it is replaced to avoid conflicts.
|
|
1026
1092
|
|
|
1027
1093
|
Parameters
|
|
1028
1094
|
----------
|
|
@@ -1039,38 +1105,65 @@ class Scheduler(ISchedule):
|
|
|
1039
1105
|
Raises
|
|
1040
1106
|
------
|
|
1041
1107
|
ValueError
|
|
1042
|
-
If the 'at' parameter is not a valid datetime object.
|
|
1108
|
+
If the 'at' parameter is not a valid datetime object or is not in the future.
|
|
1109
|
+
CLIOrionisRuntimeError
|
|
1110
|
+
If the scheduler is not running or if an error occurs during job scheduling.
|
|
1043
1111
|
"""
|
|
1044
1112
|
|
|
1045
1113
|
# Validate that the 'at' parameter is a datetime object
|
|
1046
1114
|
if not isinstance(at, datetime):
|
|
1047
1115
|
raise ValueError("The 'at' parameter must be a datetime object.")
|
|
1048
1116
|
|
|
1049
|
-
#
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1117
|
+
# Define a function to resume the scheduler
|
|
1118
|
+
def schedule_resume():
|
|
1119
|
+
if self.isRunning():
|
|
1120
|
+
self.__scheduler.resume()
|
|
1121
|
+
|
|
1122
|
+
try:
|
|
1123
|
+
|
|
1124
|
+
# Remove any existing resume job to avoid conflicts
|
|
1125
|
+
try:
|
|
1126
|
+
self.__scheduler.remove_job("scheduler_resume_at")
|
|
1127
|
+
except:
|
|
1128
|
+
pass # If the job doesn't exist, it's fine to proceed
|
|
1129
|
+
|
|
1130
|
+
# Add a job to the scheduler to resume it at the specified datetime
|
|
1131
|
+
self.__scheduler.add_job(
|
|
1132
|
+
func=schedule_resume, # Function to resume the scheduler
|
|
1133
|
+
trigger=DateTrigger(run_date=at), # Trigger type is 'date' for one-time execution
|
|
1134
|
+
id="scheduler_resume_at", # Unique job ID for resuming the scheduler
|
|
1135
|
+
name="Resume Scheduler", # Descriptive name for the job
|
|
1136
|
+
replace_existing=True # Replace any existing job with the same ID
|
|
1137
|
+
)
|
|
1138
|
+
|
|
1139
|
+
# Log the scheduled resume
|
|
1140
|
+
self.__logger.info(f"Scheduler resume scheduled for {at.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
1141
|
+
|
|
1142
|
+
except Exception as e:
|
|
1143
|
+
|
|
1144
|
+
# Handle exceptions that may occur during job scheduling
|
|
1145
|
+
raise CLIOrionisRuntimeError(f"Failed to schedule scheduler resume: {str(e)}") from e
|
|
1057
1146
|
|
|
1058
1147
|
def shutdownEverythingAt(
|
|
1059
1148
|
self,
|
|
1060
|
-
at: datetime
|
|
1149
|
+
at: datetime,
|
|
1150
|
+
wait: bool = True
|
|
1061
1151
|
) -> None:
|
|
1062
1152
|
"""
|
|
1063
1153
|
Schedule the scheduler to shut down all operations at a specific datetime.
|
|
1064
1154
|
|
|
1065
|
-
This method
|
|
1066
|
-
|
|
1067
|
-
|
|
1155
|
+
This method schedules a job that shuts down the AsyncIOScheduler at the specified datetime.
|
|
1156
|
+
The job is added to the scheduler with a 'date' trigger, ensuring it executes exactly
|
|
1157
|
+
at the given time. If a shutdown job already exists, it is replaced to avoid conflicts.
|
|
1068
1158
|
|
|
1069
1159
|
Parameters
|
|
1070
1160
|
----------
|
|
1071
1161
|
at : datetime
|
|
1072
1162
|
The datetime at which the scheduler should be shut down. Must be a valid
|
|
1073
1163
|
datetime object.
|
|
1164
|
+
wait : bool, optional
|
|
1165
|
+
Whether to wait for currently running jobs to complete before shutdown.
|
|
1166
|
+
Default is True.
|
|
1074
1167
|
|
|
1075
1168
|
Returns
|
|
1076
1169
|
-------
|
|
@@ -1081,88 +1174,144 @@ class Scheduler(ISchedule):
|
|
|
1081
1174
|
Raises
|
|
1082
1175
|
------
|
|
1083
1176
|
ValueError
|
|
1084
|
-
If the 'at' parameter is not a valid datetime object
|
|
1177
|
+
If the 'at' parameter is not a valid datetime object or 'wait' is not boolean,
|
|
1178
|
+
or if the scheduled time is not in the future.
|
|
1179
|
+
CLIOrionisRuntimeError
|
|
1180
|
+
If the scheduler is not running or if an error occurs during job scheduling.
|
|
1085
1181
|
"""
|
|
1086
1182
|
|
|
1087
1183
|
# Validate that the 'at' parameter is a datetime object
|
|
1088
1184
|
if not isinstance(at, datetime):
|
|
1089
1185
|
raise ValueError("The 'at' parameter must be a datetime object.")
|
|
1090
1186
|
|
|
1091
|
-
#
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1187
|
+
# Validate that the 'wait' parameter is a boolean
|
|
1188
|
+
if not isinstance(wait, bool):
|
|
1189
|
+
raise ValueError("The 'wait' parameter must be a boolean value.")
|
|
1190
|
+
|
|
1191
|
+
# Define a function to shut down the scheduler
|
|
1192
|
+
def schedule_shutdown():
|
|
1193
|
+
if self.isRunning():
|
|
1194
|
+
self.__scheduler.shutdown(wait=wait)
|
|
1195
|
+
# Signal the stop event to break the wait in start()
|
|
1196
|
+
if self._stop_event and not self._stop_event.is_set():
|
|
1197
|
+
self._stop_event.set()
|
|
1198
|
+
|
|
1199
|
+
try:
|
|
1200
|
+
|
|
1201
|
+
# Remove any existing shutdown job to avoid conflicts
|
|
1202
|
+
try:
|
|
1203
|
+
self.__scheduler.remove_job("scheduler_shutdown_at")
|
|
1204
|
+
except:
|
|
1205
|
+
pass # If the job doesn't exist, it's fine to proceed
|
|
1206
|
+
|
|
1207
|
+
# Add a job to the scheduler to shut it down at the specified datetime
|
|
1208
|
+
self.__scheduler.add_job(
|
|
1209
|
+
func=schedule_shutdown, # Function to shut down the scheduler
|
|
1210
|
+
trigger=DateTrigger(run_date=at), # Trigger type is 'date' for one-time execution
|
|
1211
|
+
id="scheduler_shutdown_at", # Unique job ID for shutting down the scheduler
|
|
1212
|
+
name="Shutdown Scheduler", # Descriptive name for the job
|
|
1213
|
+
replace_existing=True # Replace any existing job with the same ID
|
|
1214
|
+
)
|
|
1215
|
+
|
|
1216
|
+
# Log the scheduled shutdown
|
|
1217
|
+
self.__logger.info(f"Scheduler shutdown scheduled for {at.strftime('%Y-%m-%d %H:%M:%S')} (wait={wait})")
|
|
1218
|
+
|
|
1219
|
+
except Exception as e:
|
|
1220
|
+
|
|
1221
|
+
# Handle exceptions that may occur during job scheduling
|
|
1222
|
+
raise CLIOrionisRuntimeError(f"Failed to schedule scheduler shutdown: {str(e)}") from e
|
|
1099
1223
|
|
|
1100
1224
|
async def start(self) -> None:
|
|
1101
1225
|
"""
|
|
1102
1226
|
Start the AsyncIO scheduler instance and keep it running.
|
|
1103
1227
|
|
|
1104
|
-
This method
|
|
1105
|
-
|
|
1106
|
-
|
|
1228
|
+
This method initializes and starts the AsyncIOScheduler, which integrates with the asyncio event loop
|
|
1229
|
+
to manage asynchronous job execution. It ensures that all scheduled events are loaded, listeners are
|
|
1230
|
+
subscribed, and the scheduler is started within an asyncio context. The method keeps the scheduler
|
|
1231
|
+
running until a stop signal is received, handling graceful shutdowns and interruptions.
|
|
1107
1232
|
|
|
1108
1233
|
Returns
|
|
1109
1234
|
-------
|
|
1110
1235
|
None
|
|
1111
|
-
This method does not return any value. It starts the AsyncIO scheduler
|
|
1112
|
-
|
|
1236
|
+
This method does not return any value. It starts the AsyncIO scheduler, keeps it running, and
|
|
1237
|
+
ensures proper cleanup during shutdown.
|
|
1113
1238
|
|
|
1114
|
-
|
|
1239
|
+
Raises
|
|
1240
|
+
------
|
|
1241
|
+
CLIOrionisRuntimeError
|
|
1242
|
+
If the scheduler fails to start due to missing an asyncio event loop or other runtime issues.
|
|
1243
|
+
"""
|
|
1115
1244
|
try:
|
|
1116
1245
|
|
|
1117
|
-
# Ensure
|
|
1118
|
-
asyncio.get_running_loop()
|
|
1246
|
+
# Ensure the method is called within an asyncio event loop
|
|
1247
|
+
loop = asyncio.get_running_loop()
|
|
1119
1248
|
|
|
1120
|
-
#
|
|
1249
|
+
# Create an asyncio event to manage clean shutdowns
|
|
1250
|
+
self._stop_event = asyncio.Event()
|
|
1251
|
+
|
|
1252
|
+
# Load all scheduled events into the internal jobs list
|
|
1121
1253
|
self.__loadEvents()
|
|
1122
1254
|
|
|
1123
|
-
# Subscribe to scheduler events
|
|
1255
|
+
# Subscribe to scheduler events for monitoring and handling
|
|
1124
1256
|
self.__subscribeListeners()
|
|
1125
1257
|
|
|
1126
|
-
# Start the scheduler
|
|
1127
|
-
if not self.
|
|
1258
|
+
# Start the scheduler if it is not already running
|
|
1259
|
+
if not self.isRunning():
|
|
1128
1260
|
self.__scheduler.start()
|
|
1129
1261
|
|
|
1130
|
-
#
|
|
1131
|
-
|
|
1262
|
+
# Log that the scheduler is now active and waiting for events
|
|
1263
|
+
self.__logger.info("Scheduler is now active and waiting for events...")
|
|
1132
1264
|
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1265
|
+
try:
|
|
1266
|
+
# Wait for the stop event to be set, which signals a shutdown
|
|
1267
|
+
# This avoids using a busy loop and is more efficient
|
|
1268
|
+
await self._stop_event.wait()
|
|
1136
1269
|
|
|
1137
1270
|
except (KeyboardInterrupt, asyncio.CancelledError):
|
|
1138
|
-
|
|
1271
|
+
|
|
1272
|
+
# Handle graceful shutdown when an interruption signal is received
|
|
1273
|
+
self.__logger.info("Received shutdown signal, stopping scheduler...")
|
|
1274
|
+
await self.shutdown(wait=True)
|
|
1275
|
+
|
|
1139
1276
|
except Exception as e:
|
|
1140
|
-
raise CLIOrionisRuntimeError(f"Failed to start the scheduler: {str(e)}") from e
|
|
1141
1277
|
|
|
1278
|
+
# Log and raise any unexpected exceptions during scheduler operation
|
|
1279
|
+
self.__logger.error(f"Error during scheduler operation: {str(e)}")
|
|
1280
|
+
raise CLIOrionisRuntimeError(f"Scheduler operation failed: {str(e)}") from e
|
|
1281
|
+
|
|
1282
|
+
finally:
|
|
1283
|
+
|
|
1284
|
+
# Ensure the scheduler is shut down properly, even if an error occurs
|
|
1285
|
+
if self.__scheduler.running:
|
|
1286
|
+
await self.shutdown(wait=False)
|
|
1287
|
+
|
|
1288
|
+
except RuntimeError as e:
|
|
1289
|
+
|
|
1290
|
+
# Handle the case where no asyncio event loop is running
|
|
1291
|
+
if "no running event loop" in str(e):
|
|
1292
|
+
raise CLIOrionisRuntimeError("Scheduler must be started within an asyncio event loop") from e
|
|
1293
|
+
raise CLIOrionisRuntimeError(f"Failed to start the scheduler: {str(e)}") from e
|
|
1142
1294
|
|
|
1143
1295
|
except Exception as e:
|
|
1144
1296
|
|
|
1145
|
-
#
|
|
1146
|
-
raise CLIOrionisRuntimeError(f"Failed to start the scheduler: {str(e)}")
|
|
1297
|
+
# Raise a runtime error for any other issues during startup
|
|
1298
|
+
raise CLIOrionisRuntimeError(f"Failed to start the scheduler: {str(e)}") from e
|
|
1147
1299
|
|
|
1148
|
-
async def shutdown(self, wait=True) -> None:
|
|
1300
|
+
async def shutdown(self, wait: bool = True) -> None:
|
|
1149
1301
|
"""
|
|
1150
1302
|
Shut down the AsyncIO scheduler instance asynchronously.
|
|
1151
1303
|
|
|
1152
|
-
This method gracefully stops the AsyncIOScheduler
|
|
1153
|
-
|
|
1154
|
-
to complete currently executing jobs before shutting down.
|
|
1304
|
+
This method gracefully stops the AsyncIOScheduler and signals the main event loop
|
|
1305
|
+
to stop waiting, allowing for clean application shutdown.
|
|
1155
1306
|
|
|
1156
1307
|
Parameters
|
|
1157
1308
|
----------
|
|
1158
1309
|
wait : bool, optional
|
|
1159
|
-
If True,
|
|
1160
|
-
If False, the scheduler shuts down immediately without waiting for running jobs to finish. Default is True.
|
|
1310
|
+
If True, waits for currently executing jobs to complete. Default is True.
|
|
1161
1311
|
|
|
1162
1312
|
Returns
|
|
1163
1313
|
-------
|
|
1164
1314
|
None
|
|
1165
|
-
This method does not return any value. It performs the shutdown operation for the AsyncIO scheduler.
|
|
1166
1315
|
|
|
1167
1316
|
Raises
|
|
1168
1317
|
------
|
|
@@ -1172,25 +1321,36 @@ class Scheduler(ISchedule):
|
|
|
1172
1321
|
If an error occurs during the shutdown process.
|
|
1173
1322
|
"""
|
|
1174
1323
|
|
|
1175
|
-
#
|
|
1324
|
+
# Validate the wait parameter
|
|
1176
1325
|
if not isinstance(wait, bool):
|
|
1177
1326
|
raise ValueError("The 'wait' parameter must be a boolean value.")
|
|
1178
1327
|
|
|
1179
|
-
# If the scheduler is not running, there
|
|
1180
|
-
if not self.
|
|
1328
|
+
# If the scheduler is not running, there's nothing to shut down
|
|
1329
|
+
if not self.isRunning():
|
|
1181
1330
|
return
|
|
1182
1331
|
|
|
1183
1332
|
try:
|
|
1184
|
-
|
|
1333
|
+
|
|
1334
|
+
# Log the shutdown process
|
|
1335
|
+
self.__logger.info(f"Shutting down scheduler (wait={wait})...")
|
|
1336
|
+
|
|
1337
|
+
# Shut down the AsyncIOScheduler
|
|
1185
1338
|
self.__scheduler.shutdown(wait=wait)
|
|
1186
1339
|
|
|
1187
|
-
#
|
|
1340
|
+
# Signal the stop event to break the wait in start()
|
|
1341
|
+
if self._stop_event and not self._stop_event.is_set():
|
|
1342
|
+
self._stop_event.set()
|
|
1343
|
+
|
|
1344
|
+
# Allow time for cleanup if waiting
|
|
1188
1345
|
if wait:
|
|
1189
|
-
await asyncio.sleep(0)
|
|
1346
|
+
await asyncio.sleep(0.1)
|
|
1347
|
+
|
|
1348
|
+
# Log the successful shutdown
|
|
1349
|
+
self.__logger.info("Scheduler shutdown completed successfully.")
|
|
1190
1350
|
|
|
1191
1351
|
except Exception as e:
|
|
1192
1352
|
|
|
1193
|
-
#
|
|
1353
|
+
# Handle exceptions that may occur during shutdown
|
|
1194
1354
|
raise CLIOrionisRuntimeError(f"Failed to shut down the scheduler: {str(e)}") from e
|
|
1195
1355
|
|
|
1196
1356
|
def pause(self, signature: str) -> bool:
|
|
@@ -1322,7 +1482,7 @@ class Scheduler(ISchedule):
|
|
|
1322
1482
|
|
|
1323
1483
|
# Iterate through the internal jobs list to find and remove the job
|
|
1324
1484
|
for job in self.__jobs:
|
|
1325
|
-
if job
|
|
1485
|
+
if job.signature == signature:
|
|
1326
1486
|
self.__jobs.remove(job) # Remove the job from the internal list
|
|
1327
1487
|
break
|
|
1328
1488
|
|
|
@@ -1388,4 +1548,182 @@ class Scheduler(ISchedule):
|
|
|
1388
1548
|
})
|
|
1389
1549
|
|
|
1390
1550
|
# Return the list of scheduled job details
|
|
1391
|
-
return events
|
|
1551
|
+
return events
|
|
1552
|
+
|
|
1553
|
+
def cancelScheduledPause(self) -> bool:
|
|
1554
|
+
"""
|
|
1555
|
+
Cancel a previously scheduled pause operation.
|
|
1556
|
+
|
|
1557
|
+
This method attempts to remove a job from the scheduler that was set to pause
|
|
1558
|
+
the scheduler at a specific time. If the job exists, it is removed, and a log entry
|
|
1559
|
+
is created to indicate the cancellation. If no such job exists, the method returns False.
|
|
1560
|
+
|
|
1561
|
+
Returns
|
|
1562
|
+
-------
|
|
1563
|
+
bool
|
|
1564
|
+
True if the scheduled pause job was successfully cancelled.
|
|
1565
|
+
False if no pause job was found or an error occurred during the cancellation process.
|
|
1566
|
+
"""
|
|
1567
|
+
try:
|
|
1568
|
+
# Attempt to remove the pause job with the specific ID
|
|
1569
|
+
self.__scheduler.remove_job("scheduler_pause_at")
|
|
1570
|
+
|
|
1571
|
+
# Log the successful cancellation of the pause operation
|
|
1572
|
+
self.__logger.info("Scheduled pause operation cancelled.")
|
|
1573
|
+
|
|
1574
|
+
# Return True to indicate the pause job was successfully cancelled
|
|
1575
|
+
return True
|
|
1576
|
+
|
|
1577
|
+
except:
|
|
1578
|
+
# Return False if the pause job does not exist or an error occurred
|
|
1579
|
+
return False
|
|
1580
|
+
|
|
1581
|
+
def cancelScheduledResume(self) -> bool:
|
|
1582
|
+
"""
|
|
1583
|
+
Cancel a previously scheduled resume operation.
|
|
1584
|
+
|
|
1585
|
+
This method attempts to remove a job from the scheduler that was set to resume
|
|
1586
|
+
the scheduler at a specific time. If the job exists, it is removed, and a log entry
|
|
1587
|
+
is created to indicate the cancellation. If no such job exists, the method returns False.
|
|
1588
|
+
|
|
1589
|
+
Returns
|
|
1590
|
+
-------
|
|
1591
|
+
bool
|
|
1592
|
+
True if the scheduled resume job was successfully cancelled.
|
|
1593
|
+
False if no resume job was found or an error occurred during the cancellation process.
|
|
1594
|
+
"""
|
|
1595
|
+
try:
|
|
1596
|
+
# Attempt to remove the resume job with the specific ID
|
|
1597
|
+
self.__scheduler.remove_job("scheduler_resume_at")
|
|
1598
|
+
|
|
1599
|
+
# Log the successful cancellation of the resume operation
|
|
1600
|
+
self.__logger.info("Scheduled resume operation cancelled.")
|
|
1601
|
+
|
|
1602
|
+
# Return True to indicate the resume job was successfully cancelled
|
|
1603
|
+
return True
|
|
1604
|
+
|
|
1605
|
+
except:
|
|
1606
|
+
|
|
1607
|
+
# Return False if the resume job does not exist or an error occurred
|
|
1608
|
+
return False
|
|
1609
|
+
|
|
1610
|
+
def cancelScheduledShutdown(self) -> bool:
|
|
1611
|
+
"""
|
|
1612
|
+
Cancel a previously scheduled shutdown operation.
|
|
1613
|
+
|
|
1614
|
+
This method attempts to remove a job from the scheduler that was set to shut down
|
|
1615
|
+
the scheduler at a specific time. If the job exists, it is removed, and a log entry
|
|
1616
|
+
is created to indicate the cancellation. If no such job exists, the method returns False.
|
|
1617
|
+
|
|
1618
|
+
Returns
|
|
1619
|
+
-------
|
|
1620
|
+
bool
|
|
1621
|
+
True if the scheduled shutdown job was successfully cancelled.
|
|
1622
|
+
False if no shutdown job was found or an error occurred during the cancellation process.
|
|
1623
|
+
"""
|
|
1624
|
+
try:
|
|
1625
|
+
# Attempt to remove the shutdown job with the specific ID
|
|
1626
|
+
self.__scheduler.remove_job("scheduler_shutdown_at")
|
|
1627
|
+
|
|
1628
|
+
# Log the successful cancellation of the shutdown operation
|
|
1629
|
+
self.__logger.info("Scheduled shutdown operation cancelled.")
|
|
1630
|
+
|
|
1631
|
+
# Return True to indicate the shutdown job was successfully cancelled
|
|
1632
|
+
return True
|
|
1633
|
+
|
|
1634
|
+
except:
|
|
1635
|
+
|
|
1636
|
+
# Return False if the shutdown job does not exist or an error occurred
|
|
1637
|
+
return False
|
|
1638
|
+
|
|
1639
|
+
def isRunning(self) -> bool:
|
|
1640
|
+
"""
|
|
1641
|
+
Determine if the scheduler is currently active and running.
|
|
1642
|
+
|
|
1643
|
+
This method checks the internal state of the AsyncIOScheduler instance to determine
|
|
1644
|
+
whether it is currently running. The scheduler is considered running if it has been
|
|
1645
|
+
started and has not been paused or shut down.
|
|
1646
|
+
|
|
1647
|
+
Returns
|
|
1648
|
+
-------
|
|
1649
|
+
bool
|
|
1650
|
+
True if the scheduler is running, False otherwise.
|
|
1651
|
+
"""
|
|
1652
|
+
|
|
1653
|
+
# Return the running state of the scheduler
|
|
1654
|
+
return self.__scheduler.running
|
|
1655
|
+
|
|
1656
|
+
async def waitUntilStopped(self) -> None:
|
|
1657
|
+
"""
|
|
1658
|
+
Wait for the scheduler to stop gracefully.
|
|
1659
|
+
|
|
1660
|
+
This method blocks the execution until the scheduler is stopped. It waits for the
|
|
1661
|
+
internal stop event to be set, which signals that the scheduler has been shut down.
|
|
1662
|
+
This is useful for ensuring that the scheduler completes its operations before
|
|
1663
|
+
proceeding with other tasks.
|
|
1664
|
+
|
|
1665
|
+
Returns
|
|
1666
|
+
-------
|
|
1667
|
+
None
|
|
1668
|
+
This method does not return any value. It waits until the scheduler is stopped.
|
|
1669
|
+
"""
|
|
1670
|
+
|
|
1671
|
+
# Check if the stop event is initialized
|
|
1672
|
+
if self._stop_event:
|
|
1673
|
+
|
|
1674
|
+
# Wait for the stop event to be set, signaling the scheduler has stopped
|
|
1675
|
+
await self._stop_event.wait()
|
|
1676
|
+
|
|
1677
|
+
def forceStop(self) -> None:
|
|
1678
|
+
"""
|
|
1679
|
+
Forcefully stop the scheduler immediately without waiting for jobs to complete.
|
|
1680
|
+
|
|
1681
|
+
This method shuts down the AsyncIOScheduler instance without waiting for currently
|
|
1682
|
+
running jobs to finish. It is intended for emergency situations where an immediate
|
|
1683
|
+
stop is required. The method also signals the internal stop event to ensure that
|
|
1684
|
+
the scheduler's main loop is interrupted and the application can proceed with
|
|
1685
|
+
shutdown procedures.
|
|
1686
|
+
|
|
1687
|
+
Returns
|
|
1688
|
+
-------
|
|
1689
|
+
None
|
|
1690
|
+
This method does not return any value. It forcefully stops the scheduler and
|
|
1691
|
+
signals the stop event.
|
|
1692
|
+
"""
|
|
1693
|
+
|
|
1694
|
+
# Check if the scheduler is currently running
|
|
1695
|
+
if self.__scheduler.running:
|
|
1696
|
+
# Shut down the scheduler immediately without waiting for jobs to complete
|
|
1697
|
+
self.__scheduler.shutdown(wait=False)
|
|
1698
|
+
|
|
1699
|
+
# Check if the stop event exists and has not already been set
|
|
1700
|
+
if self._stop_event and not self._stop_event.is_set():
|
|
1701
|
+
# Signal the stop event to interrupt the scheduler's main loop
|
|
1702
|
+
self._stop_event.set()
|
|
1703
|
+
|
|
1704
|
+
def stop(self) -> None:
|
|
1705
|
+
"""
|
|
1706
|
+
Stop the scheduler synchronously by setting the stop event.
|
|
1707
|
+
|
|
1708
|
+
This method signals the scheduler to stop by setting the internal stop event.
|
|
1709
|
+
It can be called from non-async contexts to initiate a shutdown. If the asyncio
|
|
1710
|
+
event loop is running, the stop event is set in a thread-safe manner. Otherwise,
|
|
1711
|
+
the stop event is set directly.
|
|
1712
|
+
|
|
1713
|
+
Returns
|
|
1714
|
+
-------
|
|
1715
|
+
None
|
|
1716
|
+
This method does not return any value. It signals the scheduler to stop.
|
|
1717
|
+
"""
|
|
1718
|
+
|
|
1719
|
+
# Check if the stop event exists and has not already been set
|
|
1720
|
+
if self._stop_event and not self._stop_event.is_set():
|
|
1721
|
+
# Get the current asyncio event loop
|
|
1722
|
+
loop = asyncio.get_event_loop()
|
|
1723
|
+
|
|
1724
|
+
# If the event loop is running, set the stop event in a thread-safe manner
|
|
1725
|
+
if loop.is_running():
|
|
1726
|
+
loop.call_soon_threadsafe(self._stop_event.set)
|
|
1727
|
+
else:
|
|
1728
|
+
# Otherwise, set the stop event directly
|
|
1729
|
+
self._stop_event.set()
|
orionis/metadata/framework.py
CHANGED
|
@@ -8,14 +8,14 @@ orionis/console/args/enums/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
|
8
8
|
orionis/console/args/enums/actions.py,sha256=S3T-vWS6DJSGtANrq3od3-90iYAjPvJwaOZ2V02y34c,1222
|
|
9
9
|
orionis/console/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
orionis/console/base/command.py,sha256=OM4xqVgpv_1RZVyVG8BzOHl1sP9FT5mPUwZjMil8IRg,6637
|
|
11
|
-
orionis/console/base/scheduler.py,sha256=
|
|
11
|
+
orionis/console/base/scheduler.py,sha256=LFzWUFk07LrcpKFL7sS7exHzTkxFRd1DPDSqDSpRcOk,15157
|
|
12
12
|
orionis/console/base/scheduler_event_listener.py,sha256=5qWPmf6jmiRwUz6U1ZvpQCG5eovOpeCl0KAb8kKDkfU,3905
|
|
13
13
|
orionis/console/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
orionis/console/commands/cache.py,sha256=8DsYoRzSBLn0P9qkGVItRbo0R6snWBDBg0_Xa7tmVhs,2322
|
|
15
15
|
orionis/console/commands/help.py,sha256=zfSw0pYaOnFN-_Ozdn4veBQDYMgSSDY10nPDCi-7tTY,3199
|
|
16
16
|
orionis/console/commands/publisher.py,sha256=FUg-EUzK7LLXsla10ZUZro8V0Z5S-KjmsaSdRHSSGbA,21381
|
|
17
17
|
orionis/console/commands/scheduler_list.py,sha256=A2N_mEXEJDHO8DX2TDrL1ROeeRhFSkWD3rCw64Hrf0o,4763
|
|
18
|
-
orionis/console/commands/scheduler_work.py,sha256=
|
|
18
|
+
orionis/console/commands/scheduler_work.py,sha256=FHBQ8Ajs1zacuQFXaG-KLVRy07m9FqwyJRNVmp7cDg0,6337
|
|
19
19
|
orionis/console/commands/test.py,sha256=-EmQwFwMBuby3OI9HwqMIwuJzd2CGbWbOqmwrR25sOE,2402
|
|
20
20
|
orionis/console/commands/version.py,sha256=SUuNDJ40f2uq69OQUmPQXJKaa9Bm_iVRDPmBd7zc1Yc,3658
|
|
21
21
|
orionis/console/commands/workflow.py,sha256=NYOmjTSvm2o6AE4h9LSTZMFSYPQreNmEJtronyOxaYk,2451
|
|
@@ -81,7 +81,7 @@ orionis/console/request/cli_request.py,sha256=7-sgYmNUCipuHLVAwWLJiHv0cJCDmsM1Lu
|
|
|
81
81
|
orionis/console/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
82
82
|
orionis/console/tasks/event.py,sha256=l4J-HEPaj1mxB_PYQMgG9dRHUe01wUag8fKLLnR2N2M,164395
|
|
83
83
|
orionis/console/tasks/listener.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
84
|
-
orionis/console/tasks/schedule.py,sha256=
|
|
84
|
+
orionis/console/tasks/schedule.py,sha256=Lpm_P0Brw8XHiqA-Me9SMevNlo0CzUf-rrt2PKTFW_I,72389
|
|
85
85
|
orionis/container/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
86
86
|
orionis/container/container.py,sha256=aF_b6lTUpG4YCo9yFJEzsntTdIzgMMXFW5LyWqAJVBQ,87987
|
|
87
87
|
orionis/container/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -239,7 +239,7 @@ orionis/foundation/providers/scheduler_provider.py,sha256=72SoixFog9IOE9Ve9Xcfw6
|
|
|
239
239
|
orionis/foundation/providers/testing_provider.py,sha256=SrJRpdvcblx9WvX7x9Y3zc7OQfiTf7la0HAJrm2ESlE,3725
|
|
240
240
|
orionis/foundation/providers/workers_provider.py,sha256=oa_2NIDH6UxZrtuGkkoo_zEoNIMGgJ46vg5CCgAm7wI,3926
|
|
241
241
|
orionis/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
242
|
-
orionis/metadata/framework.py,sha256=
|
|
242
|
+
orionis/metadata/framework.py,sha256=LNUd5w9wDJVTDwfg_BIGNlRYyyCu3UpQnvpmOQsMlIQ,4109
|
|
243
243
|
orionis/metadata/package.py,sha256=k7Yriyp5aUcR-iR8SK2ec_lf0_Cyc-C7JczgXa-I67w,16039
|
|
244
244
|
orionis/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
245
245
|
orionis/services/asynchrony/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -415,7 +415,7 @@ orionis/test/validators/web_report.py,sha256=n9BfzOZz6aEiNTypXcwuWbFRG0OdHNSmCNu
|
|
|
415
415
|
orionis/test/validators/workers.py,sha256=rWcdRexINNEmGaO7mnc1MKUxkHKxrTsVuHgbnIfJYgc,1206
|
|
416
416
|
orionis/test/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
417
417
|
orionis/test/view/render.py,sha256=f-zNhtKSg9R5Njqujbg2l2amAs2-mRVESneLIkWOZjU,4082
|
|
418
|
-
orionis-0.
|
|
418
|
+
orionis-0.522.0.dist-info/licenses/LICENCE,sha256=JhC-z_9mbpUrCfPjcl3DhDA8trNDMzb57cvRSam1avc,1463
|
|
419
419
|
tests/container/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
420
420
|
tests/container/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
421
421
|
tests/container/context/test_manager.py,sha256=wOwXpl9rHNfTTexa9GBKYMwK0_-KSQPbI-AEyGNkmAE,1356
|
|
@@ -561,8 +561,8 @@ tests/testing/validators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
561
561
|
tests/testing/validators/test_testing_validators.py,sha256=WPo5GxTP6xE-Dw3X1vZoqOMpb6HhokjNSbgDsDRDvy4,16588
|
|
562
562
|
tests/testing/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
563
563
|
tests/testing/view/test_render.py,sha256=tnnMBwS0iKUIbogLvu-7Rii50G6Koddp3XT4wgdFEYM,1050
|
|
564
|
-
orionis-0.
|
|
565
|
-
orionis-0.
|
|
566
|
-
orionis-0.
|
|
567
|
-
orionis-0.
|
|
568
|
-
orionis-0.
|
|
564
|
+
orionis-0.522.0.dist-info/METADATA,sha256=tcvGNIB23Yo-X3d9ClTaZfP9tChX79uyxXZPstmEyhs,4801
|
|
565
|
+
orionis-0.522.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
566
|
+
orionis-0.522.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
|
|
567
|
+
orionis-0.522.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
568
|
+
orionis-0.522.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|