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.
@@ -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
- # Check if the scheduler has specific pause, resume, and finalize times
114
- if hasattr(scheduler, "PAUSE_AT") and scheduler.PAUSE_AT is not None:
115
- if not isinstance(scheduler.PAUSE_AT, datetime):
116
- raise CLIOrionisRuntimeError("PAUSE_AT must be a datetime instance.")
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.RESUME_AT)
121
+ schedule_service.resumeEverythingAt(scheduler.resumeAt())
123
122
 
124
- if hasattr(scheduler, "FINALIZE_AT") and scheduler.FINALIZE_AT is not None:
125
- if not isinstance(scheduler.FINALIZE_AT, datetime):
126
- raise CLIOrionisRuntimeError("FINALIZE_AT must be a datetime instance.")
127
- schedule_service.shutdownEverythingAt(scheduler.FINALIZE_AT)
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.__scheduler.running:
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
- # Store the command and its arguments for scheduling
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
- # If no event loop is running, create a new one
381
- asyncio.run(listener(event_data, self))
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
- # Log any exceptions that occur during listener invocation
389
- self.__logger.error(f"Error invoking global listener for event '{scheduler_event}': {str(e)}")
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
- # Schedule the coroutine listener method as an asyncio task
458
- asyncio.create_task(listener_method(event_data, self))
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
- # Log any exceptions that occur during listener invocation
464
- self.__logger.error(f"Error invoking listener method '{scheduler_event}' for job '{event_data.job_id}': {str(e)}")
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
- # Convert the event to its entity representation
903
- entity = event.toEntity()
904
-
905
- # Add the job to the internal jobs list
906
- self.__jobs.append(entity)
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
- # Create a unique key for the job based on its signature
909
- def create_job_func(cmd, args_list):
910
- return lambda: self.__reactor.call(cmd, args_list)
957
+ # If a listener is associated with the event, register it
958
+ if entity.listener:
959
+ self.setListener(signature, entity.listener)
911
960
 
912
- self.__scheduler.add_job(
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
- # If a listener is associated with the event, register it
921
- if entity.listener:
922
- self.setListener(signature, entity.listener)
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 allows you to schedule a job that will pause the AsyncIOScheduler
982
- at the specified datetime. The job is added to the scheduler with a 'date'
983
- trigger, ensuring it executes exactly at the given time.
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
- # Add a job to the scheduler to pause it at the specified datetime
1008
- self.__scheduler.add_job(
1009
- func=self.__scheduler.pause, # Function to pause the scheduler
1010
- trigger=DateTrigger(run_date=at), # Trigger type is 'date' for one-time execution
1011
- id=ListeningEvent.SCHEDULER_PAUSED.value, # Unique job ID for pausing the scheduler
1012
- name=ListeningEvent.SCHEDULER_PAUSED.value, # Descriptive name for the job
1013
- replace_existing=True # Replace any existing job with the same ID
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 allows you to schedule a job that will resume the AsyncIOScheduler
1024
- at the specified datetime. The job is added to the scheduler with a 'date'
1025
- trigger, ensuring it executes exactly at the given time.
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
- # Add a job to the scheduler to resume it at the specified datetime
1050
- self.__scheduler.add_job(
1051
- func=self.__scheduler.resume, # Function to resume the scheduler
1052
- trigger=DateTrigger(run_date=at), # Trigger type is 'date' for one-time execution
1053
- id=ListeningEvent.SCHEDULER_RESUMED.value, # Unique job ID for resuming the scheduler
1054
- name=ListeningEvent.SCHEDULER_RESUMED.value, # Descriptive name for the job
1055
- replace_existing=True # Replace any existing job with the same ID
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 allows you to schedule a job that will shut down the AsyncIOScheduler
1066
- at the specified datetime. The job is added to the scheduler with a 'date'
1067
- trigger, ensuring it executes exactly at the given time.
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
- # Add a job to the scheduler to shut it down at the specified datetime
1092
- self.__scheduler.add_job(
1093
- func=self.__scheduler.shutdown, # Function to shut down the scheduler
1094
- trigger=DateTrigger(run_date=at), # Trigger type is 'date' for one-time execution
1095
- id=ListeningEvent.SCHEDULER_SHUTDOWN.value, # Unique job ID for shutting down the scheduler
1096
- name=ListeningEvent.SCHEDULER_SHUTDOWN.value, # Descriptive name for the job
1097
- replace_existing=True # Replace any existing job with the same ID
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 initiates the AsyncIOScheduler which integrates with asyncio event loops
1105
- for asynchronous job execution. It ensures the scheduler starts properly within
1106
- an asyncio context and maintains the event loop active to process scheduled jobs.
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 and keeps it running.
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
- # Start the AsyncIOScheduler to handle asynchronous jobs.
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 we're in an asyncio context
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
- # Ensure all events are loaded into the internal jobs list
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.__scheduler.running:
1258
+ # Start the scheduler if it is not already running
1259
+ if not self.isRunning():
1128
1260
  self.__scheduler.start()
1129
1261
 
1130
- # Keep the event loop alive to process scheduled jobs
1131
- try:
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
- # Run indefinitely until interrupted
1134
- while self.__scheduler.running:
1135
- await asyncio.sleep(1)
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
- await self.shutdown()
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
- # Handle exceptions that may occur during scheduler startup
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 that manages asynchronous job execution.
1153
- It ensures proper cleanup in asyncio environments and allows for an optional wait period
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, the method waits until all currently executing jobs are completed before shutting down the scheduler.
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
- # Ensure the 'wait' parameter is a boolean value.
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 is nothing to shut down.
1180
- if not self.__scheduler.running:
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
- # Shut down the AsyncIOScheduler. If 'wait' is True, it waits for currently executing jobs to finish.
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
- # If 'wait' is True, allow a small delay to ensure proper cleanup of resources.
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
- # Raise a runtime error if the shutdown process fails.
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['signature'] == signature:
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()
@@ -5,7 +5,7 @@
5
5
  NAME = "orionis"
6
6
 
7
7
  # Current version of the framework
8
- VERSION = "0.520.0"
8
+ VERSION = "0.522.0"
9
9
 
10
10
  # Full name of the author or maintainer of the project
11
11
  AUTHOR = "Raul Mauricio Uñate Castro"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orionis
3
- Version: 0.520.0
3
+ Version: 0.522.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
@@ -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=w86p-4KjfMqMcGlQBsmiBASpzv33M-PWLbgYza7Um9g,8030
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=yHTbnDH1frAmyvPaUgn0a5q34Eym9QYMXdqYZWwodFs,6336
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=y6fnvxofKw_sd-Br2ICzsvinCyV5HDLygLJtDlyTCLw,58292
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=9ERo-wA31Fhf-QLk4fPAYjSxTyE4FM6xHG0dXUxPL_0,4109
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.520.0.dist-info/licenses/LICENCE,sha256=JhC-z_9mbpUrCfPjcl3DhDA8trNDMzb57cvRSam1avc,1463
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.520.0.dist-info/METADATA,sha256=UAhnPWMKxyQiTmmFb6zL_34hsjfhmzdPIjOrXhmJDYM,4801
565
- orionis-0.520.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
566
- orionis-0.520.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
567
- orionis-0.520.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
568
- orionis-0.520.0.dist-info/RECORD,,
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,,