orionis 0.511.0__py3-none-any.whl → 0.513.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/command.py +1 -1
- orionis/console/base/scheduler.py +1 -1
- orionis/console/base/scheduler_event_listener.py +136 -0
- orionis/console/commands/scheduler_work.py +37 -25
- orionis/console/contracts/event.py +3032 -9
- orionis/console/contracts/schedule.py +34 -0
- orionis/console/contracts/schedule_event_listener.py +320 -0
- orionis/console/contracts/scheduler.py +140 -0
- orionis/console/core/reactor.py +1 -1
- orionis/console/entities/all_jobs_removed.py +23 -0
- orionis/console/entities/executor_added.py +23 -0
- orionis/console/entities/executor_removed.py +25 -0
- orionis/console/entities/job_added.py +24 -0
- orionis/console/entities/job_error.py +35 -0
- orionis/console/entities/job_event_data.py +40 -0
- orionis/console/entities/job_executed.py +31 -0
- orionis/console/entities/job_max_instances.py +27 -0
- orionis/console/entities/job_missed.py +25 -0
- orionis/console/entities/job_modified.py +23 -0
- orionis/console/entities/job_pause.py +22 -0
- orionis/console/entities/job_removed.py +22 -0
- orionis/console/entities/job_resume.py +25 -0
- orionis/console/entities/job_store_added.py +24 -0
- orionis/console/entities/job_store_removed.py +25 -0
- orionis/console/entities/job_submitted.py +24 -0
- orionis/console/entities/scheduler_event_data.py +33 -0
- orionis/console/entities/scheduler_paused.py +17 -0
- orionis/console/entities/scheduler_resumed.py +24 -0
- orionis/console/entities/scheduler_shutdown.py +23 -0
- orionis/console/entities/scheduler_started.py +21 -0
- orionis/console/enums/listener.py +75 -0
- orionis/console/tasks/event.py +3703 -21
- orionis/console/tasks/schedule.py +701 -52
- orionis/foundation/application.py +1 -1
- orionis/metadata/framework.py +1 -1
- {orionis-0.511.0.dist-info → orionis-0.513.0.dist-info}/METADATA +1 -1
- {orionis-0.511.0.dist-info → orionis-0.513.0.dist-info}/RECORD +42 -22
- orionis/console/base/contracts/__init__.py +0 -0
- orionis/console/base/contracts/scheduler.py +0 -38
- orionis/console/contracts/listener.py +0 -132
- orionis/console/entities/listeners.py +0 -241
- orionis/console/tasks/exception_report.py +0 -94
- /orionis/console/{base/contracts → contracts}/command.py +0 -0
- {orionis-0.511.0.dist-info → orionis-0.513.0.dist-info}/WHEEL +0 -0
- {orionis-0.511.0.dist-info → orionis-0.513.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.511.0.dist-info → orionis-0.513.0.dist-info}/top_level.txt +0 -0
- {orionis-0.511.0.dist-info → orionis-0.513.0.dist-info}/zip-safe +0 -0
|
@@ -1,15 +1,38 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
from datetime import datetime
|
|
2
3
|
import logging
|
|
3
4
|
from typing import Dict, List, Optional
|
|
4
5
|
import pytz
|
|
5
|
-
from apscheduler.events import
|
|
6
|
+
from apscheduler.events import (
|
|
7
|
+
EVENT_JOB_ERROR,
|
|
8
|
+
EVENT_JOB_EXECUTED,
|
|
9
|
+
EVENT_JOB_MISSED,
|
|
10
|
+
EVENT_JOB_SUBMITTED,
|
|
11
|
+
EVENT_SCHEDULER_PAUSED,
|
|
12
|
+
EVENT_SCHEDULER_RESUMED,
|
|
13
|
+
EVENT_SCHEDULER_SHUTDOWN,
|
|
14
|
+
EVENT_SCHEDULER_STARTED,
|
|
15
|
+
EVENT_JOB_MAX_INSTANCES
|
|
16
|
+
)
|
|
6
17
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler as APSAsyncIOScheduler
|
|
18
|
+
from rich.console import Console
|
|
19
|
+
from rich.panel import Panel
|
|
20
|
+
from rich.text import Text
|
|
7
21
|
from orionis.console.contracts.reactor import IReactor
|
|
8
22
|
from orionis.console.contracts.schedule import ISchedule
|
|
23
|
+
from orionis.console.entities.job_error import JobError
|
|
24
|
+
from orionis.console.entities.job_executed import JobExecuted
|
|
25
|
+
from orionis.console.entities.job_max_instances import JobMaxInstances
|
|
26
|
+
from orionis.console.entities.job_missed import JobMissed
|
|
27
|
+
from orionis.console.entities.job_submitted import JobSubmitted
|
|
28
|
+
from orionis.console.entities.scheduler_paused import SchedulerPaused
|
|
29
|
+
from orionis.console.entities.scheduler_resumed import SchedulerResumed
|
|
30
|
+
from orionis.console.entities.scheduler_shutdown import SchedulerShutdown
|
|
31
|
+
from orionis.console.entities.scheduler_started import SchedulerStarted
|
|
32
|
+
from orionis.console.enums.listener import ListeningEvent
|
|
9
33
|
from orionis.console.exceptions import CLIOrionisRuntimeError
|
|
10
34
|
from orionis.console.output.contracts.console import IConsole
|
|
11
35
|
from orionis.console.tasks.event import Event
|
|
12
|
-
from orionis.console.tasks.exception_report import ScheduleErrorReporter
|
|
13
36
|
from orionis.foundation.contracts.application import IApplication
|
|
14
37
|
from orionis.services.log.contracts.log_service import ILogger
|
|
15
38
|
|
|
@@ -20,7 +43,7 @@ class Scheduler(ISchedule):
|
|
|
20
43
|
reactor: IReactor,
|
|
21
44
|
app: IApplication,
|
|
22
45
|
console: IConsole,
|
|
23
|
-
|
|
46
|
+
rich_console: Console
|
|
24
47
|
) -> None:
|
|
25
48
|
"""
|
|
26
49
|
Initialize a new instance of the Scheduler class.
|
|
@@ -48,8 +71,8 @@ class Scheduler(ISchedule):
|
|
|
48
71
|
# Store the console instance for output operations.
|
|
49
72
|
self.__console = console
|
|
50
73
|
|
|
51
|
-
# Store the
|
|
52
|
-
self.
|
|
74
|
+
# Store the rich console instance for advanced output formatting.
|
|
75
|
+
self.__rich_console = rich_console
|
|
53
76
|
|
|
54
77
|
# Initialize AsyncIOScheduler instance with timezone configuration.
|
|
55
78
|
self.__scheduler: APSAsyncIOScheduler = APSAsyncIOScheduler(
|
|
@@ -79,49 +102,519 @@ class Scheduler(ISchedule):
|
|
|
79
102
|
# Initialize the jobs list to keep track of all scheduled jobs.
|
|
80
103
|
self.__jobs: List[dict] = []
|
|
81
104
|
|
|
82
|
-
#
|
|
105
|
+
# Initialize the listeners dictionary to manage event listeners.
|
|
106
|
+
self.__listeners: Dict[str, callable] = {}
|
|
107
|
+
|
|
108
|
+
def __getCurrentTime(
|
|
109
|
+
self
|
|
110
|
+
) -> str:
|
|
111
|
+
"""
|
|
112
|
+
Get the current date and time formatted as a string.
|
|
113
|
+
|
|
114
|
+
This method retrieves the current date and time in the timezone configured
|
|
115
|
+
for the application and formats it as a string in the "YYYY-MM-DD HH:MM:SS" format.
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
str
|
|
120
|
+
A string representing the current date and time in the configured timezone,
|
|
121
|
+
formatted as "YYYY-MM-DD HH:MM:SS".
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
tz = pytz.timezone(self.__app.config("app.timezone", "UTC"))
|
|
125
|
+
now = datetime.now(tz)
|
|
126
|
+
return now.strftime("%Y-%m-%d %H:%M:%S")
|
|
127
|
+
|
|
128
|
+
def __suscribeListeners(
|
|
129
|
+
self
|
|
130
|
+
) -> None:
|
|
131
|
+
"""
|
|
132
|
+
Subscribe to scheduler events for monitoring and handling.
|
|
133
|
+
|
|
134
|
+
This method sets up event listeners for the AsyncIOScheduler instance to monitor
|
|
135
|
+
various scheduler events such as scheduler start, shutdown, pause, resume, job submission,
|
|
136
|
+
execution, missed jobs, and errors. Each listener is associated with a specific event type
|
|
137
|
+
and is responsible for handling the corresponding event.
|
|
138
|
+
|
|
139
|
+
The listeners log relevant information, invoke registered callbacks, and handle errors
|
|
140
|
+
or missed jobs as needed. This ensures that the scheduler's state and job execution
|
|
141
|
+
are monitored effectively.
|
|
142
|
+
|
|
143
|
+
Returns
|
|
144
|
+
-------
|
|
145
|
+
None
|
|
146
|
+
This method does not return any value. It configures event listeners on the scheduler.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
# Add a listener for the scheduler started event
|
|
150
|
+
self.__scheduler.add_listener(self.__startedListener, EVENT_SCHEDULER_STARTED)
|
|
151
|
+
|
|
152
|
+
# Add a listener for the scheduler shutdown event
|
|
153
|
+
self.__scheduler.add_listener(self.__shutdownListener, EVENT_SCHEDULER_SHUTDOWN)
|
|
154
|
+
|
|
155
|
+
# Add a listener for the scheduler paused event
|
|
156
|
+
self.__scheduler.add_listener(self.__pausedListener, EVENT_SCHEDULER_PAUSED)
|
|
157
|
+
|
|
158
|
+
# Add a listener for the scheduler resumed event
|
|
159
|
+
self.__scheduler.add_listener(self.__resumedListener, EVENT_SCHEDULER_RESUMED)
|
|
160
|
+
|
|
161
|
+
# Add a listener for job submission events
|
|
162
|
+
self.__scheduler.add_listener(self.__submittedListener, EVENT_JOB_SUBMITTED)
|
|
163
|
+
|
|
164
|
+
# Add a listener for job execution events
|
|
165
|
+
self.__scheduler.add_listener(self.__executedListener, EVENT_JOB_EXECUTED)
|
|
166
|
+
|
|
167
|
+
# Add a listener for missed job events
|
|
168
|
+
self.__scheduler.add_listener(self.__missedListener, EVENT_JOB_MISSED)
|
|
169
|
+
|
|
170
|
+
# Add a listener for job error events
|
|
171
|
+
self.__scheduler.add_listener(self.__errorListener, EVENT_JOB_ERROR)
|
|
172
|
+
|
|
173
|
+
# Add a listener for job max instances events
|
|
174
|
+
self.__scheduler.add_listener(self.__maxInstancesListener, EVENT_JOB_MAX_INSTANCES)
|
|
175
|
+
|
|
176
|
+
def __startedListener(
|
|
177
|
+
self,
|
|
178
|
+
event: SchedulerStarted
|
|
179
|
+
) -> None:
|
|
180
|
+
"""
|
|
181
|
+
Handle the scheduler started event for logging and invoking registered listeners.
|
|
182
|
+
|
|
183
|
+
This method is triggered when the scheduler starts. It logs an informational
|
|
184
|
+
message indicating that the scheduler has started successfully and displays
|
|
185
|
+
a formatted message on the rich console. If a listener is registered for the
|
|
186
|
+
scheduler started event, it invokes the listener with the event details.
|
|
187
|
+
|
|
188
|
+
Parameters
|
|
189
|
+
----------
|
|
190
|
+
event : SchedulerStarted
|
|
191
|
+
An event object containing details about the scheduler start event.
|
|
192
|
+
|
|
193
|
+
Returns
|
|
194
|
+
-------
|
|
195
|
+
None
|
|
196
|
+
This method does not return any value. It performs logging, displays
|
|
197
|
+
a message on the console, and invokes any registered listener for the
|
|
198
|
+
scheduler started event.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
# Get the current time in the configured timezone
|
|
202
|
+
now = self.__getCurrentTime()
|
|
203
|
+
|
|
204
|
+
# Log an informational message indicating that the scheduler has started
|
|
205
|
+
self.__logger.info(f"Orionis Scheduler started successfully at {now}.")
|
|
206
|
+
|
|
207
|
+
# Display a start message for the scheduler worker on the rich console
|
|
208
|
+
# Add a blank line for better formatting
|
|
209
|
+
self.__rich_console.line()
|
|
210
|
+
panel_content = Text.assemble(
|
|
211
|
+
(" Orionis Scheduler Worker ", "bold white on green"), # Header text with styling
|
|
212
|
+
("\n\n", ""), # Add spacing
|
|
213
|
+
("The scheduled tasks worker has started successfully.\n", "white"), # Main message
|
|
214
|
+
(f"Started at: {now}\n", "dim"), # Display the start time in dim text
|
|
215
|
+
("To stop the worker, press ", "white"), # Instruction text
|
|
216
|
+
("Ctrl+C", "bold yellow"), # Highlight the key combination
|
|
217
|
+
(".", "white") # End the instruction
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# Display the message in a styled panel
|
|
221
|
+
self.__rich_console.print(
|
|
222
|
+
Panel(panel_content, border_style="green", padding=(1, 2))
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Add another blank line for better formatting
|
|
226
|
+
self.__rich_console.line()
|
|
227
|
+
|
|
228
|
+
# Retrieve the global identifier for the scheduler started event
|
|
229
|
+
scheduler_started = ListeningEvent.SCHEDULER_STARTED.value
|
|
230
|
+
|
|
231
|
+
# Check if a listener is registered for the scheduler started event
|
|
232
|
+
if scheduler_started in self.__listeners:
|
|
233
|
+
listener = self.__listeners[scheduler_started]
|
|
234
|
+
|
|
235
|
+
# Ensure the listener is callable before invoking it
|
|
236
|
+
if callable(listener):
|
|
237
|
+
|
|
238
|
+
# Invoke the registered listener with the event details
|
|
239
|
+
listener(event)
|
|
240
|
+
|
|
241
|
+
def __shutdownListener(
|
|
242
|
+
self,
|
|
243
|
+
event: SchedulerShutdown
|
|
244
|
+
) -> None:
|
|
245
|
+
"""
|
|
246
|
+
Handle the scheduler shutdown event for logging and invoking registered listeners.
|
|
247
|
+
|
|
248
|
+
This method is triggered when the scheduler shuts down. It logs an informational
|
|
249
|
+
message indicating that the scheduler has shut down successfully and displays
|
|
250
|
+
a formatted message on the rich console. If a listener is registered for the
|
|
251
|
+
scheduler shutdown event, it invokes the listener with the event details.
|
|
252
|
+
|
|
253
|
+
Parameters
|
|
254
|
+
----------
|
|
255
|
+
event : SchedulerShutdown
|
|
256
|
+
An event object containing details about the scheduler shutdown event.
|
|
257
|
+
|
|
258
|
+
Returns
|
|
259
|
+
-------
|
|
260
|
+
None
|
|
261
|
+
This method does not return any value. It performs logging, displays
|
|
262
|
+
a message on the console, and invokes any registered listener for the
|
|
263
|
+
scheduler shutdown event.
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
# Get the current time in the configured timezone
|
|
267
|
+
now = self.__getCurrentTime()
|
|
268
|
+
|
|
269
|
+
# Create a shutdown message
|
|
270
|
+
message = f"Orionis Scheduler shut down successfully at {now}."
|
|
271
|
+
|
|
272
|
+
# Log an informational message indicating that the scheduler has shut down
|
|
273
|
+
self.__logger.info(message)
|
|
274
|
+
|
|
275
|
+
# Display a shutdown message for the scheduler worker on console
|
|
276
|
+
if self.__app.config('app.debug', False):
|
|
277
|
+
self.__console.info(message)
|
|
278
|
+
|
|
279
|
+
# Retrieve the global identifier for the scheduler shutdown event
|
|
280
|
+
scheduler_shutdown = GlobalListener.SCHEDULER_SHUTDOWN.value
|
|
281
|
+
|
|
282
|
+
# Check if a listener is registered for the scheduler shutdown event
|
|
283
|
+
if scheduler_shutdown in self.__listeners:
|
|
284
|
+
listener = self.__listeners[scheduler_shutdown]
|
|
285
|
+
|
|
286
|
+
# Ensure the listener is callable before invoking it
|
|
287
|
+
if callable(listener):
|
|
288
|
+
# Invoke the registered listener with the event details
|
|
289
|
+
listener(event)
|
|
290
|
+
|
|
291
|
+
def __pausedListener(
|
|
292
|
+
self,
|
|
293
|
+
event: SchedulerPaused
|
|
294
|
+
) -> None:
|
|
295
|
+
"""
|
|
296
|
+
Handle the scheduler paused event for logging and invoking registered listeners.
|
|
297
|
+
|
|
298
|
+
This method is triggered when the scheduler is paused. It logs an informational
|
|
299
|
+
message indicating that the scheduler has been paused successfully and displays
|
|
300
|
+
a formatted message on the rich console. If a listener is registered for the
|
|
301
|
+
scheduler paused event, it invokes the listener with the event details.
|
|
302
|
+
|
|
303
|
+
Parameters
|
|
304
|
+
----------
|
|
305
|
+
event : SchedulerPaused
|
|
306
|
+
An event object containing details about the scheduler paused event.
|
|
307
|
+
|
|
308
|
+
Returns
|
|
309
|
+
-------
|
|
310
|
+
None
|
|
311
|
+
This method does not return any value. It performs logging, displays
|
|
312
|
+
a message on the console, and invokes any registered listener for the
|
|
313
|
+
scheduler paused event.
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
# Get the current time in the configured timezone
|
|
317
|
+
now = self.__getCurrentTime()
|
|
318
|
+
|
|
319
|
+
# Create a paused message
|
|
320
|
+
message = f"Orionis Scheduler paused successfully at {now}."
|
|
321
|
+
|
|
322
|
+
# Log an informational message indicating that the scheduler has been paused
|
|
323
|
+
self.__logger.info(message)
|
|
324
|
+
|
|
325
|
+
# Display a paused message for the scheduler worker on console
|
|
326
|
+
if self.__app.config('app.debug', False):
|
|
327
|
+
self.__console.info(message)
|
|
328
|
+
|
|
329
|
+
# Retrieve the global identifier for the scheduler paused event
|
|
330
|
+
scheduler_paused = GlobalListener.SCHEDULER_PAUSED.value
|
|
331
|
+
|
|
332
|
+
# Check if a listener is registered for the scheduler paused event
|
|
333
|
+
if scheduler_paused in self.__listeners:
|
|
334
|
+
listener = self.__listeners[scheduler_paused]
|
|
335
|
+
|
|
336
|
+
# Ensure the listener is callable before invoking it
|
|
337
|
+
if callable(listener):
|
|
338
|
+
# Invoke the registered listener with the event details
|
|
339
|
+
listener(event)
|
|
340
|
+
|
|
341
|
+
def __resumedListener(
|
|
342
|
+
self,
|
|
343
|
+
event: SchedulerResumed
|
|
344
|
+
) -> None:
|
|
345
|
+
"""
|
|
346
|
+
Handle the scheduler resumed event for logging and invoking registered listeners.
|
|
347
|
+
|
|
348
|
+
This method is triggered when the scheduler resumes from a paused state. It logs an informational
|
|
349
|
+
message indicating that the scheduler has resumed successfully and displays a formatted message
|
|
350
|
+
on the rich console. If a listener is registered for the scheduler resumed event, it invokes
|
|
351
|
+
the listener with the event details.
|
|
352
|
+
|
|
353
|
+
Parameters
|
|
354
|
+
----------
|
|
355
|
+
event : SchedulerResumed
|
|
356
|
+
An event object containing details about the scheduler resumed event.
|
|
357
|
+
|
|
358
|
+
Returns
|
|
359
|
+
-------
|
|
360
|
+
None
|
|
361
|
+
This method does not return any value. It performs logging, displays
|
|
362
|
+
a message on the console, and invokes any registered listener for the
|
|
363
|
+
scheduler resumed event.
|
|
364
|
+
"""
|
|
365
|
+
|
|
366
|
+
# Get the current time in the configured timezone
|
|
367
|
+
now = self.__getCurrentTime()
|
|
368
|
+
|
|
369
|
+
# Create a resumed message
|
|
370
|
+
message = f"Orionis Scheduler resumed successfully at {now}."
|
|
371
|
+
|
|
372
|
+
# Log an informational message indicating that the scheduler has resumed
|
|
373
|
+
self.__logger.info(message)
|
|
374
|
+
|
|
375
|
+
# Display a resumed message for the scheduler worker on console
|
|
376
|
+
if self.__app.config('app.debug', False):
|
|
377
|
+
self.__console.info(message)
|
|
378
|
+
|
|
379
|
+
# Retrieve the global identifier for the scheduler resumed event
|
|
380
|
+
scheduler_resumed = GlobalListener.SCHEDULER_RESUMED.value
|
|
381
|
+
|
|
382
|
+
# Check if a listener is registered for the scheduler resumed event
|
|
383
|
+
if scheduler_resumed in self.__listeners:
|
|
384
|
+
listener = self.__listeners[scheduler_resumed]
|
|
385
|
+
|
|
386
|
+
# Ensure the listener is callable before invoking it
|
|
387
|
+
if callable(listener):
|
|
388
|
+
# Invoke the registered listener with the event details
|
|
389
|
+
listener(event)
|
|
390
|
+
|
|
391
|
+
def __submittedListener(
|
|
392
|
+
self,
|
|
393
|
+
event: JobSubmitted
|
|
394
|
+
) -> None:
|
|
395
|
+
"""
|
|
396
|
+
Handle job submission events for logging and error reporting.
|
|
397
|
+
|
|
398
|
+
This method is triggered when a job is submitted to its executor. It logs an informational
|
|
399
|
+
message indicating that the job has been submitted successfully. If the application is in
|
|
400
|
+
debug mode, it also displays a message on the console. Additionally, if a listener is
|
|
401
|
+
registered for the submitted job, it invokes the listener with the event details.
|
|
402
|
+
|
|
403
|
+
Parameters
|
|
404
|
+
----------
|
|
405
|
+
event : JobSubmitted
|
|
406
|
+
An instance of the JobSubmitted containing details about the submitted job,
|
|
407
|
+
including its ID and scheduled run times.
|
|
408
|
+
|
|
409
|
+
Returns
|
|
410
|
+
-------
|
|
411
|
+
None
|
|
412
|
+
This method does not return any value. It performs logging, error reporting,
|
|
413
|
+
and listener invocation for the job submission event.
|
|
414
|
+
"""
|
|
415
|
+
|
|
416
|
+
# Create a submission message
|
|
417
|
+
message = f"Task {event.job_id} submitted to executor."
|
|
418
|
+
|
|
419
|
+
# Log an informational message indicating that the job has been submitted
|
|
420
|
+
self.__logger.info(message)
|
|
421
|
+
|
|
422
|
+
# If the application is in debug mode, display a message on the console
|
|
423
|
+
if self.__app.config('app.debug', False):
|
|
424
|
+
self.__console.info(message)
|
|
425
|
+
|
|
426
|
+
# If a listener is registered for this job ID, invoke the listener with the event details
|
|
427
|
+
if event.job_id in self.__listeners:
|
|
428
|
+
listener = self.__listeners[event.job_id]
|
|
429
|
+
|
|
430
|
+
# Ensure the listener is callable before invoking it
|
|
431
|
+
if callable(listener):
|
|
432
|
+
|
|
433
|
+
# Invoke the registered listener with the event details
|
|
434
|
+
listener(event)
|
|
435
|
+
|
|
436
|
+
def __executedListener(
|
|
437
|
+
self,
|
|
438
|
+
event: JobExecuted
|
|
439
|
+
) -> None:
|
|
440
|
+
"""
|
|
441
|
+
Handle job execution events for logging and error reporting.
|
|
442
|
+
|
|
443
|
+
This method is triggered when a job is executed by its executor. It logs an informational
|
|
444
|
+
message indicating that the job has been executed successfully. If the application is in
|
|
445
|
+
debug mode, it also displays a message on the console. If the job execution resulted in
|
|
446
|
+
an exception, it logs the error and reports it using the error reporter. Additionally,
|
|
447
|
+
if a listener is registered for the executed job, it invokes the listener with the event details.
|
|
448
|
+
|
|
449
|
+
Parameters
|
|
450
|
+
----------
|
|
451
|
+
event : JobExecuted
|
|
452
|
+
An instance of the JobExecuted containing details about the executed job,
|
|
453
|
+
including its ID, return value, exception (if any), and traceback.
|
|
454
|
+
|
|
455
|
+
Returns
|
|
456
|
+
-------
|
|
457
|
+
None
|
|
458
|
+
This method does not return any value. It performs logging, error reporting,
|
|
459
|
+
and listener invocation for the job execution event.
|
|
460
|
+
"""
|
|
461
|
+
|
|
462
|
+
# Create an execution message
|
|
463
|
+
message = f"Task {event.job_id} executed."
|
|
464
|
+
|
|
465
|
+
# Log an informational message indicating that the job has been executed
|
|
466
|
+
self.__logger.info(message)
|
|
467
|
+
|
|
468
|
+
# If the application is in debug mode, display a message on the console
|
|
83
469
|
if self.__app.config('app.debug', False):
|
|
84
|
-
self.
|
|
470
|
+
self.__console.info(message)
|
|
471
|
+
|
|
472
|
+
# If a listener is registered for this job ID, invoke the listener with the event details
|
|
473
|
+
if event.job_id in self.__listeners:
|
|
474
|
+
listener = self.__listeners[event.job_id]
|
|
475
|
+
|
|
476
|
+
# Ensure the listener is callable before invoking it
|
|
477
|
+
if callable(listener):
|
|
85
478
|
|
|
86
|
-
|
|
479
|
+
# Invoke the registered listener with the event details
|
|
480
|
+
listener(event)
|
|
481
|
+
|
|
482
|
+
def __missedListener(
|
|
483
|
+
self,
|
|
484
|
+
event: JobMissed
|
|
485
|
+
) -> None:
|
|
87
486
|
"""
|
|
88
|
-
Handle job events
|
|
487
|
+
Handle job missed events for debugging and error reporting.
|
|
89
488
|
|
|
90
|
-
This method
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
489
|
+
This method is triggered when a scheduled job is missed. It logs a warning
|
|
490
|
+
message indicating the missed job and its scheduled run time. If the application
|
|
491
|
+
is in debug mode, it reports the missed job using the error reporter. Additionally,
|
|
492
|
+
if a listener is registered for the missed job, it invokes the listener with the
|
|
493
|
+
event details.
|
|
95
494
|
|
|
96
495
|
Parameters
|
|
97
496
|
----------
|
|
98
|
-
event :
|
|
99
|
-
|
|
100
|
-
|
|
497
|
+
event : JobMissed
|
|
498
|
+
An instance of the JobMissed event containing details about the missed job,
|
|
499
|
+
including its ID and scheduled run time.
|
|
101
500
|
|
|
102
501
|
Returns
|
|
103
502
|
-------
|
|
104
503
|
None
|
|
105
|
-
This method does not return any value. It performs logging
|
|
106
|
-
|
|
107
|
-
"""
|
|
108
|
-
|
|
109
|
-
#
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
self.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
)
|
|
504
|
+
This method does not return any value. It performs logging, error reporting,
|
|
505
|
+
and listener invocation for the missed job event.
|
|
506
|
+
"""
|
|
507
|
+
|
|
508
|
+
# Create a missed job message
|
|
509
|
+
message = f"Task {event.job_id} was missed. It was scheduled to run at {event.scheduled_run_time}."
|
|
510
|
+
|
|
511
|
+
# Log a warning indicating that the job was missed
|
|
512
|
+
self.__logger.warning(message)
|
|
513
|
+
|
|
514
|
+
# If the application is in debug mode, report the missed job using the error reporter
|
|
515
|
+
if self.__app.config('app.debug', False):
|
|
516
|
+
self.__console.warning(message)
|
|
517
|
+
|
|
518
|
+
# If a listener is registered for this job ID, invoke the listener with the event details
|
|
519
|
+
if event.job_id in self.__listeners:
|
|
520
|
+
listener = self.__listeners[event.job_id]
|
|
521
|
+
|
|
522
|
+
# Ensure the listener is callable before invoking it
|
|
523
|
+
if callable(listener):
|
|
524
|
+
|
|
525
|
+
# Invoke the registered listener with the event details
|
|
526
|
+
listener(event)
|
|
527
|
+
|
|
528
|
+
def __errorListener(
|
|
529
|
+
self,
|
|
530
|
+
event: JobError
|
|
531
|
+
) -> None:
|
|
532
|
+
"""
|
|
533
|
+
Handle job error events for logging and error reporting.
|
|
534
|
+
|
|
535
|
+
This method is triggered when a job execution results in an error. It logs an error
|
|
536
|
+
message indicating the job ID and the exception raised. If the application is in
|
|
537
|
+
debug mode, it also reports the error using the error reporter. Additionally, if a
|
|
538
|
+
listener is registered for the errored job, it invokes the listener with the event details.
|
|
539
|
+
|
|
540
|
+
Parameters
|
|
541
|
+
----------
|
|
542
|
+
event : JobError
|
|
543
|
+
An instance of the JobError event containing details about the errored job,
|
|
544
|
+
including its ID and the exception raised.
|
|
545
|
+
|
|
546
|
+
Returns
|
|
547
|
+
-------
|
|
548
|
+
None
|
|
549
|
+
This method does not return any value. It performs logging, error reporting,
|
|
550
|
+
and listener invocation for the job error event.
|
|
551
|
+
"""
|
|
552
|
+
|
|
553
|
+
# Create an error message
|
|
554
|
+
message = f"Task {event.job_id} raised an exception: {event.exception}"
|
|
555
|
+
|
|
556
|
+
# Log an error message indicating that the job raised an exception
|
|
557
|
+
self.__logger.error(message)
|
|
558
|
+
|
|
559
|
+
# If the application is in debug mode, display a message on the console
|
|
560
|
+
if self.__app.config('app.debug', False):
|
|
561
|
+
self.__console.error(message)
|
|
562
|
+
|
|
563
|
+
# If a listener is registered for this job ID, invoke the listener with the event details
|
|
564
|
+
if event.job_id in self.__listeners:
|
|
565
|
+
listener = self.__listeners[event.job_id]
|
|
566
|
+
|
|
567
|
+
# Ensure the listener is callable before invoking it
|
|
568
|
+
if callable(listener):
|
|
569
|
+
|
|
570
|
+
# Invoke the registered listener with the event details
|
|
571
|
+
listener(event)
|
|
572
|
+
|
|
573
|
+
def __maxInstancesListener(
|
|
574
|
+
self,
|
|
575
|
+
event: JobMaxInstances
|
|
576
|
+
) -> None:
|
|
577
|
+
"""
|
|
578
|
+
Handle job max instances events for logging and error reporting.
|
|
579
|
+
|
|
580
|
+
This method is triggered when a job execution exceeds the maximum allowed
|
|
581
|
+
concurrent instances. It logs an error message indicating the job ID and
|
|
582
|
+
the exception raised. If the application is in debug mode, it also reports
|
|
583
|
+
the error using the error reporter. Additionally, if a listener is registered
|
|
584
|
+
for the job that exceeded max instances, it invokes the listener with the event details.
|
|
585
|
+
|
|
586
|
+
Parameters
|
|
587
|
+
----------
|
|
588
|
+
event : JobMaxInstances
|
|
589
|
+
An instance of the JobMaxInstances event containing details about the job that
|
|
590
|
+
exceeded max instances, including its ID and the exception raised.
|
|
591
|
+
|
|
592
|
+
Returns
|
|
593
|
+
-------
|
|
594
|
+
None
|
|
595
|
+
This method does not return any value. It performs logging, error reporting,
|
|
596
|
+
and listener invocation for the job max instances event.
|
|
597
|
+
"""
|
|
598
|
+
|
|
599
|
+
# Create a max instances error message
|
|
600
|
+
message = f"Task {event.job_id} exceeded maximum instances"
|
|
601
|
+
|
|
602
|
+
# Log an error message indicating that the job exceeded maximum instances
|
|
603
|
+
self.__logger.error(message)
|
|
604
|
+
|
|
605
|
+
# If the application is in debug mode, display a message on the console
|
|
606
|
+
if self.__app.config('app.debug', False):
|
|
607
|
+
self.__console.error(message)
|
|
608
|
+
|
|
609
|
+
# If a listener is registered for this job ID, invoke the listener with the event details
|
|
610
|
+
if event.job_id in self.__listeners:
|
|
611
|
+
listener = self.__listeners[event.job_id]
|
|
612
|
+
|
|
613
|
+
# Ensure the listener is callable before invoking it
|
|
614
|
+
if callable(listener):
|
|
615
|
+
|
|
616
|
+
# Invoke the registered listener with the event details
|
|
617
|
+
listener(event)
|
|
125
618
|
|
|
126
619
|
def __getCommands(
|
|
127
620
|
self
|
|
@@ -311,25 +804,178 @@ class Scheduler(ISchedule):
|
|
|
311
804
|
# Return the Event instance for further scheduling configuration
|
|
312
805
|
return self.__events[signature]
|
|
313
806
|
|
|
314
|
-
def
|
|
807
|
+
def _setListener(
|
|
315
808
|
self,
|
|
809
|
+
event: str,
|
|
316
810
|
listener: callable
|
|
317
811
|
) -> None:
|
|
318
812
|
"""
|
|
319
|
-
|
|
813
|
+
Register a listener callback for a specific scheduler event.
|
|
320
814
|
|
|
321
|
-
This method allows
|
|
322
|
-
when the scheduler
|
|
323
|
-
|
|
815
|
+
This method allows the registration of a callable listener function that will be
|
|
816
|
+
invoked when the specified scheduler event occurs. The event can be one of the
|
|
817
|
+
predefined global events or a specific job ID. The listener must be a callable
|
|
818
|
+
function that accepts a single argument, which will be the event object.
|
|
324
819
|
|
|
325
820
|
Parameters
|
|
326
821
|
----------
|
|
822
|
+
event : str
|
|
823
|
+
The name of the event to listen for. This can be a global event name (e.g., 'scheduler_started')
|
|
824
|
+
or a specific job ID.
|
|
327
825
|
listener : callable
|
|
328
|
-
A function that will be
|
|
329
|
-
|
|
826
|
+
A callable function that will be invoked when the specified event occurs.
|
|
827
|
+
The function should accept one parameter, which will be the event object.
|
|
828
|
+
|
|
829
|
+
Returns
|
|
830
|
+
-------
|
|
831
|
+
None
|
|
832
|
+
This method does not return any value. It registers the listener for the specified event.
|
|
833
|
+
|
|
834
|
+
Raises
|
|
835
|
+
------
|
|
836
|
+
ValueError
|
|
837
|
+
If the event name is not a non-empty string or if the listener is not callable.
|
|
838
|
+
"""
|
|
839
|
+
|
|
840
|
+
# Validate that the event name is a non-empty string
|
|
841
|
+
if not isinstance(event, str) or not event.strip():
|
|
842
|
+
raise ValueError("Event name must be a non-empty string.")
|
|
843
|
+
|
|
844
|
+
# Validate that the listener is a callable function
|
|
845
|
+
if not callable(listener):
|
|
846
|
+
raise ValueError("Listener must be a callable function.")
|
|
847
|
+
|
|
848
|
+
# Register the listener for the specified event
|
|
849
|
+
self.__listeners[event] = listener
|
|
850
|
+
|
|
851
|
+
def pauseEverythingAt(
|
|
852
|
+
self,
|
|
853
|
+
at: datetime
|
|
854
|
+
) -> None:
|
|
330
855
|
"""
|
|
331
|
-
|
|
332
|
-
|
|
856
|
+
Schedule the scheduler to pause all operations at a specific datetime.
|
|
857
|
+
|
|
858
|
+
This method allows you to schedule a job that will pause the AsyncIOScheduler
|
|
859
|
+
at the specified datetime. The job is added to the scheduler with a 'date'
|
|
860
|
+
trigger, ensuring it executes exactly at the given time.
|
|
861
|
+
|
|
862
|
+
Parameters
|
|
863
|
+
----------
|
|
864
|
+
at : datetime
|
|
865
|
+
The datetime at which the scheduler should be paused. Must be a valid
|
|
866
|
+
datetime object.
|
|
867
|
+
|
|
868
|
+
Returns
|
|
869
|
+
-------
|
|
870
|
+
None
|
|
871
|
+
This method does not return any value. It schedules a job to pause the
|
|
872
|
+
scheduler at the specified datetime.
|
|
873
|
+
|
|
874
|
+
Raises
|
|
875
|
+
------
|
|
876
|
+
ValueError
|
|
877
|
+
If the 'at' parameter is not a valid datetime object.
|
|
878
|
+
"""
|
|
879
|
+
|
|
880
|
+
# Validate that the 'at' parameter is a datetime object
|
|
881
|
+
if not isinstance(at, datetime):
|
|
882
|
+
raise ValueError("The 'at' parameter must be a datetime object.")
|
|
883
|
+
|
|
884
|
+
# Add a job to the scheduler to pause it at the specified datetime
|
|
885
|
+
self.__scheduler.add_job(
|
|
886
|
+
func=self.__scheduler.pause, # Function to pause the scheduler
|
|
887
|
+
trigger='date', # Trigger type is 'date' for one-time execution
|
|
888
|
+
run_date=at, # The datetime at which the job will run
|
|
889
|
+
id=f"pause_scheduler_at_{at.isoformat()}", # Unique job ID based on the datetime
|
|
890
|
+
name=f"Pause Scheduler at {at.isoformat()}", # Descriptive name for the job
|
|
891
|
+
replace_existing=True # Replace any existing job with the same ID
|
|
892
|
+
)
|
|
893
|
+
|
|
894
|
+
def resumeEverythingAt(
|
|
895
|
+
self,
|
|
896
|
+
at: datetime
|
|
897
|
+
) -> None:
|
|
898
|
+
"""
|
|
899
|
+
Schedule the scheduler to resume all operations at a specific datetime.
|
|
900
|
+
|
|
901
|
+
This method allows you to schedule a job that will resume the AsyncIOScheduler
|
|
902
|
+
at the specified datetime. The job is added to the scheduler with a 'date'
|
|
903
|
+
trigger, ensuring it executes exactly at the given time.
|
|
904
|
+
|
|
905
|
+
Parameters
|
|
906
|
+
----------
|
|
907
|
+
at : datetime
|
|
908
|
+
The datetime at which the scheduler should be resumed. Must be a valid
|
|
909
|
+
datetime object.
|
|
910
|
+
|
|
911
|
+
Returns
|
|
912
|
+
-------
|
|
913
|
+
None
|
|
914
|
+
This method does not return any value. It schedules a job to resume the
|
|
915
|
+
scheduler at the specified datetime.
|
|
916
|
+
|
|
917
|
+
Raises
|
|
918
|
+
------
|
|
919
|
+
ValueError
|
|
920
|
+
If the 'at' parameter is not a valid datetime object.
|
|
921
|
+
"""
|
|
922
|
+
|
|
923
|
+
# Validate that the 'at' parameter is a datetime object
|
|
924
|
+
if not isinstance(at, datetime):
|
|
925
|
+
raise ValueError("The 'at' parameter must be a datetime object.")
|
|
926
|
+
|
|
927
|
+
# Add a job to the scheduler to resume it at the specified datetime
|
|
928
|
+
self.__scheduler.add_job(
|
|
929
|
+
func=self.__scheduler.resume, # Function to resume the scheduler
|
|
930
|
+
trigger='date', # Trigger type is 'date' for one-time execution
|
|
931
|
+
run_date=at, # The datetime at which the job will run
|
|
932
|
+
id=f"resume_scheduler_at_{at.isoformat()}", # Unique job ID based on the datetime
|
|
933
|
+
name=f"Resume Scheduler at {at.isoformat()}", # Descriptive name for the job
|
|
934
|
+
replace_existing=True # Replace any existing job with the same ID
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
def shutdownEverythingAt(
|
|
938
|
+
self,
|
|
939
|
+
at: datetime
|
|
940
|
+
) -> None:
|
|
941
|
+
"""
|
|
942
|
+
Schedule the scheduler to shut down all operations at a specific datetime.
|
|
943
|
+
|
|
944
|
+
This method allows you to schedule a job that will shut down the AsyncIOScheduler
|
|
945
|
+
at the specified datetime. The job is added to the scheduler with a 'date'
|
|
946
|
+
trigger, ensuring it executes exactly at the given time.
|
|
947
|
+
|
|
948
|
+
Parameters
|
|
949
|
+
----------
|
|
950
|
+
at : datetime
|
|
951
|
+
The datetime at which the scheduler should be shut down. Must be a valid
|
|
952
|
+
datetime object.
|
|
953
|
+
|
|
954
|
+
Returns
|
|
955
|
+
-------
|
|
956
|
+
None
|
|
957
|
+
This method does not return any value. It schedules a job to shut down the
|
|
958
|
+
scheduler at the specified datetime.
|
|
959
|
+
|
|
960
|
+
Raises
|
|
961
|
+
------
|
|
962
|
+
ValueError
|
|
963
|
+
If the 'at' parameter is not a valid datetime object.
|
|
964
|
+
"""
|
|
965
|
+
|
|
966
|
+
# Validate that the 'at' parameter is a datetime object
|
|
967
|
+
if not isinstance(at, datetime):
|
|
968
|
+
raise ValueError("The 'at' parameter must be a datetime object.")
|
|
969
|
+
|
|
970
|
+
# Add a job to the scheduler to shut it down at the specified datetime
|
|
971
|
+
self.__scheduler.add_job(
|
|
972
|
+
func=self.shutdown, # Function to shut down the scheduler
|
|
973
|
+
trigger='date', # Trigger type is 'date' for one-time execution
|
|
974
|
+
run_date=at, # The datetime at which the job will run
|
|
975
|
+
id=f"shutdown_scheduler_at_{at.isoformat()}", # Unique job ID based on the datetime
|
|
976
|
+
name=f"Shutdown Scheduler at {at.isoformat()}", # Descriptive name for the job
|
|
977
|
+
replace_existing=True # Replace any existing job with the same ID
|
|
978
|
+
)
|
|
333
979
|
|
|
334
980
|
async def start(self) -> None:
|
|
335
981
|
"""
|
|
@@ -348,22 +994,28 @@ class Scheduler(ISchedule):
|
|
|
348
994
|
# Start the AsyncIOScheduler to handle asynchronous jobs.
|
|
349
995
|
try:
|
|
350
996
|
|
|
351
|
-
#
|
|
352
|
-
self.
|
|
997
|
+
# Ensure all events are loaded into the internal jobs list
|
|
998
|
+
self.__loadEvents()
|
|
999
|
+
|
|
1000
|
+
# Subscribe to scheduler events for monitoring and handling
|
|
1001
|
+
self.__suscribeListeners()
|
|
353
1002
|
|
|
354
1003
|
# Ensure we're in an asyncio context
|
|
355
1004
|
asyncio.get_running_loop()
|
|
356
1005
|
|
|
357
1006
|
# Start the scheduler
|
|
358
1007
|
if not self.__scheduler.running:
|
|
359
|
-
self.__logger.info(f"Orionis Scheduler started. {len(self.__jobs)} jobs scheduled.")
|
|
360
1008
|
self.__scheduler.start()
|
|
361
1009
|
|
|
362
1010
|
# Keep the event loop alive to process scheduled jobs
|
|
363
1011
|
try:
|
|
1012
|
+
|
|
1013
|
+
# Run indefinitely until interrupted
|
|
364
1014
|
while self.__scheduler.running and self.__scheduler.get_jobs():
|
|
365
1015
|
await asyncio.sleep(1)
|
|
1016
|
+
|
|
366
1017
|
except (KeyboardInterrupt, asyncio.CancelledError):
|
|
1018
|
+
|
|
367
1019
|
# Handle graceful shutdown on keyboard interrupt or cancellation
|
|
368
1020
|
await self.shutdown()
|
|
369
1021
|
|
|
@@ -408,9 +1060,6 @@ class Scheduler(ISchedule):
|
|
|
408
1060
|
if wait:
|
|
409
1061
|
await asyncio.sleep(0.1)
|
|
410
1062
|
|
|
411
|
-
# Log the shutdown of the scheduler
|
|
412
|
-
self.__logger.info("Orionis Scheduler has been shut down.")
|
|
413
|
-
|
|
414
1063
|
except Exception:
|
|
415
1064
|
|
|
416
1065
|
# AsyncIOScheduler may not be running or may have issues in shutdown
|