diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8bbedc3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,389 @@
+# Created by https://www.toptal.com/developers/gitignore/api/python,windows,macos,linux,virtualenv,intellij,visualstudiocode
+# Edit at https://www.toptal.com/developers/gitignore?templates=python,windows,macos,linux,virtualenv,intellij,visualstudiocode
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### Intellij Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+# *.iml
+# modules.xml
+# .idea/misc.xml
+# *.ipr
+
+# Sonarlint plugin
+# https://plugins.jetbrains.com/plugin/7973-sonarlint
+.idea/**/sonarlint/
+
+# SonarQube Plugin
+# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
+.idea/**/sonarIssues.xml
+
+# Markdown Navigator plugin
+# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
+.idea/**/markdown-navigator.xml
+.idea/**/markdown-navigator-enh.xml
+.idea/**/markdown-navigator/
+
+# Cache file creation bug
+# See https://youtrack.jetbrains.com/issue/JBR-2257
+.idea/$CACHE_FILE$
+
+# CodeStream plugin
+# https://plugins.jetbrains.com/plugin/12206-codestream
+.idea/codestream.xml
+
+# Azure Toolkit for IntelliJ plugin
+# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
+.idea/**/azureSettings.xml
+
+### Linux ###
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### macOS Patch ###
+# iCloud generated files
+*.icloud
+
+### Python ###
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
+### Python Patch ###
+# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
+poetry.toml
+
+
+### VirtualEnv ###
+# Virtualenv
+# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
+[Bb]in
+[Ii]nclude
+[Ll]ib
+[Ll]ib64
+[Ll]ocal
+[Ss]cripts
+pyvenv.cfg
+pip-selfcheck.json
+
+### VisualStudioCode ###
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+!.vscode/*.code-snippets
+
+# Local History for Visual Studio Code
+.history/
+
+# Built Visual Studio Code Extensions
+*.vsix
+
+### VisualStudioCode Patch ###
+# Ignore all local history of files
+.history
+.ionide
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# End of https://www.toptal.com/developers/gitignore/api/python,windows,macos,linux,virtualenv,intellij,visualstudiocode
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/fast-s3-url.iml b/.idea/fast-s3-url.iml
new file mode 100644
index 0000000..ca6b55d
--- /dev/null
+++ b/.idea/fast-s3-url.iml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..dd4c951
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..f2d3430
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..c437521
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3853aa2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+.PHONY: benchmark
+benchmark:
+ pytest test/perf \
+ --benchmark-group-by="group,param:object_key_count" \
+ --benchmark-warmup=on \
+ --benchmark-warmup-iterations=10 \
+ --benchmark-disable-gc
+
+.PHONY: unit-test
+unit-test:
+ pytest test/unit --cov=fast_s3_url --cov-report xml:coverage.xml --cov-report term
+
+.PHONY: integration-test
+integration-test:
+ pytest test/integration --cov=fast_s3_url --cov-report xml:coverage.xml --cov-report term
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b6404cf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,137 @@
+# fast_s3_url
+
+An S3 URL generator that's (much) faster than boto3/aiobotocore's, while being compliant with the s3v4 signed URL format. How much faster? [See below](#benchmarks).
+
+Largely based upon: https://github.com/WeTransfer/wt_s3_signer.
+
+## Important notice
+
+This library, much like the one it's based upon, skips several steps that boto3 and aiobotocore do. As a result, this library should be used one very specific use case only -- when you need to generate thousands of signed URLs fast, and most importantly, when you control the object keys themselves.
+
+For any other use case scenario, please use boto3 or aiobotocore instead.
+
+## Installation
+
+After making sure this is for you, with your favourite package manager (e.g., pip):
+
+```shell
+pip install fast-s3-url
+```
+
+## Recommended reading
+
+- [Speeding up S3 URL generation in ruby](https://bibwild.wordpress.com/2020/08/26/speeding-up-s3-url-generation-in-ruby/)
+- [Super fast signing of S3 URLs](https://wetransfer.com/engineering/super-fast-signing-of-s3-urls)
+- [Authenticating Requests: Using Query Parameters (AWS Signature Version 4)](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html)
+
+## Usage
+
+Note: This library does not depend on boto3 or aiobotocore explicitly. If you want to use it without them, you can create an instance directly (see below).
+
+### With boto3
+
+Use `FastS3UrlSigner.from_boto3_client` to create an instance from an existing (and properly configured) client.
+
+```python
+client = boto3.client("s3")
+
+signer = FastS3UrlSigner.from_boto3_client(client, bucket="test")
+
+object_keys = ["cat.jpg", ...]
+
+signed_urls = signer.generate_presigned_get_object_urls(object_keys, expires_in=3600)
+```
+
+### With aiobotocore
+
+Use `FastS3UrlSigner.from_aiobotocore_client` to create an instance from an existing (and properly configured) client. Note that unlike the boto3 variant, this method needs to be `await`ed.
+
+```python
+session = get_session()
+
+async with session.create_client("s3") as client:
+ signer = await FastS3UrlSigner.from_aiobotocore_client(client, "test")
+
+ object_keys = ["cat.jpg", ...]
+
+ signed_urls = signer.generate_presigned_get_object_urls(object_keys, expires_in=3600)
+```
+
+### Manually creating an object
+
+The following example assumes you have a [MinIO](https://min.io/) instance running on http://localhost:9000 with default settings.
+
+Note that you shouldn't hardcode credentials, this is merely for illustration purposes.
+
+```python
+signer = FastS3UrlSigner(
+ bucket_endpoint_url="http://localhost:9000/my-bucket/",
+ aws_region="us-east-1",
+ access_key_id="minioadmin",
+ secret_access_key="minioadmin",
+)
+```
+
+## Local development
+
+This project uses [Poetry](https://python-poetry.org/) for dependency management. Make sure you have it installed.
+
+You can then create a Virtual Environment with it (any Python version >= 3.8 will do), and set up a shell. See Poetry's documentation for tutorials on how to do this.
+
+### Running tests
+
+If you have `make` installed, the following targets are set up for you:
+
+- `unit-test`: Runs unit tests. Doesn't require any S3 instance.
+- `integration-test`: Runs integration tests. Requires an S3 instance running on `http://localhost:9000`. You can use MinIO to create one.
+
+Note: These targets assume you are running a shell with a Poetry Virtual Environment set up. They do not spawn a shell for running the commands.
+
+## Benchmarks
+
+The following results are from Python 3.11 on Fedora 37 running on a Lenovo ThinkPad L390, with an Intel Core i7 8565U and 16 GB RAM. Turbo Boost was disabled because it quickly causes throttling and it can skew results.
+
+As shown below, depending on the number of URLs to generate, this library can be 6 times faster than the official libraries when generating a single URL, and over 30 times faster when generating 100 or more URLs.
+
+You can run the following to run the benchmarks with pytest-benchmark:
+
+```
+make benchmark
+```
+
+```text
+---------------------------------------------------------------- benchmark 'signed_url_generation object_key_count=1': 2 tests ----------------------------------------------------------------
+Name (time in us) Min Max Mean StdDev Median IQR Outliers OPS (Kops/s) Rounds Iterations
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+test_custom[1] 55.5300 (1.0) 103.7830 (1.0) 57.4275 (1.0) 2.1685 (1.0) 56.8480 (1.0) 0.6910 (1.0) 1187;1741 17.4133 (1.0) 16791 1
+test_boto3[1] 355.7460 (6.41) 492.3180 (4.74) 367.2364 (6.39) 6.9269 (3.19) 364.7620 (6.42) 10.6518 (15.41) 659;13 2.7230 (0.16) 2621 1
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+------------------------------------------------------------ benchmark 'signed_url_generation object_key_count=1024': 2 tests -----------------------------------------------------------
+Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+test_custom[1024] 11.3772 (1.0) 11.5678 (1.0) 11.4341 (1.0) 0.0283 (1.0) 11.4352 (1.0) 0.0308 (1.0) 19;2 87.4577 (1.0) 88 1
+test_boto3[1024] 427.2083 (37.55) 429.6503 (37.14) 427.9307 (37.43) 0.9811 (34.63) 427.6766 (37.40) 0.7958 (25.81) 1;1 2.3368 (0.03) 5 1
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+---------------------------------------------------------- benchmark 'signed_url_generation object_key_count=128': 2 tests -----------------------------------------------------------
+Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+test_custom[128] 1.4635 (1.0) 1.6152 (1.0) 1.4895 (1.0) 0.0110 (1.0) 1.4889 (1.0) 0.0114 (1.0) 131;11 671.3848 (1.0) 665 1
+test_boto3[128] 53.6779 (36.68) 55.7975 (34.54) 54.3269 (36.47) 0.6903 (63.01) 53.9923 (36.26) 0.9660 (85.04) 4;0 18.4071 (0.03) 19 1
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+------------------------------------------------------------ benchmark 'signed_url_generation object_key_count=512': 2 tests -------------------------------------------------------------
+Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+test_custom[512] 5.7369 (1.0) 5.8751 (1.0) 5.8002 (1.0) 0.0266 (1.0) 5.8028 (1.0) 0.0361 (1.0) 53;1 172.4078 (1.0) 172 1
+test_boto3[512] 215.7762 (37.61) 219.7181 (37.40) 217.4519 (37.49) 1.6630 (62.51) 217.8732 (37.55) 2.6384 (73.16) 2;0 4.5987 (0.03) 5 1
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+------------------------------------------------------------------- benchmark 'signed_url_generation object_key_count=8': 2 tests --------------------------------------------------------------------
+Name (time in us) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+test_custom[8] 133.2900 (1.0) 189.1850 (1.0) 137.3525 (1.0) 3.2919 (1.0) 136.2515 (1.0) 1.6770 (1.0) 1137;1124 7,280.5388 (1.0) 7182 1
+test_boto3[8] 2,829.3420 (21.23) 4,067.5490 (21.50) 2,869.7947 (20.89) 82.0675 (24.93) 2,854.3680 (20.95) 13.6910 (8.16) 16;43 348.4570 (0.05) 349 1
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+```
diff --git a/fast_s3_url/__init__.py b/fast_s3_url/__init__.py
new file mode 100644
index 0000000..45521e9
--- /dev/null
+++ b/fast_s3_url/__init__.py
@@ -0,0 +1,3 @@
+from .s3_signer import FastS3UrlSigner
+
+__all__ = ["FastS3UrlSigner"]
diff --git a/fast_s3_url/py.typed b/fast_s3_url/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/fast_s3_url/s3_signer.py b/fast_s3_url/s3_signer.py
new file mode 100644
index 0000000..8f7fbde
--- /dev/null
+++ b/fast_s3_url/s3_signer.py
@@ -0,0 +1,236 @@
+from __future__ import annotations
+from datetime import datetime, timezone
+import hashlib
+import hmac
+import secrets
+from urllib.parse import quote, urlparse, urlunparse
+
+from typing import TYPE_CHECKING, List, Optional
+
+
+if TYPE_CHECKING:
+ from types_aiobotocore_s3 import S3Client as AioBotocoreS3Client
+ from mypy_boto3_s3.client import S3Client as Boto3S3Client
+
+__all__ = ["FastS3UrlSigner"]
+
+
+class _Credentials:
+ access_key: str
+ secret_key: str
+ token: Optional[str]
+
+
+class FastS3UrlSigner:
+ """
+ Alternative signed URL generator for S3, compliant with the s3v4 format,
+ and faster than (aio)botocore or boto3 implementations,
+ for quickly generating lots of signed URLs at once.
+ """
+
+ def __init__(
+ self,
+ *,
+ bucket_endpoint_url: str,
+ access_key_id: str,
+ secret_access_key: str,
+ aws_region: Optional[str] = None,
+ session_token: Optional[str] = None,
+ ) -> None:
+ """
+ Create an instance of this object with the specified parameters.
+
+ :param bucket_endpoint_url: The URL to a bucket. Can be virtual-style, e.g.: 'https://my-bucket.s3.amazonaws.com/' or path-style, e.g.: 'https://s3.amazonaws.com/my-bucket/'.
+ :param access_key_id: The Access Key ID. Usually mapped to the `AWS_ACCESS_KEY_ID` environment variable.
+ :param secret_access_key: The Secret Access Key. Usually mapped to the `AWS_SECRET_ACCESS_KEY` environment variable.
+ :param aws_region: The AWS region that will be used to generate URLs with. Defaults to 'us-east-1'.
+ :param session_token: An STS session token. Usually mapped to the `AWS_SESSION_TOKEN` environment variable.
+ """
+
+ # Get the components from the bucket's endpoint URL.
+ # The canonical path must include the bucket's name if we're using path-style URLs,
+ # and we must ensure it's not repeated in the endpoint URL in these situations.
+ parsed_bucket_url = urlparse(bucket_endpoint_url)
+
+ self.canonical_uri_prefix = parsed_bucket_url.path.rstrip("/")
+ self.endpoint_url = urlunparse(parsed_bucket_url._replace(path="/")).rstrip("/")
+ self.bucket_host = parsed_bucket_url.netloc
+
+ self.region = aws_region or "us-east-1"
+ self.secret_key = secret_access_key
+ self.access_key = access_key_id
+ self.session_token = session_token
+
+ @classmethod
+ def from_boto3_client(
+ cls,
+ client: Boto3S3Client,
+ bucket_name: str,
+ ):
+ """
+ Create an instance from a boto3 S3 client, using its credentials.
+ """
+
+ # Generate a dummy URL to see how the client does it with the current configuration,
+ # rather than reverse-engineering the logic.
+ dummy_key = secrets.token_hex(8)
+
+ dummy_url: str = client.generate_presigned_url(
+ "get_object", Params={"Bucket": bucket_name, "Key": dummy_key}
+ )
+ bucket_endpoint_url = dummy_url[0 : dummy_url.index(dummy_key, 0)]
+
+ # Get the client's current credentials.
+ credentials: _Credentials = client._request_signer._credentials.get_frozen_credentials() # type: ignore
+
+ return cls(
+ aws_region=client.meta.region_name,
+ bucket_endpoint_url=bucket_endpoint_url,
+ access_key_id=credentials.access_key,
+ secret_access_key=credentials.secret_key,
+ session_token=credentials.token,
+ )
+
+ @classmethod
+ async def from_aiobotocore_client(
+ cls,
+ client: AioBotocoreS3Client,
+ bucket_name: str,
+ ):
+ """
+ Create an instance from an aiobotocore S3 client, using its credentials.
+ """
+
+ # Generate a dummy URL to see how the client does it with the current configuration,
+ # rather than reverse-engineering the logic.
+ dummy_key = secrets.token_hex(8)
+
+ dummy_url: str = await client.generate_presigned_url(
+ "get_object", Params={"Bucket": bucket_name, "Key": dummy_key}
+ )
+ bucket_endpoint_url = dummy_url[0 : dummy_url.index(dummy_key, 0)]
+
+ # Get the client's current credentials.
+ credentials: _Credentials = await client._request_signer._credentials.get_frozen_credentials() # type: ignore
+
+ return cls(
+ aws_region=client.meta.region_name,
+ bucket_endpoint_url=bucket_endpoint_url,
+ access_key_id=credentials.access_key,
+ secret_access_key=credentials.secret_key,
+ session_token=credentials.token,
+ )
+
+ def generate_presigned_get_object_urls(
+ self,
+ object_keys: List[str],
+ *,
+ expires_in: int = 3600,
+ ) -> List[str]:
+ """
+ Generate presigned URLs for objects.
+
+ :param object_keys: The object keys to sign.
+ :param expires_in: The validity of the signed URLs, in seconds. Defaults to 3600 (1h).
+ """
+
+ if not object_keys:
+ return []
+
+ if any(not k for k in object_keys):
+ raise ValueError("All 'object_keys' must be non-empty strings.")
+
+ now = datetime.now(tz=timezone.utc)
+
+ datestamp = now.strftime("%Y%m%d")
+ amz_date = now.strftime("%Y%m%dT%H%M%SZ")
+
+ signing_key = _derive_signing_key(
+ self.secret_key.encode(), datestamp, self.region, "s3"
+ )
+
+ canonical_headers = f"host:{self.bucket_host}\n"
+ signed_headers = "host"
+
+ algorithm = "AWS4-HMAC-SHA256"
+
+ credential_scope = f"{datestamp}/{self.region}/s3/aws4_request"
+
+ encoded_credential = quote(f"{self.access_key}/{credential_scope}", safe="~")
+
+ canonical_querystring_template_parts = [
+ f"X-Amz-Algorithm={algorithm}",
+ f"X-Amz-Credential={encoded_credential}",
+ f"X-Amz-Date={amz_date}",
+ f"X-Amz-Expires={expires_in}",
+ f"X-Amz-SignedHeaders={signed_headers}",
+ ]
+
+ if self.session_token:
+ canonical_querystring_template_parts.append(
+ f"X-Amz-Security-Token={quote(self.session_token)}"
+ )
+
+ # The query string parameters must be sorted by their name.
+ canonical_querystring_template = "&".join(
+ sorted(canonical_querystring_template_parts)
+ )
+
+ payload = "UNSIGNED-PAYLOAD"
+
+ signed_urls: List[str] = []
+
+ for object_key in object_keys:
+ # The object key must be URL encoded, where the only safe characters are
+ # '/' and '~'.
+ object_key = quote(object_key, safe="/~")
+
+ canonical_uri = f"{self.canonical_uri_prefix}/{object_key}"
+
+ canonical_request = "\n".join(
+ (
+ "GET",
+ canonical_uri,
+ canonical_querystring_template,
+ canonical_headers,
+ signed_headers,
+ payload,
+ )
+ )
+
+ string_to_sign = "\n".join(
+ (
+ algorithm,
+ amz_date,
+ credential_scope,
+ hashlib.sha256(canonical_request.encode("utf-8")).hexdigest(),
+ )
+ )
+
+ signature = hmac_hex(signing_key, string_to_sign.encode())
+
+ qs_with_signature = (
+ f"{canonical_querystring_template}&X-Amz-Signature={signature}"
+ )
+
+ signed_urls.append(
+ f"{self.endpoint_url}{canonical_uri}?{qs_with_signature}"
+ )
+
+ return signed_urls
+
+
+def _derive_signing_key(key: bytes, datestamp: str, region: str, service: str) -> bytes:
+ k_date = hmac_bytes(b"AWS4" + key, datestamp.encode("utf-8"))
+ k_region = hmac_bytes(k_date, region.encode("utf-8"))
+ k_service = hmac_bytes(k_region, service.encode("utf-8"))
+
+ return hmac_bytes(k_service, b"aws4_request")
+
+
+def hmac_bytes(key: bytes, data: bytes) -> bytes:
+ return hmac.new(key, data, hashlib.sha256).digest()
+
+
+def hmac_hex(key: bytes, data: bytes) -> str:
+ return hmac.new(key, data, hashlib.sha256).hexdigest()
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 0000000..24eeaaa
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,1874 @@
+[[package]]
+name = "aiobotocore"
+version = "2.4.1"
+description = "Async client for aws services using botocore and aiohttp"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+aiohttp = ">=3.3.1"
+aioitertools = ">=0.5.1"
+botocore = ">=1.27.59,<1.27.60"
+wrapt = ">=1.10.10"
+
+[package.extras]
+awscli = ["awscli (>=1.25.60,<1.25.61)"]
+boto3 = ["boto3 (>=1.24.59,<1.24.60)"]
+
+[[package]]
+name = "aiohttp"
+version = "3.8.3"
+description = "Async http client/server framework (asyncio)"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+aiosignal = ">=1.1.2"
+async-timeout = ">=4.0.0a3,<5.0"
+attrs = ">=17.3.0"
+charset-normalizer = ">=2.0,<3.0"
+frozenlist = ">=1.1.1"
+multidict = ">=4.5,<7.0"
+yarl = ">=1.0,<2.0"
+
+[package.extras]
+speedups = ["Brotli", "aiodns", "cchardet"]
+
+[[package]]
+name = "aioitertools"
+version = "0.11.0"
+description = "itertools and builtins for AsyncIO and mixed iterables"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+typing_extensions = {version = ">=4.0", markers = "python_version < \"3.10\""}
+
+[[package]]
+name = "aiosignal"
+version = "1.3.1"
+description = "aiosignal: a list of registered asynchronous callbacks"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+frozenlist = ">=1.1.0"
+
+[[package]]
+name = "async-timeout"
+version = "4.0.2"
+description = "Timeout context manager for asyncio programs"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "attrs"
+version = "22.1.0"
+description = "Classes Without Boilerplate"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
+docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
+tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
+tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
+
+[[package]]
+name = "black"
+version = "22.12.0"
+description = "The uncompromising code formatter."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+click = ">=8.0.0"
+mypy-extensions = ">=0.4.3"
+pathspec = ">=0.9.0"
+platformdirs = ">=2"
+tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
+typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+colorama = ["colorama (>=0.4.3)"]
+d = ["aiohttp (>=3.7.4)"]
+jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
+uvloop = ["uvloop (>=0.15.2)"]
+
+[[package]]
+name = "boto3"
+version = "1.24.59"
+description = "The AWS SDK for Python"
+category = "dev"
+optional = false
+python-versions = ">= 3.7"
+
+[package.dependencies]
+botocore = ">=1.27.59,<1.28.0"
+jmespath = ">=0.7.1,<2.0.0"
+s3transfer = ">=0.6.0,<0.7.0"
+
+[package.extras]
+crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
+
+[[package]]
+name = "boto3-stubs-lite"
+version = "1.26.27"
+description = "Type annotations for boto3 1.26.27 generated with mypy-boto3-builder 7.12.0"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+botocore-stubs = "*"
+mypy-boto3-s3 = {version = ">=1.26.0,<1.27.0", optional = true, markers = "extra == \"s3\""}
+types-s3transfer = "*"
+typing-extensions = ">=4.1.0"
+
+[package.extras]
+accessanalyzer = ["mypy-boto3-accessanalyzer (>=1.26.0,<1.27.0)"]
+account = ["mypy-boto3-account (>=1.26.0,<1.27.0)"]
+acm = ["mypy-boto3-acm (>=1.26.0,<1.27.0)"]
+acm-pca = ["mypy-boto3-acm-pca (>=1.26.0,<1.27.0)"]
+alexaforbusiness = ["mypy-boto3-alexaforbusiness (>=1.26.0,<1.27.0)"]
+all = ["mypy-boto3-accessanalyzer (>=1.26.0,<1.27.0)", "mypy-boto3-account (>=1.26.0,<1.27.0)", "mypy-boto3-acm (>=1.26.0,<1.27.0)", "mypy-boto3-acm-pca (>=1.26.0,<1.27.0)", "mypy-boto3-alexaforbusiness (>=1.26.0,<1.27.0)", "mypy-boto3-amp (>=1.26.0,<1.27.0)", "mypy-boto3-amplify (>=1.26.0,<1.27.0)", "mypy-boto3-amplifybackend (>=1.26.0,<1.27.0)", "mypy-boto3-amplifyuibuilder (>=1.26.0,<1.27.0)", "mypy-boto3-apigateway (>=1.26.0,<1.27.0)", "mypy-boto3-apigatewaymanagementapi (>=1.26.0,<1.27.0)", "mypy-boto3-apigatewayv2 (>=1.26.0,<1.27.0)", "mypy-boto3-appconfig (>=1.26.0,<1.27.0)", "mypy-boto3-appconfigdata (>=1.26.0,<1.27.0)", "mypy-boto3-appflow (>=1.26.0,<1.27.0)", "mypy-boto3-appintegrations (>=1.26.0,<1.27.0)", "mypy-boto3-application-autoscaling (>=1.26.0,<1.27.0)", "mypy-boto3-application-insights (>=1.26.0,<1.27.0)", "mypy-boto3-applicationcostprofiler (>=1.26.0,<1.27.0)", "mypy-boto3-appmesh (>=1.26.0,<1.27.0)", "mypy-boto3-apprunner (>=1.26.0,<1.27.0)", "mypy-boto3-appstream (>=1.26.0,<1.27.0)", "mypy-boto3-appsync (>=1.26.0,<1.27.0)", "mypy-boto3-arc-zonal-shift (>=1.26.0,<1.27.0)", "mypy-boto3-athena (>=1.26.0,<1.27.0)", "mypy-boto3-auditmanager (>=1.26.0,<1.27.0)", "mypy-boto3-autoscaling (>=1.26.0,<1.27.0)", "mypy-boto3-autoscaling-plans (>=1.26.0,<1.27.0)", "mypy-boto3-backup (>=1.26.0,<1.27.0)", "mypy-boto3-backup-gateway (>=1.26.0,<1.27.0)", "mypy-boto3-backupstorage (>=1.26.0,<1.27.0)", "mypy-boto3-batch (>=1.26.0,<1.27.0)", "mypy-boto3-billingconductor (>=1.26.0,<1.27.0)", "mypy-boto3-braket (>=1.26.0,<1.27.0)", "mypy-boto3-budgets (>=1.26.0,<1.27.0)", "mypy-boto3-ce (>=1.26.0,<1.27.0)", "mypy-boto3-chime (>=1.26.0,<1.27.0)", "mypy-boto3-chime-sdk-identity (>=1.26.0,<1.27.0)", "mypy-boto3-chime-sdk-media-pipelines (>=1.26.0,<1.27.0)", "mypy-boto3-chime-sdk-meetings (>=1.26.0,<1.27.0)", "mypy-boto3-chime-sdk-messaging (>=1.26.0,<1.27.0)", "mypy-boto3-chime-sdk-voice (>=1.26.0,<1.27.0)", "mypy-boto3-cloud9 (>=1.26.0,<1.27.0)", "mypy-boto3-cloudcontrol (>=1.26.0,<1.27.0)", "mypy-boto3-clouddirectory (>=1.26.0,<1.27.0)", "mypy-boto3-cloudformation (>=1.26.0,<1.27.0)", "mypy-boto3-cloudfront (>=1.26.0,<1.27.0)", "mypy-boto3-cloudhsm (>=1.26.0,<1.27.0)", "mypy-boto3-cloudhsmv2 (>=1.26.0,<1.27.0)", "mypy-boto3-cloudsearch (>=1.26.0,<1.27.0)", "mypy-boto3-cloudsearchdomain (>=1.26.0,<1.27.0)", "mypy-boto3-cloudtrail (>=1.26.0,<1.27.0)", "mypy-boto3-cloudwatch (>=1.26.0,<1.27.0)", "mypy-boto3-codeartifact (>=1.26.0,<1.27.0)", "mypy-boto3-codebuild (>=1.26.0,<1.27.0)", "mypy-boto3-codecatalyst (>=1.26.0,<1.27.0)", "mypy-boto3-codecommit (>=1.26.0,<1.27.0)", "mypy-boto3-codedeploy (>=1.26.0,<1.27.0)", "mypy-boto3-codeguru-reviewer (>=1.26.0,<1.27.0)", "mypy-boto3-codeguruprofiler (>=1.26.0,<1.27.0)", "mypy-boto3-codepipeline (>=1.26.0,<1.27.0)", "mypy-boto3-codestar (>=1.26.0,<1.27.0)", "mypy-boto3-codestar-connections (>=1.26.0,<1.27.0)", "mypy-boto3-codestar-notifications (>=1.26.0,<1.27.0)", "mypy-boto3-cognito-identity (>=1.26.0,<1.27.0)", "mypy-boto3-cognito-idp (>=1.26.0,<1.27.0)", "mypy-boto3-cognito-sync (>=1.26.0,<1.27.0)", "mypy-boto3-comprehend (>=1.26.0,<1.27.0)", "mypy-boto3-comprehendmedical (>=1.26.0,<1.27.0)", "mypy-boto3-compute-optimizer (>=1.26.0,<1.27.0)", "mypy-boto3-config (>=1.26.0,<1.27.0)", "mypy-boto3-connect (>=1.26.0,<1.27.0)", "mypy-boto3-connect-contact-lens (>=1.26.0,<1.27.0)", "mypy-boto3-connectcampaigns (>=1.26.0,<1.27.0)", "mypy-boto3-connectcases (>=1.26.0,<1.27.0)", "mypy-boto3-connectparticipant (>=1.26.0,<1.27.0)", "mypy-boto3-controltower (>=1.26.0,<1.27.0)", "mypy-boto3-cur (>=1.26.0,<1.27.0)", "mypy-boto3-customer-profiles (>=1.26.0,<1.27.0)", "mypy-boto3-databrew (>=1.26.0,<1.27.0)", "mypy-boto3-dataexchange (>=1.26.0,<1.27.0)", "mypy-boto3-datapipeline (>=1.26.0,<1.27.0)", "mypy-boto3-datasync (>=1.26.0,<1.27.0)", "mypy-boto3-dax (>=1.26.0,<1.27.0)", "mypy-boto3-detective (>=1.26.0,<1.27.0)", "mypy-boto3-devicefarm (>=1.26.0,<1.27.0)", "mypy-boto3-devops-guru (>=1.26.0,<1.27.0)", "mypy-boto3-directconnect (>=1.26.0,<1.27.0)", "mypy-boto3-discovery (>=1.26.0,<1.27.0)", "mypy-boto3-dlm (>=1.26.0,<1.27.0)", "mypy-boto3-dms (>=1.26.0,<1.27.0)", "mypy-boto3-docdb (>=1.26.0,<1.27.0)", "mypy-boto3-docdb-elastic (>=1.26.0,<1.27.0)", "mypy-boto3-drs (>=1.26.0,<1.27.0)", "mypy-boto3-ds (>=1.26.0,<1.27.0)", "mypy-boto3-dynamodb (>=1.26.0,<1.27.0)", "mypy-boto3-dynamodbstreams (>=1.26.0,<1.27.0)", "mypy-boto3-ebs (>=1.26.0,<1.27.0)", "mypy-boto3-ec2 (>=1.26.0,<1.27.0)", "mypy-boto3-ec2-instance-connect (>=1.26.0,<1.27.0)", "mypy-boto3-ecr (>=1.26.0,<1.27.0)", "mypy-boto3-ecr-public (>=1.26.0,<1.27.0)", "mypy-boto3-ecs (>=1.26.0,<1.27.0)", "mypy-boto3-efs (>=1.26.0,<1.27.0)", "mypy-boto3-eks (>=1.26.0,<1.27.0)", "mypy-boto3-elastic-inference (>=1.26.0,<1.27.0)", "mypy-boto3-elasticache (>=1.26.0,<1.27.0)", "mypy-boto3-elasticbeanstalk (>=1.26.0,<1.27.0)", "mypy-boto3-elastictranscoder (>=1.26.0,<1.27.0)", "mypy-boto3-elb (>=1.26.0,<1.27.0)", "mypy-boto3-elbv2 (>=1.26.0,<1.27.0)", "mypy-boto3-emr (>=1.26.0,<1.27.0)", "mypy-boto3-emr-containers (>=1.26.0,<1.27.0)", "mypy-boto3-emr-serverless (>=1.26.0,<1.27.0)", "mypy-boto3-es (>=1.26.0,<1.27.0)", "mypy-boto3-events (>=1.26.0,<1.27.0)", "mypy-boto3-evidently (>=1.26.0,<1.27.0)", "mypy-boto3-finspace (>=1.26.0,<1.27.0)", "mypy-boto3-finspace-data (>=1.26.0,<1.27.0)", "mypy-boto3-firehose (>=1.26.0,<1.27.0)", "mypy-boto3-fis (>=1.26.0,<1.27.0)", "mypy-boto3-fms (>=1.26.0,<1.27.0)", "mypy-boto3-forecast (>=1.26.0,<1.27.0)", "mypy-boto3-forecastquery (>=1.26.0,<1.27.0)", "mypy-boto3-frauddetector (>=1.26.0,<1.27.0)", "mypy-boto3-fsx (>=1.26.0,<1.27.0)", "mypy-boto3-gamelift (>=1.26.0,<1.27.0)", "mypy-boto3-gamesparks (>=1.26.0,<1.27.0)", "mypy-boto3-glacier (>=1.26.0,<1.27.0)", "mypy-boto3-globalaccelerator (>=1.26.0,<1.27.0)", "mypy-boto3-glue (>=1.26.0,<1.27.0)", "mypy-boto3-grafana (>=1.26.0,<1.27.0)", "mypy-boto3-greengrass (>=1.26.0,<1.27.0)", "mypy-boto3-greengrassv2 (>=1.26.0,<1.27.0)", "mypy-boto3-groundstation (>=1.26.0,<1.27.0)", "mypy-boto3-guardduty (>=1.26.0,<1.27.0)", "mypy-boto3-health (>=1.26.0,<1.27.0)", "mypy-boto3-healthlake (>=1.26.0,<1.27.0)", "mypy-boto3-honeycode (>=1.26.0,<1.27.0)", "mypy-boto3-iam (>=1.26.0,<1.27.0)", "mypy-boto3-identitystore (>=1.26.0,<1.27.0)", "mypy-boto3-imagebuilder (>=1.26.0,<1.27.0)", "mypy-boto3-importexport (>=1.26.0,<1.27.0)", "mypy-boto3-inspector (>=1.26.0,<1.27.0)", "mypy-boto3-inspector2 (>=1.26.0,<1.27.0)", "mypy-boto3-iot (>=1.26.0,<1.27.0)", "mypy-boto3-iot-data (>=1.26.0,<1.27.0)", "mypy-boto3-iot-jobs-data (>=1.26.0,<1.27.0)", "mypy-boto3-iot-roborunner (>=1.26.0,<1.27.0)", "mypy-boto3-iot1click-devices (>=1.26.0,<1.27.0)", "mypy-boto3-iot1click-projects (>=1.26.0,<1.27.0)", "mypy-boto3-iotanalytics (>=1.26.0,<1.27.0)", "mypy-boto3-iotdeviceadvisor (>=1.26.0,<1.27.0)", "mypy-boto3-iotevents (>=1.26.0,<1.27.0)", "mypy-boto3-iotevents-data (>=1.26.0,<1.27.0)", "mypy-boto3-iotfleethub (>=1.26.0,<1.27.0)", "mypy-boto3-iotfleetwise (>=1.26.0,<1.27.0)", "mypy-boto3-iotsecuretunneling (>=1.26.0,<1.27.0)", "mypy-boto3-iotsitewise (>=1.26.0,<1.27.0)", "mypy-boto3-iotthingsgraph (>=1.26.0,<1.27.0)", "mypy-boto3-iottwinmaker (>=1.26.0,<1.27.0)", "mypy-boto3-iotwireless (>=1.26.0,<1.27.0)", "mypy-boto3-ivs (>=1.26.0,<1.27.0)", "mypy-boto3-ivschat (>=1.26.0,<1.27.0)", "mypy-boto3-kafka (>=1.26.0,<1.27.0)", "mypy-boto3-kafkaconnect (>=1.26.0,<1.27.0)", "mypy-boto3-kendra (>=1.26.0,<1.27.0)", "mypy-boto3-keyspaces (>=1.26.0,<1.27.0)", "mypy-boto3-kinesis (>=1.26.0,<1.27.0)", "mypy-boto3-kinesis-video-archived-media (>=1.26.0,<1.27.0)", "mypy-boto3-kinesis-video-media (>=1.26.0,<1.27.0)", "mypy-boto3-kinesis-video-signaling (>=1.26.0,<1.27.0)", "mypy-boto3-kinesisanalytics (>=1.26.0,<1.27.0)", "mypy-boto3-kinesisanalyticsv2 (>=1.26.0,<1.27.0)", "mypy-boto3-kinesisvideo (>=1.26.0,<1.27.0)", "mypy-boto3-kms (>=1.26.0,<1.27.0)", "mypy-boto3-lakeformation (>=1.26.0,<1.27.0)", "mypy-boto3-lambda (>=1.26.0,<1.27.0)", "mypy-boto3-lex-models (>=1.26.0,<1.27.0)", "mypy-boto3-lex-runtime (>=1.26.0,<1.27.0)", "mypy-boto3-lexv2-models (>=1.26.0,<1.27.0)", "mypy-boto3-lexv2-runtime (>=1.26.0,<1.27.0)", "mypy-boto3-license-manager (>=1.26.0,<1.27.0)", "mypy-boto3-license-manager-user-subscriptions (>=1.26.0,<1.27.0)", "mypy-boto3-lightsail (>=1.26.0,<1.27.0)", "mypy-boto3-location (>=1.26.0,<1.27.0)", "mypy-boto3-logs (>=1.26.0,<1.27.0)", "mypy-boto3-lookoutequipment (>=1.26.0,<1.27.0)", "mypy-boto3-lookoutmetrics (>=1.26.0,<1.27.0)", "mypy-boto3-lookoutvision (>=1.26.0,<1.27.0)", "mypy-boto3-m2 (>=1.26.0,<1.27.0)", "mypy-boto3-machinelearning (>=1.26.0,<1.27.0)", "mypy-boto3-macie (>=1.26.0,<1.27.0)", "mypy-boto3-macie2 (>=1.26.0,<1.27.0)", "mypy-boto3-managedblockchain (>=1.26.0,<1.27.0)", "mypy-boto3-marketplace-catalog (>=1.26.0,<1.27.0)", "mypy-boto3-marketplace-entitlement (>=1.26.0,<1.27.0)", "mypy-boto3-marketplacecommerceanalytics (>=1.26.0,<1.27.0)", "mypy-boto3-mediaconnect (>=1.26.0,<1.27.0)", "mypy-boto3-mediaconvert (>=1.26.0,<1.27.0)", "mypy-boto3-medialive (>=1.26.0,<1.27.0)", "mypy-boto3-mediapackage (>=1.26.0,<1.27.0)", "mypy-boto3-mediapackage-vod (>=1.26.0,<1.27.0)", "mypy-boto3-mediastore (>=1.26.0,<1.27.0)", "mypy-boto3-mediastore-data (>=1.26.0,<1.27.0)", "mypy-boto3-mediatailor (>=1.26.0,<1.27.0)", "mypy-boto3-memorydb (>=1.26.0,<1.27.0)", "mypy-boto3-meteringmarketplace (>=1.26.0,<1.27.0)", "mypy-boto3-mgh (>=1.26.0,<1.27.0)", "mypy-boto3-mgn (>=1.26.0,<1.27.0)", "mypy-boto3-migration-hub-refactor-spaces (>=1.26.0,<1.27.0)", "mypy-boto3-migrationhub-config (>=1.26.0,<1.27.0)", "mypy-boto3-migrationhuborchestrator (>=1.26.0,<1.27.0)", "mypy-boto3-migrationhubstrategy (>=1.26.0,<1.27.0)", "mypy-boto3-mobile (>=1.26.0,<1.27.0)", "mypy-boto3-mq (>=1.26.0,<1.27.0)", "mypy-boto3-mturk (>=1.26.0,<1.27.0)", "mypy-boto3-mwaa (>=1.26.0,<1.27.0)", "mypy-boto3-neptune (>=1.26.0,<1.27.0)", "mypy-boto3-network-firewall (>=1.26.0,<1.27.0)", "mypy-boto3-networkmanager (>=1.26.0,<1.27.0)", "mypy-boto3-nimble (>=1.26.0,<1.27.0)", "mypy-boto3-oam (>=1.26.0,<1.27.0)", "mypy-boto3-omics (>=1.26.0,<1.27.0)", "mypy-boto3-opensearch (>=1.26.0,<1.27.0)", "mypy-boto3-opensearchserverless (>=1.26.0,<1.27.0)", "mypy-boto3-opsworks (>=1.26.0,<1.27.0)", "mypy-boto3-opsworkscm (>=1.26.0,<1.27.0)", "mypy-boto3-organizations (>=1.26.0,<1.27.0)", "mypy-boto3-outposts (>=1.26.0,<1.27.0)", "mypy-boto3-panorama (>=1.26.0,<1.27.0)", "mypy-boto3-personalize (>=1.26.0,<1.27.0)", "mypy-boto3-personalize-events (>=1.26.0,<1.27.0)", "mypy-boto3-personalize-runtime (>=1.26.0,<1.27.0)", "mypy-boto3-pi (>=1.26.0,<1.27.0)", "mypy-boto3-pinpoint (>=1.26.0,<1.27.0)", "mypy-boto3-pinpoint-email (>=1.26.0,<1.27.0)", "mypy-boto3-pinpoint-sms-voice (>=1.26.0,<1.27.0)", "mypy-boto3-pinpoint-sms-voice-v2 (>=1.26.0,<1.27.0)", "mypy-boto3-pipes (>=1.26.0,<1.27.0)", "mypy-boto3-polly (>=1.26.0,<1.27.0)", "mypy-boto3-pricing (>=1.26.0,<1.27.0)", "mypy-boto3-privatenetworks (>=1.26.0,<1.27.0)", "mypy-boto3-proton (>=1.26.0,<1.27.0)", "mypy-boto3-qldb (>=1.26.0,<1.27.0)", "mypy-boto3-qldb-session (>=1.26.0,<1.27.0)", "mypy-boto3-quicksight (>=1.26.0,<1.27.0)", "mypy-boto3-ram (>=1.26.0,<1.27.0)", "mypy-boto3-rbin (>=1.26.0,<1.27.0)", "mypy-boto3-rds (>=1.26.0,<1.27.0)", "mypy-boto3-rds-data (>=1.26.0,<1.27.0)", "mypy-boto3-redshift (>=1.26.0,<1.27.0)", "mypy-boto3-redshift-data (>=1.26.0,<1.27.0)", "mypy-boto3-redshift-serverless (>=1.26.0,<1.27.0)", "mypy-boto3-rekognition (>=1.26.0,<1.27.0)", "mypy-boto3-resiliencehub (>=1.26.0,<1.27.0)", "mypy-boto3-resource-explorer-2 (>=1.26.0,<1.27.0)", "mypy-boto3-resource-groups (>=1.26.0,<1.27.0)", "mypy-boto3-resourcegroupstaggingapi (>=1.26.0,<1.27.0)", "mypy-boto3-robomaker (>=1.26.0,<1.27.0)", "mypy-boto3-rolesanywhere (>=1.26.0,<1.27.0)", "mypy-boto3-route53 (>=1.26.0,<1.27.0)", "mypy-boto3-route53-recovery-cluster (>=1.26.0,<1.27.0)", "mypy-boto3-route53-recovery-control-config (>=1.26.0,<1.27.0)", "mypy-boto3-route53-recovery-readiness (>=1.26.0,<1.27.0)", "mypy-boto3-route53domains (>=1.26.0,<1.27.0)", "mypy-boto3-route53resolver (>=1.26.0,<1.27.0)", "mypy-boto3-rum (>=1.26.0,<1.27.0)", "mypy-boto3-s3 (>=1.26.0,<1.27.0)", "mypy-boto3-s3control (>=1.26.0,<1.27.0)", "mypy-boto3-s3outposts (>=1.26.0,<1.27.0)", "mypy-boto3-sagemaker (>=1.26.0,<1.27.0)", "mypy-boto3-sagemaker-a2i-runtime (>=1.26.0,<1.27.0)", "mypy-boto3-sagemaker-edge (>=1.26.0,<1.27.0)", "mypy-boto3-sagemaker-featurestore-runtime (>=1.26.0,<1.27.0)", "mypy-boto3-sagemaker-geospatial (>=1.26.0,<1.27.0)", "mypy-boto3-sagemaker-runtime (>=1.26.0,<1.27.0)", "mypy-boto3-savingsplans (>=1.26.0,<1.27.0)", "mypy-boto3-scheduler (>=1.26.0,<1.27.0)", "mypy-boto3-schemas (>=1.26.0,<1.27.0)", "mypy-boto3-sdb (>=1.26.0,<1.27.0)", "mypy-boto3-secretsmanager (>=1.26.0,<1.27.0)", "mypy-boto3-securityhub (>=1.26.0,<1.27.0)", "mypy-boto3-securitylake (>=1.26.0,<1.27.0)", "mypy-boto3-serverlessrepo (>=1.26.0,<1.27.0)", "mypy-boto3-service-quotas (>=1.26.0,<1.27.0)", "mypy-boto3-servicecatalog (>=1.26.0,<1.27.0)", "mypy-boto3-servicecatalog-appregistry (>=1.26.0,<1.27.0)", "mypy-boto3-servicediscovery (>=1.26.0,<1.27.0)", "mypy-boto3-ses (>=1.26.0,<1.27.0)", "mypy-boto3-sesv2 (>=1.26.0,<1.27.0)", "mypy-boto3-shield (>=1.26.0,<1.27.0)", "mypy-boto3-signer (>=1.26.0,<1.27.0)", "mypy-boto3-simspaceweaver (>=1.26.0,<1.27.0)", "mypy-boto3-sms (>=1.26.0,<1.27.0)", "mypy-boto3-sms-voice (>=1.26.0,<1.27.0)", "mypy-boto3-snow-device-management (>=1.26.0,<1.27.0)", "mypy-boto3-snowball (>=1.26.0,<1.27.0)", "mypy-boto3-sns (>=1.26.0,<1.27.0)", "mypy-boto3-sqs (>=1.26.0,<1.27.0)", "mypy-boto3-ssm (>=1.26.0,<1.27.0)", "mypy-boto3-ssm-contacts (>=1.26.0,<1.27.0)", "mypy-boto3-ssm-incidents (>=1.26.0,<1.27.0)", "mypy-boto3-ssm-sap (>=1.26.0,<1.27.0)", "mypy-boto3-sso (>=1.26.0,<1.27.0)", "mypy-boto3-sso-admin (>=1.26.0,<1.27.0)", "mypy-boto3-sso-oidc (>=1.26.0,<1.27.0)", "mypy-boto3-stepfunctions (>=1.26.0,<1.27.0)", "mypy-boto3-storagegateway (>=1.26.0,<1.27.0)", "mypy-boto3-sts (>=1.26.0,<1.27.0)", "mypy-boto3-support (>=1.26.0,<1.27.0)", "mypy-boto3-support-app (>=1.26.0,<1.27.0)", "mypy-boto3-swf (>=1.26.0,<1.27.0)", "mypy-boto3-synthetics (>=1.26.0,<1.27.0)", "mypy-boto3-textract (>=1.26.0,<1.27.0)", "mypy-boto3-timestream-query (>=1.26.0,<1.27.0)", "mypy-boto3-timestream-write (>=1.26.0,<1.27.0)", "mypy-boto3-transcribe (>=1.26.0,<1.27.0)", "mypy-boto3-transfer (>=1.26.0,<1.27.0)", "mypy-boto3-translate (>=1.26.0,<1.27.0)", "mypy-boto3-voice-id (>=1.26.0,<1.27.0)", "mypy-boto3-waf (>=1.26.0,<1.27.0)", "mypy-boto3-waf-regional (>=1.26.0,<1.27.0)", "mypy-boto3-wafv2 (>=1.26.0,<1.27.0)", "mypy-boto3-wellarchitected (>=1.26.0,<1.27.0)", "mypy-boto3-wisdom (>=1.26.0,<1.27.0)", "mypy-boto3-workdocs (>=1.26.0,<1.27.0)", "mypy-boto3-worklink (>=1.26.0,<1.27.0)", "mypy-boto3-workmail (>=1.26.0,<1.27.0)", "mypy-boto3-workmailmessageflow (>=1.26.0,<1.27.0)", "mypy-boto3-workspaces (>=1.26.0,<1.27.0)", "mypy-boto3-workspaces-web (>=1.26.0,<1.27.0)", "mypy-boto3-xray (>=1.26.0,<1.27.0)"]
+amp = ["mypy-boto3-amp (>=1.26.0,<1.27.0)"]
+amplify = ["mypy-boto3-amplify (>=1.26.0,<1.27.0)"]
+amplifybackend = ["mypy-boto3-amplifybackend (>=1.26.0,<1.27.0)"]
+amplifyuibuilder = ["mypy-boto3-amplifyuibuilder (>=1.26.0,<1.27.0)"]
+apigateway = ["mypy-boto3-apigateway (>=1.26.0,<1.27.0)"]
+apigatewaymanagementapi = ["mypy-boto3-apigatewaymanagementapi (>=1.26.0,<1.27.0)"]
+apigatewayv2 = ["mypy-boto3-apigatewayv2 (>=1.26.0,<1.27.0)"]
+appconfig = ["mypy-boto3-appconfig (>=1.26.0,<1.27.0)"]
+appconfigdata = ["mypy-boto3-appconfigdata (>=1.26.0,<1.27.0)"]
+appflow = ["mypy-boto3-appflow (>=1.26.0,<1.27.0)"]
+appintegrations = ["mypy-boto3-appintegrations (>=1.26.0,<1.27.0)"]
+application-autoscaling = ["mypy-boto3-application-autoscaling (>=1.26.0,<1.27.0)"]
+application-insights = ["mypy-boto3-application-insights (>=1.26.0,<1.27.0)"]
+applicationcostprofiler = ["mypy-boto3-applicationcostprofiler (>=1.26.0,<1.27.0)"]
+appmesh = ["mypy-boto3-appmesh (>=1.26.0,<1.27.0)"]
+apprunner = ["mypy-boto3-apprunner (>=1.26.0,<1.27.0)"]
+appstream = ["mypy-boto3-appstream (>=1.26.0,<1.27.0)"]
+appsync = ["mypy-boto3-appsync (>=1.26.0,<1.27.0)"]
+arc-zonal-shift = ["mypy-boto3-arc-zonal-shift (>=1.26.0,<1.27.0)"]
+athena = ["mypy-boto3-athena (>=1.26.0,<1.27.0)"]
+auditmanager = ["mypy-boto3-auditmanager (>=1.26.0,<1.27.0)"]
+autoscaling = ["mypy-boto3-autoscaling (>=1.26.0,<1.27.0)"]
+autoscaling-plans = ["mypy-boto3-autoscaling-plans (>=1.26.0,<1.27.0)"]
+backup = ["mypy-boto3-backup (>=1.26.0,<1.27.0)"]
+backup-gateway = ["mypy-boto3-backup-gateway (>=1.26.0,<1.27.0)"]
+backupstorage = ["mypy-boto3-backupstorage (>=1.26.0,<1.27.0)"]
+batch = ["mypy-boto3-batch (>=1.26.0,<1.27.0)"]
+billingconductor = ["mypy-boto3-billingconductor (>=1.26.0,<1.27.0)"]
+boto3 = ["boto3 (==1.26.27)", "botocore (==1.29.27)"]
+braket = ["mypy-boto3-braket (>=1.26.0,<1.27.0)"]
+budgets = ["mypy-boto3-budgets (>=1.26.0,<1.27.0)"]
+ce = ["mypy-boto3-ce (>=1.26.0,<1.27.0)"]
+chime = ["mypy-boto3-chime (>=1.26.0,<1.27.0)"]
+chime-sdk-identity = ["mypy-boto3-chime-sdk-identity (>=1.26.0,<1.27.0)"]
+chime-sdk-media-pipelines = ["mypy-boto3-chime-sdk-media-pipelines (>=1.26.0,<1.27.0)"]
+chime-sdk-meetings = ["mypy-boto3-chime-sdk-meetings (>=1.26.0,<1.27.0)"]
+chime-sdk-messaging = ["mypy-boto3-chime-sdk-messaging (>=1.26.0,<1.27.0)"]
+chime-sdk-voice = ["mypy-boto3-chime-sdk-voice (>=1.26.0,<1.27.0)"]
+cloud9 = ["mypy-boto3-cloud9 (>=1.26.0,<1.27.0)"]
+cloudcontrol = ["mypy-boto3-cloudcontrol (>=1.26.0,<1.27.0)"]
+clouddirectory = ["mypy-boto3-clouddirectory (>=1.26.0,<1.27.0)"]
+cloudformation = ["mypy-boto3-cloudformation (>=1.26.0,<1.27.0)"]
+cloudfront = ["mypy-boto3-cloudfront (>=1.26.0,<1.27.0)"]
+cloudhsm = ["mypy-boto3-cloudhsm (>=1.26.0,<1.27.0)"]
+cloudhsmv2 = ["mypy-boto3-cloudhsmv2 (>=1.26.0,<1.27.0)"]
+cloudsearch = ["mypy-boto3-cloudsearch (>=1.26.0,<1.27.0)"]
+cloudsearchdomain = ["mypy-boto3-cloudsearchdomain (>=1.26.0,<1.27.0)"]
+cloudtrail = ["mypy-boto3-cloudtrail (>=1.26.0,<1.27.0)"]
+cloudwatch = ["mypy-boto3-cloudwatch (>=1.26.0,<1.27.0)"]
+codeartifact = ["mypy-boto3-codeartifact (>=1.26.0,<1.27.0)"]
+codebuild = ["mypy-boto3-codebuild (>=1.26.0,<1.27.0)"]
+codecatalyst = ["mypy-boto3-codecatalyst (>=1.26.0,<1.27.0)"]
+codecommit = ["mypy-boto3-codecommit (>=1.26.0,<1.27.0)"]
+codedeploy = ["mypy-boto3-codedeploy (>=1.26.0,<1.27.0)"]
+codeguru-reviewer = ["mypy-boto3-codeguru-reviewer (>=1.26.0,<1.27.0)"]
+codeguruprofiler = ["mypy-boto3-codeguruprofiler (>=1.26.0,<1.27.0)"]
+codepipeline = ["mypy-boto3-codepipeline (>=1.26.0,<1.27.0)"]
+codestar = ["mypy-boto3-codestar (>=1.26.0,<1.27.0)"]
+codestar-connections = ["mypy-boto3-codestar-connections (>=1.26.0,<1.27.0)"]
+codestar-notifications = ["mypy-boto3-codestar-notifications (>=1.26.0,<1.27.0)"]
+cognito-identity = ["mypy-boto3-cognito-identity (>=1.26.0,<1.27.0)"]
+cognito-idp = ["mypy-boto3-cognito-idp (>=1.26.0,<1.27.0)"]
+cognito-sync = ["mypy-boto3-cognito-sync (>=1.26.0,<1.27.0)"]
+comprehend = ["mypy-boto3-comprehend (>=1.26.0,<1.27.0)"]
+comprehendmedical = ["mypy-boto3-comprehendmedical (>=1.26.0,<1.27.0)"]
+compute-optimizer = ["mypy-boto3-compute-optimizer (>=1.26.0,<1.27.0)"]
+config = ["mypy-boto3-config (>=1.26.0,<1.27.0)"]
+connect = ["mypy-boto3-connect (>=1.26.0,<1.27.0)"]
+connect-contact-lens = ["mypy-boto3-connect-contact-lens (>=1.26.0,<1.27.0)"]
+connectcampaigns = ["mypy-boto3-connectcampaigns (>=1.26.0,<1.27.0)"]
+connectcases = ["mypy-boto3-connectcases (>=1.26.0,<1.27.0)"]
+connectparticipant = ["mypy-boto3-connectparticipant (>=1.26.0,<1.27.0)"]
+controltower = ["mypy-boto3-controltower (>=1.26.0,<1.27.0)"]
+cur = ["mypy-boto3-cur (>=1.26.0,<1.27.0)"]
+customer-profiles = ["mypy-boto3-customer-profiles (>=1.26.0,<1.27.0)"]
+databrew = ["mypy-boto3-databrew (>=1.26.0,<1.27.0)"]
+dataexchange = ["mypy-boto3-dataexchange (>=1.26.0,<1.27.0)"]
+datapipeline = ["mypy-boto3-datapipeline (>=1.26.0,<1.27.0)"]
+datasync = ["mypy-boto3-datasync (>=1.26.0,<1.27.0)"]
+dax = ["mypy-boto3-dax (>=1.26.0,<1.27.0)"]
+detective = ["mypy-boto3-detective (>=1.26.0,<1.27.0)"]
+devicefarm = ["mypy-boto3-devicefarm (>=1.26.0,<1.27.0)"]
+devops-guru = ["mypy-boto3-devops-guru (>=1.26.0,<1.27.0)"]
+directconnect = ["mypy-boto3-directconnect (>=1.26.0,<1.27.0)"]
+discovery = ["mypy-boto3-discovery (>=1.26.0,<1.27.0)"]
+dlm = ["mypy-boto3-dlm (>=1.26.0,<1.27.0)"]
+dms = ["mypy-boto3-dms (>=1.26.0,<1.27.0)"]
+docdb = ["mypy-boto3-docdb (>=1.26.0,<1.27.0)"]
+docdb-elastic = ["mypy-boto3-docdb-elastic (>=1.26.0,<1.27.0)"]
+drs = ["mypy-boto3-drs (>=1.26.0,<1.27.0)"]
+ds = ["mypy-boto3-ds (>=1.26.0,<1.27.0)"]
+dynamodb = ["mypy-boto3-dynamodb (>=1.26.0,<1.27.0)"]
+dynamodbstreams = ["mypy-boto3-dynamodbstreams (>=1.26.0,<1.27.0)"]
+ebs = ["mypy-boto3-ebs (>=1.26.0,<1.27.0)"]
+ec2 = ["mypy-boto3-ec2 (>=1.26.0,<1.27.0)"]
+ec2-instance-connect = ["mypy-boto3-ec2-instance-connect (>=1.26.0,<1.27.0)"]
+ecr = ["mypy-boto3-ecr (>=1.26.0,<1.27.0)"]
+ecr-public = ["mypy-boto3-ecr-public (>=1.26.0,<1.27.0)"]
+ecs = ["mypy-boto3-ecs (>=1.26.0,<1.27.0)"]
+efs = ["mypy-boto3-efs (>=1.26.0,<1.27.0)"]
+eks = ["mypy-boto3-eks (>=1.26.0,<1.27.0)"]
+elastic-inference = ["mypy-boto3-elastic-inference (>=1.26.0,<1.27.0)"]
+elasticache = ["mypy-boto3-elasticache (>=1.26.0,<1.27.0)"]
+elasticbeanstalk = ["mypy-boto3-elasticbeanstalk (>=1.26.0,<1.27.0)"]
+elastictranscoder = ["mypy-boto3-elastictranscoder (>=1.26.0,<1.27.0)"]
+elb = ["mypy-boto3-elb (>=1.26.0,<1.27.0)"]
+elbv2 = ["mypy-boto3-elbv2 (>=1.26.0,<1.27.0)"]
+emr = ["mypy-boto3-emr (>=1.26.0,<1.27.0)"]
+emr-containers = ["mypy-boto3-emr-containers (>=1.26.0,<1.27.0)"]
+emr-serverless = ["mypy-boto3-emr-serverless (>=1.26.0,<1.27.0)"]
+es = ["mypy-boto3-es (>=1.26.0,<1.27.0)"]
+essential = ["mypy-boto3-cloudformation (>=1.26.0,<1.27.0)", "mypy-boto3-dynamodb (>=1.26.0,<1.27.0)", "mypy-boto3-ec2 (>=1.26.0,<1.27.0)", "mypy-boto3-lambda (>=1.26.0,<1.27.0)", "mypy-boto3-rds (>=1.26.0,<1.27.0)", "mypy-boto3-s3 (>=1.26.0,<1.27.0)", "mypy-boto3-sqs (>=1.26.0,<1.27.0)"]
+events = ["mypy-boto3-events (>=1.26.0,<1.27.0)"]
+evidently = ["mypy-boto3-evidently (>=1.26.0,<1.27.0)"]
+finspace = ["mypy-boto3-finspace (>=1.26.0,<1.27.0)"]
+finspace-data = ["mypy-boto3-finspace-data (>=1.26.0,<1.27.0)"]
+firehose = ["mypy-boto3-firehose (>=1.26.0,<1.27.0)"]
+fis = ["mypy-boto3-fis (>=1.26.0,<1.27.0)"]
+fms = ["mypy-boto3-fms (>=1.26.0,<1.27.0)"]
+forecast = ["mypy-boto3-forecast (>=1.26.0,<1.27.0)"]
+forecastquery = ["mypy-boto3-forecastquery (>=1.26.0,<1.27.0)"]
+frauddetector = ["mypy-boto3-frauddetector (>=1.26.0,<1.27.0)"]
+fsx = ["mypy-boto3-fsx (>=1.26.0,<1.27.0)"]
+gamelift = ["mypy-boto3-gamelift (>=1.26.0,<1.27.0)"]
+gamesparks = ["mypy-boto3-gamesparks (>=1.26.0,<1.27.0)"]
+glacier = ["mypy-boto3-glacier (>=1.26.0,<1.27.0)"]
+globalaccelerator = ["mypy-boto3-globalaccelerator (>=1.26.0,<1.27.0)"]
+glue = ["mypy-boto3-glue (>=1.26.0,<1.27.0)"]
+grafana = ["mypy-boto3-grafana (>=1.26.0,<1.27.0)"]
+greengrass = ["mypy-boto3-greengrass (>=1.26.0,<1.27.0)"]
+greengrassv2 = ["mypy-boto3-greengrassv2 (>=1.26.0,<1.27.0)"]
+groundstation = ["mypy-boto3-groundstation (>=1.26.0,<1.27.0)"]
+guardduty = ["mypy-boto3-guardduty (>=1.26.0,<1.27.0)"]
+health = ["mypy-boto3-health (>=1.26.0,<1.27.0)"]
+healthlake = ["mypy-boto3-healthlake (>=1.26.0,<1.27.0)"]
+honeycode = ["mypy-boto3-honeycode (>=1.26.0,<1.27.0)"]
+iam = ["mypy-boto3-iam (>=1.26.0,<1.27.0)"]
+identitystore = ["mypy-boto3-identitystore (>=1.26.0,<1.27.0)"]
+imagebuilder = ["mypy-boto3-imagebuilder (>=1.26.0,<1.27.0)"]
+importexport = ["mypy-boto3-importexport (>=1.26.0,<1.27.0)"]
+inspector = ["mypy-boto3-inspector (>=1.26.0,<1.27.0)"]
+inspector2 = ["mypy-boto3-inspector2 (>=1.26.0,<1.27.0)"]
+iot = ["mypy-boto3-iot (>=1.26.0,<1.27.0)"]
+iot-data = ["mypy-boto3-iot-data (>=1.26.0,<1.27.0)"]
+iot-jobs-data = ["mypy-boto3-iot-jobs-data (>=1.26.0,<1.27.0)"]
+iot-roborunner = ["mypy-boto3-iot-roborunner (>=1.26.0,<1.27.0)"]
+iot1click-devices = ["mypy-boto3-iot1click-devices (>=1.26.0,<1.27.0)"]
+iot1click-projects = ["mypy-boto3-iot1click-projects (>=1.26.0,<1.27.0)"]
+iotanalytics = ["mypy-boto3-iotanalytics (>=1.26.0,<1.27.0)"]
+iotdeviceadvisor = ["mypy-boto3-iotdeviceadvisor (>=1.26.0,<1.27.0)"]
+iotevents = ["mypy-boto3-iotevents (>=1.26.0,<1.27.0)"]
+iotevents-data = ["mypy-boto3-iotevents-data (>=1.26.0,<1.27.0)"]
+iotfleethub = ["mypy-boto3-iotfleethub (>=1.26.0,<1.27.0)"]
+iotfleetwise = ["mypy-boto3-iotfleetwise (>=1.26.0,<1.27.0)"]
+iotsecuretunneling = ["mypy-boto3-iotsecuretunneling (>=1.26.0,<1.27.0)"]
+iotsitewise = ["mypy-boto3-iotsitewise (>=1.26.0,<1.27.0)"]
+iotthingsgraph = ["mypy-boto3-iotthingsgraph (>=1.26.0,<1.27.0)"]
+iottwinmaker = ["mypy-boto3-iottwinmaker (>=1.26.0,<1.27.0)"]
+iotwireless = ["mypy-boto3-iotwireless (>=1.26.0,<1.27.0)"]
+ivs = ["mypy-boto3-ivs (>=1.26.0,<1.27.0)"]
+ivschat = ["mypy-boto3-ivschat (>=1.26.0,<1.27.0)"]
+kafka = ["mypy-boto3-kafka (>=1.26.0,<1.27.0)"]
+kafkaconnect = ["mypy-boto3-kafkaconnect (>=1.26.0,<1.27.0)"]
+kendra = ["mypy-boto3-kendra (>=1.26.0,<1.27.0)"]
+keyspaces = ["mypy-boto3-keyspaces (>=1.26.0,<1.27.0)"]
+kinesis = ["mypy-boto3-kinesis (>=1.26.0,<1.27.0)"]
+kinesis-video-archived-media = ["mypy-boto3-kinesis-video-archived-media (>=1.26.0,<1.27.0)"]
+kinesis-video-media = ["mypy-boto3-kinesis-video-media (>=1.26.0,<1.27.0)"]
+kinesis-video-signaling = ["mypy-boto3-kinesis-video-signaling (>=1.26.0,<1.27.0)"]
+kinesisanalytics = ["mypy-boto3-kinesisanalytics (>=1.26.0,<1.27.0)"]
+kinesisanalyticsv2 = ["mypy-boto3-kinesisanalyticsv2 (>=1.26.0,<1.27.0)"]
+kinesisvideo = ["mypy-boto3-kinesisvideo (>=1.26.0,<1.27.0)"]
+kms = ["mypy-boto3-kms (>=1.26.0,<1.27.0)"]
+lakeformation = ["mypy-boto3-lakeformation (>=1.26.0,<1.27.0)"]
+lambda = ["mypy-boto3-lambda (>=1.26.0,<1.27.0)"]
+lex-models = ["mypy-boto3-lex-models (>=1.26.0,<1.27.0)"]
+lex-runtime = ["mypy-boto3-lex-runtime (>=1.26.0,<1.27.0)"]
+lexv2-models = ["mypy-boto3-lexv2-models (>=1.26.0,<1.27.0)"]
+lexv2-runtime = ["mypy-boto3-lexv2-runtime (>=1.26.0,<1.27.0)"]
+license-manager = ["mypy-boto3-license-manager (>=1.26.0,<1.27.0)"]
+license-manager-user-subscriptions = ["mypy-boto3-license-manager-user-subscriptions (>=1.26.0,<1.27.0)"]
+lightsail = ["mypy-boto3-lightsail (>=1.26.0,<1.27.0)"]
+location = ["mypy-boto3-location (>=1.26.0,<1.27.0)"]
+logs = ["mypy-boto3-logs (>=1.26.0,<1.27.0)"]
+lookoutequipment = ["mypy-boto3-lookoutequipment (>=1.26.0,<1.27.0)"]
+lookoutmetrics = ["mypy-boto3-lookoutmetrics (>=1.26.0,<1.27.0)"]
+lookoutvision = ["mypy-boto3-lookoutvision (>=1.26.0,<1.27.0)"]
+m2 = ["mypy-boto3-m2 (>=1.26.0,<1.27.0)"]
+machinelearning = ["mypy-boto3-machinelearning (>=1.26.0,<1.27.0)"]
+macie = ["mypy-boto3-macie (>=1.26.0,<1.27.0)"]
+macie2 = ["mypy-boto3-macie2 (>=1.26.0,<1.27.0)"]
+managedblockchain = ["mypy-boto3-managedblockchain (>=1.26.0,<1.27.0)"]
+marketplace-catalog = ["mypy-boto3-marketplace-catalog (>=1.26.0,<1.27.0)"]
+marketplace-entitlement = ["mypy-boto3-marketplace-entitlement (>=1.26.0,<1.27.0)"]
+marketplacecommerceanalytics = ["mypy-boto3-marketplacecommerceanalytics (>=1.26.0,<1.27.0)"]
+mediaconnect = ["mypy-boto3-mediaconnect (>=1.26.0,<1.27.0)"]
+mediaconvert = ["mypy-boto3-mediaconvert (>=1.26.0,<1.27.0)"]
+medialive = ["mypy-boto3-medialive (>=1.26.0,<1.27.0)"]
+mediapackage = ["mypy-boto3-mediapackage (>=1.26.0,<1.27.0)"]
+mediapackage-vod = ["mypy-boto3-mediapackage-vod (>=1.26.0,<1.27.0)"]
+mediastore = ["mypy-boto3-mediastore (>=1.26.0,<1.27.0)"]
+mediastore-data = ["mypy-boto3-mediastore-data (>=1.26.0,<1.27.0)"]
+mediatailor = ["mypy-boto3-mediatailor (>=1.26.0,<1.27.0)"]
+memorydb = ["mypy-boto3-memorydb (>=1.26.0,<1.27.0)"]
+meteringmarketplace = ["mypy-boto3-meteringmarketplace (>=1.26.0,<1.27.0)"]
+mgh = ["mypy-boto3-mgh (>=1.26.0,<1.27.0)"]
+mgn = ["mypy-boto3-mgn (>=1.26.0,<1.27.0)"]
+migration-hub-refactor-spaces = ["mypy-boto3-migration-hub-refactor-spaces (>=1.26.0,<1.27.0)"]
+migrationhub-config = ["mypy-boto3-migrationhub-config (>=1.26.0,<1.27.0)"]
+migrationhuborchestrator = ["mypy-boto3-migrationhuborchestrator (>=1.26.0,<1.27.0)"]
+migrationhubstrategy = ["mypy-boto3-migrationhubstrategy (>=1.26.0,<1.27.0)"]
+mobile = ["mypy-boto3-mobile (>=1.26.0,<1.27.0)"]
+mq = ["mypy-boto3-mq (>=1.26.0,<1.27.0)"]
+mturk = ["mypy-boto3-mturk (>=1.26.0,<1.27.0)"]
+mwaa = ["mypy-boto3-mwaa (>=1.26.0,<1.27.0)"]
+neptune = ["mypy-boto3-neptune (>=1.26.0,<1.27.0)"]
+network-firewall = ["mypy-boto3-network-firewall (>=1.26.0,<1.27.0)"]
+networkmanager = ["mypy-boto3-networkmanager (>=1.26.0,<1.27.0)"]
+nimble = ["mypy-boto3-nimble (>=1.26.0,<1.27.0)"]
+oam = ["mypy-boto3-oam (>=1.26.0,<1.27.0)"]
+omics = ["mypy-boto3-omics (>=1.26.0,<1.27.0)"]
+opensearch = ["mypy-boto3-opensearch (>=1.26.0,<1.27.0)"]
+opensearchserverless = ["mypy-boto3-opensearchserverless (>=1.26.0,<1.27.0)"]
+opsworks = ["mypy-boto3-opsworks (>=1.26.0,<1.27.0)"]
+opsworkscm = ["mypy-boto3-opsworkscm (>=1.26.0,<1.27.0)"]
+organizations = ["mypy-boto3-organizations (>=1.26.0,<1.27.0)"]
+outposts = ["mypy-boto3-outposts (>=1.26.0,<1.27.0)"]
+panorama = ["mypy-boto3-panorama (>=1.26.0,<1.27.0)"]
+personalize = ["mypy-boto3-personalize (>=1.26.0,<1.27.0)"]
+personalize-events = ["mypy-boto3-personalize-events (>=1.26.0,<1.27.0)"]
+personalize-runtime = ["mypy-boto3-personalize-runtime (>=1.26.0,<1.27.0)"]
+pi = ["mypy-boto3-pi (>=1.26.0,<1.27.0)"]
+pinpoint = ["mypy-boto3-pinpoint (>=1.26.0,<1.27.0)"]
+pinpoint-email = ["mypy-boto3-pinpoint-email (>=1.26.0,<1.27.0)"]
+pinpoint-sms-voice = ["mypy-boto3-pinpoint-sms-voice (>=1.26.0,<1.27.0)"]
+pinpoint-sms-voice-v2 = ["mypy-boto3-pinpoint-sms-voice-v2 (>=1.26.0,<1.27.0)"]
+pipes = ["mypy-boto3-pipes (>=1.26.0,<1.27.0)"]
+polly = ["mypy-boto3-polly (>=1.26.0,<1.27.0)"]
+pricing = ["mypy-boto3-pricing (>=1.26.0,<1.27.0)"]
+privatenetworks = ["mypy-boto3-privatenetworks (>=1.26.0,<1.27.0)"]
+proton = ["mypy-boto3-proton (>=1.26.0,<1.27.0)"]
+qldb = ["mypy-boto3-qldb (>=1.26.0,<1.27.0)"]
+qldb-session = ["mypy-boto3-qldb-session (>=1.26.0,<1.27.0)"]
+quicksight = ["mypy-boto3-quicksight (>=1.26.0,<1.27.0)"]
+ram = ["mypy-boto3-ram (>=1.26.0,<1.27.0)"]
+rbin = ["mypy-boto3-rbin (>=1.26.0,<1.27.0)"]
+rds = ["mypy-boto3-rds (>=1.26.0,<1.27.0)"]
+rds-data = ["mypy-boto3-rds-data (>=1.26.0,<1.27.0)"]
+redshift = ["mypy-boto3-redshift (>=1.26.0,<1.27.0)"]
+redshift-data = ["mypy-boto3-redshift-data (>=1.26.0,<1.27.0)"]
+redshift-serverless = ["mypy-boto3-redshift-serverless (>=1.26.0,<1.27.0)"]
+rekognition = ["mypy-boto3-rekognition (>=1.26.0,<1.27.0)"]
+resiliencehub = ["mypy-boto3-resiliencehub (>=1.26.0,<1.27.0)"]
+resource-explorer-2 = ["mypy-boto3-resource-explorer-2 (>=1.26.0,<1.27.0)"]
+resource-groups = ["mypy-boto3-resource-groups (>=1.26.0,<1.27.0)"]
+resourcegroupstaggingapi = ["mypy-boto3-resourcegroupstaggingapi (>=1.26.0,<1.27.0)"]
+robomaker = ["mypy-boto3-robomaker (>=1.26.0,<1.27.0)"]
+rolesanywhere = ["mypy-boto3-rolesanywhere (>=1.26.0,<1.27.0)"]
+route53 = ["mypy-boto3-route53 (>=1.26.0,<1.27.0)"]
+route53-recovery-cluster = ["mypy-boto3-route53-recovery-cluster (>=1.26.0,<1.27.0)"]
+route53-recovery-control-config = ["mypy-boto3-route53-recovery-control-config (>=1.26.0,<1.27.0)"]
+route53-recovery-readiness = ["mypy-boto3-route53-recovery-readiness (>=1.26.0,<1.27.0)"]
+route53domains = ["mypy-boto3-route53domains (>=1.26.0,<1.27.0)"]
+route53resolver = ["mypy-boto3-route53resolver (>=1.26.0,<1.27.0)"]
+rum = ["mypy-boto3-rum (>=1.26.0,<1.27.0)"]
+s3 = ["mypy-boto3-s3 (>=1.26.0,<1.27.0)"]
+s3control = ["mypy-boto3-s3control (>=1.26.0,<1.27.0)"]
+s3outposts = ["mypy-boto3-s3outposts (>=1.26.0,<1.27.0)"]
+sagemaker = ["mypy-boto3-sagemaker (>=1.26.0,<1.27.0)"]
+sagemaker-a2i-runtime = ["mypy-boto3-sagemaker-a2i-runtime (>=1.26.0,<1.27.0)"]
+sagemaker-edge = ["mypy-boto3-sagemaker-edge (>=1.26.0,<1.27.0)"]
+sagemaker-featurestore-runtime = ["mypy-boto3-sagemaker-featurestore-runtime (>=1.26.0,<1.27.0)"]
+sagemaker-geospatial = ["mypy-boto3-sagemaker-geospatial (>=1.26.0,<1.27.0)"]
+sagemaker-runtime = ["mypy-boto3-sagemaker-runtime (>=1.26.0,<1.27.0)"]
+savingsplans = ["mypy-boto3-savingsplans (>=1.26.0,<1.27.0)"]
+scheduler = ["mypy-boto3-scheduler (>=1.26.0,<1.27.0)"]
+schemas = ["mypy-boto3-schemas (>=1.26.0,<1.27.0)"]
+sdb = ["mypy-boto3-sdb (>=1.26.0,<1.27.0)"]
+secretsmanager = ["mypy-boto3-secretsmanager (>=1.26.0,<1.27.0)"]
+securityhub = ["mypy-boto3-securityhub (>=1.26.0,<1.27.0)"]
+securitylake = ["mypy-boto3-securitylake (>=1.26.0,<1.27.0)"]
+serverlessrepo = ["mypy-boto3-serverlessrepo (>=1.26.0,<1.27.0)"]
+service-quotas = ["mypy-boto3-service-quotas (>=1.26.0,<1.27.0)"]
+servicecatalog = ["mypy-boto3-servicecatalog (>=1.26.0,<1.27.0)"]
+servicecatalog-appregistry = ["mypy-boto3-servicecatalog-appregistry (>=1.26.0,<1.27.0)"]
+servicediscovery = ["mypy-boto3-servicediscovery (>=1.26.0,<1.27.0)"]
+ses = ["mypy-boto3-ses (>=1.26.0,<1.27.0)"]
+sesv2 = ["mypy-boto3-sesv2 (>=1.26.0,<1.27.0)"]
+shield = ["mypy-boto3-shield (>=1.26.0,<1.27.0)"]
+signer = ["mypy-boto3-signer (>=1.26.0,<1.27.0)"]
+simspaceweaver = ["mypy-boto3-simspaceweaver (>=1.26.0,<1.27.0)"]
+sms = ["mypy-boto3-sms (>=1.26.0,<1.27.0)"]
+sms-voice = ["mypy-boto3-sms-voice (>=1.26.0,<1.27.0)"]
+snow-device-management = ["mypy-boto3-snow-device-management (>=1.26.0,<1.27.0)"]
+snowball = ["mypy-boto3-snowball (>=1.26.0,<1.27.0)"]
+sns = ["mypy-boto3-sns (>=1.26.0,<1.27.0)"]
+sqs = ["mypy-boto3-sqs (>=1.26.0,<1.27.0)"]
+ssm = ["mypy-boto3-ssm (>=1.26.0,<1.27.0)"]
+ssm-contacts = ["mypy-boto3-ssm-contacts (>=1.26.0,<1.27.0)"]
+ssm-incidents = ["mypy-boto3-ssm-incidents (>=1.26.0,<1.27.0)"]
+ssm-sap = ["mypy-boto3-ssm-sap (>=1.26.0,<1.27.0)"]
+sso = ["mypy-boto3-sso (>=1.26.0,<1.27.0)"]
+sso-admin = ["mypy-boto3-sso-admin (>=1.26.0,<1.27.0)"]
+sso-oidc = ["mypy-boto3-sso-oidc (>=1.26.0,<1.27.0)"]
+stepfunctions = ["mypy-boto3-stepfunctions (>=1.26.0,<1.27.0)"]
+storagegateway = ["mypy-boto3-storagegateway (>=1.26.0,<1.27.0)"]
+sts = ["mypy-boto3-sts (>=1.26.0,<1.27.0)"]
+support = ["mypy-boto3-support (>=1.26.0,<1.27.0)"]
+support-app = ["mypy-boto3-support-app (>=1.26.0,<1.27.0)"]
+swf = ["mypy-boto3-swf (>=1.26.0,<1.27.0)"]
+synthetics = ["mypy-boto3-synthetics (>=1.26.0,<1.27.0)"]
+textract = ["mypy-boto3-textract (>=1.26.0,<1.27.0)"]
+timestream-query = ["mypy-boto3-timestream-query (>=1.26.0,<1.27.0)"]
+timestream-write = ["mypy-boto3-timestream-write (>=1.26.0,<1.27.0)"]
+transcribe = ["mypy-boto3-transcribe (>=1.26.0,<1.27.0)"]
+transfer = ["mypy-boto3-transfer (>=1.26.0,<1.27.0)"]
+translate = ["mypy-boto3-translate (>=1.26.0,<1.27.0)"]
+voice-id = ["mypy-boto3-voice-id (>=1.26.0,<1.27.0)"]
+waf = ["mypy-boto3-waf (>=1.26.0,<1.27.0)"]
+waf-regional = ["mypy-boto3-waf-regional (>=1.26.0,<1.27.0)"]
+wafv2 = ["mypy-boto3-wafv2 (>=1.26.0,<1.27.0)"]
+wellarchitected = ["mypy-boto3-wellarchitected (>=1.26.0,<1.27.0)"]
+wisdom = ["mypy-boto3-wisdom (>=1.26.0,<1.27.0)"]
+workdocs = ["mypy-boto3-workdocs (>=1.26.0,<1.27.0)"]
+worklink = ["mypy-boto3-worklink (>=1.26.0,<1.27.0)"]
+workmail = ["mypy-boto3-workmail (>=1.26.0,<1.27.0)"]
+workmailmessageflow = ["mypy-boto3-workmailmessageflow (>=1.26.0,<1.27.0)"]
+workspaces = ["mypy-boto3-workspaces (>=1.26.0,<1.27.0)"]
+workspaces-web = ["mypy-boto3-workspaces-web (>=1.26.0,<1.27.0)"]
+xray = ["mypy-boto3-xray (>=1.26.0,<1.27.0)"]
+
+[[package]]
+name = "botocore"
+version = "1.27.59"
+description = "Low-level, data-driven core of boto 3."
+category = "dev"
+optional = false
+python-versions = ">= 3.7"
+
+[package.dependencies]
+jmespath = ">=0.7.1,<2.0.0"
+python-dateutil = ">=2.1,<3.0.0"
+urllib3 = ">=1.25.4,<1.27"
+
+[package.extras]
+crt = ["awscrt (==0.14.0)"]
+
+[[package]]
+name = "botocore-stubs"
+version = "1.29.26"
+description = "Type annotations and code completion for botocore"
+category = "dev"
+optional = false
+python-versions = ">=3.7,<4.0"
+
+[package.dependencies]
+types-awscrt = "*"
+
+[[package]]
+name = "certifi"
+version = "2022.12.7"
+description = "Python package for providing Mozilla's CA Bundle."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "charset-normalizer"
+version = "2.1.1"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+category = "dev"
+optional = false
+python-versions = ">=3.6.0"
+
+[package.extras]
+unicode-backport = ["unicodedata2"]
+
+[[package]]
+name = "click"
+version = "8.1.3"
+description = "Composable command line interface toolkit"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+description = "Cross-platform colored terminal text."
+category = "dev"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+
+[[package]]
+name = "coverage"
+version = "6.5.0"
+description = "Code coverage measurement for Python"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
+
+[package.extras]
+toml = ["tomli"]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.0.4"
+description = "Backport of PEP 654 (exception groups)"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+test = ["pytest (>=6)"]
+
+[[package]]
+name = "freezegun"
+version = "1.2.2"
+description = "Let your Python tests travel through time"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+python-dateutil = ">=2.7"
+
+[[package]]
+name = "frozenlist"
+version = "1.3.3"
+description = "A list-like structure which implements collections.abc.MutableSequence"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "idna"
+version = "3.4"
+description = "Internationalized Domain Names in Applications (IDNA)"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "iniconfig"
+version = "1.1.1"
+description = "iniconfig: brain-dead simple config-ini parsing"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "isort"
+version = "5.10.1"
+description = "A Python utility / library to sort Python imports."
+category = "dev"
+optional = false
+python-versions = ">=3.6.1,<4.0"
+
+[package.extras]
+colors = ["colorama (>=0.4.3,<0.5.0)"]
+pipfile-deprecated-finder = ["pipreqs", "requirementslib"]
+plugins = ["setuptools"]
+requirements-deprecated-finder = ["pip-api", "pipreqs"]
+
+[[package]]
+name = "jmespath"
+version = "1.0.1"
+description = "JSON Matching Expressions"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "multidict"
+version = "6.0.3"
+description = "multidict implementation"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "mypy-boto3-s3"
+version = "1.26.0.post1"
+description = "Type annotations for boto3.S3 1.26.0 service generated with mypy-boto3-builder 7.11.10"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+typing-extensions = ">=4.1.0"
+
+[[package]]
+name = "mypy-extensions"
+version = "0.4.3"
+description = "Experimental type system extensions for programs checked with the mypy typechecker."
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "packaging"
+version = "22.0"
+description = "Core utilities for Python packages"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "pathspec"
+version = "0.10.2"
+description = "Utility library for gitignore style pattern matching of file paths."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "platformdirs"
+version = "2.6.0"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"]
+test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
+
+[[package]]
+name = "pluggy"
+version = "1.0.0"
+description = "plugin and hook calling mechanisms for python"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
+
+[[package]]
+name = "py-cpuinfo"
+version = "9.0.0"
+description = "Get CPU info with pure Python"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "py-spy"
+version = "0.3.14"
+description = "Sampling profiler for Python programs"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pytest"
+version = "7.2.0"
+description = "pytest: simple powerful testing with Python"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+attrs = ">=19.2.0"
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
+iniconfig = "*"
+packaging = "*"
+pluggy = ">=0.12,<2.0"
+tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
+
+[package.extras]
+testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
+
+[[package]]
+name = "pytest-asyncio"
+version = "0.20.3"
+description = "Pytest support for asyncio"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+pytest = ">=6.1.0"
+
+[package.extras]
+docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
+testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
+
+[[package]]
+name = "pytest-benchmark"
+version = "4.0.0"
+description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+py-cpuinfo = "*"
+pytest = ">=3.8"
+
+[package.extras]
+aspect = ["aspectlib"]
+elasticsearch = ["elasticsearch"]
+histogram = ["pygal", "pygaljs"]
+
+[[package]]
+name = "pytest-cov"
+version = "4.0.0"
+description = "Pytest plugin for measuring coverage."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+coverage = {version = ">=5.2.1", extras = ["toml"]}
+pytest = ">=4.6"
+
+[package.extras]
+testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
+
+[[package]]
+name = "pytest-freezegun"
+version = "0.4.2"
+description = "Wrap tests with fixtures in freeze_time"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+freezegun = ">0.3"
+pytest = ">=3.0.0"
+
+[[package]]
+name = "python-dateutil"
+version = "2.8.2"
+description = "Extensions to the standard Python datetime module"
+category = "dev"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+
+[package.dependencies]
+six = ">=1.5"
+
+[[package]]
+name = "requests"
+version = "2.28.1"
+description = "Python HTTP for Humans."
+category = "dev"
+optional = false
+python-versions = ">=3.7, <4"
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+charset-normalizer = ">=2,<3"
+idna = ">=2.5,<4"
+urllib3 = ">=1.21.1,<1.27"
+
+[package.extras]
+socks = ["PySocks (>=1.5.6,!=1.5.7)"]
+use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
+
+[[package]]
+name = "s3transfer"
+version = "0.6.0"
+description = "An Amazon S3 Transfer Manager"
+category = "dev"
+optional = false
+python-versions = ">= 3.7"
+
+[package.dependencies]
+botocore = ">=1.12.36,<2.0a.0"
+
+[package.extras]
+crt = ["botocore[crt] (>=1.20.29,<2.0a.0)"]
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "tomli"
+version = "2.0.1"
+description = "A lil' TOML parser"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "types-aiobotocore-lite"
+version = "2.4.1"
+description = "Type annotations for aiobotocore 2.4.1 generated with mypy-boto3-builder 7.11.11"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+botocore-stubs = "*"
+types-aiobotocore-s3 = {version = ">=2.4.0,<2.5.0", optional = true, markers = "extra == \"s3\""}
+typing-extensions = ">=4.1.0"
+
+[package.extras]
+accessanalyzer = ["types-aiobotocore-accessanalyzer (>=2.4.0,<2.5.0)"]
+account = ["types-aiobotocore-account (>=2.4.0,<2.5.0)"]
+acm = ["types-aiobotocore-acm (>=2.4.0,<2.5.0)"]
+acm-pca = ["types-aiobotocore-acm-pca (>=2.4.0,<2.5.0)"]
+alexaforbusiness = ["types-aiobotocore-alexaforbusiness (>=2.4.0,<2.5.0)"]
+all = ["types-aiobotocore-accessanalyzer (>=2.4.0,<2.5.0)", "types-aiobotocore-account (>=2.4.0,<2.5.0)", "types-aiobotocore-acm (>=2.4.0,<2.5.0)", "types-aiobotocore-acm-pca (>=2.4.0,<2.5.0)", "types-aiobotocore-alexaforbusiness (>=2.4.0,<2.5.0)", "types-aiobotocore-amp (>=2.4.0,<2.5.0)", "types-aiobotocore-amplify (>=2.4.0,<2.5.0)", "types-aiobotocore-amplifybackend (>=2.4.0,<2.5.0)", "types-aiobotocore-amplifyuibuilder (>=2.4.0,<2.5.0)", "types-aiobotocore-apigateway (>=2.4.0,<2.5.0)", "types-aiobotocore-apigatewaymanagementapi (>=2.4.0,<2.5.0)", "types-aiobotocore-apigatewayv2 (>=2.4.0,<2.5.0)", "types-aiobotocore-appconfig (>=2.4.0,<2.5.0)", "types-aiobotocore-appconfigdata (>=2.4.0,<2.5.0)", "types-aiobotocore-appflow (>=2.4.0,<2.5.0)", "types-aiobotocore-appintegrations (>=2.4.0,<2.5.0)", "types-aiobotocore-application-autoscaling (>=2.4.0,<2.5.0)", "types-aiobotocore-application-insights (>=2.4.0,<2.5.0)", "types-aiobotocore-applicationcostprofiler (>=2.4.0,<2.5.0)", "types-aiobotocore-appmesh (>=2.4.0,<2.5.0)", "types-aiobotocore-apprunner (>=2.4.0,<2.5.0)", "types-aiobotocore-appstream (>=2.4.0,<2.5.0)", "types-aiobotocore-appsync (>=2.4.0,<2.5.0)", "types-aiobotocore-athena (>=2.4.0,<2.5.0)", "types-aiobotocore-auditmanager (>=2.4.0,<2.5.0)", "types-aiobotocore-autoscaling (>=2.4.0,<2.5.0)", "types-aiobotocore-autoscaling-plans (>=2.4.0,<2.5.0)", "types-aiobotocore-backup (>=2.4.0,<2.5.0)", "types-aiobotocore-backup-gateway (>=2.4.0,<2.5.0)", "types-aiobotocore-backupstorage (>=2.4.0,<2.5.0)", "types-aiobotocore-batch (>=2.4.0,<2.5.0)", "types-aiobotocore-billingconductor (>=2.4.0,<2.5.0)", "types-aiobotocore-braket (>=2.4.0,<2.5.0)", "types-aiobotocore-budgets (>=2.4.0,<2.5.0)", "types-aiobotocore-ce (>=2.4.0,<2.5.0)", "types-aiobotocore-chime (>=2.4.0,<2.5.0)", "types-aiobotocore-chime-sdk-identity (>=2.4.0,<2.5.0)", "types-aiobotocore-chime-sdk-media-pipelines (>=2.4.0,<2.5.0)", "types-aiobotocore-chime-sdk-meetings (>=2.4.0,<2.5.0)", "types-aiobotocore-chime-sdk-messaging (>=2.4.0,<2.5.0)", "types-aiobotocore-cloud9 (>=2.4.0,<2.5.0)", "types-aiobotocore-cloudcontrol (>=2.4.0,<2.5.0)", "types-aiobotocore-clouddirectory (>=2.4.0,<2.5.0)", "types-aiobotocore-cloudformation (>=2.4.0,<2.5.0)", "types-aiobotocore-cloudfront (>=2.4.0,<2.5.0)", "types-aiobotocore-cloudhsm (>=2.4.0,<2.5.0)", "types-aiobotocore-cloudhsmv2 (>=2.4.0,<2.5.0)", "types-aiobotocore-cloudsearch (>=2.4.0,<2.5.0)", "types-aiobotocore-cloudsearchdomain (>=2.4.0,<2.5.0)", "types-aiobotocore-cloudtrail (>=2.4.0,<2.5.0)", "types-aiobotocore-cloudwatch (>=2.4.0,<2.5.0)", "types-aiobotocore-codeartifact (>=2.4.0,<2.5.0)", "types-aiobotocore-codebuild (>=2.4.0,<2.5.0)", "types-aiobotocore-codecommit (>=2.4.0,<2.5.0)", "types-aiobotocore-codedeploy (>=2.4.0,<2.5.0)", "types-aiobotocore-codeguru-reviewer (>=2.4.0,<2.5.0)", "types-aiobotocore-codeguruprofiler (>=2.4.0,<2.5.0)", "types-aiobotocore-codepipeline (>=2.4.0,<2.5.0)", "types-aiobotocore-codestar (>=2.4.0,<2.5.0)", "types-aiobotocore-codestar-connections (>=2.4.0,<2.5.0)", "types-aiobotocore-codestar-notifications (>=2.4.0,<2.5.0)", "types-aiobotocore-cognito-identity (>=2.4.0,<2.5.0)", "types-aiobotocore-cognito-idp (>=2.4.0,<2.5.0)", "types-aiobotocore-cognito-sync (>=2.4.0,<2.5.0)", "types-aiobotocore-comprehend (>=2.4.0,<2.5.0)", "types-aiobotocore-comprehendmedical (>=2.4.0,<2.5.0)", "types-aiobotocore-compute-optimizer (>=2.4.0,<2.5.0)", "types-aiobotocore-config (>=2.4.0,<2.5.0)", "types-aiobotocore-connect (>=2.4.0,<2.5.0)", "types-aiobotocore-connect-contact-lens (>=2.4.0,<2.5.0)", "types-aiobotocore-connectcampaigns (>=2.4.0,<2.5.0)", "types-aiobotocore-connectparticipant (>=2.4.0,<2.5.0)", "types-aiobotocore-cur (>=2.4.0,<2.5.0)", "types-aiobotocore-customer-profiles (>=2.4.0,<2.5.0)", "types-aiobotocore-databrew (>=2.4.0,<2.5.0)", "types-aiobotocore-dataexchange (>=2.4.0,<2.5.0)", "types-aiobotocore-datapipeline (>=2.4.0,<2.5.0)", "types-aiobotocore-datasync (>=2.4.0,<2.5.0)", "types-aiobotocore-dax (>=2.4.0,<2.5.0)", "types-aiobotocore-detective (>=2.4.0,<2.5.0)", "types-aiobotocore-devicefarm (>=2.4.0,<2.5.0)", "types-aiobotocore-devops-guru (>=2.4.0,<2.5.0)", "types-aiobotocore-directconnect (>=2.4.0,<2.5.0)", "types-aiobotocore-discovery (>=2.4.0,<2.5.0)", "types-aiobotocore-dlm (>=2.4.0,<2.5.0)", "types-aiobotocore-dms (>=2.4.0,<2.5.0)", "types-aiobotocore-docdb (>=2.4.0,<2.5.0)", "types-aiobotocore-drs (>=2.4.0,<2.5.0)", "types-aiobotocore-ds (>=2.4.0,<2.5.0)", "types-aiobotocore-dynamodb (>=2.4.0,<2.5.0)", "types-aiobotocore-dynamodbstreams (>=2.4.0,<2.5.0)", "types-aiobotocore-ebs (>=2.4.0,<2.5.0)", "types-aiobotocore-ec2 (>=2.4.0,<2.5.0)", "types-aiobotocore-ec2-instance-connect (>=2.4.0,<2.5.0)", "types-aiobotocore-ecr (>=2.4.0,<2.5.0)", "types-aiobotocore-ecr-public (>=2.4.0,<2.5.0)", "types-aiobotocore-ecs (>=2.4.0,<2.5.0)", "types-aiobotocore-efs (>=2.4.0,<2.5.0)", "types-aiobotocore-eks (>=2.4.0,<2.5.0)", "types-aiobotocore-elastic-inference (>=2.4.0,<2.5.0)", "types-aiobotocore-elasticache (>=2.4.0,<2.5.0)", "types-aiobotocore-elasticbeanstalk (>=2.4.0,<2.5.0)", "types-aiobotocore-elastictranscoder (>=2.4.0,<2.5.0)", "types-aiobotocore-elb (>=2.4.0,<2.5.0)", "types-aiobotocore-elbv2 (>=2.4.0,<2.5.0)", "types-aiobotocore-emr (>=2.4.0,<2.5.0)", "types-aiobotocore-emr-containers (>=2.4.0,<2.5.0)", "types-aiobotocore-emr-serverless (>=2.4.0,<2.5.0)", "types-aiobotocore-es (>=2.4.0,<2.5.0)", "types-aiobotocore-events (>=2.4.0,<2.5.0)", "types-aiobotocore-evidently (>=2.4.0,<2.5.0)", "types-aiobotocore-finspace (>=2.4.0,<2.5.0)", "types-aiobotocore-finspace-data (>=2.4.0,<2.5.0)", "types-aiobotocore-firehose (>=2.4.0,<2.5.0)", "types-aiobotocore-fis (>=2.4.0,<2.5.0)", "types-aiobotocore-fms (>=2.4.0,<2.5.0)", "types-aiobotocore-forecast (>=2.4.0,<2.5.0)", "types-aiobotocore-forecastquery (>=2.4.0,<2.5.0)", "types-aiobotocore-frauddetector (>=2.4.0,<2.5.0)", "types-aiobotocore-fsx (>=2.4.0,<2.5.0)", "types-aiobotocore-gamelift (>=2.4.0,<2.5.0)", "types-aiobotocore-gamesparks (>=2.4.0,<2.5.0)", "types-aiobotocore-glacier (>=2.4.0,<2.5.0)", "types-aiobotocore-globalaccelerator (>=2.4.0,<2.5.0)", "types-aiobotocore-glue (>=2.4.0,<2.5.0)", "types-aiobotocore-grafana (>=2.4.0,<2.5.0)", "types-aiobotocore-greengrass (>=2.4.0,<2.5.0)", "types-aiobotocore-greengrassv2 (>=2.4.0,<2.5.0)", "types-aiobotocore-groundstation (>=2.4.0,<2.5.0)", "types-aiobotocore-guardduty (>=2.4.0,<2.5.0)", "types-aiobotocore-health (>=2.4.0,<2.5.0)", "types-aiobotocore-healthlake (>=2.4.0,<2.5.0)", "types-aiobotocore-honeycode (>=2.4.0,<2.5.0)", "types-aiobotocore-iam (>=2.4.0,<2.5.0)", "types-aiobotocore-identitystore (>=2.4.0,<2.5.0)", "types-aiobotocore-imagebuilder (>=2.4.0,<2.5.0)", "types-aiobotocore-importexport (>=2.4.0,<2.5.0)", "types-aiobotocore-inspector (>=2.4.0,<2.5.0)", "types-aiobotocore-inspector2 (>=2.4.0,<2.5.0)", "types-aiobotocore-iot (>=2.4.0,<2.5.0)", "types-aiobotocore-iot-data (>=2.4.0,<2.5.0)", "types-aiobotocore-iot-jobs-data (>=2.4.0,<2.5.0)", "types-aiobotocore-iot1click-devices (>=2.4.0,<2.5.0)", "types-aiobotocore-iot1click-projects (>=2.4.0,<2.5.0)", "types-aiobotocore-iotanalytics (>=2.4.0,<2.5.0)", "types-aiobotocore-iotdeviceadvisor (>=2.4.0,<2.5.0)", "types-aiobotocore-iotevents (>=2.4.0,<2.5.0)", "types-aiobotocore-iotevents-data (>=2.4.0,<2.5.0)", "types-aiobotocore-iotfleethub (>=2.4.0,<2.5.0)", "types-aiobotocore-iotsecuretunneling (>=2.4.0,<2.5.0)", "types-aiobotocore-iotsitewise (>=2.4.0,<2.5.0)", "types-aiobotocore-iotthingsgraph (>=2.4.0,<2.5.0)", "types-aiobotocore-iottwinmaker (>=2.4.0,<2.5.0)", "types-aiobotocore-iotwireless (>=2.4.0,<2.5.0)", "types-aiobotocore-ivs (>=2.4.0,<2.5.0)", "types-aiobotocore-ivschat (>=2.4.0,<2.5.0)", "types-aiobotocore-kafka (>=2.4.0,<2.5.0)", "types-aiobotocore-kafkaconnect (>=2.4.0,<2.5.0)", "types-aiobotocore-kendra (>=2.4.0,<2.5.0)", "types-aiobotocore-keyspaces (>=2.4.0,<2.5.0)", "types-aiobotocore-kinesis (>=2.4.0,<2.5.0)", "types-aiobotocore-kinesis-video-archived-media (>=2.4.0,<2.5.0)", "types-aiobotocore-kinesis-video-media (>=2.4.0,<2.5.0)", "types-aiobotocore-kinesis-video-signaling (>=2.4.0,<2.5.0)", "types-aiobotocore-kinesisanalytics (>=2.4.0,<2.5.0)", "types-aiobotocore-kinesisanalyticsv2 (>=2.4.0,<2.5.0)", "types-aiobotocore-kinesisvideo (>=2.4.0,<2.5.0)", "types-aiobotocore-kms (>=2.4.0,<2.5.0)", "types-aiobotocore-lakeformation (>=2.4.0,<2.5.0)", "types-aiobotocore-lambda (>=2.4.0,<2.5.0)", "types-aiobotocore-lex-models (>=2.4.0,<2.5.0)", "types-aiobotocore-lex-runtime (>=2.4.0,<2.5.0)", "types-aiobotocore-lexv2-models (>=2.4.0,<2.5.0)", "types-aiobotocore-lexv2-runtime (>=2.4.0,<2.5.0)", "types-aiobotocore-license-manager (>=2.4.0,<2.5.0)", "types-aiobotocore-license-manager-user-subscriptions (>=2.4.0,<2.5.0)", "types-aiobotocore-lightsail (>=2.4.0,<2.5.0)", "types-aiobotocore-location (>=2.4.0,<2.5.0)", "types-aiobotocore-logs (>=2.4.0,<2.5.0)", "types-aiobotocore-lookoutequipment (>=2.4.0,<2.5.0)", "types-aiobotocore-lookoutmetrics (>=2.4.0,<2.5.0)", "types-aiobotocore-lookoutvision (>=2.4.0,<2.5.0)", "types-aiobotocore-m2 (>=2.4.0,<2.5.0)", "types-aiobotocore-machinelearning (>=2.4.0,<2.5.0)", "types-aiobotocore-macie (>=2.4.0,<2.5.0)", "types-aiobotocore-macie2 (>=2.4.0,<2.5.0)", "types-aiobotocore-managedblockchain (>=2.4.0,<2.5.0)", "types-aiobotocore-marketplace-catalog (>=2.4.0,<2.5.0)", "types-aiobotocore-marketplace-entitlement (>=2.4.0,<2.5.0)", "types-aiobotocore-marketplacecommerceanalytics (>=2.4.0,<2.5.0)", "types-aiobotocore-mediaconnect (>=2.4.0,<2.5.0)", "types-aiobotocore-mediaconvert (>=2.4.0,<2.5.0)", "types-aiobotocore-medialive (>=2.4.0,<2.5.0)", "types-aiobotocore-mediapackage (>=2.4.0,<2.5.0)", "types-aiobotocore-mediapackage-vod (>=2.4.0,<2.5.0)", "types-aiobotocore-mediastore (>=2.4.0,<2.5.0)", "types-aiobotocore-mediastore-data (>=2.4.0,<2.5.0)", "types-aiobotocore-mediatailor (>=2.4.0,<2.5.0)", "types-aiobotocore-memorydb (>=2.4.0,<2.5.0)", "types-aiobotocore-meteringmarketplace (>=2.4.0,<2.5.0)", "types-aiobotocore-mgh (>=2.4.0,<2.5.0)", "types-aiobotocore-mgn (>=2.4.0,<2.5.0)", "types-aiobotocore-migration-hub-refactor-spaces (>=2.4.0,<2.5.0)", "types-aiobotocore-migrationhub-config (>=2.4.0,<2.5.0)", "types-aiobotocore-migrationhubstrategy (>=2.4.0,<2.5.0)", "types-aiobotocore-mobile (>=2.4.0,<2.5.0)", "types-aiobotocore-mq (>=2.4.0,<2.5.0)", "types-aiobotocore-mturk (>=2.4.0,<2.5.0)", "types-aiobotocore-mwaa (>=2.4.0,<2.5.0)", "types-aiobotocore-neptune (>=2.4.0,<2.5.0)", "types-aiobotocore-network-firewall (>=2.4.0,<2.5.0)", "types-aiobotocore-networkmanager (>=2.4.0,<2.5.0)", "types-aiobotocore-nimble (>=2.4.0,<2.5.0)", "types-aiobotocore-opensearch (>=2.4.0,<2.5.0)", "types-aiobotocore-opsworks (>=2.4.0,<2.5.0)", "types-aiobotocore-opsworkscm (>=2.4.0,<2.5.0)", "types-aiobotocore-organizations (>=2.4.0,<2.5.0)", "types-aiobotocore-outposts (>=2.4.0,<2.5.0)", "types-aiobotocore-panorama (>=2.4.0,<2.5.0)", "types-aiobotocore-personalize (>=2.4.0,<2.5.0)", "types-aiobotocore-personalize-events (>=2.4.0,<2.5.0)", "types-aiobotocore-personalize-runtime (>=2.4.0,<2.5.0)", "types-aiobotocore-pi (>=2.4.0,<2.5.0)", "types-aiobotocore-pinpoint (>=2.4.0,<2.5.0)", "types-aiobotocore-pinpoint-email (>=2.4.0,<2.5.0)", "types-aiobotocore-pinpoint-sms-voice (>=2.4.0,<2.5.0)", "types-aiobotocore-pinpoint-sms-voice-v2 (>=2.4.0,<2.5.0)", "types-aiobotocore-polly (>=2.4.0,<2.5.0)", "types-aiobotocore-pricing (>=2.4.0,<2.5.0)", "types-aiobotocore-privatenetworks (>=2.4.0,<2.5.0)", "types-aiobotocore-proton (>=2.4.0,<2.5.0)", "types-aiobotocore-qldb (>=2.4.0,<2.5.0)", "types-aiobotocore-qldb-session (>=2.4.0,<2.5.0)", "types-aiobotocore-quicksight (>=2.4.0,<2.5.0)", "types-aiobotocore-ram (>=2.4.0,<2.5.0)", "types-aiobotocore-rbin (>=2.4.0,<2.5.0)", "types-aiobotocore-rds (>=2.4.0,<2.5.0)", "types-aiobotocore-rds-data (>=2.4.0,<2.5.0)", "types-aiobotocore-redshift (>=2.4.0,<2.5.0)", "types-aiobotocore-redshift-data (>=2.4.0,<2.5.0)", "types-aiobotocore-redshift-serverless (>=2.4.0,<2.5.0)", "types-aiobotocore-rekognition (>=2.4.0,<2.5.0)", "types-aiobotocore-resiliencehub (>=2.4.0,<2.5.0)", "types-aiobotocore-resource-groups (>=2.4.0,<2.5.0)", "types-aiobotocore-resourcegroupstaggingapi (>=2.4.0,<2.5.0)", "types-aiobotocore-robomaker (>=2.4.0,<2.5.0)", "types-aiobotocore-rolesanywhere (>=2.4.0,<2.5.0)", "types-aiobotocore-route53 (>=2.4.0,<2.5.0)", "types-aiobotocore-route53-recovery-cluster (>=2.4.0,<2.5.0)", "types-aiobotocore-route53-recovery-control-config (>=2.4.0,<2.5.0)", "types-aiobotocore-route53-recovery-readiness (>=2.4.0,<2.5.0)", "types-aiobotocore-route53domains (>=2.4.0,<2.5.0)", "types-aiobotocore-route53resolver (>=2.4.0,<2.5.0)", "types-aiobotocore-rum (>=2.4.0,<2.5.0)", "types-aiobotocore-s3 (>=2.4.0,<2.5.0)", "types-aiobotocore-s3control (>=2.4.0,<2.5.0)", "types-aiobotocore-s3outposts (>=2.4.0,<2.5.0)", "types-aiobotocore-sagemaker (>=2.4.0,<2.5.0)", "types-aiobotocore-sagemaker-a2i-runtime (>=2.4.0,<2.5.0)", "types-aiobotocore-sagemaker-edge (>=2.4.0,<2.5.0)", "types-aiobotocore-sagemaker-featurestore-runtime (>=2.4.0,<2.5.0)", "types-aiobotocore-sagemaker-runtime (>=2.4.0,<2.5.0)", "types-aiobotocore-savingsplans (>=2.4.0,<2.5.0)", "types-aiobotocore-schemas (>=2.4.0,<2.5.0)", "types-aiobotocore-sdb (>=2.4.0,<2.5.0)", "types-aiobotocore-secretsmanager (>=2.4.0,<2.5.0)", "types-aiobotocore-securityhub (>=2.4.0,<2.5.0)", "types-aiobotocore-serverlessrepo (>=2.4.0,<2.5.0)", "types-aiobotocore-service-quotas (>=2.4.0,<2.5.0)", "types-aiobotocore-servicecatalog (>=2.4.0,<2.5.0)", "types-aiobotocore-servicecatalog-appregistry (>=2.4.0,<2.5.0)", "types-aiobotocore-servicediscovery (>=2.4.0,<2.5.0)", "types-aiobotocore-ses (>=2.4.0,<2.5.0)", "types-aiobotocore-sesv2 (>=2.4.0,<2.5.0)", "types-aiobotocore-shield (>=2.4.0,<2.5.0)", "types-aiobotocore-signer (>=2.4.0,<2.5.0)", "types-aiobotocore-sms (>=2.4.0,<2.5.0)", "types-aiobotocore-sms-voice (>=2.4.0,<2.5.0)", "types-aiobotocore-snow-device-management (>=2.4.0,<2.5.0)", "types-aiobotocore-snowball (>=2.4.0,<2.5.0)", "types-aiobotocore-sns (>=2.4.0,<2.5.0)", "types-aiobotocore-sqs (>=2.4.0,<2.5.0)", "types-aiobotocore-ssm (>=2.4.0,<2.5.0)", "types-aiobotocore-ssm-contacts (>=2.4.0,<2.5.0)", "types-aiobotocore-ssm-incidents (>=2.4.0,<2.5.0)", "types-aiobotocore-sso (>=2.4.0,<2.5.0)", "types-aiobotocore-sso-admin (>=2.4.0,<2.5.0)", "types-aiobotocore-sso-oidc (>=2.4.0,<2.5.0)", "types-aiobotocore-stepfunctions (>=2.4.0,<2.5.0)", "types-aiobotocore-storagegateway (>=2.4.0,<2.5.0)", "types-aiobotocore-sts (>=2.4.0,<2.5.0)", "types-aiobotocore-support (>=2.4.0,<2.5.0)", "types-aiobotocore-support-app (>=2.4.0,<2.5.0)", "types-aiobotocore-swf (>=2.4.0,<2.5.0)", "types-aiobotocore-synthetics (>=2.4.0,<2.5.0)", "types-aiobotocore-textract (>=2.4.0,<2.5.0)", "types-aiobotocore-timestream-query (>=2.4.0,<2.5.0)", "types-aiobotocore-timestream-write (>=2.4.0,<2.5.0)", "types-aiobotocore-transcribe (>=2.4.0,<2.5.0)", "types-aiobotocore-transfer (>=2.4.0,<2.5.0)", "types-aiobotocore-translate (>=2.4.0,<2.5.0)", "types-aiobotocore-voice-id (>=2.4.0,<2.5.0)", "types-aiobotocore-waf (>=2.4.0,<2.5.0)", "types-aiobotocore-waf-regional (>=2.4.0,<2.5.0)", "types-aiobotocore-wafv2 (>=2.4.0,<2.5.0)", "types-aiobotocore-wellarchitected (>=2.4.0,<2.5.0)", "types-aiobotocore-wisdom (>=2.4.0,<2.5.0)", "types-aiobotocore-workdocs (>=2.4.0,<2.5.0)", "types-aiobotocore-worklink (>=2.4.0,<2.5.0)", "types-aiobotocore-workmail (>=2.4.0,<2.5.0)", "types-aiobotocore-workmailmessageflow (>=2.4.0,<2.5.0)", "types-aiobotocore-workspaces (>=2.4.0,<2.5.0)", "types-aiobotocore-workspaces-web (>=2.4.0,<2.5.0)", "types-aiobotocore-xray (>=2.4.0,<2.5.0)"]
+amp = ["types-aiobotocore-amp (>=2.4.0,<2.5.0)"]
+amplify = ["types-aiobotocore-amplify (>=2.4.0,<2.5.0)"]
+amplifybackend = ["types-aiobotocore-amplifybackend (>=2.4.0,<2.5.0)"]
+amplifyuibuilder = ["types-aiobotocore-amplifyuibuilder (>=2.4.0,<2.5.0)"]
+apigateway = ["types-aiobotocore-apigateway (>=2.4.0,<2.5.0)"]
+apigatewaymanagementapi = ["types-aiobotocore-apigatewaymanagementapi (>=2.4.0,<2.5.0)"]
+apigatewayv2 = ["types-aiobotocore-apigatewayv2 (>=2.4.0,<2.5.0)"]
+appconfig = ["types-aiobotocore-appconfig (>=2.4.0,<2.5.0)"]
+appconfigdata = ["types-aiobotocore-appconfigdata (>=2.4.0,<2.5.0)"]
+appflow = ["types-aiobotocore-appflow (>=2.4.0,<2.5.0)"]
+appintegrations = ["types-aiobotocore-appintegrations (>=2.4.0,<2.5.0)"]
+application-autoscaling = ["types-aiobotocore-application-autoscaling (>=2.4.0,<2.5.0)"]
+application-insights = ["types-aiobotocore-application-insights (>=2.4.0,<2.5.0)"]
+applicationcostprofiler = ["types-aiobotocore-applicationcostprofiler (>=2.4.0,<2.5.0)"]
+appmesh = ["types-aiobotocore-appmesh (>=2.4.0,<2.5.0)"]
+apprunner = ["types-aiobotocore-apprunner (>=2.4.0,<2.5.0)"]
+appstream = ["types-aiobotocore-appstream (>=2.4.0,<2.5.0)"]
+appsync = ["types-aiobotocore-appsync (>=2.4.0,<2.5.0)"]
+athena = ["types-aiobotocore-athena (>=2.4.0,<2.5.0)"]
+auditmanager = ["types-aiobotocore-auditmanager (>=2.4.0,<2.5.0)"]
+autoscaling = ["types-aiobotocore-autoscaling (>=2.4.0,<2.5.0)"]
+autoscaling-plans = ["types-aiobotocore-autoscaling-plans (>=2.4.0,<2.5.0)"]
+backup = ["types-aiobotocore-backup (>=2.4.0,<2.5.0)"]
+backup-gateway = ["types-aiobotocore-backup-gateway (>=2.4.0,<2.5.0)"]
+backupstorage = ["types-aiobotocore-backupstorage (>=2.4.0,<2.5.0)"]
+batch = ["types-aiobotocore-batch (>=2.4.0,<2.5.0)"]
+billingconductor = ["types-aiobotocore-billingconductor (>=2.4.0,<2.5.0)"]
+braket = ["types-aiobotocore-braket (>=2.4.0,<2.5.0)"]
+budgets = ["types-aiobotocore-budgets (>=2.4.0,<2.5.0)"]
+ce = ["types-aiobotocore-ce (>=2.4.0,<2.5.0)"]
+chime = ["types-aiobotocore-chime (>=2.4.0,<2.5.0)"]
+chime-sdk-identity = ["types-aiobotocore-chime-sdk-identity (>=2.4.0,<2.5.0)"]
+chime-sdk-media-pipelines = ["types-aiobotocore-chime-sdk-media-pipelines (>=2.4.0,<2.5.0)"]
+chime-sdk-meetings = ["types-aiobotocore-chime-sdk-meetings (>=2.4.0,<2.5.0)"]
+chime-sdk-messaging = ["types-aiobotocore-chime-sdk-messaging (>=2.4.0,<2.5.0)"]
+cloud9 = ["types-aiobotocore-cloud9 (>=2.4.0,<2.5.0)"]
+cloudcontrol = ["types-aiobotocore-cloudcontrol (>=2.4.0,<2.5.0)"]
+clouddirectory = ["types-aiobotocore-clouddirectory (>=2.4.0,<2.5.0)"]
+cloudformation = ["types-aiobotocore-cloudformation (>=2.4.0,<2.5.0)"]
+cloudfront = ["types-aiobotocore-cloudfront (>=2.4.0,<2.5.0)"]
+cloudhsm = ["types-aiobotocore-cloudhsm (>=2.4.0,<2.5.0)"]
+cloudhsmv2 = ["types-aiobotocore-cloudhsmv2 (>=2.4.0,<2.5.0)"]
+cloudsearch = ["types-aiobotocore-cloudsearch (>=2.4.0,<2.5.0)"]
+cloudsearchdomain = ["types-aiobotocore-cloudsearchdomain (>=2.4.0,<2.5.0)"]
+cloudtrail = ["types-aiobotocore-cloudtrail (>=2.4.0,<2.5.0)"]
+cloudwatch = ["types-aiobotocore-cloudwatch (>=2.4.0,<2.5.0)"]
+codeartifact = ["types-aiobotocore-codeartifact (>=2.4.0,<2.5.0)"]
+codebuild = ["types-aiobotocore-codebuild (>=2.4.0,<2.5.0)"]
+codecommit = ["types-aiobotocore-codecommit (>=2.4.0,<2.5.0)"]
+codedeploy = ["types-aiobotocore-codedeploy (>=2.4.0,<2.5.0)"]
+codeguru-reviewer = ["types-aiobotocore-codeguru-reviewer (>=2.4.0,<2.5.0)"]
+codeguruprofiler = ["types-aiobotocore-codeguruprofiler (>=2.4.0,<2.5.0)"]
+codepipeline = ["types-aiobotocore-codepipeline (>=2.4.0,<2.5.0)"]
+codestar = ["types-aiobotocore-codestar (>=2.4.0,<2.5.0)"]
+codestar-connections = ["types-aiobotocore-codestar-connections (>=2.4.0,<2.5.0)"]
+codestar-notifications = ["types-aiobotocore-codestar-notifications (>=2.4.0,<2.5.0)"]
+cognito-identity = ["types-aiobotocore-cognito-identity (>=2.4.0,<2.5.0)"]
+cognito-idp = ["types-aiobotocore-cognito-idp (>=2.4.0,<2.5.0)"]
+cognito-sync = ["types-aiobotocore-cognito-sync (>=2.4.0,<2.5.0)"]
+comprehend = ["types-aiobotocore-comprehend (>=2.4.0,<2.5.0)"]
+comprehendmedical = ["types-aiobotocore-comprehendmedical (>=2.4.0,<2.5.0)"]
+compute-optimizer = ["types-aiobotocore-compute-optimizer (>=2.4.0,<2.5.0)"]
+config = ["types-aiobotocore-config (>=2.4.0,<2.5.0)"]
+connect = ["types-aiobotocore-connect (>=2.4.0,<2.5.0)"]
+connect-contact-lens = ["types-aiobotocore-connect-contact-lens (>=2.4.0,<2.5.0)"]
+connectcampaigns = ["types-aiobotocore-connectcampaigns (>=2.4.0,<2.5.0)"]
+connectparticipant = ["types-aiobotocore-connectparticipant (>=2.4.0,<2.5.0)"]
+cur = ["types-aiobotocore-cur (>=2.4.0,<2.5.0)"]
+customer-profiles = ["types-aiobotocore-customer-profiles (>=2.4.0,<2.5.0)"]
+databrew = ["types-aiobotocore-databrew (>=2.4.0,<2.5.0)"]
+dataexchange = ["types-aiobotocore-dataexchange (>=2.4.0,<2.5.0)"]
+datapipeline = ["types-aiobotocore-datapipeline (>=2.4.0,<2.5.0)"]
+datasync = ["types-aiobotocore-datasync (>=2.4.0,<2.5.0)"]
+dax = ["types-aiobotocore-dax (>=2.4.0,<2.5.0)"]
+detective = ["types-aiobotocore-detective (>=2.4.0,<2.5.0)"]
+devicefarm = ["types-aiobotocore-devicefarm (>=2.4.0,<2.5.0)"]
+devops-guru = ["types-aiobotocore-devops-guru (>=2.4.0,<2.5.0)"]
+directconnect = ["types-aiobotocore-directconnect (>=2.4.0,<2.5.0)"]
+discovery = ["types-aiobotocore-discovery (>=2.4.0,<2.5.0)"]
+dlm = ["types-aiobotocore-dlm (>=2.4.0,<2.5.0)"]
+dms = ["types-aiobotocore-dms (>=2.4.0,<2.5.0)"]
+docdb = ["types-aiobotocore-docdb (>=2.4.0,<2.5.0)"]
+drs = ["types-aiobotocore-drs (>=2.4.0,<2.5.0)"]
+ds = ["types-aiobotocore-ds (>=2.4.0,<2.5.0)"]
+dynamodb = ["types-aiobotocore-dynamodb (>=2.4.0,<2.5.0)"]
+dynamodbstreams = ["types-aiobotocore-dynamodbstreams (>=2.4.0,<2.5.0)"]
+ebs = ["types-aiobotocore-ebs (>=2.4.0,<2.5.0)"]
+ec2 = ["types-aiobotocore-ec2 (>=2.4.0,<2.5.0)"]
+ec2-instance-connect = ["types-aiobotocore-ec2-instance-connect (>=2.4.0,<2.5.0)"]
+ecr = ["types-aiobotocore-ecr (>=2.4.0,<2.5.0)"]
+ecr-public = ["types-aiobotocore-ecr-public (>=2.4.0,<2.5.0)"]
+ecs = ["types-aiobotocore-ecs (>=2.4.0,<2.5.0)"]
+efs = ["types-aiobotocore-efs (>=2.4.0,<2.5.0)"]
+eks = ["types-aiobotocore-eks (>=2.4.0,<2.5.0)"]
+elastic-inference = ["types-aiobotocore-elastic-inference (>=2.4.0,<2.5.0)"]
+elasticache = ["types-aiobotocore-elasticache (>=2.4.0,<2.5.0)"]
+elasticbeanstalk = ["types-aiobotocore-elasticbeanstalk (>=2.4.0,<2.5.0)"]
+elastictranscoder = ["types-aiobotocore-elastictranscoder (>=2.4.0,<2.5.0)"]
+elb = ["types-aiobotocore-elb (>=2.4.0,<2.5.0)"]
+elbv2 = ["types-aiobotocore-elbv2 (>=2.4.0,<2.5.0)"]
+emr = ["types-aiobotocore-emr (>=2.4.0,<2.5.0)"]
+emr-containers = ["types-aiobotocore-emr-containers (>=2.4.0,<2.5.0)"]
+emr-serverless = ["types-aiobotocore-emr-serverless (>=2.4.0,<2.5.0)"]
+es = ["types-aiobotocore-es (>=2.4.0,<2.5.0)"]
+essential = ["types-aiobotocore-cloudformation (>=2.4.0,<2.5.0)", "types-aiobotocore-dynamodb (>=2.4.0,<2.5.0)", "types-aiobotocore-ec2 (>=2.4.0,<2.5.0)", "types-aiobotocore-lambda (>=2.4.0,<2.5.0)", "types-aiobotocore-rds (>=2.4.0,<2.5.0)", "types-aiobotocore-s3 (>=2.4.0,<2.5.0)", "types-aiobotocore-sqs (>=2.4.0,<2.5.0)"]
+events = ["types-aiobotocore-events (>=2.4.0,<2.5.0)"]
+evidently = ["types-aiobotocore-evidently (>=2.4.0,<2.5.0)"]
+finspace = ["types-aiobotocore-finspace (>=2.4.0,<2.5.0)"]
+finspace-data = ["types-aiobotocore-finspace-data (>=2.4.0,<2.5.0)"]
+firehose = ["types-aiobotocore-firehose (>=2.4.0,<2.5.0)"]
+fis = ["types-aiobotocore-fis (>=2.4.0,<2.5.0)"]
+fms = ["types-aiobotocore-fms (>=2.4.0,<2.5.0)"]
+forecast = ["types-aiobotocore-forecast (>=2.4.0,<2.5.0)"]
+forecastquery = ["types-aiobotocore-forecastquery (>=2.4.0,<2.5.0)"]
+frauddetector = ["types-aiobotocore-frauddetector (>=2.4.0,<2.5.0)"]
+fsx = ["types-aiobotocore-fsx (>=2.4.0,<2.5.0)"]
+gamelift = ["types-aiobotocore-gamelift (>=2.4.0,<2.5.0)"]
+gamesparks = ["types-aiobotocore-gamesparks (>=2.4.0,<2.5.0)"]
+glacier = ["types-aiobotocore-glacier (>=2.4.0,<2.5.0)"]
+globalaccelerator = ["types-aiobotocore-globalaccelerator (>=2.4.0,<2.5.0)"]
+glue = ["types-aiobotocore-glue (>=2.4.0,<2.5.0)"]
+grafana = ["types-aiobotocore-grafana (>=2.4.0,<2.5.0)"]
+greengrass = ["types-aiobotocore-greengrass (>=2.4.0,<2.5.0)"]
+greengrassv2 = ["types-aiobotocore-greengrassv2 (>=2.4.0,<2.5.0)"]
+groundstation = ["types-aiobotocore-groundstation (>=2.4.0,<2.5.0)"]
+guardduty = ["types-aiobotocore-guardduty (>=2.4.0,<2.5.0)"]
+health = ["types-aiobotocore-health (>=2.4.0,<2.5.0)"]
+healthlake = ["types-aiobotocore-healthlake (>=2.4.0,<2.5.0)"]
+honeycode = ["types-aiobotocore-honeycode (>=2.4.0,<2.5.0)"]
+iam = ["types-aiobotocore-iam (>=2.4.0,<2.5.0)"]
+identitystore = ["types-aiobotocore-identitystore (>=2.4.0,<2.5.0)"]
+imagebuilder = ["types-aiobotocore-imagebuilder (>=2.4.0,<2.5.0)"]
+importexport = ["types-aiobotocore-importexport (>=2.4.0,<2.5.0)"]
+inspector = ["types-aiobotocore-inspector (>=2.4.0,<2.5.0)"]
+inspector2 = ["types-aiobotocore-inspector2 (>=2.4.0,<2.5.0)"]
+iot = ["types-aiobotocore-iot (>=2.4.0,<2.5.0)"]
+iot-data = ["types-aiobotocore-iot-data (>=2.4.0,<2.5.0)"]
+iot-jobs-data = ["types-aiobotocore-iot-jobs-data (>=2.4.0,<2.5.0)"]
+iot1click-devices = ["types-aiobotocore-iot1click-devices (>=2.4.0,<2.5.0)"]
+iot1click-projects = ["types-aiobotocore-iot1click-projects (>=2.4.0,<2.5.0)"]
+iotanalytics = ["types-aiobotocore-iotanalytics (>=2.4.0,<2.5.0)"]
+iotdeviceadvisor = ["types-aiobotocore-iotdeviceadvisor (>=2.4.0,<2.5.0)"]
+iotevents = ["types-aiobotocore-iotevents (>=2.4.0,<2.5.0)"]
+iotevents-data = ["types-aiobotocore-iotevents-data (>=2.4.0,<2.5.0)"]
+iotfleethub = ["types-aiobotocore-iotfleethub (>=2.4.0,<2.5.0)"]
+iotsecuretunneling = ["types-aiobotocore-iotsecuretunneling (>=2.4.0,<2.5.0)"]
+iotsitewise = ["types-aiobotocore-iotsitewise (>=2.4.0,<2.5.0)"]
+iotthingsgraph = ["types-aiobotocore-iotthingsgraph (>=2.4.0,<2.5.0)"]
+iottwinmaker = ["types-aiobotocore-iottwinmaker (>=2.4.0,<2.5.0)"]
+iotwireless = ["types-aiobotocore-iotwireless (>=2.4.0,<2.5.0)"]
+ivs = ["types-aiobotocore-ivs (>=2.4.0,<2.5.0)"]
+ivschat = ["types-aiobotocore-ivschat (>=2.4.0,<2.5.0)"]
+kafka = ["types-aiobotocore-kafka (>=2.4.0,<2.5.0)"]
+kafkaconnect = ["types-aiobotocore-kafkaconnect (>=2.4.0,<2.5.0)"]
+kendra = ["types-aiobotocore-kendra (>=2.4.0,<2.5.0)"]
+keyspaces = ["types-aiobotocore-keyspaces (>=2.4.0,<2.5.0)"]
+kinesis = ["types-aiobotocore-kinesis (>=2.4.0,<2.5.0)"]
+kinesis-video-archived-media = ["types-aiobotocore-kinesis-video-archived-media (>=2.4.0,<2.5.0)"]
+kinesis-video-media = ["types-aiobotocore-kinesis-video-media (>=2.4.0,<2.5.0)"]
+kinesis-video-signaling = ["types-aiobotocore-kinesis-video-signaling (>=2.4.0,<2.5.0)"]
+kinesisanalytics = ["types-aiobotocore-kinesisanalytics (>=2.4.0,<2.5.0)"]
+kinesisanalyticsv2 = ["types-aiobotocore-kinesisanalyticsv2 (>=2.4.0,<2.5.0)"]
+kinesisvideo = ["types-aiobotocore-kinesisvideo (>=2.4.0,<2.5.0)"]
+kms = ["types-aiobotocore-kms (>=2.4.0,<2.5.0)"]
+lakeformation = ["types-aiobotocore-lakeformation (>=2.4.0,<2.5.0)"]
+lambda = ["types-aiobotocore-lambda (>=2.4.0,<2.5.0)"]
+lex-models = ["types-aiobotocore-lex-models (>=2.4.0,<2.5.0)"]
+lex-runtime = ["types-aiobotocore-lex-runtime (>=2.4.0,<2.5.0)"]
+lexv2-models = ["types-aiobotocore-lexv2-models (>=2.4.0,<2.5.0)"]
+lexv2-runtime = ["types-aiobotocore-lexv2-runtime (>=2.4.0,<2.5.0)"]
+license-manager = ["types-aiobotocore-license-manager (>=2.4.0,<2.5.0)"]
+license-manager-user-subscriptions = ["types-aiobotocore-license-manager-user-subscriptions (>=2.4.0,<2.5.0)"]
+lightsail = ["types-aiobotocore-lightsail (>=2.4.0,<2.5.0)"]
+location = ["types-aiobotocore-location (>=2.4.0,<2.5.0)"]
+logs = ["types-aiobotocore-logs (>=2.4.0,<2.5.0)"]
+lookoutequipment = ["types-aiobotocore-lookoutequipment (>=2.4.0,<2.5.0)"]
+lookoutmetrics = ["types-aiobotocore-lookoutmetrics (>=2.4.0,<2.5.0)"]
+lookoutvision = ["types-aiobotocore-lookoutvision (>=2.4.0,<2.5.0)"]
+m2 = ["types-aiobotocore-m2 (>=2.4.0,<2.5.0)"]
+machinelearning = ["types-aiobotocore-machinelearning (>=2.4.0,<2.5.0)"]
+macie = ["types-aiobotocore-macie (>=2.4.0,<2.5.0)"]
+macie2 = ["types-aiobotocore-macie2 (>=2.4.0,<2.5.0)"]
+managedblockchain = ["types-aiobotocore-managedblockchain (>=2.4.0,<2.5.0)"]
+marketplace-catalog = ["types-aiobotocore-marketplace-catalog (>=2.4.0,<2.5.0)"]
+marketplace-entitlement = ["types-aiobotocore-marketplace-entitlement (>=2.4.0,<2.5.0)"]
+marketplacecommerceanalytics = ["types-aiobotocore-marketplacecommerceanalytics (>=2.4.0,<2.5.0)"]
+mediaconnect = ["types-aiobotocore-mediaconnect (>=2.4.0,<2.5.0)"]
+mediaconvert = ["types-aiobotocore-mediaconvert (>=2.4.0,<2.5.0)"]
+medialive = ["types-aiobotocore-medialive (>=2.4.0,<2.5.0)"]
+mediapackage = ["types-aiobotocore-mediapackage (>=2.4.0,<2.5.0)"]
+mediapackage-vod = ["types-aiobotocore-mediapackage-vod (>=2.4.0,<2.5.0)"]
+mediastore = ["types-aiobotocore-mediastore (>=2.4.0,<2.5.0)"]
+mediastore-data = ["types-aiobotocore-mediastore-data (>=2.4.0,<2.5.0)"]
+mediatailor = ["types-aiobotocore-mediatailor (>=2.4.0,<2.5.0)"]
+memorydb = ["types-aiobotocore-memorydb (>=2.4.0,<2.5.0)"]
+meteringmarketplace = ["types-aiobotocore-meteringmarketplace (>=2.4.0,<2.5.0)"]
+mgh = ["types-aiobotocore-mgh (>=2.4.0,<2.5.0)"]
+mgn = ["types-aiobotocore-mgn (>=2.4.0,<2.5.0)"]
+migration-hub-refactor-spaces = ["types-aiobotocore-migration-hub-refactor-spaces (>=2.4.0,<2.5.0)"]
+migrationhub-config = ["types-aiobotocore-migrationhub-config (>=2.4.0,<2.5.0)"]
+migrationhubstrategy = ["types-aiobotocore-migrationhubstrategy (>=2.4.0,<2.5.0)"]
+mobile = ["types-aiobotocore-mobile (>=2.4.0,<2.5.0)"]
+mq = ["types-aiobotocore-mq (>=2.4.0,<2.5.0)"]
+mturk = ["types-aiobotocore-mturk (>=2.4.0,<2.5.0)"]
+mwaa = ["types-aiobotocore-mwaa (>=2.4.0,<2.5.0)"]
+neptune = ["types-aiobotocore-neptune (>=2.4.0,<2.5.0)"]
+network-firewall = ["types-aiobotocore-network-firewall (>=2.4.0,<2.5.0)"]
+networkmanager = ["types-aiobotocore-networkmanager (>=2.4.0,<2.5.0)"]
+nimble = ["types-aiobotocore-nimble (>=2.4.0,<2.5.0)"]
+opensearch = ["types-aiobotocore-opensearch (>=2.4.0,<2.5.0)"]
+opsworks = ["types-aiobotocore-opsworks (>=2.4.0,<2.5.0)"]
+opsworkscm = ["types-aiobotocore-opsworkscm (>=2.4.0,<2.5.0)"]
+organizations = ["types-aiobotocore-organizations (>=2.4.0,<2.5.0)"]
+outposts = ["types-aiobotocore-outposts (>=2.4.0,<2.5.0)"]
+panorama = ["types-aiobotocore-panorama (>=2.4.0,<2.5.0)"]
+personalize = ["types-aiobotocore-personalize (>=2.4.0,<2.5.0)"]
+personalize-events = ["types-aiobotocore-personalize-events (>=2.4.0,<2.5.0)"]
+personalize-runtime = ["types-aiobotocore-personalize-runtime (>=2.4.0,<2.5.0)"]
+pi = ["types-aiobotocore-pi (>=2.4.0,<2.5.0)"]
+pinpoint = ["types-aiobotocore-pinpoint (>=2.4.0,<2.5.0)"]
+pinpoint-email = ["types-aiobotocore-pinpoint-email (>=2.4.0,<2.5.0)"]
+pinpoint-sms-voice = ["types-aiobotocore-pinpoint-sms-voice (>=2.4.0,<2.5.0)"]
+pinpoint-sms-voice-v2 = ["types-aiobotocore-pinpoint-sms-voice-v2 (>=2.4.0,<2.5.0)"]
+polly = ["types-aiobotocore-polly (>=2.4.0,<2.5.0)"]
+pricing = ["types-aiobotocore-pricing (>=2.4.0,<2.5.0)"]
+privatenetworks = ["types-aiobotocore-privatenetworks (>=2.4.0,<2.5.0)"]
+proton = ["types-aiobotocore-proton (>=2.4.0,<2.5.0)"]
+qldb = ["types-aiobotocore-qldb (>=2.4.0,<2.5.0)"]
+qldb-session = ["types-aiobotocore-qldb-session (>=2.4.0,<2.5.0)"]
+quicksight = ["types-aiobotocore-quicksight (>=2.4.0,<2.5.0)"]
+ram = ["types-aiobotocore-ram (>=2.4.0,<2.5.0)"]
+rbin = ["types-aiobotocore-rbin (>=2.4.0,<2.5.0)"]
+rds = ["types-aiobotocore-rds (>=2.4.0,<2.5.0)"]
+rds-data = ["types-aiobotocore-rds-data (>=2.4.0,<2.5.0)"]
+redshift = ["types-aiobotocore-redshift (>=2.4.0,<2.5.0)"]
+redshift-data = ["types-aiobotocore-redshift-data (>=2.4.0,<2.5.0)"]
+redshift-serverless = ["types-aiobotocore-redshift-serverless (>=2.4.0,<2.5.0)"]
+rekognition = ["types-aiobotocore-rekognition (>=2.4.0,<2.5.0)"]
+resiliencehub = ["types-aiobotocore-resiliencehub (>=2.4.0,<2.5.0)"]
+resource-groups = ["types-aiobotocore-resource-groups (>=2.4.0,<2.5.0)"]
+resourcegroupstaggingapi = ["types-aiobotocore-resourcegroupstaggingapi (>=2.4.0,<2.5.0)"]
+robomaker = ["types-aiobotocore-robomaker (>=2.4.0,<2.5.0)"]
+rolesanywhere = ["types-aiobotocore-rolesanywhere (>=2.4.0,<2.5.0)"]
+route53 = ["types-aiobotocore-route53 (>=2.4.0,<2.5.0)"]
+route53-recovery-cluster = ["types-aiobotocore-route53-recovery-cluster (>=2.4.0,<2.5.0)"]
+route53-recovery-control-config = ["types-aiobotocore-route53-recovery-control-config (>=2.4.0,<2.5.0)"]
+route53-recovery-readiness = ["types-aiobotocore-route53-recovery-readiness (>=2.4.0,<2.5.0)"]
+route53domains = ["types-aiobotocore-route53domains (>=2.4.0,<2.5.0)"]
+route53resolver = ["types-aiobotocore-route53resolver (>=2.4.0,<2.5.0)"]
+rum = ["types-aiobotocore-rum (>=2.4.0,<2.5.0)"]
+s3 = ["types-aiobotocore-s3 (>=2.4.0,<2.5.0)"]
+s3control = ["types-aiobotocore-s3control (>=2.4.0,<2.5.0)"]
+s3outposts = ["types-aiobotocore-s3outposts (>=2.4.0,<2.5.0)"]
+sagemaker = ["types-aiobotocore-sagemaker (>=2.4.0,<2.5.0)"]
+sagemaker-a2i-runtime = ["types-aiobotocore-sagemaker-a2i-runtime (>=2.4.0,<2.5.0)"]
+sagemaker-edge = ["types-aiobotocore-sagemaker-edge (>=2.4.0,<2.5.0)"]
+sagemaker-featurestore-runtime = ["types-aiobotocore-sagemaker-featurestore-runtime (>=2.4.0,<2.5.0)"]
+sagemaker-runtime = ["types-aiobotocore-sagemaker-runtime (>=2.4.0,<2.5.0)"]
+savingsplans = ["types-aiobotocore-savingsplans (>=2.4.0,<2.5.0)"]
+schemas = ["types-aiobotocore-schemas (>=2.4.0,<2.5.0)"]
+sdb = ["types-aiobotocore-sdb (>=2.4.0,<2.5.0)"]
+secretsmanager = ["types-aiobotocore-secretsmanager (>=2.4.0,<2.5.0)"]
+securityhub = ["types-aiobotocore-securityhub (>=2.4.0,<2.5.0)"]
+serverlessrepo = ["types-aiobotocore-serverlessrepo (>=2.4.0,<2.5.0)"]
+service-quotas = ["types-aiobotocore-service-quotas (>=2.4.0,<2.5.0)"]
+servicecatalog = ["types-aiobotocore-servicecatalog (>=2.4.0,<2.5.0)"]
+servicecatalog-appregistry = ["types-aiobotocore-servicecatalog-appregistry (>=2.4.0,<2.5.0)"]
+servicediscovery = ["types-aiobotocore-servicediscovery (>=2.4.0,<2.5.0)"]
+ses = ["types-aiobotocore-ses (>=2.4.0,<2.5.0)"]
+sesv2 = ["types-aiobotocore-sesv2 (>=2.4.0,<2.5.0)"]
+shield = ["types-aiobotocore-shield (>=2.4.0,<2.5.0)"]
+signer = ["types-aiobotocore-signer (>=2.4.0,<2.5.0)"]
+sms = ["types-aiobotocore-sms (>=2.4.0,<2.5.0)"]
+sms-voice = ["types-aiobotocore-sms-voice (>=2.4.0,<2.5.0)"]
+snow-device-management = ["types-aiobotocore-snow-device-management (>=2.4.0,<2.5.0)"]
+snowball = ["types-aiobotocore-snowball (>=2.4.0,<2.5.0)"]
+sns = ["types-aiobotocore-sns (>=2.4.0,<2.5.0)"]
+sqs = ["types-aiobotocore-sqs (>=2.4.0,<2.5.0)"]
+ssm = ["types-aiobotocore-ssm (>=2.4.0,<2.5.0)"]
+ssm-contacts = ["types-aiobotocore-ssm-contacts (>=2.4.0,<2.5.0)"]
+ssm-incidents = ["types-aiobotocore-ssm-incidents (>=2.4.0,<2.5.0)"]
+sso = ["types-aiobotocore-sso (>=2.4.0,<2.5.0)"]
+sso-admin = ["types-aiobotocore-sso-admin (>=2.4.0,<2.5.0)"]
+sso-oidc = ["types-aiobotocore-sso-oidc (>=2.4.0,<2.5.0)"]
+stepfunctions = ["types-aiobotocore-stepfunctions (>=2.4.0,<2.5.0)"]
+storagegateway = ["types-aiobotocore-storagegateway (>=2.4.0,<2.5.0)"]
+sts = ["types-aiobotocore-sts (>=2.4.0,<2.5.0)"]
+support = ["types-aiobotocore-support (>=2.4.0,<2.5.0)"]
+support-app = ["types-aiobotocore-support-app (>=2.4.0,<2.5.0)"]
+swf = ["types-aiobotocore-swf (>=2.4.0,<2.5.0)"]
+synthetics = ["types-aiobotocore-synthetics (>=2.4.0,<2.5.0)"]
+textract = ["types-aiobotocore-textract (>=2.4.0,<2.5.0)"]
+timestream-query = ["types-aiobotocore-timestream-query (>=2.4.0,<2.5.0)"]
+timestream-write = ["types-aiobotocore-timestream-write (>=2.4.0,<2.5.0)"]
+transcribe = ["types-aiobotocore-transcribe (>=2.4.0,<2.5.0)"]
+transfer = ["types-aiobotocore-transfer (>=2.4.0,<2.5.0)"]
+translate = ["types-aiobotocore-translate (>=2.4.0,<2.5.0)"]
+voice-id = ["types-aiobotocore-voice-id (>=2.4.0,<2.5.0)"]
+waf = ["types-aiobotocore-waf (>=2.4.0,<2.5.0)"]
+waf-regional = ["types-aiobotocore-waf-regional (>=2.4.0,<2.5.0)"]
+wafv2 = ["types-aiobotocore-wafv2 (>=2.4.0,<2.5.0)"]
+wellarchitected = ["types-aiobotocore-wellarchitected (>=2.4.0,<2.5.0)"]
+wisdom = ["types-aiobotocore-wisdom (>=2.4.0,<2.5.0)"]
+workdocs = ["types-aiobotocore-workdocs (>=2.4.0,<2.5.0)"]
+worklink = ["types-aiobotocore-worklink (>=2.4.0,<2.5.0)"]
+workmail = ["types-aiobotocore-workmail (>=2.4.0,<2.5.0)"]
+workmailmessageflow = ["types-aiobotocore-workmailmessageflow (>=2.4.0,<2.5.0)"]
+workspaces = ["types-aiobotocore-workspaces (>=2.4.0,<2.5.0)"]
+workspaces-web = ["types-aiobotocore-workspaces-web (>=2.4.0,<2.5.0)"]
+xray = ["types-aiobotocore-xray (>=2.4.0,<2.5.0)"]
+
+[[package]]
+name = "types-aiobotocore-s3"
+version = "2.4.1"
+description = "Type annotations for aiobotocore.S3 2.4.1 service generated with mypy-boto3-builder 7.11.11"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+typing-extensions = ">=4.1.0"
+
+[[package]]
+name = "types-awscrt"
+version = "0.16.1"
+description = "Type annotations and code completion for awscrt"
+category = "dev"
+optional = false
+python-versions = ">=3.7,<4.0"
+
+[[package]]
+name = "types-s3transfer"
+version = "0.6.0.post5"
+description = "Type annotations and code completion for s3transfer"
+category = "dev"
+optional = false
+python-versions = ">=3.7,<4.0"
+
+[package.dependencies]
+types-awscrt = "*"
+
+[[package]]
+name = "typing-extensions"
+version = "4.4.0"
+description = "Backported and Experimental Type Hints for Python 3.7+"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "urllib3"
+version = "1.26.13"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[package.extras]
+brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
+secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
+socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
+
+[[package]]
+name = "wrapt"
+version = "1.14.1"
+description = "Module for decorators, wrappers and monkey patching."
+category = "dev"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+
+[[package]]
+name = "yarl"
+version = "1.8.2"
+description = "Yet another URL library"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+idna = ">=2.0"
+multidict = ">=4.0"
+
+[metadata]
+lock-version = "1.1"
+python-versions = "^3.8"
+content-hash = "83881f75c5de8038c1b1b96d22787eaa01fdf5440718ea3d37b25adc332e5ccf"
+
+[metadata.files]
+aiobotocore = [
+ {file = "aiobotocore-2.4.1-py3-none-any.whl", hash = "sha256:26e0d55d5901f487c3467616c028abb85036ca33a0f88e279770adae6b865c69"},
+ {file = "aiobotocore-2.4.1.tar.gz", hash = "sha256:5c8ee79d1f14273e3f8f3af5c893db889ef163227b178ecacb5aea4547d3b9a7"},
+]
+aiohttp = [
+ {file = "aiohttp-3.8.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ba71c9b4dcbb16212f334126cc3d8beb6af377f6703d9dc2d9fb3874fd667ee9"},
+ {file = "aiohttp-3.8.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d24b8bb40d5c61ef2d9b6a8f4528c2f17f1c5d2d31fed62ec860f6006142e83e"},
+ {file = "aiohttp-3.8.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f88df3a83cf9df566f171adba39d5bd52814ac0b94778d2448652fc77f9eb491"},
+ {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97decbb3372d4b69e4d4c8117f44632551c692bb1361b356a02b97b69e18a62"},
+ {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:309aa21c1d54b8ef0723181d430347d7452daaff93e8e2363db8e75c72c2fb2d"},
+ {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad5383a67514e8e76906a06741febd9126fc7c7ff0f599d6fcce3e82b80d026f"},
+ {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20acae4f268317bb975671e375493dbdbc67cddb5f6c71eebdb85b34444ac46b"},
+ {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05a3c31c6d7cd08c149e50dc7aa2568317f5844acd745621983380597f027a18"},
+ {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d6f76310355e9fae637c3162936e9504b4767d5c52ca268331e2756e54fd4ca5"},
+ {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:256deb4b29fe5e47893fa32e1de2d73c3afe7407738bd3c63829874661d4822d"},
+ {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:5c59fcd80b9049b49acd29bd3598cada4afc8d8d69bd4160cd613246912535d7"},
+ {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:059a91e88f2c00fe40aed9031b3606c3f311414f86a90d696dd982e7aec48142"},
+ {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2feebbb6074cdbd1ac276dbd737b40e890a1361b3cc30b74ac2f5e24aab41f7b"},
+ {file = "aiohttp-3.8.3-cp310-cp310-win32.whl", hash = "sha256:5bf651afd22d5f0c4be16cf39d0482ea494f5c88f03e75e5fef3a85177fecdeb"},
+ {file = "aiohttp-3.8.3-cp310-cp310-win_amd64.whl", hash = "sha256:653acc3880459f82a65e27bd6526e47ddf19e643457d36a2250b85b41a564715"},
+ {file = "aiohttp-3.8.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:86fc24e58ecb32aee09f864cb11bb91bc4c1086615001647dbfc4dc8c32f4008"},
+ {file = "aiohttp-3.8.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75e14eac916f024305db517e00a9252714fce0abcb10ad327fb6dcdc0d060f1d"},
+ {file = "aiohttp-3.8.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d1fde0f44029e02d02d3993ad55ce93ead9bb9b15c6b7ccd580f90bd7e3de476"},
+ {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab94426ddb1ecc6a0b601d832d5d9d421820989b8caa929114811369673235c"},
+ {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89d2e02167fa95172c017732ed7725bc8523c598757f08d13c5acca308e1a061"},
+ {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02f9a2c72fc95d59b881cf38a4b2be9381b9527f9d328771e90f72ac76f31ad8"},
+ {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c7149272fb5834fc186328e2c1fa01dda3e1fa940ce18fded6d412e8f2cf76d"},
+ {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:512bd5ab136b8dc0ffe3fdf2dfb0c4b4f49c8577f6cae55dca862cd37a4564e2"},
+ {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7018ecc5fe97027214556afbc7c502fbd718d0740e87eb1217b17efd05b3d276"},
+ {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88c70ed9da9963d5496d38320160e8eb7e5f1886f9290475a881db12f351ab5d"},
+ {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:da22885266bbfb3f78218dc40205fed2671909fbd0720aedba39b4515c038091"},
+ {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:e65bc19919c910127c06759a63747ebe14f386cda573d95bcc62b427ca1afc73"},
+ {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:08c78317e950e0762c2983f4dd58dc5e6c9ff75c8a0efeae299d363d439c8e34"},
+ {file = "aiohttp-3.8.3-cp311-cp311-win32.whl", hash = "sha256:45d88b016c849d74ebc6f2b6e8bc17cabf26e7e40c0661ddd8fae4c00f015697"},
+ {file = "aiohttp-3.8.3-cp311-cp311-win_amd64.whl", hash = "sha256:96372fc29471646b9b106ee918c8eeb4cca423fcbf9a34daa1b93767a88a2290"},
+ {file = "aiohttp-3.8.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c971bf3786b5fad82ce5ad570dc6ee420f5b12527157929e830f51c55dc8af77"},
+ {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff25f48fc8e623d95eca0670b8cc1469a83783c924a602e0fbd47363bb54aaca"},
+ {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e381581b37db1db7597b62a2e6b8b57c3deec95d93b6d6407c5b61ddc98aca6d"},
+ {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db19d60d846283ee275d0416e2a23493f4e6b6028825b51290ac05afc87a6f97"},
+ {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25892c92bee6d9449ffac82c2fe257f3a6f297792cdb18ad784737d61e7a9a85"},
+ {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398701865e7a9565d49189f6c90868efaca21be65c725fc87fc305906be915da"},
+ {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4a4fbc769ea9b6bd97f4ad0b430a6807f92f0e5eb020f1e42ece59f3ecfc4585"},
+ {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:b29bfd650ed8e148f9c515474a6ef0ba1090b7a8faeee26b74a8ff3b33617502"},
+ {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:1e56b9cafcd6531bab5d9b2e890bb4937f4165109fe98e2b98ef0dcfcb06ee9d"},
+ {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ec40170327d4a404b0d91855d41bfe1fe4b699222b2b93e3d833a27330a87a6d"},
+ {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2df5f139233060578d8c2c975128fb231a89ca0a462b35d4b5fcf7c501ebdbe1"},
+ {file = "aiohttp-3.8.3-cp36-cp36m-win32.whl", hash = "sha256:f973157ffeab5459eefe7b97a804987876dd0a55570b8fa56b4e1954bf11329b"},
+ {file = "aiohttp-3.8.3-cp36-cp36m-win_amd64.whl", hash = "sha256:437399385f2abcd634865705bdc180c8314124b98299d54fe1d4c8990f2f9494"},
+ {file = "aiohttp-3.8.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:09e28f572b21642128ef31f4e8372adb6888846f32fecb288c8b0457597ba61a"},
+ {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f3553510abdbec67c043ca85727396ceed1272eef029b050677046d3387be8d"},
+ {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e168a7560b7c61342ae0412997b069753f27ac4862ec7867eff74f0fe4ea2ad9"},
+ {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db4c979b0b3e0fa7e9e69ecd11b2b3174c6963cebadeecfb7ad24532ffcdd11a"},
+ {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e164e0a98e92d06da343d17d4e9c4da4654f4a4588a20d6c73548a29f176abe2"},
+ {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8a78079d9a39ca9ca99a8b0ac2fdc0c4d25fc80c8a8a82e5c8211509c523363"},
+ {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:21b30885a63c3f4ff5b77a5d6caf008b037cb521a5f33eab445dc566f6d092cc"},
+ {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4b0f30372cef3fdc262f33d06e7b411cd59058ce9174ef159ad938c4a34a89da"},
+ {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:8135fa153a20d82ffb64f70a1b5c2738684afa197839b34cc3e3c72fa88d302c"},
+ {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:ad61a9639792fd790523ba072c0555cd6be5a0baf03a49a5dd8cfcf20d56df48"},
+ {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978b046ca728073070e9abc074b6299ebf3501e8dee5e26efacb13cec2b2dea0"},
+ {file = "aiohttp-3.8.3-cp37-cp37m-win32.whl", hash = "sha256:0d2c6d8c6872df4a6ec37d2ede71eff62395b9e337b4e18efd2177de883a5033"},
+ {file = "aiohttp-3.8.3-cp37-cp37m-win_amd64.whl", hash = "sha256:21d69797eb951f155026651f7e9362877334508d39c2fc37bd04ff55b2007091"},
+ {file = "aiohttp-3.8.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ca9af5f8f5812d475c5259393f52d712f6d5f0d7fdad9acdb1107dd9e3cb7eb"},
+ {file = "aiohttp-3.8.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d90043c1882067f1bd26196d5d2db9aa6d268def3293ed5fb317e13c9413ea4"},
+ {file = "aiohttp-3.8.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d737fc67b9a970f3234754974531dc9afeea11c70791dcb7db53b0cf81b79784"},
+ {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebf909ea0a3fc9596e40d55d8000702a85e27fd578ff41a5500f68f20fd32e6c"},
+ {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5835f258ca9f7c455493a57ee707b76d2d9634d84d5d7f62e77be984ea80b849"},
+ {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da37dcfbf4b7f45d80ee386a5f81122501ec75672f475da34784196690762f4b"},
+ {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87f44875f2804bc0511a69ce44a9595d5944837a62caecc8490bbdb0e18b1342"},
+ {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:527b3b87b24844ea7865284aabfab08eb0faf599b385b03c2aa91fc6edd6e4b6"},
+ {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d5ba88df9aa5e2f806650fcbeedbe4f6e8736e92fc0e73b0400538fd25a4dd96"},
+ {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e7b8813be97cab8cb52b1375f41f8e6804f6507fe4660152e8ca5c48f0436017"},
+ {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:2dea10edfa1a54098703cb7acaa665c07b4e7568472a47f4e64e6319d3821ccf"},
+ {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:713d22cd9643ba9025d33c4af43943c7a1eb8547729228de18d3e02e278472b6"},
+ {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2d252771fc85e0cf8da0b823157962d70639e63cb9b578b1dec9868dd1f4f937"},
+ {file = "aiohttp-3.8.3-cp38-cp38-win32.whl", hash = "sha256:66bd5f950344fb2b3dbdd421aaa4e84f4411a1a13fca3aeb2bcbe667f80c9f76"},
+ {file = "aiohttp-3.8.3-cp38-cp38-win_amd64.whl", hash = "sha256:84b14f36e85295fe69c6b9789b51a0903b774046d5f7df538176516c3e422446"},
+ {file = "aiohttp-3.8.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16c121ba0b1ec2b44b73e3a8a171c4f999b33929cd2397124a8c7fcfc8cd9e06"},
+ {file = "aiohttp-3.8.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8d6aaa4e7155afaf994d7924eb290abbe81a6905b303d8cb61310a2aba1c68ba"},
+ {file = "aiohttp-3.8.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43046a319664a04b146f81b40e1545d4c8ac7b7dd04c47e40bf09f65f2437346"},
+ {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599418aaaf88a6d02a8c515e656f6faf3d10618d3dd95866eb4436520096c84b"},
+ {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92a2964319d359f494f16011e23434f6f8ef0434acd3cf154a6b7bec511e2fb7"},
+ {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73a4131962e6d91109bca6536416aa067cf6c4efb871975df734f8d2fd821b37"},
+ {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:598adde339d2cf7d67beaccda3f2ce7c57b3b412702f29c946708f69cf8222aa"},
+ {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75880ed07be39beff1881d81e4a907cafb802f306efd6d2d15f2b3c69935f6fb"},
+ {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0239da9fbafd9ff82fd67c16704a7d1bccf0d107a300e790587ad05547681c8"},
+ {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4e3a23ec214e95c9fe85a58470b660efe6534b83e6cbe38b3ed52b053d7cb6ad"},
+ {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:47841407cc89a4b80b0c52276f3cc8138bbbfba4b179ee3acbd7d77ae33f7ac4"},
+ {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:54d107c89a3ebcd13228278d68f1436d3f33f2dd2af5415e3feaeb1156e1a62c"},
+ {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c37c5cce780349d4d51739ae682dec63573847a2a8dcb44381b174c3d9c8d403"},
+ {file = "aiohttp-3.8.3-cp39-cp39-win32.whl", hash = "sha256:f178d2aadf0166be4df834c4953da2d7eef24719e8aec9a65289483eeea9d618"},
+ {file = "aiohttp-3.8.3-cp39-cp39-win_amd64.whl", hash = "sha256:88e5be56c231981428f4f506c68b6a46fa25c4123a2e86d156c58a8369d31ab7"},
+ {file = "aiohttp-3.8.3.tar.gz", hash = "sha256:3828fb41b7203176b82fe5d699e0d845435f2374750a44b480ea6b930f6be269"},
+]
+aioitertools = [
+ {file = "aioitertools-0.11.0-py3-none-any.whl", hash = "sha256:04b95e3dab25b449def24d7df809411c10e62aab0cbe31a50ca4e68748c43394"},
+ {file = "aioitertools-0.11.0.tar.gz", hash = "sha256:42c68b8dd3a69c2bf7f2233bf7df4bb58b557bca5252ac02ed5187bbc67d6831"},
+]
+aiosignal = [
+ {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"},
+ {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"},
+]
+async-timeout = [
+ {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
+ {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
+]
+attrs = [
+ {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
+ {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
+]
+black = [
+ {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"},
+ {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"},
+ {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"},
+ {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"},
+ {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"},
+ {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"},
+ {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"},
+ {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"},
+ {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"},
+ {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"},
+ {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"},
+ {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"},
+]
+boto3 = [
+ {file = "boto3-1.24.59-py3-none-any.whl", hash = "sha256:34ab44146a2c4e7f4e72737f4b27e6eb5e0a7855c2f4599e3d9199b6a0a2d575"},
+ {file = "boto3-1.24.59.tar.gz", hash = "sha256:a50b4323f9579cfe22fcf5531fbd40b567d4d74c1adce06aeb5c95fce2a6fb40"},
+]
+boto3-stubs-lite = [
+ {file = "boto3-stubs-lite-1.26.27.tar.gz", hash = "sha256:cbd5b8d357021946973892d3aa11739189ca0bc6bbb72ddefd04ccb6428c7f7d"},
+ {file = "boto3_stubs_lite-1.26.27-py3-none-any.whl", hash = "sha256:4553258a7d064f0d390a17956d77334b2b194c73914283ee5628f52997ed1e54"},
+]
+botocore = [
+ {file = "botocore-1.27.59-py3-none-any.whl", hash = "sha256:69d756791fc024bda54f6c53f71ae34e695ee41bbbc1743d9179c4837a4929da"},
+ {file = "botocore-1.27.59.tar.gz", hash = "sha256:eda4aed6ee719a745d1288eaf1beb12f6f6448ad1fa12f159405db14ba9c92cf"},
+]
+botocore-stubs = [
+ {file = "botocore_stubs-1.29.26-py3-none-any.whl", hash = "sha256:2fb791ec38c02098d3f07f5f19b3b65920d0fce04a34080c128c84a4cbc07e3e"},
+ {file = "botocore_stubs-1.29.26.tar.gz", hash = "sha256:f77acc610ae1f501c0d7f3dd7115ede990e0de622021670f7800611209f46698"},
+]
+certifi = [
+ {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"},
+ {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
+]
+charset-normalizer = [
+ {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
+ {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
+]
+click = [
+ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
+ {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
+]
+colorama = [
+ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
+ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
+]
+coverage = [
+ {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"},
+ {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"},
+ {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"},
+ {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"},
+ {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"},
+ {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"},
+ {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"},
+ {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"},
+ {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"},
+ {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"},
+ {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"},
+ {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"},
+ {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"},
+ {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"},
+ {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"},
+ {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"},
+ {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"},
+ {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"},
+ {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"},
+ {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"},
+ {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"},
+ {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"},
+ {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"},
+ {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"},
+ {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"},
+ {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"},
+ {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"},
+ {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"},
+ {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"},
+ {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"},
+ {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"},
+ {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"},
+ {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"},
+ {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"},
+ {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"},
+ {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"},
+ {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"},
+ {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"},
+ {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"},
+ {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"},
+ {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"},
+ {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"},
+ {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"},
+ {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"},
+ {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"},
+ {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"},
+ {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"},
+ {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"},
+ {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"},
+ {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"},
+]
+exceptiongroup = [
+ {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"},
+ {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"},
+]
+freezegun = [
+ {file = "freezegun-1.2.2-py3-none-any.whl", hash = "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f"},
+ {file = "freezegun-1.2.2.tar.gz", hash = "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446"},
+]
+frozenlist = [
+ {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff8bf625fe85e119553b5383ba0fb6aa3d0ec2ae980295aaefa552374926b3f4"},
+ {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dfbac4c2dfcc082fcf8d942d1e49b6aa0766c19d3358bd86e2000bf0fa4a9cf0"},
+ {file = "frozenlist-1.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b1c63e8d377d039ac769cd0926558bb7068a1f7abb0f003e3717ee003ad85530"},
+ {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fdfc24dcfce5b48109867c13b4cb15e4660e7bd7661741a391f821f23dfdca7"},
+ {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c926450857408e42f0bbc295e84395722ce74bae69a3b2aa2a65fe22cb14b99"},
+ {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1841e200fdafc3d51f974d9d377c079a0694a8f06de2e67b48150328d66d5483"},
+ {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f470c92737afa7d4c3aacc001e335062d582053d4dbe73cda126f2d7031068dd"},
+ {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:783263a4eaad7c49983fe4b2e7b53fa9770c136c270d2d4bbb6d2192bf4d9caf"},
+ {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:924620eef691990dfb56dc4709f280f40baee568c794b5c1885800c3ecc69816"},
+ {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae4dc05c465a08a866b7a1baf360747078b362e6a6dbeb0c57f234db0ef88ae0"},
+ {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bed331fe18f58d844d39ceb398b77d6ac0b010d571cba8267c2e7165806b00ce"},
+ {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:02c9ac843e3390826a265e331105efeab489ffaf4dd86384595ee8ce6d35ae7f"},
+ {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9545a33965d0d377b0bc823dcabf26980e77f1b6a7caa368a365a9497fb09420"},
+ {file = "frozenlist-1.3.3-cp310-cp310-win32.whl", hash = "sha256:d5cd3ab21acbdb414bb6c31958d7b06b85eeb40f66463c264a9b343a4e238642"},
+ {file = "frozenlist-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:b756072364347cb6aa5b60f9bc18e94b2f79632de3b0190253ad770c5df17db1"},
+ {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4395e2f8d83fbe0c627b2b696acce67868793d7d9750e90e39592b3626691b7"},
+ {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14143ae966a6229350021384870458e4777d1eae4c28d1a7aa47f24d030e6678"},
+ {file = "frozenlist-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5d8860749e813a6f65bad8285a0520607c9500caa23fea6ee407e63debcdbef6"},
+ {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23d16d9f477bb55b6154654e0e74557040575d9d19fe78a161bd33d7d76808e8"},
+ {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb82dbba47a8318e75f679690190c10a5e1f447fbf9df41cbc4c3afd726d88cb"},
+ {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9309869032abb23d196cb4e4db574232abe8b8be1339026f489eeb34a4acfd91"},
+ {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a97b4fe50b5890d36300820abd305694cb865ddb7885049587a5678215782a6b"},
+ {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c188512b43542b1e91cadc3c6c915a82a5eb95929134faf7fd109f14f9892ce4"},
+ {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:303e04d422e9b911a09ad499b0368dc551e8c3cd15293c99160c7f1f07b59a48"},
+ {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0771aed7f596c7d73444c847a1c16288937ef988dc04fb9f7be4b2aa91db609d"},
+ {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:66080ec69883597e4d026f2f71a231a1ee9887835902dbe6b6467d5a89216cf6"},
+ {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:41fe21dc74ad3a779c3d73a2786bdf622ea81234bdd4faf90b8b03cad0c2c0b4"},
+ {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f20380df709d91525e4bee04746ba612a4df0972c1b8f8e1e8af997e678c7b81"},
+ {file = "frozenlist-1.3.3-cp311-cp311-win32.whl", hash = "sha256:f30f1928162e189091cf4d9da2eac617bfe78ef907a761614ff577ef4edfb3c8"},
+ {file = "frozenlist-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a6394d7dadd3cfe3f4b3b186e54d5d8504d44f2d58dcc89d693698e8b7132b32"},
+ {file = "frozenlist-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8df3de3a9ab8325f94f646609a66cbeeede263910c5c0de0101079ad541af332"},
+ {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0693c609e9742c66ba4870bcee1ad5ff35462d5ffec18710b4ac89337ff16e27"},
+ {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd4210baef299717db0a600d7a3cac81d46ef0e007f88c9335db79f8979c0d3d"},
+ {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:394c9c242113bfb4b9aa36e2b80a05ffa163a30691c7b5a29eba82e937895d5e"},
+ {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6327eb8e419f7d9c38f333cde41b9ae348bec26d840927332f17e887a8dcb70d"},
+ {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e24900aa13212e75e5b366cb9065e78bbf3893d4baab6052d1aca10d46d944c"},
+ {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3843f84a6c465a36559161e6c59dce2f2ac10943040c2fd021cfb70d58c4ad56"},
+ {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:84610c1502b2461255b4c9b7d5e9c48052601a8957cd0aea6ec7a7a1e1fb9420"},
+ {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c21b9aa40e08e4f63a2f92ff3748e6b6c84d717d033c7b3438dd3123ee18f70e"},
+ {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:efce6ae830831ab6a22b9b4091d411698145cb9b8fc869e1397ccf4b4b6455cb"},
+ {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:40de71985e9042ca00b7953c4f41eabc3dc514a2d1ff534027f091bc74416401"},
+ {file = "frozenlist-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:180c00c66bde6146a860cbb81b54ee0df350d2daf13ca85b275123bbf85de18a"},
+ {file = "frozenlist-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9bbbcedd75acdfecf2159663b87f1bb5cfc80e7cd99f7ddd9d66eb98b14a8411"},
+ {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:034a5c08d36649591be1cbb10e09da9f531034acfe29275fc5454a3b101ce41a"},
+ {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba64dc2b3b7b158c6660d49cdb1d872d1d0bf4e42043ad8d5006099479a194e5"},
+ {file = "frozenlist-1.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:47df36a9fe24054b950bbc2db630d508cca3aa27ed0566c0baf661225e52c18e"},
+ {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:008a054b75d77c995ea26629ab3a0c0d7281341f2fa7e1e85fa6153ae29ae99c"},
+ {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:841ea19b43d438a80b4de62ac6ab21cfe6827bb8a9dc62b896acc88eaf9cecba"},
+ {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e235688f42b36be2b6b06fc37ac2126a73b75fb8d6bc66dd632aa35286238703"},
+ {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca713d4af15bae6e5d79b15c10c8522859a9a89d3b361a50b817c98c2fb402a2"},
+ {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ac5995f2b408017b0be26d4a1d7c61bce106ff3d9e3324374d66b5964325448"},
+ {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4ae8135b11652b08a8baf07631d3ebfe65a4c87909dbef5fa0cdde440444ee4"},
+ {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ea42116ceb6bb16dbb7d526e242cb6747b08b7710d9782aa3d6732bd8d27649"},
+ {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:810860bb4bdce7557bc0febb84bbd88198b9dbc2022d8eebe5b3590b2ad6c842"},
+ {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ee78feb9d293c323b59a6f2dd441b63339a30edf35abcb51187d2fc26e696d13"},
+ {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0af2e7c87d35b38732e810befb9d797a99279cbb85374d42ea61c1e9d23094b3"},
+ {file = "frozenlist-1.3.3-cp38-cp38-win32.whl", hash = "sha256:899c5e1928eec13fd6f6d8dc51be23f0d09c5281e40d9cf4273d188d9feeaf9b"},
+ {file = "frozenlist-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:7f44e24fa70f6fbc74aeec3e971f60a14dde85da364aa87f15d1be94ae75aeef"},
+ {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2b07ae0c1edaa0a36339ec6cce700f51b14a3fc6545fdd32930d2c83917332cf"},
+ {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ebb86518203e12e96af765ee89034a1dbb0c3c65052d1b0c19bbbd6af8a145e1"},
+ {file = "frozenlist-1.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5cf820485f1b4c91e0417ea0afd41ce5cf5965011b3c22c400f6d144296ccbc0"},
+ {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c11e43016b9024240212d2a65043b70ed8dfd3b52678a1271972702d990ac6d"},
+ {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8fa3c6e3305aa1146b59a09b32b2e04074945ffcfb2f0931836d103a2c38f936"},
+ {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:352bd4c8c72d508778cf05ab491f6ef36149f4d0cb3c56b1b4302852255d05d5"},
+ {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65a5e4d3aa679610ac6e3569e865425b23b372277f89b5ef06cf2cdaf1ebf22b"},
+ {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e2c1185858d7e10ff045c496bbf90ae752c28b365fef2c09cf0fa309291669"},
+ {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f163d2fd041c630fed01bc48d28c3ed4a3b003c00acd396900e11ee5316b56bb"},
+ {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05cdb16d09a0832eedf770cb7bd1fe57d8cf4eaf5aced29c4e41e3f20b30a784"},
+ {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:8bae29d60768bfa8fb92244b74502b18fae55a80eac13c88eb0b496d4268fd2d"},
+ {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eedab4c310c0299961ac285591acd53dc6723a1ebd90a57207c71f6e0c2153ab"},
+ {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3bbdf44855ed8f0fbcd102ef05ec3012d6a4fd7c7562403f76ce6a52aeffb2b1"},
+ {file = "frozenlist-1.3.3-cp39-cp39-win32.whl", hash = "sha256:efa568b885bca461f7c7b9e032655c0c143d305bf01c30caf6db2854a4532b38"},
+ {file = "frozenlist-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfe33efc9cb900a4c46f91a5ceba26d6df370ffddd9ca386eb1d4f0ad97b9ea9"},
+ {file = "frozenlist-1.3.3.tar.gz", hash = "sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a"},
+]
+idna = [
+ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
+ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
+]
+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.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
+ {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
+]
+jmespath = [
+ {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"},
+ {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"},
+]
+multidict = [
+ {file = "multidict-6.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:73009ea04205966d47e16d98686ac5c438af23a1bb30b48a2c5da3423ec9ce37"},
+ {file = "multidict-6.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8b92a9f3ab904397a33b193000dc4de7318ea175c4c460a1e154c415f9008e3d"},
+ {file = "multidict-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:578bfcb16f4b8675ef71b960c00f174b0426e0eeb796bab6737389d8288eb827"},
+ {file = "multidict-6.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1650ea41c408755da5eed52ac6ccbc8938ccc3e698d81e6f6a1be02ff2a0945"},
+ {file = "multidict-6.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d52442e7c951e4c9ee591d6047706e66923d248d83958bbf99b8b19515fffaef"},
+ {file = "multidict-6.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad7d66422b9cc51125509229693d27e18c08f2dea3ac9de408d821932b1b3759"},
+ {file = "multidict-6.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cd14e61f0da2a2cfb9fe05bfced2a1ed7063ce46a7a8cd473be4973de9a7f91"},
+ {file = "multidict-6.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:190626ced82d4cc567a09e7346340d380154a493bac6905e0095d8158cdf1e38"},
+ {file = "multidict-6.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:791458a1f7d1b4ab3bd9e93e0dcd1d59ef7ee9aa051dcd1ea030e62e49b923fd"},
+ {file = "multidict-6.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b46e79a9f4db53897d17bc64a39d1c7c2be3e3d4f8dba6d6730a2b13ddf0f986"},
+ {file = "multidict-6.0.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e4a095e18847c12ec20e55326ab8782d9c2d599400a3a2f174fab4796875d0e2"},
+ {file = "multidict-6.0.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fb6c3dc3d65014d2c782f5acf0b3ba14e639c6c33d3ed8932ead76b9080b3544"},
+ {file = "multidict-6.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3541882266247c7cd3dba78d6ef28dbe704774df60c9e4231edaa4493522e614"},
+ {file = "multidict-6.0.3-cp310-cp310-win32.whl", hash = "sha256:67090b17a0a5be5704fd109f231ee73cefb1b3802d41288d6378b5df46ae89ba"},
+ {file = "multidict-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:36df958b15639e40472adaa4f0c2c7828fe680f894a6b48c4ce229f59a6a798b"},
+ {file = "multidict-6.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b51969503709415a35754954c2763f536a70b8bf7360322b2edb0c0a44391f6"},
+ {file = "multidict-6.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:24e8d513bfcaadc1f8b0ebece3ff50961951c54b07d5a775008a882966102418"},
+ {file = "multidict-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d325d61cac602976a5d47b19eaa7d04e3daf4efce2164c630219885087234102"},
+ {file = "multidict-6.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbbe17f8a7211b623502d2bf41022a51da3025142401417c765bf9a56fed4c"},
+ {file = "multidict-6.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4fb3fe591956d8841882c463f934c9f7485cfd5f763a08c0d467b513dc18ef89"},
+ {file = "multidict-6.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1925f78a543b94c3d46274c66a366fee8a263747060220ed0188e5f3eeea1c0"},
+ {file = "multidict-6.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21e1ce0b187c4e93112304dcde2aa18922fdbe8fb4f13d8aa72a5657bce0563a"},
+ {file = "multidict-6.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e07c24018986fb00d6e7eafca8fcd6e05095649e17fcf0e33a592caaa62a78b9"},
+ {file = "multidict-6.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:114a4ab3e5cfbc56c4b6697686ecb92376c7e8c56893ef20547921552f8bdf57"},
+ {file = "multidict-6.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4ccf55f28066b4f08666764a957c2b7c241c7547b0921d69c7ceab5f74fe1a45"},
+ {file = "multidict-6.0.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:9d359b0a962e052b713647ac1f13eabf2263167b149ed1e27d5c579f5c8c7d2c"},
+ {file = "multidict-6.0.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:df7b4cee3ff31b3335aba602f8d70dbc641e5b7164b1e9565570c9d3c536a438"},
+ {file = "multidict-6.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ee9b1cae9a6c5d023e5a150f6f6b9dbb3c3bbc7887d6ee07d4c0ecb49a473734"},
+ {file = "multidict-6.0.3-cp311-cp311-win32.whl", hash = "sha256:960ce1b790952916e682093788696ef7e33ac6a97482f9b983abdc293091b531"},
+ {file = "multidict-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:2b66d61966b12e6bba500e5cbb2c721a35e119c30ee02495c5629bd0e91eea30"},
+ {file = "multidict-6.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:526f8397fc124674b8f39748680a0ff673bd6a715fecb4866716d36e380f015f"},
+ {file = "multidict-6.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f5d5129a937af4e3c4a1d6c139f4051b7d17d43276cefdd8d442a7031f7eef2"},
+ {file = "multidict-6.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38d394814b39be1c36ac709006d39d50d72a884f9551acd9c8cc1ffae3fc8c4e"},
+ {file = "multidict-6.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99341ca1f1db9e7f47914cb2461305665a662383765ced6f843712564766956d"},
+ {file = "multidict-6.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5790cc603456b6dcf8a9a4765f666895a6afddc88b3d3ba7b53dea2b6e23116"},
+ {file = "multidict-6.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce8e51774eb03844588d3c279adb94efcd0edeccd2f97516623292445bcc01f9"},
+ {file = "multidict-6.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:baa96a3418e27d723064854143b2f414a422c84cc87285a71558722049bebc5a"},
+ {file = "multidict-6.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:cb4a08f0aaaa869f189ffea0e17b86ad0237b51116d494da15ef7991ee6ad2d7"},
+ {file = "multidict-6.0.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:62db44727d0befea68e8ad2881bb87a9cfb6b87d45dd78609009627167f37b69"},
+ {file = "multidict-6.0.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:4cc5c8cd205a9810d16a5cd428cd81bac554ad1477cb87f4ad722b10992e794d"},
+ {file = "multidict-6.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f76109387e1ec8d8e2137c94c437b89fe002f29e0881aae8ae45529bdff92000"},
+ {file = "multidict-6.0.3-cp37-cp37m-win32.whl", hash = "sha256:f8a728511c977df6f3d8af388fcb157e49f11db4a6637dd60131b8b6e40b0253"},
+ {file = "multidict-6.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c2a1168e5aa7c72499fb03c850e0f03f624fa4a5c8d2e215c518d0a73872eb64"},
+ {file = "multidict-6.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:eddf604a3de2ace3d9a4e4d491be7562a1ac095a0a1c95a9ec5781ef0273ef11"},
+ {file = "multidict-6.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d09daf5c6ce7fc6ed444c9339bbde5ea84e2534d1ca1cd37b60f365c77f00dea"},
+ {file = "multidict-6.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:12e0d396faa6dc55ff5379eee54d1df3b508243ff15bfc8295a6ec7a4483a335"},
+ {file = "multidict-6.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70740c2bc9ab1c99f7cdcb104f27d16c63860c56d51c5bf0ef82fc1d892a2131"},
+ {file = "multidict-6.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e322c94596054352f5a02771eec71563c018b15699b961aba14d6dd943367022"},
+ {file = "multidict-6.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4159fc1ec9ede8ab93382e0d6ba9b1b3d23c72da39a834db7a116986605c7ab4"},
+ {file = "multidict-6.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47defc0218682281a52fb1f6346ebb8b68b17538163a89ea24dfe4da37a8a9a3"},
+ {file = "multidict-6.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f9511e48bde6b995825e8d35e434fc96296cf07a25f4aae24ff9162be7eaa46"},
+ {file = "multidict-6.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e0bce9f7c30e7e3a9e683f670314c0144e8d34be6b7019e40604763bd278d84f"},
+ {file = "multidict-6.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:01b456046a05ff7cceefb0e1d2a9d32f05efcb1c7e0d152446304e11557639ce"},
+ {file = "multidict-6.0.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:8230a39bae6c2e8a09e4da6bace5064693b00590a4a213e38f9a9366da10e7dd"},
+ {file = "multidict-6.0.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:445c0851a1cbc1f2ec3b40bc22f9c4a235edb3c9a0906122a9df6ea8d51f886c"},
+ {file = "multidict-6.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9aac6881454a750554ed4b280a839dcf9e2133a9d12ab4d417d673fb102289b7"},
+ {file = "multidict-6.0.3-cp38-cp38-win32.whl", hash = "sha256:81c3d597591b0940e04949e4e4f79359b2d2e542a686ba0da5e25de33fec13e0"},
+ {file = "multidict-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:dc4cfef5d899f5f1a15f3d2ac49f71107a01a5a2745b4dd53fa0cede1419385a"},
+ {file = "multidict-6.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d408172519049e36fb6d29672f060dc8461fc7174eba9883c7026041ef9bfb38"},
+ {file = "multidict-6.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e068dfeadbce63072b2d8096486713d04db4946aad0a0f849bd4fc300799d0d3"},
+ {file = "multidict-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8b817d4ed68fd568ec5e45dd75ddf30cc72a47a6b41b74d5bb211374c296f5e"},
+ {file = "multidict-6.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cf5d19e12eff855aa198259c0b02fd3f5d07e1291fbd20279c37b3b0e6c9852"},
+ {file = "multidict-6.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5a811aab1b4aea0b4be669363c19847a8c547510f0e18fb632956369fdbdf67"},
+ {file = "multidict-6.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2cfda34b7cb99eacada2072e0f69c0ad3285cb6f8e480b11f2b6d6c1c6f92718"},
+ {file = "multidict-6.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beeca903e4270b4afcd114f371a9602240dc143f9e944edfea00f8d4ad56c40d"},
+ {file = "multidict-6.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd5771e8ea325f85cbb361ddbdeb9ae424a68e5dfb6eea786afdcd22e68a7d5d"},
+ {file = "multidict-6.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9dbab2a7e9c073bc9538824a01f5ed689194db7f55f2b8102766873e906a6c1a"},
+ {file = "multidict-6.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f2c0957b3e8c66c10d27272709a5299ab3670a0f187c9428f3b90d267119aedb"},
+ {file = "multidict-6.0.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:94cbe5535ef150546b8321aebea22862a3284da51e7b55f6f95b7d73e96d90ee"},
+ {file = "multidict-6.0.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d0e798b072cf2aab9daceb43d97c9c527a0c7593e67a7846ad4cc6051de1e303"},
+ {file = "multidict-6.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a27b029caa3b555a4f3da54bc1e718eb55fcf1a11fda8bf0132147b476cf4c08"},
+ {file = "multidict-6.0.3-cp39-cp39-win32.whl", hash = "sha256:018c8e3be7f161a12b3e41741b6721f9baeb2210f4ab25a6359b7d76c1017dce"},
+ {file = "multidict-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:5e58ec0375803526d395f6f7e730ecc45d06e15f68f7b9cdbf644a2918324e51"},
+ {file = "multidict-6.0.3.tar.gz", hash = "sha256:2523a29006c034687eccd3ee70093a697129a3ffe8732535d3b2df6a4ecc279d"},
+]
+mypy-boto3-s3 = [
+ {file = "mypy-boto3-s3-1.26.0.post1.tar.gz", hash = "sha256:6d7079f8c739dc993cbedad0736299c413b297814b73795a3855a79169ecc938"},
+ {file = "mypy_boto3_s3-1.26.0.post1-py3-none-any.whl", hash = "sha256:7de2792ff0cc541b84cd46ff3a6aa2b6e5f267217f2203f27f6e4016bddc644d"},
+]
+mypy-extensions = [
+ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
+ {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
+]
+packaging = [
+ {file = "packaging-22.0-py3-none-any.whl", hash = "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3"},
+ {file = "packaging-22.0.tar.gz", hash = "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3"},
+]
+pathspec = [
+ {file = "pathspec-0.10.2-py3-none-any.whl", hash = "sha256:88c2606f2c1e818b978540f73ecc908e13999c6c3a383daf3705652ae79807a5"},
+ {file = "pathspec-0.10.2.tar.gz", hash = "sha256:8f6bf73e5758fd365ef5d58ce09ac7c27d2833a8d7da51712eac6e27e35141b0"},
+]
+platformdirs = [
+ {file = "platformdirs-2.6.0-py3-none-any.whl", hash = "sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca"},
+ {file = "platformdirs-2.6.0.tar.gz", hash = "sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e"},
+]
+pluggy = [
+ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
+ {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
+]
+py-cpuinfo = [
+ {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"},
+ {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"},
+]
+py-spy = [
+ {file = "py_spy-0.3.14-py2.py3-none-macosx_10_7_x86_64.whl", hash = "sha256:5b342cc5feb8d160d57a7ff308de153f6be68dcf506ad02b4d67065f2bae7f45"},
+ {file = "py_spy-0.3.14-py2.py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:fe7efe6c91f723442259d428bf1f9ddb9c1679828866b353d539345ca40d9dd2"},
+ {file = "py_spy-0.3.14-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590905447241d789d9de36cff9f52067b6f18d8b5e9fb399242041568d414461"},
+ {file = "py_spy-0.3.14-py2.py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd6211fe7f587b3532ba9d300784326d9a6f2b890af7bf6fff21a029ebbc812b"},
+ {file = "py_spy-0.3.14-py2.py3-none-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3e8e48032e71c94c3dd51694c39e762e4bbfec250df5bf514adcdd64e79371e0"},
+ {file = "py_spy-0.3.14-py2.py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f59b0b52e56ba9566305236375e6fc68888261d0d36b5addbe3cf85affbefc0e"},
+ {file = "py_spy-0.3.14-py2.py3-none-win_amd64.whl", hash = "sha256:8f5b311d09f3a8e33dbd0d44fc6e37b715e8e0c7efefafcda8bfd63b31ab5a31"},
+]
+pytest = [
+ {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"},
+ {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"},
+]
+pytest-asyncio = [
+ {file = "pytest-asyncio-0.20.3.tar.gz", hash = "sha256:83cbf01169ce3e8eb71c6c278ccb0574d1a7a3bb8eaaf5e50e0ad342afb33b36"},
+ {file = "pytest_asyncio-0.20.3-py3-none-any.whl", hash = "sha256:f129998b209d04fcc65c96fc85c11e5316738358909a8399e93be553d7656442"},
+]
+pytest-benchmark = [
+ {file = "pytest-benchmark-4.0.0.tar.gz", hash = "sha256:fb0785b83efe599a6a956361c0691ae1dbb5318018561af10f3e915caa0048d1"},
+ {file = "pytest_benchmark-4.0.0-py3-none-any.whl", hash = "sha256:fdb7db64e31c8b277dff9850d2a2556d8b60bcb0ea6524e36e28ffd7c87f71d6"},
+]
+pytest-cov = [
+ {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"},
+ {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"},
+]
+pytest-freezegun = [
+ {file = "pytest-freezegun-0.4.2.zip", hash = "sha256:19c82d5633751bf3ec92caa481fb5cffaac1787bd485f0df6436fd6242176949"},
+ {file = "pytest_freezegun-0.4.2-py2.py3-none-any.whl", hash = "sha256:5318a6bfb8ba4b709c8471c94d0033113877b3ee02da5bfcd917c1889cde99a7"},
+]
+python-dateutil = [
+ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
+ {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
+]
+requests = [
+ {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
+ {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
+]
+s3transfer = [
+ {file = "s3transfer-0.6.0-py3-none-any.whl", hash = "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd"},
+ {file = "s3transfer-0.6.0.tar.gz", hash = "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947"},
+]
+six = [
+ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+tomli = [
+ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
+ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
+]
+types-aiobotocore-lite = [
+ {file = "types-aiobotocore-lite-2.4.1.tar.gz", hash = "sha256:3eeba6311e56bbe2d128e8f452d483d789ef0030f147475e9478a5038d462a0f"},
+ {file = "types_aiobotocore_lite-2.4.1-py3-none-any.whl", hash = "sha256:c043e254227edba6e2c3311627e93c47bad07c95c38247800c463b484a30e1a4"},
+]
+types-aiobotocore-s3 = [
+ {file = "types-aiobotocore-s3-2.4.1.tar.gz", hash = "sha256:2a176a6170803fb1905a1b4896409d033cde252be312771e42e80313b464073d"},
+ {file = "types_aiobotocore_s3-2.4.1-py3-none-any.whl", hash = "sha256:44b8bbbb46386c25ffe89de4eb22088c0ff1c88f4d344f9c2dd14942d84590a4"},
+]
+types-awscrt = [
+ {file = "types_awscrt-0.16.1-py3-none-any.whl", hash = "sha256:8dce0e6b35bb41ed3824b2db82e15408766b3d5f7113a684441ec3ae10a867f7"},
+ {file = "types_awscrt-0.16.1.tar.gz", hash = "sha256:4e9d8fa79d0b540ccf780a46aa5bb08a30acd5fb6b6aac6491eaee41d69c560b"},
+]
+types-s3transfer = [
+ {file = "types_s3transfer-0.6.0.post5-py3-none-any.whl", hash = "sha256:cfcbee11c16d60af3feb3dbffea3a85b32129235b562912dece310a45ae83a2c"},
+ {file = "types_s3transfer-0.6.0.post5.tar.gz", hash = "sha256:2cf1e07cf4d1a5a2a68d89c654f45d9c3b678d39f7fe03a6f36903b6dbd3bcbc"},
+]
+typing-extensions = [
+ {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
+ {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"},
+]
+urllib3 = [
+ {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"},
+ {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"},
+]
+wrapt = [
+ {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"},
+ {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"},
+ {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"},
+ {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"},
+ {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"},
+ {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"},
+ {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"},
+ {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"},
+ {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"},
+ {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"},
+ {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"},
+ {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"},
+ {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"},
+ {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"},
+ {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"},
+ {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"},
+ {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"},
+ {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"},
+ {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"},
+ {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"},
+ {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"},
+ {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"},
+ {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"},
+ {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"},
+ {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"},
+ {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"},
+ {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"},
+ {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"},
+ {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"},
+ {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"},
+ {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"},
+ {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"},
+ {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"},
+ {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"},
+ {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"},
+ {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"},
+ {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"},
+ {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"},
+ {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"},
+ {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"},
+ {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"},
+ {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"},
+ {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"},
+ {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"},
+ {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"},
+ {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"},
+ {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"},
+ {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"},
+ {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"},
+ {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"},
+ {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"},
+ {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"},
+ {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"},
+ {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"},
+ {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"},
+ {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"},
+ {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"},
+ {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"},
+ {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"},
+ {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"},
+ {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"},
+ {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"},
+ {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"},
+ {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"},
+]
+yarl = [
+ {file = "yarl-1.8.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bb81f753c815f6b8e2ddd2eef3c855cf7da193b82396ac013c661aaa6cc6b0a5"},
+ {file = "yarl-1.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:47d49ac96156f0928f002e2424299b2c91d9db73e08c4cd6742923a086f1c863"},
+ {file = "yarl-1.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3fc056e35fa6fba63248d93ff6e672c096f95f7836938241ebc8260e062832fe"},
+ {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58a3c13d1c3005dbbac5c9f0d3210b60220a65a999b1833aa46bd6677c69b08e"},
+ {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10b08293cda921157f1e7c2790999d903b3fd28cd5c208cf8826b3b508026996"},
+ {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de986979bbd87272fe557e0a8fcb66fd40ae2ddfe28a8b1ce4eae22681728fef"},
+ {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c4fcfa71e2c6a3cb568cf81aadc12768b9995323186a10827beccf5fa23d4f8"},
+ {file = "yarl-1.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae4d7ff1049f36accde9e1ef7301912a751e5bae0a9d142459646114c70ecba6"},
+ {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bf071f797aec5b96abfc735ab97da9fd8f8768b43ce2abd85356a3127909d146"},
+ {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:74dece2bfc60f0f70907c34b857ee98f2c6dd0f75185db133770cd67300d505f"},
+ {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:df60a94d332158b444301c7f569659c926168e4d4aad2cfbf4bce0e8fb8be826"},
+ {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:63243b21c6e28ec2375f932a10ce7eda65139b5b854c0f6b82ed945ba526bff3"},
+ {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cfa2bbca929aa742b5084fd4663dd4b87c191c844326fcb21c3afd2d11497f80"},
+ {file = "yarl-1.8.2-cp310-cp310-win32.whl", hash = "sha256:b05df9ea7496df11b710081bd90ecc3a3db6adb4fee36f6a411e7bc91a18aa42"},
+ {file = "yarl-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:24ad1d10c9db1953291f56b5fe76203977f1ed05f82d09ec97acb623a7976574"},
+ {file = "yarl-1.8.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2a1fca9588f360036242f379bfea2b8b44cae2721859b1c56d033adfd5893634"},
+ {file = "yarl-1.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f37db05c6051eff17bc832914fe46869f8849de5b92dc4a3466cd63095d23dfd"},
+ {file = "yarl-1.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77e913b846a6b9c5f767b14dc1e759e5aff05502fe73079f6f4176359d832581"},
+ {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0978f29222e649c351b173da2b9b4665ad1feb8d1daa9d971eb90df08702668a"},
+ {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388a45dc77198b2460eac0aca1efd6a7c09e976ee768b0d5109173e521a19daf"},
+ {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2305517e332a862ef75be8fad3606ea10108662bc6fe08509d5ca99503ac2aee"},
+ {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42430ff511571940d51e75cf42f1e4dbdded477e71c1b7a17f4da76c1da8ea76"},
+ {file = "yarl-1.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3150078118f62371375e1e69b13b48288e44f6691c1069340081c3fd12c94d5b"},
+ {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c15163b6125db87c8f53c98baa5e785782078fbd2dbeaa04c6141935eb6dab7a"},
+ {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4d04acba75c72e6eb90745447d69f84e6c9056390f7a9724605ca9c56b4afcc6"},
+ {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e7fd20d6576c10306dea2d6a5765f46f0ac5d6f53436217913e952d19237efc4"},
+ {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:75c16b2a900b3536dfc7014905a128a2bea8fb01f9ee26d2d7d8db0a08e7cb2c"},
+ {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6d88056a04860a98341a0cf53e950e3ac9f4e51d1b6f61a53b0609df342cc8b2"},
+ {file = "yarl-1.8.2-cp311-cp311-win32.whl", hash = "sha256:fb742dcdd5eec9f26b61224c23baea46c9055cf16f62475e11b9b15dfd5c117b"},
+ {file = "yarl-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8c46d3d89902c393a1d1e243ac847e0442d0196bbd81aecc94fcebbc2fd5857c"},
+ {file = "yarl-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ceff9722e0df2e0a9e8a79c610842004fa54e5b309fe6d218e47cd52f791d7ef"},
+ {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f6b4aca43b602ba0f1459de647af954769919c4714706be36af670a5f44c9c1"},
+ {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1684a9bd9077e922300ecd48003ddae7a7474e0412bea38d4631443a91d61077"},
+ {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ebb78745273e51b9832ef90c0898501006670d6e059f2cdb0e999494eb1450c2"},
+ {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3adeef150d528ded2a8e734ebf9ae2e658f4c49bf413f5f157a470e17a4a2e89"},
+ {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57a7c87927a468e5a1dc60c17caf9597161d66457a34273ab1760219953f7f4c"},
+ {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:efff27bd8cbe1f9bd127e7894942ccc20c857aa8b5a0327874f30201e5ce83d0"},
+ {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a783cd344113cb88c5ff7ca32f1f16532a6f2142185147822187913eb989f739"},
+ {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:705227dccbe96ab02c7cb2c43e1228e2826e7ead880bb19ec94ef279e9555b5b"},
+ {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:34c09b43bd538bf6c4b891ecce94b6fa4f1f10663a8d4ca589a079a5018f6ed7"},
+ {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a48f4f7fea9a51098b02209d90297ac324241bf37ff6be6d2b0149ab2bd51b37"},
+ {file = "yarl-1.8.2-cp37-cp37m-win32.whl", hash = "sha256:0414fd91ce0b763d4eadb4456795b307a71524dbacd015c657bb2a39db2eab89"},
+ {file = "yarl-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:d881d152ae0007809c2c02e22aa534e702f12071e6b285e90945aa3c376463c5"},
+ {file = "yarl-1.8.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5df5e3d04101c1e5c3b1d69710b0574171cc02fddc4b23d1b2813e75f35a30b1"},
+ {file = "yarl-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7a66c506ec67eb3159eea5096acd05f5e788ceec7b96087d30c7d2865a243918"},
+ {file = "yarl-1.8.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2b4fa2606adf392051d990c3b3877d768771adc3faf2e117b9de7eb977741229"},
+ {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e21fb44e1eff06dd6ef971d4bdc611807d6bd3691223d9c01a18cec3677939e"},
+ {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93202666046d9edadfe9f2e7bf5e0782ea0d497b6d63da322e541665d65a044e"},
+ {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc77086ce244453e074e445104f0ecb27530d6fd3a46698e33f6c38951d5a0f1"},
+ {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dd68a92cab699a233641f5929a40f02a4ede8c009068ca8aa1fe87b8c20ae3"},
+ {file = "yarl-1.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b372aad2b5f81db66ee7ec085cbad72c4da660d994e8e590c997e9b01e44901"},
+ {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e6f3515aafe0209dd17fb9bdd3b4e892963370b3de781f53e1746a521fb39fc0"},
+ {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dfef7350ee369197106805e193d420b75467b6cceac646ea5ed3049fcc950a05"},
+ {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:728be34f70a190566d20aa13dc1f01dc44b6aa74580e10a3fb159691bc76909d"},
+ {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ff205b58dc2929191f68162633d5e10e8044398d7a45265f90a0f1d51f85f72c"},
+ {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baf211dcad448a87a0d9047dc8282d7de59473ade7d7fdf22150b1d23859f946"},
+ {file = "yarl-1.8.2-cp38-cp38-win32.whl", hash = "sha256:272b4f1599f1b621bf2aabe4e5b54f39a933971f4e7c9aa311d6d7dc06965165"},
+ {file = "yarl-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:326dd1d3caf910cd26a26ccbfb84c03b608ba32499b5d6eeb09252c920bcbe4f"},
+ {file = "yarl-1.8.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f8ca8ad414c85bbc50f49c0a106f951613dfa5f948ab69c10ce9b128d368baf8"},
+ {file = "yarl-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:418857f837347e8aaef682679f41e36c24250097f9e2f315d39bae3a99a34cbf"},
+ {file = "yarl-1.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae0eec05ab49e91a78700761777f284c2df119376e391db42c38ab46fd662b77"},
+ {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:009a028127e0a1755c38b03244c0bea9d5565630db9c4cf9572496e947137a87"},
+ {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3edac5d74bb3209c418805bda77f973117836e1de7c000e9755e572c1f7850d0"},
+ {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da65c3f263729e47351261351b8679c6429151ef9649bba08ef2528ff2c423b2"},
+ {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ef8fb25e52663a1c85d608f6dd72e19bd390e2ecaf29c17fb08f730226e3a08"},
+ {file = "yarl-1.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcd7bb1e5c45274af9a1dd7494d3c52b2be5e6bd8d7e49c612705fd45420b12d"},
+ {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44ceac0450e648de86da8e42674f9b7077d763ea80c8ceb9d1c3e41f0f0a9951"},
+ {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:97209cc91189b48e7cfe777237c04af8e7cc51eb369004e061809bcdf4e55220"},
+ {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:48dd18adcf98ea9cd721a25313aef49d70d413a999d7d89df44f469edfb38a06"},
+ {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e59399dda559688461762800d7fb34d9e8a6a7444fd76ec33220a926c8be1516"},
+ {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d617c241c8c3ad5c4e78a08429fa49e4b04bedfc507b34b4d8dceb83b4af3588"},
+ {file = "yarl-1.8.2-cp39-cp39-win32.whl", hash = "sha256:cb6d48d80a41f68de41212f3dfd1a9d9898d7841c8f7ce6696cf2fd9cb57ef83"},
+ {file = "yarl-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:6604711362f2dbf7160df21c416f81fac0de6dbcf0b5445a2ef25478ecc4c778"},
+ {file = "yarl-1.8.2.tar.gz", hash = "sha256:49d43402c6e3013ad0978602bf6bf5328535c48d192304b91b97a3c6790b1562"},
+]
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..5cede1a
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,37 @@
+[tool.poetry]
+name = "fast-s3-url"
+version = "0.1.0"
+description = "An S3 signed URL generator, faster than boto3's, for generating lots of signed URLs quickly."
+authors = ["André Carvalho "]
+readme = "README.md"
+packages = [
+ {include = "fast_s3_url"},
+ {include = "fast_s3_url/py.typed"}
+]
+license = "MIT"
+homepage = "https://github.com/RedRoserade/py-fast-s3-url"
+repository = "https://github.com/RedRoserade/py-fast-s3-url"
+
+[tool.poetry.dependencies]
+python = "^3.8"
+
+
+[tool.poetry.group.dev.dependencies]
+aiobotocore = "^2.4.1"
+pytest = "^7.2.0"
+pytest-benchmark = "^4.0.0"
+types-aiobotocore-lite = {extras = ["s3"], version = "^2.4.1"}
+py-spy = "^0.3.14"
+black = "^22.12.0"
+freezegun = "^1.2.2"
+pytest-asyncio = "^0.20.3"
+boto3 = "<1.26.27"
+boto3-stubs-lite = {extras = ["s3"], version = "^1.26.27"}
+pytest-freezegun = "^0.4.2"
+requests = "^2.28.1"
+isort = "^5.10.1"
+pytest-cov = "^4.0.0"
+
+[build-system]
+requires = ["poetry-core"]
+build-backend = "poetry.core.masonry.api"
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/conftest.py b/test/conftest.py
new file mode 100644
index 0000000..24d865d
--- /dev/null
+++ b/test/conftest.py
@@ -0,0 +1,63 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, AsyncIterable
+import pytest
+import pytest_asyncio
+
+import boto3
+
+from aiobotocore.session import get_session
+
+from botocore.config import Config
+
+if TYPE_CHECKING:
+ from types_aiobotocore_s3 import S3Client as AioBotocoreS3Client
+ from mypy_boto3_s3.client import S3Client as Boto3S3Client
+
+
+@pytest.fixture
+def s3_config() -> Config:
+ """
+ S3 configuration to ensure signed URLs use the s3v4 format,
+ regardless of region (some default to v2 format).
+ """
+ return Config(
+ signature_version="s3v4",
+ )
+
+
+@pytest_asyncio.fixture # type: ignore
+async def aio_s3_client(s3_config: Config) -> AsyncIterable[AioBotocoreS3Client]:
+ session = get_session()
+
+ endpoint_url = "http://localhost:9000"
+
+ access_key_id = "minioadmin"
+ secret_key = "minioadmin"
+
+ async with session.create_client(
+ "s3",
+ endpoint_url=endpoint_url,
+ aws_access_key_id=access_key_id,
+ aws_secret_access_key=secret_key,
+ config=s3_config,
+ ) as client:
+ yield client # type: ignore
+
+
+@pytest.fixture
+def boto3_client(s3_config: Config) -> Boto3S3Client:
+ endpoint_url = "http://localhost:9000"
+
+ access_key_id = "minioadmin"
+ secret_key = "minioadmin"
+
+ client = boto3.client(
+ "s3",
+ endpoint_url=endpoint_url,
+ aws_access_key_id=access_key_id,
+ aws_secret_access_key=secret_key,
+ config=s3_config,
+ )
+
+ return client # type: ignore
diff --git a/test/integration/__init__.py b/test/integration/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/integration/test_integration.py b/test/integration/test_integration.py
new file mode 100644
index 0000000..1cefc7f
--- /dev/null
+++ b/test/integration/test_integration.py
@@ -0,0 +1,66 @@
+from __future__ import annotations
+
+import secrets
+from typing import TYPE_CHECKING, Iterable, Tuple
+import requests
+
+import pytest
+
+from fast_s3_url import FastS3UrlSigner
+
+
+if TYPE_CHECKING:
+ from mypy_boto3_s3.client import S3Client as Boto3S3Client
+
+
+@pytest.fixture
+def bucket(boto3_client: Boto3S3Client) -> Iterable[str]:
+ bucket_name = f"test-{secrets.token_hex(4)}"
+
+ try:
+ boto3_client.create_bucket(Bucket=bucket_name)
+ yield bucket_name
+ finally:
+ boto3_client.delete_bucket(Bucket=bucket_name)
+
+
+@pytest.fixture
+def object_with_contents(
+ boto3_client: Boto3S3Client, bucket: str
+) -> Iterable[Tuple[str, bytes]]:
+ object_key = f"test/object/{secrets.token_hex(16)}.txt"
+ contents = secrets.token_bytes(64)
+
+ try:
+ boto3_client.put_object(
+ Bucket=bucket,
+ Key=object_key,
+ Body=contents,
+ )
+
+ yield (object_key, contents)
+ finally:
+ boto3_client.delete_object(
+ Bucket=bucket,
+ Key=object_key,
+ )
+
+
+def test_integration(
+ boto3_client: Boto3S3Client, bucket: str, object_with_contents: Tuple[str, bytes]
+):
+ # Arrange
+ signer = FastS3UrlSigner.from_boto3_client(boto3_client, bucket)
+
+ object_key, expected_contents = object_with_contents
+
+ # Act
+ signed_url, *_ = signer.generate_presigned_get_object_urls([object_key])
+
+ # Assert
+ # The URL must be valid and points to the actual object
+ response = requests.get(signed_url)
+
+ response.raise_for_status()
+
+ assert response.content == expected_contents
diff --git a/test/perf/__init__.py b/test/perf/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/perf/test_perf.py b/test/perf/test_perf.py
new file mode 100644
index 0000000..40b9ae7
--- /dev/null
+++ b/test/perf/test_perf.py
@@ -0,0 +1,58 @@
+from __future__ import annotations
+import secrets
+
+from typing import TYPE_CHECKING, List
+import pytest
+
+from fast_s3_url import FastS3UrlSigner
+
+
+if TYPE_CHECKING:
+ from mypy_boto3_s3.client import S3Client as Boto3S3Client
+ from pytest_benchmark.fixture import BenchmarkFixture # type: ignore
+
+
+pytestmark = [pytest.mark.benchmark(group="signed_url_generation")]
+
+
+OBJECT_KEY_COUNT = [1, 8, 128, 512, 1024]
+
+
+@pytest.mark.parametrize("object_key_count", OBJECT_KEY_COUNT)
+def test_boto3(
+ benchmark: BenchmarkFixture,
+ boto3_client: Boto3S3Client,
+ object_key_count: int,
+) -> None:
+ object_keys = [secrets.token_hex(16) for _ in range(object_key_count)]
+
+ def sign_many():
+ return [
+ boto3_client.generate_presigned_url(
+ "get_object",
+ Params={"Bucket": "my-bucket", "Key": object_key},
+ )
+ for object_key in object_keys
+ ]
+
+ signed_urls: List[str] = benchmark(sign_many)
+
+ assert len(signed_urls) == len(object_keys)
+
+
+@pytest.mark.parametrize("object_key_count", OBJECT_KEY_COUNT)
+def test_custom(
+ benchmark: BenchmarkFixture,
+ boto3_client: Boto3S3Client,
+ object_key_count: int,
+) -> None:
+ object_keys = [secrets.token_hex(16) for _ in range(object_key_count)]
+
+ sig = FastS3UrlSigner.from_boto3_client(boto3_client, "my-bucket")
+
+ signed_urls: List[str] = benchmark(
+ sig.generate_presigned_get_object_urls,
+ object_keys,
+ )
+
+ assert len(signed_urls) == len(object_keys)
diff --git a/test/unit/__init__.py b/test/unit/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/unit/test_signed_url_comparison.py b/test/unit/test_signed_url_comparison.py
new file mode 100644
index 0000000..baf3c5d
--- /dev/null
+++ b/test/unit/test_signed_url_comparison.py
@@ -0,0 +1,199 @@
+from __future__ import annotations
+import os
+
+from typing import TYPE_CHECKING, Any, Dict
+from unittest import mock
+
+from aiobotocore.session import get_session
+import boto3
+
+import pytest
+
+from fast_s3_url import FastS3UrlSigner
+
+from botocore.config import Config
+
+from test.util.url_comparison import assert_urls_match
+
+if TYPE_CHECKING:
+ from types_aiobotocore_s3 import S3Client as AioBotocoreS3Client
+ from mypy_boto3_s3.client import S3Client as Boto3S3Client
+
+
+pytestmark = [
+ # Freeze time during test runs such that
+ # the URLs are consistent (they have timestamps in them)
+ pytest.mark.freeze_time,
+]
+
+OBJECT_KEYS = [
+ "cat.jpg",
+ "/my/image.png",
+ "something/else.gif",
+ " ",
+ "hello world.txt",
+ "Scheiße.dat",
+ "hello~world.txt",
+ "abc..def.xyz",
+]
+
+EXPIRES_IN = [
+ 3600,
+]
+
+
+@pytest.mark.parametrize("object_key", OBJECT_KEYS)
+@pytest.mark.parametrize("expires_in", EXPIRES_IN)
+def test_generates_urls_like_boto(
+ object_key: str, expires_in: int, boto3_client: Boto3S3Client
+) -> None:
+ # Arrange
+ expected_url = boto3_client.generate_presigned_url(
+ "get_object",
+ Params={"Bucket": "bucket", "Key": object_key},
+ ExpiresIn=expires_in,
+ )
+
+ sig = FastS3UrlSigner.from_boto3_client(boto3_client, "bucket")
+
+ # Act
+ actual_url, *_ = sig.generate_presigned_get_object_urls(
+ [object_key], expires_in=expires_in
+ )
+
+ # Assert
+ assert_urls_match(actual_url, expected_url)
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize("object_key", OBJECT_KEYS)
+@pytest.mark.parametrize("expires_in", EXPIRES_IN)
+async def test_generates_urls_like_aiobotocore(
+ object_key: str, expires_in: int, aio_s3_client: AioBotocoreS3Client
+) -> None:
+ # Arrange
+ expected_url = await aio_s3_client.generate_presigned_url(
+ "get_object",
+ Params={"Bucket": "test", "Key": object_key},
+ ExpiresIn=expires_in,
+ )
+
+ sig = await FastS3UrlSigner.from_aiobotocore_client(aio_s3_client, "test")
+
+ # Act
+ actual_url, *_ = sig.generate_presigned_get_object_urls(
+ [object_key], expires_in=expires_in
+ )
+
+ # Assert
+ assert_urls_match(actual_url, expected_url)
+
+
+_DUMMY_S3_CREDENTIALS = {
+ "AWS_ACCESS_KEY_ID": "ACCESS",
+ "AWS_SECRET_ACCESS_KEY": "SuperS3cret",
+}
+
+_DUMMY_S3_CREDENTIALS_WITH_TOKEN = {
+ **_DUMMY_S3_CREDENTIALS,
+ "AWS_SESSION_TOKEN": (
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
+ "eyJjdXJpb3NpdHkiOiJLaWxsZWQgdGhlIGNhdCJ9."
+ "Q1DwKcK9NBZdNxMj_BOeU1VFmjuisMKKtNgPVI1_Ie8"
+ ),
+}
+
+_DUMMY_S3_CREDENTIALS_WITH_REGION = {
+ **_DUMMY_S3_CREDENTIALS,
+ "AWS_DEFAULT_REGION": "us-west-2",
+}
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+ "credentials",
+ [
+ _DUMMY_S3_CREDENTIALS,
+ _DUMMY_S3_CREDENTIALS_WITH_TOKEN,
+ _DUMMY_S3_CREDENTIALS_WITH_REGION,
+ ],
+ ids=[
+ "DUMMY",
+ "DUMMY_WITH_TOKEN",
+ "DUMMY_WITH_REGION",
+ ],
+)
+async def test_generates_urls_like_aiobotocore_with_default_credentials(
+ credentials: Dict[str, Any],
+ s3_config: Config,
+) -> None:
+ # Arrange
+
+ object_key = "cat.jpg"
+ expires_in = 300
+ bucket = "test"
+
+ session = get_session()
+
+ with mock.patch.dict(os.environ, credentials):
+ aio_s3_client: AioBotocoreS3Client
+ async with session.create_client("s3", config=s3_config) as aio_s3_client: # type: ignore
+
+ expected_url = await aio_s3_client.generate_presigned_url(
+ "get_object",
+ Params={"Bucket": bucket, "Key": object_key},
+ ExpiresIn=expires_in,
+ )
+
+ sig = await FastS3UrlSigner.from_aiobotocore_client(aio_s3_client, bucket)
+
+ # Act
+ actual_url, *_ = sig.generate_presigned_get_object_urls(
+ [object_key], expires_in=expires_in
+ )
+
+ # Assert
+ assert_urls_match(actual_url, expected_url)
+
+
+@pytest.mark.parametrize(
+ "credentials",
+ [
+ _DUMMY_S3_CREDENTIALS,
+ _DUMMY_S3_CREDENTIALS_WITH_TOKEN,
+ _DUMMY_S3_CREDENTIALS_WITH_REGION,
+ ],
+ ids=[
+ "DUMMY",
+ "DUMMY_WITH_TOKEN",
+ "DUMMY_WITH_REGION",
+ ],
+)
+def test_generates_urls_like_boto3_with_default_credentials(
+ credentials: Dict[str, Any],
+ s3_config: Config,
+) -> None:
+ # Arrange
+
+ bucket = "test"
+ object_key = "cat.jpg"
+ expires_in = 300
+
+ with mock.patch.dict(os.environ, credentials):
+ boto3_client: Boto3S3Client = boto3.client("s3", config=s3_config) # type: ignore
+
+ expected_url = boto3_client.generate_presigned_url(
+ "get_object",
+ Params={"Bucket": bucket, "Key": object_key},
+ ExpiresIn=expires_in,
+ )
+
+ sig = FastS3UrlSigner.from_boto3_client(boto3_client, bucket)
+
+ # Act
+ actual_url, *_ = sig.generate_presigned_get_object_urls(
+ [object_key], expires_in=expires_in
+ )
+
+ # Assert
+ assert_urls_match(actual_url, expected_url)
diff --git a/test/unit/test_validation.py b/test/unit/test_validation.py
new file mode 100644
index 0000000..02ea6d0
--- /dev/null
+++ b/test/unit/test_validation.py
@@ -0,0 +1,26 @@
+from __future__ import annotations
+
+
+import pytest
+
+from fast_s3_url import FastS3UrlSigner
+
+
+@pytest.mark.parametrize(
+ "invalid_object_key",
+ [
+ None,
+ "",
+ ],
+)
+def test_validates_parameters(invalid_object_key: str):
+ # Arrange
+ sig = FastS3UrlSigner(
+ bucket_endpoint_url="http://localhost:9000/my-bucket/",
+ access_key_id="minioadmin",
+ secret_access_key="minioadmin",
+ )
+
+ # Act / Assert
+ with pytest.raises(ValueError):
+ sig.generate_presigned_get_object_urls([invalid_object_key])
diff --git a/test/util/__init__.py b/test/util/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/util/url_comparison.py b/test/util/url_comparison.py
new file mode 100644
index 0000000..70918d1
--- /dev/null
+++ b/test/util/url_comparison.py
@@ -0,0 +1,16 @@
+from urllib.parse import urlparse, parse_qs
+
+
+def assert_urls_match(actual: str, expected: str) -> None:
+ """
+ Check that the URLs match, by comparing their components.
+ Query string parameters are compared out-of-order.
+ """
+
+ parsed_actual = urlparse(actual)
+ parsed_expected = urlparse(expected)
+
+ assert parsed_actual.scheme == parsed_expected.scheme
+ assert parsed_actual.netloc == parsed_expected.netloc
+ assert parsed_actual.path == parsed_expected.path
+ assert parse_qs(parsed_actual.params) == parse_qs(parsed_expected.params)