Initial commit: Email alerts application

This commit is contained in:
Iyeoluwa Akinrinola
2025-07-25 11:31:36 +01:00
commit adfb625ae9
6322 changed files with 2882826 additions and 0 deletions
@@ -0,0 +1 @@
Dmitry Inyutin <inyutin.da@gmail.com>
@@ -0,0 +1,8 @@
The MIT License (MIT)
Copyright (c) 2020 aiohttp_retry Authors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,261 @@
Metadata-Version: 2.1
Name: aiohttp-retry
Version: 2.9.1
Summary: Simple retry client for aiohttp
Home-page: https://github.com/inyutin/aiohttp_retry
Author: Dmitry Inyutin
Author-email: inyutin.da@gmail.com
License: MIT
Keywords: aiohttp retry client
Platform: any
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: AUTHORS
Requires-Dist: aiohttp
# Simple aiohttp retry client
Python 3.7 or higher.
**Install**: `pip install aiohttp-retry`.
[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/inyutin)
### Breaking API changes
- Everything between [2.7.0 - 2.8.3) is yanked.
There is a bug with evaluate_response_callback, it led to infinite retries
- 2.8.0 is incorrect and yanked.
https://github.com/inyutin/aiohttp_retry/issues/79
- Since 2.5.6 this is a new parameter in ```get_timeout``` func called "response".
If you have defined your own ```RetryOptions```, you should add this param into it.
Issue about this: https://github.com/inyutin/aiohttp_retry/issues/59
### Examples of usage:
```python
from aiohttp_retry import RetryClient, ExponentialRetry
async def main():
retry_options = ExponentialRetry(attempts=1)
retry_client = RetryClient(raise_for_status=False, retry_options=retry_options)
async with retry_client.get('https://ya.ru') as response:
print(response.status)
await retry_client.close()
```
```python
from aiohttp import ClientSession
from aiohttp_retry import RetryClient
async def main():
client_session = ClientSession()
retry_client = RetryClient(client_session=client_session)
async with retry_client.get('https://ya.ru') as response:
print(response.status)
await client_session.close()
```
```python
from aiohttp_retry import RetryClient, RandomRetry
async def main():
retry_options = RandomRetry(attempts=1)
retry_client = RetryClient(raise_for_status=False, retry_options=retry_options)
response = await retry_client.get('/ping')
print(response.status)
await retry_client.close()
```
```python
from aiohttp_retry import RetryClient
async def main():
async with RetryClient() as client:
async with client.get('https://ya.ru') as response:
print(response.status)
```
You can change parameters between attempts by passing multiple requests params:
```python
from aiohttp_retry import RetryClient, RequestParams, ExponentialRetry
async def main():
retry_client = RetryClient(raise_for_status=False)
async with retry_client.requests(
params_list=[
RequestParams(
method='GET',
url='https://ya.ru',
),
RequestParams(
method='GET',
url='https://ya.ru',
headers={'some_header': 'some_value'},
),
]
) as response:
print(response.status)
await retry_client.close()
```
You can also add some logic, F.E. logging, on failures by using trace mechanic.
```python
import logging
import sys
from types import SimpleNamespace
from aiohttp import ClientSession, TraceConfig, TraceRequestStartParams
from aiohttp_retry import RetryClient, ExponentialRetry
handler = logging.StreamHandler(sys.stdout)
logging.basicConfig(handlers=[handler])
logger = logging.getLogger(__name__)
retry_options = ExponentialRetry(attempts=2)
async def on_request_start(
session: ClientSession,
trace_config_ctx: SimpleNamespace,
params: TraceRequestStartParams,
) -> None:
current_attempt = trace_config_ctx.trace_request_ctx['current_attempt']
if retry_options.attempts <= current_attempt:
logger.warning('Wow! We are in last attempt')
async def main():
trace_config = TraceConfig()
trace_config.on_request_start.append(on_request_start)
retry_client = RetryClient(retry_options=retry_options, trace_configs=[trace_config])
response = await retry_client.get('https://httpstat.us/503', ssl=False)
print(response.status)
await retry_client.close()
```
Look tests for more examples. \
**Be aware: last request returns as it is.**
**If the last request ended with exception, that this exception will be raised from RetryClient request**
### Documentation
`RetryClient` takes the same arguments as ClientSession[[docs](https://docs.aiohttp.org/en/stable/client_reference.html)] \
`RetryClient` has methods:
- request
- get
- options
- head
- post
- put
- patch
- put
- delete
They are same as for `ClientSession`, but take one possible additional argument:
```python
class RetryOptionsBase:
def __init__(
self,
attempts: int = 3, # How many times we should retry
statuses: Iterable[int] | None = None, # On which statuses we should retry
exceptions: Iterable[type[Exception]] | None = None, # On which exceptions we should retry, by default on all
retry_all_server_errors: bool = True, # If should retry all 500 errors or not
# a callback that will run on response to decide if retry
evaluate_response_callback: EvaluateResponseCallbackType | None = None,
):
...
@abc.abstractmethod
def get_timeout(self, attempt: int, response: Optional[Response] = None) -> float:
raise NotImplementedError
```
You can specify `RetryOptions` both for `RetryClient` and it's methods.
`RetryOptions` in methods override `RetryOptions` defined in `RetryClient` constructor.
**Important**: by default all 5xx responses are retried + statuses you specified as ```statuses``` param
If you will pass ```retry_all_server_errors=False``` than you can manually set what 5xx errors to retry.
You can define your own timeouts logic or use:
- ```ExponentialRetry``` with exponential backoff
- ```RandomRetry``` for random backoff
- ```ListRetry``` with backoff you predefine by list
- ```FibonacciRetry``` with backoff that looks like fibonacci sequence
- ```JitterRetry``` exponential retry with a bit of randomness
**Important**: you can proceed server response as an parameter for calculating next timeout.
However this response can be None, server didn't make a response or you have set up ```raise_for_status=True```
Look here for an example: https://github.com/inyutin/aiohttp_retry/issues/59
Additionally, you can specify ```evaluate_response_callback```. It receive a ```ClientResponse``` and decide to retry or not by returning a bool.
It can be useful, if server API sometimes response with malformed data.
#### Request Trace Context
`RetryClient` add *current attempt number* to `request_trace_ctx` (see examples,
for more info see [aiohttp doc](https://docs.aiohttp.org/en/stable/client_advanced.html#aiohttp-client-tracing)).
### Change parameters between retries
`RetryClient` also has a method called `requests`. This method should be used if you want to make requests with different params.
```python
@dataclass
class RequestParams:
method: str
url: _RAW_URL_TYPE
headers: dict[str, Any] | None = None
trace_request_ctx: dict[str, Any] | None = None
kwargs: dict[str, Any] | None = None
```
```python
def requests(
self,
params_list: list[RequestParams],
retry_options: RetryOptionsBase | None = None,
raise_for_status: bool | None = None,
) -> _RequestContext:
```
You can find an example of usage above or in tests.
But basically `RequestParams` is a structure to define params for `ClientSession.request` func.
`method`, `url`, `headers` `trace_request_ctx` defined outside kwargs, because they are popular.
There is also an old way to change URL between retries by specifying ```url``` as list of urls. Example:
```python
from aiohttp_retry import RetryClient
retry_client = RetryClient()
async with retry_client.get(url=['/internal_error', '/ping']) as response:
text = await response.text()
assert response.status == 200
assert text == 'Ok!'
await retry_client.close()
```
In this example we request ```/interval_error```, fail and then successfully request ```/ping```.
If you specify less urls than ```attempts``` number in ```RetryOptions```, ```RetryClient``` will request last url at last attempts.
This means that in example above we would request ```/ping``` once again in case of failure.
### Types
`aiohttp_retry` is a typed project. It should be fully compatible with mypy.
It also introduce one special type:
```
ClientType = Union[ClientSession, RetryClient]
```
This type can be imported by ```from aiohttp_retry.types import ClientType```
@@ -0,0 +1,16 @@
aiohttp_retry-2.9.1.dist-info/AUTHORS,sha256=GlJRXBnogod3eAzFrL3YBj8lrNy1PVoeaKCnNm2zN5M,38
aiohttp_retry-2.9.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
aiohttp_retry-2.9.1.dist-info/LICENSE,sha256=rKYSbPXF6EyrQeo7xh9TV4YWMWqD6-EwzeDUt2boof4,1087
aiohttp_retry-2.9.1.dist-info/METADATA,sha256=NVvnJQd6heUBRFSC8S1AR5TtTODUR4JmoY3grJKfkwg,8750
aiohttp_retry-2.9.1.dist-info/RECORD,,
aiohttp_retry-2.9.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
aiohttp_retry-2.9.1.dist-info/top_level.txt,sha256=b-4ybD7H7PD7sNUDod6pes6gfHuEde1xETO4fHqAMog,14
aiohttp_retry/__init__.py,sha256=YIVHUfIbOL5QhMEOs7cQlyrmm57o2SgjYC_V-P0aUes,79
aiohttp_retry/__pycache__/__init__.cpython-311.pyc,,
aiohttp_retry/__pycache__/client.cpython-311.pyc,,
aiohttp_retry/__pycache__/retry_options.cpython-311.pyc,,
aiohttp_retry/__pycache__/types.cpython-311.pyc,,
aiohttp_retry/client.py,sha256=VjrglGKv8KPyVXn1RLnv3vXcAm83m8WDJyXX9Pds_yo,12328
aiohttp_retry/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiohttp_retry/retry_options.py,sha256=l9k-P_0v-K7Q9F1wY2Z_jjUNlhplH2_F9zZ73xZ-El0,9014
aiohttp_retry/types.py,sha256=lO3nb__G8p1jWBcD9ptKh_YHgEy6pPOH_HCNM69BH8s,141
@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.42.0)
Root-Is-Purelib: true
Tag: py3-none-any
@@ -0,0 +1 @@
aiohttp_retry