Repository: jgosmann/dmarc-metrics-exporter (language: python)

Issue:
Fetch issue
Hello,

I still have an issue probably linked to Office365's IMAP implementation with version 0.4.2
I'll investigate asap

Any idea?
I tried marking emails as unread without success.

```
INFO:     Waiting for application startup.                                                                                                                                                                       │
│ INFO:     ASGI 'lifespan' protocol appears unsupported.                                                                                                                                                          │
│ INFO:     Application startup complete.                                                                                                                                                                          │
│ INFO:     Uvicorn running on http://0.0.0.0:9797 (Press CTRL+C to quit)                                                                                                                                          │
│ Error during IMAP queue polling.                                                                                                                                                                                 │
│ Traceback (most recent call last):                                                                                                                                                                               │
│   File "/venv/lib/python3.7/site-packages/dmarc_metrics_exporter/imap_queue.py", line 59, in _consume                                                                                                            │
│     async for uid, msg in client.fetch(1, msg_count):                                                                                                                                                            │
│   File "/venv/lib/python3.7/site-packages/dmarc_metrics_exporter/imap_queue.py", line 132, in fetch                                                                                                              │
│     f"Expected group termination with ')', but got '{terminator}'."                                                                                                                                              │
│ dmarc_metrics_exporter.imap_queue.ImapClientError: Expected group termination with ')', but got 'b' FLAGS (\\Seen))''.
```

Thanks for your project!


Discussion / hints:
I didn't implement the parsing of the server's response in a sufficiently general way. I'm working on it.
I pushed a commit (https://github.com/jgosmann/dmarc-metrics-exporter/commit/b0a14edf53306006f65e333907ce5e7d2f871f54) to the `main` branch that hopefully fixes this issue. Can you try it out and verify that the fix works?
Thanks for the commit.

WIth b0a14ed, I got:
```
Error during IMAP queue polling.
Traceback (most recent call last):
  File "/home/user/dev/dmarc-metrics-exporter/dmarc_metrics_exporter/imap_queue.py", line 61, in _consume
    async for uid, msg in client.fetch(1, msg_count):
  File "/home/user/dev/dmarc-metrics-exporter/dmarc_metrics_exporter/imap_queue.py", line 124, in fetch
    uid = next(value for key, value in parsed_line[2] if key == "UID")
  File "/home/user/.local/lib/python3.9/site-packages/pyparsing/results.py", line 193, in __getitem__
    return self._toklist[i]
IndexError: list index out of range
```
I need to know more precisely what the response of the IMAP server looks like. I pushed a commit to main that allows to modify the logging configuration. Can you use that version and add the following to you configuration file?
```json
    "logging": {
        "version": 1,
        "loggers": {
            "aioimaplib": {
                "level": "DEBUG"
            },
            "uvicorn": {
                "level": "INFO"
            }
        }
    }
```
⚠️ Please be aware that this might log the IMAP password in clear text!

The post the relevant lines from the log here. The relevant lines should start with these lines:
```
DEBUG:aioimaplib.aioimaplib:state -> SELECTED
DEBUG:aioimaplib.aioimaplib:Sending : b'XXXXX FETCH X:X (UID RFC822)\r\n'
```
where the `X` will can change (i.e. they won't match exactly). The relevant lines should end with either with a line like the following or when the program raises the exception you posted above:
```
DEBUG:aioimaplib.aioimaplib:tagged status b'XXXXX OK FETCH completed.'
```

Full sample output with test data:
```
DEBUG:aioimaplib.aioimaplib:state -> SELECTED
DEBUG:aioimaplib.aioimaplib:Sending : b'HGKD6 FETCH 1:1 (UID RFC822)\r\n'
DEBUG:aioimaplib.aioimaplib:Received : b'* 1 FETCH (FLAGS (\\Seen) UID 29 RFC822 {2618}\r\nReturn-Path: <noreply-dmarc-support@google.com>\r\nReceived: from 172.18.0.1 (HELO jans-mbp-870.speedport_w_921v_1_48_000); Fri Jan 07 16:52:21 UTC 2022\r\nContent-Type: multipart/mixed; boundary="===============8004666782478388790=="\r\nSubject: DMARC Aggregate Report\r\nFrom: noreply-dmarc-support@google.com\r\nTo: queue@localhost\r\n\r\n--===============8004666782478388790==\r\nContent-Type: message/rfc822\r\nContent-Transfer-Encoding: 8bit\r\nMIME-Version: 1.0\r\nContent-Disposition: attachment\r\n\r\nContent-Type: application/zip\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n filename="reporter.com!localhost!1601510400!1601596799.zip"\r\n\r\nUEsDBBQAAAAAAIqOJ1RA51hJrAQAAKwEAAAwAAAAcmVwb3J0ZXIuY29tIWxvY2FsaG9zdCExNjAx\r\nNTEwNDAwITE2MDE1OTY3OTkueG1sPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgi\r\nID8+CjxmZWVkYmFjaz4KICA8cmVwb3J0X21ldGFkYXRhPgogICAgPG9yZ19uYW1lPmdvb2dsZS5j\r\nb208L29yZ19uYW1lPgogICAgPGVtYWlsPm5vcmVwbHktZG1hcmMtc3VwcG9ydEBnb29nbGUuY29t\r\nPC9lbWFpbD4KICAgIDxleHRyYV9jb250YWN0X2luZm8+aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5j\r\nb20vYS9hbnN3ZXIvMjQ2NjU4MDwvZXh0cmFfY29udGFjdF9pbmZvPgogICAgPHJlcG9ydF9pZD4x\r\nMjU5ODg2NjkxNTgxNzc0ODY2MTwvcmVwb3J0X2lkPgogICAgPGRhdGVfcmFuZ2U+CiAgICAgIDxi\r\nZWdpbj4xNjA3Mjk5MjAwPC9iZWdpbj4KICAgICAgPGVuZD4xNjA3Mzg1NTk5PC9lbmQ+CiAgICA8\r\nL2RhdGVfcmFuZ2U+CiAgPC9yZXBvcnRfbWV0YWRhdGE+CiAgPHBvbGljeV9wdWJsaXNoZWQ+CiAg\r\nICA8ZG9tYWluPm15ZG9tYWluLmRlPC9kb21haW4+CiAgICA8YWRraW0+cjwvYWRraW0+CiAgICA8\r\nYXNwZj5yPC9hc3BmPgogICAgPHA+bm9uZTwvcD4KICAgIDxzcD5ub25lPC9zcD4KICAgIDxwY3Q+\r\nMTAwPC9wY3Q+CiAgPC9wb2xpY3lfcHVibGlzaGVkPgogIDxyZWNvcmQ+CiAgICA8cm93PgogICAg\r\nICA8c291cmNlX2lwPmRlYWQ6YmVlZjoxOmFiYzo6PC9zb3VyY2VfaXA+CiAgICAgIDxjb3VudD4x\r\nPC9jb3VudD4KICAgICAgPHBvbGljeV9ldmFsdWF0ZWQ+CiAgICAgICAgPGRpc3Bvc2l0aW9uPm5v\r\nbmU8L2Rpc3Bvc2l0aW9uPgogICAgICAgIDxka2ltPnBhc3M8L2RraW0+CiAgICAgICAgPHNwZj5m\r\nYWlsPC9zcGY+CiAgICAgIDwvcG9saWN5X2V2YWx1YXRlZD4KICAgIDwvcm93PgogICAgPGlkZW50\r\naWZpZXJzPgogICAgICA8aGVhZGVyX2Zyb20+bXlkb21haW4uZGU8L2hlYWRlcl9mcm9tPgogICAg\r\nPC9pZGVudGlmaWVycz4KICAgIDxhdXRoX3Jlc3VsdHM+CiAgICAgIDxka2ltPgogICAgICAgIDxk\r\nb21haW4+bXlkb21haW4uZGU8L2RvbWFpbj4KICAgICAgICA8cmVzdWx0PnBhc3M8L3Jlc3VsdD4K\r\nICAgICAgICA8c2VsZWN0b3I+ZGVmYXVsdDwvc2VsZWN0b3I+CiAgICAgIDwvZGtpbT4KICAgICAg\r\nPHNwZj4KICAgICAgICA8ZG9tYWluPm15LXNwZi1kb21haW4uZGU8L2RvbWFpbj4KICAgICAgICA8\r\ncmVzdWx0PnBhc3M8L3Jlc3VsdD4KICAgICAgPC9zcGY+CiAgICA8L2F1dGhfcmVzdWx0cz4KICA8\r\nL3JlY29yZD4KPC9mZWVkYmFjaz5QSwECFAMUAAAAAACKjidUQOdYSawEAACsBAAAMAAAAAAAAAAA\r\nAAAAgAEAAAAAcmVwb3J0ZXIuY29tIWxvY2FsaG9zdCExNjAxNTEwNDAwITE2MDE1OTY3OTkueG1s\r\nUEsFBgAAAAABAAEAXgAAAPoEAAAAAA==\r\n\r\n--===============8004666782478388790==--\r\n)\r\n'
DEBUG:aioimaplib.aioimaplib:Received : b'HGKD6 OK FETCH completed.\r\n'
DEBUG:aioimaplib.aioimaplib:tagged status b'HGKD6 OK FETCH completed.'
```
Hello,

With 47194d0,

Fetch sounds ok:
```
DEBUG:aioimaplib.aioimaplib:state -> SELECTED
DEBUG:aioimaplib.aioimaplib:Sending : b'NGNJ6 FETCH 1:1 (UID RFC822)\r\n'
DEBUG:aioimaplib.aioimaplib:Received : b'* 1 FETCH (UID 1771 RFC822 {9958}\r\nMESSAGE_SOURCE\r\n FLAGS (\\Seen))\r\n'
DEBUG:aioimaplib.aioimaplib:Received : b'NGNJ6 OK FETCH completed.\r\n'
DEBUG:aioimaplib.aioimaplib:tagged status b'NGNJ6 OK FETCH completed.'
```
I replaced message content by MESSAGE_SOURCE for confidentiality.

Output without DEBUG mode:
```
INFO:uvicorn.error:Waiting for application startup.
INFO:uvicorn.error:ASGI 'lifespan' protocol appears unsupported.
INFO:uvicorn.error:Application startup complete.
INFO:uvicorn.error:Uvicorn running on http://0.0.0.0:9797 (Press CTRL+C to quit)
INFO:uvicorn.access:127.0.0.1:42724 - "GET /metrics HTTP/1.1" 200
INFO:uvicorn.access:127.0.0.1:42726 - "GET /metrics HTTP/1.1" 200
```
So everything looks fine (not exception is raised).

However prometheus time series are not created:
```sh
$ curl -s http://127.0.0.1:9797/metrics | grep dmarc
# HELP dmarc_total Total number of reported messages.
# TYPE dmarc_total counter
# HELP dmarc_compliant_total Total number of DMARC compliant messages.
# TYPE dmarc_compliant_total counter
# HELP dmarc_quarantine_total Total number of quarantined messages.
# TYPE dmarc_quarantine_total counter
# HELP dmarc_reject_total Total number of rejected messages.
# TYPE dmarc_reject_total counter
# HELP dmarc_spf_aligned_total Total number of SPF algined messages.
# TYPE dmarc_spf_aligned_total counter
# HELP dmarc_spf_pass_total Total number of messages with raw SPF pass.
# TYPE dmarc_spf_pass_total counter
# HELP dmarc_dkim_aligned_total Total number of DKIM algined messages.
# TYPE dmarc_dkim_aligned_total counter
# HELP dmarc_dkim_pass_total Total number of messages with raw DKIM pass.
# TYPE dmarc_dkim_pass_total counter
```

I waited few minutes without much success (poll_interval_seconds = 10 in config)

Thanks for your support
That still leaves unclear what caused [the previous failure](https://github.com/jgosmann/dmarc-metrics-exporter/issues/17#issuecomment-1005875730). :(

It seems that the email got processed, but for some reason the DMARC report data was not extracted. Possible reasons for that:
* Failure to extract the report from the email. But this should log a warning (`Failed to extract report from email ...`).
* The report has been processed before. The reports's organization name and report ID will be stored in `seen-reports.db` under the "storage path" for `deduplication_max_seconds` (7 days by default). You could try either removing `seen-reports.db` or setting `deduplication_max_seconds` to `0` and then reprocess the report (just move the email from `Archive/Aggregate` back to the inbox).
* The DMARC report does not contain any records. Should be easily checked by looking at the report XML. It should contain some `<record>` tags.
I checked manually the DMARC report from Google, it has few records (xml in ZIP).
I tried deleting the storage_path, so not an issue on this side.

Actually this is still the same issue:
```
ERROR:dmarc_metrics_exporter.imap_queue:Error during IMAP queue polling.
Traceback (most recent call last):
  File "/home/user/dev/dmarc-metrics-exporter/dmarc_metrics_exporter/imap_queue.py", line 61, in _consume
    async for uid, msg in client.fetch(1, msg_count):
  File "/home/user/dev/dmarc-metrics-exporter/dmarc_metrics_exporter/imap_queue.py", line 124, in fetch
    uid = next(value for key, value in parsed_line[2] if key == "UID")
  File "/home/user/.local/lib/python3.9/site-packages/pyparsing/results.py", line 193, in __getitem__
    return self._toklist[i]
IndexError: list index out of range
```

I had to remove logging config from config file and I added `logging.basicConfig(level=logging.DEBUG)` for the message to appear.

Idea: would you mind adding a --debug argument that sets root logger to level DEBUG, that would allow to show all debug messages more easily.

I'll keep you informed

EDIT: parsed_line is empty, which is not expected
Adding DEBUG messages to the IMAP parser may help to troubleshoot

EDIT2: Why not using built-in parser [email.message_from_bytes](https://docs.python.org/3/library/email.parser.html)?
> Idea: would you mind adding a --debug argument that sets root logger to level DEBUG, that would allow to show all debug messages more easily.

I'll consider it.

> EDIT: parsed_line is empty, which is not expected

Yes, but it is not clear to my why that happens. I think this would require that the parsing of the IMAP response fails, but the output you posted looks fine to me. At least as long as `{9958}` matches the actual `MESSAGE_SOURCE` length ... (but why wouldn't it?)

I would like to enforce that the full response must be parsed, but the `FETCH completed.` is included in it which isn't standardized in the RFC. The tag with the result `NGNJ6 OK` is, but that's already removed by aiomaplib ...

> Adding DEBUG messages to the IMAP parser may help to troubleshoot

Yes, changing [this line](https://github.com/jgosmann/dmarc-metrics-exporter/blob/main/dmarc_metrics_exporter/imap_parser.py#L108) to
```python
fetch_response = Group(fetch_response_line[...]).set_debug(True)
```
should do the trick for now. Not sure how useful the output actually is, but maybe worth a try.

> EDIT2: Why not using built-in parser email.message_from_bytes?

[This is what I am essentially using to parse the email itself.](https://github.com/jgosmann/dmarc-metrics-exporter/blob/main/dmarc_metrics_exporter/imap_queue.py#L126-L130) However, the problem is one step before that: parsing the IMAP response containing the email. Unfortunately, IMAP has some annoying complexities and the aiomaplib is still fairly low-level ... 
Unfortunately I cannot provide a full example as it contains sensitive data.
However I'll have a look of the issue.

Some time ago I wrote a [small script](https://github.com/The-Kube-Way/imap2imap/blob/main/imap2imap.py#L309) to copy emails between IMAP servers.
I didn't have any problems retrieving the messages, it may be useful for you.
Note: I used built-in imaplib, not aiomaplib.

Fix patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index c731e68..dfad59c 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,27 +2,27 @@
 # See https://pre-commit.com/hooks.html for more hooks
 repos:
 - repo: https://github.com/pre-commit/pre-commit-hooks
-  rev: v4.0.1
+  rev: v4.1.0
   hooks:
   - id: trailing-whitespace
   - id: end-of-file-fixer
   - id: check-yaml
   - id: check-added-large-files
 - repo: https://github.com/pre-commit/mirrors-isort
-  rev: v5.9.3
+  rev: v5.10.1
   hooks:
   - id: isort
 - repo: https://github.com/psf/black
-  rev: 21.10b0
+  rev: 21.12b0
   hooks:
   - id: black
 - repo: https://github.com/PyCQA/pylint
-  rev: pylint-2.6.0
+  rev: v2.12.2
   hooks:
   - id: pylint
     additional_dependencies:
-    - aioimaplib==0.9.0
     - aiohttp==3.7.3
+    - bite-parser==0.1.1
     - dataclasses-serialization==1.3.1
     - prometheus_client==0.11
     - pytest==6.2.1
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index b88bb27..91b5793 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -15,6 +15,7 @@ Added
 * Possibility to configure log output with ``logging`` key in the configuration
   file. `See logging.config documentation for details.
   <https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema>`_
+* `--debug` flag to quickly enable debug log output.
 
 
 Fixed
diff --git a/LICENSE b/LICENSE
index 8da10b0..bb9c09e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2020 Jan Gosmann
+Copyright (c) 2020-2021 Jan Gosmann
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.rst b/README.rst
index a463b69..18a9406 100644
--- a/README.rst
+++ b/README.rst
@@ -101,6 +101,8 @@ The following configuration options are available:
   * ``port`` (number, default ``993``): Port of the IMAP server to connect to.
   * ``username`` (string, required): Login username for the IMAP connection.
   * ``password``: (string, required): Login password for the IMAP connection.
+  * ``use_ssl``: (boolean, default ``true``): Whether to use SSL encryption for the connection. Disabling this will transmit the password in clear text! Currently, there is no support for STARTTLS.
+  * ``verify_certificate``: (boolean, default ``true``): Whether to verify the server's SSL certificate. You might have to set this to ``false`` if you are using a self-signed certificate. If this is disabled, someone else could impersonate the server and obtain the login data.
 
 * ``folders`` (object):
 
@@ -112,7 +114,7 @@ The following configuration options are available:
   Directory to persist data in that has to persisted between restarts.
 * ``poll_interval_seconds`` (number, default ``60``): How often to poll the IMAP server in seconds.
 * ``deduplication_max_seconds`` (number, default ``604800`` which is 7 days): How long individual report IDs will be remembered to avoid counting double delivered reports twice.
-* ``logging`` (object, default ``{ "version": 1, "loggers": { "uvicorn": { "level": "INFO" } } }``): Logging configuration. `See logging.config documentation. <https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema>`_
+* ``logging`` (object, default ``{ "version": 1, "disable_existing_loggers": false }``): Logging configuration. `See logging.config documentation. <https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema>`_
 
 Usage
 -----
@@ -130,6 +132,13 @@ To use a different configuration file:
 
     sudo -u dmarc-metrics python3 -m dmarc_metrics_exporter --configuration <path>
 
+You can enable debug logging with the `--debug`
+if you do not want to provide your own logging configuration:
+
+.. code-block:: bash
+
+    sudo -u dmarc-metrics python3 -m dmarc_metrics_exporter --debug
+
 
 systemd
 ^^^^^^^
diff --git a/config/dmarc-metrics-exporter.sample.json b/config/dmarc-metrics-exporter.sample.json
index 9bb50c7..2d873ad 100644
--- a/config/dmarc-metrics-exporter.sample.json
+++ b/config/dmarc-metrics-exporter.sample.json
@@ -5,7 +5,9 @@
         "host": "localhost",
         "port": 993,
         "username": "dmarc@your-server.com",
-        "password": "supersecret"
+        "password": "supersecret",
+        "use_ssl": true,
+        "verify_certificate": true
     },
     "folders": {
         "inbox": "INBOX",
diff --git a/dmarc_metrics_exporter/app.py b/dmarc_metrics_exporter/app.py
index f4d6966..8400e44 100644
--- a/dmarc_metrics_exporter/app.py
+++ b/dmarc_metrics_exporter/app.py
@@ -31,18 +31,24 @@ def main(argv: Sequence[str]):
         default="/etc/dmarc-metrics-exporter.json",
         help="Configuration file",
     )
+    parser.add_argument(
+        "--debug",
+        default=False,
+        action="store_true",
+        help="Enable debug logging",
+    )
     args = parser.parse_args(argv)
 
     configuration = json.load(args.configuration)
     args.configuration.close()
 
-    logging.basicConfig()
+    logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
     logging.config.dictConfig(
         configuration.get(
             "logging",
             dict(
                 version=1,
-                loggers={"uvicorn": {"level": "INFO"}},
+                disable_existing_loggers=False,
             ),
         )
     )
diff --git a/dmarc_metrics_exporter/imap_client.py b/dmarc_metrics_exporter/imap_client.py
new file mode 100644
index 0000000..16a6830
--- /dev/null
+++ b/dmarc_metrics_exporter/imap_client.py
@@ -0,0 +1,379 @@
+import asyncio
+import itertools
+import logging
+import ssl
+import time
+from asyncio import (
+    Event,
+    Lock,
+    Queue,
+    StreamReader,
+    StreamWriter,
+    create_task,
+    open_connection,
+    wait_for,
+)
+from dataclasses import dataclass
+from enum import Enum
+from typing import Callable, Coroutine, Dict, FrozenSet, Optional, Union
+
+from bite import parse_incremental
+from typing_extensions import Literal
+
+from .imap_parser import response as response_grammar
+
+logger = logging.getLogger(__name__)
+
+
+@dataclass
+class ConnectionConfig:
+    username: str
+    password: str
+    host: str = "localhost"
+    port: int = 993
+    use_ssl: bool = True
+    verify_certificate: bool = True
+
+    def create_ssl_context(self) -> Union[Literal[False], ssl.SSLContext]:
+        if self.use_ssl:
+            ssl_context = ssl.create_default_context()
+            if not self.verify_certificate:
+                ssl_context.check_hostname = False
+                ssl_context.verify_mode = ssl.CERT_NONE
+            return ssl_context
+        else:
+            return False
+
+
+class ImapError(Exception):
+    pass
+
+
+class IncompleteResponse(ImapError):
+    pass
+
+
+class ResponseType(Enum):
+    CONTINUE_REQ = "+"
+    UNTAGGED = "*"
+    TAGGED = "tagged"
+
+
+class _ImapTag:
+    state: Optional[bytes]
+    text: Optional[bytes]
+
+    def __init__(self, name: bytes):
+        self.name = name
+        self._response_received = Event()
+        self.state = None
+        self.text = None
+
+    async def wait_response(self):
+        await self._response_received.wait()
+
+    def set_response(self, state: bytes, text: bytes):
+        logger.debug("IMAP command '%s' completed with '%s %s'", self.name, state, text)
+        self.state = state
+        self.text = text
+        self._response_received.set()
+
+
+class _ImapCommandWriter:
+    def __init__(self, writer: StreamWriter, server_ready: Event, timeout_seconds: int):
+        self.writer = writer
+        self._server_ready = server_ready
+        self.timeout_seconds = timeout_seconds
+
+    async def _drain(self):
+        await asyncio.wait_for(self.writer.drain(), timeout=self.timeout_seconds)
+
+    async def write_raw(self, buf: bytes):
+        self.writer.write(buf)
+        await self._drain()
+
+    async def write_int(self, num: int):
+        self.writer.write(str(num).encode("ascii"))
+        await self._drain()
+
+    async def write_string_literal(self, string: str):
+        encoded = string.encode("utf-8")
+        self._server_ready.clear()
+        self.writer.write(b"{")
+        self.writer.write(str(len(encoded)).encode("ascii"))
+        self.writer.write(b"}\r\n")
+        await self._drain()
+        await self._server_ready.wait()
+
+        self.writer.write(encoded)
+        await self._drain()
+
+
+class _CommandsInUse:
+    def __init__(self):
+        self._in_use = set()
+        self._change_condition = asyncio.Condition()
+
+    async def acquire(self, name: str):
+        async with self._change_condition:
+            await self._change_condition.wait_for(lambda: name not in self._in_use)
+            self._in_use.add(name)
+
+    async def release(self, name: str):
+        async with self._change_condition:
+            self._in_use.remove(name)
+            self._change_condition.notify_all()
+
+
+# pylint: disable=too-many-instance-attributes
+class ImapClient:
+    num_exists: Optional[int]
+    fetched_queue: Queue
+    _capabilities: FrozenSet[str]
+    _tag_completions: Dict[bytes, _ImapTag]
+
+    def __init__(self, connection: ConnectionConfig, timeout_seconds: int = 10):
+        self.connection = connection
+        self.timeout_seconds = timeout_seconds
+        self.num_exists = None
+        self.fetched_queue = Queue()
+        self._last_response = time.time()
+        self._capabilities = frozenset()
+        self._ongoing_commands = _CommandsInUse()
+        self._command_lock = Lock()
+        self._server_ready = Event()
+        self._process_responses_task = None
+        self._writer = None
+        self._tag_gen = (f"a{i}".encode("ascii") for i in itertools.count())
+        self._tag_completions = {}
+
+    async def __aenter__(self):
+        reader, self._writer = await open_connection(
+            self.connection.host,
+            self.connection.port,
+            ssl=self.connection.create_ssl_context(),
+        )
+        self._process_responses_task = create_task(self._process_responses(reader))
+
+        try:
+            await wait_for(self._server_ready.wait(), self.timeout_seconds)
+            logger.debug("IMAP server ready.")
+            await self._capability()
+            await self._login(self.connection.username, self.connection.password)
+        except:
+            self._process_responses_task.cancel()
+            raise
+        return self
+
+    async def __aexit__(self, exc_type, exc, traceback):
+        if not self._writer.is_closing():
+            await wait_for(self._logout(), self.timeout_seconds)
+            self._writer.close()
+            await self._writer.wait_closed()
+        await self._process_responses_task
+        self._server_ready.clear()
+
+    async def _process_responses(self, reader: StreamReader):
+        logger.debug("foo")
+        try:
+            async for parse_tree in parse_incremental(response_grammar, reader):
+                response = parse_tree.values
+                logger.debug("IMAP response: %s", response)
+
+                self._last_response = time.time()
+
+                if response[0] == b"+":
+                    self._server_ready.set()
+                elif response[0] == b"*":
+                    if response[1] == b"OK":
+                        self._server_ready.set()
+                    elif response[1] == b"CAPABILITY":
+                        logger.debug(
+                            "IMAP server reports capabilities: %s", response[2]
+                        )
+                        self._capabilities = frozenset(
+                            c.strip().upper()
+                            for c in response[2].decode("utf-8").split(" ")
+                        )
+                    elif len(response) >= 3 and response[2] == b"EXISTS":
+                        self.num_exists = response[1]
+                    elif len(response) >= 3 and response[2] == b"EXPUNGE":
+                        if self.num_exists is not None:
+                            self.num_exists -= 1
+                    elif len(response) >= 3 and response[2] == b"FETCH":
+                        await self.fetched_queue.put(response[1:])
+                    else:
+                        logger.debug("Ignored untagged IMAP response: %s", response[1])
+                else:
+                    tag_name, state, text = response[0:3]
+                    self._tag_completions[tag_name].set_response(state, text)
+            logger.debug("End of response stream.")
+        except Exception:  # pylint: disable=broad-except
+            logger.exception("Error while processing server responses.")
+
+    async def _command(
+        self, name: str, write_command: Callable[[_ImapCommandWriter], Coroutine]
+    ):
+        assert self._writer
+        tag = _ImapTag(next(self._tag_gen))
+        self._tag_completions[tag.name] = tag
+        wait_response = asyncio.ensure_future(tag.wait_response())
+        try:
+            await self._ongoing_commands.acquire(name)
+
+            async with self._command_lock:
+                self._writer.write(tag.name)
+                self._writer.write(b" ")
+                cmd_writer = _ImapCommandWriter(
+                    self._writer, self._server_ready, self.timeout_seconds
+                )
+                _, pending = await asyncio.wait(
+                    [write_command(cmd_writer), wait_response],
+                    return_when=asyncio.FIRST_COMPLETED,
+                )
+
+            while not wait_response.done():
+                _, pending = await asyncio.wait(
+                    [wait_response], timeout=self.timeout_seconds
+                )
+                if (
+                    not wait_response.done()
+                    and self.timeout_seconds < time.time() - self._last_response
+                ):
+                    for future in pending:
+                        future.cancel()
+                    raise asyncio.TimeoutError("Waiting for response timed out.")
+
+            if tag.state and not tag.state.upper() == b"OK":
+                raise ImapServerError(name, tag.state, tag.text)
+        finally:
+            del self._tag_completions[tag.name]
+            if not wait_response.done():
+                wait_response.cancel()
+            await self._ongoing_commands.release(name)
+
+    async def _login(self, username: str, password: str):
+        async def login_writer(cmd_writer: _ImapCommandWriter):
+            await cmd_writer.write_raw(b"LOGIN ")
+            await cmd_writer.write_string_literal(username)
+            await cmd_writer.write_raw(b" ")
+            await cmd_writer.write_string_literal(password)
+            await cmd_writer.write_raw(b"\r\n")
+
+        await self._command("LOGIN", login_writer)
+
+    async def _logout(self):
+        async def logout_writer(cmd_writer: _ImapCommandWriter):
+            await cmd_writer.write_raw(b"LOGOUT\r\n")
+
+        await self._command("LOGOUT", logout_writer)
+
+    async def _capability(self):
+        async def capability_writer(cmd_writer: _ImapCommandWriter):
+            await cmd_writer.write_raw(b"CAPABILITY\r\n")
+
+        await self._command("CAPABILITY", capability_writer)
+
+    def has_capability(self, capability: str) -> bool:
+        return capability.upper() in self._capabilities
+
+    async def select(self, mailbox: str = "INBOX") -> Optional[int]:
+        async def select_writer(cmd_writer: _ImapCommandWriter):
+            await cmd_writer.write_raw(b"SELECT ")
+            await cmd_writer.write_string_literal(mailbox)
+            await cmd_writer.write_raw(b"\r\n")
+
+        await self._command("SELECT", select_writer)
+        return self.num_exists
+
+    async def fetch(self, sequence_set: bytes, attrs: bytes):
+        async def fetch_writer(cmd_writer: _ImapCommandWriter):
+            await cmd_writer.write_raw(b"FETCH ")
+            await cmd_writer.write_raw(sequence_set)
+            await cmd_writer.write_raw(b" ")
+            await cmd_writer.write_raw(attrs)
+            await cmd_writer.write_raw(b"\r\n")
+
+        await self._command("FETCH", fetch_writer)
+
+    async def create(self, name: str):
+        async def create_writer(cmd_writer: _ImapCommandWriter):
+            await cmd_writer.write_raw(b"CREATE ")
+            await cmd_writer.write_string_literal(name)
+            await cmd_writer.write_raw(b"\r\n")
+
+        await self._command("CREATE", create_writer)
+
+    async def create_if_not_exists(self, name: str):
+        try:
+            await self.select(name)
+        except ImapServerError:
+            await self.create(name)
+
+    async def delete(self, name: str):
+        async def create_writer(cmd_writer: _ImapCommandWriter):
+            await cmd_writer.write_raw(b"DELETE ")
+            await cmd_writer.write_string_literal(name)
+            await cmd_writer.write_raw(b"\r\n")
+
+        await self._command("DELETE", create_writer)
+
+    async def uid_copy(self, uid: int, destination: str):
+        async def uid_copy_writer(cmd_writer: _ImapCommandWriter):
+            await cmd_writer.write_raw(b"UID COPY ")
+            await cmd_writer.write_int(uid)
+            await cmd_writer.write_raw(b" ")
+            await cmd_writer.write_string_literal(destination)
+            await cmd_writer.write_raw(b"\r\n")
+
+        await self._command("UID COPY", uid_copy_writer)
+
+    async def uid_move(self, uid: int, destination: str):
+        async def uid_move_writer(cmd_writer: _ImapCommandWriter):
+            await cmd_writer.write_raw(b"UID MOVE ")
+            await cmd_writer.write_int(uid)
+            await cmd_writer.write_raw(b" ")
+            await cmd_writer.write_string_literal(destination)
+            await cmd_writer.write_raw(b"\r\n")
+
+        await self._command("UID MOVE", uid_move_writer)
+
+    async def uid_move_graceful(self, uid: int, destination: str):
+        if self.has_capability("MOVE"):
+            await self.uid_move(uid, destination)
+        else:
+            await self.uid_copy(uid, destination)
+            await self.uid_store(uid, rb"+FLAGS.SILENT (\Deleted)")
+            await self.expunge()
+
+    async def uid_store(self, uid: int, flags: bytes):
+        async def uid_store_writer(cmd_writer: _ImapCommandWriter):
+            await cmd_writer.write_raw(b"UID STORE ")
+            await cmd_writer.write_int(uid)
+            await cmd_writer.write_raw(b" ")
+            await cmd_writer.write_raw(flags)
+            await cmd_writer.write_raw(b"\r\n")
+
+        await self._command("STORE", uid_store_writer)
+
+    async def expunge(self):
+        async def expunge_writer(cmd_writer: _ImapCommandWriter):
+            await cmd_writer.write_raw(b"EXPUNGE\r\n")
+
+        await self._command("EXPUNGE", expunge_writer)
+
+
+class ImapServerError(ImapError):
+    """Error class for errors reported from the server."""
+
+    def __init__(self, command, result, server_response):
+        self.command = command
+        self.result = result
+        self.server_response = server_response
+        super().__init__(command, result, server_response)
+
+    def __str__(self):
+        return (
+            f"IMAP error: Command {self.command} returned {self.result} "
+            f"with response data: {self.server_response}"
+        )
diff --git a/dmarc_metrics_exporter/imap_parser.py b/dmarc_metrics_exporter/imap_parser.py
index f3c6b17..559a914 100644
--- a/dmarc_metrics_exporter/imap_parser.py
+++ b/dmarc_metrics_exporter/imap_parser.py
@@ -1,42 +1,61 @@
-import re
-
-from pyparsing import (
-    CaselessKeyword,
+from bite import (
+    And,
+    CaselessLiteral,
+    CharacterSet,
     Combine,
-    Group,
+    Counted,
+    FixedByteCount,
     Literal,
     Opt,
-    Regex,
-    common,
-    dbl_quoted_string,
-    remove_quotes,
+    Parser,
+    Suppress,
+    TransformValues,
 )
-from pyparsing.core import ParserElement
-from pyparsing.helpers import counted_array, nested_expr
+from bite.core import Forward
+from bite.transformers import Group
 
-nil = CaselessKeyword("NIL")
-atom_char = Regex(r'[^(){ %*"\\\]\x00-\x1f\x7f-\x9f]')
-resp_specials = Literal("]")
+nil = CaselessLiteral(b"NIL")
+atom_char = CharacterSet(
+    rb'(){ %*"\]' + bytes(range(0x1F + 1)) + bytes(range(0x7F, 0x9F + 1)), invert=True
+)
+atom = Combine(atom_char[1, ...])
+resp_specials = Literal(b"]")
 astring_char = atom_char | resp_specials
 
-literal_string = Combine(
-    counted_array(
-        Regex(".", re.DOTALL).leave_whitespace(),
-        Literal("{").suppress() + common.integer + Literal("}\r\n").suppress(),
-    )
+sp = Suppress(CharacterSet(b" \t")[1, ...])
+crlf = Suppress(Literal(b"\r\n"))
+
+integer = TransformValues(
+    Combine(CharacterSet(b"0123456789")[1, ...]),
+    lambda values: tuple(int(v) for v in values),
+)
+literal_string = Counted(
+    Suppress(Literal(b"{")) + integer + Suppress(Literal(b"}") + crlf),
+    FixedByteCount,
 )
-string = dbl_quoted_string.set_parse_action(remove_quotes) | literal_string
+dbl_quoted_string = (
+    Suppress(Literal(b'"'))
+    + Combine(CharacterSet(b'"', invert=True)[0, ...])
+    + Suppress(Literal(b'"'))
+)
+string = dbl_quoted_string | literal_string
 
-astring = astring_char[1, ...] | string
+astring = Combine(astring_char[1, ...]) | string
 nstring = string | nil
 
 
-def parenthesized_list(items_expr: ParserElement) -> ParserElement:
-    return Literal("(").suppress() + Group(items_expr[...]) + Literal(")").suppress()
+def parenthesized_list(items_expr: Parser) -> Parser:
+    return Group(
+        Suppress(Literal(b"("))
+        + Opt(sp)
+        + Opt(items_expr + (sp + items_expr)[0, ...])
+        + Opt(sp)
+        + Suppress(Literal(b")"))
+    )
 
 
-def pair(keyword_expr: ParserElement, expr: ParserElement) -> ParserElement:
-    return Group(keyword_expr.set_results_name("key") + expr.set_results_name("value"))
+def pair(keyword_expr: Parser, expr: Parser) -> Parser:
+    return Group(And([keyword_expr, sp, expr]))
 
 
 address = parenthesized_list(nstring)
@@ -45,49 +64,56 @@ def pair(keyword_expr: ParserElement, expr: ParserElement) -> ParserElement:
 header_list = parenthesized_list(header_field_name)
 
 section_msgtext = (
-    CaselessKeyword("HEADER")
-    | (CaselessKeyword("HEADER.FIELDS") + Opt(CaselessKeyword(".NOT")) + header_list)
-    | CaselessKeyword("TEXT")
+    (
+        CaselessLiteral(b"HEADER.FIELDS")
+        + Opt(CaselessLiteral(b".NOT"))
+        + Opt(sp)
+        + header_list
+    )
+    | CaselessLiteral(b"HEADER")
+    | CaselessLiteral(b"TEXT")
 )
-section_text = section_msgtext | CaselessKeyword("MIME")
-section_part = common.integer + (Literal(".").suppress() + common.integer)[...]
+section_text = section_msgtext | CaselessLiteral(b"MIME")
+section_part = integer + (Suppress(Literal(b".")) + integer)[0, ...]
 section_spec = section_msgtext | (
-    Group(section_part + Opt(Literal(".").suppress() + section_text))
+    section_part + Opt(Suppress(Literal(b".")) + section_text)
 )
-section = (
-    Literal("[").suppress()
-    + Opt(section_spec, default="").set_results_name("section")
-    + Literal("]").suppress()
+section = Suppress(Literal(b"[")) + Group(Opt(section_spec)) + Suppress(Literal(b"]"))
+
+nested_lists = Forward()
+nested_lists.assign(
+    parenthesized_list(
+        nstring | nested_lists | Combine(CharacterSet(b" )", invert=True)[1, ...])
+    )
 )
 
-body_structure = pair(
-    CaselessKeyword("BODY") | CaselessKeyword("BODYSTRUCTURE"),
-    nested_expr(ignore_expr=string),
+
+body_structure = Group(
+    (CaselessLiteral(b"BODYSTRUCTURE") | CaselessLiteral(b"BODY"))
+    + sp
+    + Group(Suppress(nested_lists))
 )
 body_section = pair(
-    Group(
-        CaselessKeyword("BODY")
-        + section
-        + Opt(
-            Literal("<").suppress() + common.integer + Literal(">").suppress()
-        ).set_results_name("offset")
-    ),
+    CaselessLiteral(b"BODY")
+    + section
+    + Opt(Suppress(Literal(b"<")) + integer + Suppress(Literal(b">"))),
     nstring,
 )
 envelope = pair(
-    CaselessKeyword("ENVELOPE"),
+    CaselessLiteral(b"ENVELOPE"),
     parenthesized_list(parenthesized_list(address) | nstring),
 )
-flag = Combine(Literal("\\") + atom_char[1, ...])
-flags = pair(CaselessKeyword("FLAGS"), parenthesized_list(flag))
-internaldate = pair(
-    CaselessKeyword("INTERNALDATE"), dbl_quoted_string.set_parse_action(remove_quotes)
+flag = Combine(Literal(b"\\") + atom)
+flags = pair(CaselessLiteral(b"FLAGS"), parenthesized_list(flag))
+internaldate = pair(CaselessLiteral(b"INTERNALDATE"), dbl_quoted_string)
+rfc822 = pair(
+    CaselessLiteral(b"RFC822"),
+    nstring,
 )
-rfc822 = pair(CaselessKeyword("RFC822"), nstring)
-rfc822_header = pair(CaselessKeyword("RFC822.HEADER"), nstring)
-rfc822_text = pair(CaselessKeyword("RFC822.TEXT"), nstring)
-rfc822_size = pair(CaselessKeyword("RFC822.SIZE"), common.integer)
-uid = pair(CaselessKeyword("UID"), common.integer)
+rfc822_header = pair(CaselessLiteral(b"RFC822.HEADER"), nstring)
+rfc822_text = pair(CaselessLiteral(b"RFC822.TEXT"), nstring)
+rfc822_size = pair(CaselessLiteral(b"RFC822.SIZE"), integer)
+uid = pair(CaselessLiteral(b"UID"), integer)
 fetch_response_pair = (
     body_section
     | body_structure
@@ -102,7 +128,72 @@ def pair(keyword_expr: ParserElement, expr: ParserElement) -> ParserElement:
 )
 
 fetch_response_line = (
-    common.integer + CaselessKeyword("FETCH") + parenthesized_list(fetch_response_pair)
+    integer
+    + sp
+    + CaselessLiteral(b"FETCH")
+    + Opt(sp)
+    + parenthesized_list(fetch_response_pair)
+)
+
+text = Combine(CharacterSet(b"\r\n", invert=True)[0, ...])
+flag_perm = flag | Literal(rb"\*")
+auth_type = atom
+capability = (CaselessLiteral(b"AUTH=") + auth_type) | atom
+capability_data = (
+    CaselessLiteral(b"CAPABILITY")
+    + capability[0, ...]
+    + CaselessLiteral(b"IMAP4rev1")
+    + capability[0, ...]
+)
+response_text_code = (
+    CaselessLiteral(b"ALERT")
+    | Group(CaselessLiteral(b"BADCHARSET") + Opt(sp) + Opt(parenthesized_list(astring)))
+    | capability_data
+    | CaselessLiteral(b"PARSE")
+    | Group(
+        CaselessLiteral(b"PERMANENTFLAGS") + Opt(sp) + parenthesized_list(flag_perm)
+    )
+    | CaselessLiteral(b"READ-ONLY")
+    | CaselessLiteral(b"READ-WRITE")
+    | CaselessLiteral(b"TRYCREATE")
+    | Group(CaselessLiteral(b"UIDNEXT") + sp + integer)
+    | Group(CaselessLiteral(b"UIDVALIDITY") + sp + integer)
+    | Group(CaselessLiteral(b"UNSEEN") + sp + integer)
+    | Group(atom + Opt(sp + Combine(CharacterSet(b"\r\r]", invert=True)[1, ...])))
+)
+resp_text = Opt(
+    Suppress(Literal(b"["))
+    + response_text_code
+    + Suppress(Literal(b"]"))
+    + Suppress(Opt(sp))
+) + (~Literal(b"[") + text)
+resp_cond_state = (
+    CaselessLiteral(b"OK") | CaselessLiteral(b"NO") | CaselessLiteral(b"BAD")
 )
+tag = ~Literal(b"+") + Combine(astring_char[1, ...])
+response_tagged = tag + sp + resp_cond_state + sp + resp_text
+
+server_greeting = Literal(b"OK") + sp + text
+server_goodbye = Literal(b"BYE") + sp + text
 
-fetch_response = Group(fetch_response_line[...])
+capability = CaselessLiteral(b"CAPABILITY") + sp + text
+response_untagged = (
+    Literal(b"*")
+    + sp
+    + (
+        server_goodbye
+        | capability
+        | (integer + sp + Literal(b"EXISTS"))
+        | (integer + sp + Literal(b"EXPUNGE"))
+        | fetch_response_line
+        | server_greeting
+        | (
+            (
+                Combine(CharacterSet(b"{\r\n", invert=True)[1, ...])
+                + Opt(literal_string)
+            )[0, ...]
+        )
+    )
+)
+response_continue = Literal(b"+") + sp + text
+response = (response_continue | response_untagged | response_tagged) + crlf
diff --git a/dmarc_metrics_exporter/imap_queue.py b/dmarc_metrics_exporter/imap_queue.py
index a9259b6..a5ddbf8 100755
--- a/dmarc_metrics_exporter/imap_queue.py
+++ b/dmarc_metrics_exporter/imap_queue.py
@@ -1,28 +1,19 @@
 import asyncio
+import contextlib
 import email.policy
 import logging
-import re
 from asyncio.tasks import Task
 from dataclasses import astuple, dataclass
 from email.message import EmailMessage
 from email.parser import BytesParser
-from typing import Any, AsyncGenerator, Awaitable, Callable, Optional, Tuple, cast
+from typing import Any, Awaitable, Callable, Optional, Tuple, cast
+from urllib.parse import ParseResult
 
-from aioimaplib import aioimaplib
-
-from .imap_parser import fetch_response
+from dmarc_metrics_exporter.imap_client import ConnectionConfig, ImapClient
 
 logger = logging.getLogger(__name__)
 
 
-@dataclass
-class ConnectionConfig:
-    username: str
-    password: str
-    host: str = "localhost"
-    port: int = 993
-
-
 @dataclass
 class QueueFolders:
     inbox: str = "INBOX"
@@ -43,142 +34,80 @@ def __init__(
         self.folders = folders
         self.poll_interval_seconds = poll_interval_seconds
         self.timeout_seconds = timeout_seconds
-        self._stop = False
-        self._consumer: Optional[Task[Any]] = None
+        self._client = ImapClient(connection, timeout_seconds)
+        self._stop = asyncio.Event()
+        self._poll_task: Optional[Task[Any]] = None
 
     def consume(self, handler: Callable[[Any], Awaitable[None]]):
-        self._consumer = asyncio.create_task(self._consume(handler))
+        self._poll_task = asyncio.create_task(self._poll_imap(handler))
 
-    async def _consume(self, handler: Callable[[Any], Awaitable[None]]):
-        while not self._stop:
+    async def _poll_imap(self, handler: Callable[[Any], Awaitable[None]]):
+        while not self._stop.is_set():
+            logger.debug("Polling IMAP ...")
             try:
                 async with ImapClient(self.connection, self.timeout_seconds) as client:
                     for folder in astuple(self.folders):
                         await client.create_if_not_exists(folder)
 
                     msg_count = await client.select(self.folders.inbox)
+                    logger.debug("%s messages to fetch.", msg_count)
                     if msg_count > 0:
-                        async for uid, msg in client.fetch(1, msg_count):
-                            try:
-                                await handler(msg)
-                            except Exception:  # pylint: disable=broad-except
-                                logger.exception(
-                                    "Handler for message in IMAP queue failed."
-                                )
-                                await client.uid_move(uid, self.folders.error)
-                            else:
-                                await client.uid_move(uid, self.folders.done)
+                        fetch_task = asyncio.create_task(
+                            client.fetch(
+                                b"1:" + str(msg_count).encode("ascii"), b"(UID RFC822)"
+                            )
+                        )
+                        while not fetch_task.done() or not client.fetched_queue.empty():
+                            uid, msg = self._extract_uid_and_msg(
+                                await client.fetched_queue.get()
+                            )
+                            if uid is not None and msg is not None:
+                                try:
+                                    logger.debug("Processing UID %s", uid)
+                                    await handler(msg)
+                                except Exception:  # pylint: disable=broad-except
+                                    logger.exception(
+                                        "Handler for message in IMAP queue failed."
+                                    )
+                                    await client.uid_move_graceful(
+                                        uid, self.folders.error
+                                    )
+                                else:
+                                    logger.debug("foo")
+                                    await client.uid_move_graceful(
+                                        uid, self.folders.done
+                                    )
+                                    logger.debug("bar")
+                        logger.debug("Processed all messages.")
+                        await fetch_task
             except (asyncio.TimeoutError, Exception):  # pylint: disable=broad-except
                 logger.exception("Error during IMAP queue polling.")
-            await asyncio.sleep(self.poll_interval_seconds)
-
-    async def stop_consumer(self):
-        self._stop = True
-        await self._consumer
-
-
-class ImapClient:
-    def __init__(self, connection: ConnectionConfig, timeout_seconds: int = 10):
-        self.connection = connection
-        self.timeout_seconds = timeout_seconds
-        self._client = aioimaplib.IMAP4_SSL(
-            host=self.connection.host,
-            port=self.connection.port,
-            timeout=timeout_seconds,
-        )
-
-    async def __aenter__(self):
-        await self._client.wait_hello_from_server()
-        await self._check(
-            "LOGIN",
-            self._client.login(self.connection.username, self.connection.password),
-        )
-        return self
-
-    async def __aexit__(self, exc_type, exc, traceback):
-        await self._check("LOGOUT", self._client.logout())
-
-    async def _check(self, command: str, awaitable: Awaitable[Tuple[str, Any]]) -> Any:
-        res, data = await asyncio.wait_for(awaitable, self.timeout_seconds)
-        if res not in ("OK", b"OK"):
-            raise ImapServerError(command, res, data)
-        return data
-
-    async def select(self, folder: str = "INBOX") -> int:
-        """Selects a mailbox and returns the existing mail count."""
-        data = await self._check("SELECT", self._client.select(folder))
-        exists_regex = re.compile(rb"^(\d+) EXISTS$")
-        matches = (exists_regex.match(line) for line in data)
-        msg_count = next(int(m.group(1)) for m in matches if m)
-        return msg_count
-
-    async def fetch(
-        self, first_msg: int, last_msg: int
-    ) -> AsyncGenerator[Tuple[int, EmailMessage], None]:
-        response_lines = await self._check(
-            "FETCH", self._client.fetch(f"{first_msg}:{last_msg}", "(UID RFC822)")
-        )
-        response_string = b"\r\n".join(response_lines).decode("ascii")
-        for parsed_line in fetch_response.parse_string(response_string):
-            try:
-                uid = next(value for key, value in parsed_line[2] if key == "UID")
-                mail = next(value for key, value in parsed_line[2] if key == "RFC822")
-                yield uid, cast(
+            logger.debug(
+                "Going to sleep for %s seconds until next poll.",
+                self.poll_interval_seconds,
+            )
+            with contextlib.suppress(asyncio.TimeoutError):
+                await asyncio.wait_for(self._stop.wait(), self.poll_interval_seconds)
+
+    @classmethod
+    def _extract_uid_and_msg(
+        cls, parsed_response: ParseResult
+    ) -> Tuple[Optional[int], Optional[EmailMessage]]:
+        uid, msg = None, None
+        if parsed_response[1] == b"FETCH":
+            for key, value in cast(Tuple[Any, Any], parsed_response[2]):
+                mail_body = None
+                if key == b"UID":
+                    uid = cast(int, value)
+                elif key == b"RFC822":
+                    mail_body = value
+            if uid and mail_body:
+                msg = cast(
                     EmailMessage,
-                    BytesParser(policy=email.policy.default).parsebytes(
-                        mail.encode("ascii")
-                    ),
-                )
-            except StopIteration:
-                logger.warning(
-                    "FETCH response was missing requested data (UID, RFC822)."
+                    BytesParser(policy=email.policy.default).parsebytes(mail_body),
                 )
+        return uid, msg
 
-    async def create_if_not_exists(self, mailbox_name: str):
-        if (await self._client.select(mailbox_name)).result != "OK":
-            await self._check("CREATE", self._client.create(mailbox_name))
-
-    async def uid_move(self, uid: int, destination: str):
-        if self._client.has_capability("MOVE"):
-            await self._check(
-                "UID MOVE",
-                self._client.uid("move", str(uid), destination),
-            )
-        else:
-            await self._check(
-                "UID COPY",
-                self._client.uid("copy", str(uid), destination),
-            )
-            await self._check(
-                "UID STORE",
-                self._client.uid(
-                    "store",
-                    str(uid),
-                    r"+FLAGS.SILENT (\Deleted)",
-                ),
-            )
-            await self._check("UID EXPUNGE", self._client.uid("expunge", str(uid)))
-
-
-class ImapError(Exception):
-    pass
-
-
-class ImapClientError(ImapError):
-    """Error class for errors encountered on the client side."""
-
-
-class ImapServerError(ImapError):
-    """Error class for errors reported from the server."""
-
-    def __init__(self, command, result, server_response):
-        self.command = command
-        self.result = result
-        self.server_response = server_response
-        super().__init__(command, result, server_response)
-
-    def __str__(self):
-        return (
-            f"IMAP error: Command {self.command} returned {self.result} "
-            f"with response data: {self.server_response}"
-        )
+    async def stop_consumer(self):
+        self._stop.set()
+        await self._poll_task
diff --git a/poetry.lock b/poetry.lock
index 9b245f2..fce050b 100755
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,6 +1,6 @@
 [[package]]
 name = "aiohttp"
-version = "3.8.0"
+version = "3.8.1"
 description = "Async http client/server framework (asyncio)"
 category = "dev"
 optional = false
@@ -20,14 +20,6 @@ yarl = ">=1.0,<2.0"
 [package.extras]
 speedups = ["aiodns", "brotli", "cchardet"]
 
-[[package]]
-name = "aioimaplib"
-version = "0.9.0"
-description = "Python asyncio IMAP4rev1 client library"
-category = "main"
-optional = false
-python-versions = "*"
-
 [[package]]
 name = "aiosignal"
 version = "1.2.0"
@@ -41,11 +33,11 @@ frozenlist = ">=1.1.0"
 
 [[package]]
 name = "asgiref"
-version = "3.4.1"
+version = "3.5.0"
 description = "ASGI specs, helper code, and adapters"
 category = "main"
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
 
 [package.dependencies]
 typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
@@ -55,28 +47,28 @@ tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
 
 [[package]]
 name = "astroid"
-version = "2.8.4"
+version = "2.9.3"
 description = "An abstract syntax tree for Python with inference support."
 category = "dev"
 optional = false
-python-versions = "~=3.6"
+python-versions = ">=3.6.2"
 
 [package.dependencies]
 lazy-object-proxy = ">=1.4.0"
-typed-ast = {version = ">=1.4.0,<1.5", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""}
+typed-ast = {version = ">=1.4.0,<2.0", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""}
 typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""}
 wrapt = ">=1.11,<1.14"
 
 [[package]]
 name = "async-timeout"
-version = "4.0.0"
+version = "4.0.2"
 description = "Timeout context manager for asyncio programs"
 category = "dev"
 optional = false
 python-versions = ">=3.6"
 
 [package.dependencies]
-typing-extensions = ">=3.6.5"
+typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""}
 
 [[package]]
 name = "asynctest"
@@ -96,21 +88,29 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 
 [[package]]
 name = "attrs"
-version = "21.2.0"
+version = "21.4.0"
 description = "Classes Without Boilerplate"
 category = "dev"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
 
 [package.extras]
-dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
+dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
 docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
-tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
-tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
+tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
+
+[[package]]
+name = "bite-parser"
+version = "0.1.1"
+description = "Asynchronous parser taking incremental bites out of your byte input stream."
+category = "main"
+optional = false
+python-versions = ">=3.7,<=3.10"
 
 [[package]]
 name = "black"
-version = "21.10b0"
+version = "21.12b0"
 description = "The uncompromising code formatter."
 category = "dev"
 optional = false
@@ -121,9 +121,8 @@ click = ">=7.1.2"
 mypy-extensions = ">=0.4.3"
 pathspec = ">=0.9.0,<1"
 platformdirs = ">=2"
-regex = ">=2020.1.8"
 tomli = ">=0.2.6,<2.0.0"
-typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\""}
+typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""}
 typing-extensions = ">=3.10.0.0"
 
 [package.extras]
@@ -143,7 +142,7 @@ python-versions = "*"
 
 [[package]]
 name = "charset-normalizer"
-version = "2.0.7"
+version = "2.0.10"
 description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
 category = "dev"
 optional = false
@@ -174,11 +173,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
 
 [[package]]
 name = "coverage"
-version = "6.2"
+version = "6.3"
 description = "Code coverage measurement for Python"
 category = "dev"
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
 
 [package.dependencies]
 tomli = {version = "*", optional = true, markers = "extra == \"toml\""}
@@ -228,20 +227,23 @@ tls = ["pyOpenSSL (>=17.5.0)", "cryptography (>=3.4.7)", "idna (>=2.0.0)"]
 
 [[package]]
 name = "frozenlist"
-version = "1.2.0"
+version = "1.3.0"
 description = "A list-like structure which implements collections.abc.MutableSequence"
 category = "dev"
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
 
 [[package]]
 name = "h11"
-version = "0.12.0"
+version = "0.13.0"
 description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
 category = "main"
 optional = false
 python-versions = ">=3.6"
 
+[package.dependencies]
+typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
+
 [[package]]
 name = "httptools"
 version = "0.2.0"
@@ -263,11 +265,11 @@ python-versions = ">=3.5"
 
 [[package]]
 name = "importlib-metadata"
-version = "4.8.1"
+version = "4.10.1"
 description = "Read metadata from Python packages"
 category = "main"
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
 
 [package.dependencies]
 typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
@@ -276,7 +278,7 @@ zipp = ">=0.5"
 [package.extras]
 docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
 perf = ["ipython"]
-testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
 
 [[package]]
 name = "iniconfig"
@@ -288,7 +290,7 @@ python-versions = "*"
 
 [[package]]
 name = "isort"
-version = "5.10.0"
+version = "5.10.1"
 description = "A Python utility / library to sort Python imports."
 category = "dev"
 optional = false
@@ -302,11 +304,11 @@ plugins = ["setuptools"]
 
 [[package]]
 name = "lazy-object-proxy"
-version = "1.6.0"
+version = "1.7.1"
 description = "A fast and thorough lazy object proxy."
 category = "dev"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+python-versions = ">=3.6"
 
 [[package]]
 name = "mccabe"
@@ -329,11 +331,11 @@ dataclasses = "*"
 
 [[package]]
 name = "multidict"
-version = "5.2.0"
+version = "6.0.1"
 description = "multidict implementation"
 category = "dev"
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
 
 [[package]]
 name = "mypy"
@@ -382,11 +384,11 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
 
 [[package]]
 name = "platformdirs"
-version = "2.4.0"
+version = "2.4.1"
 description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
 category = "dev"
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
 
 [package.extras]
 docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"]
@@ -428,26 +430,26 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
 
 [[package]]
 name = "pylint"
-version = "2.11.1"
+version = "2.12.2"
 description = "python code static checker"
 category = "dev"
 optional = false
-python-versions = "~=3.6"
+python-versions = ">=3.6.2"
 
 [package.dependencies]
-astroid = ">=2.8.0,<2.9"
+astroid = ">=2.9.0,<2.10"
 colorama = {version = "*", markers = "sys_platform == \"win32\""}
 isort = ">=4.2.5,<6"
 mccabe = ">=0.6,<0.7"
 platformdirs = ">=2.2.0"
-toml = ">=0.7.1"
+toml = ">=0.9.2"
 typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
 
 [[package]]
 name = "pyparsing"
-version = "3.0.6"
+version = "3.0.7"
 description = "Python parsing module"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.6"
 
@@ -507,7 +509,7 @@ testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtuale
 
 [[package]]
 name = "python-dotenv"
-version = "0.19.1"
+version = "0.19.2"
 description = "Read key-value pairs from a .env file and set them as environment variables"
 category = "main"
 optional = false
@@ -532,17 +534,9 @@ category = "main"
 optional = false
 python-versions = ">=3.6"
 
-[[package]]
-name = "regex"
-version = "2021.11.2"
-description = "Alternative regular expression module, to replace re."
-category = "dev"
-optional = false
-python-versions = "*"
-
 [[package]]
 name = "requests"
-version = "2.26.0"
+version = "2.27.1"
 description = "Python HTTP for Humans."
 category = "dev"
 optional = false
@@ -568,7 +562,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
 
 [[package]]
 name = "tomli"
-version = "1.2.2"
+version = "1.2.3"
 description = "A lil' TOML parser"
 category = "dev"
 optional = false
@@ -576,7 +570,7 @@ python-versions = ">=3.6"
 
 [[package]]
 name = "toolz"
-version = "0.11.1"
+version = "0.11.2"
 description = "List processing tools and functional utilities"
 category = "main"
 optional = false
@@ -600,19 +594,30 @@ python-versions = "*"
 
 [[package]]
 name = "types-requests"
-version = "2.25.11"
+version = "2.27.7"
 description = "Typing stubs for requests"
 category = "dev"
 optional = false
 python-versions = "*"
 
+[package.dependencies]
+types-urllib3 = "<1.27"
+
+[[package]]
+name = "types-urllib3"
+version = "1.26.7"
+description = "Typing stubs for urllib3"
+category = "dev"
+optional = false
+python-versions = "*"
+
 [[package]]
 name = "typing-extensions"
-version = "3.10.0.2"
-description = "Backported and Experimental Type Hints for Python 3.5+"
+version = "4.0.1"
+description = "Backported and Experimental Type Hints for Python 3.6+"
 category = "main"
 optional = false
-python-versions = "*"
+python-versions = ">=3.6"
 
 [[package]]
 name = "typing-inspect"
@@ -628,7 +633,7 @@ typing-extensions = ">=3.7.4"
 
 [[package]]
 name = "urllib3"
-version = "1.26.7"
+version = "1.26.8"
 description = "HTTP library with thread-safe connection pooling, file post, and more."
 category = "dev"
 optional = false
@@ -686,19 +691,20 @@ python-versions = ">=3.5"
 
 [[package]]
 name = "websocket-client"
-version = "1.2.1"
+version = "1.2.3"
 description = "WebSocket client for Python with low level API options"
 category = "dev"
 optional = false
 python-versions = ">=3.6"
 
 [package.extras]
+docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"]
 optional = ["python-socks", "wsaccel"]
 test = ["websockets"]
 
 [[package]]
 name = "websockets"
-version = "10.0"
+version = "10.1"
 description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
 category = "main"
 optional = false
@@ -714,7 +720,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
 
 [[package]]
 name = "xsdata"
-version = "21.11"
+version = "21.12"
 description = "Python XML Binding"
 category = "main"
 optional = false
@@ -745,116 +751,111 @@ typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""}
 
 [[package]]
 name = "zipp"
-version = "3.6.0"
+version = "3.7.0"
 description = "Backport of pathlib-compatible object wrapper for zip files"
 category = "main"
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
 
 [package.extras]
 docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
-testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
 
 [metadata]
 lock-version = "1.1"
 python-versions = "^3.7,<3.10"
-content-hash = "921ee7e1d687313062ab456ebefc77d28963ccb8c33e16e5a91f43b0062615d5"
+content-hash = "a0d01f8fc773b43cc0af516aabf0b023eeeba2ce993b39d5a8715dedcc91b8c6"
 
 [metadata.files]
 aiohttp = [
-    {file = "aiohttp-3.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:48f218a5257b6bc16bcf26a91d97ecea0c7d29c811a90d965f3dd97c20f016d6"},
-    {file = "aiohttp-3.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2fee4d656a7cc9ab47771b2a9e8fad8a9a33331c1b59c3057ecf0ac858f5bfe"},
-    {file = "aiohttp-3.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:688a1eb8c1a5f7e795c7cb67e0fe600194e6723ba35f138dfae0db20c0cb8f94"},
-    {file = "aiohttp-3.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ba09bb3dcb0b7ec936a485db2b64be44fe14cdce0a5eac56f50e55da3627385"},
-    {file = "aiohttp-3.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7715daf84f10bcebc083ad137e3eced3e1c8e7fa1f096ade9a8d02b08f0d91c"},
-    {file = "aiohttp-3.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e3f81fbbc170418e22918a9585fd7281bbc11d027064d62aa4b507552c92671"},
-    {file = "aiohttp-3.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1fa9f50aa1f114249b7963c98e20dc35c51be64096a85bc92433185f331de9cc"},
-    {file = "aiohttp-3.8.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8a50150419b741ee048b53146c39c47053f060cb9d98e78be08fdbe942eaa3c4"},
-    {file = "aiohttp-3.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a84c335337b676d832c1e2bc47c3a97531b46b82de9f959dafb315cbcbe0dfcd"},
-    {file = "aiohttp-3.8.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:88d4917c30fcd7f6404fb1dc713fa21de59d3063dcc048f4a8a1a90e6bbbd739"},
-    {file = "aiohttp-3.8.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b76669b7c058b8020b11008283c3b8e9c61bfd978807c45862956119b77ece45"},
-    {file = "aiohttp-3.8.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:84fe1732648c1bc303a70faa67cbc2f7f2e810c8a5bca94f6db7818e722e4c0a"},
-    {file = "aiohttp-3.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:730b7c2b7382194d9985ffdc32ab317e893bca21e0665cb1186bdfbb4089d990"},
-    {file = "aiohttp-3.8.0-cp310-cp310-win32.whl", hash = "sha256:0a96473a1f61d7920a9099bc8e729dc8282539d25f79c12573ee0fdb9c8b66a8"},
-    {file = "aiohttp-3.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:764c7c6aa1f78bd77bd9674fc07d1ec44654da1818d0eef9fb48aa8371a3c847"},
-    {file = "aiohttp-3.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9951c2696c4357703001e1fe6edc6ae8e97553ac630492ea1bf64b429cb712a3"},
-    {file = "aiohttp-3.8.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0af379221975054162959e00daf21159ff69a712fc42ed0052caddbd70d52ff4"},
-    {file = "aiohttp-3.8.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9689af0f0a89e5032426c143fa3683b0451f06c83bf3b1e27902bd33acfae769"},
-    {file = "aiohttp-3.8.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe4a327da0c6b6e59f2e474ae79d6ee7745ac3279fd15f200044602fa31e3d79"},
-    {file = "aiohttp-3.8.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ecb314e59bedb77188017f26e6b684b1f6d0465e724c3122a726359fa62ca1ba"},
-    {file = "aiohttp-3.8.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a5399a44a529083951b55521cf4ecbf6ad79fd54b9df57dbf01699ffa0549fc9"},
-    {file = "aiohttp-3.8.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:09754a0d5eaab66c37591f2f8fac8f9781a5f61d51aa852a3261c4805ca6b984"},
-    {file = "aiohttp-3.8.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:adf0cb251b1b842c9dee5cfcdf880ba0aae32e841b8d0e6b6feeaef002a267c5"},
-    {file = "aiohttp-3.8.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:a4759e85a191de58e0ea468ab6fd9c03941986eee436e0518d7a9291fab122c8"},
-    {file = "aiohttp-3.8.0-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:28369fe331a59d80393ec82df3d43307c7461bfaf9217999e33e2acc7984ff7c"},
-    {file = "aiohttp-3.8.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2f44d1b1c740a9e2275160d77c73a11f61e8a916191c572876baa7b282bcc934"},
-    {file = "aiohttp-3.8.0-cp36-cp36m-win32.whl", hash = "sha256:e27cde1e8d17b09730801ce97b6e0c444ba2a1f06348b169fd931b51d3402f0d"},
-    {file = "aiohttp-3.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:15a660d06092b7c92ed17c1dbe6c1eab0a02963992d60e3e8b9d5fa7fa81f01e"},
-    {file = "aiohttp-3.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:257f4fad1714d26d562572095c8c5cd271d5a333252795cb7a002dca41fdbad7"},
-    {file = "aiohttp-3.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6074a3b2fa2d0c9bf0963f8dfc85e1e54a26114cc8594126bc52d3fa061c40e"},
-    {file = "aiohttp-3.8.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a315ceb813208ef32bdd6ec3a85cbe3cb3be9bbda5fd030c234592fa9116993"},
-    {file = "aiohttp-3.8.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a52b141ff3b923a9166595de6e3768a027546e75052ffba267d95b54267f4ab"},
-    {file = "aiohttp-3.8.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6a038cb1e6e55b26bb5520ccffab7f539b3786f5553af2ee47eb2ec5cbd7084e"},
-    {file = "aiohttp-3.8.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98b1ea2763b33559dd9ec621d67fc17b583484cb90735bfb0ec3614c17b210e4"},
-    {file = "aiohttp-3.8.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9e8723c3256641e141cd18f6ce478d54a004138b9f1a36e41083b36d9ecc5fc5"},
-    {file = "aiohttp-3.8.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:14a6f026eca80dfa3d52e86be89feb5cd878f6f4a6adb34457e2c689fd85229b"},
-    {file = "aiohttp-3.8.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c62d4791a8212c885b97a63ef5f3974b2cd41930f0cd224ada9c6ee6654f8150"},
-    {file = "aiohttp-3.8.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:90a97c2ed2830e7974cbe45f0838de0aefc1c123313f7c402e21c29ec063fbb4"},
-    {file = "aiohttp-3.8.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dcc4d5dd5fba3affaf4fd08f00ef156407573de8c63338787614ccc64f96b321"},
-    {file = "aiohttp-3.8.0-cp37-cp37m-win32.whl", hash = "sha256:de42f513ed7a997bc821bddab356b72e55e8396b1b7ba1bf39926d538a76a90f"},
-    {file = "aiohttp-3.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7d76e8a83396e06abe3df569b25bd3fc88bf78b7baa2c8e4cf4aaf5983af66a3"},
-    {file = "aiohttp-3.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5d79174d96446a02664e2bffc95e7b6fa93b9e6d8314536c5840dff130d0878b"},
-    {file = "aiohttp-3.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a6551057a846bf72c7a04f73de3fcaca269c0bd85afe475ceb59d261c6a938c"},
-    {file = "aiohttp-3.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:871d4fdc56288caa58b1094c20f2364215f7400411f76783ea19ad13be7c8e19"},
-    {file = "aiohttp-3.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ba08a71caa42eef64357257878fb17f3fba3fba6e81a51d170e32321569e079"},
-    {file = "aiohttp-3.8.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51f90dabd9933b1621260b32c2f0d05d36923c7a5a909eb823e429dba0fd2f3e"},
-    {file = "aiohttp-3.8.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f348ebd20554e8bc26e8ef3ed8a134110c0f4bf015b3b4da6a4ddf34e0515b19"},
-    {file = "aiohttp-3.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d5f8c04574efa814a24510122810e3a3c77c0552f9f6ff65c9862f1f046be2c3"},
-    {file = "aiohttp-3.8.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5ecffdc748d3b40dd3618ede0170e4f5e1d3c9647cfb410d235d19e62cb54ee0"},
-    {file = "aiohttp-3.8.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:577cc2c7b807b174814dac2d02e673728f2e46c7f90ceda3a70ea4bb6d90b769"},
-    {file = "aiohttp-3.8.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6b79f6c31e68b6dafc0317ec453c83c86dd8db1f8f0c6f28e97186563fca87a0"},
-    {file = "aiohttp-3.8.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:2bdd655732e38b40f8a8344d330cfae3c727fb257585df923316aabbd489ccb8"},
-    {file = "aiohttp-3.8.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:63fa57a0708573d3c059f7b5527617bd0c291e4559298473df238d502e4ab98c"},
-    {file = "aiohttp-3.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d3f90ee275b1d7c942e65b5c44c8fb52d55502a0b9a679837d71be2bd8927661"},
-    {file = "aiohttp-3.8.0-cp38-cp38-win32.whl", hash = "sha256:fa818609357dde5c4a94a64c097c6404ad996b1d38ca977a72834b682830a722"},
-    {file = "aiohttp-3.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:097ecf52f6b9859b025c1e36401f8aa4573552e887d1b91b4b999d68d0b5a3b3"},
-    {file = "aiohttp-3.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:be03a7483ad9ea60388f930160bb3728467dd0af538aa5edc60962ee700a0bdc"},
-    {file = "aiohttp-3.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:78d51e35ed163783d721b6f2ce8ce3f82fccfe471e8e50a10fba13a766d31f5a"},
-    {file = "aiohttp-3.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bda75d73e7400e81077b0910c9a60bf9771f715420d7e35fa7739ae95555f195"},
-    {file = "aiohttp-3.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:707adc30ea6918fba725c3cb3fe782d271ba352b22d7ae54a7f9f2e8a8488c41"},
-    {file = "aiohttp-3.8.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f58aa995b905ab82fe228acd38538e7dc1509e01508dcf307dad5046399130f"},
-    {file = "aiohttp-3.8.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c996eb91bfbdab1e01e2c02e7ff678c51e2b28e3a04e26e41691991cc55795"},
-    {file = "aiohttp-3.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d6a1a66bb8bac9bc2892c2674ea363486bfb748b86504966a390345a11b1680e"},
-    {file = "aiohttp-3.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dafc01a32b4a1d7d3ef8bfd3699406bb44f7b2e0d3eb8906d574846e1019b12f"},
-    {file = "aiohttp-3.8.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:949a605ef3907254b122f845baa0920407080cdb1f73aa64f8d47df4a7f4c4f9"},
-    {file = "aiohttp-3.8.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0d7b056fd3972d353cb4bc305c03f9381583766b7f8c7f1c44478dba69099e33"},
-    {file = "aiohttp-3.8.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f1d39a744101bf4043fa0926b3ead616607578192d0a169974fb5265ab1e9d2"},
-    {file = "aiohttp-3.8.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:67ca7032dfac8d001023fadafc812d9f48bf8a8c3bb15412d9cdcf92267593f4"},
-    {file = "aiohttp-3.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cb751ef712570d3bda9a73fd765ff3e1aba943ec5d52a54a0c2e89c7eef9da1e"},
-    {file = "aiohttp-3.8.0-cp39-cp39-win32.whl", hash = "sha256:6d3e027fe291b77f6be9630114a0200b2c52004ef20b94dc50ca59849cd623b3"},
-    {file = "aiohttp-3.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:3c5e9981e449d54308c6824f172ec8ab63eb9c5f922920970249efee83f7e919"},
-    {file = "aiohttp-3.8.0.tar.gz", hash = "sha256:d3b19d8d183bcfd68b25beebab8dc3308282fe2ca3d6ea3cb4cd101b3c279f8d"},
-]
-aioimaplib = [
-    {file = "aioimaplib-0.9.0-py3-none-any.whl", hash = "sha256:64ac8167f9474647e71bc81933f849b63c7ef4509516fc4986c7fc5fd57aafb7"},
-    {file = "aioimaplib-0.9.0-py3.9.egg", hash = "sha256:91a3c6a9804b8c45f4de14e86cb8e80730a6c9c0206d87e9efd2c27c2e802e97"},
-    {file = "aioimaplib-0.9.0.tar.gz", hash = "sha256:a42335aa153698227afc2478b81ad1fd1605202548adb519348d322a6e5bd445"},
+    {file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1ed0b6477896559f17b9eaeb6d38e07f7f9ffe40b9f0f9627ae8b9926ae260a8"},
+    {file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7dadf3c307b31e0e61689cbf9e06be7a867c563d5a63ce9dca578f956609abf8"},
+    {file = "aiohttp-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a79004bb58748f31ae1cbe9fa891054baaa46fb106c2dc7af9f8e3304dc30316"},
+    {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12de6add4038df8f72fac606dff775791a60f113a725c960f2bab01d8b8e6b15"},
+    {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f0d5f33feb5f69ddd57a4a4bd3d56c719a141080b445cbf18f238973c5c9923"},
+    {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eaba923151d9deea315be1f3e2b31cc39a6d1d2f682f942905951f4e40200922"},
+    {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:099ebd2c37ac74cce10a3527d2b49af80243e2a4fa39e7bce41617fbc35fa3c1"},
+    {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2e5d962cf7e1d426aa0e528a7e198658cdc8aa4fe87f781d039ad75dcd52c516"},
+    {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fa0ffcace9b3aa34d205d8130f7873fcfefcb6a4dd3dd705b0dab69af6712642"},
+    {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61bfc23df345d8c9716d03717c2ed5e27374e0fe6f659ea64edcd27b4b044cf7"},
+    {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:31560d268ff62143e92423ef183680b9829b1b482c011713ae941997921eebc8"},
+    {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:01d7bdb774a9acc838e6b8f1d114f45303841b89b95984cbb7d80ea41172a9e3"},
+    {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97ef77eb6b044134c0b3a96e16abcb05ecce892965a2124c566af0fd60f717e2"},
+    {file = "aiohttp-3.8.1-cp310-cp310-win32.whl", hash = "sha256:c2aef4703f1f2ddc6df17519885dbfa3514929149d3ff900b73f45998f2532fa"},
+    {file = "aiohttp-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:713ac174a629d39b7c6a3aa757b337599798da4c1157114a314e4e391cd28e32"},
+    {file = "aiohttp-3.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:473d93d4450880fe278696549f2e7aed8cd23708c3c1997981464475f32137db"},
+    {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b5eeae8e019e7aad8af8bb314fb908dd2e028b3cdaad87ec05095394cce632"},
+    {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af642b43ce56c24d063325dd2cf20ee012d2b9ba4c3c008755a301aaea720ad"},
+    {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3630c3ef435c0a7c549ba170a0633a56e92629aeed0e707fec832dee313fb7a"},
+    {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4a4a4e30bf1edcad13fb0804300557aedd07a92cabc74382fdd0ba6ca2661091"},
+    {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6f8b01295e26c68b3a1b90efb7a89029110d3a4139270b24fda961893216c440"},
+    {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a25fa703a527158aaf10dafd956f7d42ac6d30ec80e9a70846253dd13e2f067b"},
+    {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5bfde62d1d2641a1f5173b8c8c2d96ceb4854f54a44c23102e2ccc7e02f003ec"},
+    {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:51467000f3647d519272392f484126aa716f747859794ac9924a7aafa86cd411"},
+    {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:03a6d5349c9ee8f79ab3ff3694d6ce1cfc3ced1c9d36200cb8f08ba06bd3b782"},
+    {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:102e487eeb82afac440581e5d7f8f44560b36cf0bdd11abc51a46c1cd88914d4"},
+    {file = "aiohttp-3.8.1-cp36-cp36m-win32.whl", hash = "sha256:4aed991a28ea3ce320dc8ce655875e1e00a11bdd29fe9444dd4f88c30d558602"},
+    {file = "aiohttp-3.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b0e20cddbd676ab8a64c774fefa0ad787cc506afd844de95da56060348021e96"},
+    {file = "aiohttp-3.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:37951ad2f4a6df6506750a23f7cbabad24c73c65f23f72e95897bb2cecbae676"},
+    {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c23b1ad869653bc818e972b7a3a79852d0e494e9ab7e1a701a3decc49c20d51"},
+    {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15b09b06dae900777833fe7fc4b4aa426556ce95847a3e8d7548e2d19e34edb8"},
+    {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:477c3ea0ba410b2b56b7efb072c36fa91b1e6fc331761798fa3f28bb224830dd"},
+    {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2f2f69dca064926e79997f45b2f34e202b320fd3782f17a91941f7eb85502ee2"},
+    {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ef9612483cb35171d51d9173647eed5d0069eaa2ee812793a75373447d487aa4"},
+    {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6d69f36d445c45cda7b3b26afef2fc34ef5ac0cdc75584a87ef307ee3c8c6d00"},
+    {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:55c3d1072704d27401c92339144d199d9de7b52627f724a949fc7d5fc56d8b93"},
+    {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d00268fcb9f66fbcc7cd9fe423741d90c75ee029a1d15c09b22d23253c0a44"},
+    {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:07b05cd3305e8a73112103c834e91cd27ce5b4bd07850c4b4dbd1877d3f45be7"},
+    {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c34dc4958b232ef6188c4318cb7b2c2d80521c9a56c52449f8f93ab7bc2a8a1c"},
+    {file = "aiohttp-3.8.1-cp37-cp37m-win32.whl", hash = "sha256:d2f9b69293c33aaa53d923032fe227feac867f81682f002ce33ffae978f0a9a9"},
+    {file = "aiohttp-3.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6ae828d3a003f03ae31915c31fa684b9890ea44c9c989056fea96e3d12a9fa17"},
+    {file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0c7ebbbde809ff4e970824b2b6cb7e4222be6b95a296e46c03cf050878fc1785"},
+    {file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b7ef7cbd4fec9a1e811a5de813311ed4f7ac7d93e0fda233c9b3e1428f7dd7b"},
+    {file = "aiohttp-3.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c3d6a4d0619e09dcd61021debf7059955c2004fa29f48788a3dfaf9c9901a7cd"},
+    {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:718626a174e7e467f0558954f94af117b7d4695d48eb980146016afa4b580b2e"},
+    {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:589c72667a5febd36f1315aa6e5f56dd4aa4862df295cb51c769d16142ddd7cd"},
+    {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ed076098b171573161eb146afcb9129b5ff63308960aeca4b676d9d3c35e700"},
+    {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:086f92daf51a032d062ec5f58af5ca6a44d082c35299c96376a41cbb33034675"},
+    {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:11691cf4dc5b94236ccc609b70fec991234e7ef8d4c02dd0c9668d1e486f5abf"},
+    {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:31d1e1c0dbf19ebccbfd62eff461518dcb1e307b195e93bba60c965a4dcf1ba0"},
+    {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:11a67c0d562e07067c4e86bffc1553f2cf5b664d6111c894671b2b8712f3aba5"},
+    {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:bb01ba6b0d3f6c68b89fce7305080145d4877ad3acaed424bae4d4ee75faa950"},
+    {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:44db35a9e15d6fe5c40d74952e803b1d96e964f683b5a78c3cc64eb177878155"},
+    {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:844a9b460871ee0a0b0b68a64890dae9c415e513db0f4a7e3cab41a0f2fedf33"},
+    {file = "aiohttp-3.8.1-cp38-cp38-win32.whl", hash = "sha256:7d08744e9bae2ca9c382581f7dce1273fe3c9bae94ff572c3626e8da5b193c6a"},
+    {file = "aiohttp-3.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:04d48b8ce6ab3cf2097b1855e1505181bdd05586ca275f2505514a6e274e8e75"},
+    {file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5315a2eb0239185af1bddb1abf472d877fede3cc8d143c6cddad37678293237"},
+    {file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a996d01ca39b8dfe77440f3cd600825d05841088fd6bc0144cc6c2ec14cc5f74"},
+    {file = "aiohttp-3.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:13487abd2f761d4be7c8ff9080de2671e53fff69711d46de703c310c4c9317ca"},
+    {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea302f34477fda3f85560a06d9ebdc7fa41e82420e892fc50b577e35fc6a50b2"},
+    {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2f635ce61a89c5732537a7896b6319a8fcfa23ba09bec36e1b1ac0ab31270d2"},
+    {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e999f2d0e12eea01caeecb17b653f3713d758f6dcc770417cf29ef08d3931421"},
+    {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0770e2806a30e744b4e21c9d73b7bee18a1cfa3c47991ee2e5a65b887c49d5cf"},
+    {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d15367ce87c8e9e09b0f989bfd72dc641bcd04ba091c68cd305312d00962addd"},
+    {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c7cefb4b0640703eb1069835c02486669312bf2f12b48a748e0a7756d0de33d"},
+    {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:71927042ed6365a09a98a6377501af5c9f0a4d38083652bcd2281a06a5976724"},
+    {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:28d490af82bc6b7ce53ff31337a18a10498303fe66f701ab65ef27e143c3b0ef"},
+    {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b6613280ccedf24354406caf785db748bebbddcf31408b20c0b48cb86af76866"},
+    {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81e3d8c34c623ca4e36c46524a3530e99c0bc95ed068fd6e9b55cb721d408fb2"},
+    {file = "aiohttp-3.8.1-cp39-cp39-win32.whl", hash = "sha256:7187a76598bdb895af0adbd2fb7474d7f6025d170bc0a1130242da817ce9e7d1"},
+    {file = "aiohttp-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c182cb873bc91b411e184dab7a2b664d4fea2743df0e4d57402f7f3fa644bac"},
+    {file = "aiohttp-3.8.1.tar.gz", hash = "sha256:fc5471e1a54de15ef71c1bc6ebe80d4dc681ea600e68bfd1cbce40427f0b7578"},
 ]
 aiosignal = [
     {file = "aiosignal-1.2.0-py3-none-any.whl", hash = "sha256:26e62109036cd181df6e6ad646f91f0dcfd05fe16d0cb924138ff2ab75d64e3a"},
     {file = "aiosignal-1.2.0.tar.gz", hash = "sha256:78ed67db6c7b7ced4f98e495e572106d5c432a93e1ddd1bf475e1dc05f5b7df2"},
 ]
 asgiref = [
-    {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
-    {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"},
+    {file = "asgiref-3.5.0-py3-none-any.whl", hash = "sha256:88d59c13d634dcffe0510be048210188edd79aeccb6a6c9028cdad6f31d730a9"},
+    {file = "asgiref-3.5.0.tar.gz", hash = "sha256:2f8abc20f7248433085eda803936d98992f1343ddb022065779f37c5da0181d0"},
 ]
 astroid = [
-    {file = "astroid-2.8.4-py3-none-any.whl", hash = "sha256:0755c998e7117078dcb7d0bda621391dd2a85da48052d948c7411ab187325346"},
-    {file = "astroid-2.8.4.tar.gz", hash = "sha256:1e83a69fd51b013ebf5912d26b9338d6643a55fec2f20c787792680610eed4a2"},
+    {file = "astroid-2.9.3-py3-none-any.whl", hash = "sha256:506daabe5edffb7e696ad82483ad0228245a9742ed7d2d8c9cdb31537decf9f6"},
+    {file = "astroid-2.9.3.tar.gz", hash = "sha256:1efdf4e867d4d8ba4a9f6cf9ce07cd182c4c41de77f23814feb27ca93ca9d877"},
 ]
 async-timeout = [
-    {file = "async-timeout-4.0.0.tar.gz", hash = "sha256:7d87a4e8adba8ededb52e579ce6bc8276985888913620c935094c2276fd83382"},
-    {file = "async_timeout-4.0.0-py3-none-any.whl", hash = "sha256:f3303dddf6cafa748a92747ab6c2ecf60e0aeca769aee4c151adfce243a05d9b"},
+    {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
+    {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
 ]
 asynctest = [
     {file = "asynctest-0.13.0-py3-none-any.whl", hash = "sha256:5da6118a7e6d6b54d83a8f7197769d046922a44d2a99c21382f0a6e4fadae676"},
@@ -865,20 +866,24 @@ atomicwrites = [
     {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
 ]
 attrs = [
-    {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
-    {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
+    {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
+    {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
+]
+bite-parser = [
+    {file = "bite-parser-0.1.1.tar.gz", hash = "sha256:8021100bfbd6cc6056605361e763a3591efdea38014b3d8aa76c74c74de4ead4"},
+    {file = "bite_parser-0.1.1-py3-none-any.whl", hash = "sha256:96803e7d63112e81544c9747fbd50cca61097dcf5973ae6cc710f3a96646600a"},
 ]
 black = [
-    {file = "black-21.10b0-py3-none-any.whl", hash = "sha256:6eb7448da9143ee65b856a5f3676b7dda98ad9abe0f87fce8c59291f15e82a5b"},
-    {file = "black-21.10b0.tar.gz", hash = "sha256:a9952229092e325fe5f3dae56d81f639b23f7131eb840781947e4b2886030f33"},
+    {file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"},
+    {file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"},
 ]
 certifi = [
     {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
     {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
 ]
 charset-normalizer = [
-    {file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"},
-    {file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"},
+    {file = "charset-normalizer-2.0.10.tar.gz", hash = "sha256:876d180e9d7432c5d1dfd4c5d26b72f099d503e8fcc0feb7532c9289be60fcbd"},
+    {file = "charset_normalizer-2.0.10-py3-none-any.whl", hash = "sha256:cb957888737fc0bbcd78e3df769addb41fd1ff8cf950dc9e7ad7793f1bf44455"},
 ]
 click = [
     {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"},
@@ -889,53 +894,50 @@ colorama = [
     {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
 ]
 coverage = [
-    {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"},
-    {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"},
-    {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"},
-    {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"},
-    {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"},
-    {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"},
-    {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"},
-    {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"},
-    {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"},
-    {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"},
-    {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"},
-    {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"},
-    {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"},
-    {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"},
-    {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"},
-    {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"},
-    {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"},
-    {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"},
-    {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"},
-    {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"},
-    {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"},
-    {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"},
-    {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"},
-    {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"},
-    {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"},
-    {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"},
-    {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"},
-    {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"},
-    {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"},
-    {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"},
-    {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"},
-    {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"},
-    {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"},
-    {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"},
-    {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"},
-    {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"},
-    {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"},
-    {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"},
-    {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"},
-    {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"},
-    {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"},
-    {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"},
-    {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"},
-    {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"},
-    {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"},
-    {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"},
-    {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"},
+    {file = "coverage-6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e8071e7d9ba9f457fc674afc3de054450be2c9b195c470147fbbc082468d8ff7"},
+    {file = "coverage-6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86c91c511853dfda81c2cf2360502cb72783f4b7cebabef27869f00cbe1db07d"},
+    {file = "coverage-6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4ce3b647bd1792d4394f5690d9df6dc035b00bcdbc5595099c01282a59ae01"},
+    {file = "coverage-6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a491e159294d756e7fc8462f98175e2d2225e4dbe062cca7d3e0d5a75ba6260"},
+    {file = "coverage-6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d008e0f67ac800b0ca04d7914b8501312c8c6c00ad8c7ba17754609fae1231a"},
+    {file = "coverage-6.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4578728c36de2801c1deb1c6b760d31883e62e33f33c7ba8f982e609dc95167d"},
+    {file = "coverage-6.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7ee317486593193e066fc5e98ac0ce712178c21529a85c07b7cb978171f25d53"},
+    {file = "coverage-6.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2bc85664b06ba42d14bb74d6ddf19d8bfc520cb660561d2d9ce5786ae72f71b5"},
+    {file = "coverage-6.3-cp310-cp310-win32.whl", hash = "sha256:27a94db5dc098c25048b0aca155f5fac674f2cf1b1736c5272ba28ead2fc267e"},
+    {file = "coverage-6.3-cp310-cp310-win_amd64.whl", hash = "sha256:bde4aeabc0d1b2e52c4036c54440b1ad05beeca8113f47aceb4998bb7471e2c2"},
+    {file = "coverage-6.3-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:509c68c3e2015022aeda03b003dd68fa19987cdcf64e9d4edc98db41cfc45d30"},
+    {file = "coverage-6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e4ff163602c5c77e7bb4ea81ba5d3b793b4419f8acd296aae149370902cf4e92"},
+    {file = "coverage-6.3-cp311-cp311-win_amd64.whl", hash = "sha256:d1675db48490e5fa0b300f6329ecb8a9a37c29b9ab64fa9c964d34111788ca2d"},
+    {file = "coverage-6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7eed8459a2b81848cafb3280b39d7d49950d5f98e403677941c752e7e7ee47cb"},
+    {file = "coverage-6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b4285fde5286b946835a1a53bba3ad41ef74285ba9e8013e14b5ea93deaeafc"},
+    {file = "coverage-6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4748349734110fd32d46ff8897b561e6300d8989a494ad5a0a2e4f0ca974fc7"},
+    {file = "coverage-6.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:823f9325283dc9565ba0aa2d240471a93ca8999861779b2b6c7aded45b58ee0f"},
+    {file = "coverage-6.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fff16a30fdf57b214778eff86391301c4509e327a65b877862f7c929f10a4253"},
+    {file = "coverage-6.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:da1a428bdbe71f9a8c270c7baab29e9552ac9d0e0cba5e7e9a4c9ee6465d258d"},
+    {file = "coverage-6.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7d82c610a2e10372e128023c5baf9ce3d270f3029fe7274ff5bc2897c68f1318"},
+    {file = "coverage-6.3-cp37-cp37m-win32.whl", hash = "sha256:11e61c5548ecf74ea1f8b059730b049871f0e32b74f88bd0d670c20c819ad749"},
+    {file = "coverage-6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8e0c3525b1a182c8ffc9bca7e56b521e0c2b8b3e82f033c8e16d6d721f1b54d6"},
+    {file = "coverage-6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a189036c50dcd56100746139a459f0d27540fef95b09aba03e786540b8feaa5f"},
+    {file = "coverage-6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32168001f33025fd756884d56d01adebb34e6c8c0b3395ca8584cdcee9c7c9d2"},
+    {file = "coverage-6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5d79c9af3f410a2b5acad91258b4ae179ee9c83897eb9de69151b179b0227f5"},
+    {file = "coverage-6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:85c5fc9029043cf8b07f73fbb0a7ab6d3b717510c3b5642b77058ea55d7cacde"},
+    {file = "coverage-6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7596aa2f2b8fa5604129cfc9a27ad9beec0a96f18078cb424d029fdd707468d"},
+    {file = "coverage-6.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ce443a3e6df90d692c38762f108fc4c88314bf477689f04de76b3f252e7a351c"},
+    {file = "coverage-6.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:012157499ec4f135fc36cd2177e3d1a1840af9b236cbe80e9a5ccfc83d912a69"},
+    {file = "coverage-6.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a34d313105cdd0d3644c56df2d743fe467270d6ab93b5d4a347eb9fec8924d6"},
+    {file = "coverage-6.3-cp38-cp38-win32.whl", hash = "sha256:6e78b1e25e5c5695dea012be473e442f7094d066925604be20b30713dbd47f89"},
+    {file = "coverage-6.3-cp38-cp38-win_amd64.whl", hash = "sha256:433b99f7b0613bdcdc0b00cc3d39ed6d756797e3b078d2c43f8a38288520aec6"},
+    {file = "coverage-6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9ed3244b415725f08ca3bdf02ed681089fd95e9465099a21c8e2d9c5d6ca2606"},
+    {file = "coverage-6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab4fc4b866b279740e0d917402f0e9a08683e002f43fa408e9655818ed392196"},
+    {file = "coverage-6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8582e9280f8d0f38114fe95a92ae8d0790b56b099d728cc4f8a2e14b1c4a18c"},
+    {file = "coverage-6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c72bb4679283c6737f452eeb9b2a0e570acaef2197ad255fb20162adc80bea76"},
+    {file = "coverage-6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca29c352389ea27a24c79acd117abdd8a865c6eb01576b6f0990cd9a4e9c9f48"},
+    {file = "coverage-6.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:152cc2624381df4e4e604e21bd8e95eb8059535f7b768c1fb8b8ae0b26f47ab0"},
+    {file = "coverage-6.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:51372e24b1f7143ee2df6b45cff6a721f3abe93b1e506196f3ffa4155c2497f7"},
+    {file = "coverage-6.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72d9d186508325a456475dd05b1756f9a204c7086b07fffb227ef8cee03b1dc2"},
+    {file = "coverage-6.3-cp39-cp39-win32.whl", hash = "sha256:649df3641eb351cdfd0d5533c92fc9df507b6b2bf48a7ef8c71ab63cbc7b5c3c"},
+    {file = "coverage-6.3-cp39-cp39-win_amd64.whl", hash = "sha256:e67ccd53da5958ea1ec833a160b96357f90859c220a00150de011b787c27b98d"},
+    {file = "coverage-6.3-pp36.pp37.pp38-none-any.whl", hash = "sha256:27ac7cb84538e278e07569ceaaa6f807a029dc194b1c819a9820b9bb5dbf63ab"},
+    {file = "coverage-6.3.tar.gz", hash = "sha256:987a84ff98a309994ca77ed3cc4b92424f824278e48e4bf7d1bb79a63cfe2099"},
 ]
 dataclasses = [
     {file = "dataclasses-0.6-py3-none-any.whl", hash = "sha256:454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f"},
@@ -950,82 +952,69 @@ docker = [
     {file = "docker-5.0.3.tar.gz", hash = "sha256:d916a26b62970e7c2f554110ed6af04c7ccff8e9f81ad17d0d40c75637e227fb"},
 ]
 frozenlist = [
-    {file = "frozenlist-1.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:977a1438d0e0d96573fd679d291a1542097ea9f4918a8b6494b06610dfeefbf9"},
-    {file = "frozenlist-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a8d86547a5e98d9edd47c432f7a14b0c5592624b496ae9880fb6332f34af1edc"},
-    {file = "frozenlist-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:181754275d5d32487431a0a29add4f897968b7157204bc1eaaf0a0ce80c5ba7d"},
-    {file = "frozenlist-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5df31bb2b974f379d230a25943d9bf0d3bc666b4b0807394b131a28fca2b0e5f"},
-    {file = "frozenlist-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4766632cd8a68e4f10f156a12c9acd7b1609941525569dd3636d859d79279ed3"},
-    {file = "frozenlist-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16eef427c51cb1203a7c0ab59d1b8abccaba9a4f58c4bfca6ed278fc896dc193"},
-    {file = "frozenlist-1.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:01d79515ed5aa3d699b05f6bdcf1fe9087d61d6b53882aa599a10853f0479c6c"},
-    {file = "frozenlist-1.2.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:28e164722ea0df0cf6d48c4d5bdf3d19e87aaa6dfb39b0ba91153f224b912020"},
-    {file = "frozenlist-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e63ad0beef6ece06475d29f47d1f2f29727805376e09850ebf64f90777962792"},
-    {file = "frozenlist-1.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:41de4db9b9501679cf7cddc16d07ac0f10ef7eb58c525a1c8cbff43022bddca4"},
-    {file = "frozenlist-1.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c6a9d84ee6427b65a81fc24e6ef589cb794009f5ca4150151251c062773e7ed2"},
-    {file = "frozenlist-1.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:f5f3b2942c3b8b9bfe76b408bbaba3d3bb305ee3693e8b1d631fe0a0d4f93673"},
-    {file = "frozenlist-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c98d3c04701773ad60d9545cd96df94d955329efc7743fdb96422c4b669c633b"},
-    {file = "frozenlist-1.2.0-cp310-cp310-win32.whl", hash = "sha256:72cfbeab7a920ea9e74b19aa0afe3b4ad9c89471e3badc985d08756efa9b813b"},
-    {file = "frozenlist-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:11ff401951b5ac8c0701a804f503d72c048173208490c54ebb8d7bb7c07a6d00"},
-    {file = "frozenlist-1.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b46f997d5ed6d222a863b02cdc9c299101ee27974d9bbb2fd1b3c8441311c408"},
-    {file = "frozenlist-1.2.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:351686ca020d1bcd238596b1fa5c8efcbc21bffda9d0efe237aaa60348421e2a"},
-    {file = "frozenlist-1.2.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfbaa08cf1452acad9cb1c1d7b89394a41e712f88df522cea1a0f296b57782a0"},
-    {file = "frozenlist-1.2.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2ae2f5e9fa10805fb1c9adbfefaaecedd9e31849434be462c3960a0139ed729"},
-    {file = "frozenlist-1.2.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6790b8d96bbb74b7a6f4594b6f131bd23056c25f2aa5d816bd177d95245a30e3"},
-    {file = "frozenlist-1.2.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:41f62468af1bd4e4b42b5508a3fe8cc46a693f0cdd0ca2f443f51f207893d837"},
-    {file = "frozenlist-1.2.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:ec6cf345771cdb00791d271af9a0a6fbfc2b6dd44cb753f1eeaa256e21622adb"},
-    {file = "frozenlist-1.2.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:14a5cef795ae3e28fb504b73e797c1800e9249f950e1c964bb6bdc8d77871161"},
-    {file = "frozenlist-1.2.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:8b54cdd2fda15467b9b0bfa78cee2ddf6dbb4585ef23a16e14926f4b076dfae4"},
-    {file = "frozenlist-1.2.0-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:f025f1d6825725b09c0038775acab9ae94264453a696cc797ce20c0769a7b367"},
-    {file = "frozenlist-1.2.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:84e97f59211b5b9083a2e7a45abf91cfb441369e8bb6d1f5287382c1c526def3"},
-    {file = "frozenlist-1.2.0-cp36-cp36m-win32.whl", hash = "sha256:c5328ed53fdb0a73c8a50105306a3bc013e5ca36cca714ec4f7bd31d38d8a97f"},
-    {file = "frozenlist-1.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:9ade70aea559ca98f4b1b1e5650c45678052e76a8ab2f76d90f2ac64180215a2"},
-    {file = "frozenlist-1.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0d3ffa8772464441b52489b985d46001e2853a3b082c655ec5fad9fb6a3d618"},
-    {file = "frozenlist-1.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3457f8cf86deb6ce1ba67e120f1b0128fcba1332a180722756597253c465fc1d"},
-    {file = "frozenlist-1.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a72eecf37eface331636951249d878750db84034927c997d47f7f78a573b72b"},
-    {file = "frozenlist-1.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:acc4614e8d1feb9f46dd829a8e771b8f5c4b1051365d02efb27a3229048ade8a"},
-    {file = "frozenlist-1.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:87521e32e18a2223311afc2492ef2d99946337da0779ddcda77b82ee7319df59"},
-    {file = "frozenlist-1.2.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8b4c7665a17c3a5430edb663e4ad4e1ad457614d1b2f2b7f87052e2ef4fa45ca"},
-    {file = "frozenlist-1.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ed58803563a8c87cf4c0771366cf0ad1aa265b6b0ae54cbbb53013480c7ad74d"},
-    {file = "frozenlist-1.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:aa44c4740b4e23fcfa259e9dd52315d2b1770064cde9507457e4c4a65a04c397"},
-    {file = "frozenlist-1.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:2de5b931701257d50771a032bba4e448ff958076380b049fd36ed8738fdb375b"},
-    {file = "frozenlist-1.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:6e105013fa84623c057a4381dc8ea0361f4d682c11f3816cc80f49a1f3bc17c6"},
-    {file = "frozenlist-1.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:705c184b77565955a99dc360f359e8249580c6b7eaa4dc0227caa861ef46b27a"},
-    {file = "frozenlist-1.2.0-cp37-cp37m-win32.whl", hash = "sha256:a37594ad6356e50073fe4f60aa4187b97d15329f2138124d252a5a19c8553ea4"},
-    {file = "frozenlist-1.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:25b358aaa7dba5891b05968dd539f5856d69f522b6de0bf34e61f133e077c1a4"},
-    {file = "frozenlist-1.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af2a51c8a381d76eabb76f228f565ed4c3701441ecec101dd18be70ebd483cfd"},
-    {file = "frozenlist-1.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:82d22f6e6f2916e837c91c860140ef9947e31194c82aaeda843d6551cec92f19"},
-    {file = "frozenlist-1.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cfe6fef507f8bac40f009c85c7eddfed88c1c0d38c75e72fe10476cef94e10f"},
-    {file = "frozenlist-1.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f602e380a5132880fa245c92030abb0fc6ff34e0c5500600366cedc6adb06a"},
-    {file = "frozenlist-1.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ad065b2ebd09f32511ff2be35c5dfafee6192978b5a1e9d279a5c6e121e3b03"},
-    {file = "frozenlist-1.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bc93f5f62df3bdc1f677066327fc81f92b83644852a31c6aa9b32c2dde86ea7d"},
-    {file = "frozenlist-1.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:89fdfc84c6bf0bff2ff3170bb34ecba8a6911b260d318d377171429c4be18c73"},
-    {file = "frozenlist-1.2.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:47b2848e464883d0bbdcd9493c67443e5e695a84694efff0476f9059b4cb6257"},
-    {file = "frozenlist-1.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4f52d0732e56906f8ddea4bd856192984650282424049c956857fed43697ea43"},
-    {file = "frozenlist-1.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:16ef7dd5b7d17495404a2e7a49bac1bc13d6d20c16d11f4133c757dd94c4144c"},
-    {file = "frozenlist-1.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1cf63243bc5f5c19762943b0aa9e0d3fb3723d0c514d820a18a9b9a5ef864315"},
-    {file = "frozenlist-1.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:54a1e09ab7a69f843cd28fefd2bcaf23edb9e3a8d7680032c8968b8ac934587d"},
-    {file = "frozenlist-1.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:954b154a4533ef28bd3e83ffdf4eadf39deeda9e38fb8feaf066d6069885e034"},
-    {file = "frozenlist-1.2.0-cp38-cp38-win32.whl", hash = "sha256:cb3957c39668d10e2b486acc85f94153520a23263b6401e8f59422ef65b9520d"},
-    {file = "frozenlist-1.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0a7c7cce70e41bc13d7d50f0e5dd175f14a4f1837a8549b0936ed0cbe6170bf9"},
-    {file = "frozenlist-1.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4c457220468d734e3077580a3642b7f682f5fd9507f17ddf1029452450912cdc"},
-    {file = "frozenlist-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e74f8b4d8677ebb4015ac01fcaf05f34e8a1f22775db1f304f497f2f88fdc697"},
-    {file = "frozenlist-1.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fbd4844ff111449f3bbe20ba24fbb906b5b1c2384d0f3287c9f7da2354ce6d23"},
-    {file = "frozenlist-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0081a623c886197ff8de9e635528fd7e6a387dccef432149e25c13946cb0cd0"},
-    {file = "frozenlist-1.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9b6e21e5770df2dea06cb7b6323fbc008b13c4a4e3b52cb54685276479ee7676"},
-    {file = "frozenlist-1.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:406aeb340613b4b559db78d86864485f68919b7141dec82aba24d1477fd2976f"},
-    {file = "frozenlist-1.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:878ebe074839d649a1cdb03a61077d05760624f36d196884a5cafb12290e187b"},
-    {file = "frozenlist-1.2.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1fef737fd1388f9b93bba8808c5f63058113c10f4e3c0763ced68431773f72f9"},
-    {file = "frozenlist-1.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4a495c3d513573b0b3f935bfa887a85d9ae09f0627cf47cad17d0cc9b9ba5c38"},
-    {file = "frozenlist-1.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e7d0dd3e727c70c2680f5f09a0775525229809f1a35d8552b92ff10b2b14f2c2"},
-    {file = "frozenlist-1.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:66a518731a21a55b7d3e087b430f1956a36793acc15912e2878431c7aec54210"},
-    {file = "frozenlist-1.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:94728f97ddf603d23c8c3dd5cae2644fa12d33116e69f49b1644a71bb77b89ae"},
-    {file = "frozenlist-1.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c1e8e9033d34c2c9e186e58279879d78c94dd365068a3607af33f2bc99357a53"},
-    {file = "frozenlist-1.2.0-cp39-cp39-win32.whl", hash = "sha256:83334e84a290a158c0c4cc4d22e8c7cfe0bba5b76d37f1c2509dabd22acafe15"},
-    {file = "frozenlist-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:735f386ec522e384f511614c01d2ef9cf799f051353876b4c6fb93ef67a6d1ee"},
-    {file = "frozenlist-1.2.0.tar.gz", hash = "sha256:68201be60ac56aff972dc18085800b6ee07973c49103a8aba669dee3d71079de"},
+    {file = "frozenlist-1.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2257aaba9660f78c7b1d8fea963b68f3feffb1a9d5d05a18401ca9eb3e8d0a3"},
+    {file = "frozenlist-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4a44ebbf601d7bac77976d429e9bdb5a4614f9f4027777f9e54fd765196e9d3b"},
+    {file = "frozenlist-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:45334234ec30fc4ea677f43171b18a27505bfb2dba9aca4398a62692c0ea8868"},
+    {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47be22dc27ed933d55ee55845d34a3e4e9f6fee93039e7f8ebadb0c2f60d403f"},
+    {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03a7dd1bfce30216a3f51a84e6dd0e4a573d23ca50f0346634916ff105ba6e6b"},
+    {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:691ddf6dc50480ce49f68441f1d16a4c3325887453837036e0fb94736eae1e58"},
+    {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bde99812f237f79eaf3f04ebffd74f6718bbd216101b35ac7955c2d47c17da02"},
+    {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a202458d1298ced3768f5a7d44301e7c86defac162ace0ab7434c2e961166e8"},
+    {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9e3e9e365991f8cc5f5edc1fd65b58b41d0514a6a7ad95ef5c7f34eb49b3d3e"},
+    {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:04cb491c4b1c051734d41ea2552fde292f5f3a9c911363f74f39c23659c4af78"},
+    {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:436496321dad302b8b27ca955364a439ed1f0999311c393dccb243e451ff66aa"},
+    {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:754728d65f1acc61e0f4df784456106e35afb7bf39cfe37227ab00436fb38676"},
+    {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6eb275c6385dd72594758cbe96c07cdb9bd6becf84235f4a594bdf21e3596c9d"},
+    {file = "frozenlist-1.3.0-cp310-cp310-win32.whl", hash = "sha256:e30b2f9683812eb30cf3f0a8e9f79f8d590a7999f731cf39f9105a7c4a39489d"},
+    {file = "frozenlist-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f7353ba3367473d1d616ee727945f439e027f0bb16ac1a750219a8344d1d5d3c"},
+    {file = "frozenlist-1.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88aafd445a233dbbf8a65a62bc3249a0acd0d81ab18f6feb461cc5a938610d24"},
+    {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4406cfabef8f07b3b3af0f50f70938ec06d9f0fc26cbdeaab431cbc3ca3caeaa"},
+    {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cf829bd2e2956066dd4de43fd8ec881d87842a06708c035b37ef632930505a2"},
+    {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:603b9091bd70fae7be28bdb8aa5c9990f4241aa33abb673390a7f7329296695f"},
+    {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25af28b560e0c76fa41f550eacb389905633e7ac02d6eb3c09017fa1c8cdfde1"},
+    {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c7a8a9fc9383b52c410a2ec952521906d355d18fccc927fca52ab575ee8b93"},
+    {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:65bc6e2fece04e2145ab6e3c47428d1bbc05aede61ae365b2c1bddd94906e478"},
+    {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3f7c935c7b58b0d78c0beea0c7358e165f95f1fd8a7e98baa40d22a05b4a8141"},
+    {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd89acd1b8bb4f31b47072615d72e7f53a948d302b7c1d1455e42622de180eae"},
+    {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:6983a31698490825171be44ffbafeaa930ddf590d3f051e397143a5045513b01"},
+    {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:adac9700675cf99e3615eb6a0eb5e9f5a4143c7d42c05cea2e7f71c27a3d0846"},
+    {file = "frozenlist-1.3.0-cp37-cp37m-win32.whl", hash = "sha256:0c36e78b9509e97042ef869c0e1e6ef6429e55817c12d78245eb915e1cca7468"},
+    {file = "frozenlist-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:57f4d3f03a18facacb2a6bcd21bccd011e3b75d463dc49f838fd699d074fabd1"},
+    {file = "frozenlist-1.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8c905a5186d77111f02144fab5b849ab524f1e876a1e75205cd1386a9be4b00a"},
+    {file = "frozenlist-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b5009062d78a8c6890d50b4e53b0ddda31841b3935c1937e2ed8c1bda1c7fb9d"},
+    {file = "frozenlist-1.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2fdc3cd845e5a1f71a0c3518528bfdbfe2efaf9886d6f49eacc5ee4fd9a10953"},
+    {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92e650bd09b5dda929523b9f8e7f99b24deac61240ecc1a32aeba487afcd970f"},
+    {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40dff8962b8eba91fd3848d857203f0bd704b5f1fa2b3fc9af64901a190bba08"},
+    {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:768efd082074bb203c934e83a61654ed4931ef02412c2fbdecea0cff7ecd0274"},
+    {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:006d3595e7d4108a12025ddf415ae0f6c9e736e726a5db0183326fd191b14c5e"},
+    {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:871d42623ae15eb0b0e9df65baeee6976b2e161d0ba93155411d58ff27483ad8"},
+    {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aff388be97ef2677ae185e72dc500d19ecaf31b698986800d3fc4f399a5e30a5"},
+    {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9f892d6a94ec5c7b785e548e42722e6f3a52f5f32a8461e82ac3e67a3bd073f1"},
+    {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:e982878792c971cbd60ee510c4ee5bf089a8246226dea1f2138aa0bb67aff148"},
+    {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c6c321dd013e8fc20735b92cb4892c115f5cdb82c817b1e5b07f6b95d952b2f0"},
+    {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:30530930410855c451bea83f7b272fb1c495ed9d5cc72895ac29e91279401db3"},
+    {file = "frozenlist-1.3.0-cp38-cp38-win32.whl", hash = "sha256:40ec383bc194accba825fbb7d0ef3dda5736ceab2375462f1d8672d9f6b68d07"},
+    {file = "frozenlist-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:f20baa05eaa2bcd5404c445ec51aed1c268d62600362dc6cfe04fae34a424bd9"},
+    {file = "frozenlist-1.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0437fe763fb5d4adad1756050cbf855bbb2bf0d9385c7bb13d7a10b0dd550486"},
+    {file = "frozenlist-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b684c68077b84522b5c7eafc1dc735bfa5b341fb011d5552ebe0968e22ed641c"},
+    {file = "frozenlist-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93641a51f89473837333b2f8100f3f89795295b858cd4c7d4a1f18e299dc0a4f"},
+    {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6d32ff213aef0fd0bcf803bffe15cfa2d4fde237d1d4838e62aec242a8362fa"},
+    {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31977f84828b5bb856ca1eb07bf7e3a34f33a5cddce981d880240ba06639b94d"},
+    {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c62964192a1c0c30b49f403495911298810bada64e4f03249ca35a33ca0417a"},
+    {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4eda49bea3602812518765810af732229b4291d2695ed24a0a20e098c45a707b"},
+    {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acb267b09a509c1df5a4ca04140da96016f40d2ed183cdc356d237286c971b51"},
+    {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e1e26ac0a253a2907d654a37e390904426d5ae5483150ce3adedb35c8c06614a"},
+    {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f96293d6f982c58ebebb428c50163d010c2f05de0cde99fd681bfdc18d4b2dc2"},
+    {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e84cb61b0ac40a0c3e0e8b79c575161c5300d1d89e13c0e02f76193982f066ed"},
+    {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:ff9310f05b9d9c5c4dd472983dc956901ee6cb2c3ec1ab116ecdde25f3ce4951"},
+    {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d26b650b71fdc88065b7a21f8ace70175bcf3b5bdba5ea22df4bfd893e795a3b"},
+    {file = "frozenlist-1.3.0-cp39-cp39-win32.whl", hash = "sha256:01a73627448b1f2145bddb6e6c2259988bb8aee0fb361776ff8604b99616cd08"},
+    {file = "frozenlist-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:772965f773757a6026dea111a15e6e2678fbd6216180f82a48a40b27de1ee2ab"},
+    {file = "frozenlist-1.3.0.tar.gz", hash = "sha256:ce6f2ba0edb7b0c1d8976565298ad2deba6f8064d2bebb6ffce2ca896eb35b0b"},
 ]
 h11 = [
-    {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
-    {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
+    {file = "h11-0.13.0-py3-none-any.whl", hash = "sha256:8ddd78563b633ca55346c8cd41ec0af27d3c79931828beffb46ce70a379e7442"},
+    {file = "h11-0.13.0.tar.gz", hash = "sha256:70813c1135087a248a4d38cc0e1a0181ffab2188141a93eaf567940c3957ff06"},
 ]
 httptools = [
     {file = "httptools-0.2.0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:79dbc21f3612a78b28384e989b21872e2e3cf3968532601544696e4ed0007ce5"},
@@ -1049,40 +1038,55 @@ idna = [
     {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
 ]
 importlib-metadata = [
-    {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"},
-    {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"},
+    {file = "importlib_metadata-4.10.1-py3-none-any.whl", hash = "sha256:899e2a40a8c4a1aec681feef45733de8a6c58f3f6a0dbed2eb6574b4387a77b6"},
+    {file = "importlib_metadata-4.10.1.tar.gz", hash = "sha256:951f0d8a5b7260e9db5e41d429285b5f451e928479f19d80818878527d36e95e"},
 ]
 iniconfig = [
     {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
     {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
 ]
 isort = [
-    {file = "isort-5.10.0-py3-none-any.whl", hash = "sha256:1a18ccace2ed8910bd9458b74a3ecbafd7b2f581301b0ab65cfdd4338272d76f"},
-    {file = "isort-5.10.0.tar.gz", hash = "sha256:e52ff6d38012b131628cf0f26c51e7bd3a7c81592eefe3ac71411e692f1b9345"},
+    {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
+    {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
 ]
 lazy-object-proxy = [
-    {file = "lazy-object-proxy-1.6.0.tar.gz", hash = "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726"},
-    {file = "lazy_object_proxy-1.6.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b"},
-    {file = "lazy_object_proxy-1.6.0-cp27-cp27m-win32.whl", hash = "sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e"},
-    {file = "lazy_object_proxy-1.6.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93"},
-    {file = "lazy_object_proxy-1.6.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741"},
-    {file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587"},
-    {file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4"},
-    {file = "lazy_object_proxy-1.6.0-cp36-cp36m-win32.whl", hash = "sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f"},
-    {file = "lazy_object_proxy-1.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3"},
-    {file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981"},
-    {file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2"},
-    {file = "lazy_object_proxy-1.6.0-cp37-cp37m-win32.whl", hash = "sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd"},
-    {file = "lazy_object_proxy-1.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837"},
-    {file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653"},
-    {file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3"},
-    {file = "lazy_object_proxy-1.6.0-cp38-cp38-win32.whl", hash = "sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8"},
-    {file = "lazy_object_proxy-1.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf"},
-    {file = "lazy_object_proxy-1.6.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad"},
-    {file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43"},
-    {file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a"},
-    {file = "lazy_object_proxy-1.6.0-cp39-cp39-win32.whl", hash = "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61"},
-    {file = "lazy_object_proxy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"},
+    {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"},
+    {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"},
+    {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"},
+    {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"},
+    {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"},
+    {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"},
+    {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"},
+    {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"},
+    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"},
+    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"},
+    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"},
+    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"},
+    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"},
+    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"},
+    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"},
+    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"},
+    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"},
+    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"},
+    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"},
+    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"},
+    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"},
+    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"},
+    {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"},
+    {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"},
+    {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"},
+    {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"},
+    {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"},
+    {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"},
+    {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"},
+    {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"},
+    {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"},
+    {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"},
+    {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"},
+    {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"},
+    {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"},
+    {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"},
+    {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"},
 ]
 mccabe = [
     {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
@@ -1093,78 +1097,65 @@ more-properties = [
     {file = "more_properties-1.1.1.tar.gz", hash = "sha256:47b136857d89c72b53def0fade2f2e0a23a95071c3eb87d5a77cfc91554f106c"},
 ]
 multidict = [
-    {file = "multidict-5.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3822c5894c72e3b35aae9909bef66ec83e44522faf767c0ad39e0e2de11d3b55"},
-    {file = "multidict-5.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:28e6d883acd8674887d7edc896b91751dc2d8e87fbdca8359591a13872799e4e"},
-    {file = "multidict-5.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b61f85101ef08cbbc37846ac0e43f027f7844f3fade9b7f6dd087178caedeee7"},
-    {file = "multidict-5.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9b668c065968c5979fe6b6fa6760bb6ab9aeb94b75b73c0a9c1acf6393ac3bf"},
-    {file = "multidict-5.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:517d75522b7b18a3385726b54a081afd425d4f41144a5399e5abd97ccafdf36b"},
-    {file = "multidict-5.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1b4ac3ba7a97b35a5ccf34f41b5a8642a01d1e55454b699e5e8e7a99b5a3acf5"},
-    {file = "multidict-5.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:df23c83398715b26ab09574217ca21e14694917a0c857e356fd39e1c64f8283f"},
-    {file = "multidict-5.2.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e58a9b5cc96e014ddf93c2227cbdeca94b56a7eb77300205d6e4001805391747"},
-    {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f76440e480c3b2ca7f843ff8a48dc82446b86ed4930552d736c0bac507498a52"},
-    {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cfde464ca4af42a629648c0b0d79b8f295cf5b695412451716531d6916461628"},
-    {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0fed465af2e0eb6357ba95795d003ac0bdb546305cc2366b1fc8f0ad67cc3fda"},
-    {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:b70913cbf2e14275013be98a06ef4b412329fe7b4f83d64eb70dce8269ed1e1a"},
-    {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a5635bcf1b75f0f6ef3c8a1ad07b500104a971e38d3683167b9454cb6465ac86"},
-    {file = "multidict-5.2.0-cp310-cp310-win32.whl", hash = "sha256:77f0fb7200cc7dedda7a60912f2059086e29ff67cefbc58d2506638c1a9132d7"},
-    {file = "multidict-5.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:9416cf11bcd73c861267e88aea71e9fcc35302b3943e45e1dbb4317f91a4b34f"},
-    {file = "multidict-5.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fd77c8f3cba815aa69cb97ee2b2ef385c7c12ada9c734b0f3b32e26bb88bbf1d"},
-    {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ec9aea6223adf46999f22e2c0ab6cf33f5914be604a404f658386a8f1fba37"},
-    {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5283c0a00f48e8cafcecadebfa0ed1dac8b39e295c7248c44c665c16dc1138b"},
-    {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5f79c19c6420962eb17c7e48878a03053b7ccd7b69f389d5831c0a4a7f1ac0a1"},
-    {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e4a67f1080123de76e4e97a18d10350df6a7182e243312426d508712e99988d4"},
-    {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:94b117e27efd8e08b4046c57461d5a114d26b40824995a2eb58372b94f9fca02"},
-    {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2e77282fd1d677c313ffcaddfec236bf23f273c4fba7cdf198108f5940ae10f5"},
-    {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:116347c63ba049c1ea56e157fa8aa6edaf5e92925c9b64f3da7769bdfa012858"},
-    {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:dc3a866cf6c13d59a01878cd806f219340f3e82eed514485e094321f24900677"},
-    {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ac42181292099d91217a82e3fa3ce0e0ddf3a74fd891b7c2b347a7f5aa0edded"},
-    {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:f0bb0973f42ffcb5e3537548e0767079420aefd94ba990b61cf7bb8d47f4916d"},
-    {file = "multidict-5.2.0-cp36-cp36m-win32.whl", hash = "sha256:ea21d4d5104b4f840b91d9dc8cbc832aba9612121eaba503e54eaab1ad140eb9"},
-    {file = "multidict-5.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:e6453f3cbeb78440747096f239d282cc57a2997a16b5197c9bc839099e1633d0"},
-    {file = "multidict-5.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3def943bfd5f1c47d51fd324df1e806d8da1f8e105cc7f1c76a1daf0f7e17b0"},
-    {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35591729668a303a02b06e8dba0eb8140c4a1bfd4c4b3209a436a02a5ac1de11"},
-    {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8cacda0b679ebc25624d5de66c705bc53dcc7c6f02a7fb0f3ca5e227d80422"},
-    {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:baf1856fab8212bf35230c019cde7c641887e3fc08cadd39d32a421a30151ea3"},
-    {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a43616aec0f0d53c411582c451f5d3e1123a68cc7b3475d6f7d97a626f8ff90d"},
-    {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25cbd39a9029b409167aa0a20d8a17f502d43f2efebfe9e3ac019fe6796c59ac"},
-    {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a2cbcfbea6dc776782a444db819c8b78afe4db597211298dd8b2222f73e9cd0"},
-    {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3d2d7d1fff8e09d99354c04c3fd5b560fb04639fd45926b34e27cfdec678a704"},
-    {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a37e9a68349f6abe24130846e2f1d2e38f7ddab30b81b754e5a1fde32f782b23"},
-    {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:637c1896497ff19e1ee27c1c2c2ddaa9f2d134bbb5e0c52254361ea20486418d"},
-    {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9815765f9dcda04921ba467957be543423e5ec6a1136135d84f2ae092c50d87b"},
-    {file = "multidict-5.2.0-cp37-cp37m-win32.whl", hash = "sha256:8b911d74acdc1fe2941e59b4f1a278a330e9c34c6c8ca1ee21264c51ec9b67ef"},
-    {file = "multidict-5.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:380b868f55f63d048a25931a1632818f90e4be71d2081c2338fcf656d299949a"},
-    {file = "multidict-5.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e7d81ce5744757d2f05fc41896e3b2ae0458464b14b5a2c1e87a6a9d69aefaa8"},
-    {file = "multidict-5.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d1d55cdf706ddc62822d394d1df53573d32a7a07d4f099470d3cb9323b721b6"},
-    {file = "multidict-5.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4771d0d0ac9d9fe9e24e33bed482a13dfc1256d008d101485fe460359476065"},
-    {file = "multidict-5.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da7d57ea65744d249427793c042094c4016789eb2562576fb831870f9c878d9e"},
-    {file = "multidict-5.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdd68778f96216596218b4e8882944d24a634d984ee1a5a049b300377878fa7c"},
-    {file = "multidict-5.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecc99bce8ee42dcad15848c7885197d26841cb24fa2ee6e89d23b8993c871c64"},
-    {file = "multidict-5.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:067150fad08e6f2dd91a650c7a49ba65085303fcc3decbd64a57dc13a2733031"},
-    {file = "multidict-5.2.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:78c106b2b506b4d895ddc801ff509f941119394b89c9115580014127414e6c2d"},
-    {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e6c4fa1ec16e01e292315ba76eb1d012c025b99d22896bd14a66628b245e3e01"},
-    {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b227345e4186809d31f22087d0265655114af7cda442ecaf72246275865bebe4"},
-    {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:06560fbdcf22c9387100979e65b26fba0816c162b888cb65b845d3def7a54c9b"},
-    {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7878b61c867fb2df7a95e44b316f88d5a3742390c99dfba6c557a21b30180cac"},
-    {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:246145bff76cc4b19310f0ad28bd0769b940c2a49fc601b86bfd150cbd72bb22"},
-    {file = "multidict-5.2.0-cp38-cp38-win32.whl", hash = "sha256:c30ac9f562106cd9e8071c23949a067b10211917fdcb75b4718cf5775356a940"},
-    {file = "multidict-5.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:f19001e790013ed580abfde2a4465388950728861b52f0da73e8e8a9418533c0"},
-    {file = "multidict-5.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c1ff762e2ee126e6f1258650ac641e2b8e1f3d927a925aafcfde943b77a36d24"},
-    {file = "multidict-5.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bd6c9c50bf2ad3f0448edaa1a3b55b2e6866ef8feca5d8dbec10ec7c94371d21"},
-    {file = "multidict-5.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc66d4016f6e50ed36fb39cd287a3878ffcebfa90008535c62e0e90a7ab713ae"},
-    {file = "multidict-5.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9acb76d5f3dd9421874923da2ed1e76041cb51b9337fd7f507edde1d86535d6"},
-    {file = "multidict-5.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dfc924a7e946dd3c6360e50e8f750d51e3ef5395c95dc054bc9eab0f70df4f9c"},
-    {file = "multidict-5.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32fdba7333eb2351fee2596b756d730d62b5827d5e1ab2f84e6cbb287cc67fe0"},
-    {file = "multidict-5.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b9aad49466b8d828b96b9e3630006234879c8d3e2b0a9d99219b3121bc5cdb17"},
-    {file = "multidict-5.2.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:93de39267c4c676c9ebb2057e98a8138bade0d806aad4d864322eee0803140a0"},
-    {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9bef5cff994ca3026fcc90680e326d1a19df9841c5e3d224076407cc21471a1"},
-    {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:5f841c4f14331fd1e36cbf3336ed7be2cb2a8f110ce40ea253e5573387db7621"},
-    {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:38ba256ee9b310da6a1a0f013ef4e422fca30a685bcbec86a969bd520504e341"},
-    {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3bc3b1621b979621cee9f7b09f024ec76ec03cc365e638126a056317470bde1b"},
-    {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6ee908c070020d682e9b42c8f621e8bb10c767d04416e2ebe44e37d0f44d9ad5"},
-    {file = "multidict-5.2.0-cp39-cp39-win32.whl", hash = "sha256:1c7976cd1c157fa7ba5456ae5d31ccdf1479680dc9b8d8aa28afabc370df42b8"},
-    {file = "multidict-5.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:c9631c642e08b9fff1c6255487e62971d8b8e821808ddd013d8ac058087591ac"},
-    {file = "multidict-5.2.0.tar.gz", hash = "sha256:0dd1c93edb444b33ba2274b66f63def8a327d607c6c790772f448a53b6ea59ce"},
+    {file = "multidict-6.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05c3cca447fd39b566615d7cf918f0e83cd92f0549f8182a6cab6e4729c02566"},
+    {file = "multidict-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b487ad10f594e78e0e61662ff7643e1adc610919ea8a95dd976126bffc0256f"},
+    {file = "multidict-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d65b5bd50a5e1f4c6427ebcabb5acc62540301de733f81fd549cf7633826fccd"},
+    {file = "multidict-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a542ceca143e19c08715defe133475961d43d7bfb48dbdc3ac506548ec168f18"},
+    {file = "multidict-6.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af441cfeb3003d96de8acdeb402187bc6a34f96e125618b8275bacfd29a6fad7"},
+    {file = "multidict-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f97f03d7dbc33e20a73428be93974e82762b393579d59053da1257c35bad13b"},
+    {file = "multidict-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c74c7a058a16b7dd24d030f0e8fe846f7d63f41cfeea89a804969cb129182a8"},
+    {file = "multidict-6.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5210e5eca8f0fc0213da31cbc29587d15195b21f40ffd53ff513126c39be0e2"},
+    {file = "multidict-6.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:af1fdc8d25eb9e9998854e996bc982ae3be733590a9e8d6b179b28fd212ddf71"},
+    {file = "multidict-6.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf0aaaef2f8a4cf1973ebe17162b0c3016859eb706d55b328993b8fd8c3515c"},
+    {file = "multidict-6.0.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:1354127c9687d0abbaadfb26e35db247ef1c35f0271e8052afa98b34a9140445"},
+    {file = "multidict-6.0.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2e82ac157bc0719b9802578964d4387e0d7dd530d1fd8949f3cef1c97f2e9d8a"},
+    {file = "multidict-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2f90eb274732fe0958f2d9d4cc7a608bf218e63dd554a690ccafa30f9d9d1b2d"},
+    {file = "multidict-6.0.1-cp310-cp310-win32.whl", hash = "sha256:593586d7ac76ab7a9f229c49d81136b8c1a78119d948ba2a672f4fa681ad54ce"},
+    {file = "multidict-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:f24b3d4405271d6d1b920261437f3acb7dd1c329b0ac782b8b4e0ee7ad187e5b"},
+    {file = "multidict-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9050a2487f9dee9b0abde090b2620a3ba57190d7fe1dc2932b32b9866f90df9e"},
+    {file = "multidict-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff8004ecfb2cc00cd4f063c5e8b3cfd7e544bf774f17c2a861067444532e4fbc"},
+    {file = "multidict-6.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b944477c1e7be8bea2ef819157fb01b79493af1a6dba83d315be63db2957af6"},
+    {file = "multidict-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70a5fff576fe9039550f8d6aacaa98ce26c41df97edc577688c563eb25bcbe83"},
+    {file = "multidict-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e545e5922a4ae99e680a96ed88d03007bd4208fc2599ff273eb2086051d3ef74"},
+    {file = "multidict-6.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502c1610c7737697108ba4717274cca01130ce3e23227034b1601fb9fa0a8aa"},
+    {file = "multidict-6.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:caa9f2874519e0fbb19b90380532e59ef9e07d8ed22d8f0e2e69af796bae2c78"},
+    {file = "multidict-6.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7c9213754993e0dcf70a4fffebef502daf9323fa8d56f0485f724bd4c2991667"},
+    {file = "multidict-6.0.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:0a9857789fd76e0394a5373598820bb2ea3dd113e2c0fb9a92248e3dadd81c5c"},
+    {file = "multidict-6.0.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:92ba0e4745937efef33399348f62a2dc98505dde4d0764061a34123a55fe02e9"},
+    {file = "multidict-6.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3424df56debab711f29c965cadb835dc3c702930265eae26f15ac784feb1dddd"},
+    {file = "multidict-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:d13af99bf380567ede69927d3188cfaccfde76e78e336151e42e60fe03ca5c00"},
+    {file = "multidict-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:37228376057f37fb013130e83e7ad0921598760fb75bd8f0da17403390241d20"},
+    {file = "multidict-6.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1b469534be490ded141d74d7a158db284055b0d04991c2634a9d26d906e063dd"},
+    {file = "multidict-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6ee83096d27dfe52075385f5067d2a54fb227b41666b207e6a64f98ce9048266"},
+    {file = "multidict-6.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19c9c56ddca200f7ffbf376cf2d1a370e8f2e306ec16510a4d90b78a1a0be62"},
+    {file = "multidict-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a6da1575300e24011352c7c620d19b072a2c9498429d1f587e5090621e6f568"},
+    {file = "multidict-6.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48e0919ed5cc5e58c68f40b6516e3f1b0837580a7a8d51d9f99bf0f415d0b73a"},
+    {file = "multidict-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7c10ab3a1ad3188d9a4be01a10c0ddb67d6feaaea538781e73d6ba69fcafbbd"},
+    {file = "multidict-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17289c1ad70e1104ea25f560f6e2941718112d59616482589646aa01fcf4d0d1"},
+    {file = "multidict-6.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a701e9ad52e47b740b5b9aba627ac8fc4ee9e68682f0228fb4a7c4562631ffac"},
+    {file = "multidict-6.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:798dd2d825ce6c0699ce261fda90020562236480192604cfc5be4a315f806810"},
+    {file = "multidict-6.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:564ff7d88b4965cb5211ebf785bca409abbaac8dcca62b0fabe39d56a5ee7283"},
+    {file = "multidict-6.0.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:39d5424379505110d6ca64e927f8b2772c57b05a3d240e66805244ecc4402311"},
+    {file = "multidict-6.0.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:efeba14caaacb4a202d977aca63866069e8a57e632282caf971ce8ff472a49cd"},
+    {file = "multidict-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:241071469989c87da1835f3ccc4143a207b99a2ca27c19b7130a5edecc41a39e"},
+    {file = "multidict-6.0.1-cp38-cp38-win32.whl", hash = "sha256:7184dae6d519a8e629ef10e0e215dde022ee38d55254bea381ca0e61b8b197ea"},
+    {file = "multidict-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:666f78daccf8133ebfacb77e81f4077570b03641b49555ebb5e75797b72770e1"},
+    {file = "multidict-6.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5a4fcf74d50a65fe99a68531d09f10ceef911992e0e64c1b2cf212effa075f8b"},
+    {file = "multidict-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4a98ee92e96157a3c787c5aa156968ceffb6fb4548c5f74ff879884fed2f9752"},
+    {file = "multidict-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b6bfb0a4df79bd78907fff46c34ed47284d5eb459b096d2de896b0b1c31f3fa7"},
+    {file = "multidict-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbda7f3be5a75ee87a2649382e47f419374420ef244b372312e662637931399f"},
+    {file = "multidict-6.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f74a680ba9d6c543c1cdd8bcef464d58b933bed29edb89a0b3c0b33cc553181a"},
+    {file = "multidict-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:389a689aa3b657b87cb21f78468caa4dfccd758639ff4b37b6e2dceef7c5b12f"},
+    {file = "multidict-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36195396f2a76dd23a67ca2bdeb8589b6eaaef7e84c97cf90da3ede69f189baa"},
+    {file = "multidict-6.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02847aa3602e21c04ac6fdef6f7c6dc916de8cce49a9eb59aeffedd3365f196c"},
+    {file = "multidict-6.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bd21540c9cc9b90373b5a6aaed8f8d53815529e89210453e0566d00389034b82"},
+    {file = "multidict-6.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e037782357ddf6741023df1a84f17a3a1bb62abba52e41efa4014199f6338143"},
+    {file = "multidict-6.0.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ea08fb7383422148bcfc066c3d2dae03fcdb8bd06e7ce713badd86bc26c25179"},
+    {file = "multidict-6.0.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b85354b2d2fe38a3ab90348f8c9778a437d376252f62115cabea1106332bd1b6"},
+    {file = "multidict-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c863a506466d64871c70e4204cecdcaf2c0c92d157683bb5d5b3e7b5aaea05a"},
+    {file = "multidict-6.0.1-cp39-cp39-win32.whl", hash = "sha256:d2cc15d2507b0d5f09726fd97bb9ffaa6bb47487fe77a208262cb6187fd9dcf3"},
+    {file = "multidict-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:38ada537db7f9089560cf16dc5b8b280096213bc5260970929aca43675682739"},
+    {file = "multidict-6.0.1.tar.gz", hash = "sha256:d40616f3f9326a18e1f2fa7c7e8e0e04a7e5228bfa2bd62c1e0d68fbc259b09b"},
 ]
 mypy = [
     {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"},
@@ -1204,8 +1195,8 @@ pathspec = [
     {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
 ]
 platformdirs = [
-    {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"},
-    {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"},
+    {file = "platformdirs-2.4.1-py3-none-any.whl", hash = "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca"},
+    {file = "platformdirs-2.4.1.tar.gz", hash = "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda"},
 ]
 pluggy = [
     {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
@@ -1220,12 +1211,12 @@ py = [
     {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
 ]
 pylint = [
-    {file = "pylint-2.11.1-py3-none-any.whl", hash = "sha256:0f358e221c45cbd4dad2a1e4b883e75d28acdcccd29d40c76eb72b307269b126"},
-    {file = "pylint-2.11.1.tar.gz", hash = "sha256:2c9843fff1a88ca0ad98a256806c82c5a8f86086e7ccbdb93297d86c3f90c436"},
+    {file = "pylint-2.12.2-py3-none-any.whl", hash = "sha256:daabda3f7ed9d1c60f52d563b1b854632fd90035bcf01443e234d3dc794e3b74"},
+    {file = "pylint-2.12.2.tar.gz", hash = "sha256:9d945a73640e1fec07ee34b42f5669b770c759acd536ec7b16d7e4b87a9c9ff9"},
 ]
 pyparsing = [
-    {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"},
-    {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"},
+    {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"},
+    {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"},
 ]
 pytest = [
     {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
@@ -1240,8 +1231,8 @@ pytest-cov = [
     {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
 ]
 python-dotenv = [
-    {file = "python-dotenv-0.19.1.tar.gz", hash = "sha256:14f8185cc8d494662683e6914addcb7e95374771e707601dfc70166946b4c4b8"},
-    {file = "python_dotenv-0.19.1-py2.py3-none-any.whl", hash = "sha256:bbd3da593fc49c249397cbfbcc449cf36cb02e75afc8157fcc6a81df6fb7750a"},
+    {file = "python-dotenv-0.19.2.tar.gz", hash = "sha256:a5de49a31e953b45ff2d2fd434bbc2670e8db5273606c1e737cc6b93eff3655f"},
+    {file = "python_dotenv-0.19.2-py2.py3-none-any.whl", hash = "sha256:32b2bdc1873fd3a3c346da1c6db83d0053c3c62f28f1f38516070c4c8971b1d3"},
 ]
 pywin32 = [
     {file = "pywin32-227-cp27-cp27m-win32.whl", hash = "sha256:371fcc39416d736401f0274dd64c2302728c9e034808e37381b5e1b22be4a6b0"},
@@ -1292,72 +1283,21 @@ pyyaml = [
     {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
     {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
 ]
-regex = [
-    {file = "regex-2021.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:897c539f0f3b2c3a715be651322bef2167de1cdc276b3f370ae81a3bda62df71"},
-    {file = "regex-2021.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:886f459db10c0f9d17c87d6594e77be915f18d343ee138e68d259eb385f044a8"},
-    {file = "regex-2021.11.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:075b0fdbaea81afcac5a39a0d1bb91de887dd0d93bf692a5dd69c430e7fc58cb"},
-    {file = "regex-2021.11.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6238d30dcff141de076344cf7f52468de61729c2f70d776fce12f55fe8df790"},
-    {file = "regex-2021.11.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fab29411d75c2eb48070020a40f80255936d7c31357b086e5931c107d48306e"},
-    {file = "regex-2021.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0148988af0182a0a4e5020e7c168014f2c55a16d11179610f7883dd48ac0ebe"},
-    {file = "regex-2021.11.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be30cd315db0168063a1755fa20a31119da91afa51da2907553493516e165640"},
-    {file = "regex-2021.11.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e9cec3a62d146e8e122d159ab93ac32c988e2ec0dcb1e18e9e53ff2da4fbd30c"},
-    {file = "regex-2021.11.2-cp310-cp310-win32.whl", hash = "sha256:41c66bd6750237a8ed23028a6c9173dc0c92dc24c473e771d3bfb9ee817700c3"},
-    {file = "regex-2021.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:0075fe4e2c2720a685fef0f863edd67740ff78c342cf20b2a79bc19388edf5db"},
-    {file = "regex-2021.11.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0ed3465acf8c7c10aa2e0f3d9671da410ead63b38a77283ef464cbb64275df58"},
-    {file = "regex-2021.11.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab1fea8832976ad0bebb11f652b692c328043057d35e9ebc78ab0a7a30cf9a70"},
-    {file = "regex-2021.11.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb1e44d860345ab5d4f533b6c37565a22f403277f44c4d2d5e06c325da959883"},
-    {file = "regex-2021.11.2-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9486ebda015913909bc28763c6b92fcc3b5e5a67dee4674bceed112109f5dfb8"},
-    {file = "regex-2021.11.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20605bfad484e1341b2cbfea0708e4b211d233716604846baa54b94821f487cb"},
-    {file = "regex-2021.11.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f20f9f430c33597887ba9bd76635476928e76cad2981643ca8be277b8e97aa96"},
-    {file = "regex-2021.11.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1d85ca137756d62c8138c971453cafe64741adad1f6a7e63a22a5a8abdbd19fa"},
-    {file = "regex-2021.11.2-cp36-cp36m-win32.whl", hash = "sha256:af23b9ca9a874ef0ec20e44467b8edd556c37b0f46f93abfa93752ea7c0e8d1e"},
-    {file = "regex-2021.11.2-cp36-cp36m-win_amd64.whl", hash = "sha256:070336382ca92c16c45b4066c4ba9fa83fb0bd13d5553a82e07d344df8d58a84"},
-    {file = "regex-2021.11.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ef4e53e2fdc997d91f5b682f81f7dc9661db9a437acce28745d765d251902d85"},
-    {file = "regex-2021.11.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35ed5714467fc606551db26f80ee5d6aa1f01185586a7bccd96f179c4b974a11"},
-    {file = "regex-2021.11.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee36d5113b6506b97f45f2e8447cb9af146e60e3f527d93013d19f6d0405f3b"},
-    {file = "regex-2021.11.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4fba661a4966adbd2c3c08d3caad6822ecb6878f5456588e2475ae23a6e47929"},
-    {file = "regex-2021.11.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77f9d16f7970791f17ecce7e7f101548314ed1ee2583d4268601f30af3170856"},
-    {file = "regex-2021.11.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6a28e87ba69f3a4f30d775b179aac55be1ce59f55799328a0d9b6df8f16b39d"},
-    {file = "regex-2021.11.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9267e4fba27e6dd1008c4f2983cc548c98b4be4444e3e342db11296c0f45512f"},
-    {file = "regex-2021.11.2-cp37-cp37m-win32.whl", hash = "sha256:d4bfe3bc3976ccaeb4ae32f51e631964e2f0e85b2b752721b7a02de5ce3b7f27"},
-    {file = "regex-2021.11.2-cp37-cp37m-win_amd64.whl", hash = "sha256:2bb7cae741de1aa03e3dd3a7d98c304871eb155921ca1f0d7cc11f5aade913fd"},
-    {file = "regex-2021.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:23f93e74409c210de4de270d4bf88fb8ab736a7400f74210df63a93728cf70d6"},
-    {file = "regex-2021.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8ee91e1c295beb5c132ebd78616814de26fedba6aa8687ea460c7f5eb289b72"},
-    {file = "regex-2021.11.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e3ff69ab203b54ce5c480c3ccbe959394ea5beef6bd5ad1785457df7acea92e"},
-    {file = "regex-2021.11.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e3c00cb5c71da655e1e5161481455479b613d500dd1bd252aa01df4f037c641f"},
-    {file = "regex-2021.11.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4abf35e16f4b639daaf05a2602c1b1d47370e01babf9821306aa138924e3fe92"},
-    {file = "regex-2021.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb11c982a849dc22782210b01d0c1b98eb3696ce655d58a54180774e4880ac66"},
-    {file = "regex-2021.11.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e3755e0f070bc31567dfe447a02011bfa8444239b3e9e5cca6773a22133839"},
-    {file = "regex-2021.11.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0621c90f28d17260b41838b22c81a79ff436141b322960eb49c7b3f91d1cbab6"},
-    {file = "regex-2021.11.2-cp38-cp38-win32.whl", hash = "sha256:8fbe1768feafd3d0156556677b8ff234c7bf94a8110e906b2d73506f577a3269"},
-    {file = "regex-2021.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:f9ee98d658a146cb6507be720a0ce1b44f2abef8fb43c2859791d91aace17cd5"},
-    {file = "regex-2021.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b3794cea825f101fe0df9af8a00f9fad8e119c91e39a28636b95ee2b45b6c2e5"},
-    {file = "regex-2021.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3576e173e7b4f88f683b4de7db0c2af1b209bb48b2bf1c827a6f3564fad59a97"},
-    {file = "regex-2021.11.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48b4f4810117a9072a5aa70f7fea5f86fa9efbe9a798312e0a05044bd707cc33"},
-    {file = "regex-2021.11.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5930d334c2f607711d54761956aedf8137f83f1b764b9640be21d25a976f3a4"},
-    {file = "regex-2021.11.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:956187ff49db7014ceb31e88fcacf4cf63371e6e44d209cf8816cd4a2d61e11a"},
-    {file = "regex-2021.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17e095f7f96a4b9f24b93c2c915f31a5201a6316618d919b0593afb070a5270e"},
-    {file = "regex-2021.11.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a56735c35a3704603d9d7b243ee06139f0837bcac2171d9ba1d638ce1df0742a"},
-    {file = "regex-2021.11.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:adf35d88d9cffc202e6046e4c32e1e11a1d0238b2fcf095c94f109e510ececea"},
-    {file = "regex-2021.11.2-cp39-cp39-win32.whl", hash = "sha256:30fe317332de0e50195665bc61a27d46e903d682f94042c36b3f88cb84bd7958"},
-    {file = "regex-2021.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:85289c25f658e3260b00178757c87f033f3d4b3e40aa4abdd4dc875ff11a94fb"},
-    {file = "regex-2021.11.2.tar.gz", hash = "sha256:5e85dcfc5d0f374955015ae12c08365b565c6f1eaf36dd182476a4d8e5a1cdb7"},
-]
 requests = [
-    {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"},
-    {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"},
+    {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
+    {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
 ]
 toml = [
     {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
     {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
 ]
 tomli = [
-    {file = "tomli-1.2.2-py3-none-any.whl", hash = "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"},
-    {file = "tomli-1.2.2.tar.gz", hash = "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee"},
+    {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"},
+    {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"},
 ]
 toolz = [
-    {file = "toolz-0.11.1-py3-none-any.whl", hash = "sha256:1bc473acbf1a1db4e72a1ce587be347450e8f08324908b8a266b486f408f04d5"},
-    {file = "toolz-0.11.1.tar.gz", hash = "sha256:c7a47921f07822fe534fb1c01c9931ab335a4390c782bd28c6bcc7c2f71f3fbf"},
+    {file = "toolz-0.11.2-py3-none-any.whl", hash = "sha256:a5700ce83414c64514d82d60bcda8aabfde092d1c1a8663f9200c07fdcc6da8f"},
+    {file = "toolz-0.11.2.tar.gz", hash = "sha256:6b312d5e15138552f1bda8a4e66c30e236c831b612b2bf0005f8a1df10a4bc33"},
 ]
 toposort = [
     {file = "toposort-1.7-py2.py3-none-any.whl", hash = "sha256:8ed8e109e96ae30bf66da2d2155e4eb9989d9c5c743c837e37d9774a4eddd804"},
@@ -1396,13 +1336,16 @@ typed-ast = [
     {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"},
 ]
 types-requests = [
-    {file = "types-requests-2.25.11.tar.gz", hash = "sha256:b279284e51f668e38ee12d9665e4d789089f532dc2a0be4a1508ca0efd98ba9e"},
-    {file = "types_requests-2.25.11-py3-none-any.whl", hash = "sha256:ba1d108d512e294b6080c37f6ae7cb2a2abf527560e2b671d1786c1fc46b541a"},
+    {file = "types-requests-2.27.7.tar.gz", hash = "sha256:f38bd488528cdcbce5b01dc953972f3cead0d060cfd9ee35b363066c25bab13c"},
+    {file = "types_requests-2.27.7-py3-none-any.whl", hash = "sha256:2e0e100dd489f83870d4f61949d3a7eae4821e7bfbf46c57e463c38f92d473d4"},
+]
+types-urllib3 = [
+    {file = "types-urllib3-1.26.7.tar.gz", hash = "sha256:cfd1fbbe4ba9a605ed148294008aac8a7b8b7472651d1cc357d507ae5962e3d2"},
+    {file = "types_urllib3-1.26.7-py3-none-any.whl", hash = "sha256:3adcf2cb5981809091dbff456e6999fe55f201652d8c360f99997de5ac2f556e"},
 ]
 typing-extensions = [
-    {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"},
-    {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"},
-    {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"},
+    {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"},
+    {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"},
 ]
 typing-inspect = [
     {file = "typing_inspect-0.7.1-py2-none-any.whl", hash = "sha256:b1f56c0783ef0f25fb064a01be6e5407e54cf4a4bf4f3ba3fe51e0bd6dcea9e5"},
@@ -1410,8 +1353,8 @@ typing-inspect = [
     {file = "typing_inspect-0.7.1.tar.gz", hash = "sha256:047d4097d9b17f46531bf6f014356111a1b6fb821a24fe7ac909853ca2a782aa"},
 ]
 urllib3 = [
-    {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"},
-    {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"},
+    {file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"},
+    {file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
 ]
 uvicorn = [
     {file = "uvicorn-0.15.0-py3-none-any.whl", hash = "sha256:17f898c64c71a2640514d4089da2689e5db1ce5d4086c2d53699bf99513421c1"},
@@ -1440,35 +1383,58 @@ watchgod = [
     {file = "watchgod-0.7.tar.gz", hash = "sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29"},
 ]
 websocket-client = [
-    {file = "websocket-client-1.2.1.tar.gz", hash = "sha256:8dfb715d8a992f5712fff8c843adae94e22b22a99b2c5e6b0ec4a1a981cc4e0d"},
-    {file = "websocket_client-1.2.1-py2.py3-none-any.whl", hash = "sha256:0133d2f784858e59959ce82ddac316634229da55b498aac311f1620567a710ec"},
+    {file = "websocket-client-1.2.3.tar.gz", hash = "sha256:1315816c0acc508997eb3ae03b9d3ff619c9d12d544c9a9b553704b1cc4f6af5"},
+    {file = "websocket_client-1.2.3-py3-none-any.whl", hash = "sha256:2eed4cc58e4d65613ed6114af2f380f7910ff416fc8c46947f6e76b6815f56c0"},
 ]
 websockets = [
-    {file = "websockets-10.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cd8c6f2ec24aedace251017bc7a414525171d4e6578f914acab9349362def4da"},
-    {file = "websockets-10.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1f6b814cff6aadc4288297cb3a248614829c6e4ff5556593c44a115e9dd49939"},
-    {file = "websockets-10.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:01db0ecd1a0ca6702d02a5ed40413e18b7d22f94afb3bbe0d323bac86c42c1c8"},
-    {file = "websockets-10.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:82b17524b1ce6ae7f7dd93e4d18e9b9474071e28b65dbf1dfe9b5767778db379"},
-    {file = "websockets-10.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:8bbf8660c3f833ddc8b1afab90213f2e672a9ddac6eecb3cde968e6b2807c1c7"},
-    {file = "websockets-10.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b8176deb6be540a46695960a765a77c28ac8b2e3ef2ec95d50a4f5df901edb1c"},
-    {file = "websockets-10.0-cp37-cp37m-win32.whl", hash = "sha256:706e200fc7f03bed99ad0574cd1ea8b0951477dd18cc978ccb190683c69dba76"},
-    {file = "websockets-10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5b2600e01c7ca6f840c42c747ffbe0254f319594ed108db847eb3d75f4aacb80"},
-    {file = "websockets-10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:085bb8a6e780d30eaa1ba48ac7f3a6707f925edea787cfb761ce5a39e77ac09b"},
-    {file = "websockets-10.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9a4d889162bd48588e80950e07fa5e039eee9deb76a58092e8c3ece96d7ef537"},
-    {file = "websockets-10.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b4ade7569b6fd17912452f9c3757d96f8e4044016b6d22b3b8391e641ca50456"},
-    {file = "websockets-10.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:2a43072e434c041a99f2e1eb9b692df0232a38c37c61d00e9f24db79474329e4"},
-    {file = "websockets-10.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:7f79f02c7f9a8320aff7d3321cd1c7e3a7dbc15d922ac996cca827301ee75238"},
-    {file = "websockets-10.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:1ac35426fe3e7d3d0fac3d63c8965c76ed67a8fd713937be072bf0ce22808539"},
-    {file = "websockets-10.0-cp38-cp38-win32.whl", hash = "sha256:ff59c6bdb87b31f7e2d596f09353d5a38c8c8ff571b0e2238e8ee2d55ad68465"},
-    {file = "websockets-10.0-cp38-cp38-win_amd64.whl", hash = "sha256:d67646ddd17a86117ae21c27005d83c1895c0cef5d7be548b7549646372f868a"},
-    {file = "websockets-10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:82bd921885231f4a30d9bc550552495b3fc36b1235add6d374e7c65c3babd805"},
-    {file = "websockets-10.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7d2e12e4f901f1bc062dfdf91831712c4106ed18a9a4cdb65e2e5f502124ca37"},
-    {file = "websockets-10.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:71358c7816e2762f3e4af3adf0040f268e219f5a38cb3487a9d0fc2e554fef6a"},
-    {file = "websockets-10.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:fe83b3ec9ef34063d86dfe1029160a85f24a5a94271036e5714a57acfdd089a1"},
-    {file = "websockets-10.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:eb282127e9c136f860c6068a4fba5756eb25e755baffb5940b6f1eae071928b2"},
-    {file = "websockets-10.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:62160772314920397f9d219147f958b33fa27a12c662d4455c9ccbba9a07e474"},
-    {file = "websockets-10.0-cp39-cp39-win32.whl", hash = "sha256:e42a1f1e03437b017af341e9bbfdc09252cd48ef32a8c3c3ead769eab3b17368"},
-    {file = "websockets-10.0-cp39-cp39-win_amd64.whl", hash = "sha256:c5880442f5fc268f1ef6d37b2c152c114deccca73f48e3a8c48004d2f16f4567"},
-    {file = "websockets-10.0.tar.gz", hash = "sha256:c4fc9a1d242317892590abe5b61a9127f1a61740477bfb121743f290b8054002"},
+    {file = "websockets-10.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:38db6e2163b021642d0a43200ee2dec8f4980bdbda96db54fde72b283b54cbfc"},
+    {file = "websockets-10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1b60fd297adb9fc78375778a5220da7f07bf54d2a33ac781319650413fc6a60"},
+    {file = "websockets-10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3477146d1f87ead8df0f27e8960249f5248dceb7c2741e8bbec9aa5338d0c053"},
+    {file = "websockets-10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb01ea7b5f52e7125bdc3c5807aeaa2d08a0553979cf2d96a8b7803ea33e15e7"},
+    {file = "websockets-10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9fd62c6dc83d5d35fb6a84ff82ec69df8f4657fff05f9cd6c7d9bec0dd57f0f6"},
+    {file = "websockets-10.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3bbf080f3892ba1dc8838786ec02899516a9d227abe14a80ef6fd17d4fb57127"},
+    {file = "websockets-10.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5560558b0dace8312c46aa8915da977db02738ac8ecffbc61acfbfe103e10155"},
+    {file = "websockets-10.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:667c41351a6d8a34b53857ceb8343a45c85d438ee4fd835c279591db8aeb85be"},
+    {file = "websockets-10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:468f0031fdbf4d643f89403a66383247eb82803430b14fa27ce2d44d2662ca37"},
+    {file = "websockets-10.1-cp310-cp310-win32.whl", hash = "sha256:d0d81b46a5c87d443e40ce2272436da8e6092aa91f5fbeb60d1be9f11eff5b4c"},
+    {file = "websockets-10.1-cp310-cp310-win_amd64.whl", hash = "sha256:b68b6caecb9a0c6db537aa79750d1b592a841e4f1a380c6196091e65b2ad35f9"},
+    {file = "websockets-10.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a249139abc62ef333e9e85064c27fefb113b16ffc5686cefc315bdaef3eefbc8"},
+    {file = "websockets-10.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8877861e3dee38c8d302eee0d5dbefa6663de3b46dc6a888f70cd7e82562d1f7"},
+    {file = "websockets-10.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e3872ae57acd4306ecf937d36177854e218e999af410a05c17168cd99676c512"},
+    {file = "websockets-10.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b66e6d514f12c28d7a2d80bb2a48ef223342e99c449782d9831b0d29a9e88a17"},
+    {file = "websockets-10.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9f304a22ece735a3da8a51309bc2c010e23961a8f675fae46fdf62541ed62123"},
+    {file = "websockets-10.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:189ed478395967d6a98bb293abf04e8815349e17456a0a15511f1088b6cb26e4"},
+    {file = "websockets-10.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:08a42856158307e231b199671c4fce52df5786dd3d703f36b5d8ac76b206c485"},
+    {file = "websockets-10.1-cp37-cp37m-win32.whl", hash = "sha256:3ef6f73854cded34e78390dbdf40dfdcf0b89b55c0e282468ef92646fce8d13a"},
+    {file = "websockets-10.1-cp37-cp37m-win_amd64.whl", hash = "sha256:89e985d40d407545d5f5e2e58e1fdf19a22bd2d8cd54d20a882e29f97e930a0a"},
+    {file = "websockets-10.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:002071169d2e44ce8eb9e5ebac9fbce142ba4b5146eef1cfb16b177a27662657"},
+    {file = "websockets-10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cfae282c2aa7f0c4be45df65c248481f3509f8c40ca8b15ed96c35668ae0ff69"},
+    {file = "websockets-10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:97b4b68a2ddaf5c4707ae79c110bfd874c5be3c6ac49261160fb243fa45d8bbb"},
+    {file = "websockets-10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c9407719f42cb77049975410490c58a705da6af541adb64716573e550e5c9db"},
+    {file = "websockets-10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1d858fb31e5ac992a2cdf17e874c95f8a5b1e917e1fb6b45ad85da30734b223f"},
+    {file = "websockets-10.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7bdd3d26315db0a9cf8a0af30ca95e0aa342eda9c1377b722e71ccd86bc5d1dd"},
+    {file = "websockets-10.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e259be0863770cb91b1a6ccf6907f1ac2f07eff0b7f01c249ed751865a70cb0d"},
+    {file = "websockets-10.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6b014875fae19577a392372075e937ebfebf53fd57f613df07b35ab210f31534"},
+    {file = "websockets-10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:98de71f86bdb29430fd7ba9997f47a6b10866800e3ea577598a786a785701bb0"},
+    {file = "websockets-10.1-cp38-cp38-win32.whl", hash = "sha256:3a02ab91d84d9056a9ee833c254895421a6333d7ae7fff94b5c68e4fa8095519"},
+    {file = "websockets-10.1-cp38-cp38-win_amd64.whl", hash = "sha256:7d6673b2753f9c5377868a53445d0c321ef41ff3c8e3b6d57868e72054bfce5f"},
+    {file = "websockets-10.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ddab2dc69ee5ae27c74dbfe9d7bb6fee260826c136dca257faa1a41d1db61a89"},
+    {file = "websockets-10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:14e9cf68a08d1a5d42109549201aefba473b1d925d233ae19035c876dd845da9"},
+    {file = "websockets-10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e4819c6fb4f336fd5388372cb556b1f3a165f3f68e66913d1a2fc1de55dc6f58"},
+    {file = "websockets-10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05e7f098c76b0a4743716590bb8f9706de19f1ef5148d61d0cf76495ec3edb9c"},
+    {file = "websockets-10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5bb6256de5a4fb1d42b3747b4e2268706c92965d75d0425be97186615bf2f24f"},
+    {file = "websockets-10.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:888a5fa2a677e0c2b944f9826c756475980f1b276b6302e606f5c4ff5635be9e"},
+    {file = "websockets-10.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6fdec1a0b3e5630c58e3d8704d2011c678929fce90b40908c97dfc47de8dca72"},
+    {file = "websockets-10.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:531d8eb013a9bc6b3ad101588182aa9b6dd994b190c56df07f0d84a02b85d530"},
+    {file = "websockets-10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0d93b7cadc761347d98da12ec1930b5c71b2096f1ceed213973e3cda23fead9c"},
+    {file = "websockets-10.1-cp39-cp39-win32.whl", hash = "sha256:d9b245db5a7e64c95816e27d72830e51411c4609c05673d1ae81eb5d23b0be54"},
+    {file = "websockets-10.1-cp39-cp39-win_amd64.whl", hash = "sha256:882c0b8bdff3bf1bd7f024ce17c6b8006042ec4cceba95cf15df57e57efa471c"},
+    {file = "websockets-10.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:10edd9d7d3581cfb9ff544ac09fc98cab7ee8f26778a5a8b2d5fd4b0684c5ba5"},
+    {file = "websockets-10.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa83174390c0ff4fc1304fbe24393843ac7a08fdd59295759c4b439e06b1536"},
+    {file = "websockets-10.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:483edee5abed738a0b6a908025be47f33634c2ad8e737edd03ffa895bd600909"},
+    {file = "websockets-10.1-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:816ae7dac2c6522cfa620947ead0ca95ac654916eebf515c94d7c28de5601a6e"},
+    {file = "websockets-10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1dafe98698ece09b8ccba81b910643ff37198e43521d977be76caf37709cf62b"},
+    {file = "websockets-10.1.tar.gz", hash = "sha256:181d2b25de5a437b36aefedaf006ecb6fa3aa1328ec0236cdde15f32f9d3ff6d"},
 ]
 wrapt = [
     {file = "wrapt-1.13.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a"},
@@ -1524,8 +1490,8 @@ wrapt = [
     {file = "wrapt-1.13.3.tar.gz", hash = "sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185"},
 ]
 xsdata = [
-    {file = "xsdata-21.11-py3-none-any.whl", hash = "sha256:ef6fd88e2a93a1e4e0ba19a70e9e4603b78919e2a3df5db8f532a33b540a4ca0"},
-    {file = "xsdata-21.11.tar.gz", hash = "sha256:83ffe4b503ab8e9127b79505d913605581bbd936c89f713f9a5f1be1b9aa17d8"},
+    {file = "xsdata-21.12-py3-none-any.whl", hash = "sha256:857ef5d1c1fdee09f7f1a0a0e8f2771c0469dcc60b28f9b2a7b2a0d2aba3a58f"},
+    {file = "xsdata-21.12.tar.gz", hash = "sha256:baba44a2909fc928f783b0cdde4e1a96d5c632414588b9934aa8adf5f60002e8"},
 ]
 yarl = [
     {file = "yarl-1.7.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2a8508f7350512434e41065684076f640ecce176d262a7d54f0da41d99c5a95"},
@@ -1602,6 +1568,6 @@ yarl = [
     {file = "yarl-1.7.2.tar.gz", hash = "sha256:45399b46d60c253327a460e99856752009fcee5f5d3c80b2f7c0cae1c38d56dd"},
 ]
 zipp = [
-    {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"},
-    {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"},
+    {file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"},
+    {file = "zipp-3.7.0.tar.gz", hash = "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d"},
 ]
diff --git a/pylintrc b/pylintrc
index 8640fa7..76d3dac 100755
--- a/pylintrc
+++ b/pylintrc
@@ -433,6 +433,7 @@ good-names=i,
            b,
            f,
            fn,
+           n,
            T,
            tb,
            to
diff --git a/pyproject.toml b/pyproject.toml
index e14fb51..eee344b 100755
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -18,16 +18,16 @@ license = "MIT"
 name = "dmarc-metrics-exporter"
 readme = "README.rst"
 repository = "https://github.com/jgosmann/dmarc-metrics-exporter/"
-version = "0.4.3"
+version = "0.5.0"
 
 [tool.poetry.dependencies]
-aioimaplib = "^0.9.0"
+bite-parser = "^0.1.1"
 dataclasses-serialization = "^1.3.1"
 prometheus_client = "^0.11.0"
 python = "^3.7,<3.10"
+typing-extensions = "^4.0.1"
 uvicorn = {extras = ["standard"], version = "^0.15.0"}
 xsdata = "^21.9"
-pyparsing = "^3.0.6"
 
 [tool.poetry.dev-dependencies]
 aiohttp = "^3.7.3"
@@ -87,11 +87,6 @@ skip = [
 ]
 use_parentheses = true
 
-[tool.pytest.ini_options]
-filterwarnings = [
-  "ignore::DeprecationWarning:aioimaplib",
-]
-
 [build-system]
 build-backend = "poetry.masonry.api"
 requires = ["poetry>=0.12"]


Test patch:
diff --git a/dmarc_metrics_exporter/tests/conftest.py b/dmarc_metrics_exporter/tests/conftest.py
index bfa512e..414ffe4 100644
--- a/dmarc_metrics_exporter/tests/conftest.py
+++ b/dmarc_metrics_exporter/tests/conftest.py
@@ -8,7 +8,8 @@
 import pytest
 import requests
 
-from dmarc_metrics_exporter.imap_queue import ConnectionConfig, ImapClient
+from dmarc_metrics_exporter.imap_client import ImapClient
+from dmarc_metrics_exporter.imap_queue import ConnectionConfig
 
 
 @dataclass
@@ -46,7 +47,12 @@ def fixture_greenmail() -> Greenmail:
     greenmail = Greenmail(
         smtp=NetworkAddress("localhost", 3025),
         imap=ConnectionConfig(
-            host="localhost", port=3993, username="queue@localhost", password="password"
+            host="localhost",
+            port=3993,
+            username="queue@localhost",
+            password="password",
+            use_ssl=True,
+            verify_certificate=False,
         ),
         api=NetworkAddress("localhost", 8080),
     )
@@ -69,6 +75,10 @@ async def try_until_success(
                 return await asyncio.wait_for(result, max_fn_duration_seconds)
             else:
                 return result
+        except asyncio.TimeoutError as err:
+            raise TimeoutError(
+                f"Function execution duration exceeded {max_fn_duration_seconds} seconds."
+            ) from err
         except Exception as err:  # pylint: disable=broad-except
             last_err = err
             await asyncio.sleep(poll_interval_seconds)
diff --git a/dmarc_metrics_exporter/tests/sample_emails.py b/dmarc_metrics_exporter/tests/sample_emails.py
index 7b8ab24..96c6881 100644
--- a/dmarc_metrics_exporter/tests/sample_emails.py
+++ b/dmarc_metrics_exporter/tests/sample_emails.py
@@ -7,6 +7,16 @@
 from dmarc_metrics_exporter.model.tests.sample_data import SAMPLE_XML
 
 
+def create_minimal_email(to="dmarc-feedback@mydomain.de", content=None):
+    msg = EmailMessage()
+    msg["Subject"] = "Minimal email"
+    msg["From"] = "noreply-dmarc-support@google.com"
+    msg["To"] = to
+    if content:
+        msg.set_content(content)
+    return msg
+
+
 def create_email_with_xml_attachment(to="dmarc-feedback@mydomain.de"):
     xml = MIMEText(SAMPLE_XML, "xml")
     xml.add_header(
diff --git a/dmarc_metrics_exporter/tests/test_imap_client.py b/dmarc_metrics_exporter/tests/test_imap_client.py
new file mode 100644
index 0000000..284da43
--- /dev/null
+++ b/dmarc_metrics_exporter/tests/test_imap_client.py
@@ -0,0 +1,476 @@
+import asyncio
+import io
+import logging
+import re
+from asyncio import (
+    Condition,
+    Event,
+    IncompleteReadError,
+    StreamReader,
+    StreamWriter,
+    create_task,
+    start_server,
+    wait_for,
+)
+from typing import Callable, Coroutine, Dict
+
+import pytest
+
+from dmarc_metrics_exporter.imap_client import (
+    ConnectionConfig,
+    ImapClient,
+    ImapServerError,
+)
+from dmarc_metrics_exporter.tests.conftest import send_email, try_until_success
+from dmarc_metrics_exporter.tests.sample_emails import create_minimal_email
+
+logger = logging.getLogger(__name__)
+
+
+class MockReader:
+    def __init__(self, input_buf: bytes):
+        self.reader = io.BytesIO(input_buf)
+        self.eol_pos = self.reader.seek(0, io.SEEK_END)
+        self.reader.seek(0, io.SEEK_SET)
+
+    async def read(self, n=-1) -> bytes:
+        return self.reader.read(n)
+
+    async def readline(self) -> bytes:
+        return self.reader.readline()
+
+    async def readexactly(self, n) -> bytes:
+        buf = await self.read(n)
+        if len(buf) < n:
+            raise IncompleteReadError(buf, n)
+        return buf
+
+    async def readuntil(self, separator=b"\n") -> bytes:
+        raise NotImplementedError()
+
+    def at_eof(self) -> bool:
+        return self.reader.tell() >= self.eol_pos
+
+
+@pytest.mark.asyncio
+async def test_basic_connection(greenmail):
+    async with ImapClient(greenmail.imap) as client:
+        assert client.has_capability("IMAP4rev1")
+
+
+@pytest.mark.asyncio
+async def test_fetch(greenmail):
+    await send_email(create_minimal_email(greenmail.imap.username), greenmail.smtp)
+    async with ImapClient(greenmail.imap) as client:
+        assert await client.select("INBOX") == 1
+        await client.fetch(b"1:1", b"(BODY[HEADER.FIELDS (SUBJECT)])")
+        fetched_email = await wait_for(client.fetched_queue.get(), 5)
+        assert fetched_email[:2] == (1, b"FETCH")
+        assert [
+            item[2]
+            for item in fetched_email[2]
+            if item[:2] == (b"BODY", (b"HEADER.FIELDS", (b"SUBJECT",)))
+        ] == [b"Subject: Minimal email\r\n"]
+
+
+@pytest.mark.asyncio
+async def test_fetch_non_ascii_chars(greenmail):
+    await send_email(
+        create_minimal_email(greenmail.imap.username, "üüüü"),
+        greenmail.smtp,
+    )
+    async with ImapClient(greenmail.imap) as client:
+        assert await client.select("INBOX") == 1
+        await client.fetch(b"1:1", b"(RFC822)")
+        fetched_email = await wait_for(client.fetched_queue.get(), 5)
+        assert fetched_email[:2] == (1, b"FETCH")
+        assert all(
+            value.endswith("üüüü\r\n".encode("utf-8"))
+            for key, value in fetched_email[2]
+            if key == b"RFC822"
+        )
+
+
+@pytest.mark.asyncio
+async def test_create_delete(greenmail):
+    async with ImapClient(greenmail.imap) as client:
+        try:
+            await client.delete("new mailbox")
+        except ImapServerError:
+            pass
+
+        try:
+            await client.create("new mailbox")
+            assert await client.select("new mailbox") == 0
+        finally:
+            await client.select("INBOX")
+            await client.delete("new mailbox")
+
+        with pytest.raises(ImapServerError):
+            await client.select("new mailbox")
+
+
+@pytest.mark.asyncio
+async def test_create_if_not_exists(greenmail):
+    async with ImapClient(greenmail.imap) as client:
+        try:
+            await client.create_if_not_exists("new mailbox")
+            await client.create_if_not_exists("new mailbox")
+            assert await client.select("new mailbox") == 0
+        finally:
+            await client.select("INBOX")
+            await client.delete("new mailbox")
+
+
+@pytest.mark.asyncio
+async def test_uid_copy(greenmail):
+    await send_email(create_minimal_email(greenmail.imap.username), greenmail.smtp)
+    async with ImapClient(greenmail.imap) as client:
+        try:
+            await client.create_if_not_exists("destination")
+            assert await client.select("INBOX") == 1
+            await client.fetch(b"1:1", b"(UID)")
+            fetched_email = await wait_for(client.fetched_queue.get(), 5)
+            assert fetched_email[:2] == (1, b"FETCH")
+            uid = [value for key, value in fetched_email[2] if key == b"UID"][0]
+            await client.uid_copy(uid, "destination")
+            assert await client.select("destination") == 1
+        finally:
+            await client.select("INBOX")
+            await client.delete("destination")
+
+
+@pytest.mark.asyncio
+@pytest.mark.xfail(reason="Feature not supported by Greenmail.", run=False)
+async def test_uid_move(greenmail):
+    await send_email(create_minimal_email(greenmail.imap.username), greenmail.smtp)
+    async with ImapClient(greenmail.imap) as client:
+        try:
+            await client.create_if_not_exists("destination")
+            assert await client.select("INBOX") == 1
+            await client.fetch(b"1:1", b"(UID)")
+            fetched_email = await wait_for(client.fetched_queue.get(), 5)
+            assert fetched_email[:2] == (1, b"FETCH")
+            uid = [value for key, value in fetched_email[2] if key == b"UID"][0]
+            await client.uid_move(uid, "destination")
+            assert client.num_exists == 0
+            assert await client.select("destination") == 1
+        finally:
+            await client.select("INBOX")
+            await client.delete("destination")
+
+
+@pytest.mark.asyncio
+async def test_uid_move_graceful(greenmail):
+    await send_email(create_minimal_email(greenmail.imap.username), greenmail.smtp)
+    async with ImapClient(greenmail.imap) as client:
+        try:
+            await client.create_if_not_exists("destination")
+            assert await client.select("INBOX") == 1
+            await client.fetch(b"1:1", b"(UID)")
+            fetched_email = await wait_for(client.fetched_queue.get(), 5)
+            assert fetched_email[:2] == (1, b"FETCH")
+            uid = [value for key, value in fetched_email[2] if key == b"UID"][0]
+            await client.uid_move_graceful(uid, "destination")
+            assert client.num_exists == 0
+            assert await client.select("destination") == 1
+        finally:
+            await client.select("INBOX")
+            await client.delete("destination")
+
+
+@pytest.mark.asyncio
+async def test_uid_store(greenmail):
+    await send_email(create_minimal_email(greenmail.imap.username), greenmail.smtp)
+    async with ImapClient(greenmail.imap) as client:
+        assert await client.select("INBOX") == 1
+        await client.fetch(b"1:1", b"(UID)")
+        fetched_email = await wait_for(client.fetched_queue.get(), 5)
+        assert fetched_email[:2] == (1, b"FETCH")
+        uid = [value for key, value in fetched_email[2] if key == b"UID"][0]
+        await client.uid_store(uid, rb"+FLAGS (\Deleted)")
+
+        fetched_email = await wait_for(client.fetched_queue.get(), 5)
+        assert fetched_email[:2] == (1, b"FETCH")
+        assert [value for key, value in fetched_email[2] if key == b"FLAGS"][0] == (
+            b"\\Deleted",
+        )
+
+
+@pytest.mark.asyncio
+async def test_uid_expunge(greenmail):
+    await send_email(create_minimal_email(greenmail.imap.username), greenmail.smtp)
+    async with ImapClient(greenmail.imap) as client:
+        assert await client.select("INBOX") == 1
+        await client.fetch(b"1:1", b"(UID)")
+        fetched_email = await wait_for(client.fetched_queue.get(), 5)
+        assert fetched_email[:2] == (1, b"FETCH")
+        uid = [value for key, value in fetched_email[2] if key == b"UID"][0]
+        await client.uid_store(uid, rb"+FLAGS (\Deleted)")
+        await client.expunge()
+        assert client.num_exists == 0
+
+
+@pytest.mark.asyncio
+async def test_executes_same_command_type_sequentially():
+    continue_triggers_change = Condition()
+    continue_triggers = []
+
+    async def select_handler(_: StreamWriter):
+        continue_event = Event()
+        async with continue_triggers_change:
+            continue_triggers.append(continue_event)
+            continue_triggers_change.notify_all()
+        await continue_event.wait()
+
+    async with MockImapServer(
+        host="localhost", port=4143, command_handlers={b"SELECT": select_handler}
+    ) as mock_server:
+        async with ImapClient(mock_server.connection_config) as client:
+            select_tasks = [
+                create_task(client.select("INBOX")),
+                create_task(client.select("foo")),
+            ]
+            async with continue_triggers_change:
+                await asyncio.wait_for(
+                    continue_triggers_change.wait_for(
+                        lambda: len(continue_triggers) >= 1
+                    ),
+                    timeout=5,
+                )
+            assert len(continue_triggers) == 1
+            continue_triggers[0].set()
+            # Lambda required because list access must be revaluated each time
+            # pylint: disable=unnecessary-lambda
+            await try_until_success(lambda: continue_triggers[1].set())
+            await asyncio.gather(*select_tasks)
+
+
+@pytest.mark.asyncio
+async def test_executes_different_commands_in_parallel():
+    continue_fetch = Event()
+    continue_store = Event()
+    num_commands_received_condition = Condition()
+    num_commands_received = 0
+
+    async def fetch_handler(_: StreamWriter):
+        nonlocal num_commands_received
+        logger.debug("fetch handle")
+        async with num_commands_received_condition:
+            num_commands_received += 1
+            num_commands_received_condition.notify_all()
+        await continue_fetch.wait()
+
+    async def store_handler(_: StreamWriter):
+        nonlocal num_commands_received
+        logger.debug("store handle")
+        async with num_commands_received_condition:
+            num_commands_received += 1
+            num_commands_received_condition.notify_all()
+        await continue_store.wait()
+
+    async with MockImapServer(
+        host="localhost",
+        port=4143,
+        command_handlers={b"FETCH": fetch_handler, b"UID": store_handler},
+    ) as mock_server:
+        async with ImapClient(mock_server.connection_config) as client:
+            await client.select("INBOX")
+            tasks = [
+                create_task(client.fetch(b"1", b"(UID)")),
+                create_task(client.uid_store(123, rb"+FLAGS (\Seen)")),
+            ]
+            async with num_commands_received_condition:
+                await asyncio.wait_for(
+                    num_commands_received_condition.wait_for(
+                        lambda: num_commands_received >= 2
+                    ),
+                    timeout=5,
+                )
+            continue_store.set()
+            continue_fetch.set()
+            await asyncio.gather(*tasks)
+
+
+@pytest.mark.asyncio
+async def test_timeout_behavior_waiting_for_server_ready():
+    event = Event()
+
+    async def client_connected_cb(reader: StreamReader, writer: StreamWriter):
+        await event.wait()
+
+    server = await start_server(client_connected_cb, host="localhost", port=4143)
+
+    async def connect():
+        try:
+            async with ImapClient(
+                ConnectionConfig(
+                    "username",
+                    "password",
+                    host="localhost",
+                    port=4143,
+                    use_ssl=False,
+                ),
+                timeout_seconds=0.2,
+            ):
+                pass
+        except asyncio.TimeoutError:
+            pass
+
+    async with server:
+        await asyncio.wait_for(connect(), timeout=1)
+        event.set()
+
+
+@pytest.mark.asyncio
+async def test_command_timeout_no_response_at_all():
+    async def select_handler():
+        return True
+
+    async with MockImapServer(
+        host="localhost",
+        port=4143,
+        command_handlers={b"SELECT": select_handler},
+    ) as mock_server:
+        async with ImapClient(
+            mock_server.connection_config,
+            timeout_seconds=0.2,
+        ) as client:
+
+            async def run_command():
+                try:
+                    await client.select()
+                except asyncio.TimeoutError:
+                    pass
+
+        await asyncio.wait_for(run_command(), timeout=1)
+
+
+@pytest.mark.asyncio
+async def test_command_timeout_single_untagged_response_only():
+    async def select_handler(writer: StreamWriter):
+        writer.write(b"* 42 EXISTS\r\n")
+        await writer.drain()
+        return True
+
+    async with MockImapServer(
+        host="localhost",
+        port=4143,
+        command_handlers={b"SELECT": select_handler},
+    ) as mock_server:
+        async with ImapClient(
+            mock_server.connection_config,
+            timeout_seconds=0.2,
+        ) as client:
+
+            async def run_command():
+                try:
+                    await client.select()
+                except asyncio.TimeoutError:
+                    pass
+
+        await asyncio.wait_for(run_command(), timeout=1)
+
+
+@pytest.mark.asyncio
+async def test_command_not_timing_out_if_interresponse_time_stays_below_threshold():
+    async def select_handler(writer: StreamWriter):
+        await asyncio.sleep(0.1)
+        writer.write(b"* 42 EXISTS\r\n")
+        await writer.drain()
+        await asyncio.sleep(0.1)
+        writer.write(b"* 42 RECENT\r\n")
+        await writer.drain()
+        await asyncio.sleep(0.1)
+        writer.write(b"* OK UNSEEN 23\r\n")
+        await writer.drain()
+        await asyncio.sleep(0.1)
+
+    async with MockImapServer(
+        host="localhost",
+        port=4143,
+        command_handlers={b"SELECT": select_handler},
+    ) as mock_server:
+        async with ImapClient(
+            mock_server.connection_config,
+            timeout_seconds=0.2,
+        ) as client:
+            assert await client.select() == 42
+
+
+class MockImapServer:
+    def __init__(
+        self,
+        host: str = "localhost",
+        port: int = 4143,
+        command_handlers: Dict[bytes, Callable[[StreamWriter], Coroutine]] = None,
+    ):
+        self.host = host
+        self.port = port
+        self.command_handlers = command_handlers or {}
+        self._server = None
+        self._write_lock = asyncio.Lock()
+
+    @property
+    def connection_config(self) -> ConnectionConfig:
+        return ConnectionConfig(
+            "username", "password", self.host, self.port, use_ssl=False
+        )
+
+    async def __aenter__(self):
+        self._server = await start_server(
+            self._client_connected_cb, host="localhost", port=4143
+        )
+        await self._server.__aenter__()
+        return self
+
+    async def __aexit__(self, exc_type, exc, traceback):
+        return await self._server.__aexit__(exc_type, exc, traceback)
+
+    async def _client_connected_cb(self, reader: StreamReader, writer: StreamWriter):
+        writer.write(b"* OK hello\r\n")
+        await writer.drain()
+
+        while not reader.at_eof():
+            line = await reader.readline()
+            logger.debug("MockImapServer received: %s", line)
+            parsed = re.match(
+                rb"^(?P<tag>\w+)\s+(?P<command>\w+)(?P<remainder>.*)$", line
+            )
+            if not parsed:
+                continue
+            tag, command, remainder = (
+                parsed.group("tag"),
+                parsed.group("command"),
+                parsed.group("remainder") + b"\n",
+            )
+            async with self._write_lock:
+                while remainder.endswith(b"}\r\n"):
+                    writer.write(b"+ OK continue\r\n")
+                    await writer.drain()
+                    remainder += await reader.readline()
+
+            asyncio.create_task(self._finish_command_handling(tag, command, writer))
+
+    async def _finish_command_handling(
+        self, tag: bytes, command: bytes, writer: StreamWriter
+    ):
+        handled = False
+        suppress_tagged_response = False
+        if command in self.command_handlers:
+            handled = True
+            suppress_tagged_response = await self.command_handlers[command](writer)
+
+        async with self._write_lock:
+            if handled:
+                pass
+            elif command == b"CAPABILITY":
+                writer.write(b"* CAPABILITY IMAP4rev1\r\n")
+            elif command == b"LOGOUT":
+                writer.write(b"* BYE see you soon\r\n")
+
+            if not suppress_tagged_response:
+                writer.write(b" ".join((tag, b"OK", command, b"completed\r\n")))
+                if command == b"LOGOUT":
+                    writer.write_eof()
+            await writer.drain()
diff --git a/dmarc_metrics_exporter/tests/test_imap_parser.py b/dmarc_metrics_exporter/tests/test_imap_parser.py
index 15a5186..b14de31 100644
--- a/dmarc_metrics_exporter/tests/test_imap_parser.py
+++ b/dmarc_metrics_exporter/tests/test_imap_parser.py
@@ -1,177 +1,295 @@
+import bite
 import pytest
+from bite.parse_functions import parse_bytes
 
-from dmarc_metrics_exporter.imap_parser import fetch_response_line, string
+from dmarc_metrics_exporter.imap_parser import (
+    fetch_response_line,
+    response,
+    response_tagged,
+    string,
+)
 
 
+@pytest.mark.asyncio
 @pytest.mark.parametrize(
-    "given_string, expected",
+    "given_input, expected",
     [
-        ('"quoted string"', "quoted string"),
-        ("{14}\r\nliteral string", "literal string"),
-        ("{13}\r\nwith\r\nnewline", "with\r\nnewline"),
+        (b'"quoted string"', b"quoted string"),
+        (b"{14}\r\nliteral string", b"literal string"),
+        (b"{13}\r\nwith\r\nnewline", b"with\r\nnewline"),
     ],
 )
-def test_parses_strings(given_string, expected):
-    assert string.parse_string(given_string).as_list() == [expected]
+async def test_parses_strings(given_input, expected):
+    parse_tree = await parse_bytes(string, given_input, parse_all=True)
+    assert parse_tree.values == (expected,)
 
 
 RFC822_HEADER = (
-    "Return-Path: <sender@some-domain.org>\r\n"
-    "Received: from 172.10.0.1 "
-    "(HELO xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx); "
-    "Wed Jan 05 10:26:45 UTC 2022\r\n"
-    'Content-Type: text/plain; charset="utf-8"\r\n'
-    "Content-Transfer-Encoding: 7bit\r\n"
-    "MIME-Version: 1.0\r\n"
-    "Subject: Message subject\r\n"
-    "From: sender@some-domain.org\r\n"
-    "To: queue@localhost\r\n\r\n"
+    b"Return-Path: <sender@some-domain.org>\r\n"
+    b"Received: from 172.10.0.1 "
+    b"(HELO xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx); "
+    b"Wed Jan 05 10:26:45 UTC 2022\r\n"
+    b'Content-Type: text/plain; charset="utf-8"\r\n'
+    b"Content-Transfer-Encoding: 7bit\r\n"
+    b"MIME-Version: 1.0\r\n"
+    b"Subject: Message subject\r\n"
+    b"From: sender@some-domain.org\r\n"
+    b"To: queue@localhost\r\n\r\n"
 )
-RFC822_BODY = "message content\r\n"
-RFC822_MESSAGE = f"{RFC822_HEADER}{RFC822_BODY}"
+RFC822_BODY = b"message content\r\n"
+RFC822_MESSAGE = RFC822_HEADER + RFC822_BODY
 
 
+@pytest.mark.asyncio
 @pytest.mark.parametrize(
-    "given_string, expected",
+    "given_input, expected",
     [
         # RFC822 body at end
         (
-            f"1 FETCH (FLAGS (\\Seen) UID 6 RFC822 {{{len(RFC822_MESSAGE)}}}\r\n"
+            f"1 FETCH (FLAGS (\\Seen) UID 6 RFC822 {{{len(RFC822_MESSAGE)}}}\r\n".encode(
+                "ascii"
+            )
             + RFC822_MESSAGE
-            + ")\r\n",
-            [
+            + b")",
+            (
                 1,
-                "FETCH",
-                [
-                    ["FLAGS", ["\\Seen"]],
-                    ["UID", 6],
-                    ["RFC822", RFC822_MESSAGE],
-                ],
-            ],
+                b"FETCH",
+                (
+                    (b"FLAGS", (b"\\Seen",)),
+                    (b"UID", 6),
+                    (b"RFC822", RFC822_MESSAGE),
+                ),
+            ),
         ),
         # RFC822 body followed by flags
         (
-            f"2 FETCH (UID 7 RFC822 {{{len(RFC822_MESSAGE)}}}\r\n"
+            f"2 FETCH (UID 7 RFC822 {{{len(RFC822_MESSAGE)}}}\r\n".encode("ascii")
             + RFC822_MESSAGE
-            + "FLAGS (\\Seen))\r\n",
-            [
+            + b" FLAGS (\\Seen))",
+            (
                 2,
-                "FETCH",
-                [
-                    ["UID", 7],
-                    ["RFC822", RFC822_MESSAGE],
-                    ["FLAGS", ["\\Seen"]],
-                ],
-            ],
+                b"FETCH",
+                (
+                    (b"UID", 7),
+                    (b"RFC822", RFC822_MESSAGE),
+                    (b"FLAGS", (b"\\Seen",)),
+                ),
+            ),
         ),
         # Test each allowed pair
+        # (body is not actually reproduced in the parsed value)
         (
-            '4 FETCH (BODY ("MESSAGE" "text/html" '
-            '("a" "b(") "body-fld-id" "body-fld-desc" "8BIT" 123))\r\n',
-            [
+            b'4 FETCH (BODY ("MESSAGE" "text/html" '
+            b'("a" "b(") "body-fld-id" "body-fld-desc" "8BIT" 123))',
+            (
                 4,
-                "FETCH",
-                [
-                    [
-                        "BODY",
-                        [
-                            "MESSAGE",
-                            "text/html",
-                            ["a", "b("],
-                            "body-fld-id",
-                            "body-fld-desc",
-                            "8BIT",
-                            "123",
-                        ],
-                    ]
-                ],
-            ],
+                b"FETCH",
+                ((b"BODY", ()),),
+            ),
         ),
         (
-            f"5 FETCH (BODY[] {{{len(RFC822_MESSAGE)}}}\r\n{RFC822_MESSAGE})\r\n",
-            [5, "FETCH", [[["BODY", ""], RFC822_MESSAGE]]],
+            f"5 FETCH (BODY[] {{{len(RFC822_MESSAGE)}}}\r\n".encode("ascii")
+            + RFC822_MESSAGE
+            + b")",
+            (5, b"FETCH", ((b"BODY", (), RFC822_MESSAGE),)),
         ),
         (
-            f"6 FETCH (BODY[]<42> {{{len(RFC822_MESSAGE) - 42}}}\r\n{RFC822_MESSAGE[42:]}))\r\n",
-            [6, "FETCH", [[["BODY", "", 42], RFC822_MESSAGE[42:]]]],
+            f"6 FETCH (BODY[]<42> {{{len(RFC822_MESSAGE) - 42}}}\r\n".encode("ascii")
+            + RFC822_MESSAGE[42:]
+            + b")",
+            (6, b"FETCH", (((b"BODY", (), 42, RFC822_MESSAGE[42:])),)),
         ),
         (
-            f"7 FETCH (BODY[HEADER] {{{len(RFC822_HEADER)}}}\r\n{RFC822_HEADER})\r\n",
-            [7, "FETCH", [[["BODY", "HEADER"], RFC822_HEADER]]],
+            f"7 FETCH (BODY[HEADER] {{{len(RFC822_HEADER)}}}\r\n".encode("ascii")
+            + RFC822_HEADER
+            + b")",
+            (7, b"FETCH", ((b"BODY", (b"HEADER",), RFC822_HEADER),)),
         ),
         (
-            '8 FETCH (BODYSTRUCTURE ("MESSAGE" "text/html" '
-            '("a" "b(") "body-fld-id" "body-fld-desc" "8BIT" 123))\r\n',
-            [
+            b'8 FETCH (BODYSTRUCTURE ("MESSAGE" "text/html" '
+            b'("a" "b(") "body-fld-id" "body-fld-desc" "8BIT" 123))',
+            (
                 8,
-                "FETCH",
-                [
-                    [
-                        "BODYSTRUCTURE",
-                        [
-                            "MESSAGE",
-                            "text/html",
-                            ["a", "b("],
-                            "body-fld-id",
-                            "body-fld-desc",
-                            "8BIT",
-                            "123",
-                        ],
-                    ]
-                ],
-            ],
-        ),
-        (
-            '9 FETCH (ENVELOPE ("date" "subject" '
-            '(("from" NIL "from" "example.com")) '
-            '(("sender" NIL "sender" "example.com")) NIL '
-            '(("to" NIL "to" "example.com")) NIL NIL NIL "message-id"'
-            "))\r\n",
-            [
+                b"FETCH",
+                ((b"BODYSTRUCTURE", ()),),
+            ),
+        ),
+        (
+            b'9 FETCH (ENVELOPE ("date" "subject" '
+            b'(("from" NIL "from" "example.com")) '
+            b'(("sender" NIL "sender" "example.com")) NIL '
+            b'(("to" NIL "to" "example.com")) NIL NIL NIL "message-id"'
+            b"))",
+            (
                 9,
-                "FETCH",
-                [
-                    [
-                        "ENVELOPE",
-                        [
-                            "date",
-                            "subject",
-                            [["from", "NIL", "from", "example.com"]],
-                            [["sender", "NIL", "sender", "example.com"]],
-                            "NIL",
-                            [["to", "NIL", "to", "example.com"]],
-                            "NIL",
-                            "NIL",
-                            "NIL",
-                            "message-id",
-                        ],
-                    ]
-                ],
-            ],
+                b"FETCH",
+                (
+                    (
+                        b"ENVELOPE",
+                        (
+                            b"date",
+                            b"subject",
+                            ((b"from", b"NIL", b"from", b"example.com"),),
+                            ((b"sender", b"NIL", b"sender", b"example.com"),),
+                            b"NIL",
+                            ((b"to", b"NIL", b"to", b"example.com"),),
+                            b"NIL",
+                            b"NIL",
+                            b"NIL",
+                            b"message-id",
+                        ),
+                    ),
+                ),
+            ),
+        ),
+        (
+            b"10 FETCH (FLAGS (\\Seen \\Foo))",
+            (10, b"FETCH", ((b"FLAGS", (b"\\Seen", b"\\Foo")),)),
+        ),
+        (
+            b'11 FETCH (INTERNALDATE " 2-Mar-2022 12:34:56 +0200")',
+            (11, b"FETCH", ((b"INTERNALDATE", b" 2-Mar-2022 12:34:56 +0200"),)),
+        ),
+        (
+            f"12 FETCH (RFC822 {{{len(RFC822_MESSAGE)}}}\r\n".encode("ascii")
+            + RFC822_MESSAGE
+            + b")",
+            (12, b"FETCH", ((b"RFC822", RFC822_MESSAGE),)),
+        ),
+        (
+            f"13 FETCH (RFC822.HEADER {{{len(RFC822_HEADER)}}}\r\n".encode("ascii")
+            + RFC822_HEADER
+            + b")",
+            (13, b"FETCH", ((b"RFC822.HEADER", RFC822_HEADER),)),
+        ),
+        (
+            b"14 FETCH (RFC822.SIZE 12345)",
+            (14, b"FETCH", ((b"RFC822.SIZE", 12345),)),
+        ),
+        (
+            f"15 FETCH (RFC822.TEXT {{{len(RFC822_BODY)}}}\r\n".encode("ascii")
+            + RFC822_BODY
+            + b")",
+            (15, b"FETCH", ((b"RFC822.TEXT", RFC822_BODY),)),
+        ),
+        (
+            b"16 FETCH (UID 42)",
+            (
+                16,
+                b"FETCH",
+                ((b"UID", 42),),
+            ),
+        ),
+        (
+            b"1 FETCH (FLAGS (\\Seen) BODY[HEADER.FIELDS (SUBJECT)] {24}\r\n"
+            b"Subject: Minimal email\r\n)",
+            (
+                1,
+                b"FETCH",
+                (
+                    (b"FLAGS", (b"\\Seen",)),
+                    (
+                        b"BODY",
+                        (b"HEADER.FIELDS", (b"SUBJECT",)),
+                        b"Subject: Minimal email\r\n",
+                    ),
+                ),
+            ),
         ),
+    ],
+)
+async def test_parses_fetch_response_line(given_input, expected):
+    parse_tree = await parse_bytes(fetch_response_line, given_input, parse_all=True)
+    assert parse_tree.values == expected
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+    "given_input, expected",
+    [
+        (b"tag123 OK some text", (b"tag123", b"OK", (b"some text"))),
         (
-            "10 FETCH (FLAGS (\\Seen \\Foo))\r\n",
-            [10, "FETCH", [["FLAGS", ["\\Seen", "\\Foo"]]]],
+            b"tag123 OK [UIDNEXT 456] some text",
+            (
+                b"tag123",
+                b"OK",
+                (b"UIDNEXT", 456),
+                b"some text",
+            ),
         ),
         (
-            '11 FETCH (INTERNALDATE " 2-Mar-2022 12:34:56 +0200")\r\n',
-            [11, "FETCH", [["INTERNALDATE", " 2-Mar-2022 12:34:56 +0200"]]],
+            b"tag123 OK [BADCHARSET ({8}\r\nfoo\r\nbar)] some text",
+            (
+                b"tag123",
+                b"OK",
+                (
+                    b"BADCHARSET",
+                    (b"foo\r\nbar",),
+                ),
+                b"some text",
+            ),
         ),
         (
-            f"12 FETCH (RFC822 {{{len(RFC822_MESSAGE)}}}\r\n{RFC822_MESSAGE})\r\n",
-            [12, "FETCH", [["RFC822", RFC822_MESSAGE]]],
+            b"tag123 OK [FOO 123] some text",
+            (b"tag123", b"OK", (b"FOO", b"123"), b"some text"),
+        ),
+    ],
+)
+async def test_parses_tagged_response_line(given_input, expected):
+    parse_tree = await parse_bytes(response_tagged, given_input, parse_all=True)
+    assert parse_tree.values == expected
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+    "given_input",
+    [
+        b"tag123 OK [BADCHARSET ({8}\r\n",
+        b"tag123 OK [BADCHARSET ({8}\r\nfoo\r\n",
+    ],
+)
+async def test_parses_tagged_response_line_exceptions(given_input):
+    with pytest.raises(bite.ParseError):
+        await parse_bytes(response_tagged, given_input)
+
+
+@pytest.mark.asyncio
+async def test_continue_response():
+    given_input = b"+ foobar\r\n"
+    parse_tree = await parse_bytes(response, given_input, parse_all=True)
+    assert parse_tree.values == (b"+", b"foobar")
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+    "given_input,expected",
+    [
+        (
+            b"* OK IMAP4rev1 Server GreenMail v1.6.5 ready\r\n",
+            (
+                b"*",
+                b"OK",
+                b"IMAP4rev1 Server GreenMail v1.6.5 ready",
+            ),
         ),
+        (b"* BYE cu later alligator\r\n", (b"*", b"BYE", b"cu later alligator")),
         (
-            f"13 FETCH (RFC822.HEADER {{{len(RFC822_HEADER)}}}\r\n{RFC822_HEADER})\r\n",
-            [13, "FETCH", [["RFC822.HEADER", RFC822_HEADER]]],
+            b"* CAPABILITY IMAP4rev1 LITERAL+ SORT UIDPLUS IDLE QUOTA\r\n",
+            (
+                b"*",
+                b"CAPABILITY",
+                b"IMAP4rev1 LITERAL+ SORT UIDPLUS IDLE QUOTA",
+            ),
         ),
-        ("14 FETCH (RFC822.SIZE 12345)\r\n", [14, "FETCH", [["RFC822.SIZE", 12345]]]),
+        (b"* 1 EXISTS\r\n", (b"*", 1, b"EXISTS")),
+        (b"* 2 EXPUNGE\r\n", (b"*", 2, b"EXPUNGE")),
+        (b"* 3 FETCH (UID 42)\r\n", (b"*", 3, b"FETCH", ((b"UID", 42),))),
         (
-            f"15 FETCH (RFC822.TEXT {{{len(RFC822_BODY)}}}\r\n{RFC822_BODY})\r\n",
-            [15, "FETCH", [["RFC822.TEXT", RFC822_BODY]]],
+            b'* foo {10}\r\n0123456789 "xyz" (A B C)\r\n',
+            (b"*", b"foo ", b"0123456789", b' "xyz" (A B C)'),
         ),
-        ("16 FETCH (UID 42)\r\n", [16, "FETCH", [["UID", 42]]]),
     ],
 )
-def test_parses_fetch_response_line(given_string, expected):
-    assert fetch_response_line.parse_string(given_string).as_list() == expected
+async def test_untagged_response(given_input, expected):
+    parse_tree = await parse_bytes(response, given_input, parse_all=True)
+    assert parse_tree.values == expected
diff --git a/dmarc_metrics_exporter/tests/test_imap_queue.py b/dmarc_metrics_exporter/tests/test_imap_queue.py
index c6b1ef4..49bb1d7 100644
--- a/dmarc_metrics_exporter/tests/test_imap_queue.py
+++ b/dmarc_metrics_exporter/tests/test_imap_queue.py
@@ -3,7 +3,7 @@
 
 import pytest
 
-from dmarc_metrics_exporter.imap_queue import ImapClient, ImapQueue, ImapServerError
+from dmarc_metrics_exporter.imap_queue import ImapClient, ImapQueue
 
 from .conftest import send_email, try_until_success, verify_email_delivered
 
@@ -139,13 +139,3 @@ async def handler(queue_msg: EmailMessage, is_done=is_done):
     finally:
         if queue is not None:
             await queue.stop_consumer()
-
-
-@pytest.mark.asyncio
-async def test_create_if_not_exists(greenmail):
-    async with ImapClient(greenmail.imap) as client:
-        await client.create_if_not_exists("MyRandomDir")
-
-        assert (await client.select("MyRandomDir")) == 0
-        with pytest.raises(ImapServerError):
-            await client.select("MyRandomDirNotExist")