Skip to content

Commit 169d97b

Browse files
committed
Remove methods that were moved to client repo
* The `running` property was renamed to `started` * The running state was removed Signed-off-by: Mathias L. Baumann <[email protected]>
1 parent 21e7591 commit 169d97b

File tree

8 files changed

+100
-285
lines changed

8 files changed

+100
-285
lines changed

README.md

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The [`Dispatcher` class](https://frequenz-floss.github.io/frequenz-dispatch-pyth
2121

2222
```python
2323
import os
24-
from frequenz.dispatch import Dispatcher, RunningState
24+
from frequenz.dispatch import Dispatcher
2525
from unittest.mock import MagicMock
2626

2727
async def run():
@@ -42,29 +42,29 @@ async def run():
4242
changed_running_status_rx = dispatcher.running_status_change.new_receiver()
4343

4444
async for dispatch in changed_running_status_rx:
45-
match dispatch.running("DEMO_TYPE"):
46-
case RunningState.RUNNING:
47-
print(f"Executing dispatch {dispatch.id}, due on {dispatch.start_time}")
48-
if actor.is_running:
49-
actor.reconfigure(
50-
components=dispatch.target,
51-
run_parameters=dispatch.payload, # custom actor parameters
52-
dry_run=dispatch.dry_run,
53-
until=dispatch.until,
54-
) # this will reconfigure the actor
55-
else:
56-
# this will start a new actor with the given components
57-
# and run it for the duration of the dispatch
58-
actor.start(
59-
components=dispatch.target,
60-
run_parameters=dispatch.payload, # custom actor parameters
61-
dry_run=dispatch.dry_run,
62-
until=dispatch.until,
63-
)
64-
case RunningState.STOPPED:
65-
actor.stop() # this will stop the actor
66-
case RunningState.DIFFERENT_TYPE:
67-
pass # dispatch not for this type
45+
if dispatch.type != "MY_TYPE":
46+
continue
47+
48+
if dispatch.started:
49+
print(f"Executing dispatch {dispatch.id}, due on {dispatch.start_time}")
50+
if actor.is_running:
51+
actor.reconfigure(
52+
components=dispatch.target,
53+
run_parameters=dispatch.payload, # custom actor parameters
54+
dry_run=dispatch.dry_run,
55+
until=dispatch.until,
56+
) # this will reconfigure the actor
57+
else:
58+
# this will start a new actor with the given components
59+
# and run it for the duration of the dispatch
60+
actor.start(
61+
components=dispatch.target,
62+
run_parameters=dispatch.payload, # custom actor parameters
63+
dry_run=dispatch.dry_run,
64+
until=dispatch.until,
65+
)
66+
else:
67+
actor.stop() # this will stop the actor
6868
```
6969

7070
## Supported Platforms

RELEASE_NOTES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
## Upgrading
88

9-
<!-- Here goes notes on how to upgrade from previous versions, including deprecations and what they should be replaced with -->
9+
* The method `Dispatch.running(type: str)` was replaced with the property `Dispatch.started: bool`.
1010

1111
## New Features
1212

src/frequenz/dispatch/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
1616
"""
1717

18-
from ._dispatch import Dispatch, RunningState
18+
from ._dispatch import Dispatch
1919
from ._dispatcher import Dispatcher, ReceiverFetcher
2020
from ._event import Created, Deleted, DispatchEvent, Updated
2121
from ._managing_actor import DispatchManagingActor, DispatchUpdate
@@ -28,7 +28,6 @@
2828
"ReceiverFetcher",
2929
"Updated",
3030
"Dispatch",
31-
"RunningState",
3231
"DispatchManagingActor",
3332
"DispatchUpdate",
3433
]

src/frequenz/dispatch/_dispatch.py

Lines changed: 13 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,12 @@
33

44
"""Dispatch type with support for next_run calculation."""
55

6-
7-
import logging
86
from dataclasses import dataclass
97
from datetime import datetime, timezone
10-
from enum import Enum
11-
from typing import Iterator, cast
8+
from typing import Iterator
129

13-
from dateutil import rrule
14-
from frequenz.client.dispatch.recurrence import Frequency, Weekday
1510
from frequenz.client.dispatch.types import Dispatch as BaseDispatch
1611

17-
_logger = logging.getLogger(__name__)
18-
"""The logger for this module."""
19-
20-
_RRULE_FREQ_MAP = {
21-
Frequency.MINUTELY: rrule.MINUTELY,
22-
Frequency.HOURLY: rrule.HOURLY,
23-
Frequency.DAILY: rrule.DAILY,
24-
Frequency.WEEKLY: rrule.WEEKLY,
25-
Frequency.MONTHLY: rrule.MONTHLY,
26-
}
27-
"""To map from our Frequency enum to the dateutil library enum."""
28-
29-
_RRULE_WEEKDAY_MAP = {
30-
Weekday.MONDAY: rrule.MO,
31-
Weekday.TUESDAY: rrule.TU,
32-
Weekday.WEDNESDAY: rrule.WE,
33-
Weekday.THURSDAY: rrule.TH,
34-
Weekday.FRIDAY: rrule.FR,
35-
Weekday.SATURDAY: rrule.SA,
36-
Weekday.SUNDAY: rrule.SU,
37-
}
38-
"""To map from our Weekday enum to the dateutil library enum."""
39-
40-
41-
class RunningState(Enum):
42-
"""The running state of a dispatch."""
43-
44-
RUNNING = "RUNNING"
45-
"""The dispatch is running."""
46-
47-
STOPPED = "STOPPED"
48-
"""The dispatch is stopped."""
49-
50-
DIFFERENT_TYPE = "DIFFERENT_TYPE"
51-
"""The dispatch is for a different type."""
52-
5312

5413
@dataclass(frozen=True)
5514
class Dispatch(BaseDispatch):
@@ -87,6 +46,18 @@ def _set_deleted(self) -> None:
8746
"""Mark the dispatch as deleted."""
8847
object.__setattr__(self, "deleted", True)
8948

49+
@property
50+
def started(self) -> bool:
51+
"""Check if the dispatch is started.
52+
53+
Returns:
54+
True if the dispatch is started, False otherwise.
55+
"""
56+
if self.deleted:
57+
return False
58+
59+
return super().started
60+
9061
@property
9162
def _running_status_notified(self) -> bool:
9263
"""Check that the latest running state change notification was sent.
@@ -100,52 +71,6 @@ def _set_running_status_notified(self) -> None:
10071
"""Mark the latest running state change notification as sent."""
10172
object.__setattr__(self, "running_state_change_synced", self.update_time)
10273

103-
def running(self, type_: str) -> RunningState:
104-
"""Check if the dispatch is currently supposed to be running.
105-
106-
Args:
107-
type_: The type of the dispatch that should be running.
108-
109-
Returns:
110-
RUNNING if the dispatch is running,
111-
STOPPED if it is stopped,
112-
DIFFERENT_TYPE if it is for a different type.
113-
"""
114-
if self.type != type_:
115-
return RunningState.DIFFERENT_TYPE
116-
117-
if not self.active or self.deleted:
118-
return RunningState.STOPPED
119-
120-
now = datetime.now(tz=timezone.utc)
121-
122-
if now < self.start_time:
123-
return RunningState.STOPPED
124-
# A dispatch without duration is always running once it started
125-
if self.duration is None:
126-
return RunningState.RUNNING
127-
128-
if until := self._until(now):
129-
return RunningState.RUNNING if now < until else RunningState.STOPPED
130-
131-
return RunningState.STOPPED
132-
133-
@property
134-
def until(self) -> datetime | None:
135-
"""Time when the dispatch should end.
136-
137-
Returns the time that a running dispatch should end.
138-
If the dispatch is not running, None is returned.
139-
140-
Returns:
141-
The time when the dispatch should end or None if the dispatch is not running.
142-
"""
143-
if not self.active or self.deleted:
144-
return None
145-
146-
now = datetime.now(tz=timezone.utc)
147-
return self._until(now)
148-
14974
@property
15075
# noqa is needed because of a bug in pydoclint that makes it think a `return` without a return
15176
# value needs documenting
@@ -170,103 +95,3 @@ def missed_runs(self) -> Iterator[datetime]: # noqa: DOC405
17095
while (next_run := self.next_run_after(from_time)) and next_run < now:
17196
yield next_run
17297
from_time = next_run
173-
174-
@property
175-
def next_run(self) -> datetime | None:
176-
"""Calculate the next run of a dispatch.
177-
178-
Returns:
179-
The next run of the dispatch or None if the dispatch is finished.
180-
"""
181-
return self.next_run_after(datetime.now(tz=timezone.utc))
182-
183-
def next_run_after(self, after: datetime) -> datetime | None:
184-
"""Calculate the next run of a dispatch.
185-
186-
Args:
187-
after: The time to calculate the next run from.
188-
189-
Returns:
190-
The next run of the dispatch or None if the dispatch is finished.
191-
"""
192-
if (
193-
not self.recurrence.frequency
194-
or self.recurrence.frequency == Frequency.UNSPECIFIED
195-
or self.duration is None # Infinite duration
196-
):
197-
if after > self.start_time:
198-
return None
199-
return self.start_time
200-
201-
# Make sure no weekday is UNSPECIFIED
202-
if Weekday.UNSPECIFIED in self.recurrence.byweekdays:
203-
_logger.warning("Dispatch %s has UNSPECIFIED weekday, ignoring...", self.id)
204-
return None
205-
206-
# No type information for rrule, so we need to cast
207-
return cast(datetime | None, self._prepare_rrule().after(after, inc=True))
208-
209-
def _prepare_rrule(self) -> rrule.rrule:
210-
"""Prepare the rrule object.
211-
212-
Returns:
213-
The rrule object.
214-
215-
Raises:
216-
ValueError: If the interval is invalid.
217-
"""
218-
count, until = (None, None)
219-
if end := self.recurrence.end_criteria:
220-
count = end.count
221-
until = end.until
222-
223-
if self.recurrence.interval is None or self.recurrence.interval < 1:
224-
raise ValueError("Interval must be at least 1")
225-
226-
rrule_obj = rrule.rrule(
227-
freq=_RRULE_FREQ_MAP[self.recurrence.frequency],
228-
dtstart=self.start_time,
229-
count=count,
230-
until=until,
231-
byminute=self.recurrence.byminutes or None,
232-
byhour=self.recurrence.byhours or None,
233-
byweekday=[
234-
_RRULE_WEEKDAY_MAP[weekday] for weekday in self.recurrence.byweekdays
235-
]
236-
or None,
237-
bymonthday=self.recurrence.bymonthdays or None,
238-
bymonth=self.recurrence.bymonths or None,
239-
interval=self.recurrence.interval,
240-
)
241-
242-
return rrule_obj
243-
244-
def _until(self, now: datetime) -> datetime | None:
245-
"""Calculate the time when the dispatch should end.
246-
247-
If no previous run is found, None is returned.
248-
249-
Args:
250-
now: The current time.
251-
252-
Returns:
253-
The time when the dispatch should end or None if the dispatch is not running.
254-
255-
Raises:
256-
ValueError: If the dispatch has no duration.
257-
"""
258-
if self.duration is None:
259-
raise ValueError("_until: Dispatch has no duration")
260-
261-
if (
262-
not self.recurrence.frequency
263-
or self.recurrence.frequency == Frequency.UNSPECIFIED
264-
):
265-
return self.start_time + self.duration
266-
267-
latest_past_start: datetime | None = self._prepare_rrule().before(now, inc=True)
268-
269-
if not latest_past_start:
270-
return None
271-
272-
return latest_past_start + self.duration

src/frequenz/dispatch/_dispatcher.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class Dispatcher:
5454
Example: Processing running state change dispatches
5555
```python
5656
import os
57-
from frequenz.dispatch import Dispatcher, RunningState
57+
from frequenz.dispatch import Dispatcher
5858
from unittest.mock import MagicMock
5959
6060
async def run():
@@ -75,29 +75,29 @@ async def run():
7575
changed_running_status = dispatcher.running_status_change.new_receiver()
7676
7777
async for dispatch in changed_running_status:
78-
match dispatch.running("DEMO_TYPE"):
79-
case RunningState.RUNNING:
80-
print(f"Executing dispatch {dispatch.id}, due on {dispatch.start_time}")
81-
if actor.is_running:
82-
actor.reconfigure(
83-
components=dispatch.target,
84-
run_parameters=dispatch.payload, # custom actor parameters
85-
dry_run=dispatch.dry_run,
86-
until=dispatch.until,
87-
) # this will reconfigure the actor
88-
else:
89-
# this will start a new actor with the given components
90-
# and run it for the duration of the dispatch
91-
actor.start(
92-
components=dispatch.target,
93-
run_parameters=dispatch.payload, # custom actor parameters
94-
dry_run=dispatch.dry_run,
95-
until=dispatch.until,
96-
)
97-
case RunningState.STOPPED:
98-
actor.stop() # this will stop the actor
99-
case RunningState.DIFFERENT_TYPE:
100-
pass # dispatch not for this type
78+
if dispatch.type != "YOUR_DISPATCH_TYPE":
79+
continue
80+
81+
if dispatch.started:
82+
print(f"Executing dispatch {dispatch.id}, due on {dispatch.start_time}")
83+
if actor.is_running:
84+
actor.reconfigure(
85+
components=dispatch.target,
86+
run_parameters=dispatch.payload, # custom actor parameters
87+
dry_run=dispatch.dry_run,
88+
until=dispatch.until,
89+
) # this will reconfigure the actor
90+
else:
91+
# this will start a new actor with the given components
92+
# and run it for the duration of the dispatch
93+
actor.start(
94+
components=dispatch.target,
95+
run_parameters=dispatch.payload, # custom actor parameters
96+
dry_run=dispatch.dry_run,
97+
until=dispatch.until,
98+
)
99+
else:
100+
actor.stop() # this will stop the actor
101101
```
102102
103103
Example: Getting notification about dispatch lifecycle events

0 commit comments

Comments
 (0)