asyncz 0.5.0__tar.gz → 0.7.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.
Files changed (55) hide show
  1. {asyncz-0.5.0 → asyncz-0.7.0}/PKG-INFO +44 -66
  2. {asyncz-0.5.0 → asyncz-0.7.0}/README.md +25 -25
  3. asyncz-0.7.0/asyncz/__init__.py +1 -0
  4. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/contrib/esmerald/decorator.py +2 -2
  5. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/contrib/esmerald/scheduler.py +13 -13
  6. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/executors/base.py +6 -4
  7. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/schedulers/base.py +51 -23
  8. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/stores/redis.py +15 -8
  9. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/triggers/cron/fields.py +1 -1
  10. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/triggers/interval.py +14 -3
  11. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/utils.py +18 -20
  12. {asyncz-0.5.0 → asyncz-0.7.0}/pyproject.toml +44 -46
  13. asyncz-0.5.0/asyncz/__init__.py +0 -1
  14. {asyncz-0.5.0 → asyncz-0.7.0}/.gitignore +0 -0
  15. {asyncz-0.5.0 → asyncz-0.7.0}/LICENSE +0 -0
  16. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/_mapping.py +0 -0
  17. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/contrib/__init__.py +0 -0
  18. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/contrib/esmerald/__init__.py +0 -0
  19. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/datastructures.py +0 -0
  20. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/enums.py +0 -0
  21. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/events/__init__.py +0 -0
  22. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/events/base.py +0 -0
  23. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/events/constants.py +0 -0
  24. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/exceptions.py +0 -0
  25. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/executors/__init__.py +0 -0
  26. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/executors/asyncio.py +0 -0
  27. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/executors/debug.py +0 -0
  28. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/executors/pool.py +0 -0
  29. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/executors/types.py +0 -0
  30. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/py.typed +0 -0
  31. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/schedulers/__init__.py +0 -0
  32. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/schedulers/asyncio.py +0 -0
  33. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/schedulers/datastructures.py +0 -0
  34. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/schedulers/types.py +0 -0
  35. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/schedulers/utils.py +0 -0
  36. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/state.py +0 -0
  37. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/stores/__init__.py +0 -0
  38. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/stores/base.py +0 -0
  39. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/stores/memory.py +0 -0
  40. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/stores/mongo.py +0 -0
  41. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/stores/types.py +0 -0
  42. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/tasks/__init__.py +0 -0
  43. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/tasks/base.py +0 -0
  44. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/tasks/types.py +0 -0
  45. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/triggers/__init__.py +0 -0
  46. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/triggers/base.py +0 -0
  47. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/triggers/combination.py +0 -0
  48. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/triggers/cron/__init__.py +0 -0
  49. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/triggers/cron/constants.py +0 -0
  50. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/triggers/cron/expressions.py +0 -0
  51. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/triggers/cron/trigger.py +0 -0
  52. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/triggers/cron/types.py +0 -0
  53. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/triggers/date.py +0 -0
  54. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/triggers/types.py +0 -0
  55. {asyncz-0.5.0 → asyncz-0.7.0}/asyncz/typing.py +0 -0
@@ -1,12 +1,12 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: asyncz
3
- Version: 0.5.0
3
+ Version: 0.7.0
4
4
  Summary: The scheduler that nobody wants but every application needs.
5
- Project-URL: Homepage, https://github.com/tarsil/asyncz
6
- Project-URL: Documentation, https://asyncz.tarsild.io/
7
- Project-URL: Changelog, https://asyncz.tarsild.io/release-notes/
5
+ Project-URL: Homepage, https://github.com/dymmond/asyncz
6
+ Project-URL: Documentation, https://asyncz.dymmond.com/
7
+ Project-URL: Changelog, https://asyncz.dymmond.com/release-notes/
8
8
  Project-URL: Funding, https://github.com/sponsors/tarsil
9
- Project-URL: Source, https://github.com/tarsil/asyncz
9
+ Project-URL: Source, https://github.com/dymmond/asyncz
10
10
  Author-email: Tiago Silva <tiago.arasilva@gmail.com>
11
11
  License-Expression: MIT
12
12
  License-File: LICENSE
@@ -37,42 +37,20 @@ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
37
37
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
38
38
  Classifier: Typing :: Typed
39
39
  Requires-Python: >=3.8
40
- Requires-Dist: asyncio<4.0.0,>=3.4.3
40
+ Requires-Dist: backports-zoneinfo; python_version < '3.9'
41
41
  Requires-Dist: loguru<0.8.0,>=0.7.0
42
- Requires-Dist: pydantic<3.0.0,>=2.1.1
43
- Requires-Dist: pytz>=2022.6
44
- Requires-Dist: tzlocal<5.0,>=4.2
45
- Provides-Extra: all
46
- Requires-Dist: asyncio<4.0.0,>=3.4.3; extra == 'all'
47
- Requires-Dist: pytz>=2022.6; extra == 'all'
48
- Requires-Dist: tzlocal<5.0,>=4.2; extra == 'all'
49
- Provides-Extra: dev
50
- Requires-Dist: autoflake<3.0.0,>=1.4.0; extra == 'dev'
51
- Requires-Dist: black<23.0.0,>=22.10.0; extra == 'dev'
52
- Requires-Dist: flake8<7.0.0,>=3.8.3; extra == 'dev'
53
- Requires-Dist: ipdb>=0.13.13; extra == 'dev'
54
- Requires-Dist: isort<6.0.0,>=5.0.6; extra == 'dev'
55
- Requires-Dist: mypy<2.0.0,>=0.982; extra == 'dev'
56
- Requires-Dist: pre-commit<3.0.0,>=2.17.0; extra == 'dev'
57
- Requires-Dist: watchfiles<0.20.0,>=0.16.1; extra == 'dev'
58
- Provides-Extra: doc
59
- Requires-Dist: mdx-include<2.0.0,>=1.4.2; extra == 'doc'
60
- Requires-Dist: mkautodoc<0.3.0,>=0.2.0; extra == 'doc'
61
- Requires-Dist: mkdocs-markdownextradata-plugin<0.3.0,>=0.2.5; extra == 'doc'
62
- Requires-Dist: mkdocs-material<10.0.0,>=9.0.13; extra == 'doc'
63
- Requires-Dist: mkdocs<2.0.0,>=1.1.2; extra == 'doc'
64
- Requires-Dist: mkdocstrings<0.30.0,>=0.20.0; extra == 'doc'
65
- Requires-Dist: pyyaml<7.0.0,>=6.0; extra == 'doc'
66
- Provides-Extra: test
67
- Requires-Dist: mock>=4.0.3; extra == 'test'
68
- Requires-Dist: pymongo<5.0.0,>=4.3.3; extra == 'test'
69
- Requires-Dist: pytest-asyncio<1.0.0,>=0.19.0; extra == 'test'
70
- Requires-Dist: pytest-cov<5.0.0,>=2.12.0; extra == 'test'
71
- Requires-Dist: pytest-loguru<1,>=0.2.0; extra == 'test'
72
- Requires-Dist: pytest<8.0.0,>=7.1.3; extra == 'test'
73
- Requires-Dist: redis<5.0.0,>=4.4.0; extra == 'test'
74
- Requires-Dist: requests<3.0.0,>=2.27.0; extra == 'test'
75
- Requires-Dist: ruff<1.0.0,>=0.0.256; extra == 'test'
42
+ Requires-Dist: pydantic<3.0.0,>=2.5.3
43
+ Requires-Dist: tzlocal<6.0,>=4.0
44
+ Provides-Extra: testing
45
+ Requires-Dist: esmerald; extra == 'testing'
46
+ Requires-Dist: mypy<2.0.0,>=0.982; extra == 'testing'
47
+ Requires-Dist: pymongo<5.0.0,>=4.3.3; extra == 'testing'
48
+ Requires-Dist: pytest-asyncio<1.0.0,>=0.19.0; extra == 'testing'
49
+ Requires-Dist: pytest-cov<6.0.0,>=2.12.0; extra == 'testing'
50
+ Requires-Dist: pytest-loguru<1,>=0.2.0; extra == 'testing'
51
+ Requires-Dist: pytest<9.0.0,>=7.1.3; extra == 'testing'
52
+ Requires-Dist: pytz>=2022.6; extra == 'testing'
53
+ Requires-Dist: redis<6.0.0,>=4.4.0; extra == 'testing'
76
54
  Description-Content-Type: text/markdown
77
55
 
78
56
  # Asyncz
@@ -86,8 +64,8 @@ Description-Content-Type: text/markdown
86
64
  </p>
87
65
 
88
66
  <p align="center">
89
- <a href="https://github.com/tarsil/asyncz/workflows/Test%20Suite/badge.svg?event=push&branch=main" target="_blank">
90
- <img src="https://github.com/tarsil/asyncz/workflows/Test%20Suite/badge.svg?event=push&branch=main" alt="Test Suite">
67
+ <a href="https://github.com/dymmond/asyncz/workflows/Test%20Suite/badge.svg?event=push&branch=main" target="_blank">
68
+ <img src="https://github.com/dymmond/asyncz/workflows/Test%20Suite/badge.svg?event=push&branch=main" alt="Test Suite">
91
69
  </a>
92
70
 
93
71
  <a href="https://pypi.org/project/asyncz" target="_blank">
@@ -101,9 +79,9 @@ Description-Content-Type: text/markdown
101
79
 
102
80
  ---
103
81
 
104
- **Documentation**: [https://asyncz.tarsild.io](https://asyncz.tarsild.io) 📚
82
+ **Documentation**: [https://asyncz.dymmond.com](https://asyncz.dymmond.com) 📚
105
83
 
106
- **Source Code**: [https://github.com/tarsil/asyncz](https://github.com/tarsil/asyncz)
84
+ **Source Code**: [https://github.com/dymmond/asyncz](https://github.com/dymmond/asyncz)
107
85
 
108
86
  ---
109
87
 
@@ -145,16 +123,16 @@ completely indepent from any framework as well as inside ASGI frameworks such as
145
123
 
146
124
  Asyncz comes with special support to [Esmerald](https://esmerald.dymmond.com) for the simple reason that the author is
147
125
  the same but it can be added more support. If you are interested in adding support to your favourite frameworks then
148
- see the [contributing](https://asyncz.tarsild.io/contributing.md) section.
126
+ see the [contributing](https://asyncz.dymmond.com/contributing.md) section.
149
127
 
150
128
  ## Concepts
151
129
 
152
130
  Like APScheduler, Asyncz also brings four kinds of components:
153
131
 
154
- * [Schedulers](https://asyncz.tarsild.io/schedulers.md)
155
- * [Triggers](https://asyncz.tarsild.io/triggers.md)
156
- * [Stores](https://asyncz.tarsild.io/stores.md)
157
- * [Executors](https://asyncz.tarsild.io/executors.md)
132
+ * [Schedulers](https://asyncz.dymmond.com/schedulers.md)
133
+ * [Triggers](https://asyncz.dymmond.com/triggers.md)
134
+ * [Stores](https://asyncz.dymmond.com/stores.md)
135
+ * [Executors](https://asyncz.dymmond.com/executors.md)
158
136
 
159
137
  ## Requirements
160
138
 
@@ -173,28 +151,28 @@ $ pip install asyncz
173
151
 
174
152
  ## The right decisions
175
153
 
176
- How do you know if you are choosing the right [scheduler](https://asyncz.tarsild.io/schedulers.md),
177
- [triggers](https://asyncz.tarsild.io/triggers.md), [stores](https://asyncz.tarsild.io/stores.md)
178
- and [executors](https://asyncz.tarsild.io/executors.md)?
154
+ How do you know if you are choosing the right [scheduler](https://asyncz.dymmond.com/schedulers.md),
155
+ [triggers](https://asyncz.dymmond.com/triggers.md), [stores](https://asyncz.dymmond.com/stores.md)
156
+ and [executors](https://asyncz.dymmond.com/executors.md)?
179
157
 
180
158
  Well, Asyncz is intentionally designed for specific systems and already helps you out with some of
181
159
  those questions.
182
160
 
183
- * **Schedulers** - Natively only supports the [AsyncIOScheduler](https://asyncz.tarsild.io/schedulers.md#asyncioscheduler).
161
+ * **Schedulers** - Natively only supports the [AsyncIOScheduler](https://asyncz.dymmond.com/schedulers.md#asyncioscheduler).
184
162
  * **Triggers** - Here it will depend of the periocidity of our tasks. Example:
185
- * [CronTrigger](https://asyncz.tarsild.io/triggers.md#crontrigger) - UNIX like cron and gives you the same feeling as
163
+ * [CronTrigger](https://asyncz.dymmond.com/triggers.md#crontrigger) - UNIX like cron and gives you the same feeling as
186
164
  scheduling a task on a native UNIX like based system.
187
- * [DateTrigger](https://asyncz.tarsild.io/triggers.md#datetrigger) - When you need to run a task once on a specific
165
+ * [DateTrigger](https://asyncz.dymmond.com/triggers.md#datetrigger) - When you need to run a task once on a specific
188
166
  point of time.
189
- * [IntervalTrigger](https://asyncz.tarsild.io/triggers.md#intervaltrigger) - When you need to run tasks in specific
167
+ * [IntervalTrigger](https://asyncz.dymmond.com/triggers.md#intervaltrigger) - When you need to run tasks in specific
190
168
  intervals of time.
191
- * [OrTrigger](https://asyncz.tarsild.io/triggers.md#ortrigger)/[AndTrigger](https://asyncz.tarsild.io/triggers.md#andtrigger) - If you would
169
+ * [OrTrigger](https://asyncz.dymmond.com/triggers.md#ortrigger)/[AndTrigger](https://asyncz.dymmond.com/triggers.md#andtrigger) - If you would
192
170
  like to combine more than one trigger (cron, interval and date) together.
193
- * **Stores** - Natively only supports [redis](https://asyncz.tarsild.io/stores.md#redisstore),
194
- [mongo](https://asyncz.tarsild.io/stores.md#mongodbstore) and [memory](https://asyncz.tarsild.io/stores.md#memorystore).
195
- * **Executors** - Natively only supports [AsyncIOExecutor](https://asyncz.tarsild.io/executors.md#asyncioexecutor),
196
- [ThreadPoolExecutor](https://asyncz.tarsild.io/executors.md#threadpoolexecutor) and
197
- [ProcessPoolExecutor](https://asyncz.tarsild.io/executors.md#processpoolexecutor).
171
+ * **Stores** - Natively only supports [redis](https://asyncz.dymmond.com/stores.md#redisstore),
172
+ [mongo](https://asyncz.dymmond.com/stores.md#mongodbstore) and [memory](https://asyncz.dymmond.com/stores.md#memorystore).
173
+ * **Executors** - Natively only supports [AsyncIOExecutor](https://asyncz.dymmond.com/executors.md#asyncioexecutor),
174
+ [ThreadPoolExecutor](https://asyncz.dymmond.com/executors.md#threadpoolexecutor) and
175
+ [ProcessPoolExecutor](https://asyncz.dymmond.com/executors.md#processpoolexecutor).
198
176
 
199
177
  Sometimes having a lot of options makes the decision making very hard and Asyncz is intentionally
200
178
  designed and driven to simplify and for specific use cases but is not limited to those. In every
@@ -221,7 +199,7 @@ scheduler = AsyncIOScheduler()
221
199
  ```
222
200
 
223
201
  Initialize the rest of the application after the `scheduler` initialisation.
224
- More [details](https://asyncz.tarsild.io/schedulers.md) can be found with more thorough explanations.
202
+ More [details](https://asyncz.dymmond.com/schedulers.md) can be found with more thorough explanations.
225
203
 
226
204
  This is in simple terms and in a nutshell how to start with Asyncz quickly. For more information,
227
205
  details and examples how to leverage Asyncz simply navigate through the documentation and have
@@ -229,11 +207,11 @@ fun 😁🎉.
229
207
 
230
208
  ## ASGI support
231
209
 
232
- Asyncz currently supports the [Esmerald framework](https://asyncz.tarsild.io/contrib/esmerald/index.md)
210
+ Asyncz currently supports the [Esmerald framework](https://asyncz.dymmond.com/contrib/esmerald/index.md)
233
211
  and brings some batteries that are currently used by the framework and leveraging Asyncz.
234
212
 
235
213
  If you wish to have support to any other framework such as FastAPI, Starlite, Starlette or
236
- literally any other, check the [contributing](https://asyncz.tarsild.io/contributing.md) section and see how you can
214
+ literally any other, check the [contributing](https://asyncz.dymmond.com/contributing.md) section and see how you can
237
215
  do it.
238
216
 
239
217
  ## Sponsors
@@ -9,8 +9,8 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="https://github.com/tarsil/asyncz/workflows/Test%20Suite/badge.svg?event=push&branch=main" target="_blank">
13
- <img src="https://github.com/tarsil/asyncz/workflows/Test%20Suite/badge.svg?event=push&branch=main" alt="Test Suite">
12
+ <a href="https://github.com/dymmond/asyncz/workflows/Test%20Suite/badge.svg?event=push&branch=main" target="_blank">
13
+ <img src="https://github.com/dymmond/asyncz/workflows/Test%20Suite/badge.svg?event=push&branch=main" alt="Test Suite">
14
14
  </a>
15
15
 
16
16
  <a href="https://pypi.org/project/asyncz" target="_blank">
@@ -24,9 +24,9 @@
24
24
 
25
25
  ---
26
26
 
27
- **Documentation**: [https://asyncz.tarsild.io](https://asyncz.tarsild.io) 📚
27
+ **Documentation**: [https://asyncz.dymmond.com](https://asyncz.dymmond.com) 📚
28
28
 
29
- **Source Code**: [https://github.com/tarsil/asyncz](https://github.com/tarsil/asyncz)
29
+ **Source Code**: [https://github.com/dymmond/asyncz](https://github.com/dymmond/asyncz)
30
30
 
31
31
  ---
32
32
 
@@ -68,16 +68,16 @@ completely indepent from any framework as well as inside ASGI frameworks such as
68
68
 
69
69
  Asyncz comes with special support to [Esmerald](https://esmerald.dymmond.com) for the simple reason that the author is
70
70
  the same but it can be added more support. If you are interested in adding support to your favourite frameworks then
71
- see the [contributing](https://asyncz.tarsild.io/contributing.md) section.
71
+ see the [contributing](https://asyncz.dymmond.com/contributing.md) section.
72
72
 
73
73
  ## Concepts
74
74
 
75
75
  Like APScheduler, Asyncz also brings four kinds of components:
76
76
 
77
- * [Schedulers](https://asyncz.tarsild.io/schedulers.md)
78
- * [Triggers](https://asyncz.tarsild.io/triggers.md)
79
- * [Stores](https://asyncz.tarsild.io/stores.md)
80
- * [Executors](https://asyncz.tarsild.io/executors.md)
77
+ * [Schedulers](https://asyncz.dymmond.com/schedulers.md)
78
+ * [Triggers](https://asyncz.dymmond.com/triggers.md)
79
+ * [Stores](https://asyncz.dymmond.com/stores.md)
80
+ * [Executors](https://asyncz.dymmond.com/executors.md)
81
81
 
82
82
  ## Requirements
83
83
 
@@ -96,28 +96,28 @@ $ pip install asyncz
96
96
 
97
97
  ## The right decisions
98
98
 
99
- How do you know if you are choosing the right [scheduler](https://asyncz.tarsild.io/schedulers.md),
100
- [triggers](https://asyncz.tarsild.io/triggers.md), [stores](https://asyncz.tarsild.io/stores.md)
101
- and [executors](https://asyncz.tarsild.io/executors.md)?
99
+ How do you know if you are choosing the right [scheduler](https://asyncz.dymmond.com/schedulers.md),
100
+ [triggers](https://asyncz.dymmond.com/triggers.md), [stores](https://asyncz.dymmond.com/stores.md)
101
+ and [executors](https://asyncz.dymmond.com/executors.md)?
102
102
 
103
103
  Well, Asyncz is intentionally designed for specific systems and already helps you out with some of
104
104
  those questions.
105
105
 
106
- * **Schedulers** - Natively only supports the [AsyncIOScheduler](https://asyncz.tarsild.io/schedulers.md#asyncioscheduler).
106
+ * **Schedulers** - Natively only supports the [AsyncIOScheduler](https://asyncz.dymmond.com/schedulers.md#asyncioscheduler).
107
107
  * **Triggers** - Here it will depend of the periocidity of our tasks. Example:
108
- * [CronTrigger](https://asyncz.tarsild.io/triggers.md#crontrigger) - UNIX like cron and gives you the same feeling as
108
+ * [CronTrigger](https://asyncz.dymmond.com/triggers.md#crontrigger) - UNIX like cron and gives you the same feeling as
109
109
  scheduling a task on a native UNIX like based system.
110
- * [DateTrigger](https://asyncz.tarsild.io/triggers.md#datetrigger) - When you need to run a task once on a specific
110
+ * [DateTrigger](https://asyncz.dymmond.com/triggers.md#datetrigger) - When you need to run a task once on a specific
111
111
  point of time.
112
- * [IntervalTrigger](https://asyncz.tarsild.io/triggers.md#intervaltrigger) - When you need to run tasks in specific
112
+ * [IntervalTrigger](https://asyncz.dymmond.com/triggers.md#intervaltrigger) - When you need to run tasks in specific
113
113
  intervals of time.
114
- * [OrTrigger](https://asyncz.tarsild.io/triggers.md#ortrigger)/[AndTrigger](https://asyncz.tarsild.io/triggers.md#andtrigger) - If you would
114
+ * [OrTrigger](https://asyncz.dymmond.com/triggers.md#ortrigger)/[AndTrigger](https://asyncz.dymmond.com/triggers.md#andtrigger) - If you would
115
115
  like to combine more than one trigger (cron, interval and date) together.
116
- * **Stores** - Natively only supports [redis](https://asyncz.tarsild.io/stores.md#redisstore),
117
- [mongo](https://asyncz.tarsild.io/stores.md#mongodbstore) and [memory](https://asyncz.tarsild.io/stores.md#memorystore).
118
- * **Executors** - Natively only supports [AsyncIOExecutor](https://asyncz.tarsild.io/executors.md#asyncioexecutor),
119
- [ThreadPoolExecutor](https://asyncz.tarsild.io/executors.md#threadpoolexecutor) and
120
- [ProcessPoolExecutor](https://asyncz.tarsild.io/executors.md#processpoolexecutor).
116
+ * **Stores** - Natively only supports [redis](https://asyncz.dymmond.com/stores.md#redisstore),
117
+ [mongo](https://asyncz.dymmond.com/stores.md#mongodbstore) and [memory](https://asyncz.dymmond.com/stores.md#memorystore).
118
+ * **Executors** - Natively only supports [AsyncIOExecutor](https://asyncz.dymmond.com/executors.md#asyncioexecutor),
119
+ [ThreadPoolExecutor](https://asyncz.dymmond.com/executors.md#threadpoolexecutor) and
120
+ [ProcessPoolExecutor](https://asyncz.dymmond.com/executors.md#processpoolexecutor).
121
121
 
122
122
  Sometimes having a lot of options makes the decision making very hard and Asyncz is intentionally
123
123
  designed and driven to simplify and for specific use cases but is not limited to those. In every
@@ -144,7 +144,7 @@ scheduler = AsyncIOScheduler()
144
144
  ```
145
145
 
146
146
  Initialize the rest of the application after the `scheduler` initialisation.
147
- More [details](https://asyncz.tarsild.io/schedulers.md) can be found with more thorough explanations.
147
+ More [details](https://asyncz.dymmond.com/schedulers.md) can be found with more thorough explanations.
148
148
 
149
149
  This is in simple terms and in a nutshell how to start with Asyncz quickly. For more information,
150
150
  details and examples how to leverage Asyncz simply navigate through the documentation and have
@@ -152,11 +152,11 @@ fun 😁🎉.
152
152
 
153
153
  ## ASGI support
154
154
 
155
- Asyncz currently supports the [Esmerald framework](https://asyncz.tarsild.io/contrib/esmerald/index.md)
155
+ Asyncz currently supports the [Esmerald framework](https://asyncz.dymmond.com/contrib/esmerald/index.md)
156
156
  and brings some batteries that are currently used by the framework and leveraging Asyncz.
157
157
 
158
158
  If you wish to have support to any other framework such as FastAPI, Starlite, Starlette or
159
- literally any other, check the [contributing](https://asyncz.tarsild.io/contributing.md) section and see how you can
159
+ literally any other, check the [contributing](https://asyncz.dymmond.com/contributing.md) section and see how you can
160
160
  do it.
161
161
 
162
162
  ## Sponsors
@@ -0,0 +1 @@
1
+ __version__ = "0.7.0"
@@ -14,7 +14,7 @@ class scheduler(Task):
14
14
  id: Optional[str] = None,
15
15
  mistrigger_grace_time: Optional[int] = None,
16
16
  coalesce: Optional[bool] = None,
17
- max_intances: Optional[int] = None,
17
+ max_instances: Optional[int] = None,
18
18
  next_run_time: Optional[datetime] = None,
19
19
  store: Optional[str] = "default",
20
20
  executor: Optional[str] = "default",
@@ -29,7 +29,7 @@ class scheduler(Task):
29
29
  id=id,
30
30
  mistrigger_grace_time=mistrigger_grace_time,
31
31
  coalesce=coalesce,
32
- max_intances=max_intances,
32
+ max_instances=max_instances,
33
33
  next_run_time=next_run_time,
34
34
  store=store,
35
35
  executor=executor,
@@ -18,7 +18,6 @@ except ImportError:
18
18
 
19
19
  if TYPE_CHECKING:
20
20
  from esmerald.applications import Esmerald
21
- from pydantic.typing import AnyCallable
22
21
 
23
22
 
24
23
  class EsmeraldScheduler:
@@ -93,14 +92,15 @@ class EsmeraldScheduler:
93
92
  """
94
93
  Registers the scheduler events in the Esmerald application.
95
94
  """
95
+ if app.on_startup is not None:
96
+ app.on_startup.append(self.handler.start)
97
+ else:
98
+ app.on_startup = [self.handler.start]
96
99
 
97
- @app.on_event("startup")
98
- def start_scheduler() -> None:
99
- self.handler.start()
100
-
101
- @app.on_event("shutdown")
102
- def stop_scheduler() -> None:
103
- self.handler.shutdown()
100
+ if app.on_shutdown is not None:
101
+ app.on_shutdown.append(self.handler.shutdown)
102
+ else:
103
+ app.on_shutdown = [self.handler.shutdown]
104
104
 
105
105
  def get_scheduler(
106
106
  self,
@@ -145,7 +145,7 @@ class Task:
145
145
  id: Optional[str] = None,
146
146
  mistrigger_grace_time: Optional[int] = None,
147
147
  coalesce: Optional[bool] = None,
148
- max_intances: Optional[int] = None,
148
+ max_instances: Optional[int] = None,
149
149
  next_run_time: Optional[datetime] = None,
150
150
  store: Optional[str] = "default",
151
151
  executor: Optional[str] = "default",
@@ -163,7 +163,7 @@ class Task:
163
163
  mistrigger_grace_time: Seconds after the designated runtime that the task is still allowed to be run (or None to allow the task to run no
164
164
  matter how late it is).
165
165
  coalesce: Run once instead of many times if the scheduler determines that the task should be run more than once in succession.
166
- max_intances: Maximum number of concurrently running instances allowed for this task.
166
+ max_instances: Maximum number of concurrently running instances allowed for this task.
167
167
  next_run_time: When to first run the task, regardless of the trigger (pass None to add the task as paused).
168
168
  store: Alias of the task store to store the task in.
169
169
  executor: Alias of the executor to run the task with.
@@ -178,7 +178,7 @@ class Task:
178
178
  self.id = id
179
179
  self.mistrigger_grace_time = mistrigger_grace_time or undefined
180
180
  self.coalesce = coalesce or undefined
181
- self.max_intances = max_intances or undefined
181
+ self.max_instances = max_instances or undefined
182
182
  self.next_run_time = next_run_time or undefined
183
183
  self.store = store
184
184
  self.executor = executor
@@ -199,7 +199,7 @@ class Task:
199
199
  name=self.name,
200
200
  mistrigger_grace_time=self.mistrigger_grace_time,
201
201
  coalesce=self.coalesce,
202
- max_instances=self.max_intances,
202
+ max_instances=self.max_instances,
203
203
  next_run_time=self.next_run_time,
204
204
  store=self.store,
205
205
  executor=self.executor,
@@ -208,7 +208,7 @@ class Task:
208
208
  except Exception as e:
209
209
  raise ImproperlyConfigured(str(e)) from e
210
210
 
211
- def __call__(self, fn: "AnyCallable") -> None:
211
+ def __call__(self, fn: Any) -> None:
212
212
  """
213
213
  Tricking the object into think it's being instantiated by in reality
214
214
  is just returning itself.
@@ -3,12 +3,12 @@ import traceback
3
3
  from abc import ABC, abstractmethod
4
4
  from collections import defaultdict
5
5
  from datetime import datetime, timedelta
6
+ from datetime import timezone as tz
6
7
  from traceback import format_tb
7
8
  from typing import TYPE_CHECKING, Any, List, Optional, Union
8
9
 
9
10
  from loguru import logger
10
11
  from loguru._logger import Logger
11
- from pytz import utc
12
12
 
13
13
  from asyncz.events import TaskExecutionEvent
14
14
  from asyncz.events.constants import TASK_ERROR, TASK_EXECUTED, TASK_MISSED
@@ -97,7 +97,9 @@ class BaseExecutor(BaseStateExtra, ABC):
97
97
  if self.instances[task_id] == 0:
98
98
  del self.instances[task_id]
99
99
 
100
- self.logger.opt(exception=True).error(f"Error running task {task_id}", exc_info=True)
100
+ self.logger.opt(exception=True).error(
101
+ f"Error running task {task_id}", exc_info=True
102
+ )
101
103
 
102
104
 
103
105
  def run_task(
@@ -118,7 +120,7 @@ def run_task(
118
120
  events = []
119
121
  for run_time in run_times:
120
122
  if getattr(task, "mistrigger_grace_time", None) is not None:
121
- difference = datetime.now(utc) - run_time
123
+ difference = datetime.now(tz.utc) - run_time
122
124
  grace_time = timedelta(seconds=task.mistrigger_grace_time)
123
125
  if difference > grace_time:
124
126
  events.append(
@@ -182,7 +184,7 @@ async def run_coroutine_task(
182
184
  events = []
183
185
  for run_time in run_times:
184
186
  if getattr(task, "mistrigger_grace_time", None) is not None:
185
- difference = datetime.now(utc) - run_time
187
+ difference = datetime.now(tz.utc) - run_time
186
188
  grace_time = timedelta(seconds=task.mistrigger_grace_time)
187
189
  if difference > grace_time:
188
190
  events.append(
@@ -1,5 +1,6 @@
1
1
  import sys
2
2
  from abc import ABC, abstractmethod
3
+ from collections.abc import MutableMapping
3
4
  from datetime import datetime, timedelta
4
5
  from importlib import import_module
5
6
  from threading import RLock
@@ -9,6 +10,7 @@ from loguru import logger
9
10
  from tzlocal import get_localzone
10
11
 
11
12
  from asyncz._mapping import AsynczObjectMapping
13
+ from asyncz.enums import PluginInstance, SchedulerState
12
14
  from asyncz.events.base import SchedulerEvent, TaskEvent, TaskSubmissionEvent
13
15
  from asyncz.events.constants import (
14
16
  ALL_EVENTS,
@@ -36,21 +38,21 @@ from asyncz.exceptions import (
36
38
  )
37
39
  from asyncz.executors.base import BaseExecutor
38
40
  from asyncz.executors.pool import ThreadPoolExecutor
41
+ from asyncz.schedulers.datastructures import TaskDefaultStruct
42
+ from asyncz.state import BaseStateExtra
39
43
  from asyncz.stores.base import BaseStore
40
44
  from asyncz.stores.memory import MemoryStore
41
45
  from asyncz.tasks import Task
42
46
  from asyncz.triggers.base import BaseTrigger
43
47
  from asyncz.typing import undefined
44
- from asyncz.utils import TIMEOUT_MAX, maybe_ref, timedelta_seconds, to_bool, to_int, to_timezone
45
-
46
- try:
47
- from collections.abc import MutableMapping
48
- except ImportError:
49
- from collections.abc import MutableMapping
50
-
51
- from asyncz.enums import PluginInstance, SchedulerState
52
- from asyncz.schedulers.datastructures import TaskDefaultStruct
53
- from asyncz.state import BaseStateExtra
48
+ from asyncz.utils import (
49
+ TIMEOUT_MAX,
50
+ maybe_ref,
51
+ timedelta_seconds,
52
+ to_bool,
53
+ to_int,
54
+ to_timezone,
55
+ )
54
56
 
55
57
  DictAny = Dict[Any, Any]
56
58
 
@@ -179,7 +181,9 @@ class BaseScheduler(BaseStateExtra, ABC):
179
181
  self.real_add_task(task, store_alias, replace_existing)
180
182
  del self.pending_tasks[:]
181
183
 
182
- self.state = SchedulerState.STATE_PAUSED if paused else SchedulerState.STATE_RUNNING
184
+ self.state = (
185
+ SchedulerState.STATE_PAUSED if paused else SchedulerState.STATE_RUNNING
186
+ )
183
187
  self.logger.info("Scheduler started.")
184
188
  self.dispatch_event(SchedulerEvent(code=SCHEDULER_START))
185
189
 
@@ -281,7 +285,9 @@ class BaseScheduler(BaseStateExtra, ABC):
281
285
 
282
286
  self.dispatch_event(SchedulerEvent(code=EXECUTOR_REMOVED, alias=alias))
283
287
 
284
- def add_store(self, store: "StoreType", alias: str = "default", **store_options: Any):
288
+ def add_store(
289
+ self, store: "StoreType", alias: str = "default", **store_options: Any
290
+ ):
285
291
  """
286
292
  Adds a task store to this scheduler.
287
293
 
@@ -418,7 +424,9 @@ class BaseScheduler(BaseStateExtra, ABC):
418
424
  "max_instances": max_instances,
419
425
  "next_run_time": next_run_time,
420
426
  }
421
- task_kwargs = {key: value for key, value in task_struct.items() if value is not undefined}
427
+ task_kwargs = {
428
+ key: value for key, value in task_struct.items() if value is not undefined
429
+ }
422
430
  task = Task(self, **task_kwargs)
423
431
 
424
432
  with self.store_lock:
@@ -532,9 +540,13 @@ class BaseScheduler(BaseStateExtra, ABC):
532
540
  trigger = self.create_trigger(trigger, trigger_args)
533
541
  now = datetime.now(self.timezone)
534
542
  next_run_time = trigger.get_next_trigger_time(None, now)
535
- return self.update_task(task_id, store, trigger=trigger, next_run_time=next_run_time)
543
+ return self.update_task(
544
+ task_id, store, trigger=trigger, next_run_time=next_run_time
545
+ )
536
546
 
537
- def pause_task(self, task_id: Union[int, str], store: Optional[str] = None) -> "TaskType":
547
+ def pause_task(
548
+ self, task_id: Union[int, str], store: Optional[str] = None
549
+ ) -> "TaskType":
538
550
  """
539
551
  Causes the given task not to be executed until it is explicitly resumed.
540
552
 
@@ -588,7 +600,9 @@ class BaseScheduler(BaseStateExtra, ABC):
588
600
 
589
601
  return tasks
590
602
 
591
- def get_task(self, task_id: str, store: Optional[str] = None) -> Union["TaskType", None]:
603
+ def get_task(
604
+ self, task_id: str, store: Optional[str] = None
605
+ ) -> Union["TaskType", None]:
592
606
  """
593
607
  Returms the Task that matches the given task_id.
594
608
 
@@ -827,7 +841,9 @@ class BaseScheduler(BaseStateExtra, ABC):
827
841
  # Calculate the next run time if there is none defined
828
842
  if not getattr(task, "next_run_time", None):
829
843
  now = datetime.now(self.timezone)
830
- replacements["next_run_time"] = task.trigger.get_next_trigger_time(None, now)
844
+ replacements["next_run_time"] = task.trigger.get_next_trigger_time(
845
+ None, now
846
+ )
831
847
 
832
848
  # Apply replacements
833
849
  task._update(**replacements)
@@ -860,7 +876,9 @@ class BaseScheduler(BaseStateExtra, ABC):
860
876
  try:
861
877
  module_path, class_name = module_name.rsplit(":", 1)
862
878
  except ValueError as err:
863
- raise ImportError("%s doesn't look like a module path" % module_name) from err
879
+ raise ImportError(
880
+ "%s doesn't look like a module path" % module_name
881
+ ) from err
864
882
 
865
883
  module = import_module(module_path)
866
884
 
@@ -892,7 +910,9 @@ class BaseScheduler(BaseStateExtra, ABC):
892
910
  f"The {format(_type)} entry point does not point to a {format(_type)} class."
893
911
  ) from None
894
912
  else:
895
- raise LookupError(f"No {_type} by the name '{alias}' was found.") from None
913
+ raise LookupError(
914
+ f"No {_type} by the name '{alias}' was found."
915
+ ) from None
896
916
 
897
917
  return plugin_cls(**constructor_args)
898
918
 
@@ -944,7 +964,9 @@ class BaseScheduler(BaseStateExtra, ABC):
944
964
  self.logger.warning(
945
965
  f"Error getting due tasks from the store {store_alias}: {e}."
946
966
  )
947
- retry_wakeup_time = now + timedelta(seconds=self.store_retry_interval)
967
+ retry_wakeup_time = now + timedelta(
968
+ seconds=self.store_retry_interval
969
+ )
948
970
  if not next_wakeup_time or next_wakeup_time > retry_wakeup_time:
949
971
  next_wakeup_time = retry_wakeup_time
950
972
  continue
@@ -960,7 +982,9 @@ class BaseScheduler(BaseStateExtra, ABC):
960
982
  continue
961
983
 
962
984
  run_times = task.get_run_times(now)
963
- run_times = run_times[-1:] if run_times and task.coalesce else run_times
985
+ run_times = (
986
+ run_times[-1:] if run_times and task.coalesce else run_times
987
+ )
964
988
 
965
989
  if run_times:
966
990
  try:
@@ -990,7 +1014,9 @@ class BaseScheduler(BaseStateExtra, ABC):
990
1014
  )
991
1015
  events.append(event)
992
1016
 
993
- next_run = task.trigger.get_next_trigger_time(run_times[-1], now)
1017
+ next_run = task.trigger.get_next_trigger_time(
1018
+ run_times[-1], now
1019
+ )
994
1020
  if next_run:
995
1021
  task._update(next_run_time=next_run)
996
1022
  store.update_task(task)
@@ -1013,7 +1039,9 @@ class BaseScheduler(BaseStateExtra, ABC):
1013
1039
  wait_seconds = None
1014
1040
  self.logger.debug("No tasks. Waiting until task is added.")
1015
1041
  else:
1016
- wait_seconds = min(max(timedelta_seconds(next_wakeup_time - now), 0), TIMEOUT_MAX)
1042
+ wait_seconds = min(
1043
+ max(timedelta_seconds(next_wakeup_time - now), 0), TIMEOUT_MAX
1044
+ )
1017
1045
  self.logger.debug(
1018
1046
  f"Next wakeup is due at {next_wakeup_time} (in {wait_seconds} seconds)."
1019
1047
  )
@@ -1,9 +1,8 @@
1
1
  import pickle
2
2
  from datetime import datetime
3
+ from datetime import timezone as tz
3
4
  from typing import Any, List, Optional, Union
4
5
 
5
- from pytz import utc
6
-
7
6
  from asyncz.exceptions import AsynczException, ConflictIdError, TaskLookupError
8
7
  from asyncz.stores.base import BaseStore
9
8
  from asyncz.tasks import Task
@@ -79,7 +78,9 @@ class RedisStore(BaseStore):
79
78
  try:
80
79
  tasks.append(self.rebuild_task(state))
81
80
  except BaseException:
82
- self.logger.exception(f"Unable to restore task '{task_id}'. Removing it...")
81
+ self.logger.exception(
82
+ f"Unable to restore task '{task_id}'. Removing it..."
83
+ )
83
84
  failed_task_ids.append(task_id)
84
85
 
85
86
  if failed_task_ids:
@@ -98,7 +99,7 @@ class RedisStore(BaseStore):
98
99
  def get_all_tasks(self) -> List["TaskType"]:
99
100
  states = self.redis.hgetall(self.tasks_key)
100
101
  tasks = self.rebuild_tasks(states.items())
101
- paused_sort_key = datetime(9999, 12, 31, tzinfo=utc)
102
+ paused_sort_key = datetime(9999, 12, 31, tzinfo=tz.utc)
102
103
  return sorted(tasks, key=lambda task: task.next_run_time or paused_sort_key)
103
104
 
104
105
  def add_task(self, task: "TaskType"):
@@ -108,12 +109,15 @@ class RedisStore(BaseStore):
108
109
  with self.redis.pipeline() as pipe:
109
110
  pipe.multi()
110
111
  pipe.hset(
111
- self.tasks_key, task.id, pickle.dumps(task.__getstate__(), self.pickle_protocol)
112
+ self.tasks_key,
113
+ task.id,
114
+ pickle.dumps(task.__getstate__(), self.pickle_protocol),
112
115
  )
113
116
 
114
117
  if task.next_run_time:
115
118
  pipe.zadd(
116
- self.run_times_key, {task.id: datetime_to_utc_timestamp(task.next_run_time)}
119
+ self.run_times_key,
120
+ {task.id: datetime_to_utc_timestamp(task.next_run_time)},
117
121
  )
118
122
  pipe.execute()
119
123
 
@@ -123,11 +127,14 @@ class RedisStore(BaseStore):
123
127
 
124
128
  with self.redis.pipeline() as pipe:
125
129
  pipe.hset(
126
- self.tasks_key, task.id, pickle.dumps(task.__getstate__(), self.pickle_protocol)
130
+ self.tasks_key,
131
+ task.id,
132
+ pickle.dumps(task.__getstate__(), self.pickle_protocol),
127
133
  )
128
134
  if task.next_run_time:
129
135
  pipe.zadd(
130
- self.run_times_key, {task.id: datetime_to_utc_timestamp(task.next_run_time)}
136
+ self.run_times_key,
137
+ {task.id: datetime_to_utc_timestamp(task.next_run_time)},
131
138
  )
132
139
  else:
133
140
  pipe.zrem(self.run_times_key, task.id)
@@ -33,7 +33,7 @@ class BaseField(BaseModel):
33
33
  exprs: Any,
34
34
  is_default: Optional[bool] = False,
35
35
  compilers: Optional[List[Any]] = None,
36
- **kwargs: Any
36
+ **kwargs: Any,
37
37
  ):
38
38
  super().__init__(**kwargs)
39
39
  self.name = name
@@ -6,7 +6,13 @@ from tzlocal import get_localzone
6
6
 
7
7
  from asyncz.datastructures import IntervalState
8
8
  from asyncz.triggers.base import BaseTrigger
9
- from asyncz.utils import datetime_repr, normalize, timedelta_seconds, to_datetime, to_timezone
9
+ from asyncz.utils import (
10
+ datetime_repr,
11
+ normalize,
12
+ timedelta_seconds,
13
+ to_datetime,
14
+ to_timezone,
15
+ )
10
16
 
11
17
 
12
18
  class IntervalTrigger(BaseTrigger):
@@ -73,7 +79,9 @@ class IntervalTrigger(BaseTrigger):
73
79
  next_trigger_time = self.start_at
74
80
  else:
75
81
  time_difference_seconds = timedelta_seconds(now - self.start_at)
76
- next_interval_number = int(ceil(time_difference_seconds / self.interval_size))
82
+ next_interval_number = int(
83
+ ceil(time_difference_seconds / self.interval_size)
84
+ )
77
85
  next_trigger_time = self.start_at + self.interval * next_interval_number
78
86
 
79
87
  if self.jitter is not None:
@@ -98,7 +106,10 @@ class IntervalTrigger(BaseTrigger):
98
106
  return f"interval[{self.interval}]"
99
107
 
100
108
  def __repr__(self) -> str:
101
- options = ["interval=%r" % self.interval, "start_at=%r" % datetime_repr(self.start_at)]
109
+ options = [
110
+ "interval=%r" % self.interval,
111
+ "start_at=%r" % datetime_repr(self.start_at),
112
+ ]
102
113
  if self.end_at:
103
114
  options.append("end_at=%r" % datetime_repr(self.end_at))
104
115
  if self.jitter:
@@ -3,17 +3,20 @@ import re
3
3
  from asyncio import iscoroutinefunction
4
4
  from calendar import timegm
5
5
  from datetime import date, datetime, time, timedelta, tzinfo
6
+ from datetime import timezone as dttz
6
7
  from functools import partial
7
8
  from typing import Any, Callable, Union
8
9
 
9
- from pytz import FixedOffset, timezone, utc
10
-
11
10
  from asyncz.exceptions import AsynczException, AsynczLookupError
12
11
 
13
12
  try:
14
13
  from threading import TIMEOUT_MAX
15
14
  except ImportError:
16
15
  TIMEOUT_MAX = 4294967
16
+ try:
17
+ from zoneinfo import ZoneInfo
18
+ except ImportError:
19
+ from backports.zoneinfo import ZoneInfo
17
20
 
18
21
  BOOL_VALIDATION = {
19
22
  "true": ["true", "yes", "on", "y", "t", "1", True],
@@ -61,22 +64,13 @@ def to_bool(value: str) -> bool:
61
64
  return False
62
65
 
63
66
 
64
- def to_timezone(value: Any) -> timezone:
67
+ def to_timezone(value: Any) -> tzinfo:
65
68
  """
66
69
  Converts a value to timezone object.
67
70
  """
68
71
  if isinstance(value, str):
69
- return timezone(value)
72
+ return ZoneInfo(value)
70
73
  if isinstance(value, tzinfo):
71
- if not hasattr(value, "localize") or not hasattr(value, "normalize"):
72
- raise TypeError("Only timezones from the pytz library are supported.")
73
- if value.tzname(None) == "local":
74
- raise ValueError(
75
- "Unable to determine the name of the local timezone -- you must explicitly "
76
- "specify the name of the local timezone. Please refrain from using timezones like "
77
- "BST to prevent problems with daylight saving time. Instead, use a locale based "
78
- "timezone name (such as Europe/London)."
79
- )
80
74
  return value
81
75
  if value is not None:
82
76
  raise TypeError("Expected tzinfo, got %s instead" % value.__class__.__name__)
@@ -109,11 +103,11 @@ def to_datetime(
109
103
  tzname = values.pop("timezone")
110
104
 
111
105
  if tzname == "Z":
112
- tz = utc
106
+ tz = dttz.utc
113
107
  elif tzname:
114
108
  hours, minutes = (int(x) for x in tzname[1:].split(":"))
115
109
  sign = 1 if tzname[0] == "+" else -1
116
- tz = FixedOffset(sign * (hours * 60 + minutes))
110
+ tz = dttz(sign * timedelta(hours=hours, minutes=minutes))
117
111
 
118
112
  values = {k: int(v or 0) for k, v in values.items()}
119
113
  _datetime = datetime(**values) # type: ignore
@@ -132,7 +126,7 @@ def to_datetime(
132
126
  )
133
127
 
134
128
  if isinstance(tz, str):
135
- tz = timezone(tz)
129
+ tz = ZoneInfo(tz)
136
130
 
137
131
  return localize(_datetime, tz)
138
132
 
@@ -150,7 +144,7 @@ def utc_timestamp_to_datetime(timestamp: Union[int, float]) -> datetime:
150
144
  Converts the given timestamp to a datetime instance.
151
145
  """
152
146
  if timestamp is not None:
153
- return datetime.fromtimestamp(timestamp, utc)
147
+ return datetime.fromtimestamp(timestamp, dttz.utc)
154
148
 
155
149
 
156
150
  def timedelta_seconds(delta: Any) -> Any:
@@ -196,7 +190,9 @@ def get_callable_name(func: Any) -> Any:
196
190
  return func.__name__
197
191
  return func.__class__.__name__
198
192
 
199
- raise TypeError("Unable to determine a name for %r -- maybe it is not a callable?" % func)
193
+ raise TypeError(
194
+ "Unable to determine a name for %r -- maybe it is not a callable?" % func
195
+ )
200
196
 
201
197
 
202
198
  def obj_to_ref(obj: Any) -> str:
@@ -320,7 +316,8 @@ def check_callable_args(func: Callable[..., Any], args: Any, kwargs: Any) -> Non
320
316
 
321
317
  if unsatisfied_args:
322
318
  raise ValueError(
323
- "The following arguments have not been supplied: %s" % ", ".join(unsatisfied_args)
319
+ "The following arguments have not been supplied: %s"
320
+ % ", ".join(unsatisfied_args)
324
321
  )
325
322
 
326
323
  if unsatisfied_kwargs:
@@ -332,7 +329,8 @@ def check_callable_args(func: Callable[..., Any], args: Any, kwargs: Any) -> Non
332
329
  if not has_varargs and unmatched_args:
333
330
  raise ValueError(
334
331
  "The list of positional arguments is longer than the target callable can handle "
335
- "(allowed: %d, given in args: %d)" % (len(args) - len(unmatched_args), len(args))
332
+ "(allowed: %d, given in args: %d)"
333
+ % (len(args) - len(unmatched_args), len(args))
336
334
  )
337
335
 
338
336
  if not has_var_kwargs and unmatched_kwargs:
@@ -39,11 +39,10 @@ classifiers = [
39
39
  "Topic :: Internet :: WWW/HTTP",
40
40
  ]
41
41
  dependencies = [
42
- "asyncio>=3.4.3,<4.0.0",
43
42
  "loguru>=0.7.0,<0.8.0",
44
- "pydantic>=2.1.1,<3.0.0",
45
- "pytz>=2022.6",
46
- "tzlocal>=4.2,<5.0",
43
+ "pydantic>=2.5.3,<3.0.0",
44
+ "tzlocal>=4.0,<6.0",
45
+ 'backports.zoneinfo;python_version<"3.9"',
47
46
  ]
48
47
  keywords = [
49
48
  "api",
@@ -66,38 +65,47 @@ keywords = [
66
65
  ]
67
66
 
68
67
  [project.urls]
69
- Homepage = "https://github.com/tarsil/asyncz"
70
- Documentation = "https://asyncz.tarsild.io/"
71
- Changelog = "https://asyncz.tarsild.io/release-notes/"
68
+ Homepage = "https://github.com/dymmond/asyncz"
69
+ Documentation = "https://asyncz.dymmond.com/"
70
+ Changelog = "https://asyncz.dymmond.com/release-notes/"
72
71
  Funding = "https://github.com/sponsors/tarsil"
73
- Source = "https://github.com/tarsil/asyncz"
72
+ Source = "https://github.com/dymmond/asyncz"
73
+
74
+
75
+ [tool.hatch.envs.default]
76
+ dependencies = ["ruff", "pre-commit>=2.17.0,<3.0.0", "twine"]
74
77
 
75
78
  [project.optional-dependencies]
76
- test = [
77
- # "esmerald==0.4.2",
78
- "mock>=4.0.3",
79
+ testing = [
79
80
  "pymongo>=4.3.3,<5.0.0",
80
- "pytest >=7.1.3,<8.0.0",
81
- "pytest-cov >=2.12.0,<5.0.0",
81
+ "pytest>=7.1.3,<9.0.0",
82
+ "pytest-cov >=2.12.0,<6.0.0",
82
83
  "pytest-asyncio >=0.19.0,<1.0.0",
83
84
  "pytest-loguru>=0.2.0,<1",
84
- "redis>=4.4.0,<5.0.0",
85
- "requests >=2.27.0,<3.0.0",
86
- "ruff>=0.0.256,<1.0.0",
87
- ]
88
-
89
- dev = [
90
- "autoflake >=1.4.0,<3.0.0",
91
- "black>=22.10.0,<23.0.0",
92
- "flake8>=3.8.3,<7.0.0",
93
- "ipdb>=0.13.13",
94
- "isort>=5.0.6,<6.0.0",
85
+ "redis>=4.4.0,<6.0.0",
95
86
  "mypy>=0.982,<2.0.0",
96
- "pre-commit>=2.17.0,<3.0.0",
97
- "watchfiles>=0.16.1,<0.20.0",
87
+ "esmerald",
88
+ "pytz>=2022.6",
98
89
  ]
99
90
 
100
- doc = [
91
+ [tool.hatch.envs.default.scripts]
92
+ clean_pyc = "find . -type f -name \"*.pyc\" -delete"
93
+ clean_pycache = "find . -type d -name \"*__pycache__*\" -delete"
94
+ lint = "ruff check --fix --line-length 99 asyncz tests docs_src {args}"
95
+ build_with_check = "hatch build; twine check dist/*"
96
+
97
+ [tool.hatch.envs.test]
98
+ dependencies = ["asyncz[testing]"]
99
+
100
+ [tool.hatch.envs.test.scripts]
101
+ # needs docker services running
102
+ test = "pytest --disable-pytest-warnings -s -vv {args}"
103
+ coverage = "pytest --cov=asyncz --cov=tests --cov-report=term-missing:skip-covered --cov-report=html tests {args}"
104
+ check_types = "mypy -p asyncz"
105
+
106
+
107
+ [tool.hatch.envs.docs]
108
+ dependencies = [
101
109
  "mkautodoc >=0.2.0,<0.3.0",
102
110
  "mkdocs >=1.1.2,<2.0.0",
103
111
  "mkdocs-material >=9.0.13,<10.0.0",
@@ -106,16 +114,13 @@ doc = [
106
114
  "mkdocstrings>=0.20.0,<0.30.0",
107
115
  "pyyaml >=6.0,<7.0.0",
108
116
  ]
109
-
110
- all = ["asyncio>=3.4.3,<4.0.0", "pytz>=2022.6", "tzlocal>=4.2,<5.0"]
117
+ [tool.hatch.envs.docs.scripts]
118
+ build = "mkdocs build"
119
+ serve = "mkdocs serve --dev-addr localhost:8000"
111
120
 
112
121
  [tool.hatch.version]
113
122
  path = "asyncz/__init__.py"
114
123
 
115
- [tool.isort]
116
- profile = "black"
117
- known_third_party = ["asyncz", "pydantic", "starlette"]
118
-
119
124
  [tool.mypy]
120
125
  warn_unused_configs = true
121
126
  warn_unreachable = true
@@ -130,22 +135,15 @@ disable_error_code = "attr-defined"
130
135
  warn_unused_ignores = true
131
136
  warn_redundant_casts = true
132
137
 
133
- [tool.ruff]
134
- select = [
135
- "E", # pycodestyle errors
136
- "W", # pycodestyle warnings
137
- "F", # pyflakes
138
- "C", # flake8-comprehensions
139
- "B", # flake8-bugbear
140
- ]
141
- ignore = [
142
- "E501", # line too long, handled by black
143
- "B008", # do not perform function calls in argument defaults
144
- "C901", # too complex
145
- ]
138
+ [tool.ruff.lint]
139
+ select = ["E", "W", "F", "C", "B", "I"]
140
+ ignore = ["E501", "B008", "C901", "B026"]
146
141
 
147
142
  exclude = ["docs_src/*"]
148
143
 
144
+ [tool.ruff.lint.isort]
145
+ known-third-party = ["esmerald", "pydantic", "starlette"]
146
+
149
147
  [[tool.mypy.overrides]]
150
148
  module = "asyncz.tests.*"
151
149
  ignore_missing_imports = true
@@ -1 +0,0 @@
1
- __version__ = "0.5.0"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes