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
@@ -1,24 +1,43 @@
|
|
1
|
+
import threading
|
1
2
|
from abc import ABC, abstractmethod
|
2
|
-
from datetime import datetime, timedelta
|
3
|
-
from typing import Optional
|
3
|
+
from datetime import datetime as _datetime, timedelta
|
4
|
+
from typing import Optional, TYPE_CHECKING
|
5
|
+
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from wiederverwendbar.task_manger.task import Task
|
4
8
|
|
5
9
|
|
6
10
|
class Trigger(ABC):
|
7
11
|
def __init__(self):
|
8
|
-
self.
|
12
|
+
self.lock = threading.Lock()
|
13
|
+
self._task = None
|
9
14
|
|
10
|
-
|
11
|
-
|
15
|
+
def __str__(self):
|
16
|
+
return f"{self.__class__.__name__}()"
|
17
|
+
|
18
|
+
def __call__(self):
|
19
|
+
if self.task is None:
|
20
|
+
raise ValueError(f"Trigger {self} is not assigned to a task.")
|
21
|
+
return self.check()
|
22
|
+
|
23
|
+
def manager_added(self) -> None:
|
12
24
|
...
|
13
25
|
|
26
|
+
def manager_removed(self) -> None:
|
27
|
+
...
|
28
|
+
|
29
|
+
@property
|
30
|
+
def task(self) -> Optional["Task"]:
|
31
|
+
return self._task
|
32
|
+
|
14
33
|
@abstractmethod
|
15
|
-
def
|
34
|
+
def check(self) -> bool:
|
16
35
|
...
|
17
36
|
|
18
37
|
|
19
38
|
class Interval(Trigger):
|
20
39
|
def __init__(self,
|
21
|
-
seconds:
|
40
|
+
seconds: float = 0,
|
22
41
|
minutes: int = 0,
|
23
42
|
hours: int = 0,
|
24
43
|
days: int = 0,
|
@@ -27,30 +46,18 @@ class Interval(Trigger):
|
|
27
46
|
years: int = 0):
|
28
47
|
super().__init__()
|
29
48
|
|
30
|
-
|
31
|
-
self.
|
32
|
-
self.minutes
|
33
|
-
self.hours
|
34
|
-
self.days
|
35
|
-
self.weeks
|
36
|
-
self.months
|
37
|
-
self.years
|
38
|
-
|
39
|
-
def init(self, manager):
|
40
|
-
self.manager = manager
|
41
|
-
|
42
|
-
# calculate interval in seconds
|
43
|
-
self.interval = self.seconds
|
44
|
-
self.interval += self.minutes * 60
|
45
|
-
self.interval += self.hours * 60 * 60
|
46
|
-
self.interval += self.days * 60 * 60 * 24
|
47
|
-
self.interval += self.weeks * 60 * 60 * 24 * 7
|
48
|
-
self.interval += self.months * 60 * 60 * 24 * 30
|
49
|
-
self.interval += self.years * 60 * 60 * 24 * 365
|
49
|
+
# calculate the interval in seconds
|
50
|
+
self._interval = float(seconds)
|
51
|
+
self._interval += minutes * 60
|
52
|
+
self._interval += hours * 60 * 60
|
53
|
+
self._interval += days * 60 * 60 * 24
|
54
|
+
self._interval += weeks * 60 * 60 * 24 * 7
|
55
|
+
self._interval += months * 60 * 60 * 24 * 30
|
56
|
+
self._interval += years * 60 * 60 * 24 * 365
|
50
57
|
|
51
58
|
def __str__(self):
|
52
59
|
interval = self.interval
|
53
|
-
interval_str = "Every "
|
60
|
+
interval_str = f"{self.__class__.__name__}(Every "
|
54
61
|
if interval >= 60 * 60 * 24 * 365:
|
55
62
|
years = interval // (60 * 60 * 24 * 365)
|
56
63
|
interval_str += f"{years}y "
|
@@ -77,24 +84,40 @@ class Interval(Trigger):
|
|
77
84
|
interval -= minutes * 60
|
78
85
|
if interval > 0:
|
79
86
|
interval_str += f"{interval}s "
|
80
|
-
interval_str = interval_str.strip()
|
87
|
+
interval_str = interval_str.strip() + ")"
|
81
88
|
return interval_str
|
82
89
|
|
83
90
|
def __int__(self):
|
91
|
+
return round(self.interval)
|
92
|
+
|
93
|
+
def __float__(self):
|
84
94
|
return self.interval
|
85
95
|
|
86
96
|
def __add__(self, other):
|
87
97
|
if isinstance(other, (Interval, int, float)):
|
88
|
-
return Interval(seconds=
|
89
|
-
|
90
|
-
raise ValueError(f"Can't add Interval and '{type(other)}'.")
|
98
|
+
return Interval(seconds=float(self) + float(other))
|
99
|
+
raise ValueError("Only Interval, int and float are supported.")
|
91
100
|
|
92
|
-
|
93
|
-
|
101
|
+
@property
|
102
|
+
def interval(self) -> float:
|
103
|
+
with self.lock:
|
104
|
+
return self._interval
|
105
|
+
|
106
|
+
@interval.setter
|
107
|
+
def interval(self, value: float):
|
108
|
+
with self.lock:
|
109
|
+
self._interval = value
|
110
|
+
|
111
|
+
def check(self) -> bool:
|
112
|
+
if self.task.last_run is None:
|
113
|
+
return True
|
114
|
+
if _datetime.now() - self.task.last_run > timedelta(seconds=self.interval):
|
115
|
+
return True
|
116
|
+
return False
|
94
117
|
|
95
118
|
|
96
119
|
class EverySeconds(Interval):
|
97
|
-
def __init__(self, seconds:
|
120
|
+
def __init__(self, seconds: float):
|
98
121
|
super().__init__(seconds=seconds)
|
99
122
|
|
100
123
|
|
@@ -135,28 +158,80 @@ class At(Trigger):
|
|
135
158
|
hour: Optional[int] = None,
|
136
159
|
day: Optional[int] = None,
|
137
160
|
month: Optional[int] = None,
|
138
|
-
year: Optional[int] = None
|
139
|
-
delay_for_seconds: int = 0,
|
140
|
-
delay_for_minutes: int = 0,
|
141
|
-
delay_for_hours: int = 0,
|
142
|
-
delay_for_days: int = 0):
|
161
|
+
year: Optional[int] = None):
|
143
162
|
super().__init__()
|
144
163
|
|
145
|
-
self.
|
146
|
-
self.
|
147
|
-
self.
|
148
|
-
self.
|
149
|
-
self.
|
150
|
-
self.
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
self.
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
164
|
+
self._second: Optional[int] = second
|
165
|
+
self._minute: Optional[int] = minute
|
166
|
+
self._hour: Optional[int] = hour
|
167
|
+
self._day: Optional[int] = day
|
168
|
+
self._month: Optional[int] = month
|
169
|
+
self._year: Optional[int] = year
|
170
|
+
|
171
|
+
@property
|
172
|
+
def second(self) -> Optional[int]:
|
173
|
+
with self.lock:
|
174
|
+
return self._second
|
175
|
+
|
176
|
+
@second.setter
|
177
|
+
def second(self, value: Optional[int]):
|
178
|
+
with self.lock:
|
179
|
+
self._second = value
|
180
|
+
|
181
|
+
@property
|
182
|
+
def minute(self) -> Optional[int]:
|
183
|
+
with self.lock:
|
184
|
+
return self._minute
|
185
|
+
|
186
|
+
@minute.setter
|
187
|
+
def minute(self, value: Optional[int]):
|
188
|
+
with self.lock:
|
189
|
+
self._minute = value
|
190
|
+
|
191
|
+
@property
|
192
|
+
def hour(self) -> Optional[int]:
|
193
|
+
with self.lock:
|
194
|
+
return self._hour
|
195
|
+
|
196
|
+
@hour.setter
|
197
|
+
def hour(self, value: Optional[int]):
|
198
|
+
with self.lock:
|
199
|
+
self._hour = value
|
200
|
+
|
201
|
+
@property
|
202
|
+
def day(self) -> Optional[int]:
|
203
|
+
with self.lock:
|
204
|
+
return self._day
|
205
|
+
|
206
|
+
@day.setter
|
207
|
+
def day(self, value: Optional[int]):
|
208
|
+
with self.lock:
|
209
|
+
self._day = value
|
210
|
+
|
211
|
+
@property
|
212
|
+
def month(self) -> Optional[int]:
|
213
|
+
with self.lock:
|
214
|
+
return self._month
|
215
|
+
|
216
|
+
@month.setter
|
217
|
+
def month(self, value: Optional[int]):
|
218
|
+
with self.lock:
|
219
|
+
self._month = value
|
220
|
+
|
221
|
+
@property
|
222
|
+
def year(self) -> Optional[int]:
|
223
|
+
with self.lock:
|
224
|
+
return self._year
|
225
|
+
|
226
|
+
@year.setter
|
227
|
+
def year(self, value: Optional[int]):
|
228
|
+
with self.lock:
|
229
|
+
self._year = value
|
230
|
+
|
231
|
+
def manager_added(self) -> None:
|
232
|
+
super().manager_added()
|
233
|
+
|
234
|
+
# check if some values are set
|
160
235
|
if not any([self.second is not None, self.minute is not None, self.hour is not None, self.day is not None, self.month is not None, self.year is not None]):
|
161
236
|
raise ValueError("At least one of the values must be set.")
|
162
237
|
|
@@ -179,108 +254,215 @@ class At(Trigger):
|
|
179
254
|
if self.year is not None:
|
180
255
|
at_str += f"year {self.year:04} "
|
181
256
|
if self.month is not None:
|
182
|
-
if at_str:
|
257
|
+
if at_str != "At ":
|
183
258
|
at_str += "and "
|
184
259
|
at_str += f"month {self.month:02} "
|
185
260
|
if self.day is not None:
|
186
|
-
if at_str:
|
261
|
+
if at_str != "At ":
|
187
262
|
at_str += "and "
|
188
263
|
at_str += f"day {self.day:02} "
|
189
264
|
if self.hour is not None:
|
190
|
-
if at_str:
|
265
|
+
if at_str != "At ":
|
191
266
|
at_str += "and "
|
192
267
|
at_str += f"hour {self.hour:02} "
|
193
268
|
if self.minute is not None:
|
194
|
-
if at_str:
|
269
|
+
if at_str != "At ":
|
195
270
|
at_str += "and "
|
196
271
|
at_str += f"minute {self.minute:02} "
|
197
272
|
if self.second is not None:
|
198
|
-
if at_str:
|
273
|
+
if at_str != "At ":
|
199
274
|
at_str += "and "
|
200
275
|
at_str += f"second {self.second:02} "
|
201
276
|
at_str = at_str.strip()
|
202
|
-
return at_str
|
277
|
+
return f"{self.__class__.__name__}({at_str})"
|
278
|
+
|
279
|
+
def check(self) -> bool:
|
280
|
+
next_run = _datetime.now().replace(microsecond=0)
|
203
281
|
|
204
|
-
def next(self) -> datetime:
|
205
|
-
n = datetime.now().replace(microsecond=0)
|
206
282
|
second = 0
|
207
283
|
if self.second is not None:
|
208
284
|
second = self.second
|
209
|
-
second_n =
|
210
|
-
if second_n <
|
211
|
-
|
212
|
-
|
285
|
+
second_n = next_run.replace(second=second)
|
286
|
+
if second_n < next_run:
|
287
|
+
next_run += timedelta(minutes=1)
|
288
|
+
if self.task.last_run is not None:
|
289
|
+
if second_n < self.task.last_run:
|
290
|
+
next_run += timedelta(minutes=1)
|
291
|
+
next_run = next_run.replace(second=second)
|
292
|
+
|
213
293
|
minute = 0
|
214
294
|
if self.minute is not None:
|
215
295
|
minute = self.minute
|
216
|
-
minute_n =
|
217
|
-
if minute_n <
|
218
|
-
|
219
|
-
|
296
|
+
minute_n = next_run.replace(minute=minute)
|
297
|
+
if minute_n < next_run:
|
298
|
+
next_run += timedelta(hours=1)
|
299
|
+
if self.task.last_run is not None:
|
300
|
+
if minute_n < self.task.last_run:
|
301
|
+
next_run += timedelta(hours=1)
|
302
|
+
next_run = next_run.replace(minute=minute, second=second)
|
303
|
+
|
220
304
|
hour = 0
|
221
305
|
if self.hour is not None:
|
222
306
|
hour = self.hour
|
223
|
-
hour_n =
|
224
|
-
if hour_n <
|
225
|
-
|
226
|
-
|
307
|
+
hour_n = next_run.replace(hour=hour)
|
308
|
+
if hour_n < next_run:
|
309
|
+
next_run += timedelta(days=1)
|
310
|
+
if self.task.last_run is not None:
|
311
|
+
if hour_n < self.task.last_run:
|
312
|
+
next_run += timedelta(days=1)
|
313
|
+
next_run = next_run.replace(hour=hour, minute=minute, second=second)
|
314
|
+
|
227
315
|
day = 1
|
228
316
|
if self.day is not None:
|
229
317
|
day = self.day
|
230
|
-
day_n =
|
231
|
-
if day_n <
|
318
|
+
day_n = next_run.replace(day=day)
|
319
|
+
if day_n < next_run:
|
232
320
|
while True:
|
233
|
-
|
234
|
-
if
|
321
|
+
next_run += timedelta(days=1)
|
322
|
+
if next_run.day == day:
|
235
323
|
break
|
236
|
-
|
324
|
+
if self.task.last_run is not None:
|
325
|
+
if day_n < self.task.last_run:
|
326
|
+
while True:
|
327
|
+
next_run += timedelta(days=1)
|
328
|
+
if next_run.day == day:
|
329
|
+
break
|
330
|
+
next_run = next_run.replace(day=day, hour=hour, minute=minute, second=second)
|
331
|
+
|
237
332
|
month = 1
|
238
333
|
if self.month is not None:
|
239
334
|
month = self.month
|
240
|
-
month_n =
|
241
|
-
if month_n <
|
335
|
+
month_n = next_run.replace(month=month)
|
336
|
+
if month_n < next_run:
|
242
337
|
while True:
|
243
|
-
|
244
|
-
if
|
338
|
+
next_run += timedelta(days=1)
|
339
|
+
if next_run.month == month:
|
245
340
|
break
|
246
|
-
|
341
|
+
if self.task.last_run is not None:
|
342
|
+
if month_n < self.task.last_run:
|
343
|
+
while True:
|
344
|
+
next_run += timedelta(days=1)
|
345
|
+
if next_run.month == month:
|
346
|
+
break
|
347
|
+
next_run = next_run.replace(month=month, day=day, hour=hour, minute=minute, second=second)
|
348
|
+
|
247
349
|
if self.year is not None:
|
248
|
-
|
350
|
+
next_run = next_run.replace(year=self.year, month=month, day=day, hour=hour, minute=minute, second=second)
|
249
351
|
|
250
|
-
|
352
|
+
if next_run <= _datetime.now():
|
353
|
+
return True
|
354
|
+
return False
|
251
355
|
|
252
356
|
|
253
|
-
class
|
254
|
-
def
|
255
|
-
|
357
|
+
class AtDatetime(Trigger):
|
358
|
+
def __init__(self,
|
359
|
+
datetime: Optional[_datetime] = None,
|
360
|
+
delay_for_seconds: int = 0,
|
361
|
+
delay_for_minutes: int = 0,
|
362
|
+
delay_for_hours: int = 0,
|
363
|
+
delay_for_days: int = 0):
|
364
|
+
super().__init__()
|
365
|
+
|
366
|
+
self._datetime = datetime
|
367
|
+
self._delay_for_seconds = delay_for_seconds
|
368
|
+
self._delay_for_minutes = delay_for_minutes
|
369
|
+
self._delay_for_hours = delay_for_hours
|
370
|
+
self._delay_for_days = delay_for_days
|
371
|
+
|
372
|
+
@property
|
373
|
+
def datetime(self) -> Optional[_datetime]:
|
374
|
+
with self.lock:
|
375
|
+
return self._datetime
|
376
|
+
|
377
|
+
@datetime.setter
|
378
|
+
def datetime(self, value: Optional[_datetime]):
|
379
|
+
with self.lock:
|
380
|
+
self._datetime = value
|
381
|
+
|
382
|
+
@property
|
383
|
+
def delay_for_seconds(self) -> int:
|
384
|
+
with self.lock:
|
385
|
+
return self._delay_for_seconds
|
386
|
+
|
387
|
+
@delay_for_seconds.setter
|
388
|
+
def delay_for_seconds(self, value: int):
|
389
|
+
with self.lock:
|
390
|
+
self._delay_for_seconds = value
|
391
|
+
|
392
|
+
@property
|
393
|
+
def delay_for_minutes(self) -> int:
|
394
|
+
with self.lock:
|
395
|
+
return self._delay_for_minutes
|
396
|
+
|
397
|
+
@delay_for_minutes.setter
|
398
|
+
def delay_for_minutes(self, value: int):
|
399
|
+
with self.lock:
|
400
|
+
self._delay_for_minutes = value
|
401
|
+
|
402
|
+
@property
|
403
|
+
def delay_for_hours(self) -> int:
|
404
|
+
with self.lock:
|
405
|
+
return self._delay_for_hours
|
406
|
+
|
407
|
+
@delay_for_hours.setter
|
408
|
+
def delay_for_hours(self, value: int):
|
409
|
+
with self.lock:
|
410
|
+
self._delay_for_hours = value
|
411
|
+
|
412
|
+
@property
|
413
|
+
def delay_for_days(self) -> int:
|
414
|
+
with self.lock:
|
415
|
+
return self._delay_for_days
|
416
|
+
|
417
|
+
@delay_for_days.setter
|
418
|
+
def delay_for_days(self, value: int):
|
419
|
+
with self.lock:
|
420
|
+
self._delay_for_days = value
|
421
|
+
|
422
|
+
def manager_added(self) -> None:
|
423
|
+
super().manager_added()
|
424
|
+
|
425
|
+
if self.datetime is None:
|
426
|
+
raise ValueError("Datetime must be set.")
|
427
|
+
if not isinstance(self.datetime, _datetime):
|
428
|
+
raise ValueError("Datetime must be an instance of datetime.")
|
429
|
+
|
430
|
+
self.datetime = self.datetime + timedelta(seconds=self.delay_for_seconds,
|
431
|
+
minutes=self.delay_for_minutes,
|
432
|
+
hours=self.delay_for_hours,
|
433
|
+
days=self.delay_for_days)
|
434
|
+
|
435
|
+
def check(self) -> bool:
|
436
|
+
if self.task.last_run is None:
|
437
|
+
return True
|
438
|
+
return False
|
439
|
+
|
440
|
+
|
441
|
+
class _AtPredefinedDatetime(AtDatetime):
|
442
|
+
def __init__(self,
|
443
|
+
delay_for_seconds: int = 0,
|
444
|
+
delay_for_minutes: int = 0,
|
445
|
+
delay_for_hours: int = 0,
|
446
|
+
delay_for_days: int = 0):
|
447
|
+
super().__init__(delay_for_seconds=delay_for_seconds,
|
448
|
+
delay_for_minutes=delay_for_minutes,
|
449
|
+
delay_for_hours=delay_for_hours,
|
450
|
+
delay_for_days=delay_for_days)
|
256
451
|
|
257
|
-
now = datetime.now() + timedelta(seconds=self.delay_for_seconds,
|
258
|
-
minutes=self.delay_for_minutes,
|
259
|
-
hours=self.delay_for_hours,
|
260
|
-
days=self.delay_for_days)
|
261
|
-
self.second = now.second
|
262
|
-
self.minute = now.minute
|
263
|
-
self.hour = now.hour
|
264
|
-
self.day = now.day
|
265
|
-
self.month = now.month
|
266
|
-
self.year = now.year
|
267
452
|
|
268
|
-
|
453
|
+
class AtNow(_AtPredefinedDatetime):
|
454
|
+
def manager_added(self) -> None:
|
455
|
+
self.datetime = _datetime.now()
|
456
|
+
super().manager_added()
|
269
457
|
|
270
458
|
|
271
|
-
class
|
272
|
-
def
|
273
|
-
self.
|
459
|
+
class AtManagerCreation(AtDatetime):
|
460
|
+
def manager_added(self) -> None:
|
461
|
+
self.datetime = self.task.manager.creation_time
|
462
|
+
super().manager_added()
|
274
463
|
|
275
|
-
creation_time = self.manager.creation_time + timedelta(seconds=self.delay_for_seconds,
|
276
|
-
minutes=self.delay_for_minutes,
|
277
|
-
hours=self.delay_for_hours,
|
278
|
-
days=self.delay_for_days)
|
279
|
-
self.second = creation_time.second
|
280
|
-
self.minute = creation_time.minute
|
281
|
-
self.hour = creation_time.hour
|
282
|
-
self.day = creation_time.day
|
283
|
-
self.month = creation_time.month
|
284
|
-
self.year = creation_time.year
|
285
464
|
|
286
|
-
|
465
|
+
class AtManagerStart(AtDatetime):
|
466
|
+
def manager_added(self) -> None:
|
467
|
+
self.datetime = self.task.manager.start_time
|
468
|
+
super().manager_added()
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import asyncio
|
2
|
+
import threading
|
3
|
+
import time
|
4
|
+
from typing import Union
|
5
|
+
|
6
|
+
_TIMERS: dict[str, float] = {}
|
7
|
+
_lock = threading.Lock()
|
8
|
+
|
9
|
+
|
10
|
+
def timer(name: str, seconds: Union[float, int]) -> bool:
|
11
|
+
global _TIMERS
|
12
|
+
|
13
|
+
seconds = float(seconds)
|
14
|
+
|
15
|
+
if name not in _TIMERS:
|
16
|
+
with _lock:
|
17
|
+
_TIMERS[name] = time.perf_counter() + seconds
|
18
|
+
wait = True
|
19
|
+
else:
|
20
|
+
with _lock:
|
21
|
+
delta = time.perf_counter() - _TIMERS[name]
|
22
|
+
wait = delta < 0
|
23
|
+
if wait:
|
24
|
+
...
|
25
|
+
else:
|
26
|
+
clear_timer(name=name)
|
27
|
+
return wait
|
28
|
+
|
29
|
+
|
30
|
+
def timer_loop(name: str, seconds: Union[float, int], loop_delay: float = 0.001) -> None:
|
31
|
+
while timer(name, seconds):
|
32
|
+
time.sleep(loop_delay)
|
33
|
+
|
34
|
+
|
35
|
+
async def timer_loop_async(name: str, seconds: Union[float, int], loop_delay: float = 0.001) -> None:
|
36
|
+
while timer(name, seconds):
|
37
|
+
await asyncio.sleep(loop_delay)
|
38
|
+
|
39
|
+
|
40
|
+
def clear_timer(name: str) -> None:
|
41
|
+
global _TIMERS
|
42
|
+
if name in _TIMERS:
|
43
|
+
with _lock:
|
44
|
+
del _TIMERS[name]
|
45
|
+
|
46
|
+
|
47
|
+
def clear_all_timers() -> None:
|
48
|
+
global _TIMERS
|
49
|
+
with _lock:
|
50
|
+
_TIMERS = {}
|
51
|
+
|
52
|
+
|
53
|
+
if __name__ == '__main__':
|
54
|
+
try:
|
55
|
+
while True:
|
56
|
+
print(time.time())
|
57
|
+
timer_loop("test-loop", 1)
|
58
|
+
except KeyboardInterrupt:
|
59
|
+
...
|
60
|
+
|
61
|
+
print("end")
|
wiederverwendbar/typer/app.py
CHANGED
@@ -136,11 +136,15 @@ class Typer(_Typer):
|
|
136
136
|
# register the main callback
|
137
137
|
self.callback()(self.main_callback)
|
138
138
|
|
139
|
+
@property
|
140
|
+
def title_header(self) -> str:
|
141
|
+
return text2art(self.title)
|
142
|
+
|
139
143
|
def main_callback(self, *args, **kwargs):
|
140
144
|
...
|
141
145
|
|
142
146
|
def info_command(self) -> Optional[int]:
|
143
|
-
card_body = [
|
147
|
+
card_body = [self.title_header]
|
144
148
|
second_section = ""
|
145
149
|
if self.description is not None:
|
146
150
|
second_section += f"{self.description}"
|