simplecron 0.1.0a2__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,348 @@
1
+ Metadata-Version: 2.3
2
+ Name: simplecron
3
+ Version: 0.1.0a2
4
+ Summary: Simplecron is a simple and lightweight Python library for scheduling tasks using cron-like syntax.
5
+ Keywords: cron,scheduler
6
+ Author: John Pendenque, Daniel Bader
7
+ Author-email: John Pendenque <pendenquejohn@gmail.com>, Daniel Bader <mail@dbader.org>
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Operating System :: POSIX
10
+ Classifier: Operating System :: Unix
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Topic :: Software Development :: Libraries
14
+ Classifier: Topic :: Utilities
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Natural Language :: English
21
+ Classifier: Operating System :: Microsoft :: Windows :: Windows 11
22
+ Classifier: Operating System :: MacOS
23
+ Requires-Dist: asgiref>=3.11.1
24
+ Requires-Dist: pydantic>=2.13.4
25
+ Requires-Dist: pytz>=2026.2
26
+ Requires-Dist: redis>=8.0.1
27
+ Maintainer: John Pendenque
28
+ Maintainer-email: John Pendenque <pendenquejohn@gmail.com>
29
+ Requires-Python: >=3.14
30
+ Project-URL: Changelog, https://github.com/Zadigo/simplecron/blob/main/CHANGELOG.md
31
+ Project-URL: Documentation, https://github.com/Zadigo/simplecron/wiki
32
+ Project-URL: Homepage, https://github.com/Zadigo/simplecron
33
+ Project-URL: Repository, https://github.com/Zadigo/simplecron.git
34
+ Description-Content-Type: text/markdown
35
+
36
+ # Simple Cron
37
+
38
+ Simplecron is a simple and lightweight Python library for scheduling tasks using cron-like syntax. It allows you to define jobs that run at specific intervals or times, making it easy to automate repetitive tasks in your applications.
39
+
40
+ This project was inspired by [**schedule.**](https://github.com/dbader/schedule) and improved to add:
41
+
42
+ * Asynchronous functionnalities
43
+ * Redis, SQLite, Postgres provider support
44
+
45
+ ## Creating a schedule
46
+
47
+ ### Default scheduler
48
+
49
+ Simplecron uses a default scheduler that is created when the library is imported. You can create jobs using the `every` function, which creates a new job instance and attaches it to the default scheduler.
50
+
51
+ ```Python
52
+ from simplecron import base
53
+
54
+
55
+ def callback(job: base.Job, *args, **kwargs):
56
+ print("Hello, World!", job)
57
+
58
+
59
+ base.every(1).second.do(callback)
60
+
61
+
62
+ while True:
63
+ base.run_pending()
64
+ time.sleep(1)
65
+ ```
66
+
67
+ The same code can be achieved using the `start_blocking` function, which simplifies the blocking loop:
68
+
69
+ ```python
70
+ from simplecron import base
71
+
72
+ def callback(job: base.Job, *args, **kwargs):
73
+ print("Hello, World!", job)
74
+
75
+ base.every(1).second.do(callback)
76
+ base.start_blocking()
77
+ ```
78
+
79
+ By calling `every`, a new job is created. `second` attaches the unit of time to the job and finally `do` attaches the callback function.
80
+
81
+ > [!NOTE]
82
+ > `start_blocking` is a blocking function, in other words, it will block the main thread and will not allow other code to run while it is executing.
83
+
84
+ ### Custom scheduler
85
+
86
+ You can also create your own scheduler by instantiating the `BaseScheduler` class. This can allow you to have multiple schedulers running concurrently, each with its own set of jobs.
87
+
88
+ ```python
89
+ from simplecron.base import BaseScheduler
90
+
91
+ s1 = BaseScheduler()
92
+ s2 = BaseScheduler()
93
+
94
+ # Adds a job to the first scheduler that runs every second
95
+ s1.every(1).second.do(lambda job: print("Scheduler 1:", job))
96
+
97
+ # Adds a job to the second scheduler that runs every 2 seconds
98
+ s2.every(2).seconds.do(lambda job: print("Scheduler 2:", job))
99
+
100
+ def main():
101
+ while True:
102
+ s1.run_pending_jobs()
103
+ s2.run_pending_jobs()
104
+ time.sleep(1)
105
+
106
+ if __name__ == "__main__":
107
+ main()
108
+ ```
109
+
110
+ <!--
111
+ The same code can be achieved using the `Schedulers` class with the `start_blocking` method, which allows you to run multiple schedulers consecutively in a blocking manner.
112
+
113
+ ```Python
114
+ import asyncio
115
+ from simplecron.base import BaseScheduler, Job
116
+ from simplecron.runners import Schedulers
117
+
118
+ synchronizer = Schedulers()
119
+
120
+ def callback(job: Job, *args, **kwargs):
121
+ print("Hello, World!", job)
122
+
123
+ def main():
124
+ s1 = BaseScheduler()
125
+ s2 = BaseScheduler()
126
+
127
+ synchronizer.add_scheduler(s1, "seconds", 1, callback)
128
+ synchronizer.add_scheduler(s2, "seconds", 2, callback)
129
+
130
+ synchronizer.start_blocking()
131
+
132
+ if __name__ == "__main__":
133
+ main()
134
+ ```
135
+
136
+ ### Concurrent schedulers
137
+
138
+ To run multiple schedulers concurrently, you can use the `asyncio` library. This allows you to run multiple schedulers in an asynchronous manner, enabling better performance and responsiveness.
139
+
140
+ ```Python
141
+ import asyncio
142
+ from simplecron.base import BaseScheduler, Job
143
+ from simplecron.runners import Schedulers
144
+
145
+ synchronizer = Schedulers()
146
+
147
+ def callback(job: Job, *args, **kwargs):
148
+ print("Hello, World!", job)
149
+
150
+ async def main():
151
+ s1 = BaseScheduler()
152
+ s2 = BaseScheduler()
153
+
154
+ synchronizer.add_scheduler(s1, "seconds", 1, callback)
155
+ synchronizer.add_scheduler(s2, "seconds", 2, callback)
156
+
157
+ await synchronizer.async_blocking()
158
+
159
+ if __name__ == "__main__":
160
+ asyncio.run(main())
161
+ ``` -->
162
+
163
+ ## Event Listeners
164
+
165
+ You can attach event listeners to a scheduler to listen for specific events. There are three main listeners:
166
+
167
+ * `before` - Triggered before a job is executed.
168
+ * `after` - Triggered after a job is executed.
169
+ * `before_all` - Triggered before all jobs are executed.
170
+
171
+ ```python
172
+ from simplecron.base import default_scheduler
173
+ from simplecron.utils import EventListenerEnum
174
+
175
+ default_scheduler.event_listener(EventListenerEnum.BEFORE_ALL, lambda scheduler: print("Before all jobs"))
176
+ ```
177
+
178
+ The same can be achieved using the shortcut method `before_all_events`, `after_events`, and `before_events`:
179
+
180
+ ```python
181
+ from simplecron.base import default_scheduler
182
+
183
+ default_scheduler.before_all_events(lambda scheduler: print("Before all jobs"))
184
+ default_scheduler.before_events(lambda job: print("Before job:", job))
185
+ default_scheduler.after_events(lambda job: print("After job:", job))
186
+ ```
187
+
188
+ ## Jobs
189
+
190
+ ### Cancelling
191
+
192
+ To cancel a job, it simply needs to return an instance of `Cancel`.
193
+
194
+ ```python
195
+ def callback(job: Job, *args, **kwargs):
196
+ print("Hello, World!", job)
197
+ return Cancel(job, reason="Some reason") # This will cancel the job after it runs once
198
+ ```
199
+
200
+ ### Types of jobs
201
+
202
+ **Every second**
203
+
204
+ ```python
205
+ default_scheduler.every(1).second.do(callback)
206
+ ```
207
+
208
+ **Every X second**
209
+
210
+ ```python
211
+ default_scheduler.every(15).seconds.do(callback)
212
+ ```
213
+
214
+ **Every minute**
215
+
216
+ ```python
217
+ default_scheduler.every(1).minute.do(callback)
218
+ ```
219
+
220
+ **Every X minutes**
221
+
222
+ ```python
223
+ default_scheduler.every(15).minutes.do(callback)
224
+ ```
225
+
226
+ **Every hour**
227
+
228
+ ```python
229
+ default_scheduler.every(1).hour.do(callback)
230
+ ```
231
+
232
+ **Every hours**
233
+
234
+ ```python
235
+ default_scheduler.every(1).hours.do(callback)
236
+ ```
237
+
238
+ **Every day**
239
+
240
+ When no specific time is provided, the job will run automatically at the start of the day (00:00). If you need to run the job at a specific time, you must use the `at` method to specify the time in 24-hour format (HH:MM).
241
+
242
+ ```python
243
+ default_scheduler.every(1).day.do(callback)
244
+ ```
245
+
246
+ ```python
247
+ default_scheduler.every(1).day.at(datetime.time(12, 00)).do(callback)
248
+ ```
249
+
250
+ **Every days**
251
+
252
+ ```python
253
+ default_scheduler.every(1).days.do(callback)
254
+ ```
255
+
256
+ **Every week**
257
+
258
+ If no specific day and time is povided, the job will run automatically at the start of the week (Monday at 00:00). If you need to run the job at a specific day, you must use one of the properties `monday`, `tuesday`, `wednesday`, `thursday`, `friday`, `saturday` or `sunday`.
259
+
260
+ You can also use the `at` method to specify the time in 24-hour format (HH:MM).
261
+
262
+ ```python
263
+ default_scheduler.every(1).week.do(callback)
264
+ ```
265
+
266
+ ```python
267
+ default_scheduler.every(1).week.at(datetime.time(12, 00)).do(callback)
268
+ ```
269
+
270
+ **Every X day**
271
+
272
+ ```python
273
+ default_scheduler.every(1).monday.do(callback)
274
+ default_scheduler.every(1).tuesday.do(callback)
275
+ default_scheduler.every(1).wednesday.do(callback)
276
+ default_scheduler.every(1).thursday.do(callback)
277
+ default_scheduler.every(1).friday.do(callback)
278
+ default_scheduler.every(1).saturday.do(callback)
279
+ default_scheduler.every(1).sunday.do(callback)
280
+ ```
281
+
282
+ ## Tags
283
+
284
+ Tags allow you to categorize and filter jobs based on specific labels. This can be useful for organizing jobs, applying actions to groups of jobs, or selectively running certain jobs based on their tags.
285
+
286
+ ```Python
287
+ base.every(15, tag="my_tag").seconds.do(executor)
288
+ ```
289
+
290
+ You can also attach event listeners to specific jobs matching a certain set of tags or criteria:
291
+
292
+ ```Python
293
+ import time
294
+
295
+ from simplecron.base import Job, default_scheduler, logger
296
+ from simplecron.utils import EventListenerEnum
297
+
298
+
299
+ def executor(job: Job):
300
+ logger.info("Executor called")
301
+
302
+
303
+ def event_before(job: Job):
304
+ print("Before job:", job._tags)
305
+
306
+
307
+ default_scheduler.create_every(10, tag="my_tag").seconds.do(executor)
308
+
309
+ default_scheduler.with_event_listener(
310
+ EventListenerEnum.BEFORE,
311
+ event_before,
312
+ for_tags=["my_tag"]
313
+ )
314
+
315
+ while True:
316
+ default_scheduler.run_pending()
317
+ time.sleep(1)
318
+ ```
319
+
320
+ ## Providers
321
+
322
+ Providers are external services or modules that can be integrated with the scheduler to extend its functionality. They allow you to connect your scheduled jobs with various platforms, APIs, or other systems seamlessly.
323
+
324
+ ### Redis Database provider
325
+
326
+ The example below will save the details of the scheduler and the jobs that were runned in a Redis backend:
327
+
328
+ ```Python
329
+ import time
330
+
331
+ from simplecron import base
332
+ from simplecron.base import Job, logger
333
+ from simplecron.providers import RedisDatabase
334
+
335
+
336
+ def executor(job: Job):
337
+ logger.warning("Executor called")
338
+
339
+
340
+ base.default_scheduler.providers.attach(RedisDatabase())
341
+ base.every(15).seconds.do(executor)
342
+ base.every(30).seconds.do(executor)
343
+
344
+
345
+ while True:
346
+ base.run_pending()
347
+ time.sleep(1)
348
+ ```
@@ -0,0 +1,313 @@
1
+ # Simple Cron
2
+
3
+ Simplecron is a simple and lightweight Python library for scheduling tasks using cron-like syntax. It allows you to define jobs that run at specific intervals or times, making it easy to automate repetitive tasks in your applications.
4
+
5
+ This project was inspired by [**schedule.**](https://github.com/dbader/schedule) and improved to add:
6
+
7
+ * Asynchronous functionnalities
8
+ * Redis, SQLite, Postgres provider support
9
+
10
+ ## Creating a schedule
11
+
12
+ ### Default scheduler
13
+
14
+ Simplecron uses a default scheduler that is created when the library is imported. You can create jobs using the `every` function, which creates a new job instance and attaches it to the default scheduler.
15
+
16
+ ```Python
17
+ from simplecron import base
18
+
19
+
20
+ def callback(job: base.Job, *args, **kwargs):
21
+ print("Hello, World!", job)
22
+
23
+
24
+ base.every(1).second.do(callback)
25
+
26
+
27
+ while True:
28
+ base.run_pending()
29
+ time.sleep(1)
30
+ ```
31
+
32
+ The same code can be achieved using the `start_blocking` function, which simplifies the blocking loop:
33
+
34
+ ```python
35
+ from simplecron import base
36
+
37
+ def callback(job: base.Job, *args, **kwargs):
38
+ print("Hello, World!", job)
39
+
40
+ base.every(1).second.do(callback)
41
+ base.start_blocking()
42
+ ```
43
+
44
+ By calling `every`, a new job is created. `second` attaches the unit of time to the job and finally `do` attaches the callback function.
45
+
46
+ > [!NOTE]
47
+ > `start_blocking` is a blocking function, in other words, it will block the main thread and will not allow other code to run while it is executing.
48
+
49
+ ### Custom scheduler
50
+
51
+ You can also create your own scheduler by instantiating the `BaseScheduler` class. This can allow you to have multiple schedulers running concurrently, each with its own set of jobs.
52
+
53
+ ```python
54
+ from simplecron.base import BaseScheduler
55
+
56
+ s1 = BaseScheduler()
57
+ s2 = BaseScheduler()
58
+
59
+ # Adds a job to the first scheduler that runs every second
60
+ s1.every(1).second.do(lambda job: print("Scheduler 1:", job))
61
+
62
+ # Adds a job to the second scheduler that runs every 2 seconds
63
+ s2.every(2).seconds.do(lambda job: print("Scheduler 2:", job))
64
+
65
+ def main():
66
+ while True:
67
+ s1.run_pending_jobs()
68
+ s2.run_pending_jobs()
69
+ time.sleep(1)
70
+
71
+ if __name__ == "__main__":
72
+ main()
73
+ ```
74
+
75
+ <!--
76
+ The same code can be achieved using the `Schedulers` class with the `start_blocking` method, which allows you to run multiple schedulers consecutively in a blocking manner.
77
+
78
+ ```Python
79
+ import asyncio
80
+ from simplecron.base import BaseScheduler, Job
81
+ from simplecron.runners import Schedulers
82
+
83
+ synchronizer = Schedulers()
84
+
85
+ def callback(job: Job, *args, **kwargs):
86
+ print("Hello, World!", job)
87
+
88
+ def main():
89
+ s1 = BaseScheduler()
90
+ s2 = BaseScheduler()
91
+
92
+ synchronizer.add_scheduler(s1, "seconds", 1, callback)
93
+ synchronizer.add_scheduler(s2, "seconds", 2, callback)
94
+
95
+ synchronizer.start_blocking()
96
+
97
+ if __name__ == "__main__":
98
+ main()
99
+ ```
100
+
101
+ ### Concurrent schedulers
102
+
103
+ To run multiple schedulers concurrently, you can use the `asyncio` library. This allows you to run multiple schedulers in an asynchronous manner, enabling better performance and responsiveness.
104
+
105
+ ```Python
106
+ import asyncio
107
+ from simplecron.base import BaseScheduler, Job
108
+ from simplecron.runners import Schedulers
109
+
110
+ synchronizer = Schedulers()
111
+
112
+ def callback(job: Job, *args, **kwargs):
113
+ print("Hello, World!", job)
114
+
115
+ async def main():
116
+ s1 = BaseScheduler()
117
+ s2 = BaseScheduler()
118
+
119
+ synchronizer.add_scheduler(s1, "seconds", 1, callback)
120
+ synchronizer.add_scheduler(s2, "seconds", 2, callback)
121
+
122
+ await synchronizer.async_blocking()
123
+
124
+ if __name__ == "__main__":
125
+ asyncio.run(main())
126
+ ``` -->
127
+
128
+ ## Event Listeners
129
+
130
+ You can attach event listeners to a scheduler to listen for specific events. There are three main listeners:
131
+
132
+ * `before` - Triggered before a job is executed.
133
+ * `after` - Triggered after a job is executed.
134
+ * `before_all` - Triggered before all jobs are executed.
135
+
136
+ ```python
137
+ from simplecron.base import default_scheduler
138
+ from simplecron.utils import EventListenerEnum
139
+
140
+ default_scheduler.event_listener(EventListenerEnum.BEFORE_ALL, lambda scheduler: print("Before all jobs"))
141
+ ```
142
+
143
+ The same can be achieved using the shortcut method `before_all_events`, `after_events`, and `before_events`:
144
+
145
+ ```python
146
+ from simplecron.base import default_scheduler
147
+
148
+ default_scheduler.before_all_events(lambda scheduler: print("Before all jobs"))
149
+ default_scheduler.before_events(lambda job: print("Before job:", job))
150
+ default_scheduler.after_events(lambda job: print("After job:", job))
151
+ ```
152
+
153
+ ## Jobs
154
+
155
+ ### Cancelling
156
+
157
+ To cancel a job, it simply needs to return an instance of `Cancel`.
158
+
159
+ ```python
160
+ def callback(job: Job, *args, **kwargs):
161
+ print("Hello, World!", job)
162
+ return Cancel(job, reason="Some reason") # This will cancel the job after it runs once
163
+ ```
164
+
165
+ ### Types of jobs
166
+
167
+ **Every second**
168
+
169
+ ```python
170
+ default_scheduler.every(1).second.do(callback)
171
+ ```
172
+
173
+ **Every X second**
174
+
175
+ ```python
176
+ default_scheduler.every(15).seconds.do(callback)
177
+ ```
178
+
179
+ **Every minute**
180
+
181
+ ```python
182
+ default_scheduler.every(1).minute.do(callback)
183
+ ```
184
+
185
+ **Every X minutes**
186
+
187
+ ```python
188
+ default_scheduler.every(15).minutes.do(callback)
189
+ ```
190
+
191
+ **Every hour**
192
+
193
+ ```python
194
+ default_scheduler.every(1).hour.do(callback)
195
+ ```
196
+
197
+ **Every hours**
198
+
199
+ ```python
200
+ default_scheduler.every(1).hours.do(callback)
201
+ ```
202
+
203
+ **Every day**
204
+
205
+ When no specific time is provided, the job will run automatically at the start of the day (00:00). If you need to run the job at a specific time, you must use the `at` method to specify the time in 24-hour format (HH:MM).
206
+
207
+ ```python
208
+ default_scheduler.every(1).day.do(callback)
209
+ ```
210
+
211
+ ```python
212
+ default_scheduler.every(1).day.at(datetime.time(12, 00)).do(callback)
213
+ ```
214
+
215
+ **Every days**
216
+
217
+ ```python
218
+ default_scheduler.every(1).days.do(callback)
219
+ ```
220
+
221
+ **Every week**
222
+
223
+ If no specific day and time is povided, the job will run automatically at the start of the week (Monday at 00:00). If you need to run the job at a specific day, you must use one of the properties `monday`, `tuesday`, `wednesday`, `thursday`, `friday`, `saturday` or `sunday`.
224
+
225
+ You can also use the `at` method to specify the time in 24-hour format (HH:MM).
226
+
227
+ ```python
228
+ default_scheduler.every(1).week.do(callback)
229
+ ```
230
+
231
+ ```python
232
+ default_scheduler.every(1).week.at(datetime.time(12, 00)).do(callback)
233
+ ```
234
+
235
+ **Every X day**
236
+
237
+ ```python
238
+ default_scheduler.every(1).monday.do(callback)
239
+ default_scheduler.every(1).tuesday.do(callback)
240
+ default_scheduler.every(1).wednesday.do(callback)
241
+ default_scheduler.every(1).thursday.do(callback)
242
+ default_scheduler.every(1).friday.do(callback)
243
+ default_scheduler.every(1).saturday.do(callback)
244
+ default_scheduler.every(1).sunday.do(callback)
245
+ ```
246
+
247
+ ## Tags
248
+
249
+ Tags allow you to categorize and filter jobs based on specific labels. This can be useful for organizing jobs, applying actions to groups of jobs, or selectively running certain jobs based on their tags.
250
+
251
+ ```Python
252
+ base.every(15, tag="my_tag").seconds.do(executor)
253
+ ```
254
+
255
+ You can also attach event listeners to specific jobs matching a certain set of tags or criteria:
256
+
257
+ ```Python
258
+ import time
259
+
260
+ from simplecron.base import Job, default_scheduler, logger
261
+ from simplecron.utils import EventListenerEnum
262
+
263
+
264
+ def executor(job: Job):
265
+ logger.info("Executor called")
266
+
267
+
268
+ def event_before(job: Job):
269
+ print("Before job:", job._tags)
270
+
271
+
272
+ default_scheduler.create_every(10, tag="my_tag").seconds.do(executor)
273
+
274
+ default_scheduler.with_event_listener(
275
+ EventListenerEnum.BEFORE,
276
+ event_before,
277
+ for_tags=["my_tag"]
278
+ )
279
+
280
+ while True:
281
+ default_scheduler.run_pending()
282
+ time.sleep(1)
283
+ ```
284
+
285
+ ## Providers
286
+
287
+ Providers are external services or modules that can be integrated with the scheduler to extend its functionality. They allow you to connect your scheduled jobs with various platforms, APIs, or other systems seamlessly.
288
+
289
+ ### Redis Database provider
290
+
291
+ The example below will save the details of the scheduler and the jobs that were runned in a Redis backend:
292
+
293
+ ```Python
294
+ import time
295
+
296
+ from simplecron import base
297
+ from simplecron.base import Job, logger
298
+ from simplecron.providers import RedisDatabase
299
+
300
+
301
+ def executor(job: Job):
302
+ logger.warning("Executor called")
303
+
304
+
305
+ base.default_scheduler.providers.attach(RedisDatabase())
306
+ base.every(15).seconds.do(executor)
307
+ base.every(30).seconds.do(executor)
308
+
309
+
310
+ while True:
311
+ base.run_pending()
312
+ time.sleep(1)
313
+ ```