wiederverwendbar 0.9.0__py3-none-any.whl → 0.9.2__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.
- wiederverwendbar/__init__.py +1 -1
- wiederverwendbar/branding/settings.py +9 -7
- wiederverwendbar/fastapi/app.py +4 -1
- wiederverwendbar/sqlalchemy/db.py +3 -3
- wiederverwendbar/task_manger/__init__.py +17 -4
- wiederverwendbar/task_manger/task.py +118 -75
- wiederverwendbar/task_manger/task_manager.py +127 -104
- wiederverwendbar/task_manger/trigger.py +297 -115
- wiederverwendbar/timer.py +61 -0
- wiederverwendbar/typer/app.py +5 -1
- wiederverwendbar-0.9.2.dist-info/METADATA +726 -0
- {wiederverwendbar-0.9.0.dist-info → wiederverwendbar-0.9.2.dist-info}/RECORD +15 -13
- wiederverwendbar-0.9.2.dist-info/licenses/LICENSE +674 -0
- wiederverwendbar-0.9.0.dist-info/METADATA +0 -52
- {wiederverwendbar-0.9.0.dist-info → wiederverwendbar-0.9.2.dist-info}/WHEEL +0 -0
- {wiederverwendbar-0.9.0.dist-info → wiederverwendbar-0.9.2.dist-info}/entry_points.txt +0 -0
wiederverwendbar/__init__.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
__title__ = "wiederverwendbar"
|
2
2
|
__description__ = "A collection of scripts, classes and tools they are \\\"wiederverwendbar\\\"."
|
3
|
-
__version__ = "0.9.
|
3
|
+
__version__ = "0.9.2"
|
4
4
|
__author__ = "Julius Koenig"
|
5
5
|
__author_email__ = "info@bastelquartier.de"
|
6
6
|
__license__ = "GPL-3.0"
|
@@ -22,13 +22,15 @@ class BrandingSettings(BaseModel):
|
|
22
22
|
main_module = sys.modules["__main__"]
|
23
23
|
module_data = self.get_attributes(main_module.__dict__)
|
24
24
|
if module_data is None:
|
25
|
-
|
26
|
-
if
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
25
|
+
# ToDO: this code is not working for pyprojects.toml [project.scripts]
|
26
|
+
if hasattr(main_module, "__file__"):
|
27
|
+
init_file = Path(main_module.__file__).parent / "__init__.py"
|
28
|
+
if init_file.is_file():
|
29
|
+
init_file_module_spec = importlib.util.spec_from_file_location("__main__.__init__", init_file)
|
30
|
+
init_file_module = importlib.util.module_from_spec(init_file_module_spec)
|
31
|
+
sys.modules["__main__.__init__"] = init_file_module
|
32
|
+
init_file_module_spec.loader.exec_module(init_file_module)
|
33
|
+
module_data = self.get_attributes(init_file_module.__dict__)
|
32
34
|
if module_data is None:
|
33
35
|
module_data = {}
|
34
36
|
|
wiederverwendbar/fastapi/app.py
CHANGED
@@ -367,7 +367,10 @@ class FastAPI(_FastAPI):
|
|
367
367
|
"terms_of_service": self.terms_of_service}
|
368
368
|
|
369
369
|
async def get_version(self, request: Request) -> dict[str, Any]:
|
370
|
-
|
370
|
+
version = self.version
|
371
|
+
if version.startswith("v"):
|
372
|
+
version = version[1:]
|
373
|
+
return {"version": version}
|
371
374
|
|
372
375
|
async def get_root_redirect(self, request: Request) -> RedirectResponse:
|
373
376
|
root_path = request.scope.get("root_path", "").rstrip("/")
|
@@ -172,13 +172,13 @@ class SqlalchemyDb:
|
|
172
172
|
else:
|
173
173
|
connection_string += self.password
|
174
174
|
if self.host is None:
|
175
|
-
raise RuntimeError(f"No host specified for {self}")
|
175
|
+
raise RuntimeError(f"No host specified for {self.__class__.__name__}")
|
176
176
|
connection_string += f"@{self.host}"
|
177
177
|
if self.port is None:
|
178
|
-
raise RuntimeError(f"No port specified for {self}")
|
178
|
+
raise RuntimeError(f"No port specified for {self.__class__.__name__}")
|
179
179
|
connection_string += f":{self.port}"
|
180
180
|
if self.name is None:
|
181
|
-
raise RuntimeError(f"No name specified for {self}")
|
181
|
+
raise RuntimeError(f"No name specified for {self.__class__.__name__}")
|
182
182
|
connection_string += f"/{self.name}"
|
183
183
|
return connection_string
|
184
184
|
|
@@ -1,4 +1,17 @@
|
|
1
|
-
from wiederverwendbar.task_manger.task_manager import TaskManager
|
2
|
-
from wiederverwendbar.task_manger.singleton import ManagerSingleton
|
3
|
-
from wiederverwendbar.task_manger.task import Task
|
4
|
-
from wiederverwendbar.task_manger.trigger import
|
1
|
+
from wiederverwendbar.task_manger.task_manager import (TaskManager)
|
2
|
+
from wiederverwendbar.task_manger.singleton import (ManagerSingleton)
|
3
|
+
from wiederverwendbar.task_manger.task import (Task)
|
4
|
+
from wiederverwendbar.task_manger.trigger import (Trigger,
|
5
|
+
Interval,
|
6
|
+
EverySeconds,
|
7
|
+
EveryMinutes,
|
8
|
+
EveryHours,
|
9
|
+
EveryDays,
|
10
|
+
EveryWeeks,
|
11
|
+
EveryMonths,
|
12
|
+
EveryYears,
|
13
|
+
At,
|
14
|
+
AtDatetime,
|
15
|
+
AtNow,
|
16
|
+
AtManagerCreation,
|
17
|
+
AtManagerStart)
|
@@ -1,104 +1,147 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
from
|
1
|
+
from datetime import datetime as _datetime
|
2
|
+
from enum import Enum
|
3
|
+
from typing import Any, Optional, Callable, Union, TYPE_CHECKING
|
3
4
|
|
4
|
-
from wiederverwendbar.
|
5
|
+
from wiederverwendbar.functions.is_coroutine_function import is_coroutine_function
|
6
|
+
from wiederverwendbar.task_manger.trigger import Trigger
|
7
|
+
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from wiederverwendbar.task_manger.task_manager import TaskManager
|
5
10
|
|
6
11
|
|
7
12
|
class Task:
|
13
|
+
class TimeMeasurement(str, Enum):
|
14
|
+
START = "START"
|
15
|
+
END = "END"
|
16
|
+
|
8
17
|
def __init__(self,
|
9
|
-
payload,
|
10
|
-
|
18
|
+
payload: Callable[..., None],
|
19
|
+
*triggers: Trigger,
|
11
20
|
name: Optional[str] = None,
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
# set task name
|
21
|
+
time_measurement: Optional[TimeMeasurement] = None,
|
22
|
+
task_args: Optional[Union[list, tuple]] = None,
|
23
|
+
task_kwargs: Optional[dict] = None):
|
24
|
+
self._manager = None
|
25
|
+
|
26
|
+
# set the task name
|
18
27
|
if name is None:
|
19
28
|
name = payload.__name__
|
20
|
-
self.
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
29
|
+
self._name = name
|
30
|
+
|
31
|
+
# set task triggers
|
32
|
+
self._triggers = []
|
33
|
+
for trigger in triggers:
|
34
|
+
if not isinstance(trigger, Trigger):
|
35
|
+
raise ValueError("Trigger must be an instance of Trigger.")
|
36
|
+
if trigger.task is not None:
|
37
|
+
raise ValueError("Trigger already assigned to a task.")
|
38
|
+
trigger._task = self
|
39
|
+
self._triggers.append(trigger)
|
40
|
+
self._triggers = tuple(self._triggers)
|
28
41
|
|
29
42
|
# set task payload
|
30
43
|
if not callable(payload):
|
31
44
|
raise ValueError("Payload must be callable.")
|
45
|
+
if is_coroutine_function(payload):
|
46
|
+
raise ValueError("Coroutine functions are not supported.")
|
32
47
|
self._payload = payload
|
33
48
|
|
49
|
+
# indicates when the last run time should be measured
|
50
|
+
if time_measurement is None:
|
51
|
+
time_measurement = self.TimeMeasurement.START
|
52
|
+
if not isinstance(time_measurement, self.TimeMeasurement):
|
53
|
+
raise ValueError("Time measurement must be an instance of TaskTimeMeasurement.")
|
54
|
+
self._time_measurement = time_measurement
|
55
|
+
|
34
56
|
# set payload args and kwargs
|
35
|
-
|
36
|
-
|
37
|
-
if not iter(
|
38
|
-
raise ValueError("
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
self._last_run
|
48
|
-
|
49
|
-
|
50
|
-
# indicate if task is done
|
57
|
+
if task_args is None:
|
58
|
+
task_args = []
|
59
|
+
if not iter(task_args):
|
60
|
+
raise ValueError("Task args must be iterable.")
|
61
|
+
self._task_args = tuple(task_args)
|
62
|
+
if task_kwargs is None:
|
63
|
+
task_kwargs = {}
|
64
|
+
if not isinstance(task_kwargs, dict):
|
65
|
+
raise ValueError("Task kwargs must be dict.")
|
66
|
+
self._task_kwargs = task_kwargs
|
67
|
+
|
68
|
+
# indicate last run
|
69
|
+
self._last_run = None
|
70
|
+
|
71
|
+
# indicate if the task is done
|
51
72
|
self._done = False
|
52
73
|
|
53
|
-
|
54
|
-
|
55
|
-
|
74
|
+
def __str__(self):
|
75
|
+
return (f"{self.__class__.__name__}("
|
76
|
+
f"name={self.name}, "
|
77
|
+
f"trigger=[{', '.join([str(trigger) for trigger in self._triggers])}], "
|
78
|
+
f"last_run={self.last_run})")
|
56
79
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
80
|
+
def __call__(self, *args, **kwargs) -> bool:
|
81
|
+
if self.manager is None:
|
82
|
+
raise ValueError(f"Task {self} is not assigned to a manager.")
|
83
|
+
for trigger in self.triggers:
|
84
|
+
if trigger():
|
85
|
+
return True
|
86
|
+
return False
|
61
87
|
|
62
|
-
|
63
|
-
|
64
|
-
self.
|
65
|
-
|
88
|
+
@property
|
89
|
+
def manager(self) -> Optional["TaskManager"]:
|
90
|
+
return self._manager
|
91
|
+
|
92
|
+
@manager.setter
|
93
|
+
def manager(self, manager: "TaskManager") -> None:
|
94
|
+
if manager is None:
|
95
|
+
if self._manager is None:
|
96
|
+
return
|
97
|
+
manager = self._manager
|
98
|
+
with manager.lock:
|
99
|
+
# noinspection PyProtectedMember
|
100
|
+
manager._tasks.remove(self)
|
101
|
+
self.manager_removed()
|
102
|
+
else:
|
103
|
+
if self._manager is not None:
|
104
|
+
raise ValueError(f"Task {self} is already assigned to manager {self._manager}.")
|
105
|
+
self._manager = manager
|
66
106
|
|
67
|
-
|
68
|
-
|
107
|
+
with manager.lock:
|
108
|
+
# noinspection PyProtectedMember
|
109
|
+
manager._tasks.append(self)
|
110
|
+
self.manager_added()
|
69
111
|
|
70
112
|
@property
|
71
|
-
def
|
72
|
-
|
73
|
-
return datetime.fromtimestamp(0)
|
74
|
-
return self._last_run
|
113
|
+
def name(self) -> str:
|
114
|
+
return self._name
|
75
115
|
|
76
116
|
@property
|
77
|
-
def
|
78
|
-
|
79
|
-
return None
|
80
|
-
return self._next_run
|
117
|
+
def triggers(self) -> tuple[Trigger, ...]:
|
118
|
+
return self._triggers
|
81
119
|
|
82
120
|
@property
|
83
|
-
def
|
84
|
-
return self.
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
121
|
+
def time_measurement(self) -> TimeMeasurement:
|
122
|
+
return self._time_measurement
|
123
|
+
|
124
|
+
@property
|
125
|
+
def task_args(self) -> tuple[Any]:
|
126
|
+
return self._task_args
|
127
|
+
|
128
|
+
@property
|
129
|
+
def task_kwargs(self) -> dict[str, Any]:
|
130
|
+
return self._task_kwargs
|
131
|
+
|
132
|
+
@property
|
133
|
+
def last_run(self) -> Optional[_datetime]:
|
134
|
+
return self._last_run
|
96
135
|
|
97
|
-
def
|
98
|
-
|
136
|
+
def manager_added(self) -> None:
|
137
|
+
for trigger in self.triggers:
|
138
|
+
trigger.manager_added()
|
139
|
+
self.manager.logger.debug(f"{self.manager} -> Task {self} added.")
|
99
140
|
|
100
|
-
def
|
101
|
-
self.
|
141
|
+
def manager_removed(self) -> None:
|
142
|
+
for trigger in self.triggers:
|
143
|
+
trigger.manager_removed()
|
144
|
+
self.manager.logger.debug(f"{self.manager} -> Task {self} removed.")
|
102
145
|
|
103
|
-
def payload(self):
|
104
|
-
self._payload(*self.
|
146
|
+
def payload(self) -> None:
|
147
|
+
self._payload(*self.task_args, **self.task_kwargs)
|