orionis 0.725.0__py3-none-any.whl → 0.727.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.
@@ -99,6 +99,7 @@ class ScheduleWorkCommand(BaseCommand):
99
99
  console.line()
100
100
  console.print(Panel("No scheduled jobs found.", border_style="green"))
101
101
  console.line()
102
+ return
102
103
 
103
104
  # If there are scheduled jobs and the scheduler has an onStarted method
104
105
  if rf_scheduler.hasMethod("onStarted"):
@@ -236,7 +236,7 @@ class IEvent(ABC):
236
236
  pass
237
237
 
238
238
  @abstractmethod
239
- def everySecond(
239
+ def everySeconds(
240
240
  self,
241
241
  seconds: int
242
242
  ) -> bool:
@@ -187,7 +187,7 @@ class ISchedule(ABC):
187
187
  pass
188
188
 
189
189
  @abstractmethod
190
- def pauseTask(self, signature: str) -> bool:
190
+ def pauseCommand(self, signature: str) -> bool:
191
191
  """
192
192
  Pause a scheduled job in the AsyncIO scheduler.
193
193
 
@@ -215,7 +215,7 @@ class ISchedule(ABC):
215
215
  pass
216
216
 
217
217
  @abstractmethod
218
- def resumeTask(self, signature: str) -> bool:
218
+ def resumeCommand(self, signature: str) -> bool:
219
219
  """
220
220
  Resume a paused job in the AsyncIO scheduler.
221
221
 
@@ -243,7 +243,7 @@ class ISchedule(ABC):
243
243
  pass
244
244
 
245
245
  @abstractmethod
246
- def removeTask(self, signature: str) -> bool:
246
+ def removeCommand(self, signature: str) -> bool:
247
247
  """
248
248
  Remove a scheduled job from the AsyncIO scheduler.
249
249
 
@@ -73,4 +73,7 @@ class Event:
73
73
  max_instances: int = 1
74
74
 
75
75
  # Grace time in seconds for misfired events
76
- misfire_grace_time: Optional[int] = None
76
+ misfire_grace_time: Optional[int] = None
77
+
78
+ # Whether to coalesce missed runs into a single run
79
+ coalesce: bool = True
@@ -83,7 +83,10 @@ class Event(IEvent):
83
83
  self.__max_instances: Optional[int] = 1
84
84
 
85
85
  # Initialize the misfire grace time attribute as None
86
- self.__misfire_grace_time: Optional[int] = None
86
+ self.__misfire_grace_time: Optional[int] = 1
87
+
88
+ # Initialize the coalesce attribute as True
89
+ self.__coalesce: bool = True
87
90
 
88
91
  def toEntity( # NOSONAR
89
92
  self
@@ -141,9 +144,13 @@ class Event(IEvent):
141
144
  raise CLIOrionisValueError("Max instances must be a positive integer or None.")
142
145
 
143
146
  # Validate that misfire_grace_time is a positive integer if it is set
144
- if self.__misfire_grace_time is not None and (not isinstance(self.__misfire_grace_time, int) or self.__misfire_grace_time < 0):
147
+ if self.__misfire_grace_time is not None and (not isinstance(self.__misfire_grace_time, int) or self.__misfire_grace_time <= 0):
145
148
  raise CLIOrionisValueError("Misfire grace time must be a positive integer or None.")
146
149
 
150
+ # Validate that coalesce is a boolean if it is set
151
+ if self.__coalesce is not None and not isinstance(self.__coalesce, bool):
152
+ raise CLIOrionisValueError("Coalesce must be a boolean value.")
153
+
147
154
  # Construct and return an EventEntity with the current event's attributes
148
155
  return EventEntity(
149
156
  signature=self.__signature,
@@ -156,9 +163,39 @@ class Event(IEvent):
156
163
  details=self.__details,
157
164
  listener=self.__listener,
158
165
  max_instances=self.__max_instances,
159
- misfire_grace_time=self.__misfire_grace_time
166
+ misfire_grace_time=self.__misfire_grace_time,
167
+ coalesce=self.__coalesce
160
168
  )
161
169
 
170
+ def coalesce(
171
+ self,
172
+ coalesce: bool = True
173
+ ) -> 'Event':
174
+ """
175
+ Set whether to coalesce missed event executions.
176
+
177
+ This method allows you to specify whether missed executions of the event
178
+ should be coalesced into a single execution when the scheduler is running
179
+ behind. If set to True, only the most recent missed execution will be run.
180
+ If set to False, all missed executions will be run in sequence.
181
+
182
+ Parameters
183
+ ----------
184
+ coalesce : bool
185
+ A boolean indicating whether to coalesce missed executions. Defaults to True.
186
+
187
+ Returns
188
+ -------
189
+ Event
190
+ Returns the current instance of the Event to allow method chaining.
191
+ """
192
+
193
+ # Set the internal coalesce attribute
194
+ self.__coalesce = coalesce
195
+
196
+ # Return self to support method chaining
197
+ return self
198
+
162
199
  def misfireGraceTime(
163
200
  self,
164
201
  seconds: int = 60
@@ -173,7 +210,7 @@ class Event(IEvent):
173
210
  Parameters
174
211
  ----------
175
212
  seconds : int
176
- The number of seconds to allow for a misfire grace period. Must be a positive integer.
213
+ The number of seconds to allow for a misfire grace period. Must be a positive integer greater than zero.
177
214
 
178
215
  Returns
179
216
  -------
@@ -187,7 +224,7 @@ class Event(IEvent):
187
224
  """
188
225
 
189
226
  # Validate that the seconds parameter is a positive integer
190
- if not isinstance(seconds, int) or seconds < 0:
227
+ if not isinstance(seconds, int) or seconds <= 0:
191
228
  raise CLIOrionisValueError("Misfire grace time must be a positive integer.")
192
229
 
193
230
  # Set the internal misfire grace time attribute
@@ -448,9 +485,14 @@ class Event(IEvent):
448
485
  if not isinstance(date, datetime):
449
486
  raise CLIOrionisValueError("The date must be a datetime instance.")
450
487
 
488
+ # Ensure that random delay is not set for a one-time execution
489
+ if self.__random_delay > 0:
490
+ raise CLIOrionisValueError("Random delay cannot be applied to a one-time execution.")
491
+
451
492
  # Set both start and end dates to the specified date for a one-time execution
452
493
  self.__start_date = date
453
494
  self.__end_date = date
495
+ self.__max_instances = 1
454
496
 
455
497
  # Use a DateTrigger to schedule the event to run once at the specified date and time
456
498
  self.__trigger = DateTrigger(run_date=date)
@@ -461,7 +503,7 @@ class Event(IEvent):
461
503
  # Indicate that the scheduling was successful
462
504
  return True
463
505
 
464
- def everySecond(
506
+ def everySeconds(
465
507
  self,
466
508
  seconds: int
467
509
  ) -> bool:
@@ -497,6 +539,10 @@ class Event(IEvent):
497
539
  if not isinstance(seconds, int) or seconds <= 0:
498
540
  raise CLIOrionisValueError(self.ERROR_MSG_INVALID_INTERVAL)
499
541
 
542
+ # Ensure that random delay is not set for second-based intervals.
543
+ if self.__random_delay > 0:
544
+ raise CLIOrionisValueError("Random delay (jitter) cannot be applied to second-based intervals.")
545
+
500
546
  # Configure the trigger to execute the event at the specified interval,
501
547
  # using any previously set start_date and end_date.
502
548
  self.__trigger = IntervalTrigger(
@@ -536,7 +582,7 @@ class Event(IEvent):
536
582
  """
537
583
 
538
584
  # Delegate scheduling to the everySecond method with an interval of 5 seconds.
539
- return self.everySecond(5)
585
+ return self.everySeconds(5)
540
586
 
541
587
  def everyTenSeconds(
542
588
  self
@@ -563,7 +609,7 @@ class Event(IEvent):
563
609
  """
564
610
 
565
611
  # Delegate scheduling to the everySecond method with an interval of 10 seconds.
566
- return self.everySecond(10)
612
+ return self.everySeconds(10)
567
613
 
568
614
  def everyFifteenSeconds(
569
615
  self
@@ -590,7 +636,7 @@ class Event(IEvent):
590
636
  """
591
637
 
592
638
  # Delegate scheduling to the everySecond method with an interval of 15 seconds.
593
- return self.everySecond(15)
639
+ return self.everySeconds(15)
594
640
 
595
641
  def everyTwentySeconds(
596
642
  self
@@ -613,7 +659,7 @@ class Event(IEvent):
613
659
  """
614
660
 
615
661
  # Delegate scheduling to the everySecond method with an interval of 20 seconds.
616
- return self.everySecond(20)
662
+ return self.everySeconds(20)
617
663
 
618
664
  def everyTwentyFiveSeconds(
619
665
  self
@@ -640,7 +686,7 @@ class Event(IEvent):
640
686
  """
641
687
 
642
688
  # Delegate scheduling to the everySecond method with an interval of 25 seconds.
643
- return self.everySecond(25)
689
+ return self.everySeconds(25)
644
690
 
645
691
  def everyThirtySeconds(
646
692
  self
@@ -667,7 +713,7 @@ class Event(IEvent):
667
713
  """
668
714
 
669
715
  # Delegate scheduling to the everySecond method with an interval of 30 seconds.
670
- return self.everySecond(30)
716
+ return self.everySeconds(30)
671
717
 
672
718
  def everyThirtyFiveSeconds(
673
719
  self
@@ -690,7 +736,7 @@ class Event(IEvent):
690
736
  """
691
737
 
692
738
  # Delegate scheduling to the everySecond method with an interval of 35 seconds.
693
- return self.everySecond(35)
739
+ return self.everySeconds(35)
694
740
 
695
741
  def everyFortySeconds(
696
742
  self
@@ -717,7 +763,7 @@ class Event(IEvent):
717
763
  """
718
764
 
719
765
  # Delegate scheduling to the everySecond method with an interval of 40 seconds.
720
- return self.everySecond(40)
766
+ return self.everySeconds(40)
721
767
 
722
768
  def everyFortyFiveSeconds(
723
769
  self
@@ -744,7 +790,7 @@ class Event(IEvent):
744
790
  """
745
791
 
746
792
  # Delegate scheduling to the everySecond method with an interval of 45 seconds.
747
- return self.everySecond(45)
793
+ return self.everySeconds(45)
748
794
 
749
795
  def everyFiftySeconds(
750
796
  self
@@ -771,7 +817,7 @@ class Event(IEvent):
771
817
  """
772
818
 
773
819
  # Delegate scheduling to the everySecond method with an interval of 50 seconds.
774
- return self.everySecond(50)
820
+ return self.everySeconds(50)
775
821
 
776
822
  def everyFiftyFiveSeconds(
777
823
  self
@@ -794,7 +840,7 @@ class Event(IEvent):
794
840
  """
795
841
 
796
842
  # Delegate scheduling to the everySecond method with an interval of 55 seconds.
797
- return self.everySecond(55)
843
+ return self.everySeconds(55)
798
844
 
799
845
  def everyMinute(
800
846
  self,
@@ -1,7 +1,7 @@
1
1
  import asyncio
2
2
  from datetime import datetime
3
3
  import logging
4
- from typing import Any, Dict, List, Optional, Union
4
+ from typing import Any, Dict, List, Optional, Tuple, Union
5
5
  from apscheduler.events import (
6
6
  EVENT_JOB_ERROR,
7
7
  EVENT_JOB_EXECUTED,
@@ -15,6 +15,9 @@ from apscheduler.events import (
15
15
  EVENT_SCHEDULER_STARTED,
16
16
  )
17
17
  from apscheduler.schedulers.asyncio import AsyncIOScheduler as APSAsyncIOScheduler
18
+ from apscheduler.schedulers.base import BaseScheduler
19
+ from apscheduler.schedulers.base import STATE_PAUSED, STATE_RUNNING
20
+ from apscheduler.triggers.date import DateTrigger
18
21
  from rich.console import Console
19
22
  from rich.panel import Panel
20
23
  from rich.text import Text
@@ -77,7 +80,7 @@ class Schedule(ISchedule):
77
80
  """
78
81
 
79
82
  # List of operations that can be performed on the scheduler
80
- self.__operations = [
83
+ self.__control_operations = [
81
84
  'schedule:pause',
82
85
  'schedule:resume',
83
86
  'schedule:shutdown'
@@ -94,6 +97,7 @@ class Schedule(ISchedule):
94
97
 
95
98
  # Initialize the AsyncIOScheduler with the configured timezone.
96
99
  self.__scheduler = APSAsyncIOScheduler(timezone=self.__tz)
100
+ self.__control_scheduler = APSAsyncIOScheduler(timezone=self.__tz)
97
101
 
98
102
  # Disable APScheduler's internal logging to avoid duplicate/conflicting logs.
99
103
  self.__disableLogging()
@@ -116,15 +120,15 @@ class Schedule(ISchedule):
116
120
  # Initialize the dictionary to manage event listeners.
117
121
  self.__listeners: Dict[str, callable] = {}
118
122
 
119
- # Initialize a set to track jobs paused by pauseEverythingAt.
120
- self.__pausedByPauseEverything: set = set()
121
-
122
123
  # Initialize the asyncio event used to signal scheduler shutdown.
123
124
  self._stopEvent: Optional[asyncio.Event] = None
124
125
 
125
126
  # Retrieve and initialize the catch instance for exception handling.
126
127
  self.__catch: ICatch = app.make(ICatch)
127
128
 
129
+ # Initialize the paused at timestamp
130
+ self.__paused_at: Optional[datetime] = None
131
+
128
132
  def __disableLogging(
129
133
  self
130
134
  ) -> None:
@@ -211,11 +215,6 @@ class Schedule(ISchedule):
211
215
  # Get the current time in the scheduler's configured timezone
212
216
  now = datetime.now(self.__tz)
213
217
 
214
- # Log the timezone currently assigned to the scheduler for traceability
215
- self.__logger.info(
216
- f"Timezone assigned to the scheduler: {self.__app.config('app.timezone') or 'UTC'}"
217
- )
218
-
219
218
  # Return the formatted current time string
220
219
  return now.strftime("%Y-%m-%d %H:%M:%S")
221
220
 
@@ -489,7 +488,7 @@ class Schedule(ISchedule):
489
488
  _misfire_grace_time = self.__getAttribute(data, 'misfire_grace_time', None)
490
489
 
491
490
  # Extract the job max_instances if available
492
- _max_instances = self.__getAttribute(data, 'max_instances', 0)
491
+ _max_instances = self.__getAttribute(data, 'max_instances', 1)
493
492
 
494
493
  # Extract the job coalesce if available
495
494
  _coalesce = self.__getAttribute(data, 'coalesce', False)
@@ -775,6 +774,7 @@ class Schedule(ISchedule):
775
774
  ("\n\n", ""), # Add spacing
776
775
  ("The scheduled tasks worker has started successfully.\n", "white"), # Main message
777
776
  (f"Started at: {now}\n", "dim"), # Display the start time in dim text
777
+ (f"Timezone: {self.__tz.key}\n", "dim"), # Display the configured timezone
778
778
  ("To stop the worker, press ", "white"), # Instruction text
779
779
  ("Ctrl+C", "bold yellow"), # Highlight the key combination
780
780
  (".", "white") # End the instruction
@@ -800,6 +800,11 @@ class Schedule(ISchedule):
800
800
  # Log an informational message indicating that the scheduler has started
801
801
  self.__logger.info(f"Orionis Scheduler started successfully at: {now}.")
802
802
 
803
+ # Log the timezone currently assigned to the scheduler for traceability
804
+ self.__logger.info(
805
+ f"Timezone assigned to the scheduler: {self.__tz.key}."
806
+ )
807
+
803
808
  def __shutdownListener(
804
809
  self,
805
810
  event
@@ -878,9 +883,6 @@ class Schedule(ISchedule):
878
883
  event_exception = self.__getAttribute(event, 'exception', None)
879
884
  event_traceback = self.__getAttribute(event, 'traceback', None)
880
885
 
881
- # Log an error message indicating that the job raised an exception
882
- self.__logger.error(f"Task '{event_id}' raised an exception: {event_exception}")
883
-
884
886
  # Retrieve the job event data and update it with error details
885
887
  job_event_data = self.__getTaskFromSchedulerById(event_id)
886
888
  job_event_data.code = event_code
@@ -939,9 +941,6 @@ class Schedule(ISchedule):
939
941
  event_id = self.__getAttribute(event, 'job_id', None)
940
942
  event_code = self.__getAttribute(event, 'code', 0)
941
943
 
942
- # Log an informational message indicating that the job has been submitted to the executor
943
- self.__logger.info(f"Task '{event_id}' submitted to executor.")
944
-
945
944
  # Create an event entity for the submitted job, including its ID and code
946
945
  data_event = self.__getTaskFromSchedulerById(event_id, event_code)
947
946
 
@@ -983,9 +982,6 @@ class Schedule(ISchedule):
983
982
  event_id = self.__getAttribute(event, 'job_id', None)
984
983
  event_code = self.__getAttribute(event, 'code', 0)
985
984
 
986
- # Log an informational message indicating that the job has been executed
987
- self.__logger.info(f"Task '{event_id}' executed.")
988
-
989
985
  # Create an event entity for the executed job, including its ID and code
990
986
  data_event = self.__getTaskFromSchedulerById(event_id, event_code)
991
987
 
@@ -1025,15 +1021,9 @@ class Schedule(ISchedule):
1025
1021
 
1026
1022
  # Extract the job ID from the event object, or None if not present
1027
1023
  event_id = self.__getAttribute(event, 'job_id', None)
1024
+
1028
1025
  # Extract the event code from the event object, defaulting to 0 if not present
1029
1026
  event_code = self.__getAttribute(event, 'code', 0)
1030
- # Extract the scheduled run time from the event object, or 'Unknown' if not present
1031
- event_scheduled_run_time = self.__getAttribute(event, 'scheduled_run_time', 'Unknown')
1032
-
1033
- # Log a warning indicating that the job was missed and when it was scheduled to run
1034
- self.__logger.warning(
1035
- f"Task '{event_id}' was missed. It was scheduled to run at: {event_scheduled_run_time}."
1036
- )
1037
1027
 
1038
1028
  # Create an event entity for the missed job, including its ID and code
1039
1029
  data_event = self.__getTaskFromSchedulerById(event_id, event_code)
@@ -1077,9 +1067,6 @@ class Schedule(ISchedule):
1077
1067
  event_id = self.__getAttribute(event, 'job_id', None)
1078
1068
  event_code = self.__getAttribute(event, 'code', 0)
1079
1069
 
1080
- # Log an error message indicating that the job exceeded maximum concurrent instances
1081
- self.__logger.error(f"Task '{event_id}' exceeded maximum instances")
1082
-
1083
1070
  # Create an event entity for the job that exceeded max instances
1084
1071
  data_event = self.__getTaskFromSchedulerById(event_id, event_code)
1085
1072
 
@@ -1120,27 +1107,109 @@ class Schedule(ISchedule):
1120
1107
  event_id = self.__getAttribute(event, 'job_id', None)
1121
1108
  event_code = self.__getAttribute(event, 'code', 0)
1122
1109
 
1123
- # Log the removal of the job
1124
- self.__logger.info(f"Task '{event_id}' has been removed.")
1125
-
1126
1110
  # Create an event entity for the removed job
1127
1111
  data_event = self.__getTaskFromSchedulerById(event_id, event_code)
1128
1112
 
1129
1113
  # If a listener is registered for this job ID, invoke the listener with the event details
1130
1114
  self.__taskCallableListener(data_event, ListeningEvent.JOB_ON_REMOVED)
1131
1115
 
1116
+ def __triggerIsPast(
1117
+ self,
1118
+ signature: str,
1119
+ entity: EventEntity
1120
+ ) -> bool:
1121
+ """
1122
+ Determine if the trigger for a scheduled event is set to a date in the past.
1123
+
1124
+ This method checks whether the trigger associated with the given event entity is a
1125
+ `DateTrigger` and, if so, compares its `run_date` to the current time in the scheduler's
1126
+ configured timezone. If the trigger's run date is in the past, a warning is logged and
1127
+ the method returns True, indicating that the event should be skipped.
1128
+
1129
+ Parameters
1130
+ ----------
1131
+ signature : str
1132
+ The unique signature identifying the scheduled event.
1133
+ entity : EventEntity
1134
+ The event entity containing the trigger and scheduling information.
1135
+
1136
+ Returns
1137
+ -------
1138
+ bool
1139
+ True if the event's trigger is a `DateTrigger` with a run date in the past; False otherwise.
1140
+
1141
+ Notes
1142
+ -----
1143
+ This method is used to prevent scheduling events that would immediately be considered expired.
1144
+ """
1145
+
1146
+ # Check if the event's trigger is a DateTrigger and its run_date is in the past
1147
+ if isinstance(entity.trigger, DateTrigger):
1148
+ run_date = getattr(entity.trigger, 'run_date', None)
1149
+ now = self.__getNow()
1150
+
1151
+ # If the run_date is set and is before the current time, log a warning and return True
1152
+ if run_date is not None and run_date < now:
1153
+ self.__logger.warning(
1154
+ f"Scheduled event '{signature}' has a run_date in the past ({run_date}); skipping."
1155
+ )
1156
+ return True
1157
+
1158
+ # Return False if the trigger is not a past DateTrigger
1159
+ return False
1160
+
1161
+ def __eventToEntity(
1162
+ self,
1163
+ event
1164
+ ) -> EventEntity:
1165
+ """
1166
+ Convert a scheduled event object to its corresponding EventEntity representation.
1167
+
1168
+ This method takes a scheduled event and transforms it into an `EventEntity` instance,
1169
+ which encapsulates all relevant attributes required for internal tracking and management
1170
+ within the scheduler. The conversion relies on the event implementing a `toEntity` method.
1171
+
1172
+ Parameters
1173
+ ----------
1174
+ event : Any
1175
+ The scheduled event object to be converted. Must implement a `toEntity` method.
1176
+
1177
+ Returns
1178
+ -------
1179
+ EventEntity
1180
+ An instance of `EventEntity` containing the extracted attributes from the event.
1181
+
1182
+ Raises
1183
+ ------
1184
+ CLIOrionisValueError
1185
+ If the provided event does not implement a `toEntity` method.
1186
+
1187
+ Notes
1188
+ -----
1189
+ This method is used internally to standardize event objects for consistent handling
1190
+ within the scheduler's job management system.
1191
+ """
1192
+
1193
+ # Ensure the event has a toEntity method for conversion
1194
+ if not hasattr(event, 'toEntity'):
1195
+ raise CLIOrionisValueError("The event must have a 'toEntity' method for conversion.")
1196
+
1197
+ # Convert the event to its entity representation (EventEntity)
1198
+ to_entity = getattr(event, 'toEntity')
1199
+ return to_entity()
1200
+
1132
1201
  def __loadEvents(
1133
1202
  self
1134
1203
  ) -> None:
1135
1204
  """
1136
- Load all scheduled events from the AsyncIOScheduler into the internal jobs list.
1205
+ Synchronize and register all scheduled events with the AsyncIOScheduler.
1137
1206
 
1138
- This method synchronizes the internal jobs list (`self.__jobs`) with the events currently
1139
- registered in the AsyncIOScheduler. For each event in the internal events dictionary,
1140
- it converts the event to its entity representation, adds it to the jobs list, and schedules
1141
- it in the AsyncIOScheduler with the appropriate configuration. If a listener is associated
1142
- with the event, it is also registered. This ensures that all scheduled jobs are properly
1143
- tracked and managed by both the internal state and the scheduler.
1207
+ This method updates the internal jobs list (`self.__jobs`) by converting each event in the internal
1208
+ events dictionary to its corresponding entity representation. It then schedules these jobs in the
1209
+ appropriate scheduler (control or main) based on their signature. Events with triggers set in the
1210
+ past are skipped to prevent immediate expiration. Associated listeners are registered for each job
1211
+ as needed. This ensures that all scheduled jobs are properly tracked and managed by both the internal
1212
+ state and the scheduler.
1144
1213
 
1145
1214
  Parameters
1146
1215
  ----------
@@ -1149,66 +1218,51 @@ class Schedule(ISchedule):
1149
1218
  Returns
1150
1219
  -------
1151
1220
  None
1152
- This method does not return any value. It updates the internal jobs list and
1153
- registers jobs with the AsyncIOScheduler.
1221
+ This method does not return any value. It updates the internal jobs list and registers jobs
1222
+ with the AsyncIOScheduler.
1154
1223
 
1155
1224
  Raises
1156
1225
  ------
1157
1226
  CLIOrionisRuntimeError
1158
- If an error occurs while loading or scheduling an event, a runtime error is raised
1159
- with a descriptive message.
1227
+ Raised if an error occurs while loading or scheduling an event.
1160
1228
 
1161
1229
  Notes
1162
1230
  -----
1163
- - Events are only loaded if the internal jobs list is empty.
1164
- - Each event must implement a `toEntity` method to be converted to an entity.
1165
- - Jobs are added to the scheduler with their respective configuration, and listeners
1166
- are registered if present.
1231
+ - Events are loaded only if the internal jobs list is empty to avoid duplicate scheduling.
1232
+ - Each event must implement a `toEntity` method for conversion.
1233
+ - Jobs are added to the scheduler with their respective configuration, and listeners are registered if present.
1167
1234
  """
1168
1235
 
1169
1236
  # Only load events if the jobs list is empty to avoid duplicate scheduling
1170
1237
  if not self.__jobs:
1171
1238
 
1239
+ # Lists to separate control jobs and custom jobs
1240
+ control_jobs = []
1241
+ custom_jobs = []
1242
+
1172
1243
  # Iterate through all scheduled events in the internal events dictionary
1173
1244
  for signature, event in self.__events.items():
1174
1245
 
1175
1246
  try:
1176
1247
 
1177
- # Ensure the event has a toEntity method for conversion
1178
- if not hasattr(event, 'toEntity'):
1179
- continue
1180
-
1181
1248
  # Convert the event to its entity representation (EventEntity)
1182
- to_entity = getattr(event, 'toEntity')
1183
- entity: EventEntity = to_entity()
1249
+ entity = self.__eventToEntity(event)
1250
+
1251
+ # Skip loading events with a DateTrigger set in the past
1252
+ if self.__triggerIsPast(signature, entity):
1253
+ continue
1184
1254
 
1185
1255
  # Add the job entity to the internal jobs list
1186
1256
  self.__jobs.append(entity)
1187
1257
 
1188
- # Helper function to create a job function that calls the reactor
1189
- def create_job_func(cmd, args_list):
1190
- # Returns a lambda that will call the command with its arguments
1191
- return lambda: self.__reactor.call(cmd, args_list)
1192
-
1193
- # Add the job to the AsyncIOScheduler with the specified configuration
1194
- self.__scheduler.add_job(
1195
- func=create_job_func(signature, list(entity.args)),
1196
- trigger=entity.trigger,
1197
- id=signature,
1198
- name=signature,
1199
- replace_existing=True,
1200
- max_instances=entity.max_instances,
1201
- misfire_grace_time=entity.misfire_grace_time
1202
- )
1203
-
1204
- # If the event entity has an associated listener, register it
1205
- if entity.listener:
1206
- self.setListener(signature, entity.listener)
1207
-
1208
- # Log the successful loading of the scheduled event for debugging
1209
- self.__logger.debug(f"Scheduled event '{signature}' loaded successfully.")
1258
+ # Determine which scheduler to use based on the job signature
1259
+ if signature in self.__control_operations:
1260
+ control_jobs.append((signature, entity))
1261
+ else:
1262
+ custom_jobs.append((signature, entity))
1210
1263
 
1211
1264
  except Exception as e:
1265
+
1212
1266
  # Construct an error message for failed event loading
1213
1267
  error_msg = f"Failed to load scheduled event '{signature}': {str(e)}"
1214
1268
 
@@ -1218,6 +1272,72 @@ class Schedule(ISchedule):
1218
1272
  # Raise a runtime error to signal failure in loading the scheduled event
1219
1273
  raise CLIOrionisRuntimeError(error_msg)
1220
1274
 
1275
+ # Register control jobs with the control scheduler
1276
+ self.__loadJobs(self.__control_scheduler, control_jobs)
1277
+
1278
+ # Register custom jobs with the main scheduler
1279
+ self.__loadJobs(self.__scheduler, custom_jobs)
1280
+
1281
+ # Log the successful loading of the scheduled events for debugging
1282
+ self.__logger.debug(f"Scheduled events loaded successfully: {len(self.__jobs)} jobs.")
1283
+
1284
+ def __loadJobs(
1285
+ self,
1286
+ scheduler: BaseScheduler,
1287
+ events: List[Tuple[str, EventEntity]]
1288
+ ) -> None:
1289
+ """
1290
+ Load and register jobs into the specified scheduler.
1291
+
1292
+ This method iterates over a list of event tuples, each containing a job signature and its corresponding
1293
+ EventEntity. For each event, it creates a job function that invokes the reactor with the provided command
1294
+ and arguments, then adds the job to the given scheduler with the appropriate configuration. If the event
1295
+ entity has an associated listener, the listener is registered for the job.
1296
+
1297
+ Parameters
1298
+ ----------
1299
+ scheduler : BaseScheduler
1300
+ The scheduler instance (either control or main) where jobs will be registered.
1301
+ events : List[Tuple[str, EventEntity]]
1302
+ A list of tuples, each containing a job signature and its corresponding EventEntity.
1303
+
1304
+ Returns
1305
+ -------
1306
+ None
1307
+ This method does not return any value. It registers jobs and listeners with the scheduler.
1308
+
1309
+ Notes
1310
+ -----
1311
+ - The job function is created as a lambda that calls the reactor with the command and arguments.
1312
+ - Jobs are added with configuration options such as trigger, ID, name, max instances, misfire grace time, and coalesce.
1313
+ - If an event entity has a listener, it is registered using the setListener method.
1314
+ """
1315
+
1316
+ # Helper function to create a job function that calls the reactor with the given command and arguments
1317
+ def create_job_func(cmd, args_list):
1318
+
1319
+ # Returns a lambda that executes the reactor call for the specified command and arguments
1320
+ return lambda: self.__reactor.call(cmd, args_list)
1321
+
1322
+ # Iterate over each event tuple and add the job to the scheduler
1323
+ for signature, entity in events:
1324
+
1325
+ # Add the job to the scheduler with the specified configuration
1326
+ scheduler.add_job(
1327
+ func=create_job_func(signature, list(entity.args)),
1328
+ trigger=entity.trigger,
1329
+ id=signature,
1330
+ name=f"{signature}({', '.join(map(str, entity.args))})",
1331
+ replace_existing=True,
1332
+ max_instances=entity.max_instances,
1333
+ misfire_grace_time=entity.misfire_grace_time,
1334
+ coalesce=entity.coalesce
1335
+ )
1336
+
1337
+ # If the event entity has an associated listener, register it
1338
+ if entity.listener:
1339
+ self.setListener(signature, entity.listener)
1340
+
1221
1341
  def __raiseException(
1222
1342
  self,
1223
1343
  exception: BaseException
@@ -1350,9 +1470,6 @@ class Schedule(ISchedule):
1350
1470
  # Only pause jobs if the scheduler is currently running
1351
1471
  if self.isRunning():
1352
1472
 
1353
- # Clear the set of previously paused jobs to avoid stale entries
1354
- self.__pausedByPauseEverything.clear()
1355
-
1356
1473
  # Retrieve all jobs currently managed by the scheduler
1357
1474
  all_jobs = self.__scheduler.get_jobs()
1358
1475
 
@@ -1360,22 +1477,14 @@ class Schedule(ISchedule):
1360
1477
  for job in all_jobs:
1361
1478
 
1362
1479
  try:
1480
+
1363
1481
  # Get the job ID safely
1364
1482
  job_id = self.__getAttribute(job, 'id', None)
1365
1483
 
1366
1484
  # Skip jobs without a valid user-defined ID (ignore system/operation jobs)
1367
- if not job_id or not isinstance(job_id, str) or job_id.strip() == "" or job_id in self.__operations:
1485
+ if not job_id or not isinstance(job_id, str) or job_id.strip() == "":
1368
1486
  continue
1369
1487
 
1370
- # Pause the job in the scheduler
1371
- self.__scheduler.pause_job(job_id)
1372
-
1373
- # Track the paused job's ID
1374
- self.__pausedByPauseEverything.add(job_id)
1375
-
1376
- # Get the current time in the configured timezone for logging
1377
- now = self.__getCurrentTime()
1378
-
1379
1488
  # Retrieve event data for the paused job
1380
1489
  event_data = self.__getTaskFromSchedulerById(job_id)
1381
1490
 
@@ -1385,9 +1494,6 @@ class Schedule(ISchedule):
1385
1494
  ListeningEvent.JOB_ON_PAUSED
1386
1495
  )
1387
1496
 
1388
- # Log the pause action for this job
1389
- self.__logger.info(f"Task '{job_id}' paused successfully at {now}.")
1390
-
1391
1497
  except Exception as e:
1392
1498
 
1393
1499
  # Handle any errors that occur while pausing a job
@@ -1399,8 +1505,17 @@ class Schedule(ISchedule):
1399
1505
  time=self.__getNow()
1400
1506
  ), ListeningEvent.SCHEDULER_PAUSED)
1401
1507
 
1508
+ # Get the current time in the configured timezone for logging
1509
+ now = self.__getCurrentTime()
1510
+
1511
+ # Clear the set of previously paused jobs to avoid stale entries
1512
+ self.__scheduler.pause()
1513
+
1514
+ # Store the timestamp when the scheduler was paused
1515
+ self.__paused_at = self.__getNow()
1516
+
1402
1517
  # Log that all tasks have been paused
1403
- self.__logger.info("All tasks have been paused.")
1518
+ self.__logger.info(f"Orionis Scheduler paused all tasks successfully at: {now}.")
1404
1519
 
1405
1520
  def resume(
1406
1521
  self
@@ -1429,55 +1544,44 @@ class Schedule(ISchedule):
1429
1544
  """
1430
1545
 
1431
1546
  # Only resume jobs if the scheduler is currently running
1432
- if self.isRunning():
1433
-
1434
- # Resume only jobs that were paused by the pause method
1435
- if self.__pausedByPauseEverything:
1436
-
1437
- # Iterate through the set of paused job IDs and resume each one
1438
- for job_id in self.__pausedByPauseEverything:
1439
-
1440
- try:
1441
-
1442
- # Resume the job in the scheduler
1443
- self.__scheduler.resume_job(job_id)
1444
-
1445
- # Retrieve event data for the resumed job
1446
- event_data = self.__getTaskFromSchedulerById(job_id)
1547
+ if self.isPaused():
1447
1548
 
1448
- # Invoke the listener for the resumed job event
1449
- self.__taskCallableListener(
1450
- event_data,
1451
- ListeningEvent.JOB_ON_RESUMED
1452
- )
1549
+ # Execute the global callable listener after all jobs are resumed
1550
+ self.__globalCallableListener(SchedulerResumed(
1551
+ code=EVENT_SCHEDULER_RESUMED,
1552
+ time=self.__getNow()
1553
+ ), ListeningEvent.SCHEDULER_RESUMED)
1453
1554
 
1454
- # Log an informational message indicating that the job has been resumed
1455
- self.__logger.info(f"Task '{job_id}' has been resumed.")
1555
+ # Get the current time in the configured timezone for logging
1556
+ now = self.__getCurrentTime()
1456
1557
 
1457
- except Exception as e:
1558
+ # Cuando resumes:
1559
+ resumed_at = self.__getNow()
1458
1560
 
1459
- # Handle any errors that occur while resuming a job
1460
- self.__raiseException(e)
1561
+ # Resume main scheduler
1562
+ self.__scheduler.resume()
1461
1563
 
1462
- # Clear the set after resuming all jobs to avoid stale entries
1463
- self.__pausedByPauseEverything.clear()
1564
+ # Iterate through all jobs to check for missed executions
1565
+ for job in self.__scheduler.get_jobs():
1464
1566
 
1465
- # Execute the global callable listener after all jobs are resumed
1466
- self.__globalCallableListener(SchedulerResumed(
1467
- code=EVENT_SCHEDULER_RESUMED,
1468
- time=self.__getNow()
1469
- ), ListeningEvent.SCHEDULER_RESUMED)
1567
+ # If the job was supposed to run while paused, trigger the missed event
1568
+ if job.next_run_time and self.__paused_at <= job.next_run_time < resumed_at:
1470
1569
 
1471
- # Get the current time in the configured timezone for logging
1472
- now = self.__getCurrentTime()
1570
+ # Retrieve event data for the missed job
1571
+ event_data = self.__getTaskFromSchedulerById(job.id)
1473
1572
 
1474
- # Log an informational message indicating that the scheduler has been resumed
1475
- self.__logger.info(f"Orionis Scheduler resumed successfully at {now}.")
1573
+ # Invoke the listener for the missed job event
1574
+ self.__taskCallableListener(
1575
+ event_data,
1576
+ ListeningEvent.JOB_ON_MISSED
1577
+ )
1476
1578
 
1477
- # Log that all previously paused jobs have been resumed
1478
- self.__logger.info("All previously task have been resumed.")
1579
+ # Log an informational message indicating that the scheduler has been resumed
1580
+ self.__logger.info(f"Orionis Scheduler resumed all tasks successfully at: {now}.")
1479
1581
 
1480
- async def start(self) -> None:
1582
+ async def start(
1583
+ self
1584
+ ) -> None:
1481
1585
  """
1482
1586
  Start the AsyncIO scheduler instance and keep it running.
1483
1587
 
@@ -1514,6 +1618,7 @@ class Schedule(ISchedule):
1514
1618
  # Start the scheduler if it is not already running
1515
1619
  if not self.isRunning():
1516
1620
  self.__scheduler.start()
1621
+ self.__control_scheduler.start()
1517
1622
 
1518
1623
  # Log that the scheduler is now active and waiting for events
1519
1624
  self.__logger.info("Orionis Scheduler is now active and waiting for events...")
@@ -1553,7 +1658,10 @@ class Schedule(ISchedule):
1553
1658
  # Raise a runtime error for any other issues during startup
1554
1659
  raise CLIOrionisRuntimeError(f"Failed to start the scheduler: {str(e)}") from e
1555
1660
 
1556
- async def shutdown(self, wait: bool = True) -> None:
1661
+ async def shutdown(
1662
+ self,
1663
+ wait: bool = True
1664
+ ) -> None:
1557
1665
  """
1558
1666
  Shut down the AsyncIO scheduler instance asynchronously.
1559
1667
 
@@ -1587,15 +1695,13 @@ class Schedule(ISchedule):
1587
1695
 
1588
1696
  # If the scheduler is not running, there's nothing to shut down
1589
1697
  if not self.isRunning():
1590
- self.__logger.info("The scheduler is already stopped. No shutdown action is required.")
1591
1698
  return
1592
1699
 
1593
1700
  try:
1594
- # Log the shutdown process
1595
- self.__logger.info(f"Starting Orionis Scheduler shutdown process (wait={wait})...")
1596
1701
 
1597
1702
  # Shut down the AsyncIOScheduler
1598
1703
  self.__scheduler.shutdown(wait=wait)
1704
+ self.__control_scheduler.shutdown(wait=wait)
1599
1705
 
1600
1706
  # Signal the stop event to break the wait in start()
1601
1707
  if self._stop_event and not self._stop_event.is_set():
@@ -1605,10 +1711,14 @@ class Schedule(ISchedule):
1605
1711
  if wait:
1606
1712
  await asyncio.sleep(0.1)
1607
1713
 
1714
+ # Get the current time in the configured timezone for logging
1715
+ now = self.__getCurrentTime()
1716
+
1608
1717
  # Log the successful shutdown
1609
- self.__logger.info("Orionis Scheduler has been shut down successfully.")
1718
+ self.__logger.info(f"Orionis Scheduler has been shut down successfully at: {now}.")
1610
1719
 
1611
1720
  except Exception as e:
1721
+
1612
1722
  # Handle exceptions that may occur during shutdown
1613
1723
  self.__raiseException(
1614
1724
  CLIOrionisRuntimeError(
@@ -1616,7 +1726,10 @@ class Schedule(ISchedule):
1616
1726
  )
1617
1727
  )
1618
1728
 
1619
- def pauseTask(self, signature: str) -> bool:
1729
+ def pauseCommand(
1730
+ self,
1731
+ signature: str
1732
+ ) -> bool:
1620
1733
  """
1621
1734
  Pause a scheduled job in the AsyncIO scheduler.
1622
1735
 
@@ -1652,9 +1765,19 @@ class Schedule(ISchedule):
1652
1765
  self.__raiseException(CLIOrionisValueError(self.SIGNATURE_REQUIRED_ERROR))
1653
1766
 
1654
1767
  try:
1768
+
1655
1769
  # Attempt to pause the job with the given signature
1656
1770
  self.__scheduler.pause_job(signature)
1657
1771
 
1772
+ # Retrieve event data for the paused job
1773
+ event_data = self.__getTaskFromSchedulerById(signature)
1774
+
1775
+ # Invoke the listener for the paused job event
1776
+ self.__taskCallableListener(
1777
+ event_data,
1778
+ ListeningEvent.JOB_ON_PAUSED
1779
+ )
1780
+
1658
1781
  # Log the successful pausing of the job
1659
1782
  self.__logger.info(f"Pause '{signature}' has been paused.")
1660
1783
 
@@ -1666,7 +1789,10 @@ class Schedule(ISchedule):
1666
1789
  # Return False if the job could not be paused (e.g., it does not exist or another error occurred)
1667
1790
  return False
1668
1791
 
1669
- def resumeTask(self, signature: str) -> bool:
1792
+ def resumeCommand(
1793
+ self,
1794
+ signature: str
1795
+ ) -> bool:
1670
1796
  """
1671
1797
  Resume a paused job in the AsyncIO scheduler.
1672
1798
 
@@ -1706,6 +1832,15 @@ class Schedule(ISchedule):
1706
1832
  # Attempt to resume the job with the given signature
1707
1833
  self.__scheduler.resume_job(signature)
1708
1834
 
1835
+ # Retrieve event data for the resumed job
1836
+ event_data = self.__getTaskFromSchedulerById(signature)
1837
+
1838
+ # Invoke the listener for the resumed job event
1839
+ self.__taskCallableListener(
1840
+ event_data,
1841
+ ListeningEvent.JOB_ON_RESUMED
1842
+ )
1843
+
1709
1844
  # Log the successful resumption of the job
1710
1845
  self.__logger.info(f"Task '{signature}' has been resumed.")
1711
1846
 
@@ -1717,7 +1852,10 @@ class Schedule(ISchedule):
1717
1852
  # Return False if the job could not be resumed (e.g., it does not exist or another error occurred)
1718
1853
  return False
1719
1854
 
1720
- def removeTask(self, signature: str) -> bool:
1855
+ def removeCommand(
1856
+ self,
1857
+ signature: str
1858
+ ) -> bool:
1721
1859
  """
1722
1860
  Remove a scheduled job from the AsyncIO scheduler by its signature.
1723
1861
 
@@ -1775,7 +1913,9 @@ class Schedule(ISchedule):
1775
1913
  # Return False if the job could not be removed (e.g., it does not exist or another error occurred)
1776
1914
  return False
1777
1915
 
1778
- def events(self) -> List[Dict]:
1916
+ def events(
1917
+ self
1918
+ ) -> List[Dict]:
1779
1919
  """
1780
1920
  Retrieve a list of all scheduled jobs managed by the scheduler.
1781
1921
 
@@ -1909,7 +2049,9 @@ class Schedule(ISchedule):
1909
2049
  # Return None if no job with the given signature is found
1910
2050
  return None
1911
2051
 
1912
- def isRunning(self) -> bool:
2052
+ def isRunning(
2053
+ self
2054
+ ) -> bool:
1913
2055
  """
1914
2056
  Check if the scheduler is currently running.
1915
2057
 
@@ -1924,9 +2066,11 @@ class Schedule(ISchedule):
1924
2066
  """
1925
2067
 
1926
2068
  # Return True if the scheduler is running, otherwise False
1927
- return self.__scheduler.running
2069
+ return self.__scheduler.running and self.__scheduler.state == STATE_RUNNING
1928
2070
 
1929
- def isPaused(self) -> bool:
2071
+ def isPaused(
2072
+ self
2073
+ ) -> bool:
1930
2074
  """
1931
2075
  Check if the scheduler is currently paused.
1932
2076
 
@@ -1942,9 +2086,11 @@ class Schedule(ISchedule):
1942
2086
  """
1943
2087
 
1944
2088
  # The scheduler is considered paused if there are any jobs in the paused set
1945
- return len(self.__pausedByPauseEverything) > 0
2089
+ return self.__scheduler.running and self.__scheduler.state == STATE_PAUSED
1946
2090
 
1947
- def forceStop(self) -> None:
2091
+ def forceStop(
2092
+ self
2093
+ ) -> None:
1948
2094
  """
1949
2095
  Forcefully stop the scheduler immediately, bypassing graceful shutdown.
1950
2096
 
@@ -1969,12 +2115,15 @@ class Schedule(ISchedule):
1969
2115
  # If the scheduler is currently running, shut it down immediately without waiting for jobs to finish
1970
2116
  if self.__scheduler.running:
1971
2117
  self.__scheduler.shutdown(wait=False)
2118
+ self.__control_scheduler.shutdown(wait=False)
1972
2119
 
1973
2120
  # If the stop event exists and has not already been set, signal it to interrupt the main loop
1974
2121
  if self._stop_event and not self._stop_event.is_set():
1975
2122
  self._stop_event.set()
1976
2123
 
1977
- def stop(self) -> None:
2124
+ def stop(
2125
+ self
2126
+ ) -> None:
1978
2127
  """
1979
2128
  Signal the scheduler to stop synchronously by setting the internal stop event.
1980
2129
 
@@ -6,7 +6,7 @@
6
6
  NAME = "orionis"
7
7
 
8
8
  # Current version of the framework
9
- VERSION = "0.725.0"
9
+ VERSION = "0.727.0"
10
10
 
11
11
  # Full name of the author or maintainer of the project
12
12
  AUTHOR = "Raul Mauricio Uñate Castro"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orionis
3
- Version: 0.725.0
3
+ Version: 0.727.0
4
4
  Summary: Orionis Framework – Elegant, Fast, and Powerful.
5
5
  Home-page: https://github.com/orionis-framework/framework
6
6
  Author: Raul Mauricio Uñate Castro
@@ -15,7 +15,7 @@ orionis/console/commands/help.py,sha256=VFIn3UqQm4pxwjfSQohVwKNpc7F-6XRcwSZQwDSL
15
15
  orionis/console/commands/log_clear.py,sha256=OI1j_myCYUOMI-SfnN-NH-6BYzzWKXOKIEb55vFTXq4,4045
16
16
  orionis/console/commands/make_command.py,sha256=WHE2J78fuY9RHScCOn9R_2KjkiayYJHFlm-Mp-k8X-o,5956
17
17
  orionis/console/commands/scheduler_list.py,sha256=7Ug0eoqusIOsmHaiakm3-JYAK8VNIypOtKMcSzn-AL0,4544
18
- orionis/console/commands/scheduler_work.py,sha256=NV-WhJwY4Lv_x6UO2FcWikA1HFFK-PmJWm-oHQPs0fg,5573
18
+ orionis/console/commands/scheduler_work.py,sha256=dDW9XTjGb-5ABuhPYjfd1rfO7xLDhgYu5nd6nToiYFE,5597
19
19
  orionis/console/commands/test.py,sha256=G3pveONRbeWTcVwaHat3wTAhxMm4g_BwhJhIejq6V0Y,3518
20
20
  orionis/console/commands/version.py,sha256=u5_8CfnEVdS3VSME8rbP6o3Z0XFZ30nSz8uHdahBAoY,4766
21
21
  orionis/console/contracts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -25,12 +25,12 @@ orionis/console/contracts/cli_request.py,sha256=Q5eiOitdZve-33e5dqO_O-Fha5I1u4u7
25
25
  orionis/console/contracts/command.py,sha256=Wvm62hw2gDIPNmoEUFyhKUERjZ6wnAbI-UDY_5gJE24,3626
26
26
  orionis/console/contracts/console.py,sha256=TuoyCLInhp5ztlyJ82rBsw_1Ki3_fOnF_gtaWUxSW1M,14253
27
27
  orionis/console/contracts/dumper.py,sha256=h8ScHF31Q3oOdEe5_EyozOD8OGcLvkMG4KkJwa3ANX8,4538
28
- orionis/console/contracts/event.py,sha256=KQESAycPzBE3xoDTnbYLz_XXg-7dwrybYKbaJjgkCMY,119842
28
+ orionis/console/contracts/event.py,sha256=cdnao4tqVhIr24X1uFzFfzZ4wdN1OZcKS1sKcX33abU,119843
29
29
  orionis/console/contracts/executor.py,sha256=JAzK64_5HfIGm3_BwsUv7-ZeC2Y-3mpxOByHaXwuy9A,4278
30
30
  orionis/console/contracts/kernel.py,sha256=mh4LlhEYHh3FuGZZQ0GBhD6ZLa5YQvaNj2r01IIHI5Y,826
31
31
  orionis/console/contracts/progress_bar.py,sha256=NYebL2h-vg2t2H6IhJjuC37gglRkpT-MW71wbJtpLNg,1784
32
32
  orionis/console/contracts/reactor.py,sha256=iT6ShoCutAWEeJzOf_PK7CGXi9TgrOD5tewHFVQ2NQw,6075
33
- orionis/console/contracts/schedule.py,sha256=xtXgp4BPhvhg3YSM4mrIdbyoBdr4OJBi1gBM_kJN5UQ,13694
33
+ orionis/console/contracts/schedule.py,sha256=NaVOUpuE08X5rm9-oXyNs6r3LbOg7B_rIz-k4r7y4Qk,13703
34
34
  orionis/console/contracts/schedule_event_listener.py,sha256=h06qsBxuEMD3KLSyu0JXdUDHlQW19BX9lA09Qrh2QXg,3818
35
35
  orionis/console/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
36
  orionis/console/core/reactor.py,sha256=qctLns-f5eB9Air7Qi4hvX5KB5A7SsHeV8M5zYJEiPA,44286
@@ -40,7 +40,7 @@ orionis/console/dynamic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
40
40
  orionis/console/dynamic/progress_bar.py,sha256=58wne7E_QZb_uN9jjqQ_V28Ci19SVNhCQQ4zzXCiOu0,2872
41
41
  orionis/console/entities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
42
  orionis/console/entities/command.py,sha256=NPdyG4U3JtDMOS7Em2_AklNyyuoTnKxyGseMWrADlc4,1571
43
- orionis/console/entities/event.py,sha256=44exJIBMB8v_Dixv2qgatl2ln-Tmv7g2b63oetsJFHc,3015
43
+ orionis/console/entities/event.py,sha256=ri7BVjBhpPlxUt-gmJObuhB2gqVQ-vTXUyPy2iv2Aj0,3101
44
44
  orionis/console/entities/event_job.py,sha256=b8fbloHnf7qYtQ0W4d7MWw0jj4l07B0RtS3UkMJwE0M,4292
45
45
  orionis/console/entities/scheduler_error.py,sha256=VzwSK8DloX-tFlBLWX93sEV4oO0Qo9rheNjiOEjdNyE,1413
46
46
  orionis/console/entities/scheduler_event_data.py,sha256=s9zYvLYO4O91l_9Qncx688QjcsjrIjXI8xsXqK2ZlHI,785
@@ -56,7 +56,7 @@ orionis/console/exceptions/__init__.py,sha256=rY9PE85TO_HpI5cfGWbZSDzk5aOUjErqBZ
56
56
  orionis/console/exceptions/cli_exceptions.py,sha256=nk3EGkRheDbw8vrxKgUxkAAPA6gPpO2dE5nDeVHJHA0,3580
57
57
  orionis/console/fluent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
58
  orionis/console/fluent/command.py,sha256=0jyhB45LIjDS7nkjNhkkvEXEzdOBmT49qKIooYY_DbI,8261
59
- orionis/console/fluent/event.py,sha256=N1MqKdXwmsHx3KES07DVCLVrdwaIMWYAtzUzQmsfsAI,166739
59
+ orionis/console/fluent/event.py,sha256=lw0lYRhHxyflA7JE4ZLye6rjEOGUmLFMO9e9btwWfNM,168532
60
60
  orionis/console/output/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
61
  orionis/console/output/console.py,sha256=xRZXSZiVqlyc1L16MBUV1M3TUHQwrWSFYUtisivm8U8,24423
62
62
  orionis/console/output/executor.py,sha256=uQjFPOlyZLFj9pcyYPugCqxwJog0AJgK1OcmQH2ELbw,7314
@@ -65,7 +65,7 @@ orionis/console/request/cli_request.py,sha256=sH7Q2MpMIasiPiEPBeGhExnbfpSic98vQd
65
65
  orionis/console/stubs/command.stub,sha256=ABQ2eFxJ0u0DvTfmoO_DdECkBy-rie-woG62G2Gxw8Y,805
66
66
  orionis/console/stubs/listener.stub,sha256=DbX-ghx2-vb73kzQ6L20lyg5vSKn58jSLTwFuwNQU9k,4331
67
67
  orionis/console/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
- orionis/console/tasks/schedule.py,sha256=yhNkOt7uDPSqc7lEWozChBx2ua6hzyx0WV7OpHp_QoU,88121
68
+ orionis/console/tasks/schedule.py,sha256=R2mkjp7zFSnhlaBWDrdzcQsHmj8cdOUNyx2_73tzWx0,92762
69
69
  orionis/container/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
70
  orionis/container/container.py,sha256=LaGFSzDH2YjmWzdiV-r8Z9xs9fyFGJnsanRsEStqHp8,114033
71
71
  orionis/container/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -207,7 +207,7 @@ orionis/foundation/providers/scheduler_provider.py,sha256=IrPQJwvQVLRm5Qnz0Cxon4
207
207
  orionis/foundation/providers/testing_provider.py,sha256=eI1p2lUlxl25b5Z487O4nmqLE31CTDb4c3Q21xFadkE,1615
208
208
  orionis/foundation/providers/workers_provider.py,sha256=GdHENYV_yGyqmHJHn0DCyWmWId5xWjD48e6Zq2PGCWY,1674
209
209
  orionis/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
210
- orionis/metadata/framework.py,sha256=qSqfkPJcUrvfrYF3lGeuXVg380szqejJ14UdE6v8PO8,4720
210
+ orionis/metadata/framework.py,sha256=nfWN_AFm6d2eQFvlJzxl1uswTO-wwXT7brAA27gKiEw,4720
211
211
  orionis/metadata/package.py,sha256=s1JeGJPwdVh4jO3IOfmpwMuJ_oX6Vf9NL7jgPEQNf5Y,16050
212
212
  orionis/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
213
213
  orionis/services/asynchrony/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -402,8 +402,8 @@ orionis/test/validators/workers.py,sha256=HcZ3cnrk6u7cvM1xZpn_lsglHAq69_jx9RcTSv
402
402
  orionis/test/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
403
403
  orionis/test/view/render.py,sha256=arysoswhkV2vUd2aVMZRPpmH317jaWbgjDpQ_AWQ5AE,5663
404
404
  orionis/test/view/report.stub,sha256=QLqqCdRoENr3ECiritRB3DO_MOjRQvgBh5jxZ3Hs1r0,28189
405
- orionis-0.725.0.dist-info/licenses/LICENCE,sha256=JhC-z_9mbpUrCfPjcl3DhDA8trNDMzb57cvRSam1avc,1463
406
- orionis-0.725.0.dist-info/METADATA,sha256=cqAlQIy-FyztgUfLFmgGSXvhkTvWUYI5PBShmSVZyo4,4962
407
- orionis-0.725.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
408
- orionis-0.725.0.dist-info/top_level.txt,sha256=lyXi6jArpqJ-0zzNqd_uwsH-z9TCEBVBL-pC3Ekv7hU,8
409
- orionis-0.725.0.dist-info/RECORD,,
405
+ orionis-0.727.0.dist-info/licenses/LICENCE,sha256=JhC-z_9mbpUrCfPjcl3DhDA8trNDMzb57cvRSam1avc,1463
406
+ orionis-0.727.0.dist-info/METADATA,sha256=SqjKGk5oqSuIgDXwexcEWVR_ie_IARUEVjM9OgmCsus,4962
407
+ orionis-0.727.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
408
+ orionis-0.727.0.dist-info/top_level.txt,sha256=lyXi6jArpqJ-0zzNqd_uwsH-z9TCEBVBL-pC3Ekv7hU,8
409
+ orionis-0.727.0.dist-info/RECORD,,