classic-scheduler 1.0.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Sergei Variasov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.1
2
+ Name: classic-scheduler
3
+ Version: 1.0.0
4
+ Summary: Provides scheduler
5
+ Home-page: https://github.com/variasov/classic-scheduler
6
+ Author: Sergei Variasov, Vasiliy Pichugin
7
+ Author-email: variasov@gmail.com, pichugin_v_i@mail.ru
8
+ Project-URL: Bug Tracker, https://github.com/variasov/classic-scheduler/issues
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Development Status :: 4 - Beta
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ Provides-Extra: dev
16
+ License-File: LICENSE
17
+
18
+ # Classic Scheduler
19
+
20
+ ## Планировщик
21
+
22
+ Данная библиотека является продолжением библиотеки акторов `classic actors`. И, как следствие, она зависит от библиотеки акторов.
23
+
24
+ Планировщик - это актор, который вызывает функции по расписанию.
25
+
26
+ Рассмотрим пример использования библиотеки:
27
+
28
+ ```python
29
+ def func():
30
+ print('Hello world!')
31
+
32
+ scheduler = Scheduler()
33
+ scheduler.with_delay(1, func)
34
+ scheduler.by_period(5, func)
35
+ scheduler.by_cron('* * * * *', func)
36
+ scheduler.run()
37
+
38
+ # Hello world!
39
+ # Hello world!
40
+ # Hello world!
41
+ # ...
42
+ ```
43
+
44
+ ## Отмена вызова (удаление задачи)
45
+
46
+ Если расписание задач планируется динамически менять, например, удалить ранее созданную задачу, то при создании задачи в методах необходимо указать атрибут `task_name`. Пример:
47
+
48
+ ```python
49
+ scheduler = Scheduler()
50
+ scheduler.by_period(1, func, task_name='periodic task')
51
+ ```
52
+
53
+ Тогда в дальнейшем вы можете отменить запланированные запуски задач через:
54
+
55
+ ```python
56
+ scheduler.cancel('periodic task')
57
+ ```
58
+
59
+ ## Параметризация вызываемой функции
60
+
61
+ Планировщик может запускать и функции с аргументами. Аргументы передаются через атрибуты `args` и `kwargs`. Отдельно заметим, что вызываемые объекты так же могут быть акторами, как в примере ниже:
62
+
63
+ ```python
64
+ class SomeActor(Actor):
65
+
66
+ @Actor.method
67
+ def func(self, a, b):
68
+ print('a + b =', a + b)
69
+
70
+ some_actor = SomeActor()
71
+ some_actor.run()
72
+
73
+ scheduler = Scheduler()
74
+ scheduler.run()
75
+ scheduler.by_period(
76
+ 2,
77
+ some_actor.func,
78
+ args=(2, 6),
79
+ task_name='some_actor_task',
80
+ )
81
+ # a + b = 8
82
+ # a + b = 8
83
+ # a + b = 8
84
+ # ...
85
+ ```
86
+
87
+ ## Типы задач
88
+
89
+ После создания экземпляра планировщика нужно добавить в него задач. Планирование и выполнение задач начнется только после запуска метода `run()`. Отметим, что добавлять задачи в планировщик можно и после вызова метода `run()`.
90
+
91
+ Планировщик поддерживает три типы задач:
92
+
93
+ ### Одноразовая отложенная задача
94
+
95
+ Создается через вызов метода планировщика `with_delay`. Задача будет выполнена ровно один раз. Ее вызов отложен на `delay` указанный либо в секундах в `float` либо в `timedelta`.
96
+
97
+ ### Переодическая задача
98
+
99
+ Создается через вызов метода планировщика `by_period`. Задача выполнится в первый раз, затем она будет выполняется через период `periodic` заданный либо в секундах в `float` либо в `timedelta`.
100
+
101
+ ### Задача по расписанию
102
+
103
+ Создается через вызов метода планировщика `by_cron`. Задача вызов который задается в формате `CRON` строкой.
@@ -0,0 +1,86 @@
1
+ # Classic Scheduler
2
+
3
+ ## Планировщик
4
+
5
+ Данная библиотека является продолжением библиотеки акторов `classic actors`. И, как следствие, она зависит от библиотеки акторов.
6
+
7
+ Планировщик - это актор, который вызывает функции по расписанию.
8
+
9
+ Рассмотрим пример использования библиотеки:
10
+
11
+ ```python
12
+ def func():
13
+ print('Hello world!')
14
+
15
+ scheduler = Scheduler()
16
+ scheduler.with_delay(1, func)
17
+ scheduler.by_period(5, func)
18
+ scheduler.by_cron('* * * * *', func)
19
+ scheduler.run()
20
+
21
+ # Hello world!
22
+ # Hello world!
23
+ # Hello world!
24
+ # ...
25
+ ```
26
+
27
+ ## Отмена вызова (удаление задачи)
28
+
29
+ Если расписание задач планируется динамически менять, например, удалить ранее созданную задачу, то при создании задачи в методах необходимо указать атрибут `task_name`. Пример:
30
+
31
+ ```python
32
+ scheduler = Scheduler()
33
+ scheduler.by_period(1, func, task_name='periodic task')
34
+ ```
35
+
36
+ Тогда в дальнейшем вы можете отменить запланированные запуски задач через:
37
+
38
+ ```python
39
+ scheduler.cancel('periodic task')
40
+ ```
41
+
42
+ ## Параметризация вызываемой функции
43
+
44
+ Планировщик может запускать и функции с аргументами. Аргументы передаются через атрибуты `args` и `kwargs`. Отдельно заметим, что вызываемые объекты так же могут быть акторами, как в примере ниже:
45
+
46
+ ```python
47
+ class SomeActor(Actor):
48
+
49
+ @Actor.method
50
+ def func(self, a, b):
51
+ print('a + b =', a + b)
52
+
53
+ some_actor = SomeActor()
54
+ some_actor.run()
55
+
56
+ scheduler = Scheduler()
57
+ scheduler.run()
58
+ scheduler.by_period(
59
+ 2,
60
+ some_actor.func,
61
+ args=(2, 6),
62
+ task_name='some_actor_task',
63
+ )
64
+ # a + b = 8
65
+ # a + b = 8
66
+ # a + b = 8
67
+ # ...
68
+ ```
69
+
70
+ ## Типы задач
71
+
72
+ После создания экземпляра планировщика нужно добавить в него задач. Планирование и выполнение задач начнется только после запуска метода `run()`. Отметим, что добавлять задачи в планировщик можно и после вызова метода `run()`.
73
+
74
+ Планировщик поддерживает три типы задач:
75
+
76
+ ### Одноразовая отложенная задача
77
+
78
+ Создается через вызов метода планировщика `with_delay`. Задача будет выполнена ровно один раз. Ее вызов отложен на `delay` указанный либо в секундах в `float` либо в `timedelta`.
79
+
80
+ ### Переодическая задача
81
+
82
+ Создается через вызов метода планировщика `by_period`. Задача выполнится в первый раз, затем она будет выполняется через период `periodic` заданный либо в секундах в `float` либо в `timedelta`.
83
+
84
+ ### Задача по расписанию
85
+
86
+ Создается через вызов метода планировщика `by_cron`. Задача вызов который задается в формате `CRON` строкой.
@@ -0,0 +1,6 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools >= 40.6.0",
4
+ "wheel"
5
+ ]
6
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,39 @@
1
+ [metadata]
2
+ name = classic-scheduler
3
+ version = 1.0.0
4
+ description = Provides scheduler
5
+ author = Sergei Variasov, Vasiliy Pichugin
6
+ author_email = variasov@gmail.com, pichugin_v_i@mail.ru
7
+ long_description = file: README.md
8
+ long_description_content_type = text/markdown
9
+ url = https://github.com/variasov/classic-scheduler
10
+ project_urls =
11
+ Bug Tracker = https://github.com/variasov/classic-scheduler/issues
12
+ classifiers =
13
+ Programming Language :: Python :: 3.10
14
+ License :: OSI Approved :: MIT License
15
+ Operating System :: OS Independent
16
+ Development Status :: 4 - Beta
17
+
18
+ [options]
19
+ packages = find_namespace:
20
+ python_requires = >=3.10
21
+ install_requires =
22
+ croniter
23
+ package_dir =
24
+ = sources
25
+
26
+ [options.packages.find]
27
+ where = sources
28
+
29
+ [options.extras_require]
30
+ dev =
31
+ pytest~=6.2
32
+ pytest-cov~=2.12
33
+ twine~=3.4
34
+ build~=0.7
35
+
36
+ [egg_info]
37
+ tag_build =
38
+ tag_date = 0
39
+
@@ -0,0 +1,3 @@
1
+ from setuptools import setup
2
+
3
+ setup()
@@ -0,0 +1 @@
1
+ from .scheduler import BaseScheduler, SimpleScheduler, ThreadPoolScheduler
@@ -0,0 +1,245 @@
1
+ import heapq
2
+ from abc import ABC, abstractmethod
3
+ from concurrent.futures import ThreadPoolExecutor
4
+ from datetime import datetime, timedelta, timezone
5
+ from queue import Empty, Queue
6
+ from threading import Thread
7
+ from typing import Any, Callable, Union
8
+
9
+ from .task import (STOP, CronTask, OneTimeTask, PeriodicTask,
10
+ ScheduleCancellation, Task)
11
+
12
+
13
+ class BaseScheduler(ABC):
14
+ """
15
+ Планировщик задач. Вызывает переданные ему Callable объекты в соответствии
16
+ с расписанием.
17
+ """
18
+ _stopped: bool
19
+
20
+ def __init__(self) -> None:
21
+ super().__init__()
22
+ self._thread = Thread(target=self.loop)
23
+ self._tasks = []
24
+ self._inbox = Queue()
25
+
26
+ def _get_timeout(self) -> float:
27
+ """
28
+ Возвращает время ожидания выполнения следующей задачи.
29
+
30
+ Returns:
31
+ float: тайм-аут ожидания задачи.
32
+ """
33
+ if len(self._tasks) < 1:
34
+ return 60
35
+
36
+ dt_next_task = self._tasks[0].next_run_time
37
+ dt_current = datetime.now(timezone.utc)
38
+ if dt_next_task < dt_current:
39
+ return 0
40
+ else:
41
+ return (dt_next_task - dt_current).total_seconds()
42
+
43
+ def _schedule(self, task: Task) -> None:
44
+ heapq.heappush(self._tasks, task)
45
+
46
+ def _unshedule(self, task_name: str) -> None:
47
+ """
48
+ Отменяет выполнение запланированной задачи.
49
+
50
+ Args:
51
+ task_name (str): Имя задачи для отмены.
52
+ """
53
+ tasks = [t for t in self._tasks if t.name != task_name]
54
+ if len(self._tasks) != len(tasks):
55
+ heapq.heapify(tasks)
56
+ self._tasks = tasks
57
+
58
+ @abstractmethod
59
+ def _execute(self, task: Task) -> None:
60
+ """
61
+ Выполняет текущую задачу.
62
+
63
+ Args:
64
+ task (Task): Задача, которую необходимо выполнить.
65
+ """
66
+ pass
67
+
68
+ def _stop(self) -> None:
69
+ self._stopped = True
70
+
71
+ def loop(self) -> None:
72
+ """
73
+ Главный цикл выполнения планировщика.
74
+ """
75
+ self._stopped = False
76
+ while not self._stopped:
77
+ try:
78
+ timeout = self._get_timeout()
79
+ if timeout > 0:
80
+ try:
81
+ command = self._inbox.get(block=True, timeout=timeout)
82
+ except Empty:
83
+ continue
84
+
85
+ if isinstance(command, Task):
86
+ self._schedule(command)
87
+ elif isinstance(command, ScheduleCancellation):
88
+ self._unshedule(command.task_name)
89
+ elif command is STOP:
90
+ break
91
+ else:
92
+ task: Task = heapq.heappop(self._tasks)
93
+ self._execute(task)
94
+
95
+ task.set_next_run_time()
96
+ if task.next_run_time:
97
+ self._schedule(task)
98
+ except KeyboardInterrupt:
99
+ break
100
+ self._stop()
101
+
102
+ def with_delay(
103
+ self,
104
+ delay: Union[timedelta, float],
105
+ job: Callable[[], None],
106
+ args: tuple = None,
107
+ kwargs: dict = None,
108
+ task_name: str = None,
109
+ ) -> None:
110
+ """
111
+ Добавляет отложенную задачу в планировщик.
112
+ Задача будет выполнена ровно один раз.
113
+
114
+ Args:
115
+ delay (Union[timedelta, float]): Отсрочка выполнения задачи.
116
+ job (Callable[[], Any]): Вызываемая в задаче функция.
117
+ args (tuple, optional): Аргументы. По умолчанию None.
118
+ kwargs (dict, optional): Аргументы. По умолчанию None.
119
+ task_name (str, optional): Имя таски. По умолчанию None.
120
+ """
121
+ if not isinstance(delay, timedelta):
122
+ delay = timedelta(seconds=delay)
123
+
124
+ date = datetime.now(timezone.utc) + delay
125
+ task = OneTimeTask(
126
+ date=date,
127
+ job=job,
128
+ args=args,
129
+ kwargs=kwargs,
130
+ name=task_name,
131
+ )
132
+ self._inbox.put(task)
133
+
134
+ def by_cron(
135
+ self,
136
+ schedule: str,
137
+ job: Callable[[], Any],
138
+ args: tuple = None,
139
+ kwargs: dict = None,
140
+ task_name: str = None,
141
+ ) -> None:
142
+ """
143
+ Добавляет задачу которая будет выполнятся по CRON расписанию.
144
+
145
+ Args:
146
+ schedule (str): Расписание задачи в CRON формате.
147
+ job (Callable[[], Any]): Вызываемая в задаче функция.
148
+ args (tuple, optional): Аргументы. По умолчанию None.
149
+ kwargs (dict, optional): Аргументы. По умолчанию None.
150
+ task_name (str, optional): Имя таски. По умолчанию None.
151
+ """
152
+ task = CronTask(
153
+ schedule=schedule,
154
+ job=job,
155
+ args=args,
156
+ kwargs=kwargs,
157
+ name=task_name,
158
+ )
159
+ self._inbox.put(task)
160
+
161
+ def by_period(
162
+ self,
163
+ period: Union[timedelta, float],
164
+ job: Callable[[], Any],
165
+ args: tuple = None,
166
+ kwargs: dict = None,
167
+ task_name: str = None,
168
+ ) -> None:
169
+ """
170
+ Добавляет задачу которая будет выполняется периодически через
171
+ указанный интервал времени.
172
+
173
+ Args:
174
+ period (Union[timedelta, float]): Период выполнения задачи.
175
+ job (Callable[[], Any]): Вызываемая в задаче функция.
176
+ args (tuple, optional): Аргументы. По умолчанию None.
177
+ kwargs (dict, optional): Аргументы. По умолчанию None.
178
+ task_name (_type_, optional): Имя таски. По умолчанию None.
179
+ """
180
+ if not isinstance(period, timedelta):
181
+ period = timedelta(seconds=period)
182
+
183
+ task = PeriodicTask(
184
+ period=period,
185
+ job=job,
186
+ args=args,
187
+ kwargs=kwargs,
188
+ name=task_name,
189
+ )
190
+ self._inbox.put(task)
191
+
192
+ def unshedule(self, task_name: str) -> None:
193
+ """
194
+ Отменяет выполнение запланированной задачи.
195
+
196
+ Args:
197
+ task_name (str): Имя задачи для отмены.
198
+ """
199
+ cancellation = ScheduleCancellation(task_name=task_name)
200
+ self._inbox.put(cancellation)
201
+
202
+ def start(self) -> None:
203
+ """
204
+ Запускает выполнение планировщика.
205
+ """
206
+ self._thread.start()
207
+
208
+ def stop(self) -> None:
209
+ """
210
+ Завершает выполнение планировщика.
211
+ """
212
+ self._inbox.put(STOP)
213
+
214
+
215
+ class SimpleScheduler(BaseScheduler):
216
+ """
217
+ Планировщик задач. Вызывает переданные ему Callable объекты в своем потоке
218
+ в соответствии с расписанием. Поскольку поток один, то не допускается
219
+ параллельное выполнение задач.
220
+ """
221
+
222
+ def _execute(self, task: Task) -> None:
223
+ task.run_job()
224
+
225
+
226
+ class ThreadPoolScheduler(BaseScheduler):
227
+ """
228
+ Планировщик задач. Вызывает переданные ему Callable объекты в пуле потоков
229
+ в соответствии с расписанием.
230
+
231
+ Данный планировщик не предоставляет гарантий
232
+ того, что одна и та же задача не будет работать в разных потоках
233
+ одновременно.
234
+ """
235
+
236
+ def __init__(self, workers_count: int = 5):
237
+ super().__init__()
238
+ self._thread_pool = ThreadPoolExecutor(max_workers=workers_count)
239
+
240
+ def _execute(self, task: Task) -> None:
241
+ self._thread_pool.submit(task.run_job)
242
+
243
+ def _stop(self) -> None:
244
+ super()._stop()
245
+ self._thread_pool.shutdown(wait=True)
@@ -0,0 +1,128 @@
1
+ import logging
2
+ from abc import ABC, abstractmethod
3
+ from dataclasses import dataclass
4
+ from datetime import datetime, timedelta, timezone
5
+ from typing import Any, Callable, Optional
6
+
7
+ from croniter import croniter
8
+
9
+
10
+ class Task(ABC):
11
+ """
12
+ Задача, выполняемая в потоке планировщика.
13
+ """
14
+
15
+ def __init__(
16
+ self,
17
+ job: Callable[[...], None],
18
+ args: tuple = None,
19
+ kwargs: dict = None,
20
+ name: Optional[str] = None,
21
+ ) -> None:
22
+ self._job = job
23
+ self._args = args or ()
24
+ self._kwargs = kwargs or {}
25
+ self._name = name
26
+ self._next_run_time = None # время следующего запуска задачи
27
+
28
+ @property
29
+ def name(self) -> str:
30
+ return self._name
31
+
32
+ @property
33
+ def next_run_time(self) -> datetime:
34
+ """
35
+ Свойство возвращающее время следующего запуска.
36
+
37
+ Returns:
38
+ datetime: Время следующего запуска.
39
+ """
40
+ return self._next_run_time
41
+
42
+ @abstractmethod
43
+ def set_next_run_time(self) -> None:
44
+ """
45
+ Метод рассчитывающий у устанавливающий время следующего запуска.
46
+ Переопределяется в дочерних классах.
47
+ """
48
+
49
+ def run_job(self) -> None:
50
+ """
51
+ Вызывает переданный Callable объект со всем аргументами
52
+ в потоке планировщика.
53
+ """
54
+ logging.info(
55
+ 'Task [%s] started with schedule [%s]',
56
+ self._name,
57
+ self._next_run_time,
58
+ )
59
+
60
+ try:
61
+ self._job(*self._args, **self._kwargs)
62
+ except Exception as ex:
63
+ logging.exception(
64
+ 'Unexpected error occurred in task [%s]: "%s".',
65
+ self._name,
66
+ ex,
67
+ )
68
+ else:
69
+ logging.info('Task completed [%s]', self._name)
70
+
71
+ def __lt__(self, other: 'Task') -> bool:
72
+ return self._next_run_time < other._next_run_time
73
+
74
+ def __gt__(self, other: 'Task') -> bool:
75
+ return self._next_run_time > other._next_run_time
76
+
77
+
78
+ class OneTimeTask(Task):
79
+ """
80
+ Одноразовая задача, которая будет выполнена ровно один раз.
81
+ """
82
+
83
+ def __init__(self, date: datetime, *args: Any, **kwargs: Any) -> None:
84
+ super().__init__(*args, **kwargs)
85
+ self._next_run_time = date
86
+
87
+ def set_next_run_time(self) -> None:
88
+ self._next_run_time = None
89
+
90
+
91
+ class CronTask(Task):
92
+ """
93
+ Задача которая выполняется по CRON расписанию.
94
+ """
95
+
96
+ def __init__(self, schedule: str, *args: Any, **kwargs: Any) -> None:
97
+ super().__init__(*args, **kwargs)
98
+ self._schedule = croniter(schedule, datetime.now(timezone.utc))
99
+ self._next_run_time = self._schedule.get_next(datetime)
100
+
101
+ def set_next_run_time(self) -> None:
102
+ self._next_run_time = self._schedule.get_next(datetime)
103
+
104
+
105
+ class PeriodicTask(Task):
106
+ """
107
+ Задача которая будет выполняется переодически
108
+ через указанный интервал времени.
109
+ """
110
+
111
+ def __init__(self, period: timedelta, *args: Any, **kwargs: Any) -> None:
112
+ super().__init__(*args, **kwargs)
113
+ self._period = period
114
+ self._next_run_time = datetime.now(timezone.utc)
115
+
116
+ def set_next_run_time(self) -> None:
117
+ self._next_run_time = datetime.now(timezone.utc) + self._period
118
+
119
+
120
+ @dataclass
121
+ class ScheduleCancellation:
122
+ """
123
+ Команда отмены запланированной задачи.
124
+ """
125
+ task_name: str
126
+
127
+
128
+ STOP = object()
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.1
2
+ Name: classic-scheduler
3
+ Version: 1.0.0
4
+ Summary: Provides scheduler
5
+ Home-page: https://github.com/variasov/classic-scheduler
6
+ Author: Sergei Variasov, Vasiliy Pichugin
7
+ Author-email: variasov@gmail.com, pichugin_v_i@mail.ru
8
+ Project-URL: Bug Tracker, https://github.com/variasov/classic-scheduler/issues
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Development Status :: 4 - Beta
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ Provides-Extra: dev
16
+ License-File: LICENSE
17
+
18
+ # Classic Scheduler
19
+
20
+ ## Планировщик
21
+
22
+ Данная библиотека является продолжением библиотеки акторов `classic actors`. И, как следствие, она зависит от библиотеки акторов.
23
+
24
+ Планировщик - это актор, который вызывает функции по расписанию.
25
+
26
+ Рассмотрим пример использования библиотеки:
27
+
28
+ ```python
29
+ def func():
30
+ print('Hello world!')
31
+
32
+ scheduler = Scheduler()
33
+ scheduler.with_delay(1, func)
34
+ scheduler.by_period(5, func)
35
+ scheduler.by_cron('* * * * *', func)
36
+ scheduler.run()
37
+
38
+ # Hello world!
39
+ # Hello world!
40
+ # Hello world!
41
+ # ...
42
+ ```
43
+
44
+ ## Отмена вызова (удаление задачи)
45
+
46
+ Если расписание задач планируется динамически менять, например, удалить ранее созданную задачу, то при создании задачи в методах необходимо указать атрибут `task_name`. Пример:
47
+
48
+ ```python
49
+ scheduler = Scheduler()
50
+ scheduler.by_period(1, func, task_name='periodic task')
51
+ ```
52
+
53
+ Тогда в дальнейшем вы можете отменить запланированные запуски задач через:
54
+
55
+ ```python
56
+ scheduler.cancel('periodic task')
57
+ ```
58
+
59
+ ## Параметризация вызываемой функции
60
+
61
+ Планировщик может запускать и функции с аргументами. Аргументы передаются через атрибуты `args` и `kwargs`. Отдельно заметим, что вызываемые объекты так же могут быть акторами, как в примере ниже:
62
+
63
+ ```python
64
+ class SomeActor(Actor):
65
+
66
+ @Actor.method
67
+ def func(self, a, b):
68
+ print('a + b =', a + b)
69
+
70
+ some_actor = SomeActor()
71
+ some_actor.run()
72
+
73
+ scheduler = Scheduler()
74
+ scheduler.run()
75
+ scheduler.by_period(
76
+ 2,
77
+ some_actor.func,
78
+ args=(2, 6),
79
+ task_name='some_actor_task',
80
+ )
81
+ # a + b = 8
82
+ # a + b = 8
83
+ # a + b = 8
84
+ # ...
85
+ ```
86
+
87
+ ## Типы задач
88
+
89
+ После создания экземпляра планировщика нужно добавить в него задач. Планирование и выполнение задач начнется только после запуска метода `run()`. Отметим, что добавлять задачи в планировщик можно и после вызова метода `run()`.
90
+
91
+ Планировщик поддерживает три типы задач:
92
+
93
+ ### Одноразовая отложенная задача
94
+
95
+ Создается через вызов метода планировщика `with_delay`. Задача будет выполнена ровно один раз. Ее вызов отложен на `delay` указанный либо в секундах в `float` либо в `timedelta`.
96
+
97
+ ### Переодическая задача
98
+
99
+ Создается через вызов метода планировщика `by_period`. Задача выполнится в первый раз, затем она будет выполняется через период `periodic` заданный либо в секундах в `float` либо в `timedelta`.
100
+
101
+ ### Задача по расписанию
102
+
103
+ Создается через вызов метода планировщика `by_cron`. Задача вызов который задается в формате `CRON` строкой.
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ setup.cfg
5
+ setup.py
6
+ sources/classic/scheduler/__init__.py
7
+ sources/classic/scheduler/scheduler.py
8
+ sources/classic/scheduler/task.py
9
+ sources/classic_scheduler.egg-info/PKG-INFO
10
+ sources/classic_scheduler.egg-info/SOURCES.txt
11
+ sources/classic_scheduler.egg-info/dependency_links.txt
12
+ sources/classic_scheduler.egg-info/requires.txt
13
+ sources/classic_scheduler.egg-info/top_level.txt
@@ -0,0 +1,7 @@
1
+ croniter
2
+
3
+ [dev]
4
+ pytest~=6.2
5
+ pytest-cov~=2.12
6
+ twine~=3.4
7
+ build~=0.7