dramatiq-crontab 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.
- dramatiq-crontab-1.0.0/LICENSE +28 -0
- dramatiq-crontab-1.0.0/PKG-INFO +135 -0
- dramatiq-crontab-1.0.0/README.md +91 -0
- dramatiq-crontab-1.0.0/dramatiq_crontab/__init__.py +85 -0
- dramatiq-crontab-1.0.0/dramatiq_crontab/_version.py +4 -0
- dramatiq-crontab-1.0.0/dramatiq_crontab/conf.py +14 -0
- dramatiq-crontab-1.0.0/dramatiq_crontab/management/__init__.py +0 -0
- dramatiq-crontab-1.0.0/dramatiq_crontab/management/commands/__init__.py +0 -0
- dramatiq-crontab-1.0.0/dramatiq_crontab/management/commands/crontab.py +96 -0
- dramatiq-crontab-1.0.0/dramatiq_crontab/tasks.py +9 -0
- dramatiq-crontab-1.0.0/pyproject.toml +91 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023, voiio GmbH
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: dramatiq-crontab
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Cron style scheduler for asynchronous Dramatiq tasks in Django.
|
|
5
|
+
Keywords: Django,Dramatiq,tasks,scheduler
|
|
6
|
+
Author-email: Rust Saiargaliev <fly.amureki@gmail.com>, Johannes Maron <johannes@maron.family>, Mostafa Mohamed <mostafa.anm91@gmail.com>, Jacqueline Kraus <jacquelinekraus1992@gmail.com>
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Classifier: Environment :: Web Environment
|
|
12
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Topic :: Communications :: Email
|
|
16
|
+
Classifier: Topic :: Text Processing :: Markup :: Markdown
|
|
17
|
+
Classifier: Topic :: Software Development
|
|
18
|
+
Classifier: Programming Language :: Python
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Framework :: Django
|
|
26
|
+
Classifier: Framework :: Django :: 3.2
|
|
27
|
+
Classifier: Framework :: Django :: 4.1
|
|
28
|
+
Classifier: Framework :: Django :: 4.2
|
|
29
|
+
Requires-Dist: dramatiq
|
|
30
|
+
Requires-Dist: apscheduler
|
|
31
|
+
Requires-Dist: django
|
|
32
|
+
Requires-Dist: redis ; extra == "redis"
|
|
33
|
+
Requires-Dist: sentry-sdk ; extra == "sentry"
|
|
34
|
+
Requires-Dist: pytest ; extra == "test"
|
|
35
|
+
Requires-Dist: pytest-cov ; extra == "test"
|
|
36
|
+
Requires-Dist: pytest-django ; extra == "test"
|
|
37
|
+
Requires-Dist: dramatiq ; extra == "test"
|
|
38
|
+
Requires-Dist: backports.zoneinfo ; extra == "test" and (python_version<'3.9')
|
|
39
|
+
Project-URL: Changelog, https://github.com/voiio/dramatiq-crontab/releases
|
|
40
|
+
Project-URL: Project-URL, https://github.com/voiio/dramatiq-crontab
|
|
41
|
+
Provides-Extra: redis
|
|
42
|
+
Provides-Extra: sentry
|
|
43
|
+
Provides-Extra: test
|
|
44
|
+
|
|
45
|
+
# Dramatiq Crontab
|
|
46
|
+
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
**Cron style scheduler for asynchronous Dramatiq tasks in Django.**
|
|
50
|
+
|
|
51
|
+
* setup recurring tasks via crontab syntax
|
|
52
|
+
* lightweight helpers build on robust tools like [Dramatiq][dramatiq] and [APScheduler][apscheduler]
|
|
53
|
+
* [Sentry][sentry] cron monitor support
|
|
54
|
+
|
|
55
|
+
[](https://pypi.python.org/pypi/dramatiq-crontab/)
|
|
56
|
+
[](https://codecov.io/gh/voiio/dramatiq-crontab)
|
|
57
|
+
[](https://raw.githubusercontent.com/voiio/dramatiq-crontab/master/LICENSE)
|
|
58
|
+
|
|
59
|
+
## Setup
|
|
60
|
+
|
|
61
|
+
You need to have [Dramatiq][dramatiq] installed and setup properly.
|
|
62
|
+
|
|
63
|
+
```ShellSession
|
|
64
|
+
python3 -m pip install dramatiq-crontab
|
|
65
|
+
# or
|
|
66
|
+
python3 -m pip install dramatiq-crontab[sentry] # with sentry cron monitor support
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Add `dramatiq_crontab` to your `INSTALLED_APPS` in `settings.py`:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
# settings.py
|
|
73
|
+
INSTALLED_APPS = [
|
|
74
|
+
'dramatiq_crontab',
|
|
75
|
+
# ...
|
|
76
|
+
]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Finally, you lauch the scheduler in a separate process:
|
|
80
|
+
|
|
81
|
+
```ShellSession
|
|
82
|
+
python3 manage.py crontab
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Setup Redis as a lock backend (optional)
|
|
86
|
+
|
|
87
|
+
If you use Redis as a broker, you can use Redis as a lock backend as well.
|
|
88
|
+
The lock backend is used to prevent multiple instances of the scheduler from running at the same time.
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# settings.py
|
|
92
|
+
DRAMATIQ_CRONTAB = {
|
|
93
|
+
"REDIS_URL": "redis://localhost:6379/0",
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Usage
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
# tasks.py
|
|
101
|
+
import dramatiq
|
|
102
|
+
from dramatiq_crontab import cron
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@cron("*/5 * * * *") # every 5 minutes
|
|
106
|
+
@dramatiq.actor
|
|
107
|
+
def my_task():
|
|
108
|
+
my_task.logger.info("Hello World")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Sentry Cron Monitors
|
|
112
|
+
|
|
113
|
+
If you use [Sentry][sentry] you can add cron monitors to your tasks.
|
|
114
|
+
The monitor's slug will be the actor's name. Like `my_task` in the example above.
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
### The crontab command
|
|
118
|
+
|
|
119
|
+
```ShellSession
|
|
120
|
+
$ python3 manage.py crontab --help
|
|
121
|
+
usage: manage.py crontab [-h] [--no-task-loading] [--no-heartbeat] [--version] [-v {0,1,2,3}]
|
|
122
|
+
[--settings SETTINGS] [--pythonpath PYTHONPATH] [--traceback] [--no-color]
|
|
123
|
+
[--force-color] [--skip-checks]
|
|
124
|
+
|
|
125
|
+
Run dramatiq task scheduler for all tasks with the `cron` decorator.
|
|
126
|
+
|
|
127
|
+
options:
|
|
128
|
+
-h, --help show this help message and exit
|
|
129
|
+
--no-task-loading Don't load tasks from installed apps.
|
|
130
|
+
--no-heartbeat Don't start the heartbeat actor.
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
[dramatiq]: https://dramatiq.io/
|
|
134
|
+
[apscheduler]: https://apscheduler.readthedocs.io/en/stable/
|
|
135
|
+
[sentry]: https://docs.sentry.io/product/crons/
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Dramatiq Crontab
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
**Cron style scheduler for asynchronous Dramatiq tasks in Django.**
|
|
6
|
+
|
|
7
|
+
* setup recurring tasks via crontab syntax
|
|
8
|
+
* lightweight helpers build on robust tools like [Dramatiq][dramatiq] and [APScheduler][apscheduler]
|
|
9
|
+
* [Sentry][sentry] cron monitor support
|
|
10
|
+
|
|
11
|
+
[](https://pypi.python.org/pypi/dramatiq-crontab/)
|
|
12
|
+
[](https://codecov.io/gh/voiio/dramatiq-crontab)
|
|
13
|
+
[](https://raw.githubusercontent.com/voiio/dramatiq-crontab/master/LICENSE)
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
You need to have [Dramatiq][dramatiq] installed and setup properly.
|
|
18
|
+
|
|
19
|
+
```ShellSession
|
|
20
|
+
python3 -m pip install dramatiq-crontab
|
|
21
|
+
# or
|
|
22
|
+
python3 -m pip install dramatiq-crontab[sentry] # with sentry cron monitor support
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Add `dramatiq_crontab` to your `INSTALLED_APPS` in `settings.py`:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
# settings.py
|
|
29
|
+
INSTALLED_APPS = [
|
|
30
|
+
'dramatiq_crontab',
|
|
31
|
+
# ...
|
|
32
|
+
]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Finally, you lauch the scheduler in a separate process:
|
|
36
|
+
|
|
37
|
+
```ShellSession
|
|
38
|
+
python3 manage.py crontab
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Setup Redis as a lock backend (optional)
|
|
42
|
+
|
|
43
|
+
If you use Redis as a broker, you can use Redis as a lock backend as well.
|
|
44
|
+
The lock backend is used to prevent multiple instances of the scheduler from running at the same time.
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
# settings.py
|
|
48
|
+
DRAMATIQ_CRONTAB = {
|
|
49
|
+
"REDIS_URL": "redis://localhost:6379/0",
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
# tasks.py
|
|
57
|
+
import dramatiq
|
|
58
|
+
from dramatiq_crontab import cron
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@cron("*/5 * * * *") # every 5 minutes
|
|
62
|
+
@dramatiq.actor
|
|
63
|
+
def my_task():
|
|
64
|
+
my_task.logger.info("Hello World")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Sentry Cron Monitors
|
|
68
|
+
|
|
69
|
+
If you use [Sentry][sentry] you can add cron monitors to your tasks.
|
|
70
|
+
The monitor's slug will be the actor's name. Like `my_task` in the example above.
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
### The crontab command
|
|
74
|
+
|
|
75
|
+
```ShellSession
|
|
76
|
+
$ python3 manage.py crontab --help
|
|
77
|
+
usage: manage.py crontab [-h] [--no-task-loading] [--no-heartbeat] [--version] [-v {0,1,2,3}]
|
|
78
|
+
[--settings SETTINGS] [--pythonpath PYTHONPATH] [--traceback] [--no-color]
|
|
79
|
+
[--force-color] [--skip-checks]
|
|
80
|
+
|
|
81
|
+
Run dramatiq task scheduler for all tasks with the `cron` decorator.
|
|
82
|
+
|
|
83
|
+
options:
|
|
84
|
+
-h, --help show this help message and exit
|
|
85
|
+
--no-task-loading Don't load tasks from installed apps.
|
|
86
|
+
--no-heartbeat Don't start the heartbeat actor.
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
[dramatiq]: https://dramatiq.io/
|
|
90
|
+
[apscheduler]: https://apscheduler.readthedocs.io/en/stable/
|
|
91
|
+
[sentry]: https://docs.sentry.io/product/crons/
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Cron style scheduler for asynchronous Dramatiq tasks in Django."""
|
|
2
|
+
|
|
3
|
+
from apscheduler.schedulers.blocking import BlockingScheduler
|
|
4
|
+
from apscheduler.triggers.cron import CronTrigger
|
|
5
|
+
|
|
6
|
+
from . import _version
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from sentry_sdk.crons import monitor
|
|
10
|
+
except ImportError:
|
|
11
|
+
monitor = None
|
|
12
|
+
|
|
13
|
+
__version__ = _version.version
|
|
14
|
+
VERSION = _version.version_tuple
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__all__ = ["cron", "scheduler"]
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from django.utils import timezone
|
|
21
|
+
except ImportError:
|
|
22
|
+
timezone = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__all__ = ["cron"]
|
|
26
|
+
|
|
27
|
+
scheduler = BlockingScheduler()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def cron(schedule):
|
|
31
|
+
"""
|
|
32
|
+
Run task on a scheduler with a cron schedule.
|
|
33
|
+
|
|
34
|
+
Usage:
|
|
35
|
+
@cron("0 0 * * *")
|
|
36
|
+
@dramatiq.actor
|
|
37
|
+
def cron_test():
|
|
38
|
+
print("Cron test")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
Please don't forget to set up a sentry monitor for the actor, otherwise you won't
|
|
42
|
+
get any notifications if the cron job fails.
|
|
43
|
+
|
|
44
|
+
The monitor slug is your actor name, the schedule should be set to the same
|
|
45
|
+
cron schedule as the cron decorator. The schedule type should be set to cron.
|
|
46
|
+
The monitors timezone should be set to Europe/Berlin.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def decorator(func):
|
|
50
|
+
*_, day_of_week = schedule.split(" ")
|
|
51
|
+
|
|
52
|
+
if day_of_week.lower() not in (
|
|
53
|
+
"*",
|
|
54
|
+
"mon",
|
|
55
|
+
"tue",
|
|
56
|
+
"wed",
|
|
57
|
+
"thu",
|
|
58
|
+
"fri",
|
|
59
|
+
"sat",
|
|
60
|
+
"sun",
|
|
61
|
+
):
|
|
62
|
+
# CronTrigger uses Python's timezone dependent first weekday,
|
|
63
|
+
# so in Berlin monday is 0 and sunday is 6. We use literals to avoid
|
|
64
|
+
# confusion. Literals are also more readable and crontab conform.
|
|
65
|
+
raise ValueError(
|
|
66
|
+
"Please use a literal day of week (Mon, Tue, Wed, Thu, Fri, Sat, Sun) or *"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
fn = getattr(func, "send")
|
|
70
|
+
if monitor is not None:
|
|
71
|
+
fn = monitor(func.actor_name)(fn)
|
|
72
|
+
|
|
73
|
+
scheduler.add_job(
|
|
74
|
+
fn,
|
|
75
|
+
CronTrigger.from_crontab(
|
|
76
|
+
schedule,
|
|
77
|
+
timezone=timezone.get_default_timezone(),
|
|
78
|
+
),
|
|
79
|
+
name=func.actor_name,
|
|
80
|
+
)
|
|
81
|
+
# We don't add the Sentry monitor on the actor itself, because we only want to
|
|
82
|
+
# monitor the cron job, not the actor itself, or it's direct invocations.
|
|
83
|
+
return func
|
|
84
|
+
|
|
85
|
+
return decorator
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import signal
|
|
3
|
+
|
|
4
|
+
from django.apps import apps
|
|
5
|
+
from django.core.management import BaseCommand
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
import redis
|
|
9
|
+
from redis.exceptions import LockError
|
|
10
|
+
except ImportError:
|
|
11
|
+
redis = None
|
|
12
|
+
LockError = Exception
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
from sentry_sdk import capture_exception
|
|
16
|
+
except ImportError:
|
|
17
|
+
capture_exception = lambda e: None
|
|
18
|
+
|
|
19
|
+
from ... import scheduler
|
|
20
|
+
from ...conf import get_settings
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def kill_softly(signum, frame):
|
|
24
|
+
"""Raise a KeyboardInterrupt to stop the scheduler and release the lock."""
|
|
25
|
+
raise KeyboardInterrupt("Received SIGTERM!")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_redis_client():
|
|
29
|
+
redis_url = get_settings().REDIS_URL
|
|
30
|
+
if redis and redis_url:
|
|
31
|
+
return redis.Redis.from_url(redis_url)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Command(BaseCommand):
|
|
35
|
+
"""Run dramatiq task scheduler for all tasks with the `cron` decorator."""
|
|
36
|
+
|
|
37
|
+
help = __doc__
|
|
38
|
+
|
|
39
|
+
def add_arguments(self, parser):
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"--no-task-loading",
|
|
42
|
+
action="store_true",
|
|
43
|
+
help="Don't load tasks from installed apps.",
|
|
44
|
+
)
|
|
45
|
+
parser.add_argument(
|
|
46
|
+
"--no-heartbeat",
|
|
47
|
+
action="store_true",
|
|
48
|
+
help="Don't start the heartbeat actor.",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def handle(self, *args, **options):
|
|
52
|
+
if not options["no_task_loading"]:
|
|
53
|
+
self.load_tasks(options)
|
|
54
|
+
if not options["no_heartbeat"]:
|
|
55
|
+
importlib.import_module("dramatiq_crontab.tasks")
|
|
56
|
+
self.stdout.write("Scheduling heartbeat.")
|
|
57
|
+
redis = get_redis_client()
|
|
58
|
+
if redis:
|
|
59
|
+
try:
|
|
60
|
+
# Lock scheduler to prevent multiple instances from running.
|
|
61
|
+
with redis.lock("dramatiq-scheduler", blocking_timeout=0):
|
|
62
|
+
self.launch_scheduler()
|
|
63
|
+
except LockError as e:
|
|
64
|
+
capture_exception(e)
|
|
65
|
+
self.stderr.write("Another scheduler is already running.")
|
|
66
|
+
else:
|
|
67
|
+
self.launch_scheduler()
|
|
68
|
+
|
|
69
|
+
def launch_scheduler(self):
|
|
70
|
+
signal.signal(signal.SIGTERM, kill_softly)
|
|
71
|
+
self.stdout.write(self.style.SUCCESS("Starting scheduler…"))
|
|
72
|
+
try:
|
|
73
|
+
scheduler.start()
|
|
74
|
+
except KeyboardInterrupt as e:
|
|
75
|
+
self.stdout.write(self.style.WARNING(str(e)))
|
|
76
|
+
self.stdout.write(self.style.NOTICE("Shutting down scheduler…"))
|
|
77
|
+
scheduler.shutdown()
|
|
78
|
+
|
|
79
|
+
def load_tasks(self, options):
|
|
80
|
+
"""
|
|
81
|
+
Load all tasks modules within installed apps.
|
|
82
|
+
|
|
83
|
+
If they are not imported, they will not have registered
|
|
84
|
+
their tasks with the scheduler.
|
|
85
|
+
"""
|
|
86
|
+
for app in apps.get_app_configs():
|
|
87
|
+
if app.name == "dramatiq_crontab":
|
|
88
|
+
continue
|
|
89
|
+
if app.ready:
|
|
90
|
+
try:
|
|
91
|
+
importlib.import_module(f"{app.name}.tasks")
|
|
92
|
+
self.stdout.write(
|
|
93
|
+
f"Loaded tasks from {self.style.NOTICE(app.name)}."
|
|
94
|
+
)
|
|
95
|
+
except ImportError:
|
|
96
|
+
pass
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["flit_core>=3.2", "flit_scm", "wheel"]
|
|
3
|
+
build-backend = "flit_scm:buildapi"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dramatiq-crontab"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Rust Saiargaliev", email = "fly.amureki@gmail.com" },
|
|
9
|
+
{ name = "Johannes Maron", email = "johannes@maron.family" },
|
|
10
|
+
{ name = "Mostafa Mohamed", email = "mostafa.anm91@gmail.com" },
|
|
11
|
+
{ name = "Jacqueline Kraus", email = "jacquelinekraus1992@gmail.com" },
|
|
12
|
+
]
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
license = { file = "LICENSE" }
|
|
15
|
+
keywords = ["Django", "Dramatiq", "tasks", "scheduler"]
|
|
16
|
+
dynamic = ["version", "description"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 5 - Production/Stable",
|
|
19
|
+
"Programming Language :: Python",
|
|
20
|
+
"Environment :: Web Environment",
|
|
21
|
+
"License :: OSI Approved :: BSD License",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Topic :: Communications :: Email",
|
|
25
|
+
"Topic :: Text Processing :: Markup :: Markdown",
|
|
26
|
+
"Topic :: Software Development",
|
|
27
|
+
"Programming Language :: Python",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
30
|
+
"Programming Language :: Python :: 3.8",
|
|
31
|
+
"Programming Language :: Python :: 3.9",
|
|
32
|
+
"Programming Language :: Python :: 3.10",
|
|
33
|
+
"Programming Language :: Python :: 3.11",
|
|
34
|
+
"Framework :: Django",
|
|
35
|
+
"Framework :: Django :: 3.2",
|
|
36
|
+
"Framework :: Django :: 4.1",
|
|
37
|
+
"Framework :: Django :: 4.2",
|
|
38
|
+
]
|
|
39
|
+
requires-python = ">=3.8"
|
|
40
|
+
dependencies = ["dramatiq", "apscheduler", "django"]
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
test = [
|
|
44
|
+
"pytest",
|
|
45
|
+
"pytest-cov",
|
|
46
|
+
"pytest-django",
|
|
47
|
+
"dramatiq",
|
|
48
|
+
"backports.zoneinfo;python_version<'3.9'"
|
|
49
|
+
]
|
|
50
|
+
sentry = ["sentry-sdk"]
|
|
51
|
+
redis = ["redis"]
|
|
52
|
+
|
|
53
|
+
[project.urls]
|
|
54
|
+
Project-URL = "https://github.com/voiio/dramatiq-crontab"
|
|
55
|
+
Changelog = "https://github.com/voiio/dramatiq-crontab/releases"
|
|
56
|
+
|
|
57
|
+
[tool.flit.module]
|
|
58
|
+
name = "dramatiq_crontab"
|
|
59
|
+
|
|
60
|
+
[tool.setuptools_scm]
|
|
61
|
+
write_to = "dramatiq_crontab/_version.py"
|
|
62
|
+
|
|
63
|
+
[tool.pytest.ini_options]
|
|
64
|
+
minversion = "6.0"
|
|
65
|
+
addopts = "--cov --tb=short -rxs"
|
|
66
|
+
testpaths = ["tests"]
|
|
67
|
+
DJANGO_SETTINGS_MODULE = "tests.testapp.settings"
|
|
68
|
+
|
|
69
|
+
[tool.coverage.run]
|
|
70
|
+
source = ["dramatiq_crontab"]
|
|
71
|
+
|
|
72
|
+
[tool.coverage.report]
|
|
73
|
+
show_missing = true
|
|
74
|
+
|
|
75
|
+
[tool.isort]
|
|
76
|
+
atomic = true
|
|
77
|
+
line_length = 88
|
|
78
|
+
known_first_party = "dramatiq_crontab, tests"
|
|
79
|
+
include_trailing_comma = true
|
|
80
|
+
default_section = "THIRDPARTY"
|
|
81
|
+
combine_as_imports = true
|
|
82
|
+
multi_line_output = 3
|
|
83
|
+
|
|
84
|
+
[tool.pydocstyle]
|
|
85
|
+
add_ignore = "D1"
|
|
86
|
+
|
|
87
|
+
[tool.djlint]
|
|
88
|
+
profile="django"
|
|
89
|
+
indent=2
|
|
90
|
+
max_line_length=120
|
|
91
|
+
exclude=".direnv,.venv,venv"
|