EAScheduler 0.2.1__tar.gz → 0.2.2__tar.gz
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.
- {eascheduler-0.2.1/src/EAScheduler.egg-info → eascheduler-0.2.2}/PKG-INFO +18 -4
- {eascheduler-0.2.1 → eascheduler-0.2.2}/requirements_setup.txt +2 -2
- {eascheduler-0.2.1 → eascheduler-0.2.2/src/EAScheduler.egg-info}/PKG-INFO +18 -4
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/EAScheduler.egg-info/requires.txt +2 -2
- eascheduler-0.2.2/src/eascheduler/__version__.py +1 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/builder/filters.py +1 -1
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/builder/helper.py +4 -3
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/builder/triggers.py +11 -10
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/helpers/time_replace.py +10 -1
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/producers/__init__.py +2 -2
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/producers/base.py +27 -4
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/producers/prod_filter.py +22 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/producers/prod_filter_holiday.py +5 -1
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/producers/prod_group.py +7 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/producers/prod_interval.py +6 -1
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/producers/prod_operation.py +22 -2
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/producers/prod_sun.py +33 -4
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/producers/prod_time.py +6 -1
- eascheduler-0.2.1/src/eascheduler/__version__.py +0 -1
- {eascheduler-0.2.1 → eascheduler-0.2.2}/LICENSE +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/MANIFEST.in +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/setup.cfg +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/setup.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/EAScheduler.egg-info/SOURCES.txt +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/EAScheduler.egg-info/dependency_links.txt +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/EAScheduler.egg-info/top_level.txt +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/__init__.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/builder/__init__.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/builder/jobs.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/builder/utils.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/const.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/default.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/errors/__init__.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/errors/errors.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/errors/handler.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/executor/__init__.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/executor/base.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/helpers/__init__.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/helpers/dst_param.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/helpers/helpers.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/job_control/__init__.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/job_control/base.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/job_control/job_countdown.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/job_control/job_datetime.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/job_control/job_onetime.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/job_stores/__init__.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/job_stores/base.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/job_stores/memory.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/jobs/__init__.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/jobs/base.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/jobs/event_handler.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/jobs/job_countdown.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/jobs/job_datetime.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/jobs/job_onetime.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/py.typed +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/schedulers/__init__.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/schedulers/async_scheduler.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/schedulers/base.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/task_managers/__init__.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/task_managers/base.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/task_managers/parallel.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/src/eascheduler/task_managers/sequential.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/tests/test_const.py +0 -0
- {eascheduler-0.2.1 → eascheduler-0.2.2}/tests/test_default.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: EAScheduler
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Easy async scheduling with a nice interface
|
|
5
5
|
Home-page: https://github.com/spacemanspiff2007/eascheduler
|
|
6
6
|
Author: spaceman_spiff
|
|
@@ -19,10 +19,20 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
19
19
|
Requires-Python: >=3.10
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: whenever==0.6.
|
|
22
|
+
Requires-Dist: whenever==0.6.16
|
|
23
23
|
Requires-Dist: astral==3.2
|
|
24
|
-
Requires-Dist: holidays==0.
|
|
24
|
+
Requires-Dist: holidays==0.64
|
|
25
25
|
Requires-Dist: typing_extensions==4.12.2
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: classifier
|
|
28
|
+
Dynamic: description
|
|
29
|
+
Dynamic: description-content-type
|
|
30
|
+
Dynamic: home-page
|
|
31
|
+
Dynamic: keywords
|
|
32
|
+
Dynamic: project-url
|
|
33
|
+
Dynamic: requires-dist
|
|
34
|
+
Dynamic: requires-python
|
|
35
|
+
Dynamic: summary
|
|
26
36
|
|
|
27
37
|
# EAScheduler
|
|
28
38
|
[](https://github.com/spacemanspiff2007/eascheduler/actions)
|
|
@@ -126,6 +136,10 @@ scheduler.at(
|
|
|
126
136
|
````
|
|
127
137
|
|
|
128
138
|
## Changelog
|
|
139
|
+
#### 0.2.2 (2025-01-07)
|
|
140
|
+
- Ensured that all trigger builders are imputable and can be composed without side effects
|
|
141
|
+
- Updated dependencies
|
|
142
|
+
|
|
129
143
|
#### 0.2.1 (2024-11-20)
|
|
130
144
|
- Updated dependencies and some type hints
|
|
131
145
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: EAScheduler
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Easy async scheduling with a nice interface
|
|
5
5
|
Home-page: https://github.com/spacemanspiff2007/eascheduler
|
|
6
6
|
Author: spaceman_spiff
|
|
@@ -19,10 +19,20 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
19
19
|
Requires-Python: >=3.10
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: whenever==0.6.
|
|
22
|
+
Requires-Dist: whenever==0.6.16
|
|
23
23
|
Requires-Dist: astral==3.2
|
|
24
|
-
Requires-Dist: holidays==0.
|
|
24
|
+
Requires-Dist: holidays==0.64
|
|
25
25
|
Requires-Dist: typing_extensions==4.12.2
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: classifier
|
|
28
|
+
Dynamic: description
|
|
29
|
+
Dynamic: description-content-type
|
|
30
|
+
Dynamic: home-page
|
|
31
|
+
Dynamic: keywords
|
|
32
|
+
Dynamic: project-url
|
|
33
|
+
Dynamic: requires-dist
|
|
34
|
+
Dynamic: requires-python
|
|
35
|
+
Dynamic: summary
|
|
26
36
|
|
|
27
37
|
# EAScheduler
|
|
28
38
|
[](https://github.com/spacemanspiff2007/eascheduler/actions)
|
|
@@ -126,6 +136,10 @@ scheduler.at(
|
|
|
126
136
|
````
|
|
127
137
|
|
|
128
138
|
## Changelog
|
|
139
|
+
#### 0.2.2 (2025-01-07)
|
|
140
|
+
- Ensured that all trigger builders are imputable and can be composed without side effects
|
|
141
|
+
- Updated dependencies
|
|
142
|
+
|
|
129
143
|
#### 0.2.1 (2024-11-20)
|
|
130
144
|
- Updated dependencies and some type hints
|
|
131
145
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.2.2'
|
|
@@ -35,7 +35,7 @@ class FilterObject:
|
|
|
35
35
|
self._filter: Final = filter
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
_get_producer_filter = BuilderTypeValidator(FilterObject, ProducerFilterBase, '_filter')
|
|
38
|
+
_get_producer_filter: Final = BuilderTypeValidator(FilterObject, ProducerFilterBase, '_filter')
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
# noinspection PyProtectedMember,PyShadowingBuiltins
|
|
@@ -103,7 +103,7 @@ def get_instant(value: HINT_INSTANT) -> Instant:
|
|
|
103
103
|
# timedelta
|
|
104
104
|
try:
|
|
105
105
|
timedelta = get_timedelta(value)
|
|
106
|
-
except ValueError:
|
|
106
|
+
except (ValueError, TypeError):
|
|
107
107
|
pass
|
|
108
108
|
else:
|
|
109
109
|
return Instant.now() + timedelta
|
|
@@ -112,7 +112,7 @@ def get_instant(value: HINT_INSTANT) -> Instant:
|
|
|
112
112
|
# time
|
|
113
113
|
try:
|
|
114
114
|
time = get_time(value)
|
|
115
|
-
except ValueError:
|
|
115
|
+
except (ValueError, TypeError):
|
|
116
116
|
pass
|
|
117
117
|
else:
|
|
118
118
|
now = SystemDateTime.now()
|
|
@@ -228,7 +228,8 @@ class BuilderTypeValidator(Generic[TYPE_OUT]):
|
|
|
228
228
|
msg = f'Expected an instance of {self.type_out.__name__}, got {obj} ({type(obj).__name__})'
|
|
229
229
|
raise TypeError(msg)
|
|
230
230
|
|
|
231
|
-
return
|
|
231
|
+
# We return a copy so we are sure that there are no side effects
|
|
232
|
+
return obj.copy()
|
|
232
233
|
|
|
233
234
|
def __repr__(self) -> str:
|
|
234
235
|
return f'{self.__class__.__name__}({self.type_in.__name__}, {self.type_out.__name__}, {self.var_name})'
|
|
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Final, Literal
|
|
|
4
4
|
|
|
5
5
|
from typing_extensions import Self
|
|
6
6
|
|
|
7
|
+
from eascheduler.builder.filters import _get_producer_filter
|
|
7
8
|
from eascheduler.builder.helper import (
|
|
8
9
|
HINT_CLOCK_BACKWARD,
|
|
9
10
|
HINT_CLOCK_FORWARD,
|
|
@@ -27,8 +28,8 @@ from eascheduler.producers import (
|
|
|
27
28
|
LatestProducerOperation,
|
|
28
29
|
NoonProducer,
|
|
29
30
|
OffsetProducerOperation,
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
SunAzimuthProducerCompare,
|
|
32
|
+
SunElevationProducerCompare,
|
|
32
33
|
SunriseProducer,
|
|
33
34
|
SunsetProducer,
|
|
34
35
|
TimeProducer,
|
|
@@ -51,7 +52,7 @@ class TriggerObject:
|
|
|
51
52
|
:param offset: The offset (positive or negative)
|
|
52
53
|
"""
|
|
53
54
|
return self.__class__(
|
|
54
|
-
OffsetProducerOperation(self
|
|
55
|
+
OffsetProducerOperation(_get_producer(self), get_timedelta(offset).in_seconds())
|
|
55
56
|
)
|
|
56
57
|
|
|
57
58
|
def earliest(self, earliest: HINT_TIME, *,
|
|
@@ -65,7 +66,7 @@ class TriggerObject:
|
|
|
65
66
|
"""
|
|
66
67
|
return self.__class__(
|
|
67
68
|
EarliestProducerOperation(
|
|
68
|
-
self
|
|
69
|
+
_get_producer(self),
|
|
69
70
|
get_time_replacer(earliest, clock_forward=clock_forward, clock_backward=clock_backward)
|
|
70
71
|
)
|
|
71
72
|
)
|
|
@@ -81,7 +82,7 @@ class TriggerObject:
|
|
|
81
82
|
"""
|
|
82
83
|
return self.__class__(
|
|
83
84
|
LatestProducerOperation(
|
|
84
|
-
self
|
|
85
|
+
_get_producer(self),
|
|
85
86
|
get_time_replacer(latest, clock_forward=clock_forward, clock_backward=clock_backward)
|
|
86
87
|
)
|
|
87
88
|
)
|
|
@@ -94,7 +95,7 @@ class TriggerObject:
|
|
|
94
95
|
"""
|
|
95
96
|
return self.__class__(
|
|
96
97
|
JitterProducerOperation(
|
|
97
|
-
self
|
|
98
|
+
_get_producer(self),
|
|
98
99
|
get_timedelta(low).in_seconds(), get_timedelta(high).in_seconds() if high is not None else None
|
|
99
100
|
)
|
|
100
101
|
)
|
|
@@ -106,7 +107,7 @@ class TriggerObject:
|
|
|
106
107
|
"""
|
|
107
108
|
if self._producer._filter is not None:
|
|
108
109
|
raise ValueError()
|
|
109
|
-
self._producer._filter = filter
|
|
110
|
+
self._producer._filter = _get_producer_filter(filter)
|
|
110
111
|
return self
|
|
111
112
|
|
|
112
113
|
only_at = only_on
|
|
@@ -146,7 +147,7 @@ class TriggerBuilder:
|
|
|
146
147
|
:param elevation: Sun elevation in degrees
|
|
147
148
|
:param direction: rising or falling
|
|
148
149
|
"""
|
|
149
|
-
return TriggerObject(
|
|
150
|
+
return TriggerObject(SunElevationProducerCompare(elevation, direction))
|
|
150
151
|
|
|
151
152
|
@staticmethod
|
|
152
153
|
def sun_azimuth(azimuth: float) -> TriggerObject:
|
|
@@ -154,7 +155,7 @@ class TriggerBuilder:
|
|
|
154
155
|
|
|
155
156
|
:param azimuth: Sun azimuth in degrees
|
|
156
157
|
"""
|
|
157
|
-
return TriggerObject(
|
|
158
|
+
return TriggerObject(SunAzimuthProducerCompare(azimuth))
|
|
158
159
|
|
|
159
160
|
@staticmethod
|
|
160
161
|
def group(*builders: TriggerObject) -> TriggerObject:
|
|
@@ -200,4 +201,4 @@ class TriggerBuilder:
|
|
|
200
201
|
)
|
|
201
202
|
|
|
202
203
|
|
|
203
|
-
_get_producer = BuilderTypeValidator(TriggerObject, DateTimeProducerBase, '_producer')
|
|
204
|
+
_get_producer: Final = BuilderTypeValidator(TriggerObject, DateTimeProducerBase, '_producer')
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from enum import Enum
|
|
4
4
|
from typing import TYPE_CHECKING, Final, Literal, TypeAlias
|
|
5
5
|
|
|
6
|
+
from typing_extensions import Self
|
|
6
7
|
from whenever import Date, LocalDateTime, RepeatedTime, SkippedTime, SystemDateTime, Time
|
|
7
8
|
|
|
8
9
|
from eascheduler.helpers.helpers import to_enum
|
|
@@ -60,7 +61,7 @@ class TimeTwiceError(Exception):
|
|
|
60
61
|
|
|
61
62
|
|
|
62
63
|
class TimeReplacer:
|
|
63
|
-
__slots__ = ('
|
|
64
|
+
__slots__ = ('_repeated', '_skipped', '_time')
|
|
64
65
|
|
|
65
66
|
def __init__(self, time: Time, if_skipped: HINT_SKIPPED, if_repeated: HINT_REPEATED) -> None:
|
|
66
67
|
super().__init__()
|
|
@@ -68,10 +69,18 @@ class TimeReplacer:
|
|
|
68
69
|
self._skipped: Final[SkippedTimeBehavior] = to_enum(SkippedTimeBehavior, if_skipped)
|
|
69
70
|
self._repeated: Final[RepeatedTimeBehavior] = to_enum(RepeatedTimeBehavior, if_repeated)
|
|
70
71
|
|
|
72
|
+
def __eq__(self, other: object) -> bool:
|
|
73
|
+
if not isinstance(other, TimeReplacer):
|
|
74
|
+
return NotImplemented
|
|
75
|
+
return self._time == other._time and self._skipped == other._skipped and self._repeated == other._repeated
|
|
76
|
+
|
|
71
77
|
def __repr__(self) -> str:
|
|
72
78
|
return (f'<{self.__class__.__name__} {self._time!s}'
|
|
73
79
|
f' if_skipped={self._skipped.value:s} if_repeated={self._repeated.value:s}>')
|
|
74
80
|
|
|
81
|
+
def copy(self) -> Self:
|
|
82
|
+
return self.__class__(self._time, self._skipped, self._repeated)
|
|
83
|
+
|
|
75
84
|
def replace(self, dt: SystemDateTime | Date) -> SystemDateTime: # noqa: C901
|
|
76
85
|
|
|
77
86
|
kwargs = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING, NoReturn
|
|
3
|
+
from typing import TYPE_CHECKING, NoReturn, TypeVar
|
|
4
4
|
|
|
5
|
-
from typing_extensions import override
|
|
5
|
+
from typing_extensions import Self, override
|
|
6
6
|
|
|
7
7
|
from eascheduler.errors import InfiniteLoopDetectedError
|
|
8
8
|
|
|
@@ -13,14 +13,30 @@ if TYPE_CHECKING:
|
|
|
13
13
|
from whenever import Instant, SystemDateTime
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
class
|
|
16
|
+
class CompareEqualityBySlotValues:
|
|
17
|
+
__slots__ = ()
|
|
18
|
+
|
|
19
|
+
# Since all classes use slots we can compare them by comparing their slot values
|
|
20
|
+
def __eq__(self, other: object) -> bool:
|
|
21
|
+
if not isinstance(other, self.__class__):
|
|
22
|
+
raise TypeError()
|
|
23
|
+
return all(getattr(self, s) == getattr(other, s) for s in self.__slots__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ProducerFilterBase(CompareEqualityBySlotValues):
|
|
17
27
|
__slots__ = ()
|
|
18
28
|
|
|
19
29
|
def allow(self, dt: SystemDateTime) -> bool:
|
|
20
30
|
raise NotImplementedError()
|
|
21
31
|
|
|
32
|
+
def copy(self) -> Self:
|
|
33
|
+
raise NotImplementedError()
|
|
34
|
+
|
|
22
35
|
|
|
23
|
-
|
|
36
|
+
DTPB_TYPE = TypeVar('DTPB_TYPE', bound='DateTimeProducerBase')
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class DateTimeProducerBase(CompareEqualityBySlotValues):
|
|
24
40
|
__slots__ = ('_filter', )
|
|
25
41
|
|
|
26
42
|
def __init__(self) -> None:
|
|
@@ -31,6 +47,13 @@ class DateTimeProducerBase:
|
|
|
31
47
|
Has to guarantee that the returned date time is after the given date time."""
|
|
32
48
|
raise NotImplementedError()
|
|
33
49
|
|
|
50
|
+
def _copy_filter(self, obj: DTPB_TYPE) -> DTPB_TYPE:
|
|
51
|
+
obj._filter = self._filter.copy() if self._filter is not None else None
|
|
52
|
+
return obj
|
|
53
|
+
|
|
54
|
+
def copy(self) -> Self:
|
|
55
|
+
raise NotImplementedError()
|
|
56
|
+
|
|
34
57
|
|
|
35
58
|
class DateTimeProducerOperationBase(DateTimeProducerBase):
|
|
36
59
|
__slots__ = ('_producer', )
|
|
@@ -24,6 +24,9 @@ class ProducerFilterGroupBase(ProducerFilterBase):
|
|
|
24
24
|
self._filters = (*self._filters, filter)
|
|
25
25
|
return self
|
|
26
26
|
|
|
27
|
+
def copy(self) -> Self:
|
|
28
|
+
return self.__class__(f.copy() for f in self._filters)
|
|
29
|
+
|
|
27
30
|
|
|
28
31
|
class AnyGroupProducerFilter(ProducerFilterGroupBase):
|
|
29
32
|
@override
|
|
@@ -44,6 +47,10 @@ class InvertingProducerFilter(ProducerFilterBase):
|
|
|
44
47
|
def __init__(self, filter: ProducerFilterBase) -> None: # noqa: A002
|
|
45
48
|
self._filter: ProducerFilterBase = filter
|
|
46
49
|
|
|
50
|
+
@override
|
|
51
|
+
def copy(self) -> Self:
|
|
52
|
+
return self.__class__(self._filter.copy())
|
|
53
|
+
|
|
47
54
|
@override
|
|
48
55
|
def allow(self, dt: SystemDateTime) -> bool:
|
|
49
56
|
return not self._filter.allow(dt)
|
|
@@ -57,6 +64,10 @@ class TimeProducerFilter(ProducerFilterBase):
|
|
|
57
64
|
self._lower: Final = lower
|
|
58
65
|
self._upper: Final = upper
|
|
59
66
|
|
|
67
|
+
@override
|
|
68
|
+
def copy(self) -> Self:
|
|
69
|
+
return self.__class__(self._lower, self._upper)
|
|
70
|
+
|
|
60
71
|
@override
|
|
61
72
|
def allow(self, dt: SystemDateTime) -> bool:
|
|
62
73
|
|
|
@@ -77,6 +88,10 @@ class DayOfWeekProducerFilter(ProducerFilterBase):
|
|
|
77
88
|
super().__init__()
|
|
78
89
|
self._weekdays: Final = frozenset(weekdays)
|
|
79
90
|
|
|
91
|
+
@override
|
|
92
|
+
def copy(self) -> Self:
|
|
93
|
+
return self.__class__(self._weekdays)
|
|
94
|
+
|
|
80
95
|
@override
|
|
81
96
|
def allow(self, dt: SystemDateTime) -> bool:
|
|
82
97
|
return dt.py_datetime().isoweekday() in self._weekdays
|
|
@@ -89,6 +104,10 @@ class DayOfMonthProducerFilter(ProducerFilterBase):
|
|
|
89
104
|
super().__init__()
|
|
90
105
|
self._days: Final = frozenset(days)
|
|
91
106
|
|
|
107
|
+
@override
|
|
108
|
+
def copy(self) -> DayOfMonthProducerFilter:
|
|
109
|
+
return self.__class__(self._days)
|
|
110
|
+
|
|
92
111
|
@override
|
|
93
112
|
def allow(self, dt: SystemDateTime) -> bool:
|
|
94
113
|
return dt.day in self._days
|
|
@@ -101,6 +120,9 @@ class MonthOfYearProducerFilter(ProducerFilterBase):
|
|
|
101
120
|
super().__init__()
|
|
102
121
|
self._months: Final = frozenset(months)
|
|
103
122
|
|
|
123
|
+
def copy(self) -> Self:
|
|
124
|
+
return self.__class__(self._months)
|
|
125
|
+
|
|
104
126
|
@override
|
|
105
127
|
def allow(self, dt: SystemDateTime) -> bool:
|
|
106
128
|
return dt.month in self._months
|
|
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Any, Final
|
|
|
4
4
|
|
|
5
5
|
from holidays import HolidayBase as _HolidayBase
|
|
6
6
|
from holidays import country_holidays
|
|
7
|
-
from typing_extensions import override
|
|
7
|
+
from typing_extensions import Self, override
|
|
8
8
|
|
|
9
9
|
from eascheduler.errors.errors import HolidaysNotSetUpError
|
|
10
10
|
from eascheduler.producers.base import ProducerFilterBase
|
|
@@ -24,6 +24,10 @@ class HolidayProducerFilterBase(ProducerFilterBase):
|
|
|
24
24
|
super().__init__()
|
|
25
25
|
self._holidays: Final = _get_holiday_obj(holidays)
|
|
26
26
|
|
|
27
|
+
@override
|
|
28
|
+
def copy(self) -> Self:
|
|
29
|
+
return self.__class__(self._holidays)
|
|
30
|
+
|
|
27
31
|
|
|
28
32
|
class HolidayProducerFilter(HolidayProducerFilterBase):
|
|
29
33
|
@override
|
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Final
|
|
4
4
|
|
|
5
|
+
from typing_extensions import Self, override
|
|
6
|
+
|
|
5
7
|
from .base import DateTimeProducerBase, not_infinite_loop
|
|
6
8
|
|
|
7
9
|
|
|
@@ -18,6 +20,11 @@ class GroupProducer(DateTimeProducerBase):
|
|
|
18
20
|
super().__init__()
|
|
19
21
|
self._producers: Final = tuple(producers)
|
|
20
22
|
|
|
23
|
+
@override
|
|
24
|
+
def copy(self) -> Self:
|
|
25
|
+
cls = self.__class__(p.copy() for p in self._producers)
|
|
26
|
+
return self._copy_filter(cls)
|
|
27
|
+
|
|
21
28
|
def get_next(self, dt: Instant) -> Instant: # type: ignore[return]
|
|
22
29
|
|
|
23
30
|
next_dt = dt
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Final
|
|
4
4
|
|
|
5
|
-
from typing_extensions import override
|
|
5
|
+
from typing_extensions import Self, override
|
|
6
6
|
|
|
7
7
|
from .base import DateTimeProducerBase
|
|
8
8
|
|
|
@@ -20,6 +20,11 @@ class IntervalProducer(DateTimeProducerBase):
|
|
|
20
20
|
self._next: Instant | None = start
|
|
21
21
|
self._interval: Final = interval
|
|
22
22
|
|
|
23
|
+
@override
|
|
24
|
+
def copy(self) -> Self:
|
|
25
|
+
cls = self.__class__(start=self._next, interval=self._interval)
|
|
26
|
+
return self._copy_filter(cls)
|
|
27
|
+
|
|
23
28
|
@override
|
|
24
29
|
def get_next(self, dt: Instant) -> Instant:
|
|
25
30
|
interval = self._interval
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from random import uniform
|
|
4
4
|
from typing import TYPE_CHECKING, Final
|
|
5
5
|
|
|
6
|
-
from typing_extensions import override
|
|
6
|
+
from typing_extensions import Self, override
|
|
7
7
|
from whenever import LocalDateTime, SkippedTime, SystemDateTime, Time, TimeDelta
|
|
8
8
|
|
|
9
9
|
from eascheduler.helpers import TimeReplacer, TimeSkippedError, TimeTwiceError
|
|
@@ -34,6 +34,11 @@ class OffsetProducerOperation(DateTimeProducerOperationBase):
|
|
|
34
34
|
super().__init__(producer)
|
|
35
35
|
self.offset: Final = offset
|
|
36
36
|
|
|
37
|
+
@override
|
|
38
|
+
def copy(self) -> Self:
|
|
39
|
+
cls = self.__class__(self._producer.copy(), self.offset)
|
|
40
|
+
return self._copy_filter(cls)
|
|
41
|
+
|
|
37
42
|
@override
|
|
38
43
|
def apply_operation(self, next_dt: Instant, dt: Instant) -> Instant:
|
|
39
44
|
return next_dt.add(seconds=self.offset)
|
|
@@ -46,6 +51,11 @@ class EarliestProducerOperation(DateTimeProducerOperationBase):
|
|
|
46
51
|
super().__init__(producer)
|
|
47
52
|
self.earliest: Final = earliest
|
|
48
53
|
|
|
54
|
+
@override
|
|
55
|
+
def copy(self) -> Self:
|
|
56
|
+
cls = self.__class__(self._producer.copy(), self.earliest.copy())
|
|
57
|
+
return self._copy_filter(cls)
|
|
58
|
+
|
|
49
59
|
@override
|
|
50
60
|
def apply_operation(self, next_dt: Instant, dt: Instant) -> Instant:
|
|
51
61
|
try:
|
|
@@ -69,6 +79,11 @@ class LatestProducerOperation(DateTimeProducerOperationBase):
|
|
|
69
79
|
super().__init__(producer)
|
|
70
80
|
self.latest: Final = earliest
|
|
71
81
|
|
|
82
|
+
@override
|
|
83
|
+
def copy(self) -> Self:
|
|
84
|
+
cls = self.__class__(self._producer.copy(), self.latest.copy())
|
|
85
|
+
return self._copy_filter(cls)
|
|
86
|
+
|
|
72
87
|
@override
|
|
73
88
|
def apply_operation(self, next_dt: Instant, dt: Instant) -> Instant:
|
|
74
89
|
try:
|
|
@@ -86,7 +101,7 @@ class LatestProducerOperation(DateTimeProducerOperationBase):
|
|
|
86
101
|
|
|
87
102
|
|
|
88
103
|
class JitterProducerOperation(DateTimeProducerOperationBase):
|
|
89
|
-
__slots__ = ('
|
|
104
|
+
__slots__ = ('high', 'low')
|
|
90
105
|
|
|
91
106
|
def __init__(self, producer: DateTimeProducerBase, low: float, high: float | None = None) -> None:
|
|
92
107
|
super().__init__(producer)
|
|
@@ -100,6 +115,11 @@ class JitterProducerOperation(DateTimeProducerOperationBase):
|
|
|
100
115
|
self.low: Final = low
|
|
101
116
|
self.high: Final = high
|
|
102
117
|
|
|
118
|
+
@override
|
|
119
|
+
def copy(self) -> Self:
|
|
120
|
+
cls = self.__class__(self._producer.copy(), self.low, self.high)
|
|
121
|
+
return self._copy_filter(cls)
|
|
122
|
+
|
|
103
123
|
@override
|
|
104
124
|
def apply_operation(self, next_dt: Instant, dt: Instant) -> Instant:
|
|
105
125
|
if (low := self.low) >= 0:
|
|
@@ -7,7 +7,7 @@ from datetime import timezone as dt_timezone
|
|
|
7
7
|
from typing import TYPE_CHECKING, Final, Literal
|
|
8
8
|
|
|
9
9
|
from astral import Observer, SunDirection, sun
|
|
10
|
-
from typing_extensions import override
|
|
10
|
+
from typing_extensions import Self, override
|
|
11
11
|
from whenever import Instant
|
|
12
12
|
|
|
13
13
|
from eascheduler.errors.errors import LocationNotSetError
|
|
@@ -66,6 +66,11 @@ class SunProducer(DateTimeProducerBase):
|
|
|
66
66
|
|
|
67
67
|
self.func: Final = func
|
|
68
68
|
|
|
69
|
+
@override
|
|
70
|
+
def copy(self) -> Self:
|
|
71
|
+
cls = self.__class__()
|
|
72
|
+
return self._copy_filter(cls)
|
|
73
|
+
|
|
69
74
|
def _cache_key(self) -> tuple[Hashable, ...]:
|
|
70
75
|
return (self.__class__, )
|
|
71
76
|
|
|
@@ -150,16 +155,33 @@ class DuskProducer(SunProducer):
|
|
|
150
155
|
super().__init__(sun.dusk)
|
|
151
156
|
|
|
152
157
|
|
|
153
|
-
class
|
|
158
|
+
class SunFuncIgnoringCompare:
|
|
159
|
+
__slots__ = ()
|
|
160
|
+
|
|
161
|
+
def __eq__(self, other: object) -> bool:
|
|
162
|
+
if not isinstance(other, self.__class__):
|
|
163
|
+
raise TypeError()
|
|
164
|
+
return all(getattr(self, s) == getattr(other, s) for s in self.__slots__ if s != 'func')
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class SunElevationProducerCompare(SunProducer, SunFuncIgnoringCompare):
|
|
168
|
+
__slots__ = ('direction', 'elevation')
|
|
169
|
+
|
|
154
170
|
def __init__(self, elevation: float, direction: Literal['rising', 'setting']) -> None:
|
|
155
171
|
if elevation < -90 or elevation > 90:
|
|
156
172
|
msg = 'Elevation must be between -90 and 90'
|
|
157
173
|
raise ValueError(msg)
|
|
158
174
|
|
|
159
175
|
self.elevation: Final = elevation
|
|
160
|
-
self.direction: Final = {'rising': SunDirection.RISING, 'setting': SunDirection.SETTING}[direction]
|
|
176
|
+
self.direction: Final = {'rising': SunDirection.RISING, 'setting': SunDirection.SETTING}[direction] \
|
|
177
|
+
if direction is not SunDirection.RISING and direction is not SunDirection.SETTING else direction
|
|
161
178
|
super().__init__(self._sun_func)
|
|
162
179
|
|
|
180
|
+
@override
|
|
181
|
+
def copy(self) -> Self:
|
|
182
|
+
cls = self.__class__(self.elevation, self.direction)
|
|
183
|
+
return self._copy_filter(cls)
|
|
184
|
+
|
|
163
185
|
def _sun_func(self, observer: Observer, date: dt_date) -> datetime:
|
|
164
186
|
return sun.time_at_elevation(observer, self.elevation, date, self.direction)
|
|
165
187
|
|
|
@@ -168,7 +190,9 @@ class SunElevationProducer(SunProducer):
|
|
|
168
190
|
return self.__class__, self.elevation, self.direction
|
|
169
191
|
|
|
170
192
|
|
|
171
|
-
class
|
|
193
|
+
class SunAzimuthProducerCompare(SunProducer, SunFuncIgnoringCompare):
|
|
194
|
+
__slots__ = ('azimuth', )
|
|
195
|
+
|
|
172
196
|
def __init__(self, azimuth: float) -> None:
|
|
173
197
|
if not 0 <= azimuth <= 360:
|
|
174
198
|
msg = 'Azimuth must be between 0° and 360°'
|
|
@@ -177,6 +201,11 @@ class SunAzimuthProducer(SunProducer):
|
|
|
177
201
|
self.azimuth: Final = azimuth
|
|
178
202
|
super().__init__(self._sun_func)
|
|
179
203
|
|
|
204
|
+
@override
|
|
205
|
+
def copy(self) -> Self:
|
|
206
|
+
cls = self.__class__(self.azimuth)
|
|
207
|
+
return self._copy_filter(cls)
|
|
208
|
+
|
|
180
209
|
@override
|
|
181
210
|
def _cache_key(self) -> tuple[Hashable, ...]:
|
|
182
211
|
return self.__class__, self.azimuth
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Final
|
|
4
4
|
|
|
5
|
-
from typing_extensions import override
|
|
5
|
+
from typing_extensions import Self, override
|
|
6
6
|
|
|
7
7
|
from eascheduler.helpers import TimeReplacer, TimeSkippedError, TimeTwiceError
|
|
8
8
|
from eascheduler.producers.base import DateTimeProducerBase, not_infinite_loop
|
|
@@ -19,6 +19,11 @@ class TimeProducer(DateTimeProducerBase):
|
|
|
19
19
|
super().__init__()
|
|
20
20
|
self._time: Final = time
|
|
21
21
|
|
|
22
|
+
@override
|
|
23
|
+
def copy(self) -> Self:
|
|
24
|
+
cls = self.__class__(time=self._time.copy())
|
|
25
|
+
return self._copy_filter(cls)
|
|
26
|
+
|
|
22
27
|
@override
|
|
23
28
|
def get_next(self, dt: Instant) -> Instant: # type: ignore[return]
|
|
24
29
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '0.2.1'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|