asyncz 0.6.0__tar.gz → 0.8.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.6.0 → asyncz-0.8.0}/PKG-INFO +42 -65
  2. {asyncz-0.6.0 → asyncz-0.8.0}/README.md +25 -25
  3. asyncz-0.8.0/asyncz/__init__.py +1 -0
  4. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/_mapping.py +1 -1
  5. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/contrib/esmerald/scheduler.py +9 -9
  6. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/executors/base.py +6 -4
  7. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/schedulers/asyncio.py +7 -2
  8. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/schedulers/base.py +5 -11
  9. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/stores/redis.py +15 -8
  10. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/triggers/interval.py +14 -3
  11. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/utils.py +11 -17
  12. {asyncz-0.6.0 → asyncz-0.8.0}/pyproject.toml +38 -30
  13. asyncz-0.6.0/asyncz/__init__.py +0 -1
  14. {asyncz-0.6.0 → asyncz-0.8.0}/.gitignore +0 -0
  15. {asyncz-0.6.0 → asyncz-0.8.0}/LICENSE +0 -0
  16. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/contrib/__init__.py +0 -0
  17. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/contrib/esmerald/__init__.py +0 -0
  18. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/contrib/esmerald/decorator.py +0 -0
  19. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/datastructures.py +0 -0
  20. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/enums.py +0 -0
  21. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/events/__init__.py +0 -0
  22. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/events/base.py +0 -0
  23. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/events/constants.py +0 -0
  24. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/exceptions.py +0 -0
  25. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/executors/__init__.py +0 -0
  26. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/executors/asyncio.py +0 -0
  27. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/executors/debug.py +0 -0
  28. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/executors/pool.py +0 -0
  29. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/executors/types.py +0 -0
  30. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/py.typed +0 -0
  31. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/schedulers/__init__.py +0 -0
  32. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/schedulers/datastructures.py +0 -0
  33. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/schedulers/types.py +0 -0
  34. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/schedulers/utils.py +0 -0
  35. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/state.py +0 -0
  36. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/stores/__init__.py +0 -0
  37. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/stores/base.py +0 -0
  38. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/stores/memory.py +0 -0
  39. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/stores/mongo.py +0 -0
  40. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/stores/types.py +0 -0
  41. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/tasks/__init__.py +0 -0
  42. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/tasks/base.py +0 -0
  43. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/tasks/types.py +0 -0
  44. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/triggers/__init__.py +0 -0
  45. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/triggers/base.py +0 -0
  46. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/triggers/combination.py +0 -0
  47. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/triggers/cron/__init__.py +0 -0
  48. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/triggers/cron/constants.py +0 -0
  49. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/triggers/cron/expressions.py +0 -0
  50. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/triggers/cron/fields.py +0 -0
  51. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/triggers/cron/trigger.py +0 -0
  52. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/triggers/cron/types.py +0 -0
  53. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/triggers/date.py +0 -0
  54. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/triggers/types.py +0 -0
  55. {asyncz-0.6.0 → asyncz-0.8.0}/asyncz/typing.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: asyncz
3
- Version: 0.6.0
3
+ Version: 0.8.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,43 +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
42
  Requires-Dist: pydantic<3.0.0,>=2.5.3
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==24.4.2; extra == 'dev'
52
- Requires-Dist: flake8<8.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: pdbpp; extra == 'dev'
57
- Requires-Dist: pre-commit<3.0.0,>=2.17.0; extra == 'dev'
58
- Requires-Dist: watchfiles<0.22.0,>=0.16.1; extra == 'dev'
59
- Provides-Extra: doc
60
- Requires-Dist: mdx-include<2.0.0,>=1.4.2; extra == 'doc'
61
- Requires-Dist: mkautodoc<0.3.0,>=0.2.0; extra == 'doc'
62
- Requires-Dist: mkdocs-markdownextradata-plugin<0.3.0,>=0.2.5; extra == 'doc'
63
- Requires-Dist: mkdocs-material<10.0.0,>=9.0.13; extra == 'doc'
64
- Requires-Dist: mkdocs<2.0.0,>=1.1.2; extra == 'doc'
65
- Requires-Dist: mkdocstrings<0.30.0,>=0.20.0; extra == 'doc'
66
- Requires-Dist: pyyaml<7.0.0,>=6.0; extra == 'doc'
67
- Provides-Extra: test
68
- Requires-Dist: mock>=4.0.3; extra == 'test'
69
- Requires-Dist: pymongo<5.0.0,>=4.3.3; extra == 'test'
70
- Requires-Dist: pytest-asyncio<1.0.0,>=0.19.0; extra == 'test'
71
- Requires-Dist: pytest-cov<6.0.0,>=2.12.0; extra == 'test'
72
- Requires-Dist: pytest-loguru<1,>=0.2.0; extra == 'test'
73
- Requires-Dist: pytest<9.0.0,>=7.1.3; extra == 'test'
74
- Requires-Dist: redis<6.0.0,>=4.4.0; extra == 'test'
75
- Requires-Dist: requests<3.0.0,>=2.27.0; extra == 'test'
76
- Requires-Dist: ruff<1.0.0,>=0.0.256; extra == 'test'
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'
77
54
  Description-Content-Type: text/markdown
78
55
 
79
56
  # Asyncz
@@ -87,8 +64,8 @@ Description-Content-Type: text/markdown
87
64
  </p>
88
65
 
89
66
  <p align="center">
90
- <a href="https://github.com/tarsil/asyncz/workflows/Test%20Suite/badge.svg?event=push&branch=main" target="_blank">
91
- <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">
92
69
  </a>
93
70
 
94
71
  <a href="https://pypi.org/project/asyncz" target="_blank">
@@ -102,9 +79,9 @@ Description-Content-Type: text/markdown
102
79
 
103
80
  ---
104
81
 
105
- **Documentation**: [https://asyncz.tarsild.io](https://asyncz.tarsild.io) 📚
82
+ **Documentation**: [https://asyncz.dymmond.com](https://asyncz.dymmond.com) 📚
106
83
 
107
- **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)
108
85
 
109
86
  ---
110
87
 
@@ -146,16 +123,16 @@ completely indepent from any framework as well as inside ASGI frameworks such as
146
123
 
147
124
  Asyncz comes with special support to [Esmerald](https://esmerald.dymmond.com) for the simple reason that the author is
148
125
  the same but it can be added more support. If you are interested in adding support to your favourite frameworks then
149
- see the [contributing](https://asyncz.tarsild.io/contributing.md) section.
126
+ see the [contributing](https://asyncz.dymmond.com/contributing.md) section.
150
127
 
151
128
  ## Concepts
152
129
 
153
130
  Like APScheduler, Asyncz also brings four kinds of components:
154
131
 
155
- * [Schedulers](https://asyncz.tarsild.io/schedulers.md)
156
- * [Triggers](https://asyncz.tarsild.io/triggers.md)
157
- * [Stores](https://asyncz.tarsild.io/stores.md)
158
- * [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)
159
136
 
160
137
  ## Requirements
161
138
 
@@ -174,28 +151,28 @@ $ pip install asyncz
174
151
 
175
152
  ## The right decisions
176
153
 
177
- How do you know if you are choosing the right [scheduler](https://asyncz.tarsild.io/schedulers.md),
178
- [triggers](https://asyncz.tarsild.io/triggers.md), [stores](https://asyncz.tarsild.io/stores.md)
179
- 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)?
180
157
 
181
158
  Well, Asyncz is intentionally designed for specific systems and already helps you out with some of
182
159
  those questions.
183
160
 
184
- * **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).
185
162
  * **Triggers** - Here it will depend of the periocidity of our tasks. Example:
186
- * [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
187
164
  scheduling a task on a native UNIX like based system.
188
- * [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
189
166
  point of time.
190
- * [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
191
168
  intervals of time.
192
- * [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
193
170
  like to combine more than one trigger (cron, interval and date) together.
194
- * **Stores** - Natively only supports [redis](https://asyncz.tarsild.io/stores.md#redisstore),
195
- [mongo](https://asyncz.tarsild.io/stores.md#mongodbstore) and [memory](https://asyncz.tarsild.io/stores.md#memorystore).
196
- * **Executors** - Natively only supports [AsyncIOExecutor](https://asyncz.tarsild.io/executors.md#asyncioexecutor),
197
- [ThreadPoolExecutor](https://asyncz.tarsild.io/executors.md#threadpoolexecutor) and
198
- [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).
199
176
 
200
177
  Sometimes having a lot of options makes the decision making very hard and Asyncz is intentionally
201
178
  designed and driven to simplify and for specific use cases but is not limited to those. In every
@@ -222,7 +199,7 @@ scheduler = AsyncIOScheduler()
222
199
  ```
223
200
 
224
201
  Initialize the rest of the application after the `scheduler` initialisation.
225
- 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.
226
203
 
227
204
  This is in simple terms and in a nutshell how to start with Asyncz quickly. For more information,
228
205
  details and examples how to leverage Asyncz simply navigate through the documentation and have
@@ -230,11 +207,11 @@ fun 😁🎉.
230
207
 
231
208
  ## ASGI support
232
209
 
233
- 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)
234
211
  and brings some batteries that are currently used by the framework and leveraging Asyncz.
235
212
 
236
213
  If you wish to have support to any other framework such as FastAPI, Starlite, Starlette or
237
- 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
238
215
  do it.
239
216
 
240
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.8.0"
@@ -27,6 +27,6 @@ class AsynczObjectMapping(BaseModel):
27
27
  def stores(self) -> Dict[str, str]:
28
28
  return {
29
29
  "memory": "asyncz.stores.memory:MemoryTaskStore",
30
- "mongo": "asyncz.stores.mongo:MongoDBStore",
30
+ "mongodb": "asyncz.stores.mongo:MongoDBStore",
31
31
  "redis": "asyncz.stores.redis:RedisStore",
32
32
  }
@@ -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,
@@ -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(
@@ -18,11 +18,16 @@ class AsyncIOScheduler(BaseScheduler):
18
18
  event_loop: AsyncOP event loop to use. Default to the global event loop.
19
19
  """
20
20
 
21
+ event_loop: Optional[Any] = None
22
+ timeout: Optional[int] = None
23
+ timezone: Optional[str] = None
24
+
21
25
  def __init__(
22
26
  self, event_loop: Optional[Any] = None, timeout: Optional[int] = None, **kwargs: Any
23
27
  ) -> None:
24
28
  super().__init__(**kwargs)
25
- self.event_loop = event_loop
29
+ if self.event_loop is None:
30
+ self.event_loop = event_loop
26
31
  self.timeout = timeout
27
32
 
28
33
  def start(self, paused: bool = False):
@@ -36,7 +41,7 @@ class AsyncIOScheduler(BaseScheduler):
36
41
  self.stop_timer()
37
42
 
38
43
  def _setup(self, config: Any) -> None:
39
- self.event_loop = maybe_ref(config.pop("event_loop", None))
44
+ self.event_loop = maybe_ref(config.pop("event_loop", asyncio.get_event_loop()))
40
45
  super()._setup(config)
41
46
 
42
47
  def start_timer(self, wait_seconds: Optional[int] = None):
@@ -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,6 +38,8 @@ 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
@@ -43,15 +47,6 @@ from asyncz.triggers.base import BaseTrigger
43
47
  from asyncz.typing import undefined
44
48
  from asyncz.utils import TIMEOUT_MAX, maybe_ref, timedelta_seconds, to_bool, to_int, to_timezone
45
49
 
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
54
-
55
50
  DictAny = Dict[Any, Any]
56
51
 
57
52
  if TYPE_CHECKING:
@@ -186,7 +181,6 @@ class BaseScheduler(BaseStateExtra, ABC):
186
181
  if not paused:
187
182
  self.wakeup()
188
183
 
189
- @abstractmethod
190
184
  def shutdown(self, wait: bool = True):
191
185
  """
192
186
  Shuts down the scheduler, along with its executors and task stores.
@@ -821,7 +815,7 @@ class BaseScheduler(BaseStateExtra, ABC):
821
815
  replace_existing: The flag indicating the replacement of the task.
822
816
  """
823
817
  replacements = {}
824
- for key, value in self.task_defaults.dict(exclude_none=True).items():
818
+ for key, value in self.task_defaults.model_dump(exclude_none=True).items():
825
819
  replacements[key] = value
826
820
 
827
821
  # Calculate the next run time if there is none defined
@@ -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)
@@ -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:
@@ -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
43
  "pydantic>=2.5.3,<3.0.0",
45
- "pytz>=2022.6",
46
- "tzlocal>=4.2,<5.0",
44
+ "tzlocal>=4.0,<6.0",
45
+ 'backports.zoneinfo;python_version<"3.9"',
47
46
  ]
48
47
  keywords = [
49
48
  "api",
@@ -66,39 +65,48 @@ 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", "ipdb", "pdbpp"]
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
81
  "pytest>=7.1.3,<9.0.0",
81
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
85
  "redis>=4.4.0,<6.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==24.4.2",
92
- "flake8>=3.8.3,<8.0.0",
93
- "ipdb>=0.13.13",
94
- "pdbpp",
95
- "isort>=5.0.6,<6.0.0",
96
86
  "mypy>=0.982,<2.0.0",
97
- "pre-commit>=2.17.0,<3.0.0",
98
- "watchfiles>=0.16.1,<0.22.0",
87
+ "esmerald",
88
+ "pytz>=2022.6",
99
89
  ]
100
90
 
101
- 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
+ features = ["testing"]
99
+
100
+ [tool.hatch.envs.test.scripts]
101
+ # needs docker services running
102
+ test = "pytest --disable-pytest-warnings {args}"
103
+ test_man = "pytest --disable-pytest-warnings -s -vv {args}"
104
+ coverage = "pytest --cov=asyncz --cov=tests --cov-report=term-missing:skip-covered --cov-report=html tests {args}"
105
+ check_types = "mypy -p asyncz"
106
+
107
+
108
+ [tool.hatch.envs.docs]
109
+ dependencies = [
102
110
  "mkautodoc >=0.2.0,<0.3.0",
103
111
  "mkdocs >=1.1.2,<2.0.0",
104
112
  "mkdocs-material >=9.0.13,<10.0.0",
@@ -107,16 +115,13 @@ doc = [
107
115
  "mkdocstrings>=0.20.0,<0.30.0",
108
116
  "pyyaml >=6.0,<7.0.0",
109
117
  ]
110
-
111
- all = ["asyncio>=3.4.3,<4.0.0", "pytz>=2022.6", "tzlocal>=4.2,<5.0"]
118
+ [tool.hatch.envs.docs.scripts]
119
+ build = "mkdocs build"
120
+ serve = "mkdocs serve --dev-addr localhost:8000"
112
121
 
113
122
  [tool.hatch.version]
114
123
  path = "asyncz/__init__.py"
115
124
 
116
- [tool.isort]
117
- profile = "black"
118
- known_third_party = ["asyncz", "pydantic", "starlette"]
119
-
120
125
  [tool.mypy]
121
126
  warn_unused_configs = true
122
127
  warn_unreachable = true
@@ -137,6 +142,9 @@ ignore = ["E501", "B008", "C901", "B026"]
137
142
 
138
143
  exclude = ["docs_src/*"]
139
144
 
145
+ [tool.ruff.lint.isort]
146
+ known-third-party = ["esmerald", "pydantic", "starlette"]
147
+
140
148
  [[tool.mypy.overrides]]
141
149
  module = "asyncz.tests.*"
142
150
  ignore_missing_imports = true
@@ -1 +0,0 @@
1
- __version__ = "0.6.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